Browse Source

Started refactoring unit tests.

Added a shared test project with a UnitTestApplication and use it in
LeakTests.
Steven Kirk 9 years ago
parent
commit
2283cfa486

+ 31 - 3
Perspex.sln

@@ -140,6 +140,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.LeakTests", "tests\
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog", "samples\ControlCatalog\ControlCatalog.csproj", "{61BEC86C-F307-4295-B5B8-9428610D7D55}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.UnitTests", "tests\Perspex.UnitTests\Perspex.UnitTests.csproj", "{88060192-33D5-4932-B0F9-8BD2763E857D}"
+EndProject
 Global
 	GlobalSection(SharedMSBuildProjectFiles) = preSolution
 		src\Shared\RenderHelpers\RenderHelpers.projitems*{fb05ac90-89ba-4f2f-a924-f37875fb547c}*SharedItemsImports = 4
@@ -148,19 +150,20 @@ Global
 		src\Shared\PlatformSupport\PlatformSupport.projitems*{e4d9629c-f168-4224-3f51-a5e482ffbc42}*SharedItemsImports = 13
 		src\Skia\Perspex.Skia\Perspex.Skia.projitems*{2f59f3d0-748d-4652-b01e-e0d954756308}*SharedItemsImports = 13
 		src\Shared\PlatformSupport\PlatformSupport.projitems*{db070a10-bf39-4752-8456-86e9d5928478}*SharedItemsImports = 4
-		src\Shared\RenderHelpers\RenderHelpers.projitems*{925dd807-b651-475f-9f7c-cbeb974ce43d}*SharedItemsImports = 4
 		src\Skia\Perspex.Skia\Perspex.Skia.projitems*{925dd807-b651-475f-9f7c-cbeb974ce43d}*SharedItemsImports = 4
+		src\Shared\RenderHelpers\RenderHelpers.projitems*{925dd807-b651-475f-9f7c-cbeb974ce43d}*SharedItemsImports = 4
 		samples\TestApplicationShared\TestApplicationShared.projitems*{78345174-5b52-4a14-b9fd-d5f2428137f0}*SharedItemsImports = 13
 		src\Shared\PlatformSupport\PlatformSupport.projitems*{54f237d5-a70a-4752-9656-0c70b1a7b047}*SharedItemsImports = 4
 		samples\TestApplicationShared\TestApplicationShared.projitems*{ff69b927-c545-49ae-8e16-3d14d621aa12}*SharedItemsImports = 4
 		src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13
 		src\Shared\PlatformSupport\PlatformSupport.projitems*{811a76cf-1cf6-440f-963b-bbe31bd72a82}*SharedItemsImports = 4
-		src\Shared\RenderHelpers\RenderHelpers.projitems*{47be08a7-5985-410b-9ffc-2264b8ea595f}*SharedItemsImports = 4
+		src\Shared\PlatformSupport\PlatformSupport.projitems*{88060192-33d5-4932-b0f9-8bd2763e857d}*SharedItemsImports = 4
 		src\Skia\Perspex.Skia\Perspex.Skia.projitems*{47be08a7-5985-410b-9ffc-2264b8ea595f}*SharedItemsImports = 4
+		src\Shared\RenderHelpers\RenderHelpers.projitems*{47be08a7-5985-410b-9ffc-2264b8ea595f}*SharedItemsImports = 4
 		samples\TestApplicationShared\TestApplicationShared.projitems*{8c923867-8a8f-4f6b-8b80-47d9e8436166}*SharedItemsImports = 4
 		samples\TestApplicationShared\TestApplicationShared.projitems*{e3a1060b-50d0-44e8-88b6-f44ef2e5bd72}*SharedItemsImports = 4
-		src\Shared\RenderHelpers\RenderHelpers.projitems*{bd43f7c0-396b-4aa1-bad9-dfde54d51298}*SharedItemsImports = 4
 		src\Skia\Perspex.Skia\Perspex.Skia.projitems*{bd43f7c0-396b-4aa1-bad9-dfde54d51298}*SharedItemsImports = 4
+		src\Shared\RenderHelpers\RenderHelpers.projitems*{bd43f7c0-396b-4aa1-bad9-dfde54d51298}*SharedItemsImports = 4
 		src\Shared\RenderHelpers\RenderHelpers.projitems*{3e908f67-5543-4879-a1dc-08eace79b3cd}*SharedItemsImports = 4
 		src\Shared\PlatformSupport\PlatformSupport.projitems*{e1aa3dbf-9056-4530-9376-18119a7a3ffe}*SharedItemsImports = 4
 	EndGlobalSection
