Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
smoogipoo committed Sep 21, 2024
1 parent c99887b commit af33d53
Show file tree
Hide file tree
Showing 20 changed files with 299 additions and 2,292 deletions.
43 changes: 24 additions & 19 deletions osu.Framework.Testing.NUnit/TestScene.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,30 +61,35 @@ protected void AddUntilStep<T>(string? description, ActualValueDelegate<T> actua
});
}, false);

protected void AddAssert<T>(string description, ActualValueDelegate<T> actualValue, Func<IResolveConstraint> constraint, string? extendedDescription = null) => Scheduler.Add(() =>
protected void AddAssert<T>(string description, ActualValueDelegate<T> actualValue, Func<IResolveConstraint> constraint, string? extendedDescription = null)
{
ConstraintResult? lastResult = null;
StackTrace callStack = new StackTrace(1);

StepsContainer.Add(new AssertButton(AddStepsAsSetupSteps, () =>
Scheduler.Add(() =>
{
if (lastResult == null)
return string.Empty;
ConstraintResult? lastResult = null;
var writer = new TextMessageWriter(string.Empty);
lastResult.WriteMessageTo(writer);
return writer.ToString().TrimStart();
})
{
Text = description,
ExtendedDescription = extendedDescription,
CallStack = new StackTrace(1),
Assertion = () =>
StepsContainer.Add(new AssertButton(AddStepsAsSetupSteps, () =>
{
lastResult = constraint().Resolve().ApplyTo(actualValue());
return lastResult.IsSuccess;
}
});
}, false);
if (lastResult == null)
return string.Empty;
var writer = new TextMessageWriter(string.Empty);
lastResult.WriteMessageTo(writer);
return writer.ToString().TrimStart();
})
{
Text = description,
ExtendedDescription = extendedDescription,
CallStack = callStack,
Assertion = () =>
{
lastResult = constraint().Resolve().ApplyTo(actualValue());
return lastResult.IsSuccess;
}
});
}, false);
}

private Task? runTask;
private ITestSceneTestRunner? runner;
Expand Down
2 changes: 0 additions & 2 deletions osu.Framework.Testing.TUnit/ManualInputManagerTestScene.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,12 @@
using osu.Framework.Testing.Input;
using osuTK;
using osuTK.Graphics;
using TUnit.Core.Executors;

