Browse Source

Merge pull request #707 from jkoritzinsky/Updated-PlatformDetect-and-Modules

Implemented extensibility system for Avalonia
pull/727/head
Steven Kirk 10 years ago
committed by GitHub
parent
commit
089c56f529
  1. 85
      src/Avalonia.Controls/AppBuilderBase.cs
  2. 2
      src/Avalonia.Controls/Avalonia.Controls.csproj
  3. 58
      src/Avalonia.Controls/Platform/ExportAvaloniaModuleAttribute.cs
  4. 27
      src/Avalonia.Controls/Platform/ExportWindowingSubsystemAttribute.cs
  5. 50
      src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs
  6. 6
      src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj
  7. 1
      src/Avalonia.SceneGraph/Avalonia.SceneGraph.csproj
  8. 28
      src/Avalonia.SceneGraph/Platform/ExportRenderingSubsystemAttribute.cs
  9. 2
      src/Gtk/Avalonia.Cairo/CairoPlatform.cs
  10. 6
      src/Gtk/Avalonia.Cairo/Properties/AssemblyInfo.cs
  11. 2
      src/Gtk/Avalonia.Gtk/GtkPlatform.cs
  12. 7
      src/Gtk/Avalonia.Gtk/Properties/AssemblyInfo.cs
  13. 4
      src/Skia/Avalonia.Skia.Desktop/Properties/AssemblyInfo.cs
  14. 2
      src/Skia/Avalonia.Skia/SkiaPlatform.cs
  15. 2
      src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
  16. 4
      src/Windows/Avalonia.Direct2D1/Properties/AssemblyInfo.cs
  17. 3
      src/Windows/Avalonia.Win32/Properties/AssemblyInfo.cs
  18. 2
      src/Windows/Avalonia.Win32/Win32Platform.cs
  19. 2
      src/iOS/Avalonia.iOS/iOSPlatform.cs
  20. 126
      tests/Avalonia.Controls.UnitTests/AppBuilderTests.cs
  21. 5
      tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj
  22. 8
      tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
  23. 3
      tests/Avalonia.UnitTests/TestServices.cs

85
src/Avalonia.Controls/AppBuilderBase.cs