@@ -1294,6 +1297,30 @@ Global
 		{61BEC86C-F307-4295-B5B8-9428610D7D55}.Release|iPhone.Build.0 = Release|Any CPU
 		{61BEC86C-F307-4295-B5B8-9428610D7D55}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
 		{61BEC86C-F307-4295-B5B8-9428610D7D55}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.AppStore|Any CPU.Build.0 = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.AppStore|iPhone.ActiveCfg = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.AppStore|iPhone.Build.0 = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Debug|iPhone.Build.0 = Debug|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Release|Any CPU.Build.0 = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Release|iPhone.ActiveCfg = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Release|iPhone.Build.0 = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+		{88060192-33D5-4932-B0F9-8BD2763E857D}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -1338,5 +1365,6 @@ Global
 		{8C923867-8A8F-4F6B-8B80-47D9E8436166} = {0CB0B92E-6CFF-4240-80A5-CCAFE75D91E1}
 		{E1AA3DBF-9056-4530-9376-18119A7A3FFE} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
 		{61BEC86C-F307-4295-B5B8-9428610D7D55} = {9B9E3891-2366-4253-A952-D08BCEB71098}
+		{88060192-33D5-4932-B0F9-8BD2763E857D} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
 	EndGlobalSection
 EndGlobal

+ 7 - 8
src/Perspex.Application/Application.cs

@@ -59,12 +59,7 @@ namespace Perspex
                 throw new InvalidOperationException("Cannot create more than one Application instance.");
             }
 
-            Current = this;
-        }
-
-        public static void RegisterPlatformCallback(Action cb)
-        {
-            _platformInitializationCallback = cb;
+            PerspexLocator.CurrentMutable.BindToSelf(this);
         }
 
         /// <summary>
@@ -75,8 +70,7 @@ namespace Perspex
         /// </value>
         public static Application Current
         {
-            get;
-            private set;
+            get { return PerspexLocator.Current.GetService<Application>(); }
         }
 
         /// <summary>
@@ -140,6 +134,11 @@ namespace Perspex
         /// </summary>
         IStyleHost IStyleHost.StylingParent => null;
 
+        public static void RegisterPlatformCallback(Action cb)
+        {
+            _platformInitializationCallback = cb;
+        }
+
         /// <summary>
         /// Runs the application's main loop until the <see cref="ICloseable"/> is closed.
         /// </summary>

+ 6 - 1
src/Perspex.Base/Threading/DispatcherTimer.cs

@@ -181,6 +181,12 @@ namespace Perspex.Threading
             if (!IsEnabled)
             {
                 IPlatformThreadingInterface threading = PerspexLocator.Current.GetService<IPlatformThreadingInterface>();
+
+                if (threading == null)
+                {
+                    throw new Exception("Could not start timer: IPlatformThreadingInterface is not registered.");
+                }
+
                 _timer = threading.StartTimer(Interval, InternalTick);
             }
         }
@@ -192,7 +198,6 @@ namespace Perspex.Threading
         {
             if (IsEnabled)
             {
-                IPlatformThreadingInterface threading = PerspexLocator.Current.GetService<IPlatformThreadingInterface>();
                 _timer.Dispose();
                 _timer = null;
             }

+ 8 - 2
src/Perspex.Controls/Platform/PlatformManager.cs

@@ -192,8 +192,14 @@ namespace Perspex.Controls.Platform
         public static IWindowImpl CreateWindow()
         {
             var platform = PerspexLocator.Current.GetService<IWindowingPlatform>();
-            return
-                new WindowDecorator(s_designerMode ? platform.CreateEmbeddableWindow() : platform.CreateWindow());
+            
+            if (platform == null)
+            {
+                throw new Exception("Could not CreateWindow(): IWindowingPlatform is not registered.");
+            }
+
+            var window = s_designerMode ? platform.CreateEmbeddableWindow() : platform.CreateWindow();
+            return new WindowDecorator(window);
         }
 
         public static IPopupImpl CreatePopup()

+ 13 - 3
src/Perspex.Input/Cursors.cs

@@ -56,12 +56,22 @@ namespace Perspex.Input
         }
 
         public Cursor(StandardCursorType cursorType)
-            : this(
-                ((IStandardCursorFactory)PerspexLocator.Current.GetService(typeof(IStandardCursorFactory))).GetCursor(
-                    cursorType))
+            : this(GetCursor(cursorType))
         {
         }
 
         public IPlatformHandle PlatformCursor { get; }
+
+        private static IPlatformHandle GetCursor(StandardCursorType type)
+        {
+            var platform = PerspexLocator.Current.GetService<IStandardCursorFactory>();
+
+            if (platform == null)
+            {
+                throw new Exception("Could not create Cursor: IStandardCursorFactory not registered.");
+            }
+
+            return platform.GetCursor(type);
+        }
     }
 }

+ 5 - 0
src/Perspex.SceneGraph/Media/FormattedText.cs

@@ -42,6 +42,11 @@ namespace Perspex.Media
 
             var platform = PerspexLocator.Current.GetService<IPlatformRenderInterface>();
 
