Skip to content
2,688 changes: 2,688 additions & 0 deletions changes.patch

Large diffs are not rendered by default.

25 changes: 19 additions & 6 deletions src/Adapter/MSTest.TestAdapter/Execution/TestAssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using UnitTestOutcome = Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestOutcome;
Expand Down Expand Up @@ -107,6 +106,8 @@ internal set
/// </summary>
internal Assembly Assembly { get; }

internal ExecutionContext? ExecutionContext { get; set; }

/// <summary>
/// Runs assembly initialize method.
/// </summary>
Expand Down Expand Up @@ -143,11 +144,17 @@ public void RunAssemblyInitialize(TestContext testContext)
try
{
AssemblyInitializationException = FixtureMethodRunner.RunWithTimeoutAndCancellation(
() => AssemblyInitializeMethod.InvokeAsSynchronousTask(null, testContext),
() =>
{
AssemblyInitializeMethod.InvokeAsSynchronousTask(null, testContext);
// **After** we have executed the assembly initialize, we save the current context.
// This context will contain async locals set by the assembly initialize method.
ExecutionContext = ExecutionContext.Capture();
},
testContext.CancellationTokenSource,
AssemblyInitializeMethodTimeoutMilliseconds,
AssemblyInitializeMethod,
new AssemblyExecutionContextScope(isCleanup: false),
executionContext: ExecutionContext,
Resource.AssemblyInitializeWasCancelled,
Resource.AssemblyInitializeTimedOut);
}
Expand Down Expand Up @@ -216,11 +223,15 @@ public void RunAssemblyInitialize(TestContext testContext)
try
{
AssemblyCleanupException = FixtureMethodRunner.RunWithTimeoutAndCancellation(
() => AssemblyCleanupMethod.InvokeAsSynchronousTask(null),
() =>
{
AssemblyCleanupMethod.InvokeAsSynchronousTask(null);
ExecutionContext = ExecutionContext.Capture();
},
new CancellationTokenSource(),
AssemblyCleanupMethodTimeoutMilliseconds,
AssemblyCleanupMethod,
new AssemblyExecutionContextScope(isCleanup: true),
ExecutionContext,
Resource.AssemblyCleanupWasCancelled,
Resource.AssemblyCleanupTimedOut);
}
Expand Down Expand Up @@ -282,11 +293,13 @@ public void RunAssemblyInitialize(TestContext testContext)
{
AssemblyCleanupMethod.InvokeAsSynchronousTask(null, testContext);
}

ExecutionContext = ExecutionContext.Capture();
},
testContext.CancellationTokenSource,
AssemblyCleanupMethodTimeoutMilliseconds,
AssemblyCleanupMethod,
new AssemblyExecutionContextScope(isCleanup: true),
ExecutionContext,
Resource.AssemblyCleanupWasCancelled,
Resource.AssemblyCleanupTimedOut);
}
Expand Down
30 changes: 21 additions & 9 deletions src/Adapter/MSTest.TestAdapter/Execution/TestClassInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestTools.UnitTesting;

Expand Down Expand Up @@ -234,6 +233,8 @@ internal set
}
}

internal ExecutionContext? ExecutionContext { get; set; }

/// <summary>
/// Gets a queue of test initialize methods to call for this type.
/// </summary>
Expand Down Expand Up @@ -507,11 +508,17 @@ TestResult DoRun()
}

return FixtureMethodRunner.RunWithTimeoutAndCancellation(
() => methodInfo.InvokeAsSynchronousTask(null, testContext),
() =>
{
methodInfo.InvokeAsSynchronousTask(null, testContext);
// **After** we have executed the class initialize, we save the current context.
// This context will contain async locals set by the class initialize method.
ExecutionContext = ExecutionContext.Capture();
},
testContext.CancellationTokenSource,
timeout,
methodInfo,
new ClassExecutionContextScope(ClassType),
ExecutionContext ?? Parent?.ExecutionContext,
Resource.ClassInitializeWasCancelled,
Resource.ClassInitializeTimedOut);
}
Expand Down Expand Up @@ -549,12 +556,12 @@ TestResult DoRun()
try
{
classCleanupMethod = ClassCleanupMethod;
ClassCleanupException = classCleanupMethod is not null ? InvokeCleanupMethod(classCleanupMethod, BaseClassCleanupMethods.Count, null!) : null;
ClassCleanupException = classCleanupMethod is not null ? InvokeCleanupMethod(classCleanupMethod, null!) : null;
var baseClassCleanupQueue = new Queue<MethodInfo>(BaseClassCleanupMethods);
while (baseClassCleanupQueue.Count > 0 && ClassCleanupException is null)
{
classCleanupMethod = baseClassCleanupQueue.Dequeue();
ClassCleanupException = InvokeCleanupMethod(classCleanupMethod, baseClassCleanupQueue.Count, null!);
ClassCleanupException = InvokeCleanupMethod(classCleanupMethod, null!);
}

IsClassCleanupExecuted = ClassCleanupException is null;
Expand Down Expand Up @@ -634,7 +641,7 @@ TestResult DoRun()
{
if (!classCleanupMethod.DeclaringType!.IsIgnored(out _))
{
ClassCleanupException = InvokeCleanupMethod(classCleanupMethod, remainingCleanupCount: BaseClassCleanupMethods.Count, testContext);
ClassCleanupException = InvokeCleanupMethod(classCleanupMethod, testContext);
}
}

Expand All @@ -645,7 +652,7 @@ TestResult DoRun()
classCleanupMethod = BaseClassCleanupMethods[i];
if (!classCleanupMethod.DeclaringType!.IsIgnored(out _))
{
ClassCleanupException = InvokeCleanupMethod(classCleanupMethod, remainingCleanupCount: BaseClassCleanupMethods.Count - 1 - i, testContext);
ClassCleanupException = InvokeCleanupMethod(classCleanupMethod, testContext);
if (ClassCleanupException is not null)
{
break;
Expand Down Expand Up @@ -798,7 +805,7 @@ void DoRun()
}
}

private TestFailedException? InvokeCleanupMethod(MethodInfo methodInfo, int remainingCleanupCount, TestContext testContext)
private TestFailedException? InvokeCleanupMethod(MethodInfo methodInfo, TestContext testContext)
{
TimeoutInfo? timeout = null;
if (ClassCleanupMethodTimeoutMilliseconds.TryGetValue(methodInfo, out TimeoutInfo localTimeout))
Expand All @@ -817,11 +824,16 @@ void DoRun()
{
methodInfo.InvokeAsSynchronousTask(null, testContext);
}

// **After** we have executed the class cleanup, we save the current context.
// This context will contain async locals set by the current class cleanup method.
// This is essential to propagate async locals between multiple class cleanup methods.
ExecutionContext = ExecutionContext.Capture();
},
testContext.CancellationTokenSource,
timeout,
methodInfo,
new ClassExecutionContextScope(ClassType, remainingCleanupCount),
ExecutionContext ?? Parent.ExecutionContext,
Resource.ClassCleanupWasCancelled,
Resource.ClassCleanupTimedOut);
}
Expand Down
Loading