MythicalWar/Assets/Invector-3rdPersonController/Basic Locomotion/Scripts/CharacterController/Editor/vInputSystemRegressionTests.cs

200 lines
9.8 KiB
C#

#if ENABLE_INPUT_SYSTEM
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Invector.vItemManager;
using NUnit.Framework;
using UnityEditor;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.InputSystem.UI;
namespace Invector.vCharacterController.InputSystemEditor
{
public class vInputSystemRegressionTests
{
private const string Root = "Assets/Invector-3rdPersonController/";
private static readonly FieldInfo ActionField = typeof(GenericInput).GetField("inputAction", BindingFlags.Instance | BindingFlags.NonPublic);
private static readonly FieldInfo InstanceField = typeof(vInput).GetField("_instance", BindingFlags.Static | BindingFlags.NonPublic);
private readonly List<Object> created = new List<Object>();
private InputActionAsset actions;
private Gamepad pad;
private Keyboard keyboard;
private object previousInstance;
private object fixture;
[SetUp]
public void Setup()
{
// The predefined editor assembly cannot reference the package's test-only asmdef directly.
var fixtureType = System.Type.GetType("UnityEngine.InputSystem.InputTestFixture, Unity.InputSystem.TestFramework", true);
fixture = System.Activator.CreateInstance(fixtureType);
fixtureType.GetMethod("Setup").Invoke(fixture, null);
previousInstance = InstanceField.GetValue(null);
var manager = Track(new GameObject("Input regression manager"));
InstanceField.SetValue(null, manager.AddComponent<vInput>());
pad = InputSystem.AddDevice<Gamepad>();
keyboard = InputSystem.AddDevice<Keyboard>();
actions = Track(InputActionAsset.FromJson(System.IO.File.ReadAllText(Root + "Basic Locomotion/InputSystem/InvectorPlayerInputs.inputactions")));
actions.Enable();
}
[TearDown]
public void TearDown()
{
try
{
foreach (var obj in created.AsEnumerable().Reverse())
if (obj) Object.DestroyImmediate(obj);
created.Clear();
InstanceField.SetValue(null, previousInstance);
}
finally { if (fixture != null) fixture.GetType().GetMethod("TearDown").Invoke(fixture, null); }
}
private T Track<T>(T obj) where T : Object
{
created.Add(obj);
return obj;
}
private void Bind(GenericInput input)
{
if (input.inputActionReference == null) return;
var reference = Track(InputActionReference.Create(actions.FindAction(input.inputActionReference.action.id)));
ActionField.SetValue(input, reference);
input.GetButtonDown();
}
private vInventory LoadInventory(string name)
{
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(Root + "ItemManager/Prefabs/" + name + ".prefab");
var root = Track(Object.Instantiate(prefab));
root.SetActive(false);
var inventory = root.GetComponentInChildren<vInventory>(true);
inventory.canEquip = true;
foreach (var control in inventory.changeEquipmentControllers)
{
control.useItemInput.useInput = false;
Bind(control.previousItemInput);
Bind(control.nextItemInput);
control.equipArea.onChangeEquipSlot = new OnChangeEquipmentEvent();
control.equipArea.onEquipItem = new OnChangeEquipmentEvent();
control.equipArea.onUnequipItem = new OnChangeEquipmentEvent();
}
foreach (var selector in root.GetComponentsInChildren<vControlAreaByInput>(true))
{
selector.inventory = inventory;
foreach (var slot in selector.slotsSelectors) Bind(slot.input);
}
return inventory;
}
private static void Tick(vInventory inventory)
{
foreach (var selector in inventory.GetComponentsInChildren<vControlAreaByInput>(true))
typeof(vControlAreaByInput).GetMethod("Update", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(selector, null);
typeof(vInventory).GetMethod("ChangeEquipmentInput", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(inventory, null);
}
[Test]
public void EquipmentDirectionsCycleOnlyTheirArea(
[Values("Inventory_ShooterMelee", "Inventory_MeleeOnly")] string prefab,
[Values(false, true)] bool gamepad,
[Values(0, 1, 2)] int area)
{
var inventory = LoadInventory(prefab);
var target = inventory.changeEquipmentControllers[area].equipArea;
var validSlots = Enumerable.Range(0, target.equipSlots.Count).Where(i => target.equipSlots[i].isValid).ToArray();
Assert.That(validSlots.Length, Is.GreaterThan(1));
foreach (var control in inventory.changeEquipmentControllers) control.equipArea.indexOfEquippedItem = 0;
var directions = new[] { GamepadButton.DpadRight, GamepadButton.DpadLeft, GamepadButton.DpadDown };
var keys = new[] { Key.RightArrow, Key.LeftArrow, Key.DownArrow };
for (int press = 0; press < validSlots.Length + 1; press++)
{
InputSystem.QueueStateEvent(pad, new GamepadState());
InputSystem.QueueStateEvent(keyboard, new KeyboardState());
InputSystem.Update();
Tick(inventory);
if (gamepad) InputSystem.QueueStateEvent(pad, new GamepadState().WithButton(directions[area]));
else InputSystem.QueueStateEvent(keyboard, new KeyboardState(keys[area]));
InputSystem.Update();
Tick(inventory);
int expected = validSlots[(press + 1) % validSlots.Length];
Assert.That(target.indexOfEquippedItem, Is.EqualTo(expected), "Each press must advance exactly one available slot.");
foreach (var other in inventory.changeEquipmentControllers.Where((c, i) => i != area))
Assert.That(other.equipArea.indexOfEquippedItem, Is.Zero, "Another equipment area consumed the same input.");
InputSystem.Update();
Tick(inventory);
Assert.That(target.indexOfEquippedItem, Is.EqualTo(expected), "Holding a direction must not select another slot.");
}
}
[TestCase(-1f, 1f, 0f)]
[TestCase(0f, 0f, 0f)]
[TestCase(1f, 0f, 1f)]
public void LockOnUsesRightStickHorizontal(float x, float previous, float next)
{
InputSystem.QueueStateEvent(pad, new GamepadState { rightStick = new Vector2(x, 0f) });
InputSystem.Update();
Assert.That(actions.FindAction("Previous Target").ReadValue<float>(), Is.EqualTo(previous));
Assert.That(actions.FindAction("Next Target").ReadValue<float>(), Is.EqualTo(next));
}
[TestCase("Previous Target", "Z")]
[TestCase("Next Target", "X")]
public void LockOnAxisFiresOncePerDeflectionAndKeepsKeyboard(string actionName, string key)
{
var input = JsonUtility.FromJson<GenericInput>("{\"useInput\":true,\"inputSource\":1,\"inputSystemAxis\":true,\"inputSystemRawThreshold\":0.1}");
ActionField.SetValue(input, Track(InputActionReference.Create(actions.FindAction(actionName))));
float x = actionName == "Previous Target" ? -1f : 1f;
Assert.That(input.GetButtonDown(), Is.False);
for (int i = 0; i < 2; i++)
{
InputSystem.QueueStateEvent(pad, new GamepadState { rightStick = new Vector2(x, 0f) });
InputSystem.Update();
Assert.That(input.GetButtonDown(), Is.True);
InputSystem.Update();
Assert.That(input.GetButtonDown(), Is.False);
InputSystem.QueueStateEvent(pad, new GamepadState());
InputSystem.Update();
Assert.That(input.GetButtonDown(), Is.False);
}
InputSystem.QueueStateEvent(keyboard, new KeyboardState((Key)System.Enum.Parse(typeof(Key), key)));
InputSystem.Update();
Assert.That(input.GetButtonDown(), Is.True);
}
[Test]
public void ShooterOnlyRetainsItsAuthoredDirectSlotShortcuts()
{
var inventory = LoadInventory("Inventory_ShooterOnly");
foreach (var selector in inventory.GetComponentsInChildren<vControlAreaByInput>(true))
foreach (var slot in selector.slotsSelectors)
{
Assert.That(slot.input.useInput, Is.True);
Assert.That(slot.input.inputActionReference, Is.Not.Null);
Assert.That(slot.input.inputActionReference.action.bindings.Any(b => b.path.StartsWith("<Gamepad>/dpad/")), Is.True);
}
}
[Test]
public void EventSystemSelectsOnlyNewBackendAndPreservesLegacyConfiguration()
{
var root = Track(new GameObject("UI backend regression", typeof(EventSystem), typeof(StandaloneInputModule)));
var legacy = root.GetComponent<StandaloneInputModule>();
legacy.horizontalAxis = "CustomHorizontal";
var backend = root.AddComponent<vEventSystemInputBackend>();
backend.Configure();
backend.Configure();
Assert.That(legacy.enabled, Is.False);
Assert.That(legacy.horizontalAxis, Is.EqualTo("CustomHorizontal"));
Assert.That(root.GetComponents<InputSystemUIInputModule>().Length, Is.EqualTo(1));
Assert.That(root.GetComponent<InputSystemUIInputModule>().enabled, Is.True);
}
}
}
#endif