public void CreateAndDisposeExplicitSetting(ReferenceHandling referenceHandling)
            {
                var source = new WithSimpleProperties {
                    Value1 = 1, Time = DateTime.MinValue
                };
                var propertyChanges         = new List <string>();
                var expectedPropertyChanges = new List <string>();
                var changes = new List <EventArgs>();

                using (var tracker = Track.Changes(source, referenceHandling))
                {
                    tracker.PropertyChanged += (_, e) => propertyChanges.Add(e.PropertyName);
                    tracker.Changed         += (_, e) => changes.Add(e);
                    CollectionAssert.IsEmpty(propertyChanges);
                    CollectionAssert.IsEmpty(changes);

                    source.Value1++;
                    Assert.AreEqual(1, tracker.Changes);
                    expectedPropertyChanges.AddRange(new[] { "Changes" });
                    CollectionAssert.AreEqual(expectedPropertyChanges, propertyChanges);
                    var expected = new[] { RootChangeEventArgs.Create(ChangeTrackerNode.GetOrCreate(source, tracker.Settings, isRoot: false).Value, new PropertyChangeEventArgs(source, source.GetProperty(nameof(source.Value1)))) };
                    CollectionAssert.AreEqual(expected, changes, EventArgsComparer.Default);
                }

                source.Value1++;
                CollectionAssert.AreEqual(expectedPropertyChanges, propertyChanges);

#if !DEBUG // debug build keeps instances alive longer for nicer debugging experience
                var wrx = new System.WeakReference(source);
                source = null;
                System.GC.Collect();
                Assert.AreEqual(false, wrx.IsAlive);
#endif
            }
            public void Remove()
            {
                var source = new With <ObservableCollection <ComplexType> > {
                    Value = new ObservableCollection <ComplexType> {
                        new ComplexType(), null
                    }
                };
                var propertyChanges = new List <string>();
                var changes         = new List <EventArgs>();
                var expectedChanges = new List <EventArgs>();

                using (var tracker = Track.Changes(source, ReferenceHandling.Structural))
                {
                    var sourceNode = ChangeTrackerNode.GetOrCreate(source, tracker.Settings, false)
                                     .Value;
                    tracker.PropertyChanged += (_, e) => propertyChanges.Add(e.PropertyName);
                    tracker.Changed         += (_, e) => changes.Add(e);
                    Assert.AreEqual(0, tracker.Changes);
                    CollectionAssert.IsEmpty(propertyChanges);
                    CollectionAssert.IsEmpty(changes);

                    source.Value.RemoveAt(1);
                    Assert.AreEqual(1, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes" }, propertyChanges);
                    expectedChanges.Add(
                        new PropertyGraphChangedEventArgs <ChangeTrackerNode>(
                            sourceNode,
                            source.GetProperty("Value"),
                            RootChangeEventArgs.Create(
                                ChangeTrackerNode.GetOrCreate(
                                    (INotifyCollectionChanged)source.Value,
                                    tracker.Settings,
                                    false)
                                .Value,
                                new RemoveEventArgs(source.Value, 1))));
                    CollectionAssert.AreEqual(expectedChanges, changes, EventArgsComparer.Default);

                    var complexType = source.Value[0];
                    source.Value.RemoveAt(0);
                    Assert.AreEqual(2, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes", "Changes" }, propertyChanges);
                    expectedChanges.Add(
                        new PropertyGraphChangedEventArgs <ChangeTrackerNode>(
                            sourceNode,
                            source.GetProperty("Value"),
                            RootChangeEventArgs.Create(
                                ChangeTrackerNode.GetOrCreate(
                                    (INotifyCollectionChanged)source.Value,
                                    tracker.Settings,
                                    false)
                                .Value,
                                new RemoveEventArgs(source.Value, 0))));
                    CollectionAssert.AreEqual(expectedChanges, changes, EventArgsComparer.Default);

                    complexType.Value++;
                    Assert.AreEqual(2, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes", "Changes" }, propertyChanges);
                    CollectionAssert.AreEqual(expectedChanges, changes, EventArgsComparer.Default);
                }
            }
Esempio n. 3
0
            public void Remove(ReferenceHandling referenceHandling)
            {
                var source = new ObservableCollection <int> {
                    1, 2
                };
                var propertyChanges = new List <string>();
                var changes         = new List <EventArgs>();

                using (var tracker = Track.Changes(source, referenceHandling))
                {
                    tracker.PropertyChanged += (_, e) => propertyChanges.Add(e.PropertyName);
                    tracker.Changed         += (_, e) => changes.Add(e);
                    Assert.AreEqual(0, tracker.Changes);
                    CollectionAssert.IsEmpty(propertyChanges);
                    CollectionAssert.IsEmpty(changes);

                    source.RemoveAt(1);
                    Assert.AreEqual(1, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes" }, propertyChanges);
                    var node     = ChangeTrackerNode.GetOrCreate((INotifyCollectionChanged)source, tracker.Settings, isRoot: false).Value;
                    var expected = new[] { RootChangeEventArgs.Create(node, new RemoveEventArgs(source, 1)) };
                    CollectionAssert.AreEqual(expected, changes, EventArgsComparer.Default);

                    source.RemoveAt(0);
                    Assert.AreEqual(2, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes", "Changes" }, propertyChanges);
                    expected = new[]
                    {
                        RootChangeEventArgs.Create(node, new RemoveEventArgs(source, 1)),
                        RootChangeEventArgs.Create(node, new RemoveEventArgs(source, 0))
                    };
                    CollectionAssert.AreEqual(expected, changes, EventArgsComparer.Default);
                }
            }
Esempio n. 4
0
            public void StopsSubscribing()
            {
                var source = new With <ComplexType> {
                    Value = new ComplexType()
                };
                var propertyChanges = new List <string>();
                var changes         = new List <EventArgs>();

                using (var tracker = Track.Changes(source, ReferenceHandling.Structural))
                {
                    tracker.PropertyChanged += (_, e) => propertyChanges.Add(e.PropertyName);
                    tracker.Changed         += (_, e) => changes.Add(e);
                    Assert.AreEqual(0, tracker.Changes);
                    CollectionAssert.IsEmpty(propertyChanges);
                    CollectionAssert.IsEmpty(changes);

                    var oldValue = source.Value;
                    source.Value = null;
                    Assert.AreEqual(1, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes" }, propertyChanges);
                    var expected = new[] { RootChangeEventArgs.Create(ChangeTrackerNode.GetOrCreate(source, tracker.Settings, false).Value, new PropertyChangeEventArgs(source, source.GetProperty(nameof(source.Value)))) };
                    CollectionAssert.AreEqual(expected, changes, EventArgsComparer.Default);

                    oldValue.Value++;
                    Assert.AreEqual(1, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes" }, propertyChanges);
                    CollectionAssert.AreEqual(expected, changes, EventArgsComparer.Default);
                }
            }
            public void StartsSubscribingOneLevel()
            {
                var source          = new Level();
                var propertyChanges = new List <string>();
                var changes         = new List <EventArgs>();
                var expectedChanges = new List <EventArgs>();

                using (var tracker = Track.Changes(source, ReferenceHandling.Structural))
                {
                    tracker.PropertyChanged += (_, e) => propertyChanges.Add(e.PropertyName);
                    tracker.Changed         += (_, e) => changes.Add(e);
                    Assert.AreEqual(0, tracker.Changes);
                    CollectionAssert.IsEmpty(propertyChanges);
                    CollectionAssert.IsEmpty(changes);

                    source.Next = new Level();
                    Assert.AreEqual(1, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes" }, propertyChanges);
                    var sourceNode = ChangeTrackerNode.GetOrCreate(source, tracker.Settings, isRoot: false).Value;
                    expectedChanges.Add(RootChangeEventArgs.Create(ChangeTrackerNode.GetOrCreate(source, tracker.Settings, isRoot: false).Value, new PropertyChangeEventArgs(source, source.GetProperty("Next"))));
                    CollectionAssert.AreEqual(expectedChanges, changes, EventArgsComparer.Default);

                    source.Next.Value++;
                    Assert.AreEqual(2, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes", "Changes" }, propertyChanges);
                    expectedChanges.Add(new PropertyGraphChangedEventArgs <ChangeTrackerNode>(sourceNode, source.GetProperty("Next"), RootChangeEventArgs.Create(ChangeTrackerNode.GetOrCreate(source.Next, tracker.Settings, isRoot: false).Value, new PropertyChangeEventArgs(source.Next, source.Next.GetProperty("Value")))));
                    CollectionAssert.AreEqual(expectedChanges, changes, EventArgsComparer.Default);
                }
            }
            public void Add()
            {
                var source          = new ObservableCollection <ComplexType>();
                var propertyChanges = new List <string>();
                var changes         = new List <EventArgs>();

                using (var tracker = Track.Changes(source, ReferenceHandling.Structural))
                {
                    tracker.PropertyChanged += (_, e) => propertyChanges.Add(e.PropertyName);
                    tracker.Changed         += (_, e) => changes.Add(e);
                    Assert.AreEqual(0, tracker.Changes);
                    CollectionAssert.IsEmpty(propertyChanges);
                    CollectionAssert.IsEmpty(changes);

                    source.Add(new ComplexType());
                    Assert.AreEqual(1, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes" }, propertyChanges);
                    var node     = ChangeTrackerNode.GetOrCreate((INotifyCollectionChanged)source, tracker.Settings, false).Value;
                    var expected = new[] { RootChangeEventArgs.Create(node, new AddEventArgs(source, 0)) };
                    CollectionAssert.AreEqual(expected, changes, EventArgsComparer.Default);

                    source.Add(new ComplexType());
                    Assert.AreEqual(2, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes", "Changes" }, propertyChanges);
                    expected = new[]
                    {
                        RootChangeEventArgs.Create(node, new AddEventArgs(source, 0)),
                        RootChangeEventArgs.Create(node, new AddEventArgs(source, 1))
                    };
                    CollectionAssert.AreEqual(expected, changes, EventArgsComparer.Default);
                }
            }
            public void IgnoresBaseClassPropertyLambda()
            {
                var source   = new DerivedClass();
                var settings = PropertiesSettings.Build()
                               .IgnoreProperty <ComplexType>(x => x.Excluded)
                               .CreateSettings(ReferenceHandling.Structural);

                var propertyChanges = new List <string>();
                var changes         = new List <EventArgs>();

                using (var tracker = Track.Changes(source, settings))
                {
                    tracker.PropertyChanged += (_, e) => propertyChanges.Add(e.PropertyName);
                    tracker.Changed         += (_, e) => changes.Add(e);
                    Assert.AreEqual(0, tracker.Changes);
                    CollectionAssert.IsEmpty(propertyChanges);
                    CollectionAssert.IsEmpty(changes);

                    source.Value++;
                    Assert.AreEqual(1, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes" }, propertyChanges);
                    var expected = new[] { RootChangeEventArgs.Create(ChangeTrackerNode.GetOrCreate(source, tracker.Settings, isRoot: false).Value, new PropertyChangeEventArgs(source, source.GetProperty(nameof(source.Value)))) };
                    CollectionAssert.AreEqual(expected, changes, EventArgsComparer.Default);

                    source.Excluded++;
                    Assert.AreEqual(1, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes" }, propertyChanges);
                    CollectionAssert.AreEqual(expected, changes, EventArgsComparer.Default);
                }
            }
            public void IgnoresProperty()
            {
                var withIllegalObject = new WithIllegal();
                var propertyInfo      = typeof(WithIllegal).GetProperty(nameof(WithIllegal.Illegal));
                var settings          = new PropertiesSettingsBuilder().IgnoreProperty(propertyInfo)
                                        .CreateSettings(ReferenceHandling.Structural);

                var propertyChanges = new List <string>();
                var changes         = new List <EventArgs>();

                using (var tracker = Track.Changes(withIllegalObject, settings))
                {
                    tracker.PropertyChanged += (_, e) => propertyChanges.Add(e.PropertyName);
                    tracker.Changed         += (_, e) => changes.Add(e);
                    Assert.AreEqual(0, tracker.Changes);
                    CollectionAssert.IsEmpty(propertyChanges);
                    CollectionAssert.IsEmpty(changes);

                    withIllegalObject.Value++;
                    Assert.AreEqual(1, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes" }, propertyChanges);
                    var expected = new[] { RootChangeEventArgs.Create(ChangeTrackerNode.GetOrCreate(withIllegalObject, tracker.Settings, isRoot: false).Value, new PropertyChangeEventArgs(withIllegalObject, withIllegalObject.GetType().GetProperty(nameof(withIllegalObject.Value)))) };
                    CollectionAssert.AreEqual(expected, changes, EventArgsComparer.Default);

                    withIllegalObject.Illegal = new IllegalType();
                    Assert.AreEqual(1, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes" }, propertyChanges);
                    CollectionAssert.AreEqual(expected, changes, EventArgsComparer.Default);
                }
            }
            public void SameItemTwiceNotifies()
            {
                var item   = new ComplexType();
                var source = new ObservableCollection <ComplexType> {
                    item, item
                };
                var propertyChanges = new List <string>();
                var changes         = new List <EventArgs>();

                using (var tracker = Track.Changes(source, ReferenceHandling.Structural))
                {
                    tracker.PropertyChanged += (_, e) => propertyChanges.Add(e.PropertyName);
                    tracker.Changed         += (_, e) => changes.Add(e);
                    Assert.AreEqual(0, tracker.Changes);
                    CollectionAssert.IsEmpty(propertyChanges);
                    CollectionAssert.IsEmpty(changes);

                    item.Value++;
                    Assert.AreEqual(2, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes", "Changes" }, propertyChanges);
                    var sourceNode = ChangeTrackerNode.GetOrCreate((INotifyCollectionChanged)source, tracker.Settings, false)
                                     .Value;
                    var expected = new[]
                    {
                        new ItemGraphChangedEventArgs <ChangeTrackerNode>(sourceNode, 0, RootChangeEventArgs.Create(ChangeTrackerNode.GetOrCreate(source[0], tracker.Settings, false).Value, new PropertyChangeEventArgs(source[0], source[0].GetProperty("Value")))),
                        new ItemGraphChangedEventArgs <ChangeTrackerNode>(sourceNode, 1, RootChangeEventArgs.Create(ChangeTrackerNode.GetOrCreate(source[1], tracker.Settings, false).Value, new PropertyChangeEventArgs(source[1], source[1].GetProperty("Value"))))
                    };
                    CollectionAssert.AreEqual(expected, changes, EventArgsComparer.Default);
                }
            }
            public void ChildNameChanges()
            {
                var parent = new Parent {
                    Child = new Child(string.Empty)
                };

                parent.Child.Parent = parent;
                var propertyChanges = new List <string>();
                var changes         = new List <EventArgs>();

                using (var tracker = Track.Changes(parent, ReferenceHandling.Structural))
                {
                    tracker.PropertyChanged += (_, e) => propertyChanges.Add(e.PropertyName);
                    tracker.Changed         += (_, e) => changes.Add(e);
                    Assert.AreEqual(0, tracker.Changes);
                    CollectionAssert.IsEmpty(propertyChanges);
                    CollectionAssert.IsEmpty(changes);
                    Assert.AreEqual(0, tracker.Changes);
                    CollectionAssert.IsEmpty(changes);

                    parent.Child.Name += "abc";
                    Assert.AreEqual(1, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes" }, propertyChanges);
                    var rootChangeEventArgs = RootChangeEventArgs.Create(ChangeTrackerNode.GetOrCreate(parent.Child, tracker.Settings, isRoot: false).Value, new PropertyChangeEventArgs(parent.Child, parent.Child.GetProperty(nameof(Child.Name))));
                    var expected            = new[] { new PropertyGraphChangedEventArgs <ChangeTrackerNode>(ChangeTrackerNode.GetOrCreate(parent, tracker.Settings, isRoot: false).Value, parent.GetType().GetProperty("Child"), rootChangeEventArgs) };
                    CollectionAssert.AreEqual(expected, changes, EventArgsComparer.Default);
                }
            }
            public void WithSelf()
            {
                var source          = new WithSelf();
                var propertyChanges = new List <string>();
                var changes         = new List <EventArgs>();
                var expectedChanges = new List <EventArgs>();

                using (var tracker = Track.Changes(source, ReferenceHandling.Structural))
                {
                    tracker.PropertyChanged += (_, e) => propertyChanges.Add(e.PropertyName);
                    tracker.Changed         += (_, e) => changes.Add(e);
                    Assert.AreEqual(0, tracker.Changes);
                    CollectionAssert.IsEmpty(propertyChanges);
                    CollectionAssert.IsEmpty(changes);

                    source.Value = source;
                    Assert.AreEqual(1, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes" }, propertyChanges);
                    expectedChanges.Add(RootChangeEventArgs.Create(ChangeTrackerNode.GetOrCreate(source, tracker.Settings, isRoot: false).Value, new PropertyChangeEventArgs(source, source.GetProperty(nameof(source.Value)))));
                    CollectionAssert.AreEqual(expectedChanges, changes, EventArgsComparer.Default);

                    source.Name += "abc";
                    Assert.AreEqual(2, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes", "Changes" }, propertyChanges);
                    expectedChanges.Add(RootChangeEventArgs.Create(ChangeTrackerNode.GetOrCreate(source, tracker.Settings, isRoot: false).Value, new PropertyChangeEventArgs(source, source.GetProperty(nameof(source.Name)))));
                    CollectionAssert.AreEqual(expectedChanges, changes, EventArgsComparer.Default);
                }
            }
            public void ReplaceStartsListeningToNew()
            {
                var source = new ObservableCollection <ComplexType> {
                    new ComplexType(), new ComplexType()
                };
                var propertyChanges = new List <string>();
                var changes         = new List <EventArgs>();
                var expected        = new List <EventArgs>();

                using (var tracker = Track.Changes(source, ReferenceHandling.Structural))
                {
                    tracker.PropertyChanged += (_, e) => propertyChanges.Add(e.PropertyName);
                    tracker.Changed         += (_, e) => changes.Add(e);
                    Assert.AreEqual(0, tracker.Changes);
                    CollectionAssert.IsEmpty(propertyChanges);
                    CollectionAssert.IsEmpty(changes);

                    source[0] = new ComplexType();
                    Assert.AreEqual(1, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes" }, propertyChanges);
                    var node = ChangeTrackerNode.GetOrCreate((INotifyCollectionChanged)source, tracker.Settings, isRoot: false).Value;

                    expected.Add(RootChangeEventArgs.Create(node, new ReplaceEventArgs(source, 0)));
                    CollectionAssert.AreEqual(expected, changes, EventArgsComparer.Default);

                    source[0].Value++;
                    Assert.AreEqual(2, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes", "Changes" }, propertyChanges);
                    var sourceNode = ChangeTrackerNode.GetOrCreate((INotifyCollectionChanged)source, tracker.Settings, isRoot: false)
                                     .Value;
                    expected.Add(new ItemGraphChangedEventArgs <ChangeTrackerNode>(sourceNode, 0, RootChangeEventArgs.Create(ChangeTrackerNode.GetOrCreate(source[0], tracker.Settings, isRoot: false).Value, new PropertyChangeEventArgs(source[0], source[0].GetProperty("Value")))));
                    CollectionAssert.AreEqual(expected, changes, EventArgsComparer.Default);
                }
            }
            public void TracksCollectionProperty()
            {
                var source = new Level
                {
                    Next =
                        new Level
                    {
                        Levels =
                            new ObservableCollection <Level>(new[] { new Level(), })
                    }
                };
                var propertyChanges = new List <string>();
                var changes         = new List <EventArgs>();
                var expectedChanges = new List <EventArgs>();

                using (var tracker = Track.Changes(source, ReferenceHandling.Structural))
                {
                    var sourceNode = ChangeTrackerNode.GetOrCreate(source, tracker.Settings, false)
                                     .Value;
                    tracker.PropertyChanged += (_, e) => propertyChanges.Add(e.PropertyName);
                    tracker.Changed         += (_, e) => changes.Add(e);
                    Assert.AreEqual(0, tracker.Changes);
                    CollectionAssert.IsEmpty(propertyChanges);
                    CollectionAssert.IsEmpty(changes);

                    source.Next.Levels[0].Value++;
                    Assert.AreEqual(1, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes" }, propertyChanges);
                    expectedChanges.Add(
                        new PropertyGraphChangedEventArgs <ChangeTrackerNode>(
                            sourceNode,
                            source.GetProperty("Next"),
                            new PropertyGraphChangedEventArgs <ChangeTrackerNode>(
                                ChangeTrackerNode.GetOrCreate(source.Next, tracker.Settings, false)
                                .Value,
                                source.GetProperty("Levels"),
                                new ItemGraphChangedEventArgs <ChangeTrackerNode>(
                                    ChangeTrackerNode.GetOrCreate(
                                        (INotifyCollectionChanged)source.Next.Levels,
                                        tracker.Settings,
                                        false)
                                    .Value,
                                    0,
                                    RootChangeEventArgs.Create(
                                        ChangeTrackerNode.GetOrCreate(source.Next.Levels[0], tracker.Settings, false)
                                        .Value,
                                        new PropertyChangeEventArgs(source.Next.Levels[0], source.Next.Levels[0].GetProperty("Value")))))));
                    CollectionAssert.AreEqual(expectedChanges, changes, EventArgsComparer.Default);
                }
            }
Esempio n. 14
0
            public void NotifiesOnAddSpecialCollection()
            {
                var source          = new SpecialCollection();
                var propertyChanges = new List <string>();
                var changes         = new List <EventArgs>();
                var expectedChanges = new List <EventArgs>();

                using (var tracker = Track.Changes(source, ReferenceHandling.Structural))
                {
                    tracker.PropertyChanged += (_, e) => propertyChanges.Add(e.PropertyName);
                    tracker.Changed         += (_, e) => changes.Add(e);
                    Assert.AreEqual(0, tracker.Changes);
                    CollectionAssert.IsEmpty(propertyChanges);
                    CollectionAssert.IsEmpty(changes);

                    var level = new Level();
                    source.Add(level);
                    Assert.AreEqual(1, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes" }, propertyChanges);
                    var sourceNode = ChangeTrackerNode.GetOrCreate((INotifyCollectionChanged)source, tracker.Settings, isRoot: false).Value;
                    expectedChanges.Add(RootChangeEventArgs.Create(sourceNode, new AddEventArgs(source, 0)));
                    CollectionAssert.AreEqual(expectedChanges, changes, EventArgsComparer.Default);

                    level.Value++;
                    Assert.AreEqual(2, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes", "Changes" }, propertyChanges);
                    expectedChanges.Add(
                        new ItemGraphChangedEventArgs <ChangeTrackerNode>(
                            sourceNode,
                            0,
                            new RootChangeEventArgs <ChangeTrackerNode>(
                                ChangeTrackerNode.GetOrCreate(level, tracker.Settings, isRoot: false)
                                .Value,
                                new PropertyChangeEventArgs(level, level.GetProperty("Value")))));
                    CollectionAssert.AreEqual(expectedChanges, changes, EventArgsComparer.Default);

                    source.Remove(level);
                    Assert.AreEqual(3, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes", "Changes", "Changes" }, propertyChanges);
                    expectedChanges.Add(RootChangeEventArgs.Create(sourceNode, new RemoveEventArgs(source, 0)));
                    CollectionAssert.AreEqual(expectedChanges, changes, EventArgsComparer.Default);

                    level.Value++;
                    Assert.AreEqual(3, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes", "Changes", "Changes" }, propertyChanges);
                    CollectionAssert.AreEqual(expectedChanges, changes, EventArgsComparer.Default);
                }
            }
            public void SequenceOfChanges()
            {
                var parent = new Parent {
                    Child = new Child("c")
                };

                var propertyChanges = new List <string>();
                var changes         = new List <EventArgs>();
                var expected        = new List <EventArgs>();

                using (var tracker = Track.Changes(parent, ReferenceHandling.Structural))
                {
                    tracker.PropertyChanged += (_, e) => propertyChanges.Add(e.PropertyName);
                    tracker.Changed         += (_, e) => changes.Add(e);
                    Assert.AreEqual(0, tracker.Changes);
                    CollectionAssert.IsEmpty(propertyChanges);
                    CollectionAssert.IsEmpty(changes);

                    parent.Name = "Poppa";
                    Assert.AreEqual(1, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes" }, propertyChanges);
                    var parentNode = ChangeTrackerNode.GetOrCreate(parent, tracker.Settings, isRoot: false).Value;
                    expected.Add(RootChangeEventArgs.Create(parentNode, new PropertyChangeEventArgs(parent, parent.GetProperty("Name"))));
                    CollectionAssert.AreEqual(expected, changes, EventArgsComparer.Default);

                    parent.Child = new Child("Child");
                    Assert.AreEqual(2, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes", "Changes" }, propertyChanges);
                    expected.Add(RootChangeEventArgs.Create(parentNode, new PropertyChangeEventArgs(parent, parent.GetProperty("Child"))));
                    CollectionAssert.AreEqual(expected, changes, EventArgsComparer.Default);

                    parent.Child.Parent = parent;
                    Assert.AreEqual(3, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes", "Changes", "Changes" }, propertyChanges);
                    expected.Add(new PropertyGraphChangedEventArgs <ChangeTrackerNode>(parentNode, parent.GetProperty("Child"), RootChangeEventArgs.Create(ChangeTrackerNode.GetOrCreate(parent.Child, tracker.Settings, isRoot: false).Value, new PropertyChangeEventArgs(parent.Child, parent.Child.GetProperty("Parent")))));
                    CollectionAssert.AreEqual(expected, changes, EventArgsComparer.Default);

                    parent.Name += "meh";
                    Assert.AreEqual(4, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes", "Changes", "Changes", "Changes" }, propertyChanges);
                    expected.Add(RootChangeEventArgs.Create(parentNode, new PropertyChangeEventArgs(parent, parent.GetProperty("Name"))));
                    CollectionAssert.AreEqual(expected, changes, EventArgsComparer.Default);
                }
            }
            public void MoveThenItemsNotifies(int from, int to)
            {
                var source = new ObservableCollection <ComplexType> {
                    new ComplexType(), new ComplexType(), new ComplexType()
                };
                var propertyChanges = new List <string>();
                var changes         = new List <EventArgs>();
                var expectedChanges = new List <EventArgs>();

                using (var tracker = Track.Changes(source, ReferenceHandling.Structural))
                {
                    tracker.PropertyChanged += (_, e) => propertyChanges.Add(e.PropertyName);
                    tracker.Changed         += (_, e) => changes.Add(e);
                    Assert.AreEqual(0, tracker.Changes);
                    CollectionAssert.IsEmpty(propertyChanges);
                    CollectionAssert.IsEmpty(changes);

                    source.Move(from, to);
                    Assert.AreEqual(1, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes" }, propertyChanges);
                    var sourceNode = ChangeTrackerNode.GetOrCreate((INotifyCollectionChanged)source, tracker.Settings, false).Value;
                    expectedChanges.Add(RootChangeEventArgs.Create(sourceNode, new MoveEventArgs(source, from, to)));
                    CollectionAssert.AreEqual(expectedChanges, changes, EventArgsComparer.Default);

                    source[0].Value++;
                    Assert.AreEqual(2, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes", "Changes" }, propertyChanges);
                    expectedChanges.Add(new ItemGraphChangedEventArgs <ChangeTrackerNode>(sourceNode, 0, RootChangeEventArgs.Create(ChangeTrackerNode.GetOrCreate(source[0], tracker.Settings, false).Value, new PropertyChangeEventArgs(source[0], source[0].GetProperty("Value")))));
                    CollectionAssert.AreEqual(expectedChanges, changes, EventArgsComparer.Default);

                    source[1].Value++;
                    Assert.AreEqual(3, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes", "Changes", "Changes" }, propertyChanges);
                    expectedChanges.Add(new ItemGraphChangedEventArgs <ChangeTrackerNode>(sourceNode, 1, RootChangeEventArgs.Create(ChangeTrackerNode.GetOrCreate(source[1], tracker.Settings, false).Value, new PropertyChangeEventArgs(source[1], source[1].GetProperty("Value")))));
                    CollectionAssert.AreEqual(expectedChanges, changes, EventArgsComparer.Default);

                    source[2].Value++;
                    Assert.AreEqual(4, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes", "Changes", "Changes", "Changes" }, propertyChanges);
                    expectedChanges.Add(new ItemGraphChangedEventArgs <ChangeTrackerNode>(sourceNode, 2, RootChangeEventArgs.Create(ChangeTrackerNode.GetOrCreate(source[2], tracker.Settings, false).Value, new PropertyChangeEventArgs(source[2], source[2].GetProperty("Value")))));
                    CollectionAssert.AreEqual(expectedChanges, changes, EventArgsComparer.Default);
                }
            }
            public void WithImmutable(ReferenceHandling referenceHandling)
            {
                var source = new With <Immutable>();

                var propertyChanges = new List <string>();
                var changes         = new List <EventArgs>();

                using (var tracker = Track.Changes(source, referenceHandling))
                {
                    tracker.PropertyChanged += (_, e) => propertyChanges.Add(e.PropertyName);
                    tracker.Changed         += (_, e) => changes.Add(e);
                    CollectionAssert.IsEmpty(propertyChanges);
                    CollectionAssert.IsEmpty(changes);

                    source.Value = new Immutable();
                    Assert.AreEqual(1, tracker.Changes);
                    CollectionAssert.AreEqual(new[] { "Changes" }, propertyChanges);
                    var node     = ChangeTrackerNode.GetOrCreate(source, tracker.Settings, isRoot: false).Value;
                    var expected = new[] { RootChangeEventArgs.Create(node, new PropertyChangeEventArgs(source, source.GetProperty(nameof(source.Value)))) };
                    CollectionAssert.AreEqual(expected, changes, EventArgsComparer.Default);
                }
            }