LayoutManagerTests.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. // Copyright (c) The Avalonia 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.Collections.Generic;
  4. using System.Linq;
  5. using Avalonia.Controls;
  6. using Xunit;
  7. namespace Avalonia.Layout.UnitTests
  8. {
  9. public class LayoutManagerTests
  10. {
  11. [Fact]
  12. public void Measures_And_Arranges_InvalidateMeasured_Control()
  13. {
  14. var control = new LayoutTestControl();
  15. var root = new LayoutTestRoot { Child = control };
  16. root.LayoutManager.ExecuteInitialLayoutPass(root);
  17. control.Measured = control.Arranged = false;
  18. control.InvalidateMeasure();
  19. root.LayoutManager.ExecuteLayoutPass();
  20. Assert.True(control.Measured);
  21. Assert.True(control.Arranged);
  22. }
  23. [Fact]
  24. public void Arranges_InvalidateArranged_Control()
  25. {
  26. var control = new LayoutTestControl();
  27. var root = new LayoutTestRoot { Child = control };
  28. root.LayoutManager.ExecuteInitialLayoutPass(root);
  29. control.Measured = control.Arranged = false;
  30. control.InvalidateArrange();
  31. root.LayoutManager.ExecuteLayoutPass();
  32. Assert.False(control.Measured);
  33. Assert.True(control.Arranged);
  34. }
  35. [Fact]
  36. public void Measures_Parent_Of_Newly_Added_Control()
  37. {
  38. var control = new LayoutTestControl();
  39. var root = new LayoutTestRoot();
  40. root.LayoutManager.ExecuteInitialLayoutPass(root);
  41. root.Child = control;
  42. root.Measured = root.Arranged = false;
  43. root.LayoutManager.ExecuteLayoutPass();
  44. Assert.True(root.Measured);
  45. Assert.True(root.Arranged);
  46. Assert.True(control.Measured);
  47. Assert.True(control.Arranged);
  48. }
  49. [Fact]
  50. public void Measures_In_Correct_Order()
  51. {
  52. LayoutTestControl control1;
  53. LayoutTestControl control2;
  54. var root = new LayoutTestRoot
  55. {
  56. Child = control1 = new LayoutTestControl
  57. {
  58. Child = control2 = new LayoutTestControl(),
  59. }
  60. };
  61. var order = new List<ILayoutable>();
  62. Size MeasureOverride(ILayoutable control, Size size)
  63. {
  64. order.Add(control);
  65. return new Size(10, 10);
  66. }
  67. root.DoMeasureOverride = MeasureOverride;
  68. control1.DoMeasureOverride = MeasureOverride;
  69. control2.DoMeasureOverride = MeasureOverride;
  70. root.LayoutManager.ExecuteInitialLayoutPass(root);
  71. control2.InvalidateMeasure();
  72. control1.InvalidateMeasure();
  73. root.InvalidateMeasure();
  74. order.Clear();
  75. root.LayoutManager.ExecuteLayoutPass();
  76. Assert.Equal(new ILayoutable[] { root, control1, control2 }, order);
  77. }
  78. [Fact]
  79. public void Measures_Root_And_Grandparent_In_Correct_Order()
  80. {
  81. LayoutTestControl control1;
  82. LayoutTestControl control2;
  83. var root = new LayoutTestRoot
  84. {
  85. Child = control1 = new LayoutTestControl
  86. {
  87. Child = control2 = new LayoutTestControl(),
  88. }
  89. };
  90. var order = new List<ILayoutable>();
  91. Size MeasureOverride(ILayoutable control, Size size)
  92. {
  93. order.Add(control);
  94. return new Size(10, 10);
  95. }
  96. root.DoMeasureOverride = MeasureOverride;
  97. control1.DoMeasureOverride = MeasureOverride;
  98. control2.DoMeasureOverride = MeasureOverride;
  99. root.LayoutManager.ExecuteInitialLayoutPass(root);
  100. control2.InvalidateMeasure();
  101. root.InvalidateMeasure();
  102. order.Clear();
  103. root.LayoutManager.ExecuteLayoutPass();
  104. Assert.Equal(new ILayoutable[] { root, control2 }, order);
  105. }
  106. [Fact]
  107. public void Doesnt_Measure_Non_Invalidated_Root()
  108. {
  109. var control = new LayoutTestControl();
  110. var root = new LayoutTestRoot { Child = control };
  111. root.LayoutManager.ExecuteInitialLayoutPass(root);
  112. root.Measured = root.Arranged = false;
  113. control.Measured = control.Arranged = false;
  114. control.InvalidateMeasure();
  115. root.LayoutManager.ExecuteLayoutPass();
  116. Assert.False(root.Measured);
  117. Assert.False(root.Arranged);
  118. Assert.True(control.Measured);
  119. Assert.True(control.Arranged);
  120. }
  121. [Fact]
  122. public void Doesnt_Measure_Removed_Control()
  123. {
  124. var control = new LayoutTestControl();
  125. var root = new LayoutTestRoot { Child = control };
  126. root.LayoutManager.ExecuteInitialLayoutPass(root);
  127. control.Measured = control.Arranged = false;
  128. control.InvalidateMeasure();
  129. root.Child = null;
  130. root.LayoutManager.ExecuteLayoutPass();
  131. Assert.False(control.Measured);
  132. Assert.False(control.Arranged);
  133. }
  134. [Fact]
  135. public void Measures_Root_With_Infinity()
  136. {
  137. var root = new LayoutTestRoot();
  138. var availableSize = default(Size);
  139. // Should not measure with this size.
  140. root.MaxClientSize = new Size(123, 456);
  141. root.DoMeasureOverride = (_, s) =>
  142. {
  143. availableSize = s;
  144. return new Size(100, 100);
  145. };
  146. root.LayoutManager.ExecuteInitialLayoutPass(root);
  147. Assert.Equal(Size.Infinity, availableSize);
  148. }
  149. [Fact]
  150. public void Arranges_Root_With_DesiredSize()
  151. {
  152. var root = new LayoutTestRoot
  153. {
  154. Width = 100,
  155. Height = 100,
  156. };
  157. var arrangeSize = default(Size);
  158. root.DoArrangeOverride = (_, s) =>
  159. {
  160. arrangeSize = s;
  161. return s;
  162. };
  163. root.LayoutManager.ExecuteInitialLayoutPass(root);
  164. Assert.Equal(new Size(100, 100), arrangeSize);
  165. root.Width = 120;
  166. root.LayoutManager.ExecuteLayoutPass();
  167. Assert.Equal(new Size(120, 100), arrangeSize);
  168. }
  169. [Fact]
  170. public void Invalidating_Child_Remeasures_Parent()
  171. {
  172. Border border;
  173. StackPanel panel;
  174. var root = new LayoutTestRoot
  175. {
  176. Child = panel = new StackPanel
  177. {
  178. Children =
  179. {
  180. (border = new Border())
  181. }
  182. }
  183. };
  184. root.LayoutManager.ExecuteInitialLayoutPass(root);
  185. Assert.Equal(new Size(0, 0), root.DesiredSize);
  186. border.Width = 100;
  187. border.Height = 100;
  188. root.LayoutManager.ExecuteLayoutPass();
  189. Assert.Equal(new Size(100, 100), panel.DesiredSize);
  190. }
  191. [Fact]
  192. public void LayoutManager_Should_Prevent_Infinite_Loop_On_Measure()
  193. {
  194. var control = new LayoutTestControl();
  195. var root = new LayoutTestRoot { Child = control };
  196. root.LayoutManager.ExecuteInitialLayoutPass(root);
  197. control.Measured = false;
  198. int cnt = 0;
  199. int maxcnt = 100;
  200. control.DoMeasureOverride = (l, s) =>
  201. {
  202. //emulate a problem in the logic of a control that triggers
  203. //invalidate measure during measure
  204. //it can lead to an infinite loop in layoutmanager
  205. if (++cnt < maxcnt)
  206. {
  207. control.InvalidateMeasure();
  208. }
  209. return new Size(100, 100);
  210. };
  211. control.InvalidateMeasure();
  212. root.LayoutManager.ExecuteLayoutPass();
  213. Assert.True(cnt < 100);
  214. }
  215. [Fact]
  216. public void LayoutManager_Should_Prevent_Infinite_Loop_On_Arrange()
  217. {
  218. var control = new LayoutTestControl();
  219. var root = new LayoutTestRoot { Child = control };
  220. root.LayoutManager.ExecuteInitialLayoutPass(root);
  221. control.Arranged = false;
  222. int cnt = 0;
  223. int maxcnt = 100;
  224. control.DoArrangeOverride = (l, s) =>
  225. {
  226. //emulate a problem in the logic of a control that triggers
  227. //invalidate measure during arrange
  228. //it can lead to infinity loop in layoutmanager
  229. if (++cnt < maxcnt)
  230. {
  231. control.InvalidateArrange();
  232. }
  233. return new Size(100, 100);
  234. };
  235. control.InvalidateArrange();
  236. root.LayoutManager.ExecuteLayoutPass();
  237. Assert.True(cnt < 100);
  238. }
  239. [Fact]
  240. public void LayoutManager_Should_Properly_Arrange_Visuals_Even_When_There_Are_Issues_With_Previous_Arranged()
  241. {
  242. var nonArrageableTargets = Enumerable.Range(1, 10).Select(_ => new LayoutTestControl()).ToArray();
  243. var targets = Enumerable.Range(1, 10).Select(_ => new LayoutTestControl()).ToArray();
  244. StackPanel panel;
  245. var root = new LayoutTestRoot
  246. {
  247. Child = panel = new StackPanel()
  248. };
  249. panel.Children.AddRange(nonArrageableTargets);
  250. panel.Children.AddRange(targets);
  251. root.LayoutManager.ExecuteInitialLayoutPass(root);
  252. foreach (var c in panel.Children.OfType<LayoutTestControl>())
  253. {
  254. c.Measured = c.Arranged = false;
  255. c.InvalidateMeasure();
  256. }
  257. foreach (var c in nonArrageableTargets)
  258. {
  259. c.DoArrangeOverride = (l, s) =>
  260. {
  261. //emulate a problem in the logic of a control that triggers
  262. //invalidate measure during arrange
  263. c.InvalidateMeasure();
  264. return new Size(100, 100);
  265. };
  266. }
  267. root.LayoutManager.ExecuteLayoutPass();
  268. //altough nonArrageableTargets has rubbish logic and can't be measured/arranged properly
  269. //layoutmanager should process properly other visuals
  270. Assert.All(targets, c => Assert.True(c.Arranged));
  271. }
  272. [Fact]
  273. public void LayoutManager_Should_Recover_From_Infinite_Loop_On_Measure()
  274. {
  275. // Test for issue #3041.
  276. var control = new LayoutTestControl();
  277. var root = new LayoutTestRoot { Child = control };
  278. root.LayoutManager.ExecuteInitialLayoutPass(root);
  279. control.Measured = false;
  280. control.DoMeasureOverride = (l, s) =>
  281. {
  282. control.InvalidateMeasure();
  283. return new Size(100, 100);
  284. };
  285. control.InvalidateMeasure();
  286. root.LayoutManager.ExecuteLayoutPass();
  287. // This is the important part: running a second layout pass in which we exceed the maximum
  288. // retries causes LayoutQueue<T>.Info.Count to exceed _maxEnqueueCountPerLoop.
  289. root.LayoutManager.ExecuteLayoutPass();
  290. control.Measured = false;
  291. control.DoMeasureOverride = null;
  292. root.LayoutManager.ExecuteLayoutPass();
  293. Assert.True(control.Measured);
  294. Assert.True(control.IsMeasureValid);
  295. }
  296. [Fact]
  297. public void Calling_ExecuteLayoutPass_From_ExecuteInitialLayoutPass_Does_Not_Break_Measure()
  298. {
  299. // Test for issue #3550.
  300. var control = new LayoutTestControl();
  301. var root = new LayoutTestRoot { Child = control };
  302. var count = 0;
  303. root.LayoutManager.ExecuteInitialLayoutPass(root);
  304. control.Measured = false;
  305. control.DoMeasureOverride = (l, s) =>
  306. {
  307. if (count++ == 0)
  308. {
  309. control.InvalidateMeasure();
  310. root.LayoutManager.ExecuteLayoutPass();
  311. return new Size(100, 100);
  312. }
  313. else
  314. {
  315. return new Size(200, 200);
  316. }
  317. };
  318. root.InvalidateMeasure();
  319. control.InvalidateMeasure();
  320. root.LayoutManager.ExecuteInitialLayoutPass(root);
  321. Assert.Equal(new Size(200, 200), control.Bounds.Size);
  322. Assert.Equal(new Size(200, 200), control.DesiredSize);
  323. }
  324. }
  325. }