@ -3,6 +3,7 @@
using System;
using System.Reflection;
using System.Linq;
using Avalonia.Platform;
namespace Avalonia.Controls
@ -21,33 +22,48 @@ namespace Avalonia.Controls
/// <summary>
/// Gets or sets a method to call the initialize the runtime platform services (e. g. AssetLoader)
/// </summary>
public Action RuntimePlatformServices { get; set; }
public Action RuntimePlatformServicesInitializer { get; private set; }
/// <summary>
/// Gets or sets the <see cref="Application"/> instance being initialized.
/// </summary>
public Application Instance { get; set; }
public Application Instance { get; protected set; }
/// <summary>
/// Gets or sets a method to call the initialize the windowing subsystem.
/// </summary>
public Action WindowingSubsystem { get; set; }
public Action WindowingSubsystemInitializer { get; private set; }
/// <summary>
/// Gets the name of the currently selected windowing subsystem.
/// </summary>
public string WindowingSubsystemName { get; private set; }
/// <summary>
/// Gets or sets a method to call the initialize the windowing subsystem.
/// </summary>
public Action RenderingSubsystem { get; set; }
public Action RenderingSubsystemInitializer { get; private set; }
/// <summary>
/// Gets the name of the currently selected rendering subsystem.
/// </summary>
public string RenderingSubsystemName { get; private set; }
/// <summary>
/// Gets or sets a method to call after the <see cref="Application"/> is setup.
/// </summary>
public Action<TAppBuilder> AfterSetupCallback { get; private set; } = builder => { };
/// <summary>
/// Gets or sets a method to call before <see cref="Start{TMainWindow}"/> is called on the
/// <see cref="Application"/>.
/// </summary>
public Action<TAppBuilder> BeforeStartCallback { get; set; }
public Action<TAppBuilder> BeforeStartCallback { get; private set; } = builder => { };
protected AppBuilderBase(IRuntimePlatform platform, Action platformSevices)
{
RuntimePlatform = platform;
RuntimePlatformServices = platformSevices;
RuntimePlatformServicesInitializer = platformSevices;
}
/// <summary>
@ -85,7 +101,13 @@ namespace Avalonia.Controls
/// <returns>An <typeparamref name="TAppBuilder"/> instance.</returns>
public TAppBuilder BeforeStarting(Action<TAppBuilder> callback)
{
BeforeStartCallback = callback;
BeforeStartCallback = (Action<TAppBuilder>)Delegate.Combine(BeforeStartCallback, callback);
return Self;
}
public TAppBuilder AfterSetup(Action<TAppBuilder> callback)
{
AfterSetupCallback = (Action<TAppBuilder>)Delegate.Combine(AfterSetupCallback, callback);
return Self;
}
@ -97,7 +119,7 @@ namespace Avalonia.Controls
where TMainWindow : Window, new()
{
Setup();
BeforeStartCallback?.Invoke(Self);
BeforeStartCallback(Self);
var window = new TMainWindow();
window.Show();
@ -119,9 +141,10 @@ namespace Avalonia.Controls
/// </summary>
/// <param name="initializer">The method to call to initialize the windowing subsystem.</param>
/// <returns>An <typeparamref name="TAppBuilder"/> instance.</returns>
public TAppBuilder UseWindowingSubsystem(Action initializer)
public TAppBuilder UseWindowingSubsystem(Action initializer, string name = "")
{
WindowingSubsystem = initializer;
WindowingSubsystemInitializer = initializer;
WindowingSubsystemName = name;
return Self;
}
@ -130,16 +153,17 @@ namespace Avalonia.Controls
/// </summary>
/// <param name="dll">The dll in which to look for subsystem.</param>
/// <returns>An <typeparamref name="TAppBuilder"/> instance.</returns>
public TAppBuilder UseWindowingSubsystem(string dll) => UseWindowingSubsystem(GetInitializer(dll));
public TAppBuilder UseWindowingSubsystem(string dll) => UseWindowingSubsystem(GetInitializer(dll), dll.Replace("Avalonia.", string.Empty));
/// <summary>
/// Specifies a rendering subsystem to use.
/// </summary>
/// <param name="initializer">The method to call to initialize the rendering subsystem.</param>
/// <returns>An <typeparamref name="TAppBuilder"/> instance.</returns>
public TAppBuilder UseRenderingSubsystem(Action initializer)
public TAppBuilder UseRenderingSubsystem(Action initializer, string name = "")
{
RenderingSubsystem = initializer;
RenderingSubsystemInitializer = initializer;
RenderingSubsystemName = name;
return Self;
}
@ -160,6 +184,28 @@ namespace Avalonia.Controls
init.Invoke(null, null);
};
public TAppBuilder UseAvaloniaModules() => AfterSetup(builder => SetupAvaloniaModules());
private void SetupAvaloniaModules()
{
var moduleInitializers = from assembly in AvaloniaLocator.Current.GetService<IRuntimePlatform>().GetLoadedAssemblies()
from attribute in assembly.GetCustomAttributes<ExportAvaloniaModuleAttribute>()
where attribute.ForWindowingSubsystem == ""
|| attribute.ForWindowingSubsystem == WindowingSubsystemName
where attribute.ForRenderingSubsystem == ""
|| attribute.ForRenderingSubsystem == RenderingSubsystemName
group attribute by attribute.Name into exports
select (from export in exports
orderby export.ForWindowingSubsystem.Length descending
orderby export.ForRenderingSubsystem.Length descending
select export).First().ModuleType into moduleType
select (from constructor in moduleType.GetTypeInfo().DeclaredConstructors
where constructor.GetParameters().Length == 0 && !constructor.IsStatic
select constructor).Single() into constructor
select (Action)(() => constructor.Invoke(new object[0]));
Delegate.Combine(moduleInitializers.ToArray()).DynamicInvoke();
}
/// <summary>
/// Sets up the platform-speciic services for the <see cref="Application"/>.
/// </summary>
@ -170,26 +216,27 @@ namespace Avalonia.Controls
throw new InvalidOperationException("No App instance configured.");
}
if (RuntimePlatformServices == null)
if (RuntimePlatformServicesInitializer == null)
{
throw new InvalidOperationException("No runtime platform services configured.");
}
if (WindowingSubsystem == null)
if (WindowingSubsystemInitializer == null)
{
throw new InvalidOperationException("No windowing system configured.");
}
if (RenderingSubsystem == null)
if (RenderingSubsystemInitializer == null)
{
throw new InvalidOperationException("No rendering system configured.");
}
Instance.RegisterServices();
RuntimePlatformServices();
WindowingSubsystem();
RenderingSubsystem();
RuntimePlatformServicesInitializer();
WindowingSubsystemInitializer();
RenderingSubsystemInitializer();
Instance.Initialize();
AfterSetupCallback(Self);
}
}
}

