Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,5 +108,58 @@ public IEnumerator ProcessorEnum_ShouldSerializeByValue_WhenSerializedToAsset()

yield return null;
}

[Test]
public void Migration_ShouldProduceValidActionAsset_WithEnumProcessorConverted()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you document what the intent of this test case is inline in code?

My interpretation is that you want to verify that processors defined in a certain way in .inputaction json are parsed/preserved correctly when imported (with migration)? If this is the case I suggest you convert this into a functional test instead of doing a lot of string comparisons. Here is a rough example (coded directly into web interface so it may contain errors but hope you get the idea):

const string legacyJson = "<as before>"; // Consider including binding in JSON to avoid writing code for it
var asset = InputActionAsset.FromJson(legacyJson);
var gamepad = InputSystem.AddDevice<Gamepad>();
var action = asset.FindAction("Player/Move");
action.Enable();

// Make sure deadzone processor is applied
Set(gamepad.leftStick, new Vector2(InputSystem.settings.defaultDeadzoneMin * 0.9f, 0.0f));
Assert.That(action.ReadValue<Vector2>(), Is.Equal(Vecto2.zero)); // Filtered by deadzone

// Make sure invert processor is applied
Set(gamepad.leftStick, new Vector2(1.0f, 0.0f));
Assert.That(action.ReadValue<Vector2>(), Is.Equal(-Vector2.right)); // Inverted

// Make sure custom processor is applied
Set(gamepad.leftStick, new Vector2(0.5f, 0.0f));
Assert.That(action.ReadValue<Vector2>(), Is.Equal(something)); // Not sure how custom should change value, so change this to what is appropriate

Ideally order dependency can be proven through the same test case.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure I'll change it into a functional test.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you see any pros/cons with changing approach? Would such a functional test miss some aspect of this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My view about authoring a regression test is to cover only the migration case where we actually verify that a JSON with a custom processor and some neighboring processors like stickdeadzone or invertVector are parsed properly after migration.

The functional tests already exists which covers most of the functionals in the input system generic tests. However it will be robust to club both of these for this case.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, that is also fine so keep this if you want. Not sure @LeoUnity has additional perspective?
Not sure we already test this specific processor order somewhere else?

{
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
Original file line number Diff line number Diff line change
Expand Up @@ -1027,43 +1027,37 @@ internal void MigrateJson(ref ReadFileJson parsedJson)
continue;

var list = NameAndParameters.ParseMultiple(raw).ToList();
var rebuilt = new List<string>(list.Count);
var converted = new List<NameAndParameters>(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<NamedValue>(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<object>().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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,7 @@ private void OnParametersChanged(ParameterListView listView, int index)

private static string ToSerializableString(IEnumerable<NameAndParameters> 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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,24 @@
return $"{name}({parameterString})";
}

internal static string SerializeMultiple(IEnumerable<NameAndParameters> list)
{
if(list == null)
return string.Empty;

Check warning on line 33 in Packages/com.unity.inputsystem/InputSystem/Utilities/NameAndParameters.cs

View check run for this annotation

Codecov GitHub.com / codecov/patch

Packages/com.unity.inputsystem/InputSystem/Utilities/NameAndParameters.cs#L33

Added line #L33 was not covered by tests

return string.Join(NamedValue.Separator, list.Select(x => x.ToString()).ToArray());
}

internal static NameAndParameters Create(string name, IList<NamedValue> parameters)
{
var result = new NameAndParameters
{
name = name,
parameters = new ReadOnlyArray<NamedValue>(parameters.ToArray())
};
return result;
}

public static IEnumerable<NameAndParameters> ParseMultiple(string text)
{
List<NameAndParameters> list = null;
Expand Down