+            if (platform == null)
+            {
+                throw new Exception("Could not create FormattedText: IPlatformRenderInterface not registered.");
+            }
+
             PlatformImpl = platform.CreateFormattedText(
                 text,
                 fontFamilyName,

+ 198 - 174
tests/Perspex.LeakTests/ControlTests.cs

@@ -9,6 +9,7 @@ using Perspex.Controls;
 using Perspex.Controls.Primitives;
 using Perspex.Controls.Templates;
 using Perspex.Layout;
+using Perspex.UnitTests;
 using Perspex.VisualTree;
 using Xunit;
 using Xunit.Abstractions;
@@ -20,282 +21,305 @@ namespace Perspex.LeakTests
     {
         public ControlTests(ITestOutputHelper atr)
         {
-            TestApp.Initialize();
             DotMemoryUnitTestOutput.SetOutputMethod(atr.WriteLine);
         }
 
         [Fact]
         public void Canvas_Is_Freed()
         {
-            Func<Window> run = () =>
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
             {
-                var window = new Window
+                Func<Window> run = () =>
                 {
-                    Content = new Canvas()
-                };
+                    var window = new Window
+                    {
+                        Content = new Canvas()
+                    };
 
-                // Do a layout and make sure that Canvas gets added to visual tree.
-                LayoutManager.Instance.ExecuteInitialLayoutPass(window);
-                Assert.IsType<Canvas>(window.Presenter.Child);
+                    // Do a layout and make sure that Canvas gets added to visual tree.
+                    LayoutManager.Instance.ExecuteInitialLayoutPass(window);
+                    Assert.IsType<Canvas>(window.Presenter.Child);
 
-                // Clear the content and ensure the Canvas is removed.
-                window.Content = null;
-                LayoutManager.Instance.ExecuteLayoutPass();
-                Assert.Null(window.Presenter.Child);
+                    // Clear the content and ensure the Canvas is removed.
+                    window.Content = null;
+                    LayoutManager.Instance.ExecuteLayoutPass();
+                    Assert.Null(window.Presenter.Child);
 
-                return window;
-            };
+                    return window;
+                };
 
-            var result = run();
+                var result = run();
 
-            dotMemory.Check(memory =>
-                Assert.Equal(0, memory.GetObjects(where => where.Type.Is<Canvas>()).ObjectsCount));
+                dotMemory.Check(memory =>
+                    Assert.Equal(0, memory.GetObjects(where => where.Type.Is<Canvas>()).ObjectsCount));
+            }
         }
 
         [Fact]
         public void Named_Canvas_Is_Freed()
         {
-            Func<Window> run = () =>
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
             {
-                var window = new Window
+                Func<Window> run = () =>
                 {
-                    Content = new Canvas
+                    var window = new Window
                     {
-                        Name = "foo"
-                    }
-                };
+                        Content = new Canvas
+                        {
+                            Name = "foo"
+                        }
+                    };
 
-                // Do a layout and make sure that Canvas gets added to visual tree.
-                LayoutManager.Instance.ExecuteInitialLayoutPass(window);
-                Assert.IsType<Canvas>(window.Find<Canvas>("foo"));
-                Assert.IsType<Canvas>(window.Presenter.Child);
+                    // Do a layout and make sure that Canvas gets added to visual tree.
+                    LayoutManager.Instance.ExecuteInitialLayoutPass(window);
+                    Assert.IsType<Canvas>(window.Find<Canvas>("foo"));
+                    Assert.IsType<Canvas>(window.Presenter.Child);
 
-                // Clear the content and ensure the Canvas is removed.
-                window.Content = null;
-                LayoutManager.Instance.ExecuteLayoutPass();
-                Assert.Null(window.Presenter.Child);
+                    // Clear the content and ensure the Canvas is removed.
+                    window.Content = null;
+                    LayoutManager.Instance.ExecuteLayoutPass();
+                    Assert.Null(window.Presenter.Child);
 
-                return window;
-            };
+                    return window;
+                };
 
-            var result = run();
+                var result = run();
 
-            dotMemory.Check(memory =>
-                Assert.Equal(0, memory.GetObjects(where => where.Type.Is<Canvas>()).ObjectsCount));
+                dotMemory.Check(memory =>
+                    Assert.Equal(0, memory.GetObjects(where => where.Type.Is<Canvas>()).ObjectsCount));
+            }
         }
 
         [Fact]
         public void Templated_Child_Is_Freed_When_Template_Cleared()
         {
-            Func<Window> run = () =>
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
             {
-                var window = new Window
+                Func<Window> run = () =>
                 {
-                    Content = new TestTemplatedControl()
-                };
+                    var window = new Window
+                    {
+                        Content = new TestTemplatedControl()
+                    };
 
-                // Do a layout and make sure that the control gets added to visual tree and its
-                // template applied.
-                LayoutManager.Instance.ExecuteInitialLayoutPass(window);
-                Assert.IsType<TestTemplatedControl>(window.Presenter.Child);
-                Assert.IsType<Canvas>(window.Presenter.Child.GetVisualChildren().SingleOrDefault());
+                    // Do a layout and make sure that the control gets added to visual tree and its
+                    // template applied.
+                    LayoutManager.Instance.ExecuteInitialLayoutPass(window);
+                    Assert.IsType<TestTemplatedControl>(window.Presenter.Child);
+                    Assert.IsType<Canvas>(window.Presenter.Child.GetVisualChildren().SingleOrDefault());
 
-                // Clear the template and ensure the control template gets removed
-                ((TestTemplatedControl)window.Content).Template = null;
-                LayoutManager.Instance.ExecuteLayoutPass();
-                Assert.Equal(0, window.Presenter.Child.GetVisualChildren().Count());
+                    // Clear the template and ensure the control template gets removed
+                    ((TestTemplatedControl)window.Content).Template = null;
+                    LayoutManager.Instance.ExecuteLayoutPass();
+                    Assert.Equal(0, window.Presenter.Child.GetVisualChildren().Count());
 
-                return window;
-            };
+                    return window;
+                };
 
