ExpressionObserverTests_Observable.cs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Reactive.Linq;
  4. using System.Reactive.Subjects;
  5. using Avalonia.Data;
  6. using Avalonia.Data.Core;
  7. using Avalonia.Markup.Parsers;
  8. using Avalonia.UnitTests;
  9. using Xunit;
  10. namespace Avalonia.Base.UnitTests.Data.Core
  11. {
  12. public class ExpressionObserverTests_Observable
  13. {
  14. [Fact]
  15. public void Should_Not_Get_Observable_Value_Without_Streaming()
  16. {
  17. using (var sync = UnitTestSynchronizationContext.Begin())
  18. {
  19. var source = new BehaviorSubject<string>("foo");
  20. var data = new { Foo = source };
  21. var target = ExpressionObserver.Create(data, o => o.Foo);
  22. var result = new List<object>();
  23. var sub = target.Subscribe(x => result.Add(x));
  24. source.OnNext("bar");
  25. sync.ExecutePostedCallbacks();
  26. Assert.Equal(new[] { source }, result);
  27. GC.KeepAlive(data);
  28. }
  29. }
  30. [Fact]
  31. public void Should_Get_Simple_Observable_Value()
  32. {
  33. using (var sync = UnitTestSynchronizationContext.Begin())
  34. {
  35. var source = new BehaviorSubject<string>("foo");
  36. var data = new { Foo = source };
  37. var target = ExpressionObserver.Create(data, o => o.Foo.StreamBinding());
  38. var result = new List<object>();
  39. var sub = target.Subscribe(x => result.Add(x));
  40. source.OnNext("bar");
  41. sync.ExecutePostedCallbacks();
  42. Assert.Equal(new[] { "foo", "bar" }, result);
  43. GC.KeepAlive(data);
  44. }
  45. }
  46. [Fact]
  47. public void Should_Get_Property_Value_From_Observable()
  48. {
  49. using (var sync = UnitTestSynchronizationContext.Begin())
  50. {
  51. var data = new Class1();
  52. var target = ExpressionObserver.Create(data, o => o.Next.StreamBinding().Foo);
  53. var result = new List<object>();
  54. var sub = target.Subscribe(x => result.Add(x));
  55. data.Next.OnNext(new Class2("foo"));
  56. sync.ExecutePostedCallbacks();
  57. Assert.Equal(new[] { "foo" }, result);
  58. sub.Dispose();
  59. Assert.Equal(0, data.PropertyChangedSubscriptionCount);
  60. GC.KeepAlive(data);
  61. }
  62. }
  63. [Fact]
  64. public void Should_Get_Simple_Observable_Value_With_DataValidation_Enabled()
  65. {
  66. using (var sync = UnitTestSynchronizationContext.Begin())
  67. {
  68. var source = new BehaviorSubject<string>("foo");
  69. var data = new { Foo = source };
  70. var target = ExpressionObserver.Create(data, o => o.Foo.StreamBinding(), true);
  71. var result = new List<object>();
  72. var sub = target.Subscribe(x => result.Add(x));
  73. source.OnNext("bar");
  74. sync.ExecutePostedCallbacks();
  75. // What does it mean to have data validation on an observable? Without a use-case
  76. // it's hard to know what to do here so for the moment the value is returned.
  77. Assert.Equal(new[] { "foo", "bar" }, result);
  78. GC.KeepAlive(data);
  79. }
  80. }
  81. [Fact]
  82. public void Should_Get_Property_Value_From_Observable_With_DataValidation_Enabled()
  83. {
  84. using (var sync = UnitTestSynchronizationContext.Begin())
  85. {
  86. var data1 = new Class1();
  87. var data2 = new Class2("foo");
  88. var target = ExpressionObserver.Create(data1, o => o.Next.StreamBinding().Foo, true);
  89. var result = new List<object>();
  90. var sub = target.Subscribe(x => result.Add(x));
  91. data1.Next.OnNext(data2);
  92. sync.ExecutePostedCallbacks();
  93. Assert.Equal(new[] { new BindingNotification("foo") }, result);
  94. sub.Dispose();
  95. Assert.Equal(0, data1.PropertyChangedSubscriptionCount);
  96. GC.KeepAlive(data1);
  97. GC.KeepAlive(data2);
  98. }
  99. }
  100. [Fact]
  101. public void Should_Return_BindingNotification_If_Stream_Operator_Applied_To_Not_Supported_Type()
  102. {
  103. using (var sync = UnitTestSynchronizationContext.Begin())
  104. {
  105. var data = new NotStreamable();
  106. var target = ExpressionObserver.Create(data, o => o.StreamBinding());
  107. var result = new List<object>();
  108. var sub = target.Subscribe(x => result.Add(x));
  109. sync.ExecutePostedCallbacks();
  110. Assert.Equal(
  111. new[]
  112. {
  113. new BindingNotification(
  114. new MarkupBindingChainException("Stream operator applied to unsupported type", "o => o.StreamBinding()", "^"),
  115. BindingErrorType.Error)
  116. },
  117. result);
  118. sub.Dispose();
  119. GC.KeepAlive(data);
  120. }
  121. }
  122. [Fact]
  123. public void Should_Work_With_Value_Type()
  124. {
  125. using (var sync = UnitTestSynchronizationContext.Begin())
  126. {
  127. var source = new BehaviorSubject<int>(1);
  128. var data = new { Foo = source };
  129. var target = ExpressionObserver.Create(data, o => o.Foo.StreamBinding());
  130. var result = new List<int>();
  131. var sub = target.Subscribe(x => result.Add((int)x));
  132. source.OnNext(42);
  133. sync.ExecutePostedCallbacks();
  134. Assert.Equal(new[] { 1, 42 }, result);
  135. GC.KeepAlive(data);
  136. }
  137. }
  138. private class Class1 : NotifyingBase
  139. {
  140. public Subject<Class2> Next { get; } = new Subject<Class2>();
  141. }
  142. private class Class2 : NotifyingBase
  143. {
  144. public Class2(string foo)
  145. {
  146. Foo = foo;
  147. }
  148. public string Foo { get; }
  149. }
  150. private class NotStreamable
  151. {
  152. public object StreamBinding() { throw new InvalidOperationException(); }
  153. }
  154. }
  155. }