committed by
GitHub
23 changed files with 552 additions and 379 deletions
@ -0,0 +1,76 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<!-- https://learn.microsoft.com/dotnet/fundamentals/package-validation/diagnostic-ids --> |
||||
|
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> |
||||
|
<Suppression> |
||||
|
<DiagnosticId>CP0001</DiagnosticId> |
||||
|
<Target>T:Avalonia.Headless.XUnit.AvaloniaTestFrameworkTypeDiscoverer</Target> |
||||
|
<Left>baseline/Avalonia.Headless.XUnit/lib/net10.0/Avalonia.Headless.XUnit.dll</Left> |
||||
|
<Right>current/Avalonia.Headless.XUnit/lib/net10.0/Avalonia.Headless.XUnit.dll</Right> |
||||
|
</Suppression> |
||||
|
<Suppression> |
||||
|
<DiagnosticId>CP0001</DiagnosticId> |
||||
|
<Target>T:Avalonia.Headless.XUnit.AvaloniaUIFactDiscoverer</Target> |
||||
|
<Left>baseline/Avalonia.Headless.XUnit/lib/net10.0/Avalonia.Headless.XUnit.dll</Left> |
||||
|
<Right>current/Avalonia.Headless.XUnit/lib/net10.0/Avalonia.Headless.XUnit.dll</Right> |
||||
|
</Suppression> |
||||
|
<Suppression> |
||||
|
<DiagnosticId>CP0001</DiagnosticId> |
||||
|
<Target>T:Avalonia.Headless.XUnit.AvaloniaTestFrameworkTypeDiscoverer</Target> |
||||
|
<Left>baseline/Avalonia.Headless.XUnit/lib/net8.0/Avalonia.Headless.XUnit.dll</Left> |
||||
|
<Right>current/Avalonia.Headless.XUnit/lib/net8.0/Avalonia.Headless.XUnit.dll</Right> |
||||
|
</Suppression> |
||||
|
<Suppression> |
||||
|
<DiagnosticId>CP0001</DiagnosticId> |
||||
|
<Target>T:Avalonia.Headless.XUnit.AvaloniaUIFactDiscoverer</Target> |
||||
|
<Left>baseline/Avalonia.Headless.XUnit/lib/net8.0/Avalonia.Headless.XUnit.dll</Left> |
||||
|
<Right>current/Avalonia.Headless.XUnit/lib/net8.0/Avalonia.Headless.XUnit.dll</Right> |
||||
|
</Suppression> |
||||
|
<Suppression> |
||||
|
<DiagnosticId>CP0002</DiagnosticId> |
||||
|
<Target>M:Avalonia.Headless.XUnit.AvaloniaFactAttribute.#ctor</Target> |
||||
|
<Left>baseline/Avalonia.Headless.XUnit/lib/net10.0/Avalonia.Headless.XUnit.dll</Left> |
||||
|
<Right>current/Avalonia.Headless.XUnit/lib/net10.0/Avalonia.Headless.XUnit.dll</Right> |
||||
|
</Suppression> |
||||
|
<Suppression> |
||||
|
<DiagnosticId>CP0002</DiagnosticId> |
||||
|
<Target>M:Avalonia.Headless.XUnit.AvaloniaTheoryDiscoverer.#ctor(Xunit.Abstractions.IMessageSink)</Target> |
||||
|
<Left>baseline/Avalonia.Headless.XUnit/lib/net10.0/Avalonia.Headless.XUnit.dll</Left> |
||||
|
<Right>current/Avalonia.Headless.XUnit/lib/net10.0/Avalonia.Headless.XUnit.dll</Right> |
||||
|
</Suppression> |
||||
|
<Suppression> |
||||
|
<DiagnosticId>CP0002</DiagnosticId> |
||||
|
<Target>M:Avalonia.Headless.XUnit.AvaloniaFactAttribute.#ctor</Target> |
||||
|
<Left>baseline/Avalonia.Headless.XUnit/lib/net8.0/Avalonia.Headless.XUnit.dll</Left> |
||||
|
<Right>current/Avalonia.Headless.XUnit/lib/net8.0/Avalonia.Headless.XUnit.dll</Right> |
||||
|
</Suppression> |
||||
|
<Suppression> |
||||
|
<DiagnosticId>CP0002</DiagnosticId> |
||||
|
<Target>M:Avalonia.Headless.XUnit.AvaloniaTheoryDiscoverer.#ctor(Xunit.Abstractions.IMessageSink)</Target> |
||||
|
<Left>baseline/Avalonia.Headless.XUnit/lib/net8.0/Avalonia.Headless.XUnit.dll</Left> |
||||
|
<Right>current/Avalonia.Headless.XUnit/lib/net8.0/Avalonia.Headless.XUnit.dll</Right> |
||||
|
</Suppression> |
||||
|
<Suppression> |
||||
|
<DiagnosticId>CP0007</DiagnosticId> |
||||
|
<Target>T:Avalonia.Headless.XUnit.AvaloniaTheoryDiscoverer</Target> |
||||
|
<Left>baseline/Avalonia.Headless.XUnit/lib/net10.0/Avalonia.Headless.XUnit.dll</Left> |
||||
|
<Right>current/Avalonia.Headless.XUnit/lib/net10.0/Avalonia.Headless.XUnit.dll</Right> |
||||
|
</Suppression> |
||||
|
<Suppression> |
||||
|
<DiagnosticId>CP0007</DiagnosticId> |
||||
|
<Target>T:Avalonia.Headless.XUnit.AvaloniaTheoryDiscoverer</Target> |
||||
|
<Left>baseline/Avalonia.Headless.XUnit/lib/net8.0/Avalonia.Headless.XUnit.dll</Left> |
||||
|
<Right>current/Avalonia.Headless.XUnit/lib/net8.0/Avalonia.Headless.XUnit.dll</Right> |
||||
|
</Suppression> |
||||
|
<Suppression> |
||||
|
<DiagnosticId>CP0008</DiagnosticId> |
||||
|
<Target>T:Avalonia.Headless.XUnit.AvaloniaTestFrameworkAttribute</Target> |
||||
|
<Left>baseline/Avalonia.Headless.XUnit/lib/net10.0/Avalonia.Headless.XUnit.dll</Left> |
||||
|
<Right>current/Avalonia.Headless.XUnit/lib/net10.0/Avalonia.Headless.XUnit.dll</Right> |
||||
|
</Suppression> |
||||
|
<Suppression> |
||||
|
<DiagnosticId>CP0008</DiagnosticId> |
||||
|
<Target>T:Avalonia.Headless.XUnit.AvaloniaTestFrameworkAttribute</Target> |
||||
|
<Left>baseline/Avalonia.Headless.XUnit/lib/net8.0/Avalonia.Headless.XUnit.dll</Left> |
||||
|
<Right>current/Avalonia.Headless.XUnit/lib/net8.0/Avalonia.Headless.XUnit.dll</Right> |
||||
|
</Suppression> |
||||
|
</Suppressions> |
||||
@ -1,14 +1,13 @@ |
|||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
<ItemGroup Condition="'$(IsXUnit2)'!='true'"> |
|
||||
|
<ItemGroup> |
||||
<PackageReference Include="xunit.v3.mtp-v2" Version="3.2.1" /> |
<PackageReference Include="xunit.v3.mtp-v2" Version="3.2.1" /> |
||||
</ItemGroup> |
</ItemGroup> |
||||
<ItemGroup Condition="'$(IsXUnit2)'=='true'"> |
|
||||
<PackageReference Include="xunit" Version="2.9.3" /> |
|
||||
<PackageReference Include="YTest.MTP.XUnit2" Version="1.0.2" /> |
|
||||
</ItemGroup> |
|
||||
<PropertyGroup> |
<PropertyGroup> |
||||
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)\avalonia.snk</AssemblyOriginatorKeyFile> |
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)\avalonia.snk</AssemblyOriginatorKeyFile> |
||||
<SignAssembly>False</SignAssembly> |
<SignAssembly>False</SignAssembly> |
||||
<NoWarn>$(NoWarn);CS8002</NoWarn> <!-- ignore signing warnings for unit tests --> |
<NoWarn>$(NoWarn);CS8002</NoWarn> <!-- ignore signing warnings for unit tests --> |
||||
</PropertyGroup> |
</PropertyGroup> |
||||
|
|
||||
</Project> |
</Project> |
||||
|
|||||
@ -0,0 +1,80 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Avalonia.Threading; |
||||
|
using Xunit.Sdk; |
||||
|
using Xunit.v3; |
||||
|
|
||||
|
namespace Avalonia.Headless.XUnit; |
||||
|
|
||||
|
internal sealed class AvaloniaDelayEnumeratedTheoryTestCase |
||||
|
: XunitDelayEnumeratedTheoryTestCase, ISelfExecutingXunitTestCase |
||||
|
{ |
||||
|
public AvaloniaDelayEnumeratedTheoryTestCase( |
||||
|
IXunitTestMethod testMethod, |
||||
|
string testCaseDisplayName, |
||||
|
string uniqueID, |
||||
|
bool @explicit, |
||||
|
bool skipTestWithoutData, |
||||
|
Type[]? skipExceptions = null, |
||||
|
string? skipReason = null, |
||||
|
Type? skipType = null, |
||||
|
string? skipUnless = null, |
||||
|
string? skipWhen = null, |
||||
|
Dictionary<string, HashSet<string>>? traits = null, |
||||
|
string? sourceFilePath = null, |
||||
|
int? sourceLineNumber = null, |
||||
|
int? timeout = null) |
||||
|
: base( |
||||
|
testMethod, |
||||
|
testCaseDisplayName, |
||||
|
uniqueID, |
||||
|
@explicit, |
||||
|
skipTestWithoutData, |
||||
|
skipExceptions, |
||||
|
skipReason, |
||||
|
skipType, |
||||
|
skipUnless, |
||||
|
skipWhen, |
||||
|
traits, |
||||
|
sourceFilePath, |
||||
|
sourceLineNumber, |
||||
|
timeout) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
[EditorBrowsable(EditorBrowsableState.Never)] |
||||
|
[Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] |
||||
|
public AvaloniaDelayEnumeratedTheoryTestCase() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public async ValueTask<RunSummary> Run( |
||||
|
ExplicitOption explicitOption, |
||||
|
IMessageBus messageBus, |
||||
|
object?[] constructorArguments, |
||||
|
ExceptionAggregator aggregator, |
||||
|
CancellationTokenSource cancellationTokenSource) |
||||
|
{ |
||||
|
var tests = await aggregator.RunAsync(CreateTests, []); |
||||
|
|
||||
|
// We need to block the XUnit thread to ensure its concurrency throttle is effective.
|
||||
|
// See https://github.com/AArnott/Xunit.StaFact/pull/55#issuecomment-826187354 for details.
|
||||
|
var runSummary = Task.Run(async () => await AvaloniaTestCaseRunner.Instance.Run( |
||||
|
this, |
||||
|
tests, |
||||
|
messageBus, |
||||
|
aggregator, |
||||
|
cancellationTokenSource, |
||||
|
TestCaseDisplayName, |
||||
|
SkipReason, |
||||
|
explicitOption, |
||||
|
constructorArguments)) |
||||
|
.GetAwaiter() |
||||
|
.GetResult(); |
||||
|
|
||||
|
return runSummary; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
using System; |
||||
|
using System.ComponentModel; |
||||
|
using Xunit.Internal; |
||||
|
using Xunit.Sdk; |
||||
|
using Xunit.v3; |
||||
|
|
||||
|
namespace Avalonia.Headless.XUnit; |
||||
|
|
||||
|
[EditorBrowsable(EditorBrowsableState.Never)] |
||||
|
public class AvaloniaFactDiscoverer : FactDiscoverer |
||||
|
{ |
||||
|
protected override IXunitTestCase CreateTestCase( |
||||
|
ITestFrameworkDiscoveryOptions discoveryOptions, |
||||
|
IXunitTestMethod testMethod, |
||||
|
IFactAttribute factAttribute) |
||||
|
{ |
||||
|
var details = TestIntrospectionHelper.GetTestCaseDetails(discoveryOptions, testMethod, factAttribute); |
||||
|
|
||||
|
return new AvaloniaTestCase( |
||||
|
details.ResolvedTestMethod, |
||||
|
details.TestCaseDisplayName, |
||||
|
details.UniqueID, |
||||
|
details.Explicit, |
||||
|
details.SkipExceptions, |
||||
|
details.SkipReason, |
||||
|
details.SkipType, |
||||
|
details.SkipUnless, |
||||
|
details.SkipWhen, |
||||
|
testMethod.Traits.ToReadWrite(StringComparer.OrdinalIgnoreCase), |
||||
|
sourceFilePath: details.SourceFilePath, |
||||
|
sourceLineNumber: details.SourceLineNumber, |
||||
|
timeout: details.Timeout); |
||||
|
} |
||||
|
} |
||||
@ -1,126 +0,0 @@ |
|||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Reflection; |
|
||||
using System.Threading; |
|
||||
using System.Threading.Tasks; |
|
||||
using Avalonia.Threading; |
|
||||
using Xunit.Abstractions; |
|
||||
using Xunit.Sdk; |
|
||||
|
|
||||
namespace Avalonia.Headless.XUnit; |
|
||||
|
|
||||
internal class AvaloniaTestAssemblyRunner : XunitTestAssemblyRunner |
|
||||
{ |
|
||||
private HeadlessUnitTestSession? _session; |
|
||||
|
|
||||
public AvaloniaTestAssemblyRunner(ITestAssembly testAssembly, IEnumerable<IXunitTestCase> testCases, |
|
||||
IMessageSink diagnosticMessageSink, IMessageSink executionMessageSink, |
|
||||
ITestFrameworkExecutionOptions executionOptions) : base(testAssembly, testCases, diagnosticMessageSink, |
|
||||
executionMessageSink, executionOptions) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
protected override void SetupSyncContext(int maxParallelThreads) |
|
||||
{ |
|
||||
_session = HeadlessUnitTestSession.GetOrStartForAssembly( |
|
||||
Assembly.Load(new AssemblyName(TestAssembly.Assembly.Name))); |
|
||||
base.SetupSyncContext(1); |
|
||||
} |
|
||||
|
|
||||
public override void Dispose() |
|
||||
{ |
|
||||
_session?.Dispose(); |
|
||||
base.Dispose(); |
|
||||
} |
|
||||
|
|
||||
protected override Task<RunSummary> RunTestCollectionAsync( |
|
||||
IMessageBus messageBus, |
|
||||
ITestCollection testCollection, |
|
||||
IEnumerable<IXunitTestCase> testCases, |
|
||||
CancellationTokenSource cancellationTokenSource) |
|
||||
{ |
|
||||
return new AvaloniaTestCollectionRunner(_session!, testCollection, testCases, DiagnosticMessageSink, messageBus, |
|
||||
TestCaseOrderer, new ExceptionAggregator(Aggregator), cancellationTokenSource).RunAsync(); |
|
||||
} |
|
||||
|
|
||||
private class AvaloniaTestCollectionRunner : XunitTestCollectionRunner |
|
||||
{ |
|
||||
private readonly HeadlessUnitTestSession _session; |
|
||||
|
|
||||
public AvaloniaTestCollectionRunner(HeadlessUnitTestSession session, |
|
||||
ITestCollection testCollection, IEnumerable<IXunitTestCase> testCases, |
|
||||
IMessageSink diagnosticMessageSink, IMessageBus messageBus, ITestCaseOrderer testCaseOrderer, |
|
||||
ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource) : base(testCollection, |
|
||||
testCases, diagnosticMessageSink, messageBus, testCaseOrderer, aggregator, cancellationTokenSource) |
|
||||
{ |
|
||||
_session = session; |
|
||||
} |
|
||||
|
|
||||
protected override Task<RunSummary> RunTestClassAsync( |
|
||||
ITestClass testClass, |
|
||||
IReflectionTypeInfo @class, |
|
||||
IEnumerable<IXunitTestCase> testCases) |
|
||||
{ |
|
||||
return new AvaloniaTestClassRunner(_session, testClass, @class, testCases, DiagnosticMessageSink, MessageBus, |
|
||||
TestCaseOrderer, new ExceptionAggregator(Aggregator), CancellationTokenSource, |
|
||||
CollectionFixtureMappings).RunAsync(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private class AvaloniaTestClassRunner : XunitTestClassRunner |
|
||||
{ |
|
||||
private readonly HeadlessUnitTestSession _session; |
|
||||
|
|
||||
public AvaloniaTestClassRunner(HeadlessUnitTestSession session, ITestClass testClass, |
|
||||
IReflectionTypeInfo @class, |
|
||||
IEnumerable<IXunitTestCase> testCases, IMessageSink diagnosticMessageSink, IMessageBus messageBus, |
|
||||
ITestCaseOrderer testCaseOrderer, ExceptionAggregator aggregator, |
|
||||
CancellationTokenSource cancellationTokenSource, IDictionary<Type, object> collectionFixtureMappings) : |
|
||||
base(testClass, @class, testCases, diagnosticMessageSink, messageBus, testCaseOrderer, aggregator, |
|
||||
cancellationTokenSource, collectionFixtureMappings) |
|
||||
{ |
|
||||
_session = session; |
|
||||
} |
|
||||
|
|
||||
protected override Task<RunSummary> RunTestMethodAsync( |
|
||||
ITestMethod testMethod, |
|
||||
IReflectionMethodInfo method, |
|
||||
IEnumerable<IXunitTestCase> testCases, |
|
||||
object[] constructorArguments) |
|
||||
{ |
|
||||
return new AvaloniaTestMethodRunner(_session, testMethod, Class, method, testCases, DiagnosticMessageSink, |
|
||||
MessageBus, new ExceptionAggregator(Aggregator), CancellationTokenSource, |
|
||||
constructorArguments).RunAsync(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private class AvaloniaTestMethodRunner : XunitTestMethodRunner |
|
||||
{ |
|
||||
private readonly HeadlessUnitTestSession _session; |
|
||||
private readonly IMessageBus _messageBus; |
|
||||
private readonly ExceptionAggregator _aggregator; |
|
||||
private readonly CancellationTokenSource _cancellationTokenSource; |
|
||||
private readonly object[] _constructorArguments; |
|
||||
|
|
||||
public AvaloniaTestMethodRunner(HeadlessUnitTestSession session, ITestMethod testMethod, |
|
||||
IReflectionTypeInfo @class, |
|
||||
IReflectionMethodInfo method, IEnumerable<IXunitTestCase> testCases, IMessageSink diagnosticMessageSink, |
|
||||
IMessageBus messageBus, ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource, |
|
||||
object[] constructorArguments) : base(testMethod, @class, method, testCases, diagnosticMessageSink, |
|
||||
messageBus, aggregator, cancellationTokenSource, constructorArguments) |
|
||||
{ |
|
||||
_session = session; |
|
||||
_messageBus = messageBus; |
|
||||
_aggregator = aggregator; |
|
||||
_cancellationTokenSource = cancellationTokenSource; |
|
||||
_constructorArguments = constructorArguments; |
|
||||
} |
|
||||
|
|
||||
protected override Task<RunSummary> RunTestCaseAsync(IXunitTestCase testCase) |
|
||||
{ |
|
||||
return AvaloniaTestCaseRunner.RunTest(_session, testCase, testCase.DisplayName, testCase.SkipReason, |
|
||||
_constructorArguments, testCase.TestMethodArguments, _messageBus, _aggregator, |
|
||||
_cancellationTokenSource); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,103 +1,59 @@ |
|||||
using System; |
using System.Collections.Generic; |
||||
using System.Collections.Generic; |
|
||||
using System.Reflection; |
|
||||
using System.Threading; |
using System.Threading; |
||||
using System.Threading.Tasks; |
using System.Threading.Tasks; |
||||
using Avalonia.Threading; |
|
||||
using Xunit.Abstractions; |
|
||||
using Xunit.Sdk; |
using Xunit.Sdk; |
||||
|
using Xunit.v3; |
||||
|
|
||||
namespace Avalonia.Headless.XUnit; |
namespace Avalonia.Headless.XUnit; |
||||
|
|
||||
internal class AvaloniaTestCaseRunner : XunitTestCaseRunner |
internal sealed class AvaloniaTestCaseRunner |
||||
|
: XunitTestCaseRunnerBase<AvaloniaTestCaseRunnerContext, IXunitTestCase, IXunitTest> |
||||
{ |
{ |
||||
private readonly HeadlessUnitTestSession _session; |
public static AvaloniaTestCaseRunner Instance { get; } = new(); |
||||
private readonly Action? _onAfterTestInvoked; |
|
||||
|
|
||||
public AvaloniaTestCaseRunner( |
private AvaloniaTestCaseRunner() |
||||
HeadlessUnitTestSession session, Action? onAfterTestInvoked, |
|
||||
IXunitTestCase testCase, string displayName, string skipReason, object[] constructorArguments, |
|
||||
object[] testMethodArguments, IMessageBus messageBus, ExceptionAggregator aggregator, |
|
||||
CancellationTokenSource cancellationTokenSource) : base(testCase, displayName, skipReason, constructorArguments, |
|
||||
testMethodArguments, messageBus, aggregator, cancellationTokenSource) |
|
||||
{ |
{ |
||||
_session = session; |
|
||||
_onAfterTestInvoked = onAfterTestInvoked; |
|
||||
} |
} |
||||
|
|
||||
public static Task<RunSummary> RunTest(HeadlessUnitTestSession session, |
public async ValueTask<RunSummary> Run( |
||||
IXunitTestCase testCase, string displayName, string skipReason, object[] constructorArguments, |
IXunitTestCase testCase, |
||||
object[] testMethodArguments, IMessageBus messageBus, ExceptionAggregator aggregator, |
IReadOnlyCollection<IXunitTest> tests, |
||||
CancellationTokenSource cancellationTokenSource) |
IMessageBus messageBus, |
||||
|
ExceptionAggregator aggregator, |
||||
|
CancellationTokenSource cancellationTokenSource, |
||||
|
string displayName, |
||||
|
string? skipReason, |
||||
|
ExplicitOption explicitOption, |
||||
|
object?[] constructorArguments) |
||||
{ |
{ |
||||
var afterTest = () => Dispatcher.UIThread.RunJobs(); |
var session = HeadlessUnitTestSession.GetOrStartForAssembly(testCase.TestClass.Class.Assembly); |
||||
|
|
||||
var runner = new AvaloniaTestCaseRunner(session, afterTest, testCase, displayName, |
await using var ctxt = new AvaloniaTestCaseRunnerContext( |
||||
skipReason, constructorArguments, testMethodArguments, messageBus, aggregator, cancellationTokenSource); |
testCase, |
||||
return runner.RunAsync(); |
tests, |
||||
} |
messageBus, |
||||
|
aggregator, |
||||
protected override XunitTestRunner CreateTestRunner(ITest test, IMessageBus messageBus, Type testClass, |
cancellationTokenSource, |
||||
object[] constructorArguments, |
displayName, |
||||
MethodInfo testMethod, object[] testMethodArguments, string skipReason, |
skipReason, |
||||
IReadOnlyList<BeforeAfterTestAttribute> beforeAfterAttributes, |
explicitOption, |
||||
ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource) |
constructorArguments, |
||||
{ |
session); |
||||
return new AvaloniaTestRunner(_session, _onAfterTestInvoked, test, messageBus, testClass, constructorArguments, |
await ctxt.InitializeAsync(); |
||||
testMethod, testMethodArguments, skipReason, beforeAfterAttributes, aggregator, cancellationTokenSource); |
|
||||
} |
return await Run(ctxt); |
||||
|
|
||||
private class AvaloniaTestRunner : XunitTestRunner |
|
||||
{ |
|
||||
private readonly HeadlessUnitTestSession _session; |
|
||||
private readonly Action? _onAfterTestInvoked; |
|
||||
|
|
||||
public AvaloniaTestRunner( |
|
||||
HeadlessUnitTestSession session, Action? onAfterTestInvoked, |
|
||||
ITest test, IMessageBus messageBus, Type testClass, object[] constructorArguments, MethodInfo testMethod, |
|
||||
object[] testMethodArguments, string skipReason, |
|
||||
IReadOnlyList<BeforeAfterTestAttribute> beforeAfterAttributes, ExceptionAggregator aggregator, |
|
||||
CancellationTokenSource cancellationTokenSource) : base(test, messageBus, testClass, constructorArguments, |
|
||||
testMethod, testMethodArguments, skipReason, beforeAfterAttributes, aggregator, cancellationTokenSource) |
|
||||
{ |
|
||||
_session = session; |
|
||||
_onAfterTestInvoked = onAfterTestInvoked; |
|
||||
} |
|
||||
|
|
||||
protected override Task<decimal> InvokeTestMethodAsync(ExceptionAggregator aggregator) |
|
||||
{ |
|
||||
return _session.Dispatch( |
|
||||
() => new AvaloniaTestInvoker(_onAfterTestInvoked, Test, MessageBus, TestClass, |
|
||||
ConstructorArguments, TestMethod, TestMethodArguments, BeforeAfterAttributes, aggregator, |
|
||||
CancellationTokenSource).RunAsync(), |
|
||||
CancellationTokenSource.Token); |
|
||||
} |
|
||||
} |
} |
||||
|
|
||||
private class AvaloniaTestInvoker : XunitTestInvoker |
protected override ValueTask<RunSummary> RunTest( |
||||
{ |
AvaloniaTestCaseRunnerContext ctxt, |
||||
private readonly Action? _onAfterTestInvoked; |
IXunitTest test) |
||||
|
=> AvaloniaTestRunner.Instance.Run( |
||||
public AvaloniaTestInvoker( |
test, |
||||
Action? onAfterTestInvoked, |
ctxt.MessageBus, |
||||
ITest test, IMessageBus messageBus, Type testClass, object[] constructorArguments, MethodInfo testMethod, |
ctxt.ConstructorArguments, |
||||
object[] testMethodArguments, IReadOnlyList<BeforeAfterTestAttribute> beforeAfterAttributes, |
ctxt.ExplicitOption, |
||||
ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource) : base(test, messageBus, |
ctxt.Aggregator.Clone(), |
||||
testClass, constructorArguments, testMethod, testMethodArguments, beforeAfterAttributes, aggregator, |
ctxt.CancellationTokenSource, |
||||
cancellationTokenSource) |
ctxt.BeforeAfterTestAttributes, |
||||
{ |
ctxt.Session); |
||||
_onAfterTestInvoked = onAfterTestInvoked; |
|
||||
} |
|
||||
|
|
||||
protected override async Task AfterTestMethodInvokedAsync() |
|
||||
{ |
|
||||
await base.AfterTestMethodInvokedAsync(); |
|
||||
|
|
||||
// Only here we can execute random code after the test, where exception will be properly handled by the XUnit.
|
|
||||
if (_onAfterTestInvoked is not null) |
|
||||
{ |
|
||||
Aggregator.Run(_onAfterTestInvoked); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
} |
||||
|
|||||
@ -0,0 +1,31 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Threading; |
||||
|
using Xunit.Sdk; |
||||
|
using Xunit.v3; |
||||
|
|
||||
|
namespace Avalonia.Headless.XUnit; |
||||
|
|
||||
|
internal sealed class AvaloniaTestCaseRunnerContext( |
||||
|
IXunitTestCase testCase, |
||||
|
IReadOnlyCollection<IXunitTest> tests, |
||||
|
IMessageBus messageBus, |
||||
|
ExceptionAggregator aggregator, |
||||
|
CancellationTokenSource cancellationTokenSource, |
||||
|
string displayName, |
||||
|
string? skipReason, |
||||
|
ExplicitOption explicitOption, |
||||
|
object?[] constructorArguments, |
||||
|
HeadlessUnitTestSession session) |
||||
|
: XunitTestCaseRunnerContext( |
||||
|
testCase, |
||||
|
tests, |
||||
|
messageBus, |
||||
|
aggregator, |
||||
|
cancellationTokenSource, |
||||
|
displayName, |
||||
|
skipReason, |
||||
|
explicitOption, |
||||
|
constructorArguments) |
||||
|
{ |
||||
|
public HeadlessUnitTestSession Session { get; } = session; |
||||
|
} |
||||
@ -1,35 +1,13 @@ |
|||||
using System.Collections.Generic; |
using System.Reflection; |
||||
using System.Reflection; |
using Xunit.v3; |
||||
using Xunit.Abstractions; |
|
||||
using Xunit.Sdk; |
|
||||
|
|
||||
namespace Avalonia.Headless.XUnit; |
namespace Avalonia.Headless.XUnit; |
||||
|
|
||||
internal class AvaloniaTestFramework : XunitTestFramework |
internal sealed class AvaloniaTestFramework : XunitTestFramework |
||||
{ |
{ |
||||
public AvaloniaTestFramework(IMessageSink messageSink) : base(messageSink) |
protected override ITestFrameworkDiscoverer CreateDiscoverer(Assembly assembly) |
||||
{ |
=> new AvaloniaTestFrameworkDiscoverer(new XunitTestAssembly(assembly, null, assembly.GetName().Version)); |
||||
} |
|
||||
|
|
||||
protected override ITestFrameworkExecutor CreateExecutor(AssemblyName assemblyName) |
protected override ITestFrameworkExecutor CreateExecutor(Assembly assembly) |
||||
=> new Executor(assemblyName, SourceInformationProvider, DiagnosticMessageSink); |
=> new AvaloniaTestFrameworkExecutor(new XunitTestAssembly(assembly, null, assembly.GetName().Version)); |
||||
|
|
||||
|
|
||||
private class Executor : XunitTestFrameworkExecutor |
|
||||
{ |
|
||||
public Executor(AssemblyName assemblyName, ISourceInformationProvider sourceInformationProvider, |
|
||||
IMessageSink diagnosticMessageSink) : base(assemblyName, sourceInformationProvider, |
|
||||
diagnosticMessageSink) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
protected override async void RunTestCases(IEnumerable<IXunitTestCase> testCases, |
|
||||
IMessageSink executionMessageSink, |
|
||||
ITestFrameworkExecutionOptions executionOptions) |
|
||||
{ |
|
||||
using (var assemblyRunner = new AvaloniaTestAssemblyRunner( |
|
||||
TestAssembly, testCases, DiagnosticMessageSink, executionMessageSink, |
|
||||
executionOptions)) await assemblyRunner.RunAsync(); |
|
||||
} |
|
||||
} |
|
||||
} |
} |
||||
|
|||||
@ -0,0 +1,16 @@ |
|||||
|
using Xunit; |
||||
|
using Xunit.v3; |
||||
|
|
||||
|
namespace Avalonia.Headless.XUnit; |
||||
|
|
||||
|
internal sealed class AvaloniaTestFrameworkDiscoverer : XunitTestFrameworkDiscoverer |
||||
|
{ |
||||
|
public AvaloniaTestFrameworkDiscoverer( |
||||
|
IXunitTestAssembly testAssembly, |
||||
|
IXunitTestCollectionFactory? collectionFactory = null) |
||||
|
: base(testAssembly, collectionFactory) |
||||
|
{ |
||||
|
DiscovererTypeCache[typeof(FactAttribute)] = typeof(AvaloniaFactDiscoverer); |
||||
|
DiscovererTypeCache[typeof(TheoryAttribute)] = typeof(AvaloniaTheoryDiscoverer); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Xunit.v3; |
||||
|
|
||||
|
namespace Avalonia.Headless.XUnit; |
||||
|
|
||||
|
internal sealed class AvaloniaTestFrameworkExecutor(IXunitTestAssembly testAssembly) |
||||
|
: XunitTestFrameworkExecutor(testAssembly) |
||||
|
{ |
||||
|
private readonly HeadlessUnitTestSession _session = HeadlessUnitTestSession.GetOrStartForAssembly(testAssembly.Assembly); |
||||
|
|
||||
|
protected override ITestFrameworkDiscoverer CreateDiscoverer() |
||||
|
=> new AvaloniaTestFrameworkDiscoverer(TestAssembly); |
||||
|
|
||||
|
public override async ValueTask DisposeAsync() |
||||
|
{ |
||||
|
await _session.DisposeAsync(); |
||||
|
await base.DisposeAsync(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,50 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Avalonia.Threading; |
||||
|
using Xunit.Sdk; |
||||
|
using Xunit.v3; |
||||
|
|
||||
|
namespace Avalonia.Headless.XUnit; |
||||
|
|
||||
|
internal sealed class AvaloniaTestRunner : XunitTestRunnerBase<AvaloniaTestRunnerContext, IXunitTest> |
||||
|
{ |
||||
|
public static AvaloniaTestRunner Instance { get; } = new(); |
||||
|
|
||||
|
private AvaloniaTestRunner() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public async ValueTask<RunSummary> Run( |
||||
|
IXunitTest test, |
||||
|
IMessageBus messageBus, |
||||
|
object?[] constructorArguments, |
||||
|
ExplicitOption explicitOption, |
||||
|
ExceptionAggregator aggregator, |
||||
|
CancellationTokenSource cancellationTokenSource, |
||||
|
IReadOnlyCollection<IBeforeAfterTestAttribute> beforeAfterAttributes, |
||||
|
HeadlessUnitTestSession session) |
||||
|
{ |
||||
|
await using var ctxt = new AvaloniaTestRunnerContext( |
||||
|
test, |
||||
|
messageBus, |
||||
|
explicitOption, |
||||
|
aggregator, |
||||
|
cancellationTokenSource, |
||||
|
beforeAfterAttributes, |
||||
|
constructorArguments, |
||||
|
session |
||||
|
); |
||||
|
await ctxt.InitializeAsync(); |
||||
|
|
||||
|
return await session.Dispatch( |
||||
|
async () => |
||||
|
{ |
||||
|
var dispatcher = Dispatcher.UIThread; |
||||
|
var summary = await Run(ctxt); |
||||
|
dispatcher.RunJobs(); |
||||
|
return summary; |
||||
|
}, |
||||
|
ctxt.CancellationTokenSource.Token); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,27 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Threading; |
||||
|
using Xunit.Sdk; |
||||
|
using Xunit.v3; |
||||
|
|
||||
|
namespace Avalonia.Headless.XUnit; |
||||
|
|
||||
|
internal sealed class AvaloniaTestRunnerContext( |
||||
|
IXunitTest test, |
||||
|
IMessageBus messageBus, |
||||
|
ExplicitOption explicitOption, |
||||
|
ExceptionAggregator aggregator, |
||||
|
CancellationTokenSource cancellationTokenSource, |
||||
|
IReadOnlyCollection<IBeforeAfterTestAttribute> beforeAfterTestAttributes, |
||||
|
object?[] constructorArguments, |
||||
|
HeadlessUnitTestSession session) |
||||
|
: XunitTestRunnerContext( |
||||
|
test, |
||||
|
messageBus, |
||||
|
explicitOption, |
||||
|
aggregator, |
||||
|
cancellationTokenSource, |
||||
|
beforeAfterTestAttributes, |
||||
|
constructorArguments) |
||||
|
{ |
||||
|
public HeadlessUnitTestSession Session { get; } = session; |
||||
|
} |
||||
@ -0,0 +1,92 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel; |
||||
|
using System.Threading.Tasks; |
||||
|
using Xunit; |
||||
|
using Xunit.Internal; |
||||
|
using Xunit.Sdk; |
||||
|
using Xunit.v3; |
||||
|
|
||||
|
namespace Avalonia.Headless.XUnit; |
||||
|
|
||||
|
[EditorBrowsable(EditorBrowsableState.Never)] |
||||
|
public class AvaloniaTheoryDiscoverer : TheoryDiscoverer |
||||
|
{ |
||||
|
protected override ValueTask<IReadOnlyCollection<IXunitTestCase>> CreateTestCasesForDataRow( |
||||
|
ITestFrameworkDiscoveryOptions discoveryOptions, |
||||
|
IXunitTestMethod testMethod, |
||||
|
ITheoryAttribute theoryAttribute, |
||||
|
ITheoryDataRow dataRow, |
||||
|
object?[] testMethodArguments) |
||||
|
{ |
||||
|
var details = TestIntrospectionHelper.GetTestCaseDetailsForTheoryDataRow( |
||||
|
discoveryOptions, |
||||
|
testMethod, |
||||
|
theoryAttribute, |
||||
|
dataRow, |
||||
|
testMethodArguments); |
||||
|
var traits = TestIntrospectionHelper.GetTraits(testMethod, dataRow); |
||||
|
|
||||
|
var testCase = new AvaloniaTestCase( |
||||
|
details.ResolvedTestMethod, |
||||
|
details.TestCaseDisplayName, |
||||
|
details.UniqueID, |
||||
|
details.Explicit, |
||||
|
details.SkipExceptions, |
||||
|
details.SkipReason, |
||||
|
details.SkipType, |
||||
|
details.SkipUnless, |
||||
|
details.SkipWhen, |
||||
|
traits, |
||||
|
testMethodArguments, |
||||
|
sourceFilePath: details.SourceFilePath, |
||||
|
sourceLineNumber: details.SourceLineNumber, |
||||
|
timeout: details.Timeout); |
||||
|
|
||||
|
return ValueTask.FromResult<IReadOnlyCollection<IXunitTestCase>>([testCase]); |
||||
|
} |
||||
|
|
||||
|
protected override ValueTask<IReadOnlyCollection<IXunitTestCase>> CreateTestCasesForTheory( |
||||
|
ITestFrameworkDiscoveryOptions discoveryOptions, |
||||
|
IXunitTestMethod testMethod, |
||||
|
ITheoryAttribute theoryAttribute) |
||||
|
{ |
||||
|
var details = TestIntrospectionHelper.GetTestCaseDetails(discoveryOptions, testMethod, theoryAttribute); |
||||
|
|
||||
|
var testCase = |
||||
|
details.SkipReason is not null && details.SkipUnless is null && details.SkipWhen is null |
||||
|
? new AvaloniaTestCase( |
||||
|
details.ResolvedTestMethod, |
||||
|
details.TestCaseDisplayName, |
||||
|
details.UniqueID, |
||||
|
details.Explicit, |
||||
|
details.SkipExceptions, |
||||
|
details.SkipReason, |
||||
|
details.SkipType, |
||||
|
details.SkipUnless, |
||||
|
details.SkipWhen, |
||||
|
testMethod.Traits.ToReadWrite(StringComparer.OrdinalIgnoreCase), |
||||
|
sourceFilePath: details.SourceFilePath, |
||||
|
sourceLineNumber: details.SourceLineNumber, |
||||
|
timeout: details.Timeout |
||||
|
) |
||||
|
: (IXunitTestCase)new AvaloniaDelayEnumeratedTheoryTestCase( |
||||
|
details.ResolvedTestMethod, |
||||
|
details.TestCaseDisplayName, |
||||
|
details.UniqueID, |
||||
|
details.Explicit, |
||||
|
theoryAttribute.SkipTestWithoutData, |
||||
|
details.SkipExceptions, |
||||
|
details.SkipReason, |
||||
|
details.SkipType, |
||||
|
details.SkipUnless, |
||||
|
details.SkipWhen, |
||||
|
testMethod.Traits.ToReadWrite(StringComparer.OrdinalIgnoreCase), |
||||
|
sourceFilePath: details.SourceFilePath, |
||||
|
sourceLineNumber: details.SourceLineNumber, |
||||
|
timeout: details.Timeout |
||||
|
); |
||||
|
|
||||
|
return ValueTask.FromResult<IReadOnlyCollection<IXunitTestCase>>([testCase]); |
||||
|
} |
||||
|
} |
||||
@ -1,31 +0,0 @@ |
|||||
using System; |
|
||||
using System.ComponentModel; |
|
||||
using System.Threading; |
|
||||
using System.Threading.Tasks; |
|
||||
using Xunit.Abstractions; |
|
||||
using Xunit.Sdk; |
|
||||
|
|
||||
namespace Avalonia.Headless.XUnit; |
|
||||
|
|
||||
internal class AvaloniaTheoryTestCase : XunitTheoryTestCase |
|
||||
{ |
|
||||
[EditorBrowsable(EditorBrowsableState.Never)] |
|
||||
[Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] |
|
||||
public AvaloniaTheoryTestCase() |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public AvaloniaTheoryTestCase(IMessageSink diagnosticMessageSink, TestMethodDisplay defaultMethodDisplay, TestMethodDisplayOptions defaultMethodDisplayOptions, ITestMethod testMethod) |
|
||||
: base(diagnosticMessageSink, defaultMethodDisplay, defaultMethodDisplayOptions, testMethod) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public override Task<RunSummary> RunAsync(IMessageSink diagnosticMessageSink, IMessageBus messageBus, object[] constructorArguments, ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource) |
|
||||
{ |
|
||||
var session = HeadlessUnitTestSession.GetOrStartForAssembly(Method.ToRuntimeMethod().DeclaringType?.Assembly); |
|
||||
|
|
||||
return AvaloniaTestCaseRunner |
|
||||
.RunTest(session, this, DisplayName, SkipReason, constructorArguments, |
|
||||
TestMethodArguments, messageBus, aggregator, cancellationTokenSource); |
|
||||
} |
|
||||
} |
|
||||
Loading…
Reference in new issue