8 changed files with 208 additions and 11 deletions
@ -0,0 +1,19 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
<PropertyGroup> |
|||
<TargetFramework>net6.0</TargetFramework> |
|||
<ImplicitUsings>enable</ImplicitUsings> |
|||
<Nullable>enable</Nullable> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="xunit.core" Version="2.4.2" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Avalonia.Headless\Avalonia.Headless.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<Import Project="..\..\..\build\ApiDiff.props" /> |
|||
<Import Project="..\..\..\build\DevAnalyzers.props" /> |
|||
<Import Project="..\..\..\build\NullableEnable.props" /> |
|||
</Project> |
|||
@ -0,0 +1,35 @@ |
|||
using System.Reflection; |
|||
using Xunit.Abstractions; |
|||
using Xunit.Sdk; |
|||
|
|||
namespace Avalonia.Headless.XUnit; |
|||
|
|||
internal class AvaloniaTestFramework<TAppBuilderEntry> : XunitTestFramework |
|||
{ |
|||
public AvaloniaTestFramework(IMessageSink messageSink) : base(messageSink) |
|||
{ |
|||
} |
|||
|
|||
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) |
|||
{ |
|||
executionOptions.SetValue("xunit.execution.DisableParallelization", false); |
|||
using (var assemblyRunner = new AvaloniaTestRunner<TAppBuilderEntry>( |
|||
TestAssembly, testCases, DiagnosticMessageSink, executionMessageSink, |
|||
executionOptions)) await assemblyRunner.RunAsync(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
using System.Diagnostics.CodeAnalysis; |
|||
using Xunit.Abstractions; |
|||
using Xunit.Sdk; |
|||
|
|||
namespace Avalonia.Headless.XUnit; |
|||
|
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
[TestFrameworkDiscoverer("Avalonia.Headless.XUnit.AvaloniaTestFrameworkTypeDiscoverer", "Avalonia.Headless.XUnit")] |
|||
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)] |
|||
public sealed class AvaloniaTestFrameworkAttribute : Attribute, ITestFrameworkAttribute |
|||
{ |
|||
/// <summary>
|
|||
/// Creates instance of <see cref="AvaloniaTestFrameworkAttribute"/>.
|
|||
/// </summary>
|
|||
/// <param name="appBuilderEntryPointType">
|
|||
/// Parameter from which <see cref="AppBuilder"/> should be created.
|
|||
/// It either needs to have BuildAvaloniaApp -> AppBuilder method or inherit Application.
|
|||
/// </param>
|
|||
public AvaloniaTestFrameworkAttribute( |
|||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] |
|||
Type appBuilderEntryPointType) { } |
|||
} |
|||
|
|||
/// <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) |
|||
{ |
|||
var builderType = attribute.GetConstructorArguments().First() as Type |
|||
?? throw new InvalidOperationException("AppBuilderEntryPointType parameter must be defined on the AvaloniaTestFrameworkAttribute attribute."); |
|||
return typeof(AvaloniaTestFramework<>).MakeGenericType(builderType); |
|||
} |
|||
} |
|||
@ -0,0 +1,61 @@ |
|||
using Avalonia.Threading; |
|||
using Xunit.Abstractions; |
|||
using Xunit.Sdk; |
|||
|
|||
namespace Avalonia.Headless.XUnit; |
|||
|
|||
internal class AvaloniaTestRunner<TAppBuilderEntry> : XunitTestAssemblyRunner |
|||
{ |
|||
private CancellationTokenSource? _cancellationTokenSource; |
|||
|
|||
public AvaloniaTestRunner(ITestAssembly testAssembly, IEnumerable<IXunitTestCase> testCases, |
|||
IMessageSink diagnosticMessageSink, IMessageSink executionMessageSink, |
|||
ITestFrameworkExecutionOptions executionOptions) : base(testAssembly, testCases, diagnosticMessageSink, |
|||
executionMessageSink, executionOptions) |
|||
{ |
|||
} |
|||
|
|||
protected override void SetupSyncContext(int maxParallelThreads) |
|||
{ |
|||
_cancellationTokenSource?.Dispose(); |
|||
_cancellationTokenSource = new CancellationTokenSource(); |
|||
SynchronizationContext.SetSynchronizationContext(InitNewApplicationContext(_cancellationTokenSource.Token).Result); |
|||
} |
|||
|
|||
public override void Dispose() |
|||
{ |
|||
_cancellationTokenSource?.Dispose(); |
|||
base.Dispose(); |
|||
} |
|||
|
|||
internal static Task<SynchronizationContext> InitNewApplicationContext(CancellationToken cancellationToken) |
|||
{ |
|||
var tcs = new TaskCompletionSource<SynchronizationContext>(); |
|||
|
|||
new Thread(() => |
|||
{ |
|||
try |
|||
{ |
|||
var appBuilder = AppBuilder.Configure(typeof(TAppBuilderEntry)); |
|||
|
|||
// If windowing subsystem wasn't initialized by user, force headless with default parameters.
|
|||
if (appBuilder.WindowingSubsystemName != "Headless") |
|||
{ |
|||
appBuilder = appBuilder.UseHeadless(new AvaloniaHeadlessPlatformOptions()); |
|||
} |
|||
|
|||
appBuilder.SetupWithoutStarting(); |
|||
|
|||
tcs.SetResult(SynchronizationContext.Current!); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
tcs.SetException(e); |
|||
} |
|||
|
|||
Dispatcher.UIThread.MainLoop(cancellationToken); |
|||
}) { IsBackground = true }.Start(); |
|||
|
|||
return tcs.Task; |
|||
} |
|||
} |
|||
Loading…
Reference in new issue