Skip to content

Commit

Permalink
NEW: Add tests for Project wide actions (#1742)
Browse files Browse the repository at this point in the history
* project-wide actions: add missing control schemes to some bindings

* ensure we only load the InputActionsAsset containing the projectwide actions at runtime

* add some tests for project wide settings

* Temporarily disable some tests in Player which have issues with ProjectWide actions

* run code formatter

* review comments
  • Loading branch information
jamesmcgill authored Aug 30, 2023
1 parent a7763b3 commit 665d11d
Show file tree
Hide file tree
Showing 9 changed files with 261 additions and 10 deletions.
2 changes: 1 addition & 1 deletion Assets/Tests/InputSystem/CoreTests_Actions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5020,7 +5020,7 @@ public void Actions_WhenControlsUpdate_TimeoutsAreCarriedOver()
[Category("Actions")]
public void Actions_WhenControlsUpdate_InProgressActionsKeepGoing()
{
#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS && UNITY_EDITOR
#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
// @TODO: This should not need disabled for this test (https://jira.unity3d.com/browse/ISX-1455)
// Causes: "[Assert] Could not find active control after binding resolution"
// when RemoveDevice() called
Expand Down
4 changes: 2 additions & 2 deletions Assets/Tests/InputSystem/CoreTests_Devices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4390,7 +4390,7 @@ public void TODO_Devices_CanHijackEventStreamOfDevice()
[TestCase(false, InputSettings.BackgroundBehavior.ResetAndDisableNonBackgroundDevices, InputSettings.EditorInputBehaviorInPlayMode.AllDeviceInputAlwaysGoesToGameView)]
public unsafe void Devices_CanHandleFocusChanges(bool appRunInBackground, InputSettings.BackgroundBehavior backgroundBehavior, InputSettings.EditorInputBehaviorInPlayMode editorInputBehaviorInPlayMode)
{
#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS && UNITY_EDITOR
#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
// @TODO: This should not need disabled for this test (https://jira.unity3d.com/browse/ISX-1455)
// Causes: "[Assert] Could not find active control after binding resolution"
// due to: mouse3 = InputSystem.AddDevice<Mouse>();
Expand Down Expand Up @@ -5363,7 +5363,7 @@ public void Devices_RemovingKeyboardMakesNextKeyboardCurrent()
[Category("Devices")]
public void Devices_RemovingDevice_MakesNextDeviceOfTypeCurrent()
{
#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS && UNITY_EDITOR
#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
// @TODO: This should not need disabled for this test (https://jira.unity3d.com/browse/ISX-1455)
// Causes: "[Assert] Could not find active control after binding resolution"
// during point where Pointer is removed
Expand Down
215 changes: 215 additions & 0 deletions Assets/Tests/InputSystem/CoreTests_ProjectWideActions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS

using System;
using System.IO;
using NUnit.Framework;
using UnityEditor;
using UnityEngine;
using UnityEngine.InputSystem;

#if UNITY_EDITOR
using UnityEngine.InputSystem.Editor;
#endif

internal partial class CoreTests
{
const string TestCategory = "ProjectWideActions";
const string TestAssetPath = "Assets/TestInputManager.asset";
string m_TemplateAssetPath;

#if UNITY_EDITOR
const int initialActionCount = 2;
const int initialMapCount = 1;
#else
const int initialActionCount = 17;
const int initialMapCount = 2;
#endif

[SetUp]
public override void Setup()
{
// @TODO: Currently we can only inject the TestActionsAsset in PlayMode tests.
// It would be nice to be able to inject it as a Preloaded asset into the Player tests so
// we don't need different tests for the player.
// This also means these tests are dependant on the content of InputManager.asset not being changed.
#if UNITY_EDITOR
// This asset takes the place of ProjectSettings/InputManager.asset for the sake of testing, as we don't
// really want to go changing that asset in every test.
// This is used as a backing for `InputSystem.actions` in PlayMode tests.
var testAsset = ScriptableObject.CreateInstance<TestActionsAsset>();
AssetDatabase.CreateAsset(testAsset, TestAssetPath);

// Create a template `InputActionAsset` containing some test actions.
// This will then be used to populate the initially empty `TestActionsAsset` when it is first acessed.
var templateActions = ScriptableObject.CreateInstance<InputActionAsset>();
templateActions.name = "TestAsset";
var map = templateActions.AddActionMap("InitialActionMapOne");
map.AddAction("InitialActionOne");
map.AddAction("InitialActionTwo");

m_TemplateAssetPath = Path.Combine(Environment.CurrentDirectory, "Assets/ProjectWideActionsTemplate.inputactions");
File.WriteAllText(m_TemplateAssetPath, templateActions.ToJson());

ProjectWideActionsAsset.SetAssetPaths(m_TemplateAssetPath, TestAssetPath);
#endif

base.Setup();
}

[TearDown]
public override void TearDown()
{
#if UNITY_EDITOR
ProjectWideActionsAsset.Reset();

if (File.Exists(m_TemplateAssetPath))
File.Delete(m_TemplateAssetPath);

AssetDatabase.DeleteAsset(TestAssetPath);
#endif

base.TearDown();
}

#if UNITY_EDITOR
[Test]
[Category(TestCategory)]
public void ProjectWideActionsAsset_TemplateAssetIsInstalledOnFirstUse()
{
var asset = ProjectWideActionsAsset.GetOrCreate();

Assert.That(asset, Is.Not.Null);
Assert.That(asset.actionMaps.Count, Is.EqualTo(initialMapCount));
Assert.That(asset.actionMaps[0].actions.Count, Is.EqualTo(initialActionCount));
Assert.That(asset.actionMaps[0].actions[0].name, Is.EqualTo("InitialActionOne"));
}

[Test]
[Category(TestCategory)]
public void ProjectWideActionsAsset_CanModifySaveAndLoadAsset()
{
var asset = ProjectWideActionsAsset.GetOrCreate();

Assert.That(asset, Is.Not.Null);
Assert.That(asset.actionMaps.Count, Is.EqualTo(initialMapCount));
Assert.That(asset.actionMaps[0].actions.Count, Is.EqualTo(initialActionCount));
Assert.That(asset.actionMaps[0].actions[0].name, Is.EqualTo("InitialActionOne"));

asset.Disable(); // Cannot modify active actions

// Add more actions
asset.actionMaps[0].AddAction("ActionTwo");
asset.actionMaps[0].AddAction("ActionThree");

// Modify existing
asset.actionMaps[0].actions[0].Rename("FirstAction");

// Add another map
asset.AddActionMap("ActionMapTwo").AddAction("AnotherAction");

// Save
AssetDatabase.SaveAssets();

// Reload
asset = ProjectWideActionsAsset.GetOrCreate();

Assert.That(asset, Is.Not.Null);
Assert.That(asset.actionMaps.Count, Is.EqualTo(initialMapCount + 1));
Assert.That(asset.actionMaps[0].actions.Count, Is.EqualTo(initialActionCount + 2));
Assert.That(asset.actionMaps[1].actions.Count, Is.EqualTo(1));
Assert.That(asset.actionMaps[0].actions[0].name, Is.EqualTo("FirstAction"));
Assert.That(asset.actionMaps[1].actions[0].name, Is.EqualTo("AnotherAction"));
}

#endif

[Test]
[Category(TestCategory)]
public void ProjectWideActions_AreEnabledByDefault()
{
Assert.That(InputSystem.actions, Is.Not.Null);
Assert.That(InputSystem.actions.enabled, Is.True);
}

[Test]
[Category(TestCategory)]
public void ProjectWideActions_ContainsTemplateActions()
{
Assert.That(InputSystem.actions, Is.Not.Null);
Assert.That(InputSystem.actions.actionMaps.Count, Is.EqualTo(initialMapCount));

#if UNITY_EDITOR
Assert.That(InputSystem.actions.actionMaps[0].actions.Count, Is.EqualTo(initialActionCount));
Assert.That(InputSystem.actions.actionMaps[0].actions[0].name, Is.EqualTo("InitialActionOne"));
#else
Assert.That(InputSystem.actions.actionMaps[0].actions.Count, Is.EqualTo(9));
Assert.That(InputSystem.actions.actionMaps[0].actions[0].name, Is.EqualTo("Move"));
#endif
}

[Test]
[Category(TestCategory)]
public void ProjectWideActions_AppearInEnabledActions()
{
var enabledActions = InputSystem.ListEnabledActions();
Assert.That(enabledActions, Has.Count.EqualTo(initialActionCount));

// Add more actions also work
var action = new InputAction(name: "standaloneAction");
action.Enable();

enabledActions = InputSystem.ListEnabledActions();
Assert.That(enabledActions, Has.Count.EqualTo(initialActionCount + 1));
Assert.That(enabledActions, Has.Exactly(1).SameAs(action));

// Disabling works
InputSystem.actions.Disable();
enabledActions = InputSystem.ListEnabledActions();
Assert.That(enabledActions, Has.Count.EqualTo(1));
Assert.That(enabledActions, Has.Exactly(1).SameAs(action));
}

[Test]
[Category(TestCategory)]
public void ProjectWideActions_CanReplaceExistingActions()
{
// Initial State
Assert.That(InputSystem.actions, Is.Not.Null);
Assert.That(InputSystem.actions.enabled, Is.True);
var enabledActions = InputSystem.ListEnabledActions();
Assert.That(enabledActions, Has.Count.EqualTo(initialActionCount));

// Build new asset
var asset = ScriptableObject.CreateInstance<InputActionAsset>();
var map1 = new InputActionMap("replacedMap1");
var map2 = new InputActionMap("replacedMap2");
var action1 = map1.AddAction("replacedAction1", InputActionType.Button);
var action2 = map1.AddAction("replacedAction2", InputActionType.Button);
var action3 = map1.AddAction("replacedAction3", InputActionType.Button);
var action4 = map2.AddAction("replacedAction4", InputActionType.Button);

action1.AddBinding("<Gamepad>/buttonSouth");
action2.AddBinding("<Gamepad>/buttonWest");
action3.AddBinding("<Gamepad>/buttonNorth");
action4.AddBinding("<Gamepad>/buttonEast");
asset.AddActionMap(map1);
asset.AddActionMap(map2);

// Replace project-wide actions
InputSystem.actions = asset;

// State after replacing
Assert.That(InputSystem.actions, Is.Not.Null);
Assert.That(InputSystem.actions.enabled, Is.True);
enabledActions = InputSystem.ListEnabledActions();
Assert.That(enabledActions, Has.Count.EqualTo(4));

Assert.That(InputSystem.actions.actionMaps.Count, Is.EqualTo(2));
Assert.That(InputSystem.actions.actionMaps[0].actions.Count, Is.EqualTo(3));
Assert.That(InputSystem.actions.actionMaps[0].actions[0].name, Is.EqualTo("replacedAction1"));
Assert.That(InputSystem.actions.actionMaps[1].actions.Count, Is.EqualTo(1));
Assert.That(InputSystem.actions.actionMaps[1].actions[0].name, Is.EqualTo("replacedAction4"));
}
}

#endif
11 changes: 11 additions & 0 deletions Assets/Tests/InputSystem/CoreTests_ProjectWideActions.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions Assets/Tests/InputSystem/TestActionsAsset.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using UnityEngine;

internal class TestActionsAsset : ScriptableObject
{
}
11 changes: 11 additions & 0 deletions Assets/Tests/InputSystem/TestActionsAsset.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,26 @@ internal static class ProjectWideActionsAsset
{
internal const string kDefaultAssetPath = "Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsTemplate.inputactions";
internal const string kAssetPath = "ProjectSettings/InputManager.asset";
internal const string kAssetName = "ProjectWideInputActions";
internal const string kAssetName = InputSystem.kProjectWideActionsAssetName;

static string s_DefaultAssetPath = kDefaultAssetPath;
static string s_AssetPath = kAssetPath;

// For Testing
#if UNITY_INCLUDE_TESTS
internal static void SetAssetPaths(string defaultAssetPath, string assetPath)
{
s_DefaultAssetPath = defaultAssetPath;
s_AssetPath = assetPath;
}

internal static void Reset()
{
s_DefaultAssetPath = kDefaultAssetPath;
s_AssetPath = kAssetPath;
}

#endif

[InitializeOnLoadMethod]
internal static void InstallProjectWideActions()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@
"path": "<Keyboard>/enter",
"interactions": "",
"processors": "",
"groups": "",
"groups": "Keyboard&Mouse",
"action": "Attack",
"isComposite": false,
"isPartOfComposite": false
Expand Down Expand Up @@ -347,7 +347,7 @@
"path": "<Keyboard>/leftShift",
"interactions": "",
"processors": "",
"groups": "",
"groups": "Keyboard&Mouse",
"action": "Sprint",
"isComposite": false,
"isPartOfComposite": false
Expand All @@ -369,7 +369,7 @@
"path": "<XRController>/trigger",
"interactions": "",
"processors": "",
"groups": "",
"groups": "XR",
"action": "Sprint",
"isComposite": false,
"isPartOfComposite": false
Expand Down Expand Up @@ -446,7 +446,7 @@
"path": "<Gamepad>/buttonNorth",
"interactions": "",
"processors": "",
"groups": "",
"groups": "Gamepad",
"action": "Interact",
"isComposite": false,
"isPartOfComposite": false
Expand Down
3 changes: 2 additions & 1 deletion Packages/com.unity.inputsystem/InputSystem/InputSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3012,6 +3012,7 @@ internal static bool ShouldDrawWarningIconForBinding(string bindingPath)
#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS

private static InputActionAsset s_projectWideActions;
internal const string kProjectWideActionsAssetName = "ProjectWideInputActions";

/// <summary>
/// An input action asset (see <see cref="InputActionAsset"/>) which is always available by default.
Expand Down Expand Up @@ -3039,7 +3040,7 @@ public static InputActionAsset actions
s_projectWideActions, true);
}
#else
s_projectWideActions = Resources.FindObjectsOfTypeAll<InputActionAsset>().FirstOrDefault();
s_projectWideActions = Resources.FindObjectsOfTypeAll<InputActionAsset>().FirstOrDefault(o => o != null && o.name == kProjectWideActionsAssetName);
#endif

if (s_projectWideActions == null)
Expand Down

0 comments on commit 665d11d

Please sign in to comment.