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 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 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 ClearDelegatesToUnderlyingSeq() { var seq = Substitute.For <ISeq <int> >(); var loader = new ThreadSafeAsyncLoader <int>(enumerable => seq); loader.Clear(); // --- Perform --- seq.Received().Clear(); }
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 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_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 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 ClearDoesNotNotifyIfNoChangesWereMade() { var loader = new ThreadSafeAsyncLoader <int>(Seq.ListBased, eventContext: new RunInlineSynchronizationContext()); var listener = Substitute.For <CollectionChangedHandler <int> >(); loader.CollectionChanged += listener; loader.Clear(); // --- Perform --- listener.DidNotReceive().Invoke(loader, Arg.Any <IntChangesAlias>()); }
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 StatusChangesTwiceDuringLoad() { var statusChanges = new List <AsyncStatusTransition>(); IEnumerable <int> initialValues = new[] { 1 }; var loader = new ThreadSafeAsyncLoader <int>( Seq.ListBased, loadDataAsync: tok => Task.FromResult(initialValues), eventContext: new RunInlineSynchronizationContext()); loader.StatusChanged += (s, e) => statusChanges.Add(e); loader.LoadAsync(); // load initial values statusChanges.Should().Equal( new AsyncStatusTransition(AsyncStatus.Ready, AsyncStatus.Loading), new AsyncStatusTransition(AsyncStatus.Loading, AsyncStatus.Ready)); }
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 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 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 ClearNotifiesOfChangesMade() { IEnumerable <int> initialValues = new[] { 1, 2, 3 }; var expectedChanges = initialValues.Select(i => new ItemChange <int>(ChangeType.Removed, i)); 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.Clear(); // --- Perform --- listener.Received().Invoke(loader, Fluent.Match <IntChangesAlias>( changes => changes.Should().BeEquivalentTo(expectedChanges))); }
public void ClearNotifiesOfChangeForSingletonSeq() { IEnumerable <int> initialValues = new[] { 1 }; 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.Clear(); // --- Perform --- listener.Received().Invoke(loader, Fluent.Match <IntChangesAlias>(changes => changes .Should().ContainSingle("because loader contained single item before Clear") .Which.Should().BeRemovalOf(1, "because loader contained the value 1 before Clear"))); }
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); }
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 --- }