Files
tiny_tackle_heroes/Assets/_Project/Tests/PlayMode/ArtifactSwitchEventPlayModeTests.cs
T

356 lines
15 KiB
C#

using System.Collections;
using System.Reflection;
using BumpCombat.Combat;
using BumpCombat.Core;
using BumpCombat.Player;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.TestTools;
namespace BumpCombat.PlayModeTests
{
public sealed class ArtifactSwitchEventPlayModeTests
{
private GameObject testPlayer;
private ActiveArtifactController artifacts;
private ActiveArtifactDefinition firstArtifact;
private ActiveArtifactDefinition secondArtifact;
private Keyboard virtualKeyboard;
private bool virtualKeyboardInputActive;
private InputSettings.BackgroundBehavior previousBackgroundBehavior;
private InputSettings.EditorInputBehaviorInPlayMode previousEditorInputBehavior;
private bool inputSettingsCaptured;
[SetUp]
public void SetUp()
{
Time.timeScale = 1f;
}
[TearDown]
public void TearDown()
{
Time.timeScale = 1f;
if (virtualKeyboardInputActive
&& virtualKeyboard != null
&& virtualKeyboard.added)
{
InputSystem.QueueStateEvent(virtualKeyboard, new KeyboardState());
InputSystem.RemoveDevice(virtualKeyboard);
}
if (inputSettingsCaptured)
{
InputSystem.settings.backgroundBehavior = previousBackgroundBehavior;
InputSystem.settings.editorInputBehaviorInPlayMode =
previousEditorInputBehavior;
}
if (testPlayer != null)
{
Object.Destroy(testPlayer);
}
if (firstArtifact != null)
{
Object.Destroy(firstArtifact);
}
if (secondArtifact != null)
{
Object.Destroy(secondArtifact);
}
virtualKeyboard = null;
virtualKeyboardInputActive = false;
inputSettingsCaptured = false;
}
[UnityTest]
public IEnumerator ArtifactSwitchedEvent_OnlyFiresOnceAfterSuccessfulSwitch()
{
RunManager previousRunManager = RunManager.Instance;
if (previousRunManager == null)
{
previousRunManager = null;
}
bool previousRunManagerEnabled = previousRunManager != null
&& previousRunManager.enabled;
if (previousRunManager != null)
{
previousRunManager.enabled = false;
}
SetRunManagerInstanceForTest(null);
Time.timeScale = 1f;
ActiveArtifactController[] existingControllers =
Object.FindObjectsByType<ActiveArtifactController>(
FindObjectsSortMode.None);
bool[] previousControllerStates = new bool[existingControllers.Length];
for (int i = 0; i < existingControllers.Length; i++)
{
previousControllerStates[i] = existingControllers[i].enabled;
existingControllers[i].enabled = false;
}
Keyboard keyboard = null;
try
{
testPlayer = new GameObject("Artifact Switch Event Test Player");
testPlayer.AddComponent<PlayerStats>();
artifacts = testPlayer.AddComponent<ActiveArtifactController>();
firstArtifact = CreateArtifact(
"switch-event-dash",
ActiveArtifactEffect.Dash);
secondArtifact = CreateArtifact(
"switch-event-pulse",
ActiveArtifactEffect.Pulse);
int switchCount = 0;
int chargeStartCount = 0;
ActiveArtifactDefinition eventDefinition = null;
ActiveArtifactDefinition selectedDuringEvent = null;
artifacts.OnArtifactSwitched += definition =>
{
switchCount++;
eventDefinition = definition;
selectedDuringEvent = artifacts.CurrentArtifact;
};
artifacts.OnArtifactChargeStarted += _ => chargeStartCount++;
int useCount = 0;
artifacts.OnArtifactUseSucceeded += (_, _) => useCount++;
Assert.That(artifacts.TryAddArtifact(firstArtifact), Is.True);
Assert.That(artifacts.SelectNext(), Is.False);
Assert.That(switchCount, Is.Zero,
"Acquisition and a failed switch must not emit the switch event.");
artifacts.AddMovementCharge(1f);
artifacts.AddMovementCharge(49f);
Assert.That(artifacts.CurrentGauge, Is.GreaterThan(0f));
Assert.That(artifacts.CurrentGauge, Is.EqualTo(100f));
Assert.That(switchCount, Is.Zero,
"Gauge changes must not emit the switch event.");
keyboard = BeginVirtualKeyboardInput();
InputSystem.QueueStateEvent(keyboard, new KeyboardState(Key.A));
yield return null;
Assert.That(artifacts.IsCharging, Is.True);
Assert.That(chargeStartCount, Is.EqualTo(1));
Assert.That(artifacts.SelectNext(), Is.False);
Assert.That(switchCount, Is.Zero,
"A one-artifact inventory cannot switch and keeps charging.");
Assert.That(artifacts.IsCharging, Is.True,
"A failed single-slot switch must preserve the charge.");
Assert.That(artifacts.TryAddArtifact(secondArtifact), Is.True);
Assert.That(switchCount, Is.Zero,
"Adding another owned artifact must not be reported as switching.");
yield return new WaitForSeconds(firstArtifact.ChargeDuration * 0.5f);
Assert.That(artifacts.ChargeProgress, Is.GreaterThan(0f).And.LessThan(1f));
Assert.That(artifacts.SelectNext(), Is.True);
Assert.That(artifacts.IsCharging, Is.False,
"A successful switch must cancel a partial charge.");
Assert.That(switchCount, Is.EqualTo(1));
Assert.That(eventDefinition, Is.SameAs(secondArtifact));
Assert.That(selectedDuringEvent, Is.SameAs(secondArtifact),
"The event must observe the newly selected artifact.");
Assert.That(artifacts.CurrentArtifact, Is.SameAs(secondArtifact));
Assert.That(artifacts.CurrentGauge, Is.EqualTo(100f),
"Switching during a partial charge must not spend gauge.");
Assert.That(useCount, Is.Zero,
"Switching during a partial charge must not activate an artifact.");
InputSystem.QueueStateEvent(keyboard, new KeyboardState());
yield return null;
Assert.That(useCount, Is.Zero,
"Releasing the held charge after switching must not activate either artifact.");
}
finally
{
if (keyboard != null && keyboard.added)
{
InputSystem.QueueStateEvent(keyboard, new KeyboardState());
}
for (int i = 0; i < existingControllers.Length; i++)
{
if (existingControllers[i] != null)
{
existingControllers[i].enabled = previousControllerStates[i];
}
}
if (previousRunManager != null)
{
previousRunManager.enabled = previousRunManagerEnabled;
}
SetRunManagerInstanceForTest(previousRunManager);
}
}
[UnityTest]
public IEnumerator FullyChargedSwitch_CancelsWithoutActivationOrGaugeCost()
{
testPlayer = new GameObject("Fully Charged Artifact Switch Test Player");
testPlayer.AddComponent<PlayerStats>();
artifacts = testPlayer.AddComponent<ActiveArtifactController>();
firstArtifact = CreateArtifact(
"fully-charged-switch-pulse",
ActiveArtifactEffect.Pulse);
secondArtifact = CreateArtifact(
"fully-charged-switch-cyclone",
ActiveArtifactEffect.Cyclone);
Assert.That(artifacts.TryAddArtifact(firstArtifact), Is.True);
Assert.That(artifacts.TryAddArtifact(secondArtifact), Is.True);
artifacts.AddMovementCharge(50f);
float gaugeBeforeSwitch = artifacts.CurrentGauge;
int useCount = 0;
artifacts.OnArtifactUseSucceeded += (_, _) => useCount++;
InvokePrivate(artifacts, "BeginCharge");
typeof(ActiveArtifactController)
.GetField("chargeStartedAt", BindingFlags.Instance | BindingFlags.NonPublic)
?.SetValue(
artifacts,
Time.time - firstArtifact.ChargeDuration - 0.01f);
Assert.That(artifacts.IsFullyCharged, Is.True);
Assert.That(artifacts.SelectNext(), Is.True);
Assert.That(artifacts.CurrentArtifact, Is.SameAs(secondArtifact));
Assert.That(artifacts.IsCharging, Is.False);
Assert.That(artifacts.IsExecutingArtifact, Is.False);
Assert.That(artifacts.CurrentGauge, Is.EqualTo(gaugeBeforeSwitch));
Assert.That(useCount, Is.Zero);
yield return null;
}
[UnityTest]
public IEnumerator SWhileHoldingA_CancelsChargeAndReleaseWaitsForFreshPress()
{
testPlayer = new GameObject("Held A Artifact Switch Test Player");
testPlayer.AddComponent<PlayerStats>();
artifacts = testPlayer.AddComponent<ActiveArtifactController>();
firstArtifact = CreateArtifact(
"held-a-switch-pulse",
ActiveArtifactEffect.Pulse);
secondArtifact = CreateArtifact(
"held-a-switch-cyclone",
ActiveArtifactEffect.Cyclone);
Assert.That(artifacts.TryAddArtifact(firstArtifact), Is.True);
Assert.That(artifacts.TryAddArtifact(secondArtifact), Is.True);
artifacts.AddMovementCharge(50f);
float gaugeBeforeSwitch = artifacts.CurrentGauge;
int chargeStartCount = 0;
int useCount = 0;
artifacts.OnArtifactChargeStarted += _ => chargeStartCount++;
artifacts.OnArtifactUseSucceeded += (_, _) => useCount++;
Keyboard keyboard = BeginVirtualKeyboardInput();
InputSystem.QueueStateEvent(keyboard, new KeyboardState(Key.A));
yield return null;
Assert.That(artifacts.IsCharging, Is.True);
Assert.That(chargeStartCount, Is.EqualTo(1));
yield return new WaitForSeconds(firstArtifact.ChargeDuration + 0.05f);
Assert.That(artifacts.IsFullyCharged, Is.True);
InputSystem.QueueStateEvent(keyboard, new KeyboardState(Key.A, Key.S));
yield return null;
Assert.That(keyboard.aKey.isPressed, Is.True);
Assert.That(keyboard.sKey.isPressed, Is.True);
Assert.That(artifacts.CurrentArtifact, Is.SameAs(secondArtifact));
Assert.That(artifacts.IsCharging, Is.False,
"S must cancel a fully charged artifact while A remains held.");
Assert.That(artifacts.CurrentGauge, Is.EqualTo(gaugeBeforeSwitch));
Assert.That(useCount, Is.Zero);
InputSystem.QueueStateEvent(keyboard, new KeyboardState());
yield return null;
Assert.That(artifacts.IsCharging, Is.False);
Assert.That(artifacts.IsExecutingArtifact, Is.False);
Assert.That(artifacts.CurrentGauge, Is.EqualTo(gaugeBeforeSwitch));
Assert.That(useCount, Is.Zero,
"Releasing the A held through the switch must not activate either artifact.");
InputSystem.QueueStateEvent(keyboard, new KeyboardState(Key.A));
yield return null;
Assert.That(artifacts.IsCharging, Is.True,
"A fresh press after switching may begin a new charge.");
Assert.That(chargeStartCount, Is.EqualTo(2));
Assert.That(artifacts.CurrentArtifact, Is.SameAs(secondArtifact));
InvokePrivate(artifacts, "CancelCharge");
InputSystem.QueueStateEvent(keyboard, new KeyboardState());
yield return null;
Assert.That(useCount, Is.Zero);
Assert.That(artifacts.CurrentGauge, Is.EqualTo(gaugeBeforeSwitch));
}
private static ActiveArtifactDefinition CreateArtifact(
string id,
ActiveArtifactEffect effect)
{
ActiveArtifactDefinition definition =
ScriptableObject.CreateInstance<ActiveArtifactDefinition>();
definition.Configure(
id,
id,
"",
effect,
DamageTag.Collision,
Color.white,
Color.white,
25f,
60f,
0.8f,
15f,
30f,
1.25f,
2f,
1.5f,
3f);
return definition;
}
private Keyboard BeginVirtualKeyboardInput()
{
previousBackgroundBehavior = InputSystem.settings.backgroundBehavior;
previousEditorInputBehavior =
InputSystem.settings.editorInputBehaviorInPlayMode;
inputSettingsCaptured = true;
InputSystem.settings.backgroundBehavior =
InputSettings.BackgroundBehavior.IgnoreFocus;
InputSystem.settings.editorInputBehaviorInPlayMode =
InputSettings.EditorInputBehaviorInPlayMode.AllDeviceInputAlwaysGoesToGameView;
virtualKeyboard = InputSystem.AddDevice<Keyboard>();
virtualKeyboard.MakeCurrent();
virtualKeyboardInputActive = true;
return virtualKeyboard;
}
private static void InvokePrivate(
ActiveArtifactController target,
string methodName)
{
MethodInfo method = typeof(ActiveArtifactController).GetMethod(
methodName,
BindingFlags.Instance | BindingFlags.NonPublic);
Assert.That(method, Is.Not.Null, $"Could not find {methodName}.");
method.Invoke(target, null);
}
private static void SetRunManagerInstanceForTest(RunManager instance)
{
FieldInfo instanceField = typeof(RunManager).GetField(
"<Instance>k__BackingField",
BindingFlags.Static | BindingFlags.NonPublic);
Assert.That(instanceField, Is.Not.Null);
instanceField.SetValue(null, instance);
}
}
}