-            var result = run();
+                var result = run();
 
-            dotMemory.Check(memory =>
-                Assert.Equal(0, memory.GetObjects(where => where.Type.Is<Canvas>()).ObjectsCount));
+                dotMemory.Check(memory =>
+                    Assert.Equal(0, memory.GetObjects(where => where.Type.Is<Canvas>()).ObjectsCount));
+            }
         }
 
         [Fact]
         public void ScrollViewer_With_Content_Is_Freed()
         {
-            Func<Window> run = () =>
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
             {
-                var window = new Window
+                Func<Window> run = () =>
                 {
-                    Content = new ScrollViewer
+                    var window = new Window
                     {
-                        Content = new Canvas()
-                    }
+                        Content = new ScrollViewer
+                        {
+                            Content = new Canvas()
+                        }
+                    };
+
+                    // Do a layout and make sure that ScrollViewer gets added to visual tree and its 
+                    // template applied.
+                    LayoutManager.Instance.ExecuteInitialLayoutPass(window);
+                    Assert.IsType<ScrollViewer>(window.Presenter.Child);
+                    Assert.IsType<Canvas>(((ScrollViewer)window.Presenter.Child).Presenter.Child);
+
+                    // Clear the content and ensure the ScrollViewer is removed.
+                    window.Content = null;
+                    LayoutManager.Instance.ExecuteLayoutPass();
+                    Assert.Null(window.Presenter.Child);
+
+                    return window;
                 };
 
-                // Do a layout and make sure that ScrollViewer gets added to visual tree and its 
-                // template applied.
-                LayoutManager.Instance.ExecuteInitialLayoutPass(window);
-                Assert.IsType<ScrollViewer>(window.Presenter.Child);
-                Assert.IsType<Canvas>(((ScrollViewer)window.Presenter.Child).Presenter.Child);
-
-                // Clear the content and ensure the ScrollViewer is removed.
-                window.Content = null;
-                LayoutManager.Instance.ExecuteLayoutPass();
-                Assert.Null(window.Presenter.Child);
+                var result = run();
 
-                return window;
-            };
-
-            var result = run();
-
-            dotMemory.Check(memory =>
-                Assert.Equal(0, memory.GetObjects(where => where.Type.Is<TextBox>()).ObjectsCount));
-            dotMemory.Check(memory =>
-                Assert.Equal(0, memory.GetObjects(where => where.Type.Is<Canvas>()).ObjectsCount));
+                dotMemory.Check(memory =>
+                    Assert.Equal(0, memory.GetObjects(where => where.Type.Is<TextBox>()).ObjectsCount));
+                dotMemory.Check(memory =>
+                    Assert.Equal(0, memory.GetObjects(where => where.Type.Is<Canvas>()).ObjectsCount));
+            }
         }
 
         [Fact]
         public void TextBox_Is_Freed()
         {
-            Func<Window> run = () =>
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
             {
-                var window = new Window
+                Func<Window> run = () =>
                 {
-                    Content = new TextBox()
-                };
+                    var window = new Window
+                    {
+                        Content = new TextBox()
+                    };
 
-                // Do a layout and make sure that TextBox gets added to visual tree and its 
-                // template applied.
-                LayoutManager.Instance.ExecuteInitialLayoutPass(window);
-                Assert.IsType<TextBox>(window.Presenter.Child);
-                Assert.NotEqual(0, window.Presenter.Child.GetVisualChildren().Count());
+                    // Do a layout and make sure that TextBox gets added to visual tree and its 
+                    // template applied.
+                    LayoutManager.Instance.ExecuteInitialLayoutPass(window);
+                    Assert.IsType<TextBox>(window.Presenter.Child);
+                    Assert.NotEqual(0, window.Presenter.Child.GetVisualChildren().Count());
 
-                // Clear the content and ensure the TextBox is removed.
-                window.Content = null;
-                LayoutManager.Instance.ExecuteLayoutPass();
-                Assert.Null(window.Presenter.Child);
+                    // Clear the content and ensure the TextBox is removed.
+                    window.Content = null;
+                    LayoutManager.Instance.ExecuteLayoutPass();
+                    Assert.Null(window.Presenter.Child);
 
-                return window;
-            };
+                    return window;
+                };
 
-            var result = run();
+                var result = run();
 
