public void ReplaceAllNotifiesOfChangesMade()
        {
            IEnumerable <int> loadedInts = new int[] { 1, 2, 3, 4 };
            var replacements             = new int[] { 5, 6 };

            var expectedChanges = loadedInts.Select(i => new ItemChange <int>(ChangeType.Removed, i))
                                  .Concat(replacements.Select(i => new ItemChange <int>(ChangeType.Added, i)));

            var loader = new ThreadSafeAsyncLoader <int>(
                Seq.ListBased,
                loadDataAsync: tok => Task.FromResult(loadedInts),
                eventContext: new RunInlineSynchronizationContext());

            loader.LoadAsync();  // load initial values

            var listener = Substitute.For <CollectionChangedHandler <int> >();

            loader.CollectionChanged += listener;


            loader.Should().Equal(loadedInts);   // sanity check
            loader.ReplaceAll(replacements);     // --- Perform ---
            loader.Should().Equal(replacements); // sanity check


            listener.Received().Invoke(loader, Fluent.Match <IntChangesAlias>(
                                           changes => changes.Should().BeEquivalentTo(expectedChanges)));
        }
        public void ReplaceAllUsesIdentityComparerGivenAtConstruction()
        {
            IEnumerable <IntWrapper> originalItems = new IntWrapper[] { 1, 2, 3 };
            var replacements = new IntWrapper[] { 1, 3 };

            // NOTE: Due to conservate update check, unchanged items will appear as item changes of type update
            // NOTE2: Need to use the actual instances, since IntWrapper uses reference equality
            var expectedChanges = new ItemChange <IntWrapper>[]
            {
                new ItemChange <IntWrapper>(ChangeType.Updated, replacements[0]),
                new ItemChange <IntWrapper>(ChangeType.Updated, replacements[1]),
                new ItemChange <IntWrapper>(ChangeType.Removed, originalItems.ElementAt(1))
            };

            var loader = new ThreadSafeAsyncLoader <IntWrapper>(
                Seq.ListBased,
                loadDataAsync: _ => Task.FromResult(originalItems),
                identityComparer: new IntWrapperComparer(),
                eventContext: new RunInlineSynchronizationContext());

            loader.LoadAsync();  // load initial values
            loader.CollectionChanged += (s, e) =>
            {
                // Verify that the actual changes match the expected changes
                e.Should().BeEquivalentTo(expectedChanges);
            };


            loader.ReplaceAll(replacements);  // --- Perform ---
        }
        public void ReplaceAllDelegatesToUnderlyingSeq()
        {
            var items  = new[] { 1, 2, 3, 4 };
            var seq    = Substitute.For <ISeq <int> >();
            var loader = new ThreadSafeAsyncLoader <int>(enumerable => seq);

            loader.ReplaceAll(items);  // --- Perform ---

            seq.Received().ReplaceAll(items);
        }
        public void ReplaceAllDoesNotAllowDuplicatesInNewItems()
        {
            IEnumerable <int> loadedInts = new[] { 1, 2, 3 };
            var replacements             = new[] { 2, 2 };

            var loader = new ThreadSafeAsyncLoader <int>(
                Seq.ListBased,
                loadDataAsync: tok => Task.FromResult(loadedInts),
                eventContext: new RunInlineSynchronizationContext());

            loader.LoadAsync();  // load initial values


            // --- Perform ---
            Action callingReplaceAll = () => loader.ReplaceAll(replacements);

            callingReplaceAll.ShouldThrow <ArgumentException>("because duplicates are not allowed").WithMessage("*duplicates*");
        }
        public void ReplaceAllUsesUpdateComparerGivenAtConstruction()
        {
            IEnumerable <int> originalItems = new int[] { 1, 2, 3 };
            var replacements = new int[] { 1, 3 };

            // Since equal int values will now count as unchanged, we do not expect item changes that are updates
            var expectedChanges = new ItemChange <int>[] { new ItemChange <int>(ChangeType.Removed, 2) };

            var loader = new ThreadSafeAsyncLoader <int>(
                Seq.ListBased,
                loadDataAsync: _ => Task.FromResult(originalItems),
                updateComparer: new IntUpdateComparer(),
                eventContext: new RunInlineSynchronizationContext());

            loader.LoadAsync();  // load initial values
            loader.CollectionChanged += (s, e) =>
            {
                // Verify that the actual changes match the expected changes
                e.Should().BeEquivalentTo(expectedChanges);
            };


            loader.ReplaceAll(replacements);  // --- Perform ---
        }