using System.Collections.ObjectModel; using System.Collections.Specialized; using Xunit; namespace ZymonicServices.Performance.Tests; public class CollectionProjectionTests { [Fact] public void UnchangedProjectionDoesNotNotify() { var item = new object(); var target = new ObservableCollection { item }; target.CollectionChanged += (_, _) => Assert.Fail("Unchanged projection notified the UI."); ZymonicCollectionProjection.Apply(target, new[] { item }); } [Fact] public void ReordersAddsAndRemovesWithoutResettingRetainedItems() { var a = new object(); var b = new object(); var c = new object(); var d = new object(); var target = new ObservableCollection { a, b, c }; var actions = new List(); target.CollectionChanged += (_, e) => actions.Add(e.Action); ZymonicCollectionProjection.Apply(target, new[] { c, d, a }); Assert.Equal(new[] { c, d, a }, target); Assert.DoesNotContain(NotifyCollectionChangedAction.Reset, actions); Assert.Contains(NotifyCollectionChangedAction.Move, actions); ZymonicCollectionProjection.Apply(target, Array.Empty()); Assert.Empty(target); Assert.DoesNotContain(NotifyCollectionChangedAction.Reset, actions); } private sealed record Value(int Id); [Fact] public void EqualValuesStillReplaceOldInstance() { var old = new Value(1); var replacement = new Value(1); var target = new ObservableCollection { old }; ZymonicCollectionProjection.Apply(target, new[] { replacement }); Assert.Same(replacement, Assert.Single(target)); } [Fact] public void DuplicateDesiredInstanceIsRejectedBeforeMutation() { var item = new object(); var target = new ObservableCollection { item }; Assert.Throws(() => ZymonicCollectionProjection.Apply(target, new[] { item, item })); Assert.Same(item, Assert.Single(target)); } }