AvaloniaObjectTests_BatchUpdate.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Reactive;
  4. using System.Reactive.Disposables;
  5. using System.Reactive.Linq;
  6. using System.Text;
  7. using Avalonia.Data;
  8. using Xunit;
  9. namespace Avalonia.Base.UnitTests
  10. {
  11. public class AvaloniaObjectTests_BatchUpdate
  12. {
  13. [Fact]
  14. public void SetValue_Should_Not_Raise_Property_Changes_During_Batch_Update()
  15. {
  16. var target = new TestClass();
  17. var raised = new List<string>();
  18. target.GetObservable(TestClass.FooProperty).Skip(1).Subscribe(x => raised.Add(x));
  19. target.BeginBatchUpdate();
  20. target.SetValue(TestClass.FooProperty, "foo", BindingPriority.LocalValue);
  21. Assert.Empty(raised);
  22. }
  23. [Fact]
  24. public void Binding_Should_Not_Raise_Property_Changes_During_Batch_Update()
  25. {
  26. var target = new TestClass();
  27. var observable = new TestObservable<string>("foo");
  28. var raised = new List<string>();
  29. target.GetObservable(TestClass.FooProperty).Skip(1).Subscribe(x => raised.Add(x));
  30. target.BeginBatchUpdate();
  31. target.Bind(TestClass.FooProperty, observable, BindingPriority.LocalValue);
  32. Assert.Empty(raised);
  33. }
  34. [Fact]
  35. public void Binding_Completion_Should_Not_Raise_Property_Changes_During_Batch_Update()
  36. {
  37. var target = new TestClass();
  38. var observable = new TestObservable<string>("foo");
  39. var raised = new List<string>();
  40. target.Bind(TestClass.FooProperty, observable, BindingPriority.LocalValue);
  41. target.GetObservable(TestClass.FooProperty).Skip(1).Subscribe(x => raised.Add(x));
  42. target.BeginBatchUpdate();
  43. observable.OnCompleted();
  44. Assert.Empty(raised);
  45. }
  46. [Fact]
  47. public void SetValue_Change_Should_Be_Raised_After_Batch_Update_1()
  48. {
  49. var target = new TestClass();
  50. var raised = new List<AvaloniaPropertyChangedEventArgs>();
  51. target.PropertyChanged += (s, e) => raised.Add(e);
  52. target.BeginBatchUpdate();
  53. target.SetValue(TestClass.FooProperty, "foo", BindingPriority.LocalValue);
  54. target.EndBatchUpdate();
  55. Assert.Equal(1, raised.Count);
  56. Assert.Equal("foo", target.Foo);
  57. Assert.Null(raised[0].OldValue);
  58. Assert.Equal("foo", raised[0].NewValue);
  59. }
  60. [Fact]
  61. public void SetValue_Change_Should_Be_Raised_After_Batch_Update_2()
  62. {
  63. var target = new TestClass();
  64. var raised = new List<AvaloniaPropertyChangedEventArgs>();
  65. target.SetValue(TestClass.FooProperty, "foo", BindingPriority.LocalValue);
  66. target.PropertyChanged += (s, e) => raised.Add(e);
  67. target.BeginBatchUpdate();
  68. target.SetValue(TestClass.FooProperty, "bar", BindingPriority.LocalValue);
  69. target.SetValue(TestClass.FooProperty, "baz", BindingPriority.LocalValue);
  70. target.EndBatchUpdate();
  71. Assert.Equal(1, raised.Count);
  72. Assert.Equal("baz", target.Foo);
  73. }
  74. [Fact]
  75. public void SetValue_Changes_Should_Be_Raised_In_Correct_Order_After_Batch_Update()
  76. {
  77. var target = new TestClass();
  78. var raised = new List<AvaloniaPropertyChangedEventArgs>();
  79. target.PropertyChanged += (s, e) => raised.Add(e);
  80. target.BeginBatchUpdate();
  81. target.SetValue(TestClass.FooProperty, "foo", BindingPriority.LocalValue);
  82. target.SetValue(TestClass.BarProperty, "bar", BindingPriority.LocalValue);
  83. target.SetValue(TestClass.FooProperty, "baz", BindingPriority.LocalValue);
  84. target.EndBatchUpdate();
  85. Assert.Equal(2, raised.Count);
  86. Assert.Equal(TestClass.BarProperty, raised[0].Property);
  87. Assert.Equal(TestClass.FooProperty, raised[1].Property);
  88. Assert.Equal("baz", target.Foo);
  89. Assert.Equal("bar", target.Bar);
  90. }
  91. [Fact]
  92. public void SetValue_And_Binding_Changes_Should_Be_Raised_In_Correct_Order_After_Batch_Update_1()
  93. {
  94. var target = new TestClass();
  95. var observable = new TestObservable<string>("baz");
  96. var raised = new List<AvaloniaPropertyChangedEventArgs>();
  97. target.PropertyChanged += (s, e) => raised.Add(e);
  98. target.BeginBatchUpdate();
  99. target.SetValue(TestClass.FooProperty, "foo", BindingPriority.LocalValue);
  100. target.SetValue(TestClass.BarProperty, "bar", BindingPriority.LocalValue);
  101. target.Bind(TestClass.FooProperty, observable, BindingPriority.LocalValue);
  102. target.EndBatchUpdate();
  103. Assert.Equal(2, raised.Count);
  104. Assert.Equal(TestClass.BarProperty, raised[0].Property);
  105. Assert.Equal(TestClass.FooProperty, raised[1].Property);
  106. Assert.Equal("baz", target.Foo);
  107. Assert.Equal("bar", target.Bar);
  108. }
  109. [Fact]
  110. public void SetValue_And_Binding_Changes_Should_Be_Raised_In_Correct_Order_After_Batch_Update_2()
  111. {
  112. var target = new TestClass();
  113. var observable = new TestObservable<string>("foo");
  114. var raised = new List<AvaloniaPropertyChangedEventArgs>();
  115. target.PropertyChanged += (s, e) => raised.Add(e);
  116. target.BeginBatchUpdate();
  117. target.Bind(TestClass.FooProperty, observable, BindingPriority.LocalValue);
  118. target.SetValue(TestClass.BarProperty, "bar", BindingPriority.LocalValue);
  119. target.SetValue(TestClass.FooProperty, "baz", BindingPriority.LocalValue);
  120. target.EndBatchUpdate();
  121. Assert.Equal(2, raised.Count);
  122. Assert.Equal(TestClass.BarProperty, raised[0].Property);
  123. Assert.Equal(TestClass.FooProperty, raised[1].Property);
  124. Assert.Equal("baz", target.Foo);
  125. Assert.Equal("bar", target.Bar);
  126. }
  127. [Fact]
  128. public void SetValue_And_Binding_Changes_Should_Be_Raised_In_Correct_Order_After_Batch_Update_3()
  129. {
  130. var target = new TestClass();
  131. var observable1 = new TestObservable<string>("foo");
  132. var observable2 = new TestObservable<string>("qux");
  133. var raised = new List<AvaloniaPropertyChangedEventArgs>();
  134. target.PropertyChanged += (s, e) => raised.Add(e);
  135. target.BeginBatchUpdate();
  136. target.Bind(TestClass.FooProperty, observable2, BindingPriority.LocalValue);
  137. target.Bind(TestClass.FooProperty, observable1, BindingPriority.LocalValue);
  138. target.SetValue(TestClass.BarProperty, "bar", BindingPriority.LocalValue);
  139. target.SetValue(TestClass.FooProperty, "baz", BindingPriority.LocalValue);
  140. target.EndBatchUpdate();
  141. Assert.Equal(2, raised.Count);
  142. Assert.Equal(TestClass.BarProperty, raised[0].Property);
  143. Assert.Equal(TestClass.FooProperty, raised[1].Property);
  144. Assert.Equal("baz", target.Foo);
  145. Assert.Equal("bar", target.Bar);
  146. }
  147. [Fact]
  148. public void Binding_Change_Should_Be_Raised_After_Batch_Update_1()
  149. {
  150. var target = new TestClass();
  151. var observable = new TestObservable<string>("foo");
  152. var raised = new List<AvaloniaPropertyChangedEventArgs>();
  153. target.PropertyChanged += (s, e) => raised.Add(e);
  154. target.BeginBatchUpdate();
  155. target.Bind(TestClass.FooProperty, observable, BindingPriority.LocalValue);
  156. target.EndBatchUpdate();
  157. Assert.Equal(1, raised.Count);
  158. Assert.Equal("foo", target.Foo);
  159. Assert.Null(raised[0].OldValue);
  160. Assert.Equal("foo", raised[0].NewValue);
  161. }
  162. [Fact]
  163. public void Binding_Change_Should_Be_Raised_After_Batch_Update_2()
  164. {
  165. var target = new TestClass();
  166. var observable1 = new TestObservable<string>("bar");
  167. var observable2 = new TestObservable<string>("baz");
  168. var raised = new List<AvaloniaPropertyChangedEventArgs>();
  169. target.SetValue(TestClass.FooProperty, "foo", BindingPriority.LocalValue);
  170. target.PropertyChanged += (s, e) => raised.Add(e);
  171. target.BeginBatchUpdate();
  172. target.Bind(TestClass.FooProperty, observable1, BindingPriority.LocalValue);
  173. target.Bind(TestClass.FooProperty, observable2, BindingPriority.LocalValue);
  174. target.EndBatchUpdate();
  175. Assert.Equal(1, raised.Count);
  176. Assert.Equal("baz", target.Foo);
  177. Assert.Equal("foo", raised[0].OldValue);
  178. Assert.Equal("baz", raised[0].NewValue);
  179. }
  180. [Fact]
  181. public void Binding_Completion_Should_Be_Raised_After_Batch_Update()
  182. {
  183. var target = new TestClass();
  184. var observable = new TestObservable<string>("foo");
  185. var raised = new List<AvaloniaPropertyChangedEventArgs>();
  186. target.Bind(TestClass.FooProperty, observable, BindingPriority.LocalValue);
  187. target.PropertyChanged += (s, e) => raised.Add(e);
  188. target.BeginBatchUpdate();
  189. observable.OnCompleted();
  190. target.EndBatchUpdate();
  191. Assert.Equal(1, raised.Count);
  192. Assert.Null(target.Foo);
  193. Assert.Equal("foo", raised[0].OldValue);
  194. Assert.Null(raised[0].NewValue);
  195. Assert.Equal(BindingPriority.Unset, raised[0].Priority);
  196. }
  197. [Fact]
  198. public void Bindings_Should_Be_Subscribed_Before_Batch_Update()
  199. {
  200. var target = new TestClass();
  201. var observable1 = new TestObservable<string>("foo");
  202. var observable2 = new TestObservable<string>("bar");
  203. target.Bind(TestClass.FooProperty, observable1, BindingPriority.LocalValue);
  204. target.Bind(TestClass.FooProperty, observable2, BindingPriority.LocalValue);
  205. Assert.Equal(1, observable1.SubscribeCount);
  206. Assert.Equal(1, observable2.SubscribeCount);
  207. }
  208. [Fact]
  209. public void Non_Active_Binding_Should_Not_Be_Subscribed_Before_Batch_Update()
  210. {
  211. var target = new TestClass();
  212. var observable1 = new TestObservable<string>("foo");
  213. var observable2 = new TestObservable<string>("bar");
  214. target.Bind(TestClass.FooProperty, observable1, BindingPriority.LocalValue);
  215. target.Bind(TestClass.FooProperty, observable2, BindingPriority.Style);
  216. Assert.Equal(1, observable1.SubscribeCount);
  217. Assert.Equal(0, observable2.SubscribeCount);
  218. }
  219. [Fact]
  220. public void LocalValue_Bindings_Should_Be_Subscribed_During_Batch_Update()
  221. {
  222. var target = new TestClass();
  223. var observable1 = new TestObservable<string>("foo");
  224. var observable2 = new TestObservable<string>("bar");
  225. var raised = new List<AvaloniaPropertyChangedEventArgs>();
  226. target.PropertyChanged += (s, e) => raised.Add(e);
  227. // We need to subscribe to LocalValue bindings even if we've got a batch operation
  228. // in progress because otherwise we don't know whether the binding or a subsequent
  229. // SetValue with local priority will win. Notifications however shouldn't be sent.
  230. target.BeginBatchUpdate();
  231. target.Bind(TestClass.FooProperty, observable1, BindingPriority.LocalValue);
  232. target.Bind(TestClass.FooProperty, observable2, BindingPriority.LocalValue);
  233. Assert.Equal(1, observable1.SubscribeCount);
  234. Assert.Equal(1, observable2.SubscribeCount);
  235. Assert.Empty(raised);
  236. }
  237. [Fact]
  238. public void Style_Bindings_Should_Not_Be_Subscribed_During_Batch_Update()
  239. {
  240. var target = new TestClass();
  241. var observable1 = new TestObservable<string>("foo");
  242. var observable2 = new TestObservable<string>("bar");
  243. target.BeginBatchUpdate();
  244. target.Bind(TestClass.FooProperty, observable1, BindingPriority.Style);
  245. target.Bind(TestClass.FooProperty, observable2, BindingPriority.StyleTrigger);
  246. Assert.Equal(0, observable1.SubscribeCount);
  247. Assert.Equal(0, observable2.SubscribeCount);
  248. }
  249. [Fact]
  250. public void Active_Style_Binding_Should_Be_Subscribed_After_Batch_Uppdate_1()
  251. {
  252. var target = new TestClass();
  253. var observable1 = new TestObservable<string>("foo");
  254. var observable2 = new TestObservable<string>("bar");
  255. target.BeginBatchUpdate();
  256. target.Bind(TestClass.FooProperty, observable1, BindingPriority.Style);
  257. target.Bind(TestClass.FooProperty, observable2, BindingPriority.Style);
  258. target.EndBatchUpdate();
  259. Assert.Equal(0, observable1.SubscribeCount);
  260. Assert.Equal(1, observable2.SubscribeCount);
  261. }
  262. [Fact]
  263. public void Active_Style_Binding_Should_Be_Subscribed_After_Batch_Uppdate_2()
  264. {
  265. var target = new TestClass();
  266. var observable1 = new TestObservable<string>("foo");
  267. var observable2 = new TestObservable<string>("bar");
  268. target.BeginBatchUpdate();
  269. target.Bind(TestClass.FooProperty, observable1, BindingPriority.StyleTrigger);
  270. target.Bind(TestClass.FooProperty, observable2, BindingPriority.Style);
  271. target.EndBatchUpdate();
  272. Assert.Equal(1, observable1.SubscribeCount);
  273. Assert.Equal(0, observable2.SubscribeCount);
  274. }
  275. public class TestClass : AvaloniaObject
  276. {
  277. public static readonly StyledProperty<string> FooProperty =
  278. AvaloniaProperty.Register<TestClass, string>(nameof(Foo));
  279. public static readonly StyledProperty<string> BarProperty =
  280. AvaloniaProperty.Register<TestClass, string>(nameof(Bar));
  281. public string Foo
  282. {
  283. get => GetValue(FooProperty);
  284. set => SetValue(FooProperty, value);
  285. }
  286. public string Bar
  287. {
  288. get => GetValue(BarProperty);
  289. set => SetValue(BarProperty, value);
  290. }
  291. }
  292. public class TestObservable<T> : ObservableBase<BindingValue<T>>
  293. {
  294. private readonly T _value;
  295. private IObserver<BindingValue<T>> _observer;
  296. public TestObservable(T value) => _value = value;
  297. public int SubscribeCount { get; private set; }
  298. public void OnCompleted() => _observer.OnCompleted();
  299. public void OnError(Exception e) => _observer.OnError(e);
  300. protected override IDisposable SubscribeCore(IObserver<BindingValue<T>> observer)
  301. {
  302. ++SubscribeCount;
  303. _observer = observer;
  304. observer.OnNext(_value);
  305. return Disposable.Empty;
  306. }
  307. }
  308. }
  309. }