diff --git a/src/Avalonia.Controls/AppBuilderBase.cs b/src/Avalonia.Controls/AppBuilderBase.cs
index 1610d33f9b..7af3deef34 100644
--- a/src/Avalonia.Controls/AppBuilderBase.cs
+++ b/src/Avalonia.Controls/AppBuilderBase.cs
@@ -14,6 +14,8 @@ namespace Avalonia.Controls
/// The type of the AppBuilder class itself.
public abstract class AppBuilderBase where TAppBuilder : AppBuilderBase, new()
{
+ private static bool s_setupWasAlreadyCalled;
+
///
/// Gets or sets the instance.
///
@@ -207,6 +209,17 @@ namespace Avalonia.Controls
public TAppBuilder UseAvaloniaModules() => AfterSetup(builder => SetupAvaloniaModules());
+ private bool CheckSetup { get; set; } = true;
+
+ ///
+ /// Set this AppBuilder to ignore the setup check. Used for testing purposes.
+ ///
+ internal TAppBuilder IgnoreSetupCheck()
+ {
+ CheckSetup = false;
+ return Self;
+ }
+
private void SetupAvaloniaModules()
{
var moduleInitializers = from assembly in AvaloniaLocator.Current.GetService().GetLoadedAssemblies()
@@ -252,6 +265,13 @@ namespace Avalonia.Controls
throw new InvalidOperationException("No rendering system configured.");
}
+ if (s_setupWasAlreadyCalled && CheckSetup)
+ {
+ throw new InvalidOperationException("Setup was already called on one of AppBuilder instances");
+ }
+
+ s_setupWasAlreadyCalled = true;
+
Instance.RegisterServices();
RuntimePlatformServicesInitializer();
WindowingSubsystemInitializer();
diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs
index abae080515..06c1a8b4cc 100644
--- a/src/Avalonia.Controls/Application.cs
+++ b/src/Avalonia.Controls/Application.cs
@@ -175,6 +175,15 @@ namespace Avalonia
closable.Closed += (s, e) => source.Cancel();
Dispatcher.UIThread.MainLoop(source.Token);
}
+
+ ///
+ /// Runs the application's main loop until the is cancelled.
+ ///
+ /// The token to track
+ public void Run(CancellationToken token)
+ {
+ Dispatcher.UIThread.MainLoop(token);
+ }
///
/// Exits the application
diff --git a/tests/Avalonia.Controls.UnitTests/AppBuilderTests.cs b/tests/Avalonia.Controls.UnitTests/AppBuilderTests.cs
index 867f740a3a..60c53d126c 100644
--- a/tests/Avalonia.Controls.UnitTests/AppBuilderTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/AppBuilderTests.cs
@@ -65,6 +65,7 @@ namespace Avalonia.Controls.UnitTests
{
ResetModuleLoadStates();
AppBuilder.Configure()
+ .IgnoreSetupCheck()
.UseWindowingSubsystem(() => { })
.UseRenderingSubsystem(() => { })
.UseAvaloniaModules()
@@ -81,6 +82,7 @@ namespace Avalonia.Controls.UnitTests
{
ResetModuleLoadStates();
var builder = AppBuilder.Configure()
+ .IgnoreSetupCheck()
.UseWindowingSubsystem(() => { })
.UseRenderingSubsystem(() => { }, "Direct2D1");
builder.UseAvaloniaModules().SetupWithoutStarting();
@@ -90,6 +92,7 @@ namespace Avalonia.Controls.UnitTests
ResetModuleLoadStates();
builder = AppBuilder.Configure()
+ .IgnoreSetupCheck()
.UseWindowingSubsystem(() => { })
.UseRenderingSubsystem(() => { }, "Skia");
builder.UseAvaloniaModules().SetupWithoutStarting();
@@ -99,13 +102,14 @@ namespace Avalonia.Controls.UnitTests
}
}
- [Fact (Skip = "We don't have rendering modules with dependencies right now")]
+ [Fact]
public void LoadsRenderingModuleWithoutDependenciesWhenNoModuleMatches()
{
using (AvaloniaLocator.EnterScope())
{
ResetModuleLoadStates();
var builder = AppBuilder.Configure()
+ .IgnoreSetupCheck()
.UseWindowingSubsystem(() => { })
.UseRenderingSubsystem(() => { }, "TBD");
builder.UseAvaloniaModules().SetupWithoutStarting();