2
src/Avalonia.Controls/Avalonia.Controls.csproj

@ -59,6 +59,8 @@
<Compile Include="PointEventArgs.cs" />
<Compile Include="Embedding\EmbeddableControlRoot.cs" />
<Compile Include="Platform\IEmbeddableWindowImpl.cs" />
<Compile Include="Platform\ExportAvaloniaModuleAttribute.cs" />
<Compile Include="Platform\ExportWindowingSubsystemAttribute.cs" />
<Compile Include="WindowIcon.cs" />
<Compile Include="IPseudoClasses.cs" />
<Compile Include="DropDownItem.cs" />

58
src/Avalonia.Controls/Platform/ExportAvaloniaModuleAttribute.cs

@ -0,0 +1,58 @@
using System;
namespace Avalonia.Platform
{
/// <summary>
/// Defines an "Avalonia Module", a 3rd party extension to Avalonia that can be automatically initialized by an AppBuilder instance.
/// </summary>
/// <remarks>
/// Avalonia Modules can either be platform independent (ex default control styles provider) or dependent on a
/// specific windowing or rendering subsystem being used (ex native rendering speedup, subsystem-specific interop backends).
/// In the case of a subsystem-specific module, you can specify multiple module implementations, and also a fallback
/// platform-independent module if you so choose. Additionally, these different implementations can be in different assemblies.
/// They just need to all share the same module name.
///
/// For example, if I had a module Foo that has a special back-end for Skia and a less performant/less user friendly back-end for
/// any other rendering subsystem, I would do the following:
/// <code>
/// // In assembly FooModuleSkia.dll
/// [assembly:ExportAvaloniaModule("Foo", typeof(FooModuleSkia), ForRenderingSubsystem="Skia")]
///
/// class FooModuleSkia
/// {
/// public FooModuleSkia()
/// {
/// InitializeModule();
/// }
/// }
///
/// // In assembly FooModuleFallback.dll
/// [assembly:ExportAvaloniaModule("Foo", typeof(FooModuleFallback))]
///
/// class FooModuleFallback
/// {
/// public FooModuleFallback()
/// {
/// InitializeModule();
/// }
/// }
///
/// </code>
/// The fallback module will only be initialized if the Skia-specific module is not applicable.
/// </remarks>
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public class ExportAvaloniaModuleAttribute : Attribute
{
public ExportAvaloniaModuleAttribute(string name, Type moduleType)
{
Name = name;
ModuleType = moduleType;
}
public string Name { get; private set; }
public Type ModuleType { get; private set; }
public string ForWindowingSubsystem { get; set; } = "";
public string ForRenderingSubsystem { get; set; } = "";
}
}

27
src/Avalonia.Controls/Platform/ExportWindowingSubsystemAttribute.cs

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Avalonia.Platform
{
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public class ExportWindowingSubsystemAttribute : Attribute
{
public ExportWindowingSubsystemAttribute(OperatingSystemType requiredRuntimePlatform, int priority, string name, Type initializationType, string initializationMethod)
{
Name = name;
InitializationType = initializationType;
InitializationMethod = initializationMethod;
RequiredOS = requiredRuntimePlatform;
Priority = priority;
}
public string InitializationMethod { get; private set; }
public Type InitializationType { get; private set; }
public string Name { get; private set; }
public int Priority { get; private set; }
public OperatingSystemType RequiredOS { get; private set; }
}
}