-            dotMemory.Check(memory =>
-                Assert.Equal(0, memory.GetObjects(where => where.Type.Is<TextBox>()).ObjectsCount));
+                dotMemory.Check(memory =>
+                    Assert.Equal(0, memory.GetObjects(where => where.Type.Is<TextBox>()).ObjectsCount));
+            }
         }
 
         [Fact]
         public void TextBox_With_Xaml_Binding_Is_Freed()
         {
-            Func<Window> run = () =>
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
             {
-                var window = new Window
+                Func<Window> run = () =>
                 {
-                    DataContext = new Node { Name = "foo" },
-                    Content = new TextBox()
-                };
+                    var window = new Window
+                    {
+                        DataContext = new Node { Name = "foo" },
+                        Content = new TextBox()
+                    };
 
-                var binding = new Perspex.Markup.Xaml.Data.Binding
-                {
-                    Path = "Name"
-                };
+                    var binding = new Perspex.Markup.Xaml.Data.Binding
+                    {
+                        Path = "Name"
+                    };
 
-                var textBox = (TextBox)window.Content;
-                textBox.Bind(TextBox.TextProperty, binding);
+                    var textBox = (TextBox)window.Content;
+                    textBox.Bind(TextBox.TextProperty, binding);
 
-                // Do a layout and make sure that TextBox gets added to visual tree and its 
-                // Text property set.
-                LayoutManager.Instance.ExecuteInitialLayoutPass(window);
-                Assert.IsType<TextBox>(window.Presenter.Child);
-                Assert.Equal("foo", ((TextBox)window.Presenter.Child).Text);
+                    // Do a layout and make sure that TextBox gets added to visual tree and its 
+                    // Text property set.
+                    LayoutManager.Instance.ExecuteInitialLayoutPass(window);
+                    Assert.IsType<TextBox>(window.Presenter.Child);
+                    Assert.Equal("foo", ((TextBox)window.Presenter.Child).Text);
 
-                // Clear the content and DataContext and ensure the TextBox is removed.
-                window.Content = null;
-                window.DataContext = null;
-                LayoutManager.Instance.ExecuteLayoutPass();
-                Assert.Null(window.Presenter.Child);
+                    // Clear the content and DataContext and ensure the TextBox is removed.
+                    window.Content = null;
+                    window.DataContext = null;
+                    LayoutManager.Instance.ExecuteLayoutPass();
+                    Assert.Null(window.Presenter.Child);
 
-                return window;
-            };
+                    return window;
+                };
 
-            var result = run();
+                var result = run();
 
-            dotMemory.Check(memory =>
-                Assert.Equal(0, memory.GetObjects(where => where.Type.Is<TextBox>()).ObjectsCount));
-            dotMemory.Check(memory =>
-                Assert.Equal(0, memory.GetObjects(where => where.Type.Is<Node>()).ObjectsCount));
+                dotMemory.Check(memory =>
+                    Assert.Equal(0, memory.GetObjects(where => where.Type.Is<TextBox>()).ObjectsCount));
+                dotMemory.Check(memory =>
+                    Assert.Equal(0, memory.GetObjects(where => where.Type.Is<Node>()).ObjectsCount));
+            }
         }
 
         [Fact]
         public void TextBox_ScrollViewer_Is_Freed_When_Template_Cleared()
         {
-            Func<Window> run = () =>
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
             {
-                var window = new Window
+                Func<Window> run = () =>
                 {
-                    Content = new TextBox()
-                };
+                    var window = new Window
+                    {
+                        Content = new TextBox()
+                    };
 
-                // Do a layout and make sure that TextBox gets added to visual tree and its 
-                // template applied.
-                LayoutManager.Instance.ExecuteInitialLayoutPass(window);
-                Assert.IsType<TextBox>(window.Presenter.Child);
-                Assert.NotEqual(0, window.Presenter.Child.GetVisualChildren().Count());
+                    // Do a layout and make sure that TextBox gets added to visual tree and its 
+                    // template applied.
+                    LayoutManager.Instance.ExecuteInitialLayoutPass(window);
+                    Assert.IsType<TextBox>(window.Presenter.Child);
+                    Assert.NotEqual(0, window.Presenter.Child.GetVisualChildren().Count());
 
-                // Clear the template and ensure the TextBox template gets removed
-                ((TextBox)window.Content).Template = null;
-                LayoutManager.Instance.ExecuteLayoutPass();
-                Assert.Equal(0, window.Presenter.Child.GetVisualChildren().Count());
+                    // Clear the template and ensure the TextBox template gets removed
+                    ((TextBox)window.Content).Template = null;
+                    LayoutManager.Instance.ExecuteLayoutPass();
+                    Assert.Equal(0, window.Presenter.Child.GetVisualChildren().Count());
 
-                return window;
-            };
+                    return window;
+                };
 
-            var result = run();
+                var result = run();
 
