public void Should_Set_Node_Target_To_Null_On_Unsubscribe() { var target = new ExpressionObserver(new { Foo = "foo" }, "Foo"); var result = new List<object>(); using (target.Subscribe(x => result.Add(x))) using (target.Subscribe(_ => { })) { Assert.NotNull(target.Node.Target); } Assert.Equal(new[] { "foo" }, result); Assert.Null(target.Node.Target); }
public void Should_Set_Node_Target_To_Null_On_Unsubscribe() { var target = new ExpressionObserver(new { Foo = "foo" }, "Foo"); var result = new List <object>(); using (target.Subscribe(x => result.Add(x))) using (target.Subscribe(_ => { })) { Assert.NotNull(target.Node.Target); } Assert.Equal(new[] { "foo" }, result); Assert.Null(target.Node.Target); }
public void Should_Track_Property_Chain_Changing() { var data = new Class1 { Next = new Class2 { Bar = "bar" } }; var target = new ExpressionObserver(data, "Next.Bar"); var result = new List <object>(); var sub = target.Subscribe(x => result.Add(x)); var old = data.Next; data.Next = new Class2 { Bar = "baz" }; data.Next = new Class2 { Bar = null }; Assert.Equal(new[] { "bar", "baz", null }, result); sub.Dispose(); Assert.Equal(0, data.PropertyChangedSubscriptionCount); Assert.Equal(0, data.Next.PropertyChangedSubscriptionCount); Assert.Equal(0, old.PropertyChangedSubscriptionCount); GC.KeepAlive(data); }
public void Can_Replace_Root() { var first = new Class1 { Foo = "foo" }; var second = new Class1 { Foo = "bar" }; var root = first; var update = new Subject <Unit>(); var target = new ExpressionObserver(() => root, "Foo", update); var result = new List <object>(); var sub = target.Subscribe(x => result.Add(x)); root = second; update.OnNext(Unit.Default); root = null; update.OnNext(Unit.Default); Assert.Equal( new object[] { "foo", "bar", AvaloniaProperty.UnsetValue, }, result); Assert.Equal(0, first.PropertyChangedSubscriptionCount); Assert.Equal(0, second.PropertyChangedSubscriptionCount); GC.KeepAlive(first); GC.KeepAlive(second); }
public void Should_Return_BindingNotification_If_Stream_Operator_Applied_To_Not_Supported_Type() { using (var sync = UnitTestSynchronizationContext.Begin()) { var data = new Class2("foo"); var target = new ExpressionObserver(data, "Foo^", true); var result = new List <object>(); var sub = target.Subscribe(x => result.Add(x)); sync.ExecutePostedCallbacks(); Assert.Equal( new[] { new BindingNotification( new MarkupBindingChainException("Stream operator applied to unsupported type", "Foo^", "Foo^"), BindingErrorType.Error) }, result); sub.Dispose(); GC.KeepAlive(data); } }
public void Should_Trigger_PropertyChanged_On_Null_Or_Empty_String() { var data = new Class1 { Bar = "foo" }; var target = new ExpressionObserver(data, "Bar"); var result = new List <object>(); var sub = target.Subscribe(x => result.Add(x)); Assert.Equal(new[] { "foo" }, result); data.Bar = "bar"; Assert.Equal(new[] { "foo" }, result); data.RaisePropertyChanged(string.Empty); Assert.Equal(new[] { "foo", "bar" }, result); data.RaisePropertyChanged(null); Assert.Equal(new[] { "foo", "bar", "bar" }, result); sub.Dispose(); Assert.Equal(0, data.PropertyChangedSubscriptionCount); GC.KeepAlive(data); }
public void Should_Track_Property_Chain_Breaking_With_Object_Then_Mending() { var data = new Class1 { Next = new Class2 { Bar = "bar" } }; var target = new ExpressionObserver(data, "Next.Bar"); var result = new List <object>(); var sub = target.Subscribe(x => result.Add(x)); var old = data.Next; var breaking = new WithoutBar(); data.Next = breaking; data.Next = new Class2 { Bar = "baz" }; Assert.Equal(3, result.Count); Assert.Equal("bar", result[0]); Assert.IsType <BindingError>(result[1]); Assert.Equal("baz", result[2]); sub.Dispose(); Assert.Equal(0, data.SubscriptionCount); Assert.Equal(0, data.Next.SubscriptionCount); Assert.Equal(0, breaking.SubscriptionCount); Assert.Equal(0, old.SubscriptionCount); }
public void Should_Unsubscribe_From_Update_Observable() { var scheduler = new TestScheduler(); var update = scheduler.CreateColdObservable<Unit>(); var target = new ExpressionObserver(() => new { Foo = "foo" }, "Foo", update); var result = new List<object>(); using (target.Subscribe(x => result.Add(x))) using (target.Subscribe(_ => { })) { scheduler.Start(); } Assert.Equal(new[] { "foo" }, result); Assert.All(update.Subscriptions, x => Assert.NotEqual(Subscription.Infinite, x.Unsubscribe)); }
public void Should_Track_Property_Chain_Breaking_With_Object_Then_Mending() { var data = new Class1 { Next = new Class2 { Bar = "bar" } }; var target = new ExpressionObserver(data, "Next.Bar"); var result = new List <object>(); var sub = target.Subscribe(x => result.Add(x)); var old = data.Next; var breaking = new WithoutBar(); data.Next = breaking; data.Next = new Class2 { Bar = "baz" }; Assert.Equal(new[] { "bar", PerspexProperty.UnsetValue, "baz" }, result); sub.Dispose(); Assert.Equal(0, data.SubscriptionCount); Assert.Equal(0, data.Next.SubscriptionCount); Assert.Equal(0, breaking.SubscriptionCount); Assert.Equal(0, old.SubscriptionCount); }
public void Should_Unsubscribe_From_Update_Observable() { var scheduler = new TestScheduler(); var update = scheduler.CreateColdObservable <Unit>(); var target = new ExpressionObserver(() => new { Foo = "foo" }, "Foo", update); var result = new List <object>(); using (target.Subscribe(x => result.Add(x))) using (target.Subscribe(_ => { })) { scheduler.Start(); } Assert.Equal(new[] { "foo" }, result); Assert.All(update.Subscriptions, x => Assert.NotEqual(Subscription.Infinite, x.Unsubscribe)); }
public void Validation_Plugins_Send_Correct_Notifications() { var data = new IndeiTest(); var observer = new ExpressionObserver(data, nameof(data.MustBePositive), true); var result = new List<object>(); observer.Subscribe(x => result.Add(x)); observer.SetValue(5); observer.SetValue(-5); observer.SetValue("foo"); observer.SetValue(5); Assert.Equal(new[] { new BindingNotification(0), // Value is notified twice as ErrorsChanged is always called by IndeiTest. new BindingNotification(5), new BindingNotification(5), // Value is first signalled without an error as validation hasn't been updated. new BindingNotification(-5), new BindingNotification(new Exception("Must be positive"), BindingErrorType.DataValidationError, -5), // Exception is thrown by trying to set value to "foo". new BindingNotification( new ArgumentException("Object of type 'System.String' cannot be converted to type 'System.Int32'."), BindingErrorType.DataValidationError), // Value is set then validation is updated. new BindingNotification(new Exception("Must be positive"), BindingErrorType.DataValidationError, 5), new BindingNotification(5), }, result); }
public void Validation_Plugins_Send_Correct_Notifications() { var data = new IndeiTest(); var observer = new ExpressionObserver(data, nameof(data.MustBePositive), true); var result = new List <object>(); observer.Subscribe(x => result.Add(x)); observer.SetValue(5); observer.SetValue(-5); observer.SetValue("foo"); observer.SetValue(5); Assert.Equal(new[] { new BindingNotification(0), // Value is notified twice as ErrorsChanged is always called by IndeiTest. new BindingNotification(5), new BindingNotification(5), // Value is first signalled without an error as validation hasn't been updated. new BindingNotification(-5), new BindingNotification(new Exception("Must be positive"), BindingErrorType.DataValidationError, -5), // Exception is thrown by trying to set value to "foo". new BindingNotification( new ArgumentException("Object of type 'System.String' cannot be converted to type 'System.Int32'."), BindingErrorType.DataValidationError), // Value is set then validation is updated. new BindingNotification(new Exception("Must be positive"), BindingErrorType.DataValidationError, 5), new BindingNotification(5), }, result); }
public void Should_Return_BindingNotification_Error_On_Task_Exception() { using (var sync = UnitTestSynchronizationContext.Begin()) { var tcs = new TaskCompletionSource <string>(); var data = new { Foo = tcs.Task }; var target = new ExpressionObserver(data, "Foo^"); var result = new List <object>(); var sub = target.Subscribe(x => result.Add(x)); tcs.SetException(new NotSupportedException()); sync.ExecutePostedCallbacks(); Assert.Equal( new[] { new BindingNotification( new AggregateException(new NotSupportedException()), BindingErrorType.Error) }, result); GC.KeepAlive(data); } }
public void Should_Unsubscribe_From_Source_Observable() { var scheduler = new TestScheduler(); var source = scheduler.CreateColdObservable( OnNext(1, new { Foo = "foo" })); var target = new ExpressionObserver(source, "Foo"); var result = new List <object>(); using (target.Subscribe(x => result.Add(x))) using (target.Subscribe(_ => { })) { scheduler.Start(); } Assert.Equal(new[] { AvaloniaProperty.UnsetValue, "foo" }, result); Assert.All(source.Subscriptions, x => Assert.NotEqual(Subscription.Infinite, x.Unsubscribe)); }
public void Subscribing_Multiple_Times_Should_Only_Add_PropertyChanged_Handlers_Once() { var data = new Class1 { Foo = "foo" }; var target = new ExpressionObserver(data, "Foo"); var sub1 = target.Subscribe(x => { }); var sub2 = target.Subscribe(x => { }); Assert.Equal(1, data.PropertyChangedSubscriptionCount); sub1.Dispose(); sub2.Dispose(); Assert.Equal(0, data.PropertyChangedSubscriptionCount); }
public void Should_Unsubscribe_From_Source_Observable() { var scheduler = new TestScheduler(); var source = scheduler.CreateColdObservable( OnNext(1, new { Foo = "foo" })); var target = new ExpressionObserver(source, "Foo"); var result = new List<object>(); using (target.Subscribe(x => result.Add(x))) using (target.Subscribe(_ => { })) { scheduler.Start(); } Assert.Equal(new[] { AvaloniaProperty.UnsetValue, "foo" }, result); Assert.All(source.Subscriptions, x => Assert.NotEqual(Subscription.Infinite, x.Unsubscribe)); }
public void Indei_Validation_Does_Not_Subscribe_When_DataValidatation_Not_Enabled() { var data = new IndeiTest { MustBePositive = 5 }; var observer = new ExpressionObserver(data, nameof(data.MustBePositive), false); observer.Subscribe(_ => { }); Assert.Equal(0, data.ErrorsChangedSubscriptionCount); }
public void Should_Get_Simple_Property_Value_Type() { var data = new { Foo = "foo" }; var target = new ExpressionObserver(data, "Foo"); target.Subscribe(_ => { }); Assert.Equal(typeof(string), target.ResultType); }
public void Should_Get_Simple_Property_Chain_Type() { var data = new { Foo = new { Bar = new { Baz = "baz" } } }; var target = new ExpressionObserver(data, "Foo.Bar.Baz"); target.Subscribe(_ => { }); Assert.Equal(typeof(string), target.ResultType); }
public void Disabled_Indei_Validation_Does_Not_Subscribe() { var data = new IndeiTest { MustBePositive = 5 }; var observer = new ExpressionObserver(data, nameof(data.MustBePositive), false); observer.Subscribe(_ => { }); Assert.Equal(0, data.SubscriptionCount); }
public void SetValue_Should_Return_False_For_Missing_Object() { var data = new Class1(); var target = new ExpressionObserver(data, "Next.Bar"); using (target.Subscribe(_ => { })) { Assert.False(target.SetValue("baz")); } }
public void Enabled_Indei_Validation_Subscribes() { var data = new IndeiTest { MustBePositive = 5 }; var observer = new ExpressionObserver(data, nameof(data.MustBePositive), true); var sub = observer.Subscribe(_ => { }); Assert.Equal(1, data.ErrorsChangedSubscriptionCount); sub.Dispose(); Assert.Equal(0, data.ErrorsChangedSubscriptionCount); }
public void SetValue_Should_Notify_New_Value_Without_Inpc() { var data = new Class1(); var target = new ExpressionObserver(data, "Bar"); var result = new List <object>(); target.Subscribe(x => result.Add(x)); target.SetValue("bar"); Assert.Equal(new[] { null, "bar" }, result); }
public void Should_Complete_When_Source_Observable_Errors() { var source = new BehaviorSubject<object>(1); var target = new ExpressionObserver(source, "Foo"); var completed = false; target.Subscribe(_ => { }, () => completed = true); source.OnError(new Exception()); Assert.True(completed); }
public void Should_Complete_When_Update_Observable_Errors() { var update = new Subject<Unit>(); var target = new ExpressionObserver(() => 1, "Foo", update); var completed = false; target.Subscribe(_ => { }, () => completed = true); update.OnError(new Exception()); Assert.True(completed); }
public void SetValue_Should_Return_False_For_Invalid_Value() { var data = new { Foo = "foo" }; var target = new ExpressionObserver(data, "!Foo"); target.Subscribe(_ => { }); Assert.False(target.SetValue("bar")); GC.KeepAlive(data); }
public void Should_Track_INCC_Add() { var data = new { Foo = new ObservableCollection<string> { "foo", "bar" } }; var target = new ExpressionObserver(data, "Foo[2]"); var result = new List<object>(); var sub = target.Subscribe(x => result.Add(x)); data.Foo.Add("baz"); Assert.Equal(new[] { PerspexProperty.UnsetValue, "baz" }, result); }
public void Should_Complete_When_Update_Observable_Completes() { var update = new Subject <Unit>(); var target = new ExpressionObserver(() => 1, "Foo", update); var completed = false; target.Subscribe(_ => { }, () => completed = true); update.OnCompleted(); Assert.True(completed); }
public void Should_Complete_When_Source_Observable_Completes() { var source = new BehaviorSubject <object>(1); var target = new ExpressionObserver(source, "Foo"); var completed = false; target.Subscribe(_ => { }, () => completed = true); source.OnCompleted(); Assert.True(completed); }
public void Should_SetArrayIndex() { var data = new { Foo = new[] { "foo", "bar" } }; var target = new ExpressionObserver(data, "Foo[1]"); using (target.Subscribe(_ => { })) { Assert.True(target.SetValue("baz")); } Assert.Equal("baz", data.Foo[1]); }
public void Should_Set_Value_On_Simple_Property_Chain() { var data = new Class1 { Foo = new Class2 { Bar = "bar" } }; var target = new ExpressionObserver(data, "Foo.Bar"); using (target.Subscribe(_ => { })) { target.SetValue("foo"); } Assert.Equal("foo", data.Foo.Bar); }
public void Should_Set_Simple_Property_Value() { var data = new { Foo = "foo" }; var target = new ExpressionObserver(data, "Foo"); using (target.Subscribe(_ => { })) { target.SetValue("bar"); } Assert.Equal("foo", data.Foo); }
public void Subscribing_Multiple_Times_Should_Return_Values_To_All() { var data = new Class1 { Foo = "foo" }; var target = new ExpressionObserver(data, "Foo"); var result1 = new List <object>(); var result2 = new List <object>(); var result3 = new List <object>(); target.Subscribe(x => result1.Add(x)); target.Subscribe(x => result2.Add(x)); data.Foo = "bar"; target.Subscribe(x => result3.Add(x)); Assert.Equal(new[] { "foo", "bar" }, result1); Assert.Equal(new[] { "foo", "bar" }, result2); Assert.Equal(new[] { "bar" }, result3); }
public void Should_Get_Completed_Task_Value() { using (var sync = UnitTestSynchronizationContext.Begin()) { var data = new { Foo = Task.FromResult("foo") }; var target = new ExpressionObserver(data, "Foo"); var result = new List<object>(); var sub = target.Subscribe(x => result.Add(x)); Assert.Equal(new object[] { "foo" }, result.ToArray()); } }
public object Select(object o) { if (string.IsNullOrEmpty(MemberName)) { return(o); } var expression = new ExpressionObserver(o, MemberName); object result = AvaloniaProperty.UnsetValue; expression.Subscribe(x => result = x); return((result == AvaloniaProperty.UnsetValue || result is BindingNotification) ? null : result); }
public void Should_Get_Completed_Task_Value() { using (var sync = UnitTestSynchronizationContext.Begin()) { var data = new { Foo = Task.FromResult("foo") }; var target = new ExpressionObserver(data, "Foo"); var result = new List <object>(); var sub = target.Subscribe(x => result.Add(x)); Assert.Equal(new object[] { "foo" }, result.ToArray()); } }
public void Should_Track_Simple_Property_Value() { var data = new Class1(); var target = new ExpressionObserver(data, "Foo"); var result = new List<object>(); var sub = target.Subscribe(x => result.Add(x)); data.SetValue(Class1.FooProperty, "bar"); Assert.Equal(new[] { "foo", "bar" }, result); sub.Dispose(); }
public void Can_SetValue_For_Valid_Value() { var data = new Test { Foo = true }; var target = new ExpressionObserver(data, "!Foo"); target.Subscribe(_ => { }); Assert.True(target.SetValue(true)); Assert.False(data.Foo); }
public object Select(object o) { if (string.IsNullOrEmpty(MemberName)) { return o; } var expression = new ExpressionObserver(o, MemberName); object result = AvaloniaProperty.UnsetValue; expression.Subscribe(x => result = x); return (result == AvaloniaProperty.UnsetValue || result is BindingNotification) ? null : result; }
public void SetValue_Should_Return_False_For_Missing_Property() { var data = new Class1 { Next = new WithoutBar() }; var target = new ExpressionObserver(data, "Next.Bar"); using (target.Subscribe(_ => { })) { Assert.False(target.SetValue("baz")); } GC.KeepAlive(data); }
public void Should_Track_Simple_Property_Value() { var data = new Class1(); var target = new ExpressionObserver(data, "Foo"); var result = new List <object>(); var sub = target.Subscribe(x => result.Add(x)); data.SetValue(Class1.FooProperty, "bar"); Assert.Equal(new[] { "foo", "bar" }, result); sub.Dispose(); }
public void Should_Track_Simple_Attached_Value() { var data = new Class1(); var target = new ExpressionObserver(data, "(Owner.Foo)"); var result = new List<object>(); var sub = target.Subscribe(x => result.Add(x)); data.SetValue(Owner.FooProperty, "bar"); Assert.Equal(new[] { "foo", "bar" }, result); sub.Dispose(); Assert.Null(((IAvaloniaObjectDebug)data).GetPropertyChangedSubscribers()); }
public void Should_Get_Property_Value_From_Task() { using (var sync = UnitTestSynchronizationContext.Begin()) { var tcs = new TaskCompletionSource<Class2>(); var data = new Class1(tcs.Task); var target = new ExpressionObserver(data, "Next.Foo"); var result = new List<object>(); var sub = target.Subscribe(x => result.Add(x)); tcs.SetResult(new Class2("foo")); sync.ExecutePostedCallbacks(); Assert.Equal(new object[] { PerspexProperty.UnsetValue, "foo" }, result.ToArray()); } }
public void Should_Not_Get_Observable_Value_Without_Modifier_Char() { using (var sync = UnitTestSynchronizationContext.Begin()) { var source = new BehaviorSubject<string>("foo"); var data = new { Foo = source }; var target = new ExpressionObserver(data, "Foo"); var result = new List<object>(); var sub = target.Subscribe(x => result.Add(x)); source.OnNext("bar"); sync.ExecutePostedCallbacks(); Assert.Equal(new[] { source }, result); } }
public void Should_Not_Keep_Source_Alive_ObservableCollection_With_DataValidation() { Func<ExpressionObserver> run = () => { var source = new { Foo = new AvaloniaList<string> { "foo", "bar" } }; var target = new ExpressionObserver(source, "Foo", true); target.Subscribe(_ => { }); return target; }; var result = run(); dotMemory.Check(memory => Assert.Equal(0, memory.GetObjects(where => where.Type.Is<AvaloniaList<string>>()).ObjectsCount)); }
public void Should_Get_Simple_Observable_Value() { using (var sync = UnitTestSynchronizationContext.Begin()) { var source = new BehaviorSubject<string>("foo"); var data = new { Foo = source }; var target = new ExpressionObserver(data, "Foo"); var result = new List<object>(); var sub = target.Subscribe(x => result.Add(x)); source.OnNext("bar"); sync.ExecutePostedCallbacks(); Assert.Equal(new[] { PerspexProperty.UnsetValue, "foo", "bar" }, result); } }
public void Should_Get_Simple_Task_Value() { using (var sync = UnitTestSynchronizationContext.Begin()) { var tcs = new TaskCompletionSource<string>(); var data = new { Foo = tcs.Task }; var target = new ExpressionObserver(data, "Foo"); var result = new List<object>(); var sub = target.Subscribe(x => result.Add(x)); tcs.SetResult("foo"); sync.ExecutePostedCallbacks(); Assert.Equal(new object[] { AvaloniaProperty.UnsetValue, "foo" }, result.ToArray()); } }
public void Should_Track_End_Of_Property_Chain_Changing() { var data = new Class1 { Next = new Class2 { Bar = "bar" } }; var target = new ExpressionObserver(data, "Next.Bar"); var result = new List<object>(); var sub = target.Subscribe(x => result.Add(x)); ((Class2)data.Next).Bar = "baz"; Assert.Equal(new[] { "bar", "baz" }, result); sub.Dispose(); Assert.Equal(0, data.SubscriptionCount); Assert.Equal(0, data.Next.SubscriptionCount); }
public void Should_Not_Keep_Source_Alive_NonIntegerIndexer() { Func<ExpressionObserver> run = () => { var source = new { Foo = new NonIntegerIndexer() }; var target = new ExpressionObserver(source, "Foo"); target.Subscribe(_ => { }); return target; }; var result = run(); dotMemory.Check(memory => Assert.Equal(0, memory.GetObjects(where => where.Type.Is<NonIntegerIndexer>()).ObjectsCount)); }
public void Should_Not_Get_Task_Result_Without_Modifier_Char() { using (var sync = UnitTestSynchronizationContext.Begin()) { var tcs = new TaskCompletionSource<string>(); var data = new { Foo = tcs.Task }; var target = new ExpressionObserver(data, "Foo"); var result = new List<object>(); var sub = target.Subscribe(x => result.Add(x)); tcs.SetResult("foo"); sync.ExecutePostedCallbacks(); Assert.Equal(1, result.Count); Assert.IsType<Task<string>>(result[0]); } }
public void Should_Get_Simple_Observable_Value_With_DataValidation_Enabled() { using (var sync = UnitTestSynchronizationContext.Begin()) { var source = new BehaviorSubject<string>("foo"); var data = new { Foo = source }; var target = new ExpressionObserver(data, "Foo^", true); var result = new List<object>(); var sub = target.Subscribe(x => result.Add(x)); source.OnNext("bar"); sync.ExecutePostedCallbacks(); // What does it mean to have data validation on an observable? Without a use-case // it's hard to know what to do here so for the moment the value is returned. Assert.Equal(new[] { "foo", "bar" }, result); } }
public void Should_Get_Property_Value_From_Observable_With_DataValidation_Enabled() { using (var sync = UnitTestSynchronizationContext.Begin()) { var data = new Class1(); var target = new ExpressionObserver(data, "Next.Foo", true); var result = new List<object>(); var sub = target.Subscribe(x => result.Add(x)); data.Next.OnNext(new Class2("foo")); sync.ExecutePostedCallbacks(); Assert.Equal(new[] { new BindingNotification("foo") }, result); sub.Dispose(); Assert.Equal(0, data.PropertyChangedSubscriptionCount); } }
public void Should_Get_Property_Value_From_Observable() { using (var sync = UnitTestSynchronizationContext.Begin()) { var data = new Class1(); var target = new ExpressionObserver(data, "Next.Foo"); var result = new List<object>(); var sub = target.Subscribe(x => result.Add(x)); data.Next.OnNext(new Class2("foo")); sync.ExecutePostedCallbacks(); Assert.Equal(new[] { PerspexProperty.UnsetValue, "foo" }, result); sub.Dispose(); Assert.Equal(0, data.SubscriptionCount); } }
public void Should_Return_BindingNotification_Error_On_Task_Exception() { using (var sync = UnitTestSynchronizationContext.Begin()) { var tcs = new TaskCompletionSource<string>(); var data = new { Foo = tcs.Task }; var target = new ExpressionObserver(data, "Foo^"); var result = new List<object>(); var sub = target.Subscribe(x => result.Add(x)); tcs.SetException(new NotSupportedException()); sync.ExecutePostedCallbacks(); Assert.Equal( new[] { new BindingNotification( new AggregateException(new NotSupportedException()), BindingErrorType.Error) }, result); } }
public void Can_Replace_Root() { var first = new Class1 { Foo = "foo" }; var second = new Class1 { Foo = "bar" }; var root = first; var update = new Subject<Unit>(); var target = new ExpressionObserver(() => root, "Foo", update); var result = new List<object>(); var sub = target.Subscribe(x => result.Add(x)); root = second; update.OnNext(Unit.Default); root = null; update.OnNext(Unit.Default); Assert.Equal(new[] { "foo", "bar", PerspexProperty.UnsetValue }, result); Assert.Equal(0, first.SubscriptionCount); Assert.Equal(0, second.SubscriptionCount); }