50
src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs

@ -7,6 +7,7 @@ using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Platform;
using Avalonia.Shared.PlatformSupport;
using System.IO;
namespace Avalonia
{
@ -23,18 +24,47 @@ namespace Avalonia
public AppBuilder UsePlatformDetect()
{
var platformId = (int)Environment.OSVersion.Platform;
if (platformId == 4 || platformId == 6)
{
UseRenderingSubsystem("Avalonia.Cairo");
UseWindowingSubsystem("Avalonia.Gtk");
}
else
var os = RuntimePlatform.GetRuntimeInfo().OperatingSystem;
LoadAssembliesInDirectory();
var windowingSubsystemAttribute = (from assembly in RuntimePlatform.GetLoadedAssemblies()
from attribute in assembly.GetCustomAttributes<ExportWindowingSubsystemAttribute>()
where attribute.RequiredOS == os
orderby attribute.Priority ascending
select attribute).First();
var renderingSubsystemAttribute = (from assembly in RuntimePlatform.GetLoadedAssemblies()
from attribute in assembly.GetCustomAttributes<ExportRenderingSubsystemAttribute>()
where attribute.RequiredOS == os
where attribute.RequiresWindowingSubsystem == null
|| attribute.RequiresWindowingSubsystem == windowingSubsystemAttribute.Name
orderby attribute.Priority ascending
select attribute).First();
UseWindowingSubsystem(() => windowingSubsystemAttribute.InitializationType
.GetRuntimeMethod(windowingSubsystemAttribute.InitializationMethod, Type.EmptyTypes).Invoke(null, null),
windowingSubsystemAttribute.Name);
UseRenderingSubsystem(() => renderingSubsystemAttribute.InitializationType
.GetRuntimeMethod(renderingSubsystemAttribute.InitializationMethod, Type.EmptyTypes).Invoke(null, null),
renderingSubsystemAttribute.Name);
return this;
}
private void LoadAssembliesInDirectory()
{
foreach (var file in new FileInfo(Assembly.GetEntryAssembly().Location).Directory.EnumerateFiles("*.dll"))
{
UseRenderingSubsystem("Avalonia.Direct2D1");
UseWindowingSubsystem("Avalonia.Win32");
try
{
Assembly.LoadFile(file.FullName);
}
catch (Exception)
{
}
}
return this;
}
}
}

6
src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj

@ -64,6 +64,10 @@
<Project>{D2221C82-4A25-4583-9B43-D791E3F6820C}</Project>
<Name>Avalonia.Controls</Name>
</ProjectReference>
<ProjectReference Include="..\Avalonia.SceneGraph\Avalonia.SceneGraph.csproj">
<Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project>
<Name>Avalonia.SceneGraph</Name>
</ProjectReference>
<ProjectReference Include="..\Avalonia.Styling\Avalonia.Styling.csproj">
<Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project>
<Name>Avalonia.Styling</Name>
@ -81,4 +85,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>

1
src/Avalonia.SceneGraph/Avalonia.SceneGraph.csproj

@ -100,6 +100,7 @@
<Compile Include="Media\FormattedText.cs" />
<Compile Include="Media\Geometry.cs" />
<Compile Include="Media\IDrawingContext.cs" />
<Compile Include="Platform\ExportRenderingSubsystemAttribute.cs" />
<Compile Include="RenderTargetCorruptedException.cs" />
<Compile Include="VisualTree\IVisual.cs" />
<Compile Include="Media\Imaging\Bitmap.cs" />

