public void ReplaceUsesIdentityComparerGivenAtConstruction()
        {
            var collectionChangedHandler = Substitute.For <CollectionChangedHandler <IntWrapper> >();

            IEnumerable <IntWrapper> originalItems = new IntWrapper[] { 1, 2, 3 };
            IntWrapper replacement = 2;

            IEnumerable <ItemChange <IntWrapper> > expectedChanges = new ItemChange <IntWrapper>[]
            {
                new ItemChange <IntWrapper>(ChangeType.Updated, replacement)
            };

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

            loader.LoadAsync();  // load original items

            loader.CollectionChanged += collectionChangedHandler;
            loader.CollectionChanged += (s, e) =>
            {
                // Verify that the expected update was made
                e.Should().Equal(expectedChanges);
            };


            loader.Replace(replacement, replacement);  // --- Perform ---


            // Verify that changes were made by checking for collection changed events
            collectionChangedHandler.Received().Invoke(loader, Arg.Any <IEnumerable <ItemChange <IntWrapper> > >());
        }
        public void Replace_WithPredicate_WorksForEmptyLoader()
        {
            var loader = new ThreadSafeAsyncLoader <int>(Seq.ListBased);

            loader.Replace(i => i == 1, 2);  // --- Perform ---
            loader.Should().BeEmpty();
        }
        public void ReplaceCanReplaceWhenLoaderHasMultipleItems()
        {
            IEnumerable <int> loadedInts = new[] { 1, 2, 3 };
            var loader = new ThreadSafeAsyncLoader <int>(Seq.ListBased, tok => Task.FromResult(loadedInts));

            loader.LoadAsync();

            loader.Replace(2, 4);  // --- Perform ---

            loader.Should().BeEquivalentTo(new[] { 1, 4, 3 });
        }
        public void Replace_WithPredicate_OnSingleton_DoesNothing_WhenNotMatched()
        {
            IEnumerable <int> loadedInts = new[] { 1 };
            var loader = new ThreadSafeAsyncLoader <int>(Seq.ListBased, tok => Task.FromResult(loadedInts));

            loader.LoadAsync();

            loader.Replace(i => i == 2, 3);  // --- Perform ---

            loader.Should().BeEquivalentTo(new[] { 1 });
        }
        public void Replace_WithPredicate_CanReplaceSingleton()
        {
            IEnumerable <int> loadedInts = new[] { 1 };
            var loader = new ThreadSafeAsyncLoader <int>(Seq.ListBased, tok => Task.FromResult(loadedInts));

            loader.LoadAsync();

            loader.Replace(i => i == 1, 2); // --- Perform ---

            loader.Should().BeEquivalentTo(new[] { 2 });
        }
        public void ReplaceDoesNotNotifyIfLoaderIsEmpty()
        {
            var loader   = new ThreadSafeAsyncLoader <int>(Seq.ListBased, eventContext: new RunInlineSynchronizationContext());
            var listener = Substitute.For <CollectionChangedHandler <int> >();

            loader.CollectionChanged += listener;


            loader.Replace(1, 2);  // --- Perform ---


            listener.DidNotReceive().Invoke(loader, Arg.Any <IntChangesAlias>());
        }
        public void Replace_WithPredicate_OnTimestampedValues_Updates_IfNewer()
        {
            IEnumerable <TimestampedInt> originalItems = new[]
            {
                new TimestampedInt(1, new DateTime(2017, 09, 08, 18, 56, 00, DateTimeKind.Utc)),
                new TimestampedInt(2, new DateTime(2016, 01, 01, 10, 00, 00, DateTimeKind.Utc))
            };

            // A newer replacement
            TimestampedInt replacement = new TimestampedInt(3, new DateTime(2018, 01, 01, 01, 08, 00, DateTimeKind.Utc));

            var loader = new ThreadSafeAsyncLoader <TimestampedInt>(seqFactory: Seq.ListBased, loadDataAsync: _ => Task.FromResult(originalItems));

            loader.LoadAsync();

            // --- Perform ---
            loader.Replace(i => i.Value == 1, replacement);

            loader.Should().BeEquivalentTo(new[] { replacement, originalItems.ElementAt(1) });
        }
        public void ReplaceDoesNotNotifyIfItemNotFound()
        {
            IEnumerable <int> initialValues = new[] { 1, 2 };

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

            loader.LoadAsync();  // load initial values

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

            loader.CollectionChanged += listener;


            loader.Replace(3, 1);  // --- Perform ---


            listener.DidNotReceive().Invoke(loader, Arg.Any <IntChangesAlias>());
        }
        public void Replace_WithPredicate_Notifies_OfEveryChangeMade()
        {
            IEnumerable <int> loadedInts = new[] { 2, 2, 2, 2 };
            var loader = new ThreadSafeAsyncLoader <int>(
                Seq.ListBased,
                loadDataAsync: tok => Task.FromResult(loadedInts),
                eventContext: new RunInlineSynchronizationContext());

            loader.LoadAsync();  // load initial items

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

            loader.CollectionChanged += listener;


            loader.Replace(i => i == 2, 1);   // --- Perform ---


            listener.Received(1).Invoke(loader, Fluent.Match <IntChangesAlias>(changes =>
                                                                               changes.Should().BeEquivalentTo(Enumerable.Repeat(new ItemChange <int>(ChangeType.Updated, 1), 4))));
        }
        public void Replace_OnTimestampedValues_DoesNotUpdate_IfOlder()
        {
            IEnumerable <TimestampedInt> originalItems = new[]
            {
                new TimestampedInt(1, new DateTime(2017, 09, 08, 18, 56, 00, DateTimeKind.Utc)),
                new TimestampedInt(2, new DateTime(2016, 01, 01, 10, 00, 00, DateTimeKind.Utc))
            };

            // An older replacement
            TimestampedInt replacement = new TimestampedInt(3, new DateTime(2017, 01, 01, 01, 08, 00, DateTimeKind.Utc));

            var loader = new ThreadSafeAsyncLoader <TimestampedInt>(
                seqFactory: Seq.ListBased,
                loadDataAsync: _ => Task.FromResult(originalItems),
                identityComparer: new TimestampedIntValueComparer());

            loader.LoadAsync();

            // --- Perform ---
            loader.Replace(originalItems.ElementAt(0), replacement);

            loader.Should().BeEquivalentTo(originalItems);
        }