namespace osu.Framework.Testing
{
/// <summary>
/// An abstract test case which is tested with manual input management.
/// </summary>
[HookExecutor<TestSceneExecutor>, TestExecutor<TestSceneExecutor>]
public abstract partial class ManualInputManagerTestScene : TestScene

Check failure on line 20 in osu.Framework.Testing.TUnit/ManualInputManagerTestScene.cs

View workflow job for this annotation

GitHub Actions / Test (Linux, ubuntu-latest, Debug, SingleThread)

Use an

Check failure on line 20 in osu.Framework.Testing.TUnit/ManualInputManagerTestScene.cs

View workflow job for this annotation

GitHub Actions / Test (Linux, ubuntu-latest, Release, SingleThread)

Use an

Check failure on line 20 in osu.Framework.Testing.TUnit/ManualInputManagerTestScene.cs

View workflow job for this annotation

GitHub Actions / Test (Linux, ubuntu-latest, Release, MultiThreaded)

Use an

Check failure on line 20 in osu.Framework.Testing.TUnit/ManualInputManagerTestScene.cs

View workflow job for this annotation

GitHub Actions / Test (Windows, windows-latest, Debug, MultiThreaded)

Use an
{
protected override Container<Drawable> Content { get; } = new Container { RelativeSizeAxes = Axes.Both };
Expand Down
72 changes: 45 additions & 27 deletions osu.Framework.Testing.TUnit/TestScene.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,55 +2,73 @@
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Diagnostics;
using System.Threading.Tasks;
using osu.Framework.Development;
using osu.Framework.Extensions;
using osu.Framework.Logging;
using osu.Framework.Testing.Drawables.Steps;
using TUnit.Core.Executors;

namespace osu.Framework.Testing
{
public abstract partial class TestScene : AdhocTestScene
{
/// <summary>
/// Tests any steps and assertions in the constructor of this <see cref="TestScene"/>.
/// This test must run before any other tests, as it relies on <see cref="TestScene.StepsContainer"/> not being cleared and not having any elements.

Check failure on line 17 in osu.Framework.Testing.TUnit/TestScene.cs

View workflow job for this annotation

GitHub Actions / Test (Linux, ubuntu-latest, Debug, SingleThread)

XML comment has cref attribute 'StepsContainer' that could not be resolved

Check failure on line 17 in osu.Framework.Testing.TUnit/TestScene.cs

View workflow job for this annotation

GitHub Actions / Test (Linux, ubuntu-latest, Release, SingleThread)

XML comment has cref attribute 'StepsContainer' that could not be resolved

Check failure on line 17 in osu.Framework.Testing.TUnit/TestScene.cs

View workflow job for this annotation

GitHub Actions / Test (Linux, ubuntu-latest, Release, MultiThreaded)

XML comment has cref attribute 'StepsContainer' that could not be resolved

Check failure on line 17 in osu.Framework.Testing.TUnit/TestScene.cs

View workflow job for this annotation

GitHub Actions / Test (Windows, windows-latest, Debug, MultiThreaded)

XML comment has cref attribute 'StepsContainer' that could not be resolved
/// </summary>
[Test]
public Task TestConstructor() => Task.CompletedTask;

public override void RunAllSteps(Action? onCompletion = null, Action<Exception>? onError = null, Func<StepButton, bool>? stopCondition = null, StepButton? startFromStep = null)
{
Logger.Log($@"🔶 Test: {TestContext.Current?.TestDetails.DisplayName}");
base.RunAllSteps(onCompletion, onError, stopCondition, startFromStep);
}

/// <summary>
/// Tests any steps and assertions in the constructor of this <see cref="TestScene"/>.
/// This test must run before any other tests, as it relies on <see cref="TestScene.StepsContainer"/> not being cleared and not having any elements.
/// </summary>
[Test, HookExecutor<TestSceneExecutor>, TestExecutor<TestSceneExecutor>]
public Task TestConstructor()
protected void AddUntilStep(string? description, Func<Task> assertion) => Scheduler.Add(() =>
{
ThreadSafety.EnsureUpdateThread();
return Task.CompletedTask;
}
Func<(bool status, string message)> func = wrap(assertion);
StepsContainer.Add(new UntilStepButton(() => func().status, AddStepsAsSetupSteps, () => func().message)
{
Text = description ?? "Until",
});
}, false);

[Before(Test), HookExecutor<TestSceneExecutor>, TestExecutor<TestSceneExecutor>]
public new void RunSetUpSteps()
protected void AddAssert(string description, Func<Task> assertion, string? extendedDescription = null)
{
ThreadSafety.EnsureUpdateThread();
base.RunSetUpSteps();
StackTrace callStack = new StackTrace(1);

Scheduler.Add(() =>
{
Func<(bool status, string message)> func = wrap(assertion);
StepsContainer.Add(new AssertButton(AddStepsAsSetupSteps, () => func().message)
{
Text = description,
ExtendedDescription = extendedDescription,
CallStack = callStack,
Assertion = () => func().status
});
}, false);
}

[After(Test), HookExecutor<TestSceneExecutor>, TestExecutor<TestSceneExecutor>]
public new void RunTearDownSteps()
private static Func<(bool status, string message)> wrap(Func<Task> assert) => () =>
{
ThreadSafety.EnsureUpdateThread();
return assertAndWait(assert).GetResultSafely();
base.RunTearDownSteps();
base.RunAfterTest();

if (FrameworkEnvironment.ForceTestGC)
static async Task<(bool status, string message)> assertAndWait(Func<Task> assert)
{
// Force any unobserved exceptions to fire against the current test run.
// Without this they could be delayed until a future test scene is running, making tracking down the cause difficult.
GC.Collect();
GC.WaitForPendingFinalizers();
try
{
await assert().ConfigureAwait(false);
return (true, string.Empty);
}
catch (TUnit.Assertions.Exceptions.AssertionException e)
{
return (false, e.Message);
}
}
}
};
}
}
16 changes: 0 additions & 16 deletions osu.Framework.Testing.TUnit/TestSceneContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,20 @@ internal record TestSceneContext
public readonly AdhocTestScene.TestSceneHost Host;
public readonly ITestSceneTestRunner Runner;