28
src/Avalonia.SceneGraph/Platform/ExportRenderingSubsystemAttribute.cs

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Avalonia.Platform
{
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public class ExportRenderingSubsystemAttribute : Attribute
{
public ExportRenderingSubsystemAttribute(OperatingSystemType requiredOS, int priority, string name, Type initializationType, string initializationMethod)
{
Name = name;
InitializationType = initializationType;
InitializationMethod = initializationMethod;
RequiredOS = requiredOS;
Priority = priority;
}
public string InitializationMethod { get; private set; }
public Type InitializationType { get; private set; }
public string Name { get; private set; }
public int Priority { get; private set; }
public OperatingSystemType RequiredOS { get; private set; }
public string RequiresWindowingSubsystem { get; set; }
}
}

2
src/Gtk/Avalonia.Cairo/CairoPlatform.cs

@ -14,7 +14,7 @@ namespace Avalonia
{
public static T UseCairo<T>(this T builder) where T : AppBuilderBase<T>, new()
{
builder.RenderingSubsystem = Avalonia.Cairo.CairoPlatform.Initialize;
builder.UseRenderingSubsystem(Cairo.CairoPlatform.Initialize, "Cairo");
return builder;
}
}

6
src/Gtk/Avalonia.Cairo/Properties/AssemblyInfo.cs

@ -1,6 +1,8 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Cairo;
using Avalonia.Platform;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -37,3 +39,7 @@ using System.Runtime.InteropServices;
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: ExportRenderingSubsystem(OperatingSystemType.WinNT, 2, "Cairo", typeof(CairoPlatform), nameof(CairoPlatform.Initialize), RequiresWindowingSubsystem = "GTK")]
[assembly: ExportRenderingSubsystem(OperatingSystemType.Linux, 1, "Cairo", typeof(CairoPlatform), nameof(CairoPlatform.Initialize), RequiresWindowingSubsystem = "GTK")]
[assembly: ExportRenderingSubsystem(OperatingSystemType.OSX, 2, "Cairo", typeof(CairoPlatform), nameof(CairoPlatform.Initialize), RequiresWindowingSubsystem = "GTK")]

2
src/Gtk/Avalonia.Gtk/GtkPlatform.cs

@ -16,7 +16,7 @@ namespace Avalonia
{
public static T UseGtk<T>(this T builder) where T : AppBuilderBase<T>, new()
{
builder.WindowingSubsystem = Avalonia.Gtk.GtkPlatform.Initialize;
builder.UseWindowingSubsystem(Gtk.GtkPlatform.Initialize, "Gtk");
return builder;
}
}

7
src/Gtk/Avalonia.Gtk/Properties/AssemblyInfo.cs

@ -1,6 +1,8 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Gtk;
using Avalonia.Platform;
using System.Reflection;
using System.Runtime.CompilerServices;
@ -19,3 +21,8 @@ using System.Runtime.CompilerServices;
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
[assembly: AssemblyVersion("1.0.*")]
[assembly: ExportWindowingSubsystem(OperatingSystemType.WinNT, 2, "GTK", typeof(GtkPlatform), nameof(GtkPlatform.Initialize))]
[assembly: ExportWindowingSubsystem(OperatingSystemType.Linux, 1, "GTK", typeof(GtkPlatform), nameof(GtkPlatform.Initialize))]
[assembly: ExportWindowingSubsystem(OperatingSystemType.OSX, 2, "GTK", typeof(GtkPlatform), nameof(GtkPlatform.Initialize))]

4
src/Skia/Avalonia.Skia.Desktop/Properties/AssemblyInfo.cs

@ -1,3 +1,5 @@
using Avalonia.Platform;
using Avalonia.Skia;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -34,3 +36,5 @@ using System.Runtime.InteropServices;
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: ExportRenderingSubsystem(OperatingSystemType.WinNT, 3, "Skia", typeof(SkiaPlatform), nameof(SkiaPlatform.Initialize))]

2
src/Skia/Avalonia.Skia/SkiaPlatform.cs

@ -10,7 +10,7 @@ namespace Avalonia
{
public static T UseSkia<T>(this T builder) where T : AppBuilderBase<T>, new()
{
builder.RenderingSubsystem = Avalonia.Skia.SkiaPlatform.Initialize;
builder.UseRenderingSubsystem(Skia.SkiaPlatform.Initialize, "Skia");
return builder;
}
}

2
src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs

