ExpressionObserverTests_Observable.cs 6.4 KB

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