Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

<ItemGroup>
<ProjectReference Include="..\TUnit.Core.SourceGenerator\TUnit.Core.SourceGenerator.csproj" />
<ProjectReference Include="..\TUnit.Core\TUnit.Core.csproj" ReferenceOutputAssembly="false" />
<ProjectReference Include="..\TUnit.Core\TUnit.Core.csproj" />
<ProjectReference Include="..\TUnit.TestProject.Library\TUnit.TestProject.Library.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.SourceGenerators.Testing" />
Expand All @@ -18,9 +19,6 @@
<None Include="..\TUnit.Core\bin\$(Configuration)\netstandard2.0\TUnit.Core.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\TUnit.TestProject.Library\bin\$(Configuration)\$(LibraryTargetFramework)\TUnit.TestProject.Library.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

<Import Project="..\TestProject.targets" />
Expand Down
63 changes: 63 additions & 0 deletions TUnit.Core/AfterTestContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using TUnit.Core.Enums;

namespace TUnit.Core;

public class AfterTestContext
{
internal readonly DiscoveredTest DiscoveredTest;

internal AfterTestContext(DiscoveredTest discoveredTest)
{
DiscoveredTest = discoveredTest;
}

public TestContext TestContext => DiscoveredTest.TestContext;
public TestDetails TestDetails => TestContext.TestDetails;

public void OverrideResult(Status status, string reason)
{
var testResult = TestContext.Result;

if (testResult is null)
{
throw new InvalidOperationException("There is no test result to override.");
}

OverrideResult(testResult with
{
Status = status,
IsOverridden = true,
OverrideReason = reason
});

if(status == Status.Skipped)
{
TestContext.SkipReason = reason;
}
}

public void OverrideResult(Exception exception, string reason)
{
var testResult = TestContext.Result;

if (testResult is null)
{
throw new InvalidOperationException("There is no test result to override.");
}

OverrideResult(testResult with
{
Status = Status.Failed,
Exception = exception,
IsOverridden = true,
OverrideReason = reason,
});
}

private void OverrideResult(TestResult result)
{
TestContext.Result = result;
}

public static implicit operator TestContext(AfterTestContext afterTestContext) => afterTestContext.TestContext;
}
2 changes: 1 addition & 1 deletion TUnit.Core/Attributes/TestData/ClassDataSources.cs
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ private static object Create([DynamicallyAccessedMembers(DynamicallyAccessedMemb
}
}

public async Task OnTestEnd<T>(TestContext context, T item) where T : new()
public async Task OnTestEnd<T>(AfterTestContext context, T item) where T : new()
{
if (item is ITestEndEventReceiver testEndEventReceiver)
{
Expand Down
2 changes: 2 additions & 0 deletions TUnit.Core/BeforeTestContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,6 @@ public void AddLinkedCancellationToken(CancellationToken cancellationToken)
}

public void AddAsyncLocalValues() => TestContext.AddAsyncLocalValues();

public static implicit operator TestContext(BeforeTestContext beforeTestContext) => beforeTestContext.TestContext;
}
2 changes: 1 addition & 1 deletion TUnit.Core/Interfaces/ITestEndEventReceiver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@

public interface ITestEndEventReceiver : IEventReceiver
{
ValueTask OnTestEnd(TestContext testContext);
ValueTask OnTestEnd(AfterTestContext afterTestContext);
}
5 changes: 4 additions & 1 deletion TUnit.Core/TestContext.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
namespace TUnit.Core;
using System.Diagnostics;

namespace TUnit.Core;

/// <summary>
/// Represents the context for a test.
/// </summary>
[DebuggerDisplay("{TestDetails.TestClass.Name}.{TestDetails.TestName}")]
public partial class TestContext : Context
{
private readonly IServiceProvider _serviceProvider;
Expand Down
4 changes: 2 additions & 2 deletions TUnit.Core/TestContextEvents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public record TestContextEvents :
public AsyncEvent<TestRegisteredContext>? OnTestRegistered { get; set; }
public AsyncEvent<TestContext>? OnInitialize { get; set; }
public AsyncEvent<BeforeTestContext>? OnTestStart { get; set; }
public AsyncEvent<TestContext>? OnTestEnd { get; set; }
public AsyncEvent<AfterTestContext>? OnTestEnd { get; set; }
public AsyncEvent<TestContext>? OnTestSkipped { get; set; }
public AsyncEvent<(ClassHookContext, TestContext)>? OnLastTestInClass { get; set; }
public AsyncEvent<(AssemblyHookContext, TestContext)>? OnLastTestInAssembly { get; set; }
Expand All @@ -40,7 +40,7 @@ ValueTask ITestStartEventReceiver.OnTestStart(BeforeTestContext beforeTestContex
return OnTestStart?.InvokeAsync(this, beforeTestContext) ?? default;
}

ValueTask ITestEndEventReceiver.OnTestEnd(TestContext testContext)
ValueTask ITestEndEventReceiver.OnTestEnd(AfterTestContext testContext)
{
return OnTestEnd?.InvokeAsync(this, testContext) ?? default;
}
Expand Down
3 changes: 3 additions & 0 deletions TUnit.Core/TestResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,7 @@ public record TestResult
/// </summary>
[JsonIgnore]
internal TestContext? TestContext { get; init; }

public string? OverrideReason { get; set; }
public bool IsOverridden { get; set; }
}
21 changes: 21 additions & 0 deletions TUnit.Engine.Tests/OverrideResultsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Shouldly;
using TUnit.Engine.Tests.Enums;

namespace TUnit.Engine.Tests;

public class OverrideResultsTests(TestMode testMode) : InvokableTestBase(testMode)
{
[Test]
public async Task Test()
{
await RunTestsWithFilter(
"/*/*/OverrideResultsTests/*",
[
result => result.ResultSummary.Outcome.ShouldBe("Completed"),
result => result.ResultSummary.Counters.Total.ShouldBe(1),
result => result.ResultSummary.Counters.Passed.ShouldBe(1),
result => result.ResultSummary.Counters.Failed.ShouldBe(0),
result => result.ResultSummary.Counters.NotExecuted.ShouldBe(0)
]);
}
}
30 changes: 9 additions & 21 deletions TUnit.Engine/Services/SingleTestExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,17 @@ private async ValueTask ExecuteTestInternalAsync(DiscoveredTest test, ITestExecu
await logger.LogInformationAsync($"Skipping {testContext.GetClassTypeName()}.{testContext.GetTestDisplayName()}...");

testContext.SetResult(skipTestException);

await messageBus.Skipped(testContext, skipTestException.Reason);
}
catch (Exception e)
{
await logger.LogDebugAsync($"Error in test {testContext.TestDetails.TestClass.Type.FullName}.{testContext.GetTestDisplayName()}: {e}");
testContext.SetResult(e);
throw;
if (testContext.Result is null
|| testContext.Result?.IsOverridden is false
|| testContext.Result?.Status is Status.Failed or Status.Cancelled)
{
await logger.LogDebugAsync($"Error in test {testContext.TestDetails.TestClass.Type.FullName}.{testContext.GetTestDisplayName()}: {e}");
testContext.SetResult(e);
throw;
}
}
finally
{
Expand All @@ -134,29 +137,14 @@ private async ValueTask ExecuteTestInternalAsync(DiscoveredTest test, ITestExecu
Status.Passed => messageBus.Passed(test.TestContext, start.GetValueOrDefault()),
Status.Failed => messageBus.Failed(test.TestContext, result.Exception!, start.GetValueOrDefault()),
Status.Cancelled => messageBus.Cancelled(test.TestContext, start.GetValueOrDefault()),
Status.Skipped => messageBus.Skipped(test.TestContext, test.TestContext.SkipReason!),
_ => default,
};

await task;
}
}

