PageRenderTime 50ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/ReactiveUI.Tests/ReactiveNotifyPropertyChangedMixinTest.cs

https://github.com/bsiegel/ReactiveUI
C# | 454 lines | 367 code | 81 blank | 6 comment | 16 complexity | f1ebfbc7adf9571a242b8063a0cfe747 MD5 | raw file
Possible License(s): Apache-2.0, CC-BY-SA-3.0, LGPL-2.0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Linq;
  5. using System.Linq.Expressions;
  6. using System.Reactive.Concurrency;
  7. using System.Reactive.Linq;
  8. using System.Text;
  9. using System.Threading;
  10. using System.Windows;
  11. using System.Windows.Controls;
  12. using ReactiveUI.Testing;
  13. using Xunit;
  14. using Microsoft.Reactive.Testing;
  15. namespace ReactiveUI.Tests
  16. {
  17. public class HostTestFixture : ReactiveObject
  18. {
  19. public TestFixture _Child;
  20. public TestFixture Child {
  21. get { return _Child; }
  22. set { this.RaiseAndSetIfChanged(x => x.Child, value); }
  23. }
  24. public int _SomeOtherParam;
  25. public int SomeOtherParam {
  26. get { return _SomeOtherParam; }
  27. set { this.RaiseAndSetIfChanged(x => x.SomeOtherParam, value); }
  28. }
  29. public NonObservableTestFixture _PocoChild;
  30. public NonObservableTestFixture PocoChild {
  31. get { return _PocoChild; }
  32. set { this.RaiseAndSetIfChanged(x => x.PocoChild, value); }
  33. }
  34. }
  35. public class NonObservableTestFixture
  36. {
  37. public TestFixture Child {get; set;}
  38. }
  39. public class NonReactiveINPCObject : INotifyPropertyChanged
  40. {
  41. public event PropertyChangingEventHandler PropertyChanging;
  42. public event PropertyChangedEventHandler PropertyChanged;
  43. TestFixture _InpcProperty;
  44. public TestFixture InpcProperty {
  45. get { return _InpcProperty; }
  46. set {
  47. if (_InpcProperty == value) {
  48. return;
  49. }
  50. _InpcProperty = value;
  51. if (PropertyChanged == null) return;
  52. PropertyChanged(this, new PropertyChangedEventArgs("InpcProperty"));
  53. }
  54. }
  55. }
  56. public class HostTestView : Control, IViewFor<HostTestFixture>
  57. {
  58. public HostTestFixture ViewModel {
  59. get { return (HostTestFixture)GetValue(ViewModelProperty); }
  60. set { SetValue(ViewModelProperty, value); }
  61. }
  62. public static readonly DependencyProperty ViewModelProperty =
  63. DependencyProperty.Register("ViewModel", typeof(HostTestFixture), typeof(HostTestView), new PropertyMetadata(null));
  64. object IViewFor.ViewModel {
  65. get { return ViewModel; }
  66. set { ViewModel = (HostTestFixture) value; }
  67. }
  68. }
  69. public class ObjChain1 : ReactiveObject
  70. {
  71. public ObjChain2 _Model = new ObjChain2();
  72. public ObjChain2 Model {
  73. get { return _Model; }
  74. set { this.RaiseAndSetIfChanged(x => x.Model, value); }
  75. }
  76. }
  77. public class ObjChain2 : ReactiveObject
  78. {
  79. public ObjChain3 _Model = new ObjChain3();
  80. public ObjChain3 Model {
  81. get { return _Model; }
  82. set { this.RaiseAndSetIfChanged(x => x.Model, value); }
  83. }
  84. }
  85. public class ObjChain3 : ReactiveObject
  86. {
  87. public HostTestFixture _Model = new HostTestFixture();
  88. public HostTestFixture Model {
  89. get { return _Model; }
  90. set { this.RaiseAndSetIfChanged(x => x.Model, value); }
  91. }
  92. }
  93. public class ReactiveNotifyPropertyChangedMixinTest
  94. {
  95. [Fact]
  96. public void OFPSimplePropertyTest()
  97. {
  98. (new TestScheduler()).With(sched => {
  99. var fixture = new TestFixture();
  100. var changes = fixture.ObservableForProperty(x => x.IsOnlyOneWord).CreateCollection();
  101. fixture.IsOnlyOneWord = "Foo";
  102. sched.Start();
  103. Assert.Equal(1, changes.Count);
  104. fixture.IsOnlyOneWord = "Bar";
  105. sched.Start();
  106. Assert.Equal(2, changes.Count);
  107. fixture.IsOnlyOneWord = "Baz";
  108. sched.Start();
  109. Assert.Equal(3, changes.Count);
  110. fixture.IsOnlyOneWord = "Baz";
  111. sched.Start();
  112. Assert.Equal(3, changes.Count);
  113. Assert.True(changes.All(x => x.Sender == fixture));
  114. Assert.True(changes.All(x => x.PropertyName == "IsOnlyOneWord"));
  115. changes.Select(x => x.Value).AssertAreEqual(new[] {"Foo", "Bar", "Baz"});
  116. });
  117. }
  118. [Fact]
  119. public void OFPSimpleChildPropertyTest()
  120. {
  121. (new TestScheduler()).With(sched => {
  122. var fixture = new HostTestFixture() {Child = new TestFixture()};
  123. var changes = fixture.ObservableForProperty(x => x.Child.IsOnlyOneWord).CreateCollection();
  124. fixture.Child.IsOnlyOneWord = "Foo";
  125. sched.Start();
  126. Assert.Equal(1, changes.Count);
  127. fixture.Child.IsOnlyOneWord = "Bar";
  128. sched.Start();
  129. Assert.Equal(2, changes.Count);
  130. fixture.Child.IsOnlyOneWord = "Baz";
  131. sched.Start();
  132. Assert.Equal(3, changes.Count);
  133. fixture.Child.IsOnlyOneWord = "Baz";
  134. sched.Start();
  135. Assert.Equal(3, changes.Count);
  136. Assert.True(changes.All(x => x.Sender == fixture));
  137. Assert.True(changes.All(x => x.PropertyName == "Child.IsOnlyOneWord"));
  138. changes.Select(x => x.Value).AssertAreEqual(new[] {"Foo", "Bar", "Baz"});
  139. });
  140. }
  141. [Fact]
  142. public void OFPReplacingTheHostShouldResubscribeTheObservable()
  143. {
  144. (new TestScheduler()).With(sched => {
  145. var fixture = new HostTestFixture() {Child = new TestFixture()};
  146. var changes = fixture.ObservableForProperty(x => x.Child.IsOnlyOneWord).CreateCollection();
  147. fixture.Child.IsOnlyOneWord = "Foo";
  148. sched.Start();
  149. Assert.Equal(1, changes.Count);
  150. fixture.Child.IsOnlyOneWord = "Bar";
  151. sched.Start();
  152. Assert.Equal(2, changes.Count);
  153. // Tricky! This is a change too, because from the perspective
  154. // of the binding, we've went from "Bar" to null
  155. fixture.Child = new TestFixture();
  156. sched.Start();
  157. Assert.Equal(3, changes.Count);
  158. // Here we've set the value but it shouldn't change
  159. fixture.Child.IsOnlyOneWord = null;
  160. sched.Start();
  161. Assert.Equal(3, changes.Count);
  162. fixture.Child.IsOnlyOneWord = "Baz";
  163. sched.Start();
  164. Assert.Equal(4, changes.Count);
  165. fixture.Child.IsOnlyOneWord = "Baz";
  166. sched.Start();
  167. Assert.Equal(4, changes.Count);
  168. Assert.True(changes.All(x => x.Sender == fixture));
  169. Assert.True(changes.All(x => x.PropertyName == "Child.IsOnlyOneWord"));
  170. changes.Select(x => x.Value).AssertAreEqual(new[] {"Foo", "Bar", null, "Baz"});
  171. });
  172. }
  173. [Fact]
  174. public void OFPReplacingTheHostWithNullThenSettingItBackShouldResubscribeTheObservable()
  175. {
  176. (new TestScheduler()).With(sched => {
  177. var fixture = new HostTestFixture() {Child = new TestFixture()};
  178. var changes = fixture.ObservableForProperty(x => x.Child.IsOnlyOneWord).CreateCollection();
  179. fixture.Child.IsOnlyOneWord = "Foo";
  180. sched.Start();
  181. Assert.Equal(1, changes.Count);
  182. fixture.Child.IsOnlyOneWord = "Bar";
  183. sched.Start();
  184. Assert.Equal(2, changes.Count);
  185. // Oops, now the child is Null, we may now blow up
  186. fixture.Child = null;
  187. sched.Start();
  188. Assert.Equal(2, changes.Count);
  189. // Tricky! This is a change too, because from the perspective
  190. // of the binding, we've went from "Bar" to null
  191. fixture.Child = new TestFixture();
  192. sched.Start();
  193. Assert.Equal(3, changes.Count);
  194. Assert.True(changes.All(x => x.Sender == fixture));
  195. Assert.True(changes.All(x => x.PropertyName == "Child.IsOnlyOneWord"));
  196. changes.Select(x => x.Value).AssertAreEqual(new[] {"Foo", "Bar", null});
  197. });
  198. }
  199. [Fact]
  200. public void OFPChangingTheHostPropertyShouldFireAChildChangeNotificationOnlyIfThePreviousChildIsDifferent()
  201. {
  202. (new TestScheduler()).With(sched => {
  203. var fixture = new HostTestFixture() {Child = new TestFixture()};
  204. var changes = fixture.ObservableForProperty(x => x.Child.IsOnlyOneWord).CreateCollection();
  205. fixture.Child.IsOnlyOneWord = "Foo";
  206. sched.Start();
  207. Assert.Equal(1, changes.Count);
  208. fixture.Child.IsOnlyOneWord = "Bar";
  209. sched.Start();
  210. Assert.Equal(2, changes.Count);
  211. fixture.Child = new TestFixture() {IsOnlyOneWord = "Bar"};
  212. sched.Start();
  213. Assert.Equal(2, changes.Count);
  214. });
  215. }
  216. [Fact]
  217. public void OFPShouldWorkWithINPCObjectsToo()
  218. {
  219. (new TestScheduler()).With(sched => {
  220. var fixture = new NonReactiveINPCObject() { InpcProperty = null };
  221. var changes = fixture.ObservableForProperty(x => x.InpcProperty.IsOnlyOneWord).CreateCollection();
  222. fixture.InpcProperty = new TestFixture();
  223. sched.Start();
  224. Assert.Equal(1, changes.Count);
  225. fixture.InpcProperty.IsOnlyOneWord = "Foo";
  226. sched.Start();
  227. Assert.Equal(2, changes.Count);
  228. fixture.InpcProperty.IsOnlyOneWord = "Bar";
  229. sched.Start();
  230. Assert.Equal(3, changes.Count);
  231. fixture.InpcProperty = new TestFixture() {IsOnlyOneWord = "Bar"};
  232. sched.Start();
  233. Assert.Equal(4, changes.Count);
  234. });
  235. }
  236. [Fact]
  237. public void AnyChangeInExpressionListTriggersUpdate()
  238. {
  239. var obj = new ObjChain1();
  240. bool obsUpdated;
  241. obj.ObservableForProperty(x => x.Model.Model.Model.SomeOtherParam).Subscribe(_ => obsUpdated = true);
  242. obsUpdated = false;
  243. obj.Model.Model.Model.SomeOtherParam = 42;
  244. Assert.True(obsUpdated);
  245. obsUpdated = false;
  246. obj.Model.Model.Model = new HostTestFixture();
  247. Assert.True(obsUpdated);
  248. obsUpdated = false;
  249. obj.Model.Model = new ObjChain3() {Model = new HostTestFixture() {SomeOtherParam = 10 } } ;
  250. Assert.True(obsUpdated);
  251. obsUpdated = false;
  252. obj.Model = new ObjChain2();
  253. Assert.True(obsUpdated);
  254. }
  255. [Fact]
  256. public void SubscriptionToWhenAnyShouldReturnCurrentValue()
  257. {
  258. var obj = new HostTestFixture();
  259. int observedValue = 1;
  260. obj.WhenAny(x => x.SomeOtherParam, x => x.Value)
  261. .Subscribe(x => observedValue = x);
  262. obj.SomeOtherParam = 42;
  263. Assert.True(observedValue == obj.SomeOtherParam);
  264. }
  265. [Fact]
  266. public void MultiPropertyExpressionsShouldBeProperlyResolved()
  267. {
  268. var data = new Dictionary<Expression<Func<HostTestFixture, object>>, string[]>() {
  269. {x => x.Child.IsOnlyOneWord.Length, new[] {"Child", "IsOnlyOneWord", "Length"}},
  270. {x => x.SomeOtherParam, new[] {"SomeOtherParam"}},
  271. {x => x.Child.IsNotNullString, new[] {"Child", "IsNotNullString"}},
  272. {x => x.Child.Changed, new[] {"Child", "Changed"}},
  273. };
  274. var dataTypes = new Dictionary<Expression<Func<HostTestFixture, object>>, Type[]>() {
  275. {x => x.Child.IsOnlyOneWord.Length, new[] {typeof(TestFixture), typeof(string), typeof(int) }},
  276. {x => x.SomeOtherParam, new[] { typeof(int) }},
  277. {x => x.Child.IsNotNullString, new[] {typeof(TestFixture), typeof(string)}},
  278. {x => x.Child.Changed, new[] {typeof(TestFixture), typeof(IObservable<IObservedChange<object, object>>)}},
  279. };
  280. var results = data.Keys.Select(x => new {input = x, output = Reflection.ExpressionToPropertyNames(x)}).ToArray();
  281. var resultTypes = dataTypes.Keys.Select(x => new {input = x, output = Reflection.ExpressionToPropertyTypes(x)}).ToArray();
  282. foreach(var x in results) {
  283. data[x.input].AssertAreEqual(x.output);
  284. }
  285. foreach (var x in resultTypes) {
  286. dataTypes[x.input].AssertAreEqual(x.output);
  287. }
  288. }
  289. [Fact]
  290. public void WhenAnySmokeTest()
  291. {
  292. (new TestScheduler()).With(sched => {
  293. var fixture = new HostTestFixture() {Child = new TestFixture()};
  294. fixture.SomeOtherParam = 5;
  295. fixture.Child.IsNotNullString = "Foo";
  296. var output1 = new List<IObservedChange<HostTestFixture, int>>();
  297. var output2 = new List<IObservedChange<HostTestFixture, string>>();
  298. fixture.WhenAny(x => x.SomeOtherParam, x => x.Child.IsNotNullString, (sop, nns) => new {sop, nns}).Subscribe(x => {
  299. output1.Add(x.sop); output2.Add(x.nns);
  300. });
  301. sched.Start();
  302. Assert.Equal(1, output1.Count);
  303. Assert.Equal(1, output2.Count);
  304. Assert.Equal(fixture, output1[0].Sender);
  305. Assert.Equal(fixture, output2[0].Sender);
  306. Assert.Equal(5, output1[0].Value);
  307. Assert.Equal("Foo", output2[0].Value);
  308. fixture.SomeOtherParam = 10;
  309. sched.Start();
  310. Assert.Equal(2, output1.Count);
  311. Assert.Equal(2, output2.Count);
  312. Assert.Equal(fixture, output1[1].Sender);
  313. Assert.Equal(fixture, output2[1].Sender);
  314. Assert.Equal(10, output1[1].Value);
  315. Assert.Equal("Foo", output2[1].Value);
  316. fixture.Child.IsNotNullString = "Bar";
  317. sched.Start();
  318. Assert.Equal(3, output1.Count);
  319. Assert.Equal(3, output2.Count);
  320. Assert.Equal(fixture, output1[2].Sender);
  321. Assert.Equal(fixture, output2[2].Sender);
  322. Assert.Equal(10, output1[2].Value);
  323. Assert.Equal("Bar", output2[2].Value);
  324. });
  325. }
  326. [Fact]
  327. public void WhenAnyShouldWorkEvenWithNormalProperties()
  328. {
  329. var fixture = new TestFixture() { IsNotNullString = "Foo", IsOnlyOneWord = "Baz", PocoProperty = "Bamf" };
  330. var output = new List<IObservedChange<TestFixture, string>>();
  331. fixture.WhenAny(x => x.PocoProperty, x => x).Subscribe(output.Add);
  332. Assert.Equal(1, output.Count);
  333. Assert.Equal(fixture, output[0].Sender);
  334. Assert.Equal("PocoProperty", output[0].PropertyName);
  335. Assert.Equal("Bamf", output[0].Value);
  336. }
  337. [Fact]
  338. public void WhenAnyShouldRunInContext()
  339. {
  340. var tid = Thread.CurrentThread.ManagedThreadId;
  341. (Scheduler.TaskPool).With(sched => {
  342. int whenAnyTid = 0;
  343. var fixture = new TestFixture() { IsNotNullString = "Foo", IsOnlyOneWord = "Baz", PocoProperty = "Bamf" };
  344. fixture.WhenAny(x => x.IsNotNullString, x => x.Value).Subscribe(x => {
  345. whenAnyTid = Thread.CurrentThread.ManagedThreadId;
  346. });
  347. int timeout = 10;
  348. fixture.IsNotNullString = "Bar";
  349. while (--timeout > 0 && whenAnyTid == 0) Thread.Sleep(250);
  350. Assert.Equal(tid, whenAnyTid);
  351. });
  352. }
  353. [Fact]
  354. public void WhenAnyThroughAViewShouldntGiveNullValues()
  355. {
  356. var vm = new HostTestFixture() {
  357. Child = new TestFixture() {IsNotNullString = "Foo", IsOnlyOneWord = "Baz", PocoProperty = "Bamf"},
  358. };
  359. var fixture = new HostTestView();
  360. var output = new List<string>();
  361. fixture.WhenAny(x => x.ViewModel.Child.IsNotNullString, x => x.Value).Subscribe(x => {
  362. output.Add(x);
  363. });
  364. Assert.Equal(0, output.Count);
  365. Assert.Null(fixture.ViewModel);
  366. fixture.ViewModel = vm;
  367. Assert.Equal(1, output.Count);
  368. fixture.ViewModel.Child.IsNotNullString = "Bar";
  369. Assert.Equal(2, output.Count);
  370. new[] { "Foo", "Bar" }.AssertAreEqual(output);
  371. }
  372. }
  373. }