Skip to content
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

GD-129-4: Add c# scene runner #223

Merged
merged 3 commits into from
Mar 14, 2022
Merged
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
27 changes: 26 additions & 1 deletion addons/gdUnit3/src/Assertions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace GdUnit3
{
Expand Down Expand Up @@ -62,9 +63,33 @@ public sealed class Assertions
/// An Assertion to verify for expecting exceptions
/// </summary>
/// <param name="supplier">A function callback where throw possible exceptions</param>
/// <returns></returns>
/// <returns>IExceptionAssert</returns>
public static IExceptionAssert AssertThrown<T>(Func<T> supplier) => new ExceptionAssert<T>(supplier);

/// <summary>
/// An Assertion to verify for expecting exceptions when performing a task.
/// <example>
/// <code>
/// await AssertThrown(task.WithTimeout(500))
/// .ContinueWith(result => result.Result.HasMessage("timed out after 500ms."));
/// </code>
/// </example>
/// </summary>
/// <param name="task">A task where throw possible exceptions</param>
/// <returns>a task of <c>IExceptionAssert</c> to await</returns>
public async static Task<IExceptionAssert> AssertThrown<T>(Task<T> task)
{
try
{
await task;
return default;
}
catch (Exception e)
{
return new ExceptionAssert<T>(e);
}
}

/// ----------- Helpers -------------------------------------------------------------------------------------------------------

///<summary>
Expand Down
199 changes: 199 additions & 0 deletions addons/gdUnit3/src/SceneRunner.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
using System;
using System.Threading.Tasks;

namespace GdUnit3
{
using Godot;

/// <summary>
/// Scene runner to test interactions like keybord/mouse inputs on a Godot scene.
/// </summary>
public interface SceneRunner : IDisposable
{

/// <summary>
/// Loads a scene into the SceneRunner to be simmulated.
/// </summary>
/// <param name="resourcePath">The path to the scene resource.</param>
/// <param name="verbose">Prints detailt infos on scene simmulation.</param>
/// <returns></returns>
public static SceneRunner Load(string resourcePath, bool verbose = false) => new Core.SceneRunner(resourcePath, verbose);

/// <summary>
/// Sets the actual mouse position relative to the viewport.
/// </summary>
/// <param name="position">The position in x/y coordinates</param>
/// <returns></returns>
SceneRunner SetMousePos(Vector2 position);

/// <summary>
/// Simulates that a key has been pressed.
/// </summary>
/// <param name="keyCode">the key code e.g. 'KeyList.Enter'</param>
/// <param name="shift">false by default set to true if simmulate shift is press</param>
/// <param name="control">false by default set to true if simmulate control is press</param>
/// <returns>SceneRunner</returns>
SceneRunner SimulateKeyPressed(KeyList keyCode, bool shift = false, bool control = false);

/// <summary>
/// Simulates that a key is pressed.
/// </summary>
/// <param name="keyCode">the key code e.g. 'KeyList.Enter'</param>
/// <param name="shift">false by default set to true if simmulate shift is press</param>
/// <param name="control">false by default set to true if simmulate control is press</param>
/// <returns>SceneRunner</returns>
SceneRunner SimulateKeyPress(KeyList keyCode, bool shift = false, bool control = false);

/// <summary>
/// Simulates that a key has been released.
/// </summary>
/// <param name="keyCode">the key code e.g. 'KeyList.Enter'</param>
/// <param name="shift">false by default set to true if simmulate shift is press</param>
/// <param name="control">false by default set to true if simmulate control is press</param>
/// <returns>SceneRunner</returns>
SceneRunner SimulateKeyRelease(KeyList keyCode, bool shift = false, bool control = false);

/// <summary>
/// Simulates a mouse moved to relative position by given speed.
/// </summary>
/// <param name="relative">The mouse position relative to the previous position (position at the last frame).</param>
/// <param name="speed">The mouse speed in pixels per second.</param>
/// <returns>SceneRunner</returns>
SceneRunner SimulateMouseMove(Vector2 relative, Vector2 speeds = default);

/// <summary>
/// Simulates a mouse button pressed.
/// </summary>
/// <param name="button">The mouse button identifier, one of the ButtonList button or button wheel constants.</param>
/// <returns>SceneRunner</returns>
SceneRunner SimulateMouseButtonPressed(ButtonList button);

/// <summary>
/// Simulates a mouse button press. (holding)
/// </summary>
/// <param name="button">The mouse button identifier, one of the ButtonList button or button wheel constants.</param>
/// <returns>SceneRunner</returns>
SceneRunner SimulateMouseButtonPress(ButtonList button);

/// <summary>
/// Simulates a mouse button released.
/// </summary>
/// <param name="button">The mouse button identifier, one of the ButtonList button or button wheel constants.</param>
/// <returns>SceneRunner</returns>
SceneRunner SimulateMouseButtonRelease(ButtonList button);

/// <summary>
/// Sets how fast or slow the scene simulation is processed (clock ticks versus the real).
/// <code>
/// 'It defaults to 1.0. A value of 2.0 means the game moves twice as fast as real life,'
/// 'whilst a value of 0.5 means the game moves at half the regular speed'
/// </code>
/// </summary>
/// <param name="timeFactor"></param>
/// <returns>SceneRunner</returns>
SceneRunner SetTimeFactor(double timeFactor = 1.0);

/// <summary>
/// Simulates scene processing for a certain number of frames by given delta peer frame by ignoring the current time factor
/// <example>
/// <code>
/// 'Waits until 100 frames are rendered with a delta of 20ms peer frame'
/// await runner.SimulateFrames(100, 20);
/// </code>
/// </example>
/// </summary>
/// <param name="frames">amount of frames to process</param>
/// <param name="deltaPeerFrame">the time delta between a frame in milliseconds</param>
/// <returns></returns>
Task<SceneRunner> SimulateFrames(uint frames, uint deltaPeerFrame);

/// <summary>
/// Simulates scene processing for a certain number of frames.
/// <example>
/// <code>
/// 'Waits until 100 frames are rendered'
/// await runner.SimulateFrames(100);
/// </code>
/// </example>
/// </summary>
/// <param name="frames">amount of frames to process</param>
/// <returns></returns>
Task<SceneRunner> SimulateFrames(uint frames);

/// <summary>
/// Waits until next frame is processed (signal idle_frame)
/// <example>
/// <code>
/// 'Waits until next frame is processed'
/// await runner.AwaitOnIdleFrame();
/// </code>
/// </example>
/// <code>await OnIdleFrame();</code>
/// </summary>
/// <returns>SignalAwaiter</returns>
SignalAwaiter AwaitOnIdleFrame();

/// <summary>
/// Waits for given signal is emited.
/// <example>
/// <code>
/// 'Waits for signal "mySignal"'
/// await runner.AwaitOnSignal("mySignal");
/// </code>
/// </example>
/// </summary>
/// <param name="signal">The name of signal to wait</param>
/// <returns>SignalAwaiter</returns>
SignalAwaiter AwaitOnSignal(string signal);

/// <summary>
/// Waits for a specific amount of milliseconds.
/// <example>
/// <code>
/// 'Waits for two seconds'
/// await runner.AwaitOnMillis(2000);
/// </code>
/// </example>
/// </summary>
/// <param name="timeMillis">Seconds to wait. 1.0 for one Second</param>
/// <returns>SignalAwaiter</returns>
Task AwaitOnMillis(uint timeMillis);

/// <summary>
/// Access to current running scene
/// </summary>
/// <returns>Node</returns>
Node Scene();

/// <summary>
/// Shows the running scene and moves the window to the foreground.
/// </summary>
void MoveWindowToForeground();

/// <summary>
/// Invokes the method by given name and arguments.
/// </summary>
/// <param name="name">The name of method to invoke</param>
/// <param name="args">The function arguments</param>
/// <returns>The invoced method return value</returns>
/// <exception cref="MissingMethodException"/>
public object Invoke(string name, params object[] args);

/// <summary>
/// Returns the property by given name.
/// </summary>
/// <typeparam name="T">The type of the property</typeparam>
/// <param name="name">The parameter name</param>
/// <returns>Returns the value of property or throws a MissingFieldException</returns>
/// <exception cref="MissingFieldException"/>
public T GetProperty<T>(string name);

/// <summary>
/// Finds the node by given name.
/// </summary>
/// <param name="name">The name of node to find</param>
/// <param name="recursive">Allow recursive search</param>
/// <returns>The node if found or Null</returns>
public Node FindNode(string name, bool recursive = true);
}
}
9 changes: 5 additions & 4 deletions addons/gdUnit3/src/asserts/Comparable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,21 +60,21 @@ public static Result IsEqual<T>(T left, T right, MODE compareMode = MODE.CASE_SE
if (left == null || right == null)
return new Result(false, left, right);

if (object.ReferenceEquals(left, right))
return new Result(true, left, right, r);

var type = left.GetType();
if (type.IsEnum)
return new Result(left.Equals(right), left, right, r);

if (type.IsPrimitive || typeof(string).Equals(type) || left is IEquatable<T>)
if (type.IsPrimitive || typeof(string).Equals(type) || left is IEquatable<T> || left is System.ValueType)
{
//Godot.GD.PrintS("IsPrimitive", type, left, right);
if (left is String && compareMode == MODE.CASE_INSENSITIVE)
return new Result(left.ToString().ToLower().Equals(right.ToString().ToLower()), left, right, r);
return new Result(left.Equals(right), left, right, r);
}

if (object.ReferenceEquals(left, right))
return new Result(true, left, right, r);

if (type.IsArray)
{
var la = left as Array;
Expand Down Expand Up @@ -116,6 +116,7 @@ public static Result IsEqual<T>(T left, T right, MODE compareMode = MODE.CASE_SE
if (!left.GetType().Equals(right.GetType()))
return new Result(false, left, right, r);


// deep compare
foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
{
Expand Down
16 changes: 8 additions & 8 deletions addons/gdUnit3/src/asserts/ExceptionAssert.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Threading.Tasks;

namespace GdUnit3.Asserts
{
Expand All @@ -10,14 +11,13 @@ internal sealed class ExceptionAssert<T> : IExceptionAssert

public ExceptionAssert(Func<T> supplier)
{
try
{
supplier.Invoke();
}
catch (Exception e)
{
Current = e;
}
try { supplier.Invoke(); }
catch (Exception e) { Current = e; }
}

public ExceptionAssert(Exception e)
{
Current = e;
}

public IExceptionAssert IsInstanceOf<ExpectedType>()
Expand Down
Loading