AnimatableTests.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. using System;
  2. using Avalonia.Animation.Animators;
  3. using Avalonia.Controls;
  4. using Avalonia.Controls.Shapes;
  5. using Avalonia.Data;
  6. using Avalonia.Layout;
  7. using Avalonia.Media;
  8. using Avalonia.Styling;
  9. using Avalonia.UnitTests;
  10. using Moq;
  11. using Xunit;
  12. namespace Avalonia.Animation.UnitTests
  13. {
  14. public class AnimatableTests
  15. {
  16. [Fact]
  17. public void Transition_Is_Not_Applied_When_Not_Attached_To_Visual_Tree()
  18. {
  19. var target = CreateTarget();
  20. var control = new Control
  21. {
  22. Transitions = new Transitions { target.Object },
  23. };
  24. control.Opacity = 0.5;
  25. target.Verify(x => x.Apply(
  26. control,
  27. It.IsAny<IClock>(),
  28. 1.0,
  29. 0.5),
  30. Times.Never);
  31. }
  32. [Fact]
  33. public void Transition_Is_Not_Applied_To_Initial_Style()
  34. {
  35. using (UnitTestApplication.Start(TestServices.RealStyler))
  36. {
  37. var target = CreateTarget();
  38. var control = new Control
  39. {
  40. Transitions = new Transitions { target.Object },
  41. };
  42. var root = new TestRoot
  43. {
  44. Styles =
  45. {
  46. new Style(x => x.OfType<Control>())
  47. {
  48. Setters =
  49. {
  50. new Setter(Visual.OpacityProperty, 0.8),
  51. }
  52. }
  53. }
  54. };
  55. root.Child = control;
  56. Assert.Equal(0.8, control.Opacity);
  57. target.Verify(x => x.Apply(
  58. It.IsAny<Control>(),
  59. It.IsAny<IClock>(),
  60. It.IsAny<object>(),
  61. It.IsAny<object>()),
  62. Times.Never);
  63. }
  64. }
  65. [Fact]
  66. public void Transition_Is_Applied_When_Local_Value_Changes()
  67. {
  68. var target = CreateTarget();
  69. var control = CreateControl(target.Object);
  70. control.Opacity = 0.5;
  71. target.Verify(x => x.Apply(
  72. control,
  73. It.IsAny<IClock>(),
  74. 1.0,
  75. 0.5));
  76. }
  77. [Fact]
  78. public void Transition_Is_Not_Applied_When_Animated_Value_Changes()
  79. {
  80. var target = CreateTarget();
  81. var control = CreateControl(target.Object);
  82. control.SetValue(Visual.OpacityProperty, 0.5, BindingPriority.Animation);
  83. target.Verify(x => x.Apply(
  84. control,
  85. It.IsAny<IClock>(),
  86. 1.0,
  87. 0.5),
  88. Times.Never);
  89. }
  90. [Theory]
  91. [InlineData(null)] //null value
  92. [InlineData("stringValue")] //string value
  93. public void Invalid_Values_In_Animation_Should_Not_Crash_Animations(object invalidValue)
  94. {
  95. var keyframe1 = new KeyFrame()
  96. {
  97. Setters =
  98. {
  99. new Setter(Layoutable.WidthProperty, 1d),
  100. },
  101. KeyTime = TimeSpan.FromSeconds(0)
  102. };
  103. var keyframe2 = new KeyFrame()
  104. {
  105. Setters =
  106. {
  107. new Setter(Layoutable.WidthProperty, 2d),
  108. },
  109. KeyTime = TimeSpan.FromSeconds(2),
  110. };
  111. var keyframe3 = new KeyFrame()
  112. {
  113. Setters =
  114. {
  115. new Setter(Layoutable.WidthProperty, invalidValue),
  116. },
  117. KeyTime = TimeSpan.FromSeconds(3),
  118. };
  119. var animation = new Animation()
  120. {
  121. Duration = TimeSpan.FromSeconds(3),
  122. Children =
  123. {
  124. keyframe1,
  125. keyframe2,
  126. keyframe3
  127. },
  128. IterationCount = new IterationCount(5),
  129. PlaybackDirection = PlaybackDirection.Alternate,
  130. };
  131. var rect = new Rectangle()
  132. {
  133. Width = 11,
  134. };
  135. var originalValue = rect.Width;
  136. var clock = new TestClock();
  137. var animationRun = animation.RunAsync(rect, clock);
  138. clock.Step(TimeSpan.Zero);
  139. Assert.Equal(rect.Width, 1);
  140. clock.Step(TimeSpan.FromSeconds(2));
  141. Assert.Equal(rect.Width, 2);
  142. clock.Step(TimeSpan.FromSeconds(3));
  143. //here we have invalid value so value should be expected and set to initial original value
  144. Assert.Equal(rect.Width, originalValue);
  145. }
  146. [Fact]
  147. public void Transition_Is_Not_Applied_When_StyleTrigger_Changes_With_LocalValue_Present()
  148. {
  149. var target = CreateTarget();
  150. var control = CreateControl(target.Object);
  151. control.SetValue(Visual.OpacityProperty, 0.5);
  152. target.Verify(x => x.Apply(
  153. control,
  154. It.IsAny<IClock>(),
  155. 1.0,
  156. 0.5));
  157. target.Invocations.Clear();
  158. control.SetValue(Visual.OpacityProperty, 0.8, BindingPriority.StyleTrigger);
  159. target.Verify(x => x.Apply(
  160. It.IsAny<Control>(),
  161. It.IsAny<IClock>(),
  162. It.IsAny<object>(),
  163. It.IsAny<object>()),
  164. Times.Never);
  165. }
  166. [Fact]
  167. public void Transition_Is_Disposed_When_Local_Value_Changes()
  168. {
  169. var target = CreateTarget();
  170. var control = CreateControl(target.Object);
  171. var sub = new Mock<IDisposable>();
  172. target.Setup(x => x.Apply(control, It.IsAny<IClock>(), 1.0, 0.5)).Returns(sub.Object);
  173. control.Opacity = 0.5;
  174. sub.Invocations.Clear();
  175. control.Opacity = 0.4;
  176. sub.Verify(x => x.Dispose());
  177. }
  178. [Fact]
  179. public void New_Transition_Is_Applied_When_Local_Value_Changes()
  180. {
  181. var target = CreateTarget();
  182. var control = CreateControl(target.Object);
  183. target.Setup(x => x.Property).Returns(Visual.OpacityProperty);
  184. target.Setup(x => x.Apply(control, It.IsAny<IClock>(), 1.0, 0.5))
  185. .Callback(() =>
  186. {
  187. control.SetValue(Visual.OpacityProperty, 0.9, BindingPriority.Animation);
  188. })
  189. .Returns(Mock.Of<IDisposable>());
  190. control.Opacity = 0.5;
  191. Assert.Equal(0.9, control.Opacity);
  192. target.Invocations.Clear();
  193. control.Opacity = 0.4;
  194. target.Verify(x => x.Apply(
  195. control,
  196. It.IsAny<IClock>(),
  197. 0.9,
  198. 0.4));
  199. }
  200. [Fact]
  201. public void Transition_Is_Not_Applied_When_Removed_From_Visual_Tree()
  202. {
  203. var target = CreateTarget();
  204. var control = CreateControl(target.Object);
  205. control.Opacity = 0.5;
  206. target.Verify(x => x.Apply(
  207. control,
  208. It.IsAny<IClock>(),
  209. 1.0,
  210. 0.5));
  211. target.Invocations.Clear();
  212. var root = (TestRoot)control.Parent;
  213. root.Child = null;
  214. control.Opacity = 0.8;
  215. target.Verify(x => x.Apply(
  216. It.IsAny<Control>(),
  217. It.IsAny<IClock>(),
  218. It.IsAny<object>(),
  219. It.IsAny<object>()),
  220. Times.Never);
  221. }
  222. [Fact]
  223. public void Animation_Is_Cancelled_When_Transition_Removed()
  224. {
  225. var target = CreateTarget();
  226. var control = CreateControl(target.Object);
  227. var sub = new Mock<IDisposable>();
  228. target.Setup(x => x.Apply(
  229. It.IsAny<Animatable>(),
  230. It.IsAny<IClock>(),
  231. It.IsAny<object>(),
  232. It.IsAny<object>())).Returns(sub.Object);
  233. control.Opacity = 0.5;
  234. control.Transitions.RemoveAt(0);
  235. sub.Verify(x => x.Dispose());
  236. }
  237. [Fact]
  238. public void Animation_Is_Cancelled_When_New_Style_Activates()
  239. {
  240. using (UnitTestApplication.Start(TestServices.RealStyler))
  241. {
  242. var target = CreateTarget();
  243. var control = CreateStyledControl(target.Object);
  244. var sub = new Mock<IDisposable>();
  245. target.Setup(x => x.Apply(
  246. control,
  247. It.IsAny<IClock>(),
  248. 1.0,
  249. 0.5)).Returns(sub.Object);
  250. control.Opacity = 0.5;
  251. target.Verify(x => x.Apply(
  252. control,
  253. It.IsAny<Clock>(),
  254. 1.0,
  255. 0.5),
  256. Times.Once);
  257. control.Classes.Add("foo");
  258. sub.Verify(x => x.Dispose());
  259. }
  260. }
  261. [Fact]
  262. public void Transition_From_Style_Trigger_Is_Applied()
  263. {
  264. using (UnitTestApplication.Start(TestServices.RealStyler))
  265. {
  266. var target = CreateTransition(Control.WidthProperty);
  267. var control = CreateStyledControl(transition2: target.Object);
  268. var sub = new Mock<IDisposable>();
  269. control.Classes.Add("foo");
  270. control.Width = 100;
  271. target.Verify(x => x.Apply(
  272. control,
  273. It.IsAny<Clock>(),
  274. double.NaN,
  275. 100.0),
  276. Times.Once);
  277. }
  278. }
  279. [Fact]
  280. public void Replacing_Transitions_During_Animation_Does_Not_Throw_KeyNotFound()
  281. {
  282. // Issue #4059
  283. using (UnitTestApplication.Start(TestServices.RealStyler))
  284. {
  285. Border target;
  286. var clock = new TestClock();
  287. var root = new TestRoot
  288. {
  289. Clock = clock,
  290. Styles =
  291. {
  292. new Style(x => x.OfType<Border>())
  293. {
  294. Setters =
  295. {
  296. new Setter(Border.TransitionsProperty,
  297. new Transitions
  298. {
  299. new DoubleTransition
  300. {
  301. Property = Border.OpacityProperty,
  302. Duration = TimeSpan.FromSeconds(1),
  303. },
  304. }),
  305. },
  306. },
  307. new Style(x => x.OfType<Border>().Class("foo"))
  308. {
  309. Setters =
  310. {
  311. new Setter(Border.TransitionsProperty,
  312. new Transitions
  313. {
  314. new DoubleTransition
  315. {
  316. Property = Border.OpacityProperty,
  317. Duration = TimeSpan.FromSeconds(1),
  318. },
  319. }),
  320. new Setter(Border.OpacityProperty, 0.0),
  321. },
  322. },
  323. },
  324. Child = target = new Border
  325. {
  326. Background = Brushes.Red,
  327. }
  328. };
  329. root.Measure(Size.Infinity);
  330. root.Arrange(new Rect(root.DesiredSize));
  331. target.Classes.Add("foo");
  332. clock.Step(TimeSpan.FromSeconds(0));
  333. clock.Step(TimeSpan.FromSeconds(0.5));
  334. Assert.Equal(0.5, target.Opacity);
  335. target.Classes.Remove("foo");
  336. }
  337. }
  338. [Fact]
  339. public void Transitions_Can_Be_Changed_To_Collection_That_Contains_The_Same_Transitions()
  340. {
  341. var target = CreateTarget();
  342. var control = CreateControl(target.Object);
  343. control.Transitions = new Transitions { target.Object };
  344. }
  345. [Fact]
  346. public void Transitions_Can_Re_Set_During_Batch_Update()
  347. {
  348. var target = CreateTarget();
  349. var control = CreateControl(target.Object);
  350. // Assigning and then clearing Transitions ensures we have a transition state
  351. // collection created.
  352. control.Transitions = null;
  353. control.BeginBatchUpdate();
  354. // Setting opacity then Transitions means that we receive the Transitions change
  355. // after the Opacity change when EndBatchUpdate is called.
  356. control.Opacity = 0.5;
  357. control.Transitions = new Transitions { target.Object };
  358. // Which means that the transition state hasn't been initialized with the new
  359. // Transitions when the Opacity change notification gets raised here.
  360. control.EndBatchUpdate();
  361. }
  362. private static Mock<ITransition> CreateTarget()
  363. {
  364. return CreateTransition(Visual.OpacityProperty);
  365. }
  366. private static Control CreateControl(ITransition transition)
  367. {
  368. var control = new Control
  369. {
  370. Transitions = new Transitions { transition },
  371. };
  372. var root = new TestRoot(control);
  373. return control;
  374. }
  375. private static Control CreateStyledControl(
  376. ITransition transition1 = null,
  377. ITransition transition2 = null)
  378. {
  379. transition1 = transition1 ?? CreateTarget().Object;
  380. transition2 = transition2 ?? CreateTransition(Control.WidthProperty).Object;
  381. var control = new Control
  382. {
  383. Styles =
  384. {
  385. new Style(x => x.OfType<Control>())
  386. {
  387. Setters =
  388. {
  389. new Setter
  390. {
  391. Property = Control.TransitionsProperty,
  392. Value = new Transitions { transition1 },
  393. }
  394. }
  395. },
  396. new Style(x => x.OfType<Control>().Class("foo"))
  397. {
  398. Setters =
  399. {
  400. new Setter
  401. {
  402. Property = Control.TransitionsProperty,
  403. Value = new Transitions { transition2 },
  404. }
  405. }
  406. }
  407. }
  408. };
  409. var root = new TestRoot(control);
  410. return control;
  411. }
  412. private static Mock<ITransition> CreateTransition(AvaloniaProperty property)
  413. {
  414. var target = new Mock<ITransition>();
  415. var sub = new Mock<IDisposable>();
  416. target.Setup(x => x.Property).Returns(property);
  417. target.Setup(x => x.Apply(
  418. It.IsAny<Animatable>(),
  419. It.IsAny<IClock>(),
  420. It.IsAny<object>(),
  421. It.IsAny<object>())).Returns(sub.Object);
  422. return target;
  423. }
  424. }
  425. }