ControlTests.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. // Copyright (c) The Perspex Project. All rights reserved.
  2. // Licensed under the MIT license. See licence.md file in the project root for full license information.
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using JetBrains.dotMemoryUnit;
  7. using Perspex.Controls;
  8. using Perspex.Controls.Primitives;
  9. using Perspex.Controls.Templates;
  10. using Perspex.VisualTree;
  11. using Xunit;
  12. using Xunit.Abstractions;
  13. namespace Perspex.LeakTests
  14. {
  15. [DotMemoryUnit(FailIfRunWithoutSupport = false)]
  16. public class ControlTests
  17. {
  18. public ControlTests(ITestOutputHelper atr)
  19. {
  20. TestApp.Initialize();
  21. DotMemoryUnitTestOutput.SetOutputMethod(atr.WriteLine);
  22. }
  23. [Fact]
  24. public void Canvas_Is_Freed()
  25. {
  26. Func<Window> run = () =>
  27. {
  28. var window = new Window
  29. {
  30. Content = new Canvas()
  31. };
  32. // Do a layout and make sure that Canvas gets added to visual tree.
  33. window.LayoutManager.ExecuteLayoutPass();
  34. Assert.IsType<Canvas>(window.Presenter.Child);
  35. // Clear the content and ensure the Canvas is removed.
  36. window.Content = null;
  37. window.LayoutManager.ExecuteLayoutPass();
  38. Assert.Null(window.Presenter.Child);
  39. return window;
  40. };
  41. var result = run();
  42. dotMemory.Check(memory =>
  43. Assert.Equal(0, memory.GetObjects(where => where.Type.Is<Canvas>()).ObjectsCount));
  44. }
  45. [Fact]
  46. public void Named_Canvas_Is_Freed()
  47. {
  48. Func<Window> run = () =>
  49. {
  50. var window = new Window
  51. {
  52. Content = new Canvas
  53. {
  54. Name = "foo"
  55. }
  56. };
  57. // Do a layout and make sure that Canvas gets added to visual tree.
  58. window.LayoutManager.ExecuteLayoutPass();
  59. Assert.IsType<Canvas>(window.Find<Canvas>("foo"));
  60. Assert.IsType<Canvas>(window.Presenter.Child);
  61. // Clear the content and ensure the Canvas is removed.
  62. window.Content = null;
  63. window.LayoutManager.ExecuteLayoutPass();
  64. Assert.Null(window.Presenter.Child);
  65. return window;
  66. };
  67. var result = run();
  68. dotMemory.Check(memory =>
  69. Assert.Equal(0, memory.GetObjects(where => where.Type.Is<Canvas>()).ObjectsCount));
  70. }
  71. [Fact]
  72. public void Templated_Child_Is_Freed_When_Template_Cleared()
  73. {
  74. Func<Window> run = () =>
  75. {
  76. var window = new Window
  77. {
  78. Content = new TestTemplatedControl()
  79. };
  80. // Do a layout and make sure that the control gets added to visual tree and its
  81. // template applied.
  82. window.LayoutManager.ExecuteLayoutPass();
  83. Assert.IsType<TestTemplatedControl>(window.Presenter.Child);
  84. Assert.IsType<Canvas>(window.Presenter.Child.GetVisualChildren().SingleOrDefault());
  85. // Clear the template and ensure the control template gets removed
  86. ((TestTemplatedControl)window.Content).Template = null;
  87. window.LayoutManager.ExecuteLayoutPass();
  88. Assert.Equal(0, window.Presenter.Child.GetVisualChildren().Count());
  89. return window;
  90. };
  91. var result = run();
  92. dotMemory.Check(memory =>
  93. Assert.Equal(0, memory.GetObjects(where => where.Type.Is<Canvas>()).ObjectsCount));
  94. }
  95. [Fact]
  96. public void ScrollViewer_With_Content_Is_Freed()
  97. {
  98. Func<Window> run = () =>
  99. {
  100. var window = new Window
  101. {
  102. Content = new ScrollViewer
  103. {
  104. Content = new Canvas()
  105. }
  106. };
  107. // Do a layout and make sure that ScrollViewer gets added to visual tree and its
  108. // template applied.
  109. window.LayoutManager.ExecuteLayoutPass();
  110. Assert.IsType<ScrollViewer>(window.Presenter.Child);
  111. Assert.IsType<Canvas>(((ScrollViewer)window.Presenter.Child).Presenter.Child);
  112. // Clear the content and ensure the ScrollViewer is removed.
  113. window.Content = null;
  114. window.LayoutManager.ExecuteLayoutPass();
  115. Assert.Null(window.Presenter.Child);
  116. return window;
  117. };
  118. var result = run();
  119. dotMemory.Check(memory =>
  120. Assert.Equal(0, memory.GetObjects(where => where.Type.Is<TextBox>()).ObjectsCount));
  121. dotMemory.Check(memory =>
  122. Assert.Equal(0, memory.GetObjects(where => where.Type.Is<Canvas>()).ObjectsCount));
  123. }
  124. [Fact]
  125. public void TextBox_Is_Freed()
  126. {
  127. Func<Window> run = () =>
  128. {
  129. var window = new Window
  130. {
  131. Content = new TextBox()
  132. };
  133. // Do a layout and make sure that TextBox gets added to visual tree and its
  134. // template applied.
  135. window.LayoutManager.ExecuteLayoutPass();
  136. Assert.IsType<TextBox>(window.Presenter.Child);
  137. Assert.NotEqual(0, window.Presenter.Child.GetVisualChildren().Count());
  138. // Clear the content and ensure the TextBox is removed.
  139. window.Content = null;
  140. window.LayoutManager.ExecuteLayoutPass();
  141. Assert.Null(window.Presenter.Child);
  142. return window;
  143. };
  144. var result = run();
  145. dotMemory.Check(memory =>
  146. Assert.Equal(0, memory.GetObjects(where => where.Type.Is<TextBox>()).ObjectsCount));
  147. }
  148. [Fact]
  149. public void TextBox_With_Xaml_Binding_Is_Freed()
  150. {
  151. Func<Window> run = () =>
  152. {
  153. var window = new Window
  154. {
  155. DataContext = new Node { Name = "foo" },
  156. Content = new TextBox()
  157. };
  158. var binding = new Perspex.Markup.Xaml.Data.Binding
  159. {
  160. Path = "Name"
  161. };
  162. var textBox = (TextBox)window.Content;
  163. textBox.Bind(TextBox.TextProperty, binding);
  164. // Do a layout and make sure that TextBox gets added to visual tree and its
  165. // Text property set.
  166. window.LayoutManager.ExecuteLayoutPass();
  167. Assert.IsType<TextBox>(window.Presenter.Child);
  168. Assert.Equal("foo", ((TextBox)window.Presenter.Child).Text);
  169. // Clear the content and DataContext and ensure the TextBox is removed.
  170. window.Content = null;
  171. window.DataContext = null;
  172. window.LayoutManager.ExecuteLayoutPass();
  173. Assert.Null(window.Presenter.Child);
  174. return window;
  175. };
  176. var result = run();
  177. dotMemory.Check(memory =>
  178. Assert.Equal(0, memory.GetObjects(where => where.Type.Is<TextBox>()).ObjectsCount));
  179. dotMemory.Check(memory =>
  180. Assert.Equal(0, memory.GetObjects(where => where.Type.Is<Node>()).ObjectsCount));
  181. }
  182. [Fact]
  183. public void TextBox_ScrollViewer_Is_Freed_When_Template_Cleared()
  184. {
  185. Func<Window> run = () =>
  186. {
  187. var window = new Window
  188. {
  189. Content = new TextBox()
  190. };
  191. // Do a layout and make sure that TextBox gets added to visual tree and its
  192. // template applied.
  193. window.LayoutManager.ExecuteLayoutPass();
  194. Assert.IsType<TextBox>(window.Presenter.Child);
  195. Assert.NotEqual(0, window.Presenter.Child.GetVisualChildren().Count());
  196. // Clear the template and ensure the TextBox template gets removed
  197. ((TextBox)window.Content).Template = null;
  198. window.LayoutManager.ExecuteLayoutPass();
  199. Assert.Equal(0, window.Presenter.Child.GetVisualChildren().Count());
  200. return window;
  201. };
  202. var result = run();
  203. dotMemory.Check(memory =>
  204. Assert.Equal(0, memory.GetObjects(where => where.Type.Is<ScrollViewer>()).ObjectsCount));
  205. }
  206. [Fact]
  207. public void TreeView_Is_Freed()
  208. {
  209. Func<Window> run = () =>
  210. {
  211. var nodes = new[]
  212. {
  213. new Node
  214. {
  215. Children = new[] { new Node() },
  216. }
  217. };
  218. TreeView target;
  219. var window = new Window
  220. {
  221. Content = target = new TreeView
  222. {
  223. DataTemplates = new DataTemplates
  224. {
  225. new FuncTreeDataTemplate<Node>(
  226. x => new TextBlock { Text = x.Name },
  227. x => x.Children)
  228. },
  229. Items = nodes
  230. }
  231. };
  232. // Do a layout and make sure that TreeViewItems get realized.
  233. window.LayoutManager.ExecuteLayoutPass();
  234. Assert.Equal(1, target.ItemContainerGenerator.Containers.Count());
  235. // Clear the content and ensure the TreeView is removed.
  236. window.Content = null;
  237. window.LayoutManager.ExecuteLayoutPass();
  238. Assert.Null(window.Presenter.Child);
  239. return window;
  240. };
  241. var result = run();
  242. dotMemory.Check(memory =>
  243. Assert.Equal(0, memory.GetObjects(where => where.Type.Is<TreeView>()).ObjectsCount));
  244. }
  245. private class TestTemplatedControl : TemplatedControl
  246. {
  247. public static readonly StyledProperty<int> IsCanvasVisibleProperty =
  248. PerspexProperty.Register<TestTemplatedControl, int>("IsCanvasVisible");
  249. public TestTemplatedControl()
  250. {
  251. Template = new FuncControlTemplate<TestTemplatedControl>(parent =>
  252. new Canvas
  253. {
  254. [~IsVisibleProperty] = parent[~IsCanvasVisibleProperty]
  255. });
  256. }
  257. }
  258. private class Node
  259. {
  260. public string Name { get; set; }
  261. public IEnumerable<Node> Children { get; set; }
  262. }
  263. }
  264. }