-            dotMemory.Check(memory =>
-                Assert.Equal(0, memory.GetObjects(where => where.Type.Is<ScrollViewer>()).ObjectsCount));
+                dotMemory.Check(memory =>
+                    Assert.Equal(0, memory.GetObjects(where => where.Type.Is<ScrollViewer>()).ObjectsCount));
+            }
         }
 
         [Fact]
         public void TreeView_Is_Freed()
         {
-            Func<Window> run = () =>
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
             {
-                var nodes = new[]
+                Func<Window> run = () =>
                 {
-                    new Node
+                    var nodes = new[]
                     {
-                        Children = new[] { new Node() },
-                    }
-                };
+                        new Node
+                        {
+                            Children = new[] { new Node() },
+                        }
+                    };
 
-                TreeView target;
+                    TreeView target;
 
-                var window = new Window
-                {
-                    Content = target = new TreeView
+                    var window = new Window
                     {
-                        DataTemplates = new DataTemplates
+                        Content = target = new TreeView
                         {
-                            new FuncTreeDataTemplate<Node>(
-                                x => new TextBlock { Text = x.Name },
-                                x => x.Children)
-                        },
-                        Items = nodes
-                    }
+                            DataTemplates = new DataTemplates
+                            {
+                                new FuncTreeDataTemplate<Node>(
+                                    x => new TextBlock { Text = x.Name },
+                                    x => x.Children)
+                            },
+                            Items = nodes
+                        }
+                    };
+
+                    // Do a layout and make sure that TreeViewItems get realized.
+                    LayoutManager.Instance.ExecuteInitialLayoutPass(window);
+                    Assert.Equal(1, target.ItemContainerGenerator.Containers.Count());
+
+                    // Clear the content and ensure the TreeView is removed.
+                    window.Content = null;
+                    LayoutManager.Instance.ExecuteLayoutPass();
+                    Assert.Null(window.Presenter.Child);
+
+                    return window;
                 };
 
-                // Do a layout and make sure that TreeViewItems get realized.
-                LayoutManager.Instance.ExecuteInitialLayoutPass(window);
-                Assert.Equal(1, target.ItemContainerGenerator.Containers.Count());
-
-                // Clear the content and ensure the TreeView is removed.
-                window.Content = null;
-                LayoutManager.Instance.ExecuteLayoutPass();
-                Assert.Null(window.Presenter.Child);
-
-                return window;
-            };
+                var result = run();
 
-            var result = run();
-
-            dotMemory.Check(memory =>
-                Assert.Equal(0, memory.GetObjects(where => where.Type.Is<TreeView>()).ObjectsCount));
+                dotMemory.Check(memory =>
+                    Assert.Equal(0, memory.GetObjects(where => where.Type.Is<TreeView>()).ObjectsCount));
+            }
         }
 
         private class TestTemplatedControl : TemplatedControl

+ 4 - 1
tests/Perspex.LeakTests/Perspex.LeakTests.csproj

@@ -93,7 +93,6 @@
   <ItemGroup>
     <Compile Include="ControlTests.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
-    <Compile Include="TestApp.cs" />
   </ItemGroup>
   <ItemGroup>
     <None Include="app.config" />
@@ -152,6 +151,10 @@
       <Project>{5ccb5571-7c30-4e7d-967d-0e2158ebd91f}</Project>
       <Name>Perspex.Controls.UnitTests</Name>
     </ProjectReference>
+    <ProjectReference Include="..\Perspex.UnitTests\Perspex.UnitTests.csproj">
+      <Project>{88060192-33d5-4932-b0f9-8bd2763e857d}</Project>
+      <Name>Perspex.UnitTests</Name>
+    </ProjectReference>
   </ItemGroup>
   <ItemGroup>
     <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />

+ 0 - 47
tests/Perspex.LeakTests/TestApp.cs

@@ -1,47 +0,0 @@
-// Copyright (c) The Perspex Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using Moq;
-using Perspex.Controls.UnitTests;
-using Perspex.Layout;
-using Perspex.Platform;
-using Perspex.Shared.PlatformSupport;
-using Perspex.Themes.Default;
-using Ploeh.AutoFixture;
-using Ploeh.AutoFixture.AutoMoq;
-
-namespace Perspex.LeakTests
-{
-    internal class TestApp : Application
-    {
-        private TestApp()
-        {
-            RegisterServices();
-
-            var fixture = new Fixture().Customize(new AutoMoqCustomization());
-            var windowImpl = new Mock<IWindowImpl>();
-            var renderInterface = fixture.Create<IPlatformRenderInterface>();
-            var threadingInterface = Mock.Of<IPlatformThreadingInterface>(x =>
-                x.CurrentThreadIsLoopThread == true);
-
-            PerspexLocator.CurrentMutable
-                .Bind<IAssetLoader>().ToConstant(new AssetLoader())
-                .Bind<ILayoutManager>().ToConstant(new LayoutManager())
-                .Bind<IPclPlatformWrapper>().ToConstant(new PclPlatformWrapper())
-                .Bind<IPlatformRenderInterface>().ToConstant(renderInterface)
-                .Bind<IPlatformThreadingInterface>().ToConstant(threadingInterface)
-                .Bind<IStandardCursorFactory>().ToConstant(new Mock<IStandardCursorFactory>().Object)
-                .Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock(() => windowImpl.Object));
-
-            Styles = new DefaultTheme();
-        }
-
-        public static void Initialize()
-        {
-            if (Current == null)
-            {
-                new TestApp();
-            }
-        }
-    }
-}

