diff --git a/Assets/Tests/InputSystem/CoreTests_Actions.cs b/Assets/Tests/InputSystem/CoreTests_Actions.cs index 2932b578ee..22c7c73776 100644 --- a/Assets/Tests/InputSystem/CoreTests_Actions.cs +++ b/Assets/Tests/InputSystem/CoreTests_Actions.cs @@ -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 diff --git a/Assets/Tests/InputSystem/CoreTests_Devices.cs b/Assets/Tests/InputSystem/CoreTests_Devices.cs index 3b6e9ed063..b963996fb1 100644 --- a/Assets/Tests/InputSystem/CoreTests_Devices.cs +++ b/Assets/Tests/InputSystem/CoreTests_Devices.cs @@ -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(); @@ -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 diff --git a/Assets/Tests/InputSystem/CoreTests_ProjectWideActions.cs b/Assets/Tests/InputSystem/CoreTests_ProjectWideActions.cs new file mode 100644 index 0000000000..097d7bead6 --- /dev/null +++ b/Assets/Tests/InputSystem/CoreTests_ProjectWideActions.cs @@ -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(); + 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(); + 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(); + 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("/buttonSouth"); + action2.AddBinding("/buttonWest"); + action3.AddBinding("/buttonNorth"); + action4.AddBinding("/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 diff --git a/Assets/Tests/InputSystem/CoreTests_ProjectWideActions.cs.meta b/Assets/Tests/InputSystem/CoreTests_ProjectWideActions.cs.meta new file mode 100644 index 0000000000..e1b8a266a6 --- /dev/null +++ b/Assets/Tests/InputSystem/CoreTests_ProjectWideActions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 52e6b4a92448f524d99a9465f6cc8b54 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/InputSystem/TestActionsAsset.cs b/Assets/Tests/InputSystem/TestActionsAsset.cs new file mode 100644 index 0000000000..70e1fd9dfb --- /dev/null +++ b/Assets/Tests/InputSystem/TestActionsAsset.cs @@ -0,0 +1,5 @@ +using UnityEngine; + +internal class TestActionsAsset : ScriptableObject +{ +} diff --git a/Assets/Tests/InputSystem/TestActionsAsset.cs.meta b/Assets/Tests/InputSystem/TestActionsAsset.cs.meta new file mode 100644 index 0000000000..90b00f3740 --- /dev/null +++ b/Assets/Tests/InputSystem/TestActionsAsset.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 91991318f6d19a74b9ac916df855fc69 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsAsset.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsAsset.cs index 6660a57a81..c2b6d1b54f 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsAsset.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsAsset.cs @@ -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() { diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsTemplate.inputactions b/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsTemplate.inputactions index 31333c2493..5d4dbca6ae 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsTemplate.inputactions +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsTemplate.inputactions @@ -314,7 +314,7 @@ "path": "/enter", "interactions": "", "processors": "", - "groups": "", + "groups": "Keyboard&Mouse", "action": "Attack", "isComposite": false, "isPartOfComposite": false @@ -347,7 +347,7 @@ "path": "/leftShift", "interactions": "", "processors": "", - "groups": "", + "groups": "Keyboard&Mouse", "action": "Sprint", "isComposite": false, "isPartOfComposite": false @@ -369,7 +369,7 @@ "path": "/trigger", "interactions": "", "processors": "", - "groups": "", + "groups": "XR", "action": "Sprint", "isComposite": false, "isPartOfComposite": false @@ -446,7 +446,7 @@ "path": "/buttonNorth", "interactions": "", "processors": "", - "groups": "", + "groups": "Gamepad", "action": "Interact", "isComposite": false, "isPartOfComposite": false diff --git a/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs b/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs index 7631760fef..c9c31911ef 100644 --- a/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs +++ b/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs @@ -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"; /// /// An input action asset (see ) which is always available by default. @@ -3039,7 +3040,7 @@ public static InputActionAsset actions s_projectWideActions, true); } #else - s_projectWideActions = Resources.FindObjectsOfTypeAll().FirstOrDefault(); + s_projectWideActions = Resources.FindObjectsOfTypeAll().FirstOrDefault(o => o != null && o.name == kProjectWideActionsAssetName); #endif if (s_projectWideActions == null)