Update headless xUnit support to v3 (#20481)
* Update headless xUnit support to v3 * Update API suppressions
This commit is contained in:
committed by
GitHub
parent
1086d9b5b0
commit
ff0a61486d
@@ -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>
|
||||||
+4
-5
@@ -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>
|
||||||
|
|||||||
@@ -5,8 +5,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<!-- Use lower minor version, as it is supposed to be compatible -->
|
<PackageReference Include="xunit.v3.extensibility.core" Version="3.2.1" />
|
||||||
<PackageReference Include="xunit.core" Version="2.4.0" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Threading;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Xunit.Abstractions;
|
using Xunit.v3;
|
||||||
using Xunit.Sdk;
|
|
||||||
|
|
||||||
namespace Avalonia.Headless.XUnit;
|
namespace Avalonia.Headless.XUnit;
|
||||||
|
|
||||||
@@ -12,24 +10,8 @@ namespace Avalonia.Headless.XUnit;
|
|||||||
/// such that awaited expressions resume on the test's "main thread".
|
/// such that awaited expressions resume on the test's "main thread".
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||||
[XunitTestCaseDiscoverer("Avalonia.Headless.XUnit.AvaloniaUIFactDiscoverer", "Avalonia.Headless.XUnit")]
|
[XunitTestCaseDiscoverer(typeof(AvaloniaFactDiscoverer))]
|
||||||
public sealed class AvaloniaFactAttribute : FactAttribute
|
public sealed class AvaloniaFactAttribute(
|
||||||
{
|
[CallerFilePath] string? sourceFilePath = null,
|
||||||
|
[CallerLineNumber] int sourceLineNumber = -1)
|
||||||
}
|
: FactAttribute(sourceFilePath, sourceLineNumber);
|
||||||
|
|
||||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
||||||
public class AvaloniaUIFactDiscoverer : FactDiscoverer
|
|
||||||
{
|
|
||||||
private readonly IMessageSink diagnosticMessageSink;
|
|
||||||
public AvaloniaUIFactDiscoverer(IMessageSink diagnosticMessageSink)
|
|
||||||
: base(diagnosticMessageSink)
|
|
||||||
{
|
|
||||||
this.diagnosticMessageSink = diagnosticMessageSink;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override IXunitTestCase CreateTestCase(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo factAttribute)
|
|
||||||
{
|
|
||||||
return new AvaloniaTestCase(diagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), testMethod);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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,20 +1,46 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Xunit.Abstractions;
|
using Avalonia.Threading;
|
||||||
using Xunit.Sdk;
|
using Xunit.Sdk;
|
||||||
|
using Xunit.v3;
|
||||||
|
|
||||||
namespace Avalonia.Headless.XUnit;
|
namespace Avalonia.Headless.XUnit;
|
||||||
|
|
||||||
internal class AvaloniaTestCase : XunitTestCase
|
internal sealed class AvaloniaTestCase : XunitTestCase, ISelfExecutingXunitTestCase
|
||||||
{
|
{
|
||||||
public AvaloniaTestCase(
|
public AvaloniaTestCase(
|
||||||
IMessageSink diagnosticMessageSink,
|
IXunitTestMethod testMethod,
|
||||||
TestMethodDisplay defaultMethodDisplay,
|
string testCaseDisplayName,
|
||||||
ITestMethod testMethod,
|
string uniqueID,
|
||||||
object?[]? testMethodArguments = null)
|
bool @explicit,
|
||||||
: base(diagnosticMessageSink, defaultMethodDisplay, TestMethodDisplayOptions.None, testMethod, testMethodArguments)
|
Type[]? skipExceptions = null,
|
||||||
|
string? skipReason = null,
|
||||||
|
Type? skipType = null,
|
||||||
|
string? skipUnless = null,
|
||||||
|
string? skipWhen = null,
|
||||||
|
Dictionary<string, HashSet<string>>? traits = null,
|
||||||
|
object?[]? testMethodArguments = null,
|
||||||
|
string? sourceFilePath = null,
|
||||||
|
int? sourceLineNumber = null,
|
||||||
|
int? timeout = null)
|
||||||
|
: base(
|
||||||
|
testMethod,
|
||||||
|
testCaseDisplayName,
|
||||||
|
uniqueID,
|
||||||
|
@explicit,
|
||||||
|
skipExceptions,
|
||||||
|
skipReason,
|
||||||
|
skipType,
|
||||||
|
skipUnless,
|
||||||
|
skipWhen,
|
||||||
|
traits,
|
||||||
|
testMethodArguments,
|
||||||
|
sourceFilePath,
|
||||||
|
sourceLineNumber,
|
||||||
|
timeout)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,23 +50,30 @@ internal class AvaloniaTestCase : XunitTestCase
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task<RunSummary> RunAsync(
|
public async ValueTask<RunSummary> Run(
|
||||||
IMessageSink diagnosticMessageSink,
|
ExplicitOption explicitOption,
|
||||||
IMessageBus messageBus,
|
IMessageBus messageBus,
|
||||||
object[] constructorArguments,
|
object?[] constructorArguments,
|
||||||
ExceptionAggregator aggregator,
|
ExceptionAggregator aggregator,
|
||||||
CancellationTokenSource cancellationTokenSource)
|
CancellationTokenSource cancellationTokenSource)
|
||||||
{
|
{
|
||||||
var session = HeadlessUnitTestSession.GetOrStartForAssembly(Method.ToRuntimeMethod().DeclaringType?.Assembly);
|
var tests = await aggregator.RunAsync(CreateTests, []);
|
||||||
|
|
||||||
// We need to block the XUnit thread to ensure its concurrency throttle is effective.
|
// 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.
|
// See https://github.com/AArnott/Xunit.StaFact/pull/55#issuecomment-826187354 for details.
|
||||||
var runSummary =
|
var runSummary = Task.Run(async () => await AvaloniaTestCaseRunner.Instance.Run(
|
||||||
Task.Run(() => AvaloniaTestCaseRunner.RunTest(session, this, DisplayName, SkipReason, constructorArguments,
|
this,
|
||||||
TestMethodArguments, messageBus, aggregator, cancellationTokenSource))
|
tests,
|
||||||
|
messageBus,
|
||||||
|
aggregator,
|
||||||
|
cancellationTokenSource,
|
||||||
|
TestCaseDisplayName,
|
||||||
|
SkipReason,
|
||||||
|
explicitOption,
|
||||||
|
constructorArguments))
|
||||||
.GetAwaiter()
|
.GetAwaiter()
|
||||||
.GetResult();
|
.GetResult();
|
||||||
|
|
||||||
return Task.FromResult(runSummary);
|
return runSummary;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
cancellationTokenSource,
|
||||||
|
displayName,
|
||||||
|
skipReason,
|
||||||
|
explicitOption,
|
||||||
|
constructorArguments,
|
||||||
|
session);
|
||||||
|
await ctxt.InitializeAsync();
|
||||||
|
|
||||||
|
return await Run(ctxt);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override XunitTestRunner CreateTestRunner(ITest test, IMessageBus messageBus, Type testClass,
|
protected override ValueTask<RunSummary> RunTest(
|
||||||
object[] constructorArguments,
|
AvaloniaTestCaseRunnerContext ctxt,
|
||||||
MethodInfo testMethod, object[] testMethodArguments, string skipReason,
|
IXunitTest test)
|
||||||
IReadOnlyList<BeforeAfterTestAttribute> beforeAfterAttributes,
|
=> AvaloniaTestRunner.Instance.Run(
|
||||||
ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource)
|
test,
|
||||||
{
|
ctxt.MessageBus,
|
||||||
return new AvaloniaTestRunner(_session, _onAfterTestInvoked, test, messageBus, testClass, constructorArguments,
|
ctxt.ConstructorArguments,
|
||||||
testMethod, testMethodArguments, skipReason, beforeAfterAttributes, aggregator, cancellationTokenSource);
|
ctxt.ExplicitOption,
|
||||||
}
|
ctxt.Aggregator.Clone(),
|
||||||
|
ctxt.CancellationTokenSource,
|
||||||
private class AvaloniaTestRunner : XunitTestRunner
|
ctxt.BeforeAfterTestAttributes,
|
||||||
{
|
ctxt.Session);
|
||||||
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
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using Xunit.v3;
|
||||||
using System.Linq;
|
|
||||||
using Xunit.Abstractions;
|
|
||||||
using Xunit.Sdk;
|
|
||||||
|
|
||||||
namespace Avalonia.Headless.XUnit;
|
namespace Avalonia.Headless.XUnit;
|
||||||
|
|
||||||
@@ -12,27 +9,9 @@ namespace Avalonia.Headless.XUnit;
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// It is an alternative to using [AvaloniaFact] or [AvaloniaTheory] attributes on every test method.
|
/// It is an alternative to using [AvaloniaFact] or [AvaloniaTheory] attributes on every test method.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[TestFrameworkDiscoverer("Avalonia.Headless.XUnit.AvaloniaTestFrameworkTypeDiscoverer", "Avalonia.Headless.XUnit")]
|
|
||||||
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
|
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
|
||||||
public sealed class AvaloniaTestFrameworkAttribute : Attribute, ITestFrameworkAttribute
|
public sealed class AvaloniaTestFrameworkAttribute : Attribute, ITestFrameworkAttribute
|
||||||
{
|
{
|
||||||
}
|
public Type FrameworkType
|
||||||
|
=> typeof(AvaloniaTestFramework);
|
||||||
/// <summary>
|
|
||||||
/// Discoverer implementation for the Avalonia testing framework.
|
|
||||||
/// </summary>
|
|
||||||
public class AvaloniaTestFrameworkTypeDiscoverer : ITestFrameworkTypeDiscoverer
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Creates instance of <see cref="AvaloniaTestFrameworkTypeDiscoverer"/>.
|
|
||||||
/// </summary>
|
|
||||||
public AvaloniaTestFrameworkTypeDiscoverer(IMessageSink _)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public Type GetTestFrameworkType(IAttributeInfo attribute)
|
|
||||||
{
|
|
||||||
return typeof(AvaloniaTestFramework);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Xunit.Abstractions;
|
using Xunit.v3;
|
||||||
using Xunit.Sdk;
|
|
||||||
|
|
||||||
namespace Avalonia.Headless.XUnit;
|
namespace Avalonia.Headless.XUnit;
|
||||||
|
|
||||||
@@ -12,26 +9,5 @@ namespace Avalonia.Headless.XUnit;
|
|||||||
/// such that awaited expressions resume on the test's "main thread".
|
/// such that awaited expressions resume on the test's "main thread".
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||||
[XunitTestCaseDiscoverer("Avalonia.Headless.XUnit.AvaloniaTheoryDiscoverer", "Avalonia.Headless.XUnit")]
|
[XunitTestCaseDiscoverer(typeof(AvaloniaTheoryDiscoverer))]
|
||||||
public sealed class AvaloniaTheoryAttribute : TheoryAttribute
|
public sealed class AvaloniaTheoryAttribute : TheoryAttribute;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
||||||
public class AvaloniaTheoryDiscoverer : TheoryDiscoverer
|
|
||||||
{
|
|
||||||
public AvaloniaTheoryDiscoverer(IMessageSink diagnosticMessageSink)
|
|
||||||
: base(diagnosticMessageSink)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override IEnumerable<IXunitTestCase> CreateTestCasesForDataRow(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo theoryAttribute, object[] dataRow)
|
|
||||||
{
|
|
||||||
yield return new AvaloniaTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), testMethod, dataRow);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override IEnumerable<IXunitTestCase> CreateTestCasesForTheory(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo theoryAttribute)
|
|
||||||
{
|
|
||||||
yield return new AvaloniaTheoryTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), TestMethodDisplayOptions.None, testMethod);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -18,7 +18,7 @@ namespace Avalonia.Headless;
|
|||||||
/// All UI tests are supposed to be executed from one of the <see cref="Dispatch"/> methods to keep execution flow on the UI thread.
|
/// All UI tests are supposed to be executed from one of the <see cref="Dispatch"/> methods to keep execution flow on the UI thread.
|
||||||
/// Disposing unit test session stops internal dispatcher loop.
|
/// Disposing unit test session stops internal dispatcher loop.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class HeadlessUnitTestSession : IDisposable
|
public sealed class HeadlessUnitTestSession : IDisposable, IAsyncDisposable
|
||||||
{
|
{
|
||||||
private static readonly Dictionary<Assembly, HeadlessUnitTestSession> s_session = new();
|
private static readonly Dictionary<Assembly, HeadlessUnitTestSession> s_session = new();
|
||||||
|
|
||||||
@@ -184,6 +184,14 @@ public sealed class HeadlessUnitTestSession : IDisposable
|
|||||||
_cancellationTokenSource.Dispose();
|
_cancellationTokenSource.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async ValueTask DisposeAsync()
|
||||||
|
{
|
||||||
|
await _cancellationTokenSource.CancelAsync().ConfigureAwait(false);
|
||||||
|
_queue.CompleteAdding();
|
||||||
|
await _dispatchTask.ConfigureAwait(false);
|
||||||
|
_cancellationTokenSource.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates instance of <see cref="HeadlessUnitTestSession"/>.
|
/// Creates instance of <see cref="HeadlessUnitTestSession"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
-1
@@ -4,7 +4,6 @@
|
|||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<IsTestProject>true</IsTestProject>
|
<IsTestProject>true</IsTestProject>
|
||||||
<DefineConstants>$(DefineConstants);XUNIT</DefineConstants>
|
<DefineConstants>$(DefineConstants);XUNIT</DefineConstants>
|
||||||
<IsXUnit2>true</IsXUnit2>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<Import Project="..\..\build\UnitTests.NetCore.targets" />
|
<Import Project="..\..\build\UnitTests.NetCore.targets" />
|
||||||
|
|||||||
-1
@@ -4,7 +4,6 @@
|
|||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<IsTestProject>true</IsTestProject>
|
<IsTestProject>true</IsTestProject>
|
||||||
<DefineConstants>$(DefineConstants);XUNIT</DefineConstants>
|
<DefineConstants>$(DefineConstants);XUNIT</DefineConstants>
|
||||||
<IsXUnit2>true</IsXUnit2>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<Import Project="..\..\build\UnitTests.NetCore.targets" />
|
<Import Project="..\..\build\UnitTests.NetCore.targets" />
|
||||||
|
|||||||
@@ -13,10 +13,7 @@
|
|||||||
<ProjectReference Include="../../src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj" />
|
<ProjectReference Include="../../src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<Import Project="../../build/XUnit.props" />
|
||||||
<PackageReference Include="xunit.v3.mtp-v2" Version="3.2.1" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<Import Project="../../build/SharedVersion.props" />
|
<Import Project="../../build/SharedVersion.props" />
|
||||||
<Import Project="../../build/ReferenceCoreLibraries.props" />
|
<Import Project="../../build/ReferenceCoreLibraries.props" />
|
||||||
<Import Project="../../build/BuildTargets.targets" />
|
<Import Project="../../build/BuildTargets.targets" />
|
||||||
|
|||||||
Reference in New Issue
Block a user