+ 30 - 0
tests/Perspex.UnitTests/MockWindowingPlatform.cs

@@ -0,0 +1,30 @@
+using System;
+using Moq;
+using Perspex.Platform;
+
+namespace Perspex.UnitTests
+{
+    public class MockWindowingPlatform : IWindowingPlatform
+    {
+        private readonly Func<IWindowImpl> _windowImpl;
+        private readonly Func<IPopupImpl> _popupImpl;
+
+        public MockWindowingPlatform(Func<IWindowImpl> windowImpl = null, Func<IPopupImpl> popupImpl = null )
+        {
+            _windowImpl = windowImpl;
+            _popupImpl = popupImpl;
+        }
+
+        public IWindowImpl CreateWindow()
+        {
+            return _windowImpl?.Invoke() ?? Mock.Of<IWindowImpl>();
+        }
+
+        public IWindowImpl CreateEmbeddableWindow()
+        {
+            throw new NotImplementedException();
+        }
+
+        public IPopupImpl CreatePopup() => _popupImpl?.Invoke() ?? Mock.Of<IPopupImpl>();
+    }
+}

+ 141 - 0
tests/Perspex.UnitTests/Perspex.UnitTests.csproj

@@ -0,0 +1,141 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{88060192-33D5-4932-B0F9-8BD2763E857D}</ProjectGuid>
+    <ProjectGuid>{88060192-33D5-4932-B0F9-8BD2763E857D}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>Perspex.UnitTests</RootNamespace>
+    <AssemblyName>Perspex.UnitTests</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <TargetFrameworkProfile />
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Moq, Version=4.2.1510.2205, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
+      <HintPath>..\..\packages\Moq.4.2.1510.2205\lib\net40\Moq.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="Ploeh.AutoFixture, Version=3.40.0.0, Culture=neutral, PublicKeyToken=b24654c590009d4f, processorArchitecture=MSIL">
+      <HintPath>..\..\packages\AutoFixture.3.40.0\lib\net40\Ploeh.AutoFixture.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="Ploeh.AutoFixture.AutoMoq, Version=3.40.0.0, Culture=neutral, PublicKeyToken=b24654c590009d4f, processorArchitecture=MSIL">
+      <HintPath>..\..\packages\AutoFixture.AutoMoq.3.40.0\lib\net40\Ploeh.AutoFixture.AutoMoq.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Reactive.Core, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+      <HintPath>..\..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="System.Reactive.Interfaces, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+      <HintPath>..\..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="System.Reactive.Linq, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+      <HintPath>..\..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="System.Reactive.PlatformServices, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+      <HintPath>..\..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="TestServices.cs" />
+    <Compile Include="UnitTestApplication.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="MockWindowingPlatform.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\src\Markup\Perspex.Markup.Xaml\Perspex.Markup.Xaml.csproj">
+      <Project>{3e53a01a-b331-47f3-b828-4a5717e77a24}</Project>
+      <Name>Perspex.Markup.Xaml</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Markup\Perspex.Markup\Perspex.Markup.csproj">
+      <Project>{6417e941-21bc-467b-a771-0de389353ce6}</Project>
+      <Name>Perspex.Markup</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.Animation\Perspex.Animation.csproj">
+      <Project>{d211e587-d8bc-45b9-95a4-f297c8fa5200}</Project>
+      <Name>Perspex.Animation</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.Application\Perspex.Application.csproj">
+      <Project>{799a7bb5-3c2c-48b6-85a7-406a12c420da}</Project>
+      <Name>Perspex.Application</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.Base\Perspex.Base.csproj">
+      <Project>{b09b78d8-9b26-48b0-9149-d64a2f120f3f}</Project>
+      <Name>Perspex.Base</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.Controls\Perspex.Controls.csproj">
+      <Project>{d2221c82-4a25-4583-9b43-d791e3f6820c}</Project>
+      <Name>Perspex.Controls</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.Input\Perspex.Input.csproj">
+      <Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project>
+      <Name>Perspex.Input</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.Interactivity\Perspex.Interactivity.csproj">
+      <Project>{6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}</Project>
+      <Name>Perspex.Interactivity</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.Layout\Perspex.Layout.csproj">
+      <Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project>
+      <Name>Perspex.Layout</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.SceneGraph\Perspex.SceneGraph.csproj">
+      <Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project>
+      <Name>Perspex.SceneGraph</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.Styling\Perspex.Styling.csproj">
+      <Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project>
+      <Name>Perspex.Styling</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.Themes.Default\Perspex.Themes.Default.csproj">
+      <Project>{3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f}</Project>
+      <Name>Perspex.Themes.Default</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="app.config" />
+    <None Include="packages.config" />
+  </ItemGroup>
+  <Import Project="..\..\src\Shared\PlatformSupport\PlatformSupport.projitems" Label="Shared" />
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 36 - 0
tests/Perspex.UnitTests/Properties/AssemblyInfo.cs

