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"> |
|||
<ItemGroup Condition="'$(IsXUnit2)'!='true'"> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="xunit.v3.mtp-v2" Version="3.2.1" /> |
|||
</ItemGroup> |
|||
<ItemGroup Condition="'$(IsXUnit2)'=='true'"> |
|||
<PackageReference Include="xunit" Version="2.9.3" /> |
|||
<PackageReference Include="YTest.MTP.XUnit2" Version="1.0.2" /> |
|||
</ItemGroup> |
|||
|
|||
<PropertyGroup> |
|||
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)\avalonia.snk</AssemblyOriginatorKeyFile> |
|||
<SignAssembly>False</SignAssembly> |
|||
<NoWarn>$(NoWarn);CS8002</NoWarn> <!-- ignore signing warnings for unit tests --> |
|||
</PropertyGroup> |
|||
|
|||
</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.Reflection; |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Threading; |
|||
using Xunit.Abstractions; |
|||
using Xunit.Sdk; |
|||
using Xunit.v3; |
|||
|
|||
namespace Avalonia.Headless.XUnit; |
|||
|
|||
internal class AvaloniaTestCaseRunner : XunitTestCaseRunner |
|||
internal sealed class AvaloniaTestCaseRunner |
|||
: XunitTestCaseRunnerBase<AvaloniaTestCaseRunnerContext, IXunitTestCase, IXunitTest> |
|||
{ |
|||
private readonly HeadlessUnitTestSession _session; |
|||
private readonly Action? _onAfterTestInvoked; |
|||
public static AvaloniaTestCaseRunner Instance { get; } = new(); |
|||
|
|||
public 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) |
|||
private AvaloniaTestCaseRunner() |
|||
{ |
|||
_session = session; |
|||
_onAfterTestInvoked = onAfterTestInvoked; |
|||
} |
|||
|
|||
public static Task<RunSummary> RunTest(HeadlessUnitTestSession session, |
|||
IXunitTestCase testCase, string displayName, string skipReason, object[] constructorArguments, |
|||
object[] testMethodArguments, IMessageBus messageBus, ExceptionAggregator aggregator, |
|||
CancellationTokenSource cancellationTokenSource) |
|||
public async ValueTask<RunSummary> Run( |
|||
IXunitTestCase testCase, |
|||
IReadOnlyCollection<IXunitTest> tests, |
|||
IMessageBus messageBus, |
|||
ExceptionAggregator aggregator, |
|||
CancellationTokenSource cancellationTokenSource, |
|||
string displayName, |
|||
string? skipReason, |
|||
ExplicitOption explicitOption, |
|||
object?[] constructorArguments) |
|||
{ |
|||
var afterTest = () => Dispatcher.UIThread.RunJobs(); |
|||
|
|||
var runner = new AvaloniaTestCaseRunner(session, afterTest, testCase, displayName, |
|||
skipReason, constructorArguments, testMethodArguments, messageBus, aggregator, cancellationTokenSource); |
|||
return runner.RunAsync(); |
|||
} |
|||
|
|||
protected override XunitTestRunner CreateTestRunner(ITest test, IMessageBus messageBus, Type testClass, |
|||
object[] constructorArguments, |
|||
MethodInfo testMethod, object[] testMethodArguments, string skipReason, |
|||
IReadOnlyList<BeforeAfterTestAttribute> beforeAfterAttributes, |
|||
ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource) |
|||
{ |
|||
return new AvaloniaTestRunner(_session, _onAfterTestInvoked, test, messageBus, testClass, constructorArguments, |
|||
testMethod, testMethodArguments, skipReason, beforeAfterAttributes, aggregator, cancellationTokenSource); |
|||
} |
|||
|
|||
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); |
|||
} |
|||
var session = HeadlessUnitTestSession.GetOrStartForAssembly(testCase.TestClass.Class.Assembly); |
|||
|
|||
await using var ctxt = new AvaloniaTestCaseRunnerContext( |
|||
testCase, |
|||
tests, |
|||
messageBus, |
|||
aggregator, |
|||
cancellationTokenSource, |
|||
displayName, |
|||
skipReason, |
|||
explicitOption, |
|||
constructorArguments, |
|||
session); |
|||
await ctxt.InitializeAsync(); |
|||
|
|||
return await Run(ctxt); |
|||
} |
|||
|
|||
private class AvaloniaTestInvoker : XunitTestInvoker |
|||
{ |
|||
private readonly Action? _onAfterTestInvoked; |
|||
|
|||
public AvaloniaTestInvoker( |
|||
Action? onAfterTestInvoked, |
|||
ITest test, IMessageBus messageBus, Type testClass, object[] constructorArguments, MethodInfo testMethod, |
|||
object[] testMethodArguments, IReadOnlyList<BeforeAfterTestAttribute> beforeAfterAttributes, |
|||
ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource) : base(test, messageBus, |
|||
testClass, constructorArguments, testMethod, testMethodArguments, beforeAfterAttributes, aggregator, |
|||
cancellationTokenSource) |
|||
{ |
|||
_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); |
|||
} |
|||
} |
|||
} |
|||
protected override ValueTask<RunSummary> RunTest( |
|||
AvaloniaTestCaseRunnerContext ctxt, |
|||
IXunitTest test) |
|||
=> AvaloniaTestRunner.Instance.Run( |
|||
test, |
|||
ctxt.MessageBus, |
|||
ctxt.ConstructorArguments, |
|||
ctxt.ExplicitOption, |
|||
ctxt.Aggregator.Clone(), |
|||
ctxt.CancellationTokenSource, |
|||
ctxt.BeforeAfterTestAttributes, |
|||
ctxt.Session); |
|||
} |
|||
|
|||
@ -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 Xunit.Abstractions; |
|||
using Xunit.Sdk; |
|||
using System.Reflection; |
|||
using Xunit.v3; |
|||
|
|||
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) |
|||
=> new Executor(assemblyName, SourceInformationProvider, DiagnosticMessageSink); |
|||
|
|||
|
|||
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(); |
|||
} |
|||
} |
|||
protected override ITestFrameworkExecutor CreateExecutor(Assembly assembly) |
|||
=> new AvaloniaTestFrameworkExecutor(new XunitTestAssembly(assembly, null, assembly.GetName().Version)); |
|||
} |
|||
|
|||
@ -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