Browse Source

Started refactoring unit tests.

Added a shared test project with a UnitTestApplication and use it in
LeakTests.
pull/417/head
Steven Kirk 10 years ago
parent
commit
2283cfa486
  1. 34
      Perspex.sln
  2. 15
      src/Perspex.Application/Application.cs
  3. 7
      src/Perspex.Base/Threading/DispatcherTimer.cs
  4. 10
      src/Perspex.Controls/Platform/PlatformManager.cs
  5. 16
      src/Perspex.Input/Cursors.cs
  6. 5
      src/Perspex.SceneGraph/Media/FormattedText.cs
  7. 372
      tests/Perspex.LeakTests/ControlTests.cs
  8. 5
      tests/Perspex.LeakTests/Perspex.LeakTests.csproj
  9. 47
      tests/Perspex.LeakTests/TestApp.cs
  10. 30
      tests/Perspex.UnitTests/MockWindowingPlatform.cs
  11. 141
      tests/Perspex.UnitTests/Perspex.UnitTests.csproj
  12. 36
      tests/Perspex.UnitTests/Properties/AssemblyInfo.cs
  13. 44
      tests/Perspex.UnitTests/TestServices.cs
  14. 43
      tests/Perspex.UnitTests/UnitTestApplication.cs
  15. 11
      tests/Perspex.UnitTests/app.config
  16. 11
      tests/Perspex.UnitTests/packages.config

34
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

15
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>

7
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;
}

10
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()

16
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
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,

372
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

5
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}" />

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
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
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
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
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
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
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
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>
Loading…
Cancel
Save