@ -14,7 +14,7 @@ namespace Avalonia
{
public static T UseDirect2D1<T>(this T builder) where T : AppBuilderBase<T>, new()
{
builder.RenderingSubsystem = Avalonia.Direct2D1.Direct2D1Platform.Initialize;
builder.UseRenderingSubsystem(Direct2D1.Direct2D1Platform.Initialize, "Direct2D1");
return builder;
}
}

4
src/Windows/Avalonia.Direct2D1/Properties/AssemblyInfo.cs

@ -2,5 +2,9 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Reflection;
using Avalonia.Platform;
using Avalonia.Direct2D1;
[assembly: AssemblyTitle("Avalonia.Direct2D1")]
[assembly: ExportRenderingSubsystem(OperatingSystemType.WinNT, 1, "Direct2D1", typeof(Direct2D1Platform), nameof(Direct2D1Platform.Initialize))]

3
src/Windows/Avalonia.Win32/Properties/AssemblyInfo.cs

@ -1,6 +1,9 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Platform;
using Avalonia.Win32;
using System.Reflection;
[assembly: AssemblyTitle("Avalonia.Win32")]
[assembly: ExportWindowingSubsystem(OperatingSystemType.WinNT, 1, "Win32", typeof(Win32Platform), nameof(Win32Platform.Initialize))]

2
src/Windows/Avalonia.Win32/Win32Platform.cs

@ -23,7 +23,7 @@ namespace Avalonia
{
public static T UseWin32<T>(this T builder) where T : AppBuilderBase<T>, new()
{
builder.WindowingSubsystem = Avalonia.Win32.Win32Platform.Initialize;
builder.UseWindowingSubsystem(Win32.Win32Platform.Initialize, "Win32");
return builder;
}
}

2
src/iOS/Avalonia.iOS/iOSPlatform.cs