private readonly TestScene testScene;
private readonly Game game;
private readonly Thread thread;

private Exception? hostException;

public TestSceneContext(TestScene testScene)
{
this.testScene = testScene;

Host = new AdhocTestScene.TestSceneHost(testScene, $"{testScene.GetType().Name}-{Guid.NewGuid()}");
Runner = testScene.CreateRunner();

if (Runner is not Game)
throw new InvalidCastException($"The test runner must be a {nameof(Game)}.");

game = (Game)Runner;
game.OnLoadComplete += _ => game.Add(testScene);

thread = new Thread(runHost);
}

Expand Down Expand Up @@ -99,16 +94,5 @@ public Task CheckForErrors()

return Task.CompletedTask;
}

public async Task RunOnUpdateThread(Func<Task> action)
{
await Task.Delay(TimeSpan.FromMilliseconds(testScene.TimePerAction)).ConfigureAwait(false);

SynchronizationContext.SetSynchronizationContext(Host.UpdateThread.SynchronizationContext);
await Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext()).Unwrap().ConfigureAwait(false);

await CheckForErrors().ConfigureAwait(false);
await Task.Delay(TimeSpan.FromMilliseconds(testScene.TimePerAction)).ConfigureAwait(false);
}
}
}
101 changes: 0 additions & 101 deletions osu.Framework.Testing.TUnit/TestSceneExecutor.cs

This file was deleted.

40 changes: 34 additions & 6 deletions osu.Framework.Testing.TUnit/TestSceneRunner.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
Expand All @@ -27,23 +28,50 @@ public static async Task PrepareGameHost(TestContext context)
return;

var hostContext = new TestSceneContext(testScene);

host_contexts[context] = hostContext;
await hostContext.Start().ConfigureAwait(false);

testScene.RunSetUpSteps();
await hostContext.CheckForErrors().ConfigureAwait(false);
}

[AfterEvery(Test)]
public static async Task ShutdownGameHost(TestContext context)
{
if (context.TestDetails.ClassInstance is not TestScene)
if (context.TestDetails.ClassInstance is not TestScene testScene)
return;

var hostContext = host_contexts[context];
host_contexts.Remove(context, out _);

await hostContext.CheckForErrors().ConfigureAwait(false);
await hostContext.Stop().ConfigureAwait(false);
}
try
{
testScene.RunTearDownSteps();
await hostContext.CheckForErrors().ConfigureAwait(false);

hostContext.Runner.RunTestBlocking(testScene);
await hostContext.CheckForErrors().ConfigureAwait(false);

public static TestSceneContext? GetCurrentContext() => host_contexts.GetValueOrDefault(TestContext.Current!);
if (FrameworkEnvironment.ForceTestGC)
{
// Force any unobserved exceptions to fire against the current test run.
// Without this they could be delayed until a future test scene is running, making tracking down the cause difficult.
GC.Collect();
GC.WaitForPendingFinalizers();
}

testScene.RunAfterTest();
await hostContext.CheckForErrors().ConfigureAwait(false);
}
catch (AssertionException exc)
{
throw new TUnit.Assertions.Exceptions.AssertionException(null, exc);
}
finally
{
await hostContext.Stop().ConfigureAwait(false);
host_contexts.Remove(context, out _);
}
}
}
}
Loading

0 comments on commit af33d53

Please sign in to comment.