Skip to content

Commit

Permalink
Async Test continuations now also run on the same thread when SIngleT…
Browse files Browse the repository at this point in the history
…hreaded is used.
  • Loading branch information
manfred-brands committed Dec 2, 2024
1 parent f597da8 commit 5a4f35c
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 21 deletions.
16 changes: 11 additions & 5 deletions src/NUnitFramework/framework/Internal/AsyncToSyncAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,19 @@ public static bool IsAsyncOperation(Delegate @delegate)
}

public static object? Await(Func<object?> invoke)
=> Await<object?>(invoke);
=> Await<object?>(default(TestExecutionContext), invoke);

public static TResult? Await<TResult>(Func<object?> invoke)
=> Await<TResult>(default(TestExecutionContext), invoke);

public static object? Await(TestExecutionContext? context, Func<object?> invoke)
=> Await<object?>(context, invoke);

public static TResult? Await<TResult>(TestExecutionContext? context, Func<object?> invoke)
{
Guard.ArgumentNotNull(invoke, nameof(invoke));

using (InitializeExecutionEnvironment())
using (InitializeExecutionEnvironment(context))
{
var awaiter = AwaitAdapter.FromAwaitable(invoke.Invoke());

Expand All @@ -41,10 +47,10 @@ public static bool IsAsyncOperation(Delegate @delegate)
return (TResult?)awaiter.GetResult();
}
}

private static IDisposable? InitializeExecutionEnvironment()
private static IDisposable? InitializeExecutionEnvironment(TestExecutionContext? executionContext)
{
if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
if (executionContext?.IsSingleThreaded == true ||
Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
{
var context = SynchronizationContext.Current;
if (context is null || context.GetType() == typeof(SynchronizationContext))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ private void RunSetUpOrTearDownMethod(TestExecutionContext context, IMethodInfo
var methodInfo = MethodInfoCache.Get(method);

if (methodInfo.IsAsyncOperation)
AsyncToSyncAdapter.Await(() => InvokeMethod(method, context));
AsyncToSyncAdapter.Await(context, () => InvokeMethod(method, context));
else
InvokeMethod(method, context);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public override TestResult Execute(TestExecutionContext context)

if (methodInfo.IsAsyncOperation)
{
return AsyncToSyncAdapter.Await(() => InvokeTestMethod(context, lastParameterAcceptsCancellationToken));
return AsyncToSyncAdapter.Await(context, () => InvokeTestMethod(context, lastParameterAcceptsCancellationToken));
}

return InvokeTestMethod(context, lastParameterAcceptsCancellationToken);
Expand Down
41 changes: 27 additions & 14 deletions src/NUnitFramework/tests/Attributes/SingleThreadedFixtureTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using NUnit.Framework.Interfaces;
using NUnit.TestData;
using NUnit.Framework.Tests.TestUtilities;
using System.Threading.Tasks;

namespace NUnit.Framework.Tests.Attributes
{
Expand Down Expand Up @@ -37,20 +38,6 @@ public void TestWithDifferentApartmentIsInvalid()
CheckTestIsInvalid<SingleThreadedFixture_TestWithDifferentApartment>("may not specify a different apartment");
}

#if NETCOREAPP
[Platform(Include = "Win, Mono")]
#endif
[SingleThreaded]
public class SingleThreadedFixtureWithApartmentStateTests : ThreadingTests
{
[Test, Apartment(ApartmentState.MTA)]
public void TestWithSameApartmentIsValid()
{
Assert.That(Thread.CurrentThread, Is.EqualTo(ParentThread));
Assert.That(Thread.CurrentThread.GetApartmentState(), Is.EqualTo(ApartmentState.MTA));
}
}

private void CheckTestIsInvalid<TFixture>(string reason)
{
var result = TestBuilder.RunTestFixture(typeof(TFixture));
Expand All @@ -60,6 +47,20 @@ private void CheckTestIsInvalid<TFixture>(string reason)
}
}

#if NETCOREAPP
[Platform(Include = "Win, Mono")]
#endif
[SingleThreaded]
public class SingleThreadedFixtureWithApartmentStateTests : ThreadingTests
{
[Test, Apartment(ApartmentState.MTA)]
public void TestWithSameApartmentIsValid()
{
Assert.That(Thread.CurrentThread, Is.EqualTo(ParentThread));
Assert.That(Thread.CurrentThread.GetApartmentState(), Is.EqualTo(ApartmentState.MTA));
}
}

#if NETCOREAPP
[Platform(Include = "Win")]
#endif
Expand All @@ -80,4 +81,16 @@ public void TestWithSameApartmentStateIsValid()
Assert.That(GetApartmentState(Thread.CurrentThread), Is.EqualTo(ApartmentState.STA));
}
}

[SingleThreaded]
public class SingleThreadedFixtureWithAsyncTests : ThreadingTests
{
[Test]
public async Task AsyncTestRunsOnSameThread()
{
Assert.That(Thread.CurrentThread, Is.EqualTo(ParentThread));
await Task.Yield();
Assert.That(Thread.CurrentThread, Is.EqualTo(ParentThread));
}
}
}

0 comments on commit 5a4f35c

Please sign in to comment.