Skip to content

Unit test for the Input system #144

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 6 additions & 7 deletions UOP1_Project/Assets/Scripts/Input/InputReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,21 @@ public class InputReader : ScriptableObject, GameInput.IGameplayActions
public event UnityAction enableMouseControlCameraEvent;
public event UnityAction disableMouseControlCameraEvent;

public GameInput gameInput;
public GameInput GameInput { get; private set; }

private void OnEnable()
{
if (gameInput == null)
if (GameInput == null)
{
gameInput = new GameInput();
gameInput.Gameplay.SetCallbacks(this);
GameInput = new GameInput();
GameInput.Gameplay.SetCallbacks(this);
}

gameInput.Gameplay.Enable();
GameInput.Gameplay.Enable();
}

private void OnDisable()
{
gameInput.Gameplay.Disable();
GameInput.Gameplay.Disable();
}

public void OnAttack(InputAction.CallbackContext context)
Expand Down
15 changes: 15 additions & 0 deletions UOP1_Project/Assets/Scripts/Input/UOP1.Input.asmdef
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "UOP1.Input",
"references": [
"GUID:75469ad4d38634e559750d17036d5f7c"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}
7 changes: 7 additions & 0 deletions UOP1_Project/Assets/Scripts/Input/UOP1.Input.asmdef.meta

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

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

22 changes: 22 additions & 0 deletions UOP1_Project/Assets/Scripts/Tests/Edit Mode/Edit Mode.asmdef
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "EditMode",
"references": [
"UnityEngine.TestRunner",
"UnityEditor.TestRunner"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": true,
"precompiledReferences": [
"nunit.framework.dll"
],
"autoReferenced": false,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"versionDefines": [],
"noEngineReferences": false
}

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

3 changes: 3 additions & 0 deletions UOP1_Project/Assets/Scripts/Tests/Edit Mode/Example.meta

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

13 changes: 13 additions & 0 deletions UOP1_Project/Assets/Scripts/Tests/Edit Mode/Example/ExampleTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using NUnit.Framework;

namespace Tests.Edit_Mode.Example
{
public class ExampleTest
{
[Test]
public void ExampleTestSimplePasses()
{
Assert.Pass();
}
}
}

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

8 changes: 8 additions & 0 deletions UOP1_Project/Assets/Scripts/Tests/Play Mode.meta

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

3 changes: 3 additions & 0 deletions UOP1_Project/Assets/Scripts/Tests/Play Mode/InputReader.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
@@ -0,0 +1,178 @@
using System.Collections;
using System.Linq;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Utilities;
using UnityEngine.TestTools;

