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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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()); From 836d6e8c95b0b585a1aaab75ad3ef8870bfe15ad Mon Sep 17 00:00:00 2001 From: Aswin Gopal <68984963+AswinRajGopal@users.noreply.github.com> Date: Fri, 24 Oct 2025 17:51:56 +0530 Subject: [PATCH 19/19] Update Changelog --- Packages/com.unity.inputsystem/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Packages/com.unity.inputsystem/CHANGELOG.md b/Packages/com.unity.inputsystem/CHANGELOG.md index 40687b93e0..94055e4757 100644 --- a/Packages/com.unity.inputsystem/CHANGELOG.md +++ b/Packages/com.unity.inputsystem/CHANGELOG.md @@ -33,6 +33,7 @@ however, it has to be formatted properly to pass verification tests. - Fixed HID parsing not handling logical minimum and maximum values correctly when they are negative. This applies for platforms that parse HID descriptors in the package, e.g. macOS at the moment. - Fix usage of correct data format for stick axes in HID Layout Builder ([User contribution](https://github.com/Unity-Technologies/InputSystem/pull/2245)) - Fixed InputSystemUIInputModule calling pointer events on parent objects even when the "Send Pointer Hover To Parent" is off on 2022.3.XX. This was was previously only available on Unity 6 versions since 1.11.0. [ISXB-1296] +- Fixed upgrading input actions containing multiple processors [ISXB-1674] ## [1.15.0] - 2025-10-03