diff --git a/Assets/Tests/InputSystem/APIVerificationTests.cs b/Assets/Tests/InputSystem/APIVerificationTests.cs index 03fd7e5275..9ae3cf2ec4 100644 --- a/Assets/Tests/InputSystem/APIVerificationTests.cs +++ b/Assets/Tests/InputSystem/APIVerificationTests.cs @@ -258,7 +258,7 @@ public void API_MonoBehavioursHaveHelpUrls() // The .api files are platform-specific so we can only compare on the platform // they were built on. - #if UNITY_EDITOR_WIN +#if UNITY_EDITOR_WIN // We disable "API Verification" tests running as part of the validation suite as they give us // false positives (specifically, for setters having changes accessibility from private to protected). @@ -541,6 +541,56 @@ public class DualShock4GamepadHID : DualShockGamepad public InputTestFixture.ActionConstraint Performed(InputAction action, InputControl control = default(InputControl), System.Nullable time = default(System.Nullable), System.Nullable duration = default(System.Nullable)); public InputTestFixture.ActionConstraint Started(InputAction action, InputControl control = default(InputControl), System.Nullable time = default(System.Nullable)); ")] + // API scraper output for these built-in XR controller types differs depending on installed XR replacement packages. + [Property("Exclusions", @"1.0.0 + public class DaydreamController : UnityEngine.InputSystem.XR.XRController + public class GearVRTrackedController : UnityEngine.InputSystem.XR.XRController + public class OculusTouchController : UnityEngine.InputSystem.XR.XRControllerWithRumble + public class HandedViveTracker : ViveTracker + public class OpenVRControllerWMR : UnityEngine.InputSystem.XR.XRController + public class OpenVROculusTouchController : UnityEngine.InputSystem.XR.XRControllerWithRumble + public class ViveWand : UnityEngine.InputSystem.XR.XRControllerWithRumble + ")] + // API scraper in 1.0.0 emitted incomplete default argument expressions for these overloads. + [Property("Exclusions", @"1.0.0 + public static string GetBindingDisplayString(this InputAction action, int bindingIndex, InputBinding.DisplayStringOptions options = ); + public static string GetBindingDisplayString(this InputAction action, InputBinding bindingMask, InputBinding.DisplayStringOptions options = ); + public static string GetBindingDisplayString(this InputAction action, InputBinding.DisplayStringOptions options = , string group = default(string)); + public static string GetBindingDisplayString(this InputAction action, int bindingIndex, out string deviceLayoutName, out string controlPath, InputBinding.DisplayStringOptions options = ); + public string ToDisplayString(InputBinding.DisplayStringOptions options = , InputControl control = default(InputControl)); + public string ToDisplayString(out string deviceLayoutName, out string controlPath, InputBinding.DisplayStringOptions options = , InputControl control = default(InputControl)); + DontIncludeInteractions = 4, + DontOmitDevice = 2, + DontUseShortDisplayNames = 1, + IgnoreBindingOverrides = 8, + OmitDevice = 2, + UseShortNames = 4, + BufferedBytes = 256, + Constant = 1, + NonLinear = 16, + NoPreferred = 32, + NullState = 64, + Relative = 4, + Variable = 2, + Volatile = 128, + Wrap = 8, + public FourCC(char a, char b = , char c = , char d = ) {} + public static string ToHumanReadableString(string path, InputControlPath.HumanReadableStringOptions options = InputControlPath.HumanReadableStringOptions.None, InputControl control = default(InputControl)); + public static string ToHumanReadableString(string path, out string deviceLayoutName, out string controlPath, InputControlPath.HumanReadableStringOptions options = InputControlPath.HumanReadableStringOptions.None, InputControl control = default(InputControl)); + public class InputStateHistory : InputStateHistory, System.Collections.Generic.IEnumerable>, System.Collections.Generic.IReadOnlyCollection>, System.Collections.Generic.IReadOnlyList>, System.Collections.IEnumerable where TValue : struct, new() + public UnityEngine.InputSystem.LowLevel.InputStateHistory this[int index] { get; set; } + public UnityEngine.InputSystem.LowLevel.InputStateHistory AddRecord(UnityEngine.InputSystem.LowLevel.InputStateHistory record); + public System.Collections.Generic.IEnumerator> GetEnumerator(); + public UnityEngine.InputSystem.LowLevel.InputStateHistory RecordStateChange(UnityEngine.InputSystem.InputControl control, TValue value, double time = -1d); + public struct Record : System.IEquatable> + public UnityEngine.InputSystem.LowLevel.InputStateHistory next { get; } + public UnityEngine.InputSystem.LowLevel.InputStateHistory owner { get; } + public UnityEngine.InputSystem.LowLevel.InputStateHistory previous { get; } + public void CopyFrom(UnityEngine.InputSystem.LowLevel.InputStateHistory record); + public bool Equals(UnityEngine.InputSystem.LowLevel.InputStateHistory other); + public SteamHandle(ulong handle) {} + public static ulong op_Explicit(UnityEngine.InputSystem.Steam.SteamHandle handle); + ")] // Api scraper seems to be unstable with fields with default values, sometimes "= 0;" appears (locally) and sometimes (on CI) doesn't. [Property("Exclusions", @"1.0.0 public int negative = 0; @@ -555,6 +605,57 @@ public class DualShock4GamepadHID : DualShockGamepad [ScopedExclusionProperty("1.0.0", "UnityEngine.InputSystem.LowLevel", "public struct KeyboardState : IInputStateTypeInfo", "public fixed byte keys[14];")] // Allow Key.IMESelected to be marked as Obsolete [ScopedExclusionProperty("1.0.0", "UnityEngine.InputSystem", "public enum Key", "IMESelected = 111,")] + +#if !UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT + // Steam support is conditional (#if UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT) and absent when + // the steam plugin is not installed, so all Steam types are excluded from the comparison. + [Property("Exclusions", @"1.0.0 + namespace UnityEngine.InputSystem.Steam + public interface ISteamControllerAPI + public void ActivateActionSet(UnityEngine.InputSystem.Steam.SteamHandle controllerHandle, UnityEngine.InputSystem.Steam.SteamHandle actionSetHandle); + public void ActivateActionSetLayer(UnityEngine.InputSystem.Steam.SteamHandle controllerHandle, UnityEngine.InputSystem.Steam.SteamHandle actionSetLayerHandle); + public void DeactivateActionSetLayer(UnityEngine.InputSystem.Steam.SteamHandle controllerHandle, UnityEngine.InputSystem.Steam.SteamHandle actionSetLayerHandle); + public void DeactivateAllActionSetLayers(UnityEngine.InputSystem.Steam.SteamHandle controllerHandle); + public UnityEngine.InputSystem.Steam.SteamHandle GetActionSetHandle(string actionSetName); + public int GetActiveActionSetLayers(UnityEngine.InputSystem.Steam.SteamHandle controllerHandle, out UnityEngine.InputSystem.Steam.SteamHandle handlesOut); + public SteamAnalogActionData GetAnalogActionData(UnityEngine.InputSystem.Steam.SteamHandle controllerHandle, UnityEngine.InputSystem.Steam.SteamHandle analogActionHandle); + public UnityEngine.InputSystem.Steam.SteamHandle GetAnalogActionHandle(string actionName); + public int GetConnectedControllers(UnityEngine.InputSystem.Steam.SteamHandle[] outHandles); + public UnityEngine.InputSystem.Steam.SteamHandle GetCurrentActionSet(UnityEngine.InputSystem.Steam.SteamHandle controllerHandle); + public SteamDigitalActionData GetDigitalActionData(UnityEngine.InputSystem.Steam.SteamHandle controllerHandle, UnityEngine.InputSystem.Steam.SteamHandle digitalActionHandle); + public UnityEngine.InputSystem.Steam.SteamHandle GetDigitalActionHandle(string actionName); + public void RunFrame(); + public struct SteamAnalogActionData + public bool active { get; set; } + public Vector2 position { get; set; } + public abstract class SteamController : InputDevice + public bool autoActivateSets { get; set; } + public UnityEngine.InputSystem.Steam.SteamHandle currentSteamActionSet { get; } + public abstract UnityEngine.InputSystem.Utilities.ReadOnlyArray steamActionSets { get; } + public UnityEngine.InputSystem.Steam.SteamHandle steamControllerHandle { get; } + protected SteamController() {} + public void ActivateSteamActionSet(UnityEngine.InputSystem.Steam.SteamHandle actionSet); + protected abstract void ResolveSteamActions(ISteamControllerAPI api); + protected abstract void Update(ISteamControllerAPI api); + public struct SteamActionSetInfo + public UnityEngine.InputSystem.Steam.SteamHandle handle { get; set; } + public struct SteamDigitalActionData + public bool active { get; set; } + public bool pressed { get; set; } + public struct SteamHandle : System.IEquatable> + public bool Equals(UnityEngine.InputSystem.Steam.SteamHandle other); + public static bool operator ==(UnityEngine.InputSystem.Steam.SteamHandle a, UnityEngine.InputSystem.Steam.SteamHandle b); + public static bool operator !=(UnityEngine.InputSystem.Steam.SteamHandle a, UnityEngine.InputSystem.Steam.SteamHandle b); + namespace UnityEngine.InputSystem.Steam.Editor + public static class SteamIGAConverter + public static string ConvertInputActionsToSteamIGA(System.Collections.Generic.IEnumerable actionMaps, string locale = @""english""); + public static string ConvertInputActionsToSteamIGA(InputActionAsset asset, string locale = @""english""); + public static string GenerateInputDeviceFromSteamIGA(string vdf, string namespaceAndClassName); + public static string GetSteamControllerInputType(InputAction action); + public static System.Collections.Generic.Dictionary ParseVDF(string vdf); + ")] +#endif + public void API_MinorVersionsHaveNoBreakingChanges() { var currentVersion = CoreTests.PackageJson.ReadVersion(); @@ -628,6 +729,32 @@ private static string FilterIgnoredChanges(string line) if (line.Length == 0) return line; + // Older API scraper versions emitted fully-qualified C# primitive type names (e.g. System.UInt32), + // while newer versions emit C# language aliases (e.g. uint). Normalize to aliases so that a scraper + // version change does not produce false-positive breaking change reports. + line = line + .Replace("System.UInt64", "ulong") + .Replace("System.UInt32", "uint") + .Replace("System.UInt16", "ushort") + .Replace("System.Int64", "long") + .Replace("System.Int32", "int") + .Replace("System.Int16", "short") + .Replace("System.Boolean", "bool") + .Replace("System.Single", "float") + .Replace("System.Double", "double") + .Replace("System.Byte", "byte") + .Replace("System.SByte", "sbyte") + .Replace("System.Char", "char"); + + // Normalize constant expressions that different scraper versions emit differently. + // Older scrapers resolved expressions to decimal; newer scrapers may keep symbolic forms. + line = line.Replace("uint.MaxValue", "4294967295") + .Replace("uint.MinValue", "0"); + line = Regex.Replace(line, @"\b0x([0-9a-fA-F]+)\b", + m => Convert.ToUInt64(m.Groups[1].Value, 16).ToString()); + line = Regex.Replace(line, @"\b(\d+) << (\d+)\b", + m => (ulong.Parse(m.Groups[1].Value) << int.Parse(m.Groups[2].Value)).ToString()); + var pos = 0; while (true) { @@ -635,21 +762,44 @@ private static string FilterIgnoredChanges(string line) while (pos < line.Length && char.IsWhiteSpace(line[pos])) ++pos; - if (pos < line.Length && line[pos] != '[') + if (pos >= line.Length || line[pos] != '[') return line; var startPos = pos; ++pos; - while (pos < line.Length + 1 && !(line[pos] == ']' && line[pos + 1] == ' ')) - ++pos; - ++pos; - var length = pos - startPos - 2; - var attribute = line.Substring(startPos + 1, length); - if (!attribute.StartsWith("System.Obsolete")) + // Find the matching closing ']' using bracket depth tracking. + // This correctly handles new[] syntax in attribute arguments, e.g.: + // [InputControl(aliases = new[] {@"a", @"b"})] public uint buttons; + // The old scraper used Mono.Cecil.CustomAttributeArgument[] (inner ] followed by ,) + // but the new scraper uses new[] {...} where the inner ] is followed by a space, + // which the old naive scan would incorrectly treat as the end of the attribute. + var depth = 1; + while (pos < line.Length && depth > 0) + { + if (line[pos] == '[') depth++; + else if (line[pos] == ']') depth--; + if (depth > 0) ++pos; + } + + if (pos >= line.Length) + return line; // No matching ']' found, bail out. + + ++pos; // Move past the closing ']'. + + // The attribute must be followed by a space to have any content after it. + if (pos >= line.Length || line[pos] != ' ') + return line; + + var attributeContent = line.Substring(startPos + 1, pos - startPos - 2); + if (!attributeContent.StartsWith("System.Obsolete")) { line = line.Substring(0, startPos) + line.Substring(pos + 1); // Snip space after ']'. - pos -= length + 2; + pos = startPos; + } + else + { + ++pos; // Skip the space after the kept Obsolete attribute. } } }