namespace Tests.Play_Mode.InputReader
{
public class InputReaderTests
{
class InputReaderTestFixture : InputTestFixture
{
// Input Boilerplate
private Keyboard Keyboard { get; set; }
private Mouse Mouse { get; set; }
private Gamepad Gamepad { get; set; }
// Systems being tested
private GameInput GameInput { get; set; }
private global::InputReader InputReader { get; set; }

// counters for input event calls
private int MoveEvents { get; set; }
private int AttackEvents { get; set; }
private int JumpEvents { get; set; }
private int JumpCanceledEvents { get; set; }

// callback targets for input event calls
public void OnMove(Vector2 v2) => MoveEvents++;
public void OnAttack() => AttackEvents++;
public void OnJump() => JumpEvents++;
public void OnJumpCanceled() => JumpCanceledEvents++;


[SetUp] // if asynchronous code is necessary use [UnitySetUp]
public void SetupInput()
{
// must AddDevice<T> to get input in a test environment from those mappings
Keyboard = InputSystem.AddDevice<Keyboard>();
Mouse = InputSystem.AddDevice<Mouse>();
Gamepad = InputSystem.AddDevice<Gamepad>();

// instantiate a SO to act as the event Observer
InputReader = ScriptableObject.CreateInstance<global::InputReader>();
GameInput = InputReader.GameInput;

ResetCounters();

// add cb's
InputReader.moveEvent += OnMove;
InputReader.attackEvent += OnAttack;
InputReader.jumpEvent += OnJump;
InputReader.jumpCanceledEvent += OnJumpCanceled;
}

/// <summary>
/// Removes subscriptions from input reader
/// </summary>
[TearDown]
public void Cleanup()
{
// remove cb's
InputReader.moveEvent -= OnMove;
InputReader.attackEvent -= OnAttack;
InputReader.jumpEvent -= OnJump;
InputReader.jumpCanceledEvent -= OnJumpCanceled;

ResetCounters();
}

/// <summary>
/// Reset counters used to keep track of events being fired off by inputreader.
/// should be replaced by a spy or mock when we have access to some library that provides the functionality.
/// </summary>
private void ResetCounters()
{
MoveEvents = 0;
AttackEvents = 0;
JumpEvents = 0;
JumpCanceledEvents = 0;
}

public static string[] InputMappings =
{
nameof(GamepadMappings),
nameof(KeyboardMappings),
};

[UnityTest]
public IEnumerator InputReaderHandlesAttackInput([ValueSource(nameof(InputMappings))] string mappingType)
{
var mapping = MappingProviderFactory.Create(mappingType);

Press(mapping.Attack);
yield return new WaitForEndOfFrame();
Release(mapping.Attack);
yield return new WaitForEndOfFrame();

Assert.That(AttackEvents, Is.EqualTo(1));
}


[UnityTest]
public IEnumerator InputReaderHandlesMoveInput([ValueSource(nameof(InputMappings))] string mappingType)
{
var mapping = MappingProviderFactory.Create(mappingType);

Press(mapping.MoveLeft);
yield return new WaitForEndOfFrame();
Release(mapping.MoveLeft);
yield return new WaitForEndOfFrame();

Assert.That(MoveEvents, Is.EqualTo(3));
}


/// <summary>
/// Example case for debugging input and a form of documentation.
/// </summary>
/// <param name="mappingType">nameof(some input provider)</param>
/// <returns>null</returns>
[UnityTest]
public IEnumerator InputReaderReceivesInput([ValueSource(nameof(InputMappings))] string mappingType)
{
var mapping = MappingProviderFactory.Create(mappingType);

var attackAction = GameInput.Gameplay.Attack;
var moveAction = GameInput.Gameplay.Move;

using (var trace = new InputActionTrace())
{
// subscribe to relevant actions so we can track their activity
trace.SubscribeTo(attackAction);
trace.SubscribeTo(moveAction);

yield return new WaitForEndOfFrame();
Press(mapping.Attack);
yield return new WaitForEndOfFrame();
Press(mapping.MoveLeft);
yield return new WaitForEndOfFrame();
Release(mapping.Attack);
yield return new WaitForEndOfFrame();
Release(mapping.MoveLeft);
yield return new WaitForEndOfFrame();

// grab the events from the trace
var actions = trace.ToArray();
// LogActionsPerformed(actions);

Assert.That(actions.Length, Is.EqualTo(6));
}
}

/// <summary>
/// Debugging tool to see which actions have been fired off. requires the usage of an InputTrace.
/// </summary>
/// <param name="actions"></param>
private static void LogActionsPerformed(InputActionTrace.ActionEventPtr[] actions)
{
foreach (var a in actions)
{
Debug.Log(a);
}
}

/// <summary>
/// Set "time" of input fixutre. tracked in the event itself.
/// </summary>
/// <param name="t">new syste time</param>
private void SetInputSystemTime(float t)
{
var time = t;
currentTime = time;
}
}
}
}

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
@@ -0,0 +1,67 @@
using System;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;

namespace Tests.Play_Mode.InputReader
{
public interface IMappingProvider
{
ButtonControl MoveLeft { get; }
ButtonControl MoveRight { get; }
ButtonControl MoveUp { get; }
ButtonControl MoveDown { get; }

ButtonControl Jump { get; }
ButtonControl Interact { get; }
ButtonControl Attack { get; }
ButtonControl Extra { get; }

ButtonControl Pause { get; }
}

public static class MappingProviderFactory
{
public static IMappingProvider Create(string type)
{
switch (type)
{
case nameof(KeyboardMappings):
return new KeyboardMappings();
case nameof(GamepadMappings):
return new GamepadMappings();
default:
throw new ArgumentException("string provided must be the nameof() some IMappingProvider. e.g. KeyboardMappings or GamepadMappings");
}
}
}

public class KeyboardMappings : IMappingProvider
{
public ButtonControl MoveLeft { get; private set; } = Keyboard.current.aKey;
public ButtonControl MoveRight { get; private set; } = Keyboard.current.aKey;
public ButtonControl MoveUp { get; private set; } = Keyboard.current.sKey;
public ButtonControl MoveDown { get; private set; } = Keyboard.current.wKey;

public ButtonControl Interact { get; private set; } = Keyboard.current.kKey;
public ButtonControl Jump { get; private set; } = Keyboard.current.spaceKey;
public ButtonControl Attack { get; private set; } = Keyboard.current.jKey;
public ButtonControl Extra { get; private set; } = Keyboard.current.lKey;

public ButtonControl Pause { get; private set; } = Keyboard.current.escapeKey;
}

public class GamepadMappings : IMappingProvider
{
public ButtonControl MoveLeft { get; private set; } = Gamepad.current.leftStick.left;
public ButtonControl MoveRight { get; private set; } = Gamepad.current.leftStick.right;
public ButtonControl MoveUp { get; private set; } = Gamepad.current.leftStick.up;
public ButtonControl MoveDown { get; private set; } = Gamepad.current.leftStick.down;

public ButtonControl Interact { get; private set; } = Gamepad.current.buttonEast;
public ButtonControl Jump { get; private set; } = Gamepad.current.buttonSouth;
public ButtonControl Attack { get; private set; } = Gamepad.current.buttonWest;
public ButtonControl Extra { get; private set; } = Gamepad.current.buttonNorth;

public ButtonControl Pause { get; private set; } = Gamepad.current.startButton;
}
}

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

Loading