@ -15,7 +15,7 @@ namespace Avalonia
{
public static T UseiOS<T>(this T builder) where T : AppBuilderBase<T>, new()
{
builder.WindowingSubsystem = Avalonia.iOS.iOSPlatform.Initialize;
builder.UseWindowingSubsystem(iOSPlatform.Initialize, "iOS");
return builder;
}

126
tests/Avalonia.Controls.UnitTests/AppBuilderTests.cs

@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xunit;
using Avalonia.Controls.UnitTests;
using Avalonia.Platform;
[assembly: ExportAvaloniaModule("DefaultModule", typeof(AppBuilderTests.DefaultModule))]
[assembly: ExportAvaloniaModule("RenderingModule", typeof(AppBuilderTests.Direct2DModule), ForRenderingSubsystem = "Direct2D1")]
[assembly: ExportAvaloniaModule("RenderingModule", typeof(AppBuilderTests.SkiaModule), ForRenderingSubsystem = "Skia")]
[assembly: ExportAvaloniaModule("RenderingModule", typeof(AppBuilderTests.DefaultRenderingModule))]
namespace Avalonia.Controls.UnitTests
{
public class AppBuilderTests
{
class App : Application
{
}
public class DefaultModule
{
public static bool IsLoaded = false;
public DefaultModule()
{
IsLoaded = true;
}
}
public class DefaultRenderingModule
{
public static bool IsLoaded = false;
public DefaultRenderingModule()
{
IsLoaded = true;
}
}
public class Direct2DModule
{
public static bool IsLoaded = false;
public Direct2DModule()
{
IsLoaded = true;
}
}
public class SkiaModule
{
public static bool IsLoaded = false;
public SkiaModule()
{
IsLoaded = true;
}
}
[Fact]
public void LoadsDefaultModule()
{
using (AvaloniaLocator.EnterScope())
{
ResetModuleLoadStates();
AppBuilder.Configure<App>()
.UseWindowingSubsystem(() => { })
.UseRenderingSubsystem(() => { })
.UseAvaloniaModules()
.SetupWithoutStarting();
Assert.True(DefaultModule.IsLoaded);
}
}
[Fact]
public void LoadsRenderingModuleWithMatchingRenderingSubsystem()
{
using (AvaloniaLocator.EnterScope())
{
ResetModuleLoadStates();
var builder = AppBuilder.Configure<App>()
.UseWindowingSubsystem(() => { })
.UseRenderingSubsystem(() => { }, "Direct2D1");
builder.UseAvaloniaModules().SetupWithoutStarting();
Assert.False(DefaultRenderingModule.IsLoaded);
Assert.True(Direct2DModule.IsLoaded);
Assert.False(SkiaModule.IsLoaded);
ResetModuleLoadStates();
builder = AppBuilder.Configure<App>()
.UseWindowingSubsystem(() => { })
.UseRenderingSubsystem(() => { }, "Skia");
builder.UseAvaloniaModules().SetupWithoutStarting();
Assert.False(DefaultRenderingModule.IsLoaded);
Assert.False(Direct2DModule.IsLoaded);
Assert.True(SkiaModule.IsLoaded);
}
}
[Fact]
public void LoadsRenderingModuleWithoutDependenciesWhenNoModuleMatches()
{
using (AvaloniaLocator.EnterScope())
{
ResetModuleLoadStates();
var builder = AppBuilder.Configure<App>()
.UseWindowingSubsystem(() => { })
.UseRenderingSubsystem(() => { }, "Cairo");
builder.UseAvaloniaModules().SetupWithoutStarting();
Assert.True(DefaultRenderingModule.IsLoaded);
Assert.False(Direct2DModule.IsLoaded);
Assert.False(SkiaModule.IsLoaded);
}
}
private static void ResetModuleLoadStates()
{
DefaultModule.IsLoaded = false;
DefaultRenderingModule.IsLoaded = false;
Direct2DModule.IsLoaded = false;
SkiaModule.IsLoaded = false;
}
}
}

5
tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj

@ -94,6 +94,7 @@
<Otherwise />
</Choose>
<ItemGroup>
<Compile Include="AppBuilderTests.cs" />
<Compile Include="ClassesTests.cs" />
<Compile Include="LayoutTransformControlTests.cs" />
<Compile Include="Presenters\ItemsPresenterTests_Virtualization_Simple.cs" />
@ -152,6 +153,10 @@
<Compile Include="WrapPanelTests.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj">
<Project>{4a1abb09-9047-4bd5-a4ad-a055e52c5ee0}</Project>
<Name>Avalonia.DotNetFrameworkRuntime</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj">
<Project>{3e53a01a-b331-47f3-b828-4a5717e77a24}</Project>
<Name>Avalonia.Markup.Xaml</Name>

8
tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs

@ -267,12 +267,16 @@ namespace Avalonia.Controls.UnitTests.Primitives
var globalStyles = new Mock<IGlobalStyles>();
globalStyles.Setup(x => x.Styles).Returns(styles);
var renderInterface = new Mock<IPlatformRenderInterface>();
renderInterface.Setup(x => x.CreateRenderer(It.IsAny<IPlatformHandle>())).Returns(() => new Mock<IRenderTarget>().Object);
AvaloniaLocator.CurrentMutable
.Bind<ILayoutManager>().ToTransient<LayoutManager>()
.Bind<IGlobalStyles>().ToFunc(() => globalStyles.Object)
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock())
.Bind<IStyler>().ToTransient<Styler>();
.Bind<IStyler>().ToTransient<Styler>()
.Bind<IPlatformRenderInterface>().ToFunc(() => renderInterface.Object);
return result;
}

3
tests/Avalonia.UnitTests/TestServices.cs

@ -152,7 +152,8 @@ namespace Avalonia.UnitTests
It.IsAny<FontWeight>(),
It.IsAny<TextWrapping>()) == Mock.Of<IFormattedTextImpl>() &&
x.CreateStreamGeometry() == Mock.Of<IStreamGeometryImpl>(
y => y.Open() == Mock.Of<IStreamGeometryContextImpl>()));
y => y.Open() == Mock.Of<IStreamGeometryContextImpl>()) &&
x.CreateRenderer(It.IsAny<IPlatformHandle>()) == Mock.Of<IRenderTarget>());
}
}
}

Loading…
Cancel
Save