private bool IsCancelled(Exception ex)
{
if (ex is TestRunCanceledException)
{
return true;
}

if (ex is TaskCanceledException or OperationCanceledException
&& engineCancellationToken.Token.IsCancellationRequested)
{
return true;
}

return false;
}

private async Task RunFirstTestEventReceivers(TestContext testContext)
{
ExecutionContextHelper.RestoreContext(await RunFirstTestInSessionEventReceivers(testContext));
Expand Down
2 changes: 1 addition & 1 deletion TUnit.Engine/Services/TestInvoker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ await Timings.Record($"After(Test): {executableHook.Name}", testContext, () =>

foreach (var testEndEventsObject in testContext.GetTestEndEventObjects())
{
await RunHelpers.RunValueTaskSafelyAsync(() => testEndEventsObject.OnTestEnd(testContext),
await RunHelpers.RunValueTaskSafelyAsync(() => testEndEventsObject.OnTestEnd(new AfterTestContext(testContext.InternalDiscoveredTest)),
cleanUpExceptions);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ namespace TUnit.Core
{
public AfterEveryAttribute(TUnit.Core.HookType hookType, [System.Runtime.CompilerServices.CallerFilePath] string file = "", [System.Runtime.CompilerServices.CallerLineNumber] int line = 0) { }
}
public class AfterTestContext
{
public TUnit.Core.TestContext TestContext { get; }
public TUnit.Core.TestDetails TestDetails { get; }
public void OverrideResult(System.Exception exception, string reason) { }
public void OverrideResult(TUnit.Core.Enums.Status status, string reason) { }
public static TUnit.Core.TestContext op_Implicit(TUnit.Core.AfterTestContext afterTestContext) { }
}
public abstract class ArgumentDisplayFormatter
{
protected ArgumentDisplayFormatter() { }
Expand Down Expand Up @@ -93,6 +101,7 @@ namespace TUnit.Core
public void AddLinkedCancellationToken(System.Threading.CancellationToken cancellationToken) { }
public void SetHookExecutor(TUnit.Core.Interfaces.IHookExecutor hookExecutor) { }
public void SetTestExecutor(TUnit.Core.Interfaces.ITestExecutor testExecutor) { }
public static TUnit.Core.TestContext op_Implicit(TUnit.Core.BeforeTestContext beforeTestContext) { }
}
public class BeforeTestDiscoveryContext : TUnit.Core.Context
{
Expand Down Expand Up @@ -768,6 +777,7 @@ namespace TUnit.Core
public TestBuilderContextAccessor(TUnit.Core.TestBuilderContext context) { }
public TUnit.Core.TestBuilderContext Current { get; set; }
}
[System.Diagnostics.DebuggerDisplay("{TestDetails.TestClass.Name}.{TestDetails.TestName}")]
public class TestContext : TUnit.Core.Context
{
public readonly object Lock;
Expand Down Expand Up @@ -797,7 +807,7 @@ namespace TUnit.Core
public TUnit.Core.AsyncEvent<System.ValueTuple<TUnit.Core.AssemblyHookContext, TUnit.Core.TestContext>>? OnLastTestInAssembly { get; set; }
public TUnit.Core.AsyncEvent<System.ValueTuple<TUnit.Core.ClassHookContext, TUnit.Core.TestContext>>? OnLastTestInClass { get; set; }
public TUnit.Core.AsyncEvent<System.ValueTuple<TUnit.Core.TestSessionContext, TUnit.Core.TestContext>>? OnLastTestInTestSession { get; set; }
public TUnit.Core.AsyncEvent<TUnit.Core.TestContext>? OnTestEnd { get; set; }
public TUnit.Core.AsyncEvent<TUnit.Core.AfterTestContext>? OnTestEnd { get; set; }
public TUnit.Core.AsyncEvent<TUnit.Core.TestRegisteredContext>? OnTestRegistered { get; set; }
[System.Runtime.CompilerServices.TupleElementNames(new string[] {
null,
Expand Down Expand Up @@ -908,7 +918,9 @@ namespace TUnit.Core
public required System.TimeSpan? Duration { get; init; }
public required System.DateTimeOffset? End { get; init; }
public required System.Exception? Exception { get; init; }
public bool IsOverridden { get; set; }
public string? Output { get; }
public string? OverrideReason { get; set; }
public required System.DateTimeOffset? Start { get; init; }
public required TUnit.Core.Enums.Status Status { get; init; }
}
Expand Down Expand Up @@ -1392,7 +1404,7 @@ namespace TUnit.Core.Interfaces
}
public interface ITestEndEventReceiver : TUnit.Core.Interfaces.IEventReceiver
{
System.Threading.Tasks.ValueTask OnTestEnd(TUnit.Core.TestContext testContext);
System.Threading.Tasks.ValueTask OnTestEnd(TUnit.Core.AfterTestContext afterTestContext);
}
public interface ITestExecutor
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ namespace TUnit.Core
{
public AfterEveryAttribute(TUnit.Core.HookType hookType, [System.Runtime.CompilerServices.CallerFilePath] string file = "", [System.Runtime.CompilerServices.CallerLineNumber] int line = 0) { }
}
public class AfterTestContext
{
public TUnit.Core.TestContext TestContext { get; }
public TUnit.Core.TestDetails TestDetails { get; }
public void OverrideResult(System.Exception exception, string reason) { }
public void OverrideResult(TUnit.Core.Enums.Status status, string reason) { }
public static TUnit.Core.TestContext op_Implicit(TUnit.Core.AfterTestContext afterTestContext) { }
}
public abstract class ArgumentDisplayFormatter
{
protected ArgumentDisplayFormatter() { }
Expand Down Expand Up @@ -93,6 +101,7 @@ namespace TUnit.Core
public void AddLinkedCancellationToken(System.Threading.CancellationToken cancellationToken) { }
public void SetHookExecutor(TUnit.Core.Interfaces.IHookExecutor hookExecutor) { }
public void SetTestExecutor(TUnit.Core.Interfaces.ITestExecutor testExecutor) { }
public static TUnit.Core.TestContext op_Implicit(TUnit.Core.BeforeTestContext beforeTestContext) { }
}
public class BeforeTestDiscoveryContext : TUnit.Core.Context
{
Expand Down Expand Up @@ -814,6 +823,7 @@ namespace TUnit.Core
public TestBuilderContextAccessor(TUnit.Core.TestBuilderContext context) { }
public TUnit.Core.TestBuilderContext Current { get; set; }
}
[System.Diagnostics.DebuggerDisplay("{TestDetails.TestClass.Name}.{TestDetails.TestName}")]
public class TestContext : TUnit.Core.Context
{
public readonly object Lock;
Expand Down Expand Up @@ -844,7 +854,7 @@ namespace TUnit.Core
public TUnit.Core.AsyncEvent<System.ValueTuple<TUnit.Core.AssemblyHookContext, TUnit.Core.TestContext>>? OnLastTestInAssembly { get; set; }
public TUnit.Core.AsyncEvent<System.ValueTuple<TUnit.Core.ClassHookContext, TUnit.Core.TestContext>>? OnLastTestInClass { get; set; }
public TUnit.Core.AsyncEvent<System.ValueTuple<TUnit.Core.TestSessionContext, TUnit.Core.TestContext>>? OnLastTestInTestSession { get; set; }
public TUnit.Core.AsyncEvent<TUnit.Core.TestContext>? OnTestEnd { get; set; }
public TUnit.Core.AsyncEvent<TUnit.Core.AfterTestContext>? OnTestEnd { get; set; }
public TUnit.Core.AsyncEvent<TUnit.Core.TestRegisteredContext>? OnTestRegistered { get; set; }
[System.Runtime.CompilerServices.TupleElementNames(new string[] {
null,
Expand Down Expand Up @@ -957,7 +967,9 @@ namespace TUnit.Core
public required System.TimeSpan? Duration { get; init; }
public required System.DateTimeOffset? End { get; init; }
public required System.Exception? Exception { get; init; }
public bool IsOverridden { get; set; }
public string? Output { get; }
public string? OverrideReason { get; set; }
public required System.DateTimeOffset? Start { get; init; }
public required TUnit.Core.Enums.Status Status { get; init; }
}
Expand Down Expand Up @@ -1455,7 +1467,7 @@ namespace TUnit.Core.Interfaces
}
public interface ITestEndEventReceiver : TUnit.Core.Interfaces.IEventReceiver
{
System.Threading.Tasks.ValueTask OnTestEnd(TUnit.Core.TestContext testContext);
System.Threading.Tasks.ValueTask OnTestEnd(TUnit.Core.AfterTestContext afterTestContext);
}
public interface ITestExecutor
{
Expand Down
Loading
Loading