ExpressionObserverTests_Observable.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  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. private class Class1 : NotifyingBase
  125. {
  126. public Subject<Class2> Next { get; } = new Subject<Class2>();
  127. }
  128. private class Class2 : NotifyingBase
  129. {
  130. public Class2(string foo)
  131. {
  132. Foo = foo;
  133. }
  134. public string Foo { get; }
  135. }
  136. private class NotStreamable
  137. {
  138. public object StreamBinding() { throw new InvalidOperationException(); }
  139. }
  140. }
  141. }