| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494 |
- using System;
- using System.Collections.Generic;
- using System.Reactive;
- using System.Reactive.Disposables;
- using System.Reactive.Linq;
- using System.Text;
- using Avalonia.Data;
- using Xunit;
- namespace Avalonia.Base.UnitTests
- {
- public class AvaloniaObjectTests_BatchUpdate
- {
- [Fact]
- public void SetValue_Should_Not_Raise_Property_Changes_During_Batch_Update()
- {
- var target = new TestClass();
- var raised = new List<string>();
- target.GetObservable(TestClass.FooProperty).Skip(1).Subscribe(x => raised.Add(x));
- target.BeginBatchUpdate();
- target.SetValue(TestClass.FooProperty, "foo", BindingPriority.LocalValue);
- Assert.Empty(raised);
- }
- [Fact]
- public void Binding_Should_Not_Raise_Property_Changes_During_Batch_Update()
- {
- var target = new TestClass();
- var observable = new TestObservable<string>("foo");
- var raised = new List<string>();
- target.GetObservable(TestClass.FooProperty).Skip(1).Subscribe(x => raised.Add(x));
- target.BeginBatchUpdate();
- target.Bind(TestClass.FooProperty, observable, BindingPriority.LocalValue);
- Assert.Empty(raised);
- }
- [Fact]
- public void Binding_Completion_Should_Not_Raise_Property_Changes_During_Batch_Update()
- {
- var target = new TestClass();
- var observable = new TestObservable<string>("foo");
- var raised = new List<string>();
- target.Bind(TestClass.FooProperty, observable, BindingPriority.LocalValue);
- target.GetObservable(TestClass.FooProperty).Skip(1).Subscribe(x => raised.Add(x));
- target.BeginBatchUpdate();
- observable.OnCompleted();
- Assert.Empty(raised);
- }
- [Fact]
- public void SetValue_Change_Should_Be_Raised_After_Batch_Update_1()
- {
- var target = new TestClass();
- var raised = new List<AvaloniaPropertyChangedEventArgs>();
- target.PropertyChanged += (s, e) => raised.Add(e);
- target.BeginBatchUpdate();
- target.SetValue(TestClass.FooProperty, "foo", BindingPriority.LocalValue);
- target.EndBatchUpdate();
- Assert.Equal(1, raised.Count);
- Assert.Equal("foo", target.Foo);
- Assert.Null(raised[0].OldValue);
- Assert.Equal("foo", raised[0].NewValue);
- }
- [Fact]
- public void SetValue_Change_Should_Be_Raised_After_Batch_Update_2()
- {
- var target = new TestClass();
- var raised = new List<AvaloniaPropertyChangedEventArgs>();
- target.SetValue(TestClass.FooProperty, "foo", BindingPriority.LocalValue);
- target.PropertyChanged += (s, e) => raised.Add(e);
- target.BeginBatchUpdate();
- target.SetValue(TestClass.FooProperty, "bar", BindingPriority.LocalValue);
- target.SetValue(TestClass.FooProperty, "baz", BindingPriority.LocalValue);
- target.EndBatchUpdate();
- Assert.Equal(1, raised.Count);
- Assert.Equal("baz", target.Foo);
- }
- [Fact]
- public void SetValue_Changes_Should_Be_Raised_In_Correct_Order_After_Batch_Update()
- {
- var target = new TestClass();
- var raised = new List<AvaloniaPropertyChangedEventArgs>();
- target.PropertyChanged += (s, e) => raised.Add(e);
- target.BeginBatchUpdate();
- target.SetValue(TestClass.FooProperty, "foo", BindingPriority.LocalValue);
- target.SetValue(TestClass.BarProperty, "bar", BindingPriority.LocalValue);
- target.SetValue(TestClass.FooProperty, "baz", BindingPriority.LocalValue);
- target.EndBatchUpdate();
- Assert.Equal(2, raised.Count);
- Assert.Equal(TestClass.BarProperty, raised[0].Property);
- Assert.Equal(TestClass.FooProperty, raised[1].Property);
- Assert.Equal("baz", target.Foo);
- Assert.Equal("bar", target.Bar);
- }
- [Fact]
- public void SetValue_And_Binding_Changes_Should_Be_Raised_In_Correct_Order_After_Batch_Update_1()
- {
- var target = new TestClass();
- var observable = new TestObservable<string>("baz");
- var raised = new List<AvaloniaPropertyChangedEventArgs>();
- target.PropertyChanged += (s, e) => raised.Add(e);
- target.BeginBatchUpdate();
- target.SetValue(TestClass.FooProperty, "foo", BindingPriority.LocalValue);
- target.SetValue(TestClass.BarProperty, "bar", BindingPriority.LocalValue);
- target.Bind(TestClass.FooProperty, observable, BindingPriority.LocalValue);
- target.EndBatchUpdate();
- Assert.Equal(2, raised.Count);
- Assert.Equal(TestClass.BarProperty, raised[0].Property);
- Assert.Equal(TestClass.FooProperty, raised[1].Property);
- Assert.Equal("baz", target.Foo);
- Assert.Equal("bar", target.Bar);
- }
- [Fact]
- public void SetValue_And_Binding_Changes_Should_Be_Raised_In_Correct_Order_After_Batch_Update_2()
- {
- var target = new TestClass();
- var observable = new TestObservable<string>("foo");
- var raised = new List<AvaloniaPropertyChangedEventArgs>();
- target.PropertyChanged += (s, e) => raised.Add(e);
- target.BeginBatchUpdate();
- target.Bind(TestClass.FooProperty, observable, BindingPriority.LocalValue);
- target.SetValue(TestClass.BarProperty, "bar", BindingPriority.LocalValue);
- target.SetValue(TestClass.FooProperty, "baz", BindingPriority.LocalValue);
- target.EndBatchUpdate();
- Assert.Equal(2, raised.Count);
- Assert.Equal(TestClass.BarProperty, raised[0].Property);
- Assert.Equal(TestClass.FooProperty, raised[1].Property);
- Assert.Equal("baz", target.Foo);
- Assert.Equal("bar", target.Bar);
- }
- [Fact]
- public void SetValue_And_Binding_Changes_Should_Be_Raised_In_Correct_Order_After_Batch_Update_3()
- {
- var target = new TestClass();
- var observable1 = new TestObservable<string>("foo");
- var observable2 = new TestObservable<string>("qux");
- var raised = new List<AvaloniaPropertyChangedEventArgs>();
- target.PropertyChanged += (s, e) => raised.Add(e);
- target.BeginBatchUpdate();
- target.Bind(TestClass.FooProperty, observable2, BindingPriority.LocalValue);
- target.Bind(TestClass.FooProperty, observable1, BindingPriority.LocalValue);
- target.SetValue(TestClass.BarProperty, "bar", BindingPriority.LocalValue);
- target.SetValue(TestClass.FooProperty, "baz", BindingPriority.LocalValue);
- target.EndBatchUpdate();
- Assert.Equal(2, raised.Count);
- Assert.Equal(TestClass.BarProperty, raised[0].Property);
- Assert.Equal(TestClass.FooProperty, raised[1].Property);
- Assert.Equal("baz", target.Foo);
- Assert.Equal("bar", target.Bar);
- }
- [Fact]
- public void Binding_Change_Should_Be_Raised_After_Batch_Update_1()
- {
- var target = new TestClass();
- var observable = new TestObservable<string>("foo");
- var raised = new List<AvaloniaPropertyChangedEventArgs>();
- target.PropertyChanged += (s, e) => raised.Add(e);
- target.BeginBatchUpdate();
- target.Bind(TestClass.FooProperty, observable, BindingPriority.LocalValue);
- target.EndBatchUpdate();
- Assert.Equal(1, raised.Count);
- Assert.Equal("foo", target.Foo);
- Assert.Null(raised[0].OldValue);
- Assert.Equal("foo", raised[0].NewValue);
- }
- [Fact]
- public void Binding_Change_Should_Be_Raised_After_Batch_Update_2()
- {
- var target = new TestClass();
- var observable1 = new TestObservable<string>("bar");
- var observable2 = new TestObservable<string>("baz");
- var raised = new List<AvaloniaPropertyChangedEventArgs>();
- target.SetValue(TestClass.FooProperty, "foo", BindingPriority.LocalValue);
- target.PropertyChanged += (s, e) => raised.Add(e);
- target.BeginBatchUpdate();
- target.Bind(TestClass.FooProperty, observable1, BindingPriority.LocalValue);
- target.Bind(TestClass.FooProperty, observable2, BindingPriority.LocalValue);
- target.EndBatchUpdate();
- Assert.Equal(1, raised.Count);
- Assert.Equal("baz", target.Foo);
- Assert.Equal("foo", raised[0].OldValue);
- Assert.Equal("baz", raised[0].NewValue);
- }
- [Fact]
- public void Binding_Completion_Should_Be_Raised_After_Batch_Update()
- {
- var target = new TestClass();
- var observable = new TestObservable<string>("foo");
- var raised = new List<AvaloniaPropertyChangedEventArgs>();
- target.Bind(TestClass.FooProperty, observable, BindingPriority.LocalValue);
- target.PropertyChanged += (s, e) => raised.Add(e);
- target.BeginBatchUpdate();
- observable.OnCompleted();
- target.EndBatchUpdate();
- Assert.Equal(1, raised.Count);
- Assert.Null(target.Foo);
- Assert.Equal("foo", raised[0].OldValue);
- Assert.Null(raised[0].NewValue);
- Assert.Equal(BindingPriority.Unset, raised[0].Priority);
- }
- [Fact]
- public void ClearValue_Change_Should_Be_Raised_After_Batch_Update_1()
- {
- var target = new TestClass();
- var raised = new List<AvaloniaPropertyChangedEventArgs>();
- target.Foo = "foo";
- target.PropertyChanged += (s, e) => raised.Add(e);
- target.BeginBatchUpdate();
- target.ClearValue(TestClass.FooProperty);
- target.EndBatchUpdate();
- Assert.Equal(1, raised.Count);
- Assert.Null(target.Foo);
- Assert.Equal("foo", raised[0].OldValue);
- Assert.Null(raised[0].NewValue);
- Assert.Equal(BindingPriority.Unset, raised[0].Priority);
- }
- [Fact]
- public void Bindings_Should_Be_Subscribed_Before_Batch_Update()
- {
- var target = new TestClass();
- var observable1 = new TestObservable<string>("foo");
- var observable2 = new TestObservable<string>("bar");
- target.Bind(TestClass.FooProperty, observable1, BindingPriority.LocalValue);
- target.Bind(TestClass.FooProperty, observable2, BindingPriority.LocalValue);
- Assert.Equal(1, observable1.SubscribeCount);
- Assert.Equal(1, observable2.SubscribeCount);
- }
- [Fact]
- public void Non_Active_Binding_Should_Not_Be_Subscribed_Before_Batch_Update()
- {
- var target = new TestClass();
- var observable1 = new TestObservable<string>("foo");
- var observable2 = new TestObservable<string>("bar");
- target.Bind(TestClass.FooProperty, observable1, BindingPriority.LocalValue);
- target.Bind(TestClass.FooProperty, observable2, BindingPriority.Style);
- Assert.Equal(1, observable1.SubscribeCount);
- Assert.Equal(0, observable2.SubscribeCount);
- }
- [Fact]
- public void LocalValue_Bindings_Should_Be_Subscribed_During_Batch_Update()
- {
- var target = new TestClass();
- var observable1 = new TestObservable<string>("foo");
- var observable2 = new TestObservable<string>("bar");
- var raised = new List<AvaloniaPropertyChangedEventArgs>();
- target.PropertyChanged += (s, e) => raised.Add(e);
- // We need to subscribe to LocalValue bindings even if we've got a batch operation
- // in progress because otherwise we don't know whether the binding or a subsequent
- // SetValue with local priority will win. Notifications however shouldn't be sent.
- target.BeginBatchUpdate();
- target.Bind(TestClass.FooProperty, observable1, BindingPriority.LocalValue);
- target.Bind(TestClass.FooProperty, observable2, BindingPriority.LocalValue);
- Assert.Equal(1, observable1.SubscribeCount);
- Assert.Equal(1, observable2.SubscribeCount);
- Assert.Empty(raised);
- }
- [Fact]
- public void Style_Bindings_Should_Not_Be_Subscribed_During_Batch_Update()
- {
- var target = new TestClass();
- var observable1 = new TestObservable<string>("foo");
- var observable2 = new TestObservable<string>("bar");
- target.BeginBatchUpdate();
- target.Bind(TestClass.FooProperty, observable1, BindingPriority.Style);
- target.Bind(TestClass.FooProperty, observable2, BindingPriority.StyleTrigger);
- Assert.Equal(0, observable1.SubscribeCount);
- Assert.Equal(0, observable2.SubscribeCount);
- }
- [Fact]
- public void Active_Style_Binding_Should_Be_Subscribed_After_Batch_Uppdate_1()
- {
- var target = new TestClass();
- var observable1 = new TestObservable<string>("foo");
- var observable2 = new TestObservable<string>("bar");
- target.BeginBatchUpdate();
- target.Bind(TestClass.FooProperty, observable1, BindingPriority.Style);
- target.Bind(TestClass.FooProperty, observable2, BindingPriority.Style);
- target.EndBatchUpdate();
- Assert.Equal(0, observable1.SubscribeCount);
- Assert.Equal(1, observable2.SubscribeCount);
- }
- [Fact]
- public void Active_Style_Binding_Should_Be_Subscribed_After_Batch_Uppdate_2()
- {
- var target = new TestClass();
- var observable1 = new TestObservable<string>("foo");
- var observable2 = new TestObservable<string>("bar");
- target.BeginBatchUpdate();
- target.Bind(TestClass.FooProperty, observable1, BindingPriority.StyleTrigger);
- target.Bind(TestClass.FooProperty, observable2, BindingPriority.Style);
- target.EndBatchUpdate();
- Assert.Equal(1, observable1.SubscribeCount);
- Assert.Equal(0, observable2.SubscribeCount);
- }
- [Fact]
- public void Change_Can_Be_Triggered_By_Ending_Batch_Update_1()
- {
- var target = new TestClass();
- var raised = new List<AvaloniaPropertyChangedEventArgs>();
- target.PropertyChanged += (s, e) => raised.Add(e);
- target.BeginBatchUpdate();
- target.Foo = "foo";
- target.PropertyChanged += (s, e) =>
- {
- if (e.Property == TestClass.FooProperty && (string)e.NewValue == "foo")
- target.Bar = "bar";
- };
- target.EndBatchUpdate();
- Assert.Equal("foo", target.Foo);
- Assert.Equal("bar", target.Bar);
- Assert.Equal(2, raised.Count);
- Assert.Equal(TestClass.FooProperty, raised[0].Property);
- Assert.Equal(TestClass.BarProperty, raised[1].Property);
- }
- [Fact]
- public void Change_Can_Be_Triggered_By_Ending_Batch_Update_2()
- {
- var target = new TestClass();
- var raised = new List<AvaloniaPropertyChangedEventArgs>();
- target.PropertyChanged += (s, e) => raised.Add(e);
- target.BeginBatchUpdate();
- target.Foo = "foo";
- target.Bar = "baz";
- target.PropertyChanged += (s, e) =>
- {
- if (e.Property == TestClass.FooProperty && (string)e.NewValue == "foo")
- target.Bar = "bar";
- };
- target.EndBatchUpdate();
- Assert.Equal("foo", target.Foo);
- Assert.Equal("bar", target.Bar);
- Assert.Equal(2, raised.Count);
- }
- [Fact]
- public void Batch_Update_Can_Be_Triggered_By_Ending_Batch_Update()
- {
- var target = new TestClass();
- var raised = new List<AvaloniaPropertyChangedEventArgs>();
- target.PropertyChanged += (s, e) => raised.Add(e);
- target.BeginBatchUpdate();
- target.Foo = "foo";
- target.Bar = "baz";
- // Simulates the following scenario:
- // - A control is added to the logical tree
- // - A batch update is started to apply styles
- // - Ending the batch update triggers something which removes the control from the logical tree
- // - A new batch update is started to detach styles
- target.PropertyChanged += (s, e) =>
- {
- if (e.Property == TestClass.FooProperty && (string)e.NewValue == "foo")
- {
- target.BeginBatchUpdate();
- target.ClearValue(TestClass.FooProperty);
- target.ClearValue(TestClass.BarProperty);
- target.EndBatchUpdate();
- }
- };
- target.EndBatchUpdate();
- Assert.Null(target.Foo);
- Assert.Null(target.Bar);
- Assert.Equal(2, raised.Count);
- Assert.Equal(TestClass.FooProperty, raised[0].Property);
- Assert.Null(raised[0].OldValue);
- Assert.Equal("foo", raised[0].NewValue);
- Assert.Equal(TestClass.FooProperty, raised[1].Property);
- Assert.Equal("foo", raised[1].OldValue);
- Assert.Null(raised[1].NewValue);
- }
- public class TestClass : AvaloniaObject
- {
- public static readonly StyledProperty<string> FooProperty =
- AvaloniaProperty.Register<TestClass, string>(nameof(Foo));
- public static readonly StyledProperty<string> BarProperty =
- AvaloniaProperty.Register<TestClass, string>(nameof(Bar));
- public string Foo
- {
- get => GetValue(FooProperty);
- set => SetValue(FooProperty, value);
- }
- public string Bar
- {
- get => GetValue(BarProperty);
- set => SetValue(BarProperty, value);
- }
- }
- public class TestObservable<T> : ObservableBase<BindingValue<T>>
- {
- private readonly T _value;
- private IObserver<BindingValue<T>> _observer;
- public TestObservable(T value) => _value = value;
- public int SubscribeCount { get; private set; }
- public void OnCompleted() => _observer.OnCompleted();
- public void OnError(Exception e) => _observer.OnError(e);
- protected override IDisposable SubscribeCore(IObserver<BindingValue<T>> observer)
- {
- ++SubscribeCount;
- _observer = observer;
- observer.OnNext(_value);
- return Disposable.Empty;
- }
- }
- }
- }
|