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);
        }
Exemple #2
0
        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);
        }
Exemple #3
0
        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);
        }
Exemple #4
0
        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);
            }
        }
Exemple #6
0
        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);
        }
Exemple #7
0
        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);
        }
Exemple #10
0
        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);
        }
Exemple #12
0
        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);
            }
        }
Exemple #14
0
        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));
        }
Exemple #15
0
        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);
        }
Exemple #18
0
        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);
        }
Exemple #19
0
        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 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 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);
        }
Exemple #22
0
        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);
        }
Exemple #24
0
        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 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 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 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);
        }
Exemple #31
0
        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);
        }
Exemple #32
0
        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 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 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 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);
        }
Exemple #38
0
        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());
            }
        }
Exemple #40
0
        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);
        }
Exemple #41
0
        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;
        }
Exemple #45
0
        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);
        }
Exemple #46
0
        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);
        }