From a260324119a9ab061c6941a248a791b782658fdc Mon Sep 17 00:00:00 2001 From: Aswin Gopal <68984963+AswinRajGopal@users.noreply.github.com> Date: Thu, 25 Sep 2025 16:28:27 +0530 Subject: [PATCH 01/18] Update the parser for migration --- .../InputSystem/Actions/InputActionAsset.cs | 129 +++++++++++++----- 1 file changed, 97 insertions(+), 32 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs index 3b9a70fcca..df0be653e5 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -using UnityEngine.InputSystem.Editor; +using System.Globalization; using UnityEngine.InputSystem.Utilities; ////TODO: make the FindAction logic available on any IEnumerable and IInputActionCollection via extension methods @@ -1014,60 +1014,125 @@ internal void MigrateJson(ref ReadFileJson parsedJson) { if (parsedJson.version >= JsonVersion.Version1) return; - if ((parsedJson.maps?.Length ?? 0) > 0 && (parsedJson.version) < JsonVersion.Version1) + + if (parsedJson.maps == null || parsedJson.maps.Length == 0) + { + parsedJson.version = JsonVersion.Version1; + return; + } + + for (var mi = 0; mi < parsedJson.maps.Length; ++mi) { - for (var mi = 0; mi < parsedJson.maps.Length; ++mi) + var mapJson = parsedJson.maps[mi]; + if (mapJson.actions == null || mapJson.actions.Length == 0) + continue; + + for (var ai = 0; ai < mapJson.actions.Length; ++ai) { - var mapJson = parsedJson.maps[mi]; - for (var ai = 0; ai < mapJson.actions.Length; ++ai) + var actionJson = mapJson.actions[ai]; + var raw = actionJson.processors; + if (string.IsNullOrEmpty(raw)) + continue; + + var parts = System.Text.RegularExpressions.Regex.Split(raw, @"\s*([,;])\s*"); + if (parts.Length == 0) + continue; + + var tokens = new List(); + for (int i = 0; i < parts.Length; i += 2) + if (!string.IsNullOrEmpty(parts[i])) + tokens.Add(parts[i]); + + if (tokens.Count == 0) + continue; + + var parsed = new List(tokens.Count); + foreach (var t in tokens) + parsed.Add(NameAndParameters.Parse(t)); + + var rebuiltTokens = new List(tokens.Count); + var anyProcessorChanged = false; + + for (int pi = 0; pi < parsed.Count; pi++) { - var actionJson = mapJson.actions[ai]; - var raw = actionJson.processors; - if (string.IsNullOrEmpty(raw)) + var nap = parsed[pi]; + + var procType = InputSystem.TryGetProcessor(nap.name); + if (procType == null || nap.parameters.Count == 0) + { + rebuiltTokens.Add(tokens[pi]); continue; + } + + var dict = new Dictionary(nap.parameters.Count, System.StringComparer.OrdinalIgnoreCase); + foreach (var p in nap.parameters) + dict[p.name] = p.value.ToString(); + + var changedThisProcessor = false; - var list = NameAndParameters.ParseMultiple(raw).ToList(); - var rebuilt = new List(list.Count); - foreach (var nap in list) + foreach (var field in procType.GetFields(BindingFlags.Public | BindingFlags.Instance)) { - var procType = InputSystem.TryGetProcessor(nap.name); - if (nap.parameters.Count == 0 || procType == null) - { - rebuilt.Add(nap.ToString()); + if (!field.FieldType.IsEnum) + continue; + + if (!dict.TryGetValue(field.Name, out var rawVal)) continue; - } - var dict = nap.parameters.ToDictionary(p => p.name, p => p.value.ToString()); - var anyChanged = false; - foreach (var field in procType.GetFields(BindingFlags.Public | BindingFlags.Instance).Where(f => f.FieldType.IsEnum)) + if (int.TryParse(rawVal, NumberStyles.Integer, CultureInfo.InvariantCulture, out var n)) { - if (dict.TryGetValue(field.Name, out var ordS) && int.TryParse(ordS, out var ord)) + var values = System.Enum.GetValues(field.FieldType); + var looksLikeOrdinal = n >= 0 && n < values.Length && !System.Enum.IsDefined(field.FieldType, n); + if (looksLikeOrdinal) { - var values = Enum.GetValues(field.FieldType).Cast().ToArray(); - if (ord >= 0 && ord < values.Length) + var underlying = Convert.ToInt32(values.GetValue(n)); + if (underlying != n) { - dict[field.Name] = Convert.ToInt32(values[ord]).ToString(); - anyChanged = true; + dict[field.Name] = underlying.ToString(CultureInfo.InvariantCulture); + changedThisProcessor = true; } } } + } + + if (!changedThisProcessor) + { + rebuiltTokens.Add(tokens[pi]); + } + else + { + var ordered = nap.parameters.Select(p => + { + var v = dict.TryGetValue(p.name, out var nv) ? nv : p.value.ToString(); + return $"{p.name}={v}"; + }); + + var migrated = $"{nap.name}({string.Join(",", ordered)})"; + rebuiltTokens.Add(migrated); + anyProcessorChanged = true; + } + } - if (!anyChanged) + if (anyProcessorChanged) + { + var sb = new System.Text.StringBuilder(raw.Length + 16); + int tokenIndex = 0; + for (int partIndex = 0; partIndex < parts.Length; ++partIndex) + { + if ((partIndex % 2) == 0) { - rebuilt.Add(nap.ToString()); + if (tokenIndex < rebuiltTokens.Count) + sb.Append(rebuiltTokens[tokenIndex++]); } else { - var paramText = string.Join(",", dict.Select(kv => $"{kv.Key}={kv.Value}")); - rebuilt.Add($"{nap.name}({paramText})"); + sb.Append(parts[partIndex]); } } - - actionJson.processors = string.Join(";", rebuilt); - mapJson.actions[ai] = actionJson; + actionJson.processors = sb.ToString(); } - parsedJson.maps[mi] = mapJson; + mapJson.actions[ai] = actionJson; } + parsedJson.maps[mi] = mapJson; } // Bump the version so we never re-migrate parsedJson.version = JsonVersion.Version1; From 00b37a57b897b396819bde1db3036a7be8c41bce Mon Sep 17 00:00:00 2001 From: Aswin Gopal <68984963+AswinRajGopal@users.noreply.github.com> Date: Thu, 2 Oct 2025 17:53:19 +0530 Subject: [PATCH 02/18] Added a test to cover the package migration. --- .../CustomProcessorEnumTest.cs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs b/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs index f2e9cb174a..b4f379d5de 100644 --- a/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs +++ b/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs @@ -108,5 +108,31 @@ public IEnumerator ProcessorEnum_ShouldSerializeByValue_WhenSerializedToAsset() yield return null; } + + [UnityTest] + public IEnumerator Migration_FromLegacyJson_ShouldConvertOrdinal_KeepInvertVector2_AndSeparators() + { + var legacyJson = m_Asset.ToJson().Replace("\"version\": 1", "\"version\": 0").Replace("Custom(SomeEnum=10)", "Custom(SomeEnum=1)"); + + // Add a trailing processor to verify the semicolon separator is preserved. + if (!legacyJson.Contains(";InvertVector2(invertX=true)")) + legacyJson = legacyJson.Replace("Custom(SomeEnum=1)\"", "Custom(SomeEnum=1);InvertVector2(invertX=true)\""); + + var migratedAsset = InputActionAsset.FromJson(legacyJson); + Assume.That(migratedAsset, Is.Not.Null, "Failed to load legacy JSON into an InputActionAsset."); + + var migratedJson = migratedAsset.ToJson(); + Assume.That(migratedJson, Is.Not.Null.And.Not.Empty, "Migrated JSON was empty."); + + Assert.Less(migratedJson.IndexOf("InvertVector2(invertX=true)", StringComparison.Ordinal), migratedJson.IndexOf(",Custom(SomeEnum=20)", StringComparison.Ordinal), + "Expected a comma between the first and second processors, with InvertVector2 first." + ); + + Assert.Greater(migratedJson.IndexOf(";InvertVector2(invertX=true)", StringComparison.Ordinal), migratedJson.IndexOf("Custom(SomeEnum=20)", StringComparison.Ordinal), + "Expected a semicolon between the second and third processors, with the trailing InvertVector2 last." + ); + + yield return null; + } } #endif From d637adf89bdde3c39cdaf01a8ab969bcdb0a9607 Mon Sep 17 00:00:00 2001 From: Aswin Gopal <68984963+AswinRajGopal@users.noreply.github.com> Date: Fri, 3 Oct 2025 21:44:54 +0530 Subject: [PATCH 03/18] Revert "Added a test to cover the package migration." This reverts commit 00b37a57b897b396819bde1db3036a7be8c41bce. --- .../CustomProcessorEnumTest.cs | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs b/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs index b4f379d5de..f2e9cb174a 100644 --- a/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs +++ b/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs @@ -108,31 +108,5 @@ public IEnumerator ProcessorEnum_ShouldSerializeByValue_WhenSerializedToAsset() yield return null; } - - [UnityTest] - public IEnumerator Migration_FromLegacyJson_ShouldConvertOrdinal_KeepInvertVector2_AndSeparators() - { - var legacyJson = m_Asset.ToJson().Replace("\"version\": 1", "\"version\": 0").Replace("Custom(SomeEnum=10)", "Custom(SomeEnum=1)"); - - // Add a trailing processor to verify the semicolon separator is preserved. - if (!legacyJson.Contains(";InvertVector2(invertX=true)")) - legacyJson = legacyJson.Replace("Custom(SomeEnum=1)\"", "Custom(SomeEnum=1);InvertVector2(invertX=true)\""); - - var migratedAsset = InputActionAsset.FromJson(legacyJson); - Assume.That(migratedAsset, Is.Not.Null, "Failed to load legacy JSON into an InputActionAsset."); - - var migratedJson = migratedAsset.ToJson(); - Assume.That(migratedJson, Is.Not.Null.And.Not.Empty, "Migrated JSON was empty."); - - Assert.Less(migratedJson.IndexOf("InvertVector2(invertX=true)", StringComparison.Ordinal), migratedJson.IndexOf(",Custom(SomeEnum=20)", StringComparison.Ordinal), - "Expected a comma between the first and second processors, with InvertVector2 first." - ); - - Assert.Greater(migratedJson.IndexOf(";InvertVector2(invertX=true)", StringComparison.Ordinal), migratedJson.IndexOf("Custom(SomeEnum=20)", StringComparison.Ordinal), - "Expected a semicolon between the second and third processors, with the trailing InvertVector2 last." - ); - - yield return null; - } } #endif From 4e7681370e91f3511d808b23337170049c6ae289 Mon Sep 17 00:00:00 2001 From: Aswin Gopal <68984963+AswinRajGopal@users.noreply.github.com> Date: Fri, 3 Oct 2025 21:45:02 +0530 Subject: [PATCH 04/18] Revert "Update the parser for migration" This reverts commit a260324119a9ab061c6941a248a791b782658fdc. --- .../InputSystem/Actions/InputActionAsset.cs | 129 +++++------------- 1 file changed, 32 insertions(+), 97 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs index df0be653e5..3b9a70fcca 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -using System.Globalization; +using UnityEngine.InputSystem.Editor; using UnityEngine.InputSystem.Utilities; ////TODO: make the FindAction logic available on any IEnumerable and IInputActionCollection via extension methods @@ -1014,125 +1014,60 @@ internal void MigrateJson(ref ReadFileJson parsedJson) { if (parsedJson.version >= JsonVersion.Version1) return; - - if (parsedJson.maps == null || parsedJson.maps.Length == 0) - { - parsedJson.version = JsonVersion.Version1; - return; - } - - for (var mi = 0; mi < parsedJson.maps.Length; ++mi) + if ((parsedJson.maps?.Length ?? 0) > 0 && (parsedJson.version) < JsonVersion.Version1) { - var mapJson = parsedJson.maps[mi]; - if (mapJson.actions == null || mapJson.actions.Length == 0) - continue; - - for (var ai = 0; ai < mapJson.actions.Length; ++ai) + for (var mi = 0; mi < parsedJson.maps.Length; ++mi) { - var actionJson = mapJson.actions[ai]; - var raw = actionJson.processors; - if (string.IsNullOrEmpty(raw)) - continue; - - var parts = System.Text.RegularExpressions.Regex.Split(raw, @"\s*([,;])\s*"); - if (parts.Length == 0) - continue; - - var tokens = new List(); - for (int i = 0; i < parts.Length; i += 2) - if (!string.IsNullOrEmpty(parts[i])) - tokens.Add(parts[i]); - - if (tokens.Count == 0) - continue; - - var parsed = new List(tokens.Count); - foreach (var t in tokens) - parsed.Add(NameAndParameters.Parse(t)); - - var rebuiltTokens = new List(tokens.Count); - var anyProcessorChanged = false; - - for (int pi = 0; pi < parsed.Count; pi++) + var mapJson = parsedJson.maps[mi]; + for (var ai = 0; ai < mapJson.actions.Length; ++ai) { - var nap = parsed[pi]; - - var procType = InputSystem.TryGetProcessor(nap.name); - if (procType == null || nap.parameters.Count == 0) - { - rebuiltTokens.Add(tokens[pi]); + var actionJson = mapJson.actions[ai]; + var raw = actionJson.processors; + if (string.IsNullOrEmpty(raw)) continue; - } - - var dict = new Dictionary(nap.parameters.Count, System.StringComparer.OrdinalIgnoreCase); - foreach (var p in nap.parameters) - dict[p.name] = p.value.ToString(); - - var changedThisProcessor = false; - foreach (var field in procType.GetFields(BindingFlags.Public | BindingFlags.Instance)) + var list = NameAndParameters.ParseMultiple(raw).ToList(); + var rebuilt = new List(list.Count); + foreach (var nap in list) { - if (!field.FieldType.IsEnum) - continue; - - if (!dict.TryGetValue(field.Name, out var rawVal)) + var procType = InputSystem.TryGetProcessor(nap.name); + if (nap.parameters.Count == 0 || procType == null) + { + rebuilt.Add(nap.ToString()); continue; + } - if (int.TryParse(rawVal, NumberStyles.Integer, CultureInfo.InvariantCulture, out var n)) + var dict = nap.parameters.ToDictionary(p => p.name, p => p.value.ToString()); + var anyChanged = false; + foreach (var field in procType.GetFields(BindingFlags.Public | BindingFlags.Instance).Where(f => f.FieldType.IsEnum)) { - var values = System.Enum.GetValues(field.FieldType); - var looksLikeOrdinal = n >= 0 && n < values.Length && !System.Enum.IsDefined(field.FieldType, n); - if (looksLikeOrdinal) + if (dict.TryGetValue(field.Name, out var ordS) && int.TryParse(ordS, out var ord)) { - var underlying = Convert.ToInt32(values.GetValue(n)); - if (underlying != n) + var values = Enum.GetValues(field.FieldType).Cast().ToArray(); + if (ord >= 0 && ord < values.Length) { - dict[field.Name] = underlying.ToString(CultureInfo.InvariantCulture); - changedThisProcessor = true; + dict[field.Name] = Convert.ToInt32(values[ord]).ToString(); + anyChanged = true; } } } - } - - if (!changedThisProcessor) - { - rebuiltTokens.Add(tokens[pi]); - } - else - { - var ordered = nap.parameters.Select(p => - { - var v = dict.TryGetValue(p.name, out var nv) ? nv : p.value.ToString(); - return $"{p.name}={v}"; - }); - - var migrated = $"{nap.name}({string.Join(",", ordered)})"; - rebuiltTokens.Add(migrated); - anyProcessorChanged = true; - } - } - if (anyProcessorChanged) - { - var sb = new System.Text.StringBuilder(raw.Length + 16); - int tokenIndex = 0; - for (int partIndex = 0; partIndex < parts.Length; ++partIndex) - { - if ((partIndex % 2) == 0) + if (!anyChanged) { - if (tokenIndex < rebuiltTokens.Count) - sb.Append(rebuiltTokens[tokenIndex++]); + rebuilt.Add(nap.ToString()); } else { - sb.Append(parts[partIndex]); + var paramText = string.Join(",", dict.Select(kv => $"{kv.Key}={kv.Value}")); + rebuilt.Add($"{nap.name}({paramText})"); } } - actionJson.processors = sb.ToString(); + + actionJson.processors = string.Join(";", rebuilt); + mapJson.actions[ai] = actionJson; } - mapJson.actions[ai] = actionJson; + parsedJson.maps[mi] = mapJson; } - parsedJson.maps[mi] = mapJson; } // Bump the version so we never re-migrate parsedJson.version = JsonVersion.Version1; From a8ba533b18dba7fbc85477f1d5d7e084da92a23d Mon Sep 17 00:00:00 2001 From: Aswin Gopal <68984963+AswinRajGopal@users.noreply.github.com> Date: Fri, 3 Oct 2025 21:46:50 +0530 Subject: [PATCH 05/18] The the processors are split by , instead of ; it was typo --- .../InputSystem/Actions/InputActionAsset.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs index 3b9a70fcca..ab379cacaa 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs @@ -1063,7 +1063,7 @@ internal void MigrateJson(ref ReadFileJson parsedJson) } } - actionJson.processors = string.Join(";", rebuilt); + actionJson.processors = string.Join(",", rebuilt); mapJson.actions[ai] = actionJson; } parsedJson.maps[mi] = mapJson; From ff05b1d3b2df8a706a32058f9e354b8e97cadb3a Mon Sep 17 00:00:00 2001 From: Aswin Gopal <68984963+AswinRajGopal@users.noreply.github.com> Date: Mon, 6 Oct 2025 12:36:21 +0530 Subject: [PATCH 06/18] Robust handling of migration logic --- .../InputSystem/Actions/InputActionAsset.cs | 36 ++++++++----------- .../Views/NameAndParametersListView.cs | 6 +--- .../Utilities/NameAndParameters.cs | 18 ++++++++++ 3 files changed, 34 insertions(+), 26 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs index ab379cacaa..3f72cab7ec 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs @@ -1027,43 +1027,37 @@ internal void MigrateJson(ref ReadFileJson parsedJson) continue; var list = NameAndParameters.ParseMultiple(raw).ToList(); - var rebuilt = new List(list.Count); + var converted = new List(list.Count); foreach (var nap in list) { var procType = InputSystem.TryGetProcessor(nap.name); if (nap.parameters.Count == 0 || procType == null) { - rebuilt.Add(nap.ToString()); + converted.Add(nap); continue; } - var dict = nap.parameters.ToDictionary(p => p.name, p => p.value.ToString()); - var anyChanged = false; - foreach (var field in procType.GetFields(BindingFlags.Public | BindingFlags.Instance).Where(f => f.FieldType.IsEnum)) + var updatedParameters = new List(nap.parameters.Count); + foreach (var param in nap.parameters) { - if (dict.TryGetValue(field.Name, out var ordS) && int.TryParse(ordS, out var ord)) + var updatedPar = param; + var fieldInfo = procType.GetField(param.name, BindingFlags.Public | BindingFlags.Instance); + if(fieldInfo != null && fieldInfo.FieldType.IsEnum) { - var values = Enum.GetValues(field.FieldType).Cast().ToArray(); - if (ord >= 0 && ord < values.Length) + var index = param.value.ToInt32(); + var values = Enum.GetValues(fieldInfo.FieldType); + if(index >= 0 && index < values.Length) { - dict[field.Name] = Convert.ToInt32(values[ord]).ToString(); - anyChanged = true; + var convertedValue = Convert.ToInt32(values.GetValue(index)); + updatedPar = NamedValue.From(param.name, convertedValue); } } + updatedParameters.Add(updatedPar); } - - if (!anyChanged) - { - rebuilt.Add(nap.ToString()); - } - else - { - var paramText = string.Join(",", dict.Select(kv => $"{kv.Key}={kv.Value}")); - rebuilt.Add($"{nap.name}({paramText})"); - } + converted.Add(NameAndParameters.Create(nap.name, updatedParameters)); } - actionJson.processors = string.Join(",", rebuilt); + actionJson.processors = NameAndParameters.SerializeMultiple(converted); mapJson.actions[ai] = actionJson; } parsedJson.maps[mi] = mapJson; diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/NameAndParametersListView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/NameAndParametersListView.cs index fd51f4d26e..a08feca97c 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/NameAndParametersListView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/NameAndParametersListView.cs @@ -83,11 +83,7 @@ private void OnParametersChanged(ParameterListView listView, int index) private static string ToSerializableString(IEnumerable parametersForEachListItem) { - if (parametersForEachListItem == null) - return string.Empty; - - return string.Join(NamedValue.Separator, - parametersForEachListItem.Select(x => x.ToString()).ToArray()); + return NameAndParameters.SerializeMultiple(parametersForEachListItem); } public override void RedrawUI(InputActionsEditorState state) diff --git a/Packages/com.unity.inputsystem/InputSystem/Utilities/NameAndParameters.cs b/Packages/com.unity.inputsystem/InputSystem/Utilities/NameAndParameters.cs index 15ceed089c..239bf59932 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Utilities/NameAndParameters.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Utilities/NameAndParameters.cs @@ -27,6 +27,24 @@ public override string ToString() return $"{name}({parameterString})"; } + internal static string SerializeMultiple(IEnumerable list) + { + if(list == null) + return string.Empty; + + return string.Join(NamedValue.Separator, list.Select(x => x.ToString()).ToArray()); + } + + internal static NameAndParameters Create(string name, IList parameters) + { + var result = new NameAndParameters + { + name = name, + parameters = new ReadOnlyArray(parameters.ToArray()) + }; + return result; + } + public static IEnumerable ParseMultiple(string text) { List list = null; From 829c4aa99368f5965a46894e192f244b073426f1 Mon Sep 17 00:00:00 2001 From: Aswin Gopal <68984963+AswinRajGopal@users.noreply.github.com> Date: Mon, 6 Oct 2025 14:38:04 +0530 Subject: [PATCH 07/18] Added test to cover the migration --- .../CustomProcessorEnumTest.cs | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs b/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs index f2e9cb174a..8693c35d67 100644 --- a/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs +++ b/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs @@ -108,5 +108,58 @@ public IEnumerator ProcessorEnum_ShouldSerializeByValue_WhenSerializedToAsset() yield return null; } + + [Test] + public void Migration_ShouldProduceValidActionAsset_WithEnumProcessorConverted() + { + var legacyJson = @" + { + ""name"": ""InputSystem_Actions"", + ""maps"": [ + { + ""name"": ""Player"", + ""id"": ""df70fa95-8a34-4494-b137-73ab6b9c7d37"", + ""actions"": [ + { + ""name"": ""Move"", + ""type"": ""Value"", + ""id"": ""351f2ccd-1f9f-44bf-9bec-d62ac5c5f408"", + ""expectedControlType"": ""Vector2"", + ""processors"": ""StickDeadzone,InvertVector2(invertX=false),Custom(SomeEnum=1)"", + ""interactions"": """", + ""initialStateCheck"": true + } + ] + } + ], + ""controlSchemes"": [], + ""version"": 0 + }"; + + // Parse and migrate the legacy JSON + var asset = InputActionAsset.FromJson(legacyJson); + + // Object is valid after migration + Assert.That(asset, Is.Not.Null, "Migration failed to produce a valid InputActionAsset."); + + var map = asset.FindActionMap("Player"); + Assert.That(map, Is.Not.Null, "Expected Player map to exist."); + + var action = map.FindAction("Move"); + Assert.That(action, Is.Not.Null, "Expected Move action to exist."); + + var processors = action.processors; + + // Verify processor order and that enum was converted properly + Assert.That(processors, Does.Contain("StickDeadzone"), "StickDeadzone processor missing."); + Assert.That(processors, Does.Contain("InvertVector2(invertX=false)"), "InvertVector2 missing."); + Assert.That(processors, Does.Contain("Custom(SomeEnum=20)"), "Custom(SomeEnum=1) should migrate to SomeEnum=20 (OptionB)."); + + // Verify To JSON + var toJson = asset.ToJson(); + var reloaded = InputActionAsset.FromJson(toJson); + Assert.That(reloaded, Is.Not.Null, "Reloaded asset after migration is null."); + Assert.That(reloaded.FindAction("Player/Move"), Is.Not.Null, "Reloaded asset did not contain expected Move action."); + } } #endif From 9467adf9b99af1813fe2390b3ef5da64f5abcd00 Mon Sep 17 00:00:00 2001 From: Aswin Gopal <68984963+AswinRajGopal@users.noreply.github.com> Date: Tue, 7 Oct 2025 17:12:09 +0530 Subject: [PATCH 08/18] Update test --- .../CustomProcessorEnumTest.cs | 62 ++++--------------- 1 file changed, 11 insertions(+), 51 deletions(-) diff --git a/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs b/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs index 8693c35d67..8a79d6b292 100644 --- a/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs +++ b/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs @@ -47,15 +47,19 @@ public override float Process(float value, InputControl control) internal class CustomProcessorEnumTest : UIToolkitBaseTestWindow { InputActionAsset m_Asset; + InputActionMap m_ActionMap; public override void OneTimeSetUp() { base.OneTimeSetUp(); m_Asset = AssetDatabaseUtils.CreateAsset(); - var actionMap = m_Asset.AddActionMap("Action Map"); + m_ActionMap = m_Asset.AddActionMap("Action Map"); - actionMap.AddAction("Action", InputActionType.Value, processors: "Custom(SomeEnum=10)"); + var action = m_ActionMap.AddAction("Action", InputActionType.Value, processors: "Custom(SomeEnum=10)"); + + // A binding is needed to resolve during test. + action.AddBinding("/leftTrigger"); } public override void OneTimeTearDown() @@ -108,58 +112,14 @@ public IEnumerator ProcessorEnum_ShouldSerializeByValue_WhenSerializedToAsset() yield return null; } - + [Test] public void Migration_ShouldProduceValidActionAsset_WithEnumProcessorConverted() { - var legacyJson = @" - { - ""name"": ""InputSystem_Actions"", - ""maps"": [ - { - ""name"": ""Player"", - ""id"": ""df70fa95-8a34-4494-b137-73ab6b9c7d37"", - ""actions"": [ - { - ""name"": ""Move"", - ""type"": ""Value"", - ""id"": ""351f2ccd-1f9f-44bf-9bec-d62ac5c5f408"", - ""expectedControlType"": ""Vector2"", - ""processors"": ""StickDeadzone,InvertVector2(invertX=false),Custom(SomeEnum=1)"", - ""interactions"": """", - ""initialStateCheck"": true - } - ] - } - ], - ""controlSchemes"": [], - ""version"": 0 - }"; - - // Parse and migrate the legacy JSON - var asset = InputActionAsset.FromJson(legacyJson); - - // Object is valid after migration - Assert.That(asset, Is.Not.Null, "Migration failed to produce a valid InputActionAsset."); - - var map = asset.FindActionMap("Player"); - Assert.That(map, Is.Not.Null, "Expected Player map to exist."); - - var action = map.FindAction("Move"); - Assert.That(action, Is.Not.Null, "Expected Move action to exist."); - - var processors = action.processors; - - // Verify processor order and that enum was converted properly - Assert.That(processors, Does.Contain("StickDeadzone"), "StickDeadzone processor missing."); - Assert.That(processors, Does.Contain("InvertVector2(invertX=false)"), "InvertVector2 missing."); - Assert.That(processors, Does.Contain("Custom(SomeEnum=20)"), "Custom(SomeEnum=1) should migrate to SomeEnum=20 (OptionB)."); - - // Verify To JSON - var toJson = asset.ToJson(); - var reloaded = InputActionAsset.FromJson(toJson); - Assert.That(reloaded, Is.Not.Null, "Reloaded asset after migration is null."); - Assert.That(reloaded.FindAction("Player/Move"), Is.Not.Null, "Reloaded asset did not contain expected Move action."); + m_ActionMap.ResolveBindings(); + + Assert.That(m_ActionMap.m_State.processors[0].GetType(), Is.EqualTo(typeof(CustomProcessor))); + Assert.That((m_ActionMap.m_State.processors[0] as CustomProcessor).SomeEnum, Is.EqualTo(SomeEnum.OptionA)); } } #endif From 3e490f19c26c75e898f3d0fa56034b6014fbc3f6 Mon Sep 17 00:00:00 2001 From: Aswin Gopal <68984963+AswinRajGopal@users.noreply.github.com> Date: Tue, 7 Oct 2025 18:01:25 +0530 Subject: [PATCH 09/18] Address PR feeback. --- .../InputSystem/Actions/InputActionAsset.cs | 42 +++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs index 3f72cab7ec..164d25ff04 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs @@ -1016,6 +1016,11 @@ internal void MigrateJson(ref ReadFileJson parsedJson) return; if ((parsedJson.maps?.Length ?? 0) > 0 && (parsedJson.version) < JsonVersion.Version1) { + List parsedList = null; + var converted = new List(8); + var updatedParameters = new List(4); + var enumValuesCache = new Dictionary(8); + for (var mi = 0; mi < parsedJson.maps.Length; ++mi) { var mapJson = parsedJson.maps[mi]; @@ -1026,37 +1031,48 @@ internal void MigrateJson(ref ReadFileJson parsedJson) if (string.IsNullOrEmpty(raw)) continue; - var list = NameAndParameters.ParseMultiple(raw).ToList(); - var converted = new List(list.Count); - foreach (var nap in list) + if (!NameAndParameters.ParseMultiple(raw, ref parsedList) || parsedList.Count == 0) + continue; + + converted.Clear(); + + for (int i = 0; i < parsedList.Count; ++i) { + var nap = parsedList[i]; var procType = InputSystem.TryGetProcessor(nap.name); if (nap.parameters.Count == 0 || procType == null) { converted.Add(nap); continue; } - - var updatedParameters = new List(nap.parameters.Count); - foreach (var param in nap.parameters) + updatedParameters.Clear(); + for (int k = 0; k < nap.parameters.Count; ++k) { + var param = nap.parameters[k]; var updatedPar = param; + var fieldInfo = procType.GetField(param.name, BindingFlags.Public | BindingFlags.Instance); - if(fieldInfo != null && fieldInfo.FieldType.IsEnum) + if (fieldInfo != null && fieldInfo.FieldType.IsEnum) { var index = param.value.ToInt32(); - var values = Enum.GetValues(fieldInfo.FieldType); - if(index >= 0 && index < values.Length) + if (index >= 0) { - var convertedValue = Convert.ToInt32(values.GetValue(index)); - updatedPar = NamedValue.From(param.name, convertedValue); + if (!enumValuesCache.TryGetValue(fieldInfo.FieldType, out var values)) + { + values = Enum.GetValues(fieldInfo.FieldType); + enumValuesCache[fieldInfo.FieldType] = values; + } + if (index < values.Length) + { + var convertedValue = Convert.ToInt32(values.GetValue(index)); + updatedPar = NamedValue.From(param.name, convertedValue); + } } } updatedParameters.Add(updatedPar); } - converted.Add(NameAndParameters.Create(nap.name, updatedParameters)); + converted.Add(NameAndParameters.Create(nap.name, updatedParameters)); } - actionJson.processors = NameAndParameters.SerializeMultiple(converted); mapJson.actions[ai] = actionJson; } From a31d9715c60828b0e57152e11674773014fb34e9 Mon Sep 17 00:00:00 2001 From: Aswin Gopal <68984963+AswinRajGopal@users.noreply.github.com> Date: Tue, 7 Oct 2025 18:13:41 +0530 Subject: [PATCH 10/18] Address PR comments. --- .../InputSystem/Utilities/NameAndParameters.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Utilities/NameAndParameters.cs b/Packages/com.unity.inputsystem/InputSystem/Utilities/NameAndParameters.cs index 239bf59932..6c37c69bbd 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Utilities/NameAndParameters.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Utilities/NameAndParameters.cs @@ -37,12 +37,11 @@ internal static string SerializeMultiple(IEnumerable list) internal static NameAndParameters Create(string name, IList parameters) { - var result = new NameAndParameters + return new NameAndParameters { name = name, parameters = new ReadOnlyArray(parameters.ToArray()) }; - return result; } public static IEnumerable ParseMultiple(string text) From 817ff0ecb940b25d39dddaab7e1053422ab94e8a Mon Sep 17 00:00:00 2001 From: Aswin Gopal <68984963+AswinRajGopal@users.noreply.github.com> Date: Wed, 8 Oct 2025 00:24:42 +0530 Subject: [PATCH 11/18] Update the test to include more than one processors in actionmap. --- .../CustomProcessorEnumTest.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs b/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs index 8a79d6b292..c1ec4394f3 100644 --- a/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs +++ b/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs @@ -56,7 +56,7 @@ public override void OneTimeSetUp() m_ActionMap = m_Asset.AddActionMap("Action Map"); - var action = m_ActionMap.AddAction("Action", InputActionType.Value, processors: "Custom(SomeEnum=10)"); + var action = m_ActionMap.AddAction("Action", InputActionType.Value, processors: "InvertVector2(invertX=false), Custom(SomeEnum=10)"); // A binding is needed to resolve during test. action.AddBinding("/leftTrigger"); @@ -114,12 +114,22 @@ public IEnumerator ProcessorEnum_ShouldSerializeByValue_WhenSerializedToAsset() } [Test] - public void Migration_ShouldProduceValidActionAsset_WithEnumProcessorConverted() + public void Migration_ShouldProduceValidActionAsset_WithAllProcessors_AndEnumConverted() { m_ActionMap.ResolveBindings(); - Assert.That(m_ActionMap.m_State.processors[0].GetType(), Is.EqualTo(typeof(CustomProcessor))); - Assert.That((m_ActionMap.m_State.processors[0] as CustomProcessor).SomeEnum, Is.EqualTo(SomeEnum.OptionA)); + var action = m_ActionMap.actions.First(); + var migratedList = UnityEngine.InputSystem.Utilities.NameAndParameters.ParseMultiple(action.processors).ToList(); + + Assert.That(migratedList.Count, Is.EqualTo(2), "Expected two processors."); + Assert.That(migratedList[0].name, Is.EqualTo("InvertVector2"), "First processor should be InvertVector2."); + + var invertX = migratedList[0].parameters.FirstOrDefault(p => p.name == "invertX"); + Assert.That(invertX.name, Is.EqualTo("invertX")); + Assert.That(invertX.value.ToBoolean(), Is.False, "invertX should be false for the first processor."); + + Assert.That(m_ActionMap.m_State.processors[1].GetType(), Is.EqualTo(typeof(CustomProcessor))); + Assert.That((m_ActionMap.m_State.processors[1] as CustomProcessor).SomeEnum, Is.EqualTo(SomeEnum.OptionA)); } } #endif From 2914bf7dfad3bca5fa9e25eae30698971916e412 Mon Sep 17 00:00:00 2001 From: Aswin Gopal <68984963+AswinRajGopal@users.noreply.github.com> Date: Wed, 8 Oct 2025 00:33:31 +0530 Subject: [PATCH 12/18] Removed redundant call --- .../InputSystem/Actions/InputActionAsset.cs | 2 +- .../UITKAssetEditor/Views/NameAndParametersListView.cs | 7 +++++-- .../InputSystem/Utilities/NameAndParameters.cs | 8 -------- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs index 164d25ff04..7ded8b1628 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs @@ -1073,7 +1073,7 @@ internal void MigrateJson(ref ReadFileJson parsedJson) } converted.Add(NameAndParameters.Create(nap.name, updatedParameters)); } - actionJson.processors = NameAndParameters.SerializeMultiple(converted); + actionJson.processors = NameAndParametersListView.ToSerializableString(converted); mapJson.actions[ai] = actionJson; } parsedJson.maps[mi] = mapJson; diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/NameAndParametersListView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/NameAndParametersListView.cs index a08feca97c..c6be73eeb0 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/NameAndParametersListView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/NameAndParametersListView.cs @@ -81,9 +81,12 @@ private void OnParametersChanged(ParameterListView listView, int index) m_ListProperty.serializedObject.ApplyModifiedProperties(); } - private static string ToSerializableString(IEnumerable parametersForEachListItem) + internal static string ToSerializableString(IEnumerable parametersForEachListItem) { - return NameAndParameters.SerializeMultiple(parametersForEachListItem); + if(parametersForEachListItem == null) + return string.Empty; + + return string.Join(NamedValue.Separator, parametersForEachListItem.Select(x => x.ToString()).ToArray()); } public override void RedrawUI(InputActionsEditorState state) diff --git a/Packages/com.unity.inputsystem/InputSystem/Utilities/NameAndParameters.cs b/Packages/com.unity.inputsystem/InputSystem/Utilities/NameAndParameters.cs index 6c37c69bbd..64b0ad044d 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Utilities/NameAndParameters.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Utilities/NameAndParameters.cs @@ -27,14 +27,6 @@ public override string ToString() return $"{name}({parameterString})"; } - internal static string SerializeMultiple(IEnumerable list) - { - if(list == null) - return string.Empty; - - return string.Join(NamedValue.Separator, list.Select(x => x.ToString()).ToArray()); - } - internal static NameAndParameters Create(string name, IList parameters) { return new NameAndParameters From 6daabc90b537a4433cf516b022dce92c5107b339 Mon Sep 17 00:00:00 2001 From: Aswin Gopal <68984963+AswinRajGopal@users.noreply.github.com> Date: Wed, 8 Oct 2025 14:29:41 +0530 Subject: [PATCH 13/18] Revert changes and moved the function changes to NameAndParameters as ListView is editor only. --- .../InputSystem/Actions/InputActionAsset.cs | 2 +- .../Views/NameAndParametersListView.cs | 16 ++++------------ .../InputSystem/Utilities/NameAndParameters.cs | 8 ++++++++ 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs index 7ded8b1628..e59e61e78c 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs @@ -1073,7 +1073,7 @@ internal void MigrateJson(ref ReadFileJson parsedJson) } converted.Add(NameAndParameters.Create(nap.name, updatedParameters)); } - actionJson.processors = NameAndParametersListView.ToSerializableString(converted); + actionJson.processors = NameAndParameters.ToSerializableString(converted); mapJson.actions[ai] = actionJson; } parsedJson.maps[mi] = mapJson; diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/NameAndParametersListView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/NameAndParametersListView.cs index c6be73eeb0..6c253c46b4 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/NameAndParametersListView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/NameAndParametersListView.cs @@ -37,7 +37,7 @@ public void OnAddElement(string name) var newElement = new NameAndParameters() { name = name}; interactionsOrProcessorsList.Add(newElement); - m_ListProperty.stringValue = ToSerializableString(interactionsOrProcessorsList); + m_ListProperty.stringValue = NameAndParameters.ToSerializableString(interactionsOrProcessorsList); m_ListProperty.serializedObject.ApplyModifiedProperties(); } @@ -61,7 +61,7 @@ private void SwapElement(int oldIndex, int newIndex) if (interactionsOrProcessors.Length == 0 || !newIndexIsValid || !oldIndexIsValid) return; MemoryHelpers.Swap(ref interactionsOrProcessors[oldIndex], ref interactionsOrProcessors[newIndex]); - m_ListProperty.stringValue = ToSerializableString(interactionsOrProcessors); + m_ListProperty.stringValue = NameAndParameters.ToSerializableString(interactionsOrProcessors); m_ListProperty.serializedObject.ApplyModifiedProperties(); } @@ -69,7 +69,7 @@ private void DeleteElement(int index) { var interactionsOrProcessorsList = NameAndParameters.ParseMultiple(m_ListProperty.stringValue).ToList(); interactionsOrProcessorsList.RemoveAt(index); - m_ListProperty.stringValue = ToSerializableString(interactionsOrProcessorsList); + m_ListProperty.stringValue = NameAndParameters.ToSerializableString(interactionsOrProcessorsList); m_ListProperty.serializedObject.ApplyModifiedProperties(); } @@ -77,18 +77,10 @@ private void OnParametersChanged(ParameterListView listView, int index) { var interactionsOrProcessorsList = NameAndParameters.ParseMultiple(m_ListProperty.stringValue).ToList(); interactionsOrProcessorsList[index] = new NameAndParameters { name = interactionsOrProcessorsList[index].name, parameters = listView.GetParameters() }; - m_ListProperty.stringValue = ToSerializableString(interactionsOrProcessorsList); + m_ListProperty.stringValue = NameAndParameters.ToSerializableString(interactionsOrProcessorsList); m_ListProperty.serializedObject.ApplyModifiedProperties(); } - internal static string ToSerializableString(IEnumerable parametersForEachListItem) - { - if(parametersForEachListItem == null) - return string.Empty; - - return string.Join(NamedValue.Separator, parametersForEachListItem.Select(x => x.ToString()).ToArray()); - } - public override void RedrawUI(InputActionsEditorState state) { if (m_ContentContainer != null) diff --git a/Packages/com.unity.inputsystem/InputSystem/Utilities/NameAndParameters.cs b/Packages/com.unity.inputsystem/InputSystem/Utilities/NameAndParameters.cs index 64b0ad044d..eac7cbd904 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Utilities/NameAndParameters.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Utilities/NameAndParameters.cs @@ -27,6 +27,14 @@ public override string ToString() return $"{name}({parameterString})"; } + internal static string ToSerializableString(IEnumerable list) + { + if(list == null) + return string.Empty; + + return string.Join(NamedValue.Separator, list.Select(x => x.ToString()).ToArray()); + } + internal static NameAndParameters Create(string name, IList parameters) { return new NameAndParameters From 6aca9dbb5a23be2ae032a5b14c5387f4a1ab80a5 Mon Sep 17 00:00:00 2001 From: Aswin Gopal <68984963+AswinRajGopal@users.noreply.github.com> Date: Fri, 10 Oct 2025 13:18:40 +0530 Subject: [PATCH 14/18] Bring back the test which verifies the migration along with processed processors and robust way to assert them. --- .../CustomProcessorEnumTest.cs | 67 +++++++++++++------ 1 file changed, 48 insertions(+), 19 deletions(-) diff --git a/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs b/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs index c1ec4394f3..53500a0779 100644 --- a/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs +++ b/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs @@ -47,19 +47,15 @@ public override float Process(float value, InputControl control) internal class CustomProcessorEnumTest : UIToolkitBaseTestWindow { InputActionAsset m_Asset; - InputActionMap m_ActionMap; public override void OneTimeSetUp() { base.OneTimeSetUp(); m_Asset = AssetDatabaseUtils.CreateAsset(); - m_ActionMap = m_Asset.AddActionMap("Action Map"); + var actionMap = m_Asset.AddActionMap("Action Map"); - var action = m_ActionMap.AddAction("Action", InputActionType.Value, processors: "InvertVector2(invertX=false), Custom(SomeEnum=10)"); - - // A binding is needed to resolve during test. - action.AddBinding("/leftTrigger"); + actionMap.AddAction("Action", InputActionType.Value, processors: "Custom(SomeEnum=10)"); } public override void OneTimeTearDown() @@ -112,24 +108,57 @@ public IEnumerator ProcessorEnum_ShouldSerializeByValue_WhenSerializedToAsset() yield return null; } - + [Test] - public void Migration_ShouldProduceValidActionAsset_WithAllProcessors_AndEnumConverted() + public void Migration_ShouldProduceValidActionAsset_WithEnumProcessorConverted() { - m_ActionMap.ResolveBindings(); - - var action = m_ActionMap.actions.First(); - var migratedList = UnityEngine.InputSystem.Utilities.NameAndParameters.ParseMultiple(action.processors).ToList(); + var legacyJson = @" + { + ""name"": ""InputSystem_Actions"", + ""maps"": [ + { + ""name"": ""Player"", + ""id"": ""df70fa95-8a34-4494-b137-73ab6b9c7d37"", + ""actions"": [ + { + ""name"": ""Move"", + ""type"": ""Value"", + ""id"": ""df70fa95-8a34-4494-b137-73ab6b9c7d37"", + ""expectedControlType"": ""Vector2"", + ""processors"": ""StickDeadzone,InvertVector2(invertX=false),Custom(SomeEnum=1)"", + ""interactions"": """", + ""initialStateCheck"": true + } + ] + } + ], + ""controlSchemes"": [], + ""version"": 0 + }"; + + var asset = InputActionAsset.FromJson(legacyJson); + Assert.That(asset, Is.Not.Null, "Migration failed to produce a valid InputActionAsset."); + + var map = asset.FindActionMap("Player"); + Assert.That(map, Is.Not.Null, "Expected Player map to exist."); + + var action = map.FindAction("Move"); + action.AddBinding("/leftTrigger"); + Assert.That(action, Is.Not.Null, "Expected Move action to exist."); + + var parsed = UnityEngine.InputSystem.Utilities.NameAndParameters.ParseMultiple(action.processors).ToList(); + Assert.That(parsed.Count, Is.EqualTo(3), "Expected three processors."); + Assert.That(parsed[0].name, Is.EqualTo("StickDeadzone")); + Assert.That(parsed[1].name, Is.EqualTo("InvertVector2")); - Assert.That(migratedList.Count, Is.EqualTo(2), "Expected two processors."); - Assert.That(migratedList[0].name, Is.EqualTo("InvertVector2"), "First processor should be InvertVector2."); + map.ResolveBindings(); - var invertX = migratedList[0].parameters.FirstOrDefault(p => p.name == "invertX"); + var invertX = parsed[1].parameters.FirstOrDefault(p => p.name == "invertX"); Assert.That(invertX.name, Is.EqualTo("invertX")); - Assert.That(invertX.value.ToBoolean(), Is.False, "invertX should be false for the first processor."); - - Assert.That(m_ActionMap.m_State.processors[1].GetType(), Is.EqualTo(typeof(CustomProcessor))); - Assert.That((m_ActionMap.m_State.processors[1] as CustomProcessor).SomeEnum, Is.EqualTo(SomeEnum.OptionA)); + Assert.That(invertX.value.ToBoolean(), Is.False); + + Assert.That(map.m_State.processors[2].GetType(), Is.EqualTo(typeof(CustomProcessor))); + Assert.That((map.m_State.processors[2] as CustomProcessor).SomeEnum, Is.EqualTo(SomeEnum.OptionB)); } } #endif From f3f7ea5b8a90a15d0e02a9d9842f2a9fd4e75ea3 Mon Sep 17 00:00:00 2001 From: Aswin Gopal <68984963+AswinRajGopal@users.noreply.github.com> Date: Thu, 16 Oct 2025 13:10:54 +0530 Subject: [PATCH 15/18] Update the test as per review comments. --- .../CustomProcessorEnumTest.cs | 101 ++++++++++-------- 1 file changed, 57 insertions(+), 44 deletions(-) diff --git a/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs b/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs index 53500a0779..c0ddb52af1 100644 --- a/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs +++ b/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs @@ -8,6 +8,7 @@ using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Editor; +using UnityEngine.InputSystem.Processors; using UnityEngine.TestTools; using UnityEngine.UIElements; @@ -110,55 +111,67 @@ public IEnumerator ProcessorEnum_ShouldSerializeByValue_WhenSerializedToAsset() } [Test] + [Description("Regression test for case ISXB-1674")] public void Migration_ShouldProduceValidActionAsset_WithEnumProcessorConverted() { - var legacyJson = @" - { - ""name"": ""InputSystem_Actions"", - ""maps"": [ - { - ""name"": ""Player"", - ""id"": ""df70fa95-8a34-4494-b137-73ab6b9c7d37"", - ""actions"": [ - { - ""name"": ""Move"", - ""type"": ""Value"", - ""id"": ""df70fa95-8a34-4494-b137-73ab6b9c7d37"", - ""expectedControlType"": ""Vector2"", - ""processors"": ""StickDeadzone,InvertVector2(invertX=false),Custom(SomeEnum=1)"", - ""interactions"": """", - ""initialStateCheck"": true - } - ] - } - ], - ""controlSchemes"": [], - ""version"": 0 - }"; - - var asset = InputActionAsset.FromJson(legacyJson); - Assert.That(asset, Is.Not.Null, "Migration failed to produce a valid InputActionAsset."); - - var map = asset.FindActionMap("Player"); - Assert.That(map, Is.Not.Null, "Expected Player map to exist."); - - var action = map.FindAction("Move"); - action.AddBinding("/leftTrigger"); - Assert.That(action, Is.Not.Null, "Expected Move action to exist."); - - var parsed = UnityEngine.InputSystem.Utilities.NameAndParameters.ParseMultiple(action.processors).ToList(); - Assert.That(parsed.Count, Is.EqualTo(3), "Expected three processors."); - Assert.That(parsed[0].name, Is.EqualTo("StickDeadzone")); - Assert.That(parsed[1].name, Is.EqualTo("InvertVector2")); + const string k_Json = @" + { + ""name"": ""InputSystem_Actions"", + ""maps"": [ + { + ""name"": ""Player"", + ""id"": ""df70fa95-8a34-4494-b137-73ab6b9c7d37"", + ""actions"": [ + { + ""name"": ""Move"", + ""type"": ""Value"", + ""id"": ""938a78a0-f8c6-4b2e-b8a7-d3c26d83a4e9"", + ""expectedControlType"": ""Vector2"", + ""processors"": ""StickDeadzone,InvertVector2(invertX=false),Custom(SomeEnum=1)"", + ""interactions"": """", + ""initialStateCheck"": true + } + ], + ""bindings"": [ + { + ""name"": """", + ""id"": ""c9a175a0-a5ed-4e2c-b3a9-1d4d3d3a7a9a"", + ""path"": ""/leftStick"", + ""interactions"": """", + ""processors"": """", + ""groups"": """", + ""action"": ""Move"", + ""isComposite"": false, + ""isPartOfComposite"": false + } + ] + } + ], + ""controlSchemes"": [] + }"; + + var asset = InputActionAsset.FromJson(k_Json); - map.ResolveBindings(); + // Enable the action to call rebinding + asset.FindAction("Move").Enable(); + + var map = asset.FindActionMap("Player"); - var invertX = parsed[1].parameters.FirstOrDefault(p => p.name == "invertX"); - Assert.That(invertX.name, Is.EqualTo("invertX")); - Assert.That(invertX.value.ToBoolean(), Is.False); + // Directly tests the outcome of the migration and parsing logic, which is the core goal. + var instantiatedProcessors = map.m_State.processors; + Assert.That(map.m_State.totalProcessorCount, Is.EqualTo(3), "Should create exactly three processors."); + + // Verify the order and type of each processor. + Assert.That(instantiatedProcessors[0], Is.TypeOf(), "First processor should be StickDeadzone."); + Assert.That(instantiatedProcessors[1], Is.TypeOf(), "Second processor should be InvertVector2."); + Assert.That(instantiatedProcessors[2], Is.TypeOf(), "Third processor should be the custom type."); + + // Verify the specific data for processors with parameters. + var invertProcessor = (InvertVector2Processor)instantiatedProcessors[1]; + Assert.That(invertProcessor.invertX, Is.False, "invertX parameter should be false."); - Assert.That(map.m_State.processors[2].GetType(), Is.EqualTo(typeof(CustomProcessor))); - Assert.That((map.m_State.processors[2] as CustomProcessor).SomeEnum, Is.EqualTo(SomeEnum.OptionB)); + var customProcessor = (CustomProcessor)instantiatedProcessors[2]; + Assert.That(customProcessor.SomeEnum, Is.EqualTo(SomeEnum.OptionB), "Enum parameter should be parsed correctly to OptionB."); } } #endif From f5b06da67183ecd85f602d4533c46cae72cbbe52 Mon Sep 17 00:00:00 2001 From: Aswin Gopal <68984963+AswinRajGopal@users.noreply.github.com> Date: Thu, 16 Oct 2025 14:19:44 +0530 Subject: [PATCH 16/18] Simply the code removing redundancy - as these checks are handled in parse multiple. --- .../InputSystem/Actions/InputActionAsset.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs index e59e61e78c..68574a400f 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs @@ -1028,10 +1028,8 @@ internal void MigrateJson(ref ReadFileJson parsedJson) { var actionJson = mapJson.actions[ai]; var raw = actionJson.processors; - if (string.IsNullOrEmpty(raw)) - continue; - if (!NameAndParameters.ParseMultiple(raw, ref parsedList) || parsedList.Count == 0) + if (!NameAndParameters.ParseMultiple(raw, ref parsedList)) continue; converted.Clear(); From 6b4c75a588fbf96af86f79d9564a30426c8107e0 Mon Sep 17 00:00:00 2001 From: Aswin Gopal <68984963+AswinRajGopal@users.noreply.github.com> Date: Thu, 16 Oct 2025 15:06:15 +0530 Subject: [PATCH 17/18] Add check incase if raw string is null. --- .../InputSystem/Actions/InputActionAsset.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs index 68574a400f..37441a8389 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs @@ -1028,6 +1028,8 @@ internal void MigrateJson(ref ReadFileJson parsedJson) { var actionJson = mapJson.actions[ai]; var raw = actionJson.processors; + if (string.IsNullOrEmpty(raw)) + continue; if (!NameAndParameters.ParseMultiple(raw, ref parsedList)) continue; From 64076cb8be7a1d56807df1960e8597d19bab3cd7 Mon Sep 17 00:00:00 2001 From: Ashwin Gopal <68984963+AswinRajGopal@users.noreply.github.com> Date: Thu, 16 Oct 2025 18:13:43 +0530 Subject: [PATCH 18/18] Reformatting files. --- .../Tests/InputSystem.Editor/CustomProcessorEnumTest.cs | 8 ++++---- .../InputSystem/Actions/InputActionAsset.cs | 6 +++--- .../InputSystem/Utilities/NameAndParameters.cs | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs b/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs index c0ddb52af1..08865b9417 100644 --- a/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs +++ b/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs @@ -109,7 +109,7 @@ public IEnumerator ProcessorEnum_ShouldSerializeByValue_WhenSerializedToAsset() yield return null; } - + [Test] [Description("Regression test for case ISXB-1674")] public void Migration_ShouldProduceValidActionAsset_WithEnumProcessorConverted() @@ -151,12 +151,12 @@ public void Migration_ShouldProduceValidActionAsset_WithEnumProcessorConverted() }"; var asset = InputActionAsset.FromJson(k_Json); - - // Enable the action to call rebinding + + // Enable the action to call rebinding asset.FindAction("Move").Enable(); var map = asset.FindActionMap("Player"); - + // Directly tests the outcome of the migration and parsing logic, which is the core goal. var instantiatedProcessors = map.m_State.processors; Assert.That(map.m_State.totalProcessorCount, Is.EqualTo(3), "Should create exactly three processors."); diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs index 37441a8389..e655a6a712 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs @@ -1016,9 +1016,9 @@ internal void MigrateJson(ref ReadFileJson parsedJson) return; if ((parsedJson.maps?.Length ?? 0) > 0 && (parsedJson.version) < JsonVersion.Version1) { - List parsedList = null; - var converted = new List(8); - var updatedParameters = new List(4); + List parsedList = null; + var converted = new List(8); + var updatedParameters = new List(4); var enumValuesCache = new Dictionary(8); for (var mi = 0; mi < parsedJson.maps.Length; ++mi) diff --git a/Packages/com.unity.inputsystem/InputSystem/Utilities/NameAndParameters.cs b/Packages/com.unity.inputsystem/InputSystem/Utilities/NameAndParameters.cs index eac7cbd904..b4491e8eec 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Utilities/NameAndParameters.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Utilities/NameAndParameters.cs @@ -29,7 +29,7 @@ public override string ToString() internal static string ToSerializableString(IEnumerable list) { - if(list == null) + if (list == null) return string.Empty; return string.Join(NamedValue.Separator, list.Select(x => x.ToString()).ToArray());