@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Perspex.UnitTests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Perspex.UnitTests")]
+[assembly: AssemblyCopyright("Copyright ©  2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("88060192-33d5-4932-b0f9-8bd2763e857d")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 44 - 0
tests/Perspex.UnitTests/TestServices.cs

@@ -0,0 +1,44 @@
+// Copyright (c) The Perspex Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using Moq;
+using Perspex.Layout;
+using Perspex.Platform;
+using Perspex.Shared.PlatformSupport;
+using Perspex.Styling;
+using Perspex.Themes.Default;
+using Ploeh.AutoFixture;
+using Ploeh.AutoFixture.AutoMoq;
+
+namespace Perspex.UnitTests
+{
+    public class TestServices
+    {
+        private static IFixture s_fixture = new Fixture().Customize(new AutoMoqCustomization());
+
+        public static readonly TestServices StyledWindow = new TestServices
+        {
+            AssetLoader = new AssetLoader(),
+            LayoutManager = new LayoutManager(),
+            PlatformWrapper = new PclPlatformWrapper(),
+            RenderInterface = s_fixture.Create<IPlatformRenderInterface>(),
+            StandardCursorFactory = Mock.Of<IStandardCursorFactory>(),
+            Styler = new Styler(),
+            Theme = () => new DefaultTheme(),
+            ThreadingInterface = Mock.Of<IPlatformThreadingInterface>(x => x.CurrentThreadIsLoopThread == true),
+            WindowingPlatform = new MockWindowingPlatform(),
+        };
+
+        public IAssetLoader AssetLoader { get; set; }
+        public ILayoutManager LayoutManager { get; set; }
+        public IPclPlatformWrapper PlatformWrapper { get; set; }
+        public IPlatformRenderInterface RenderInterface { get; set; }
+        public IStandardCursorFactory StandardCursorFactory { get; set; }
+        public IStyler Styler { get; set; }
+        public Func<Styles> Theme { get; set; }
+        public IPlatformThreadingInterface ThreadingInterface { get; set; }
+        public IWindowImpl WindowImpl { get; set; }
+        public IWindowingPlatform WindowingPlatform { get; set; }
+    }
+}

+ 43 - 0
tests/Perspex.UnitTests/UnitTestApplication.cs

@@ -0,0 +1,43 @@
+// Copyright (c) The Perspex Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using Perspex.Layout;
+using Perspex.Platform;
+using Perspex.Styling;
+
+namespace Perspex.UnitTests
+{
+    public class UnitTestApplication : Application
+    {
+        public UnitTestApplication(TestServices services)
+        {
+            Services = services;
+            RegisterServices();
+            Styles = services.Theme();
+        }
+
+        public TestServices Services { get; }
+
+        public static IDisposable Start(TestServices services = null)
+        {
+            var scope = PerspexLocator.EnterScope();
+            var app = new UnitTestApplication(services);
+            return scope;
+        }
+
+        protected override void RegisterServices()
+        {
+            PerspexLocator.CurrentMutable
+                .Bind<IAssetLoader>().ToConstant(Services.AssetLoader)
+                .BindToSelf<IGlobalStyles>(this)
+                .Bind<ILayoutManager>().ToConstant(Services.LayoutManager)
+                .Bind<IPclPlatformWrapper>().ToConstant(Services.PlatformWrapper)
+                .Bind<IPlatformRenderInterface>().ToConstant(Services.RenderInterface)
+                .Bind<IPlatformThreadingInterface>().ToConstant(Services.ThreadingInterface)
+                .Bind<IStandardCursorFactory>().ToConstant(Services.StandardCursorFactory)
+                .Bind<IStyler>().ToConstant(Services.Styler)
+                .Bind<IWindowingPlatform>().ToConstant(Services.WindowingPlatform);
+        }
+    }
+}

+ 11 - 0
tests/Perspex.UnitTests/app.config

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+  <runtime>
+    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
+      <dependentAssembly>
+        <assemblyIdentity name="Moq" publicKeyToken="69f491c39445e920" culture="neutral" />
+        <bindingRedirect oldVersion="0.0.0.0-4.2.1510.2205" newVersion="4.2.1510.2205" />
+      </dependentAssembly>
+    </assemblyBinding>
+  </runtime>
+</configuration>

+ 11 - 0
tests/Perspex.UnitTests/packages.config

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="AutoFixture" version="3.40.0" targetFramework="net46" />
+  <package id="AutoFixture.AutoMoq" version="3.40.0" targetFramework="net46" />
+  <package id="Moq" version="4.2.1510.2205" targetFramework="net46" />
+  <package id="Rx-Core" version="2.2.5" targetFramework="net45" />
+  <package id="Rx-Interfaces" version="2.2.5" targetFramework="net45" />
+  <package id="Rx-Linq" version="2.2.5" targetFramework="net45" />
+  <package id="Rx-Main" version="2.2.5" targetFramework="net45" />
+  <package id="Rx-PlatformServices" version="2.2.5" targetFramework="net45" />
+</packages>