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