static TrackingCollection <Thing> CreateSource()
    {
        var result = new TrackingCollection <Thing>(Observable.Empty <Thing>());

        result.Subscribe();
        result.AddItem(new Thing(1, "item1", DateTimeOffset.MinValue));
        result.AddItem(new Thing(2, "item2", DateTimeOffset.MinValue));
        result.AddItem(new Thing(3, "item3", DateTimeOffset.MinValue));
        return(result);
    }
        public override void Initialize([AllowNull] ViewWithData data)
        {
            base.Initialize(data);

            IsLoaded = false;

            PullRequests = repositoryHost.ModelService.GetPullRequests(repository, pullRequests);
            pullRequests.Subscribe(pr =>
            {
                trackingAssignees.AddItem(pr.Assignee);
                trackingAuthors.AddItem(pr.Author);
            }, () => { });

            pullRequests.OriginalCompleted
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(_ =>
            {
                if (listSettings.SelectedAuthor != null)
                {
                    SelectedAuthor = Authors.FirstOrDefault(x => x.Login == listSettings.SelectedAuthor);
                }

                if (listSettings.SelectedAssignee != null)
                {
                    SelectedAssignee = Assignees.FirstOrDefault(x => x.Login == listSettings.SelectedAssignee);
                }

                IsLoaded = true;
                UpdateFilter(SelectedState, SelectedAssignee, SelectedAuthor);
            });
        }
        async Task Load()
        {
            IsBusy = true;

            if (modelService == null)
            {
                modelService = await modelServiceFactory.CreateAsync(connection);
            }

            if (remoteRepository == null)
            {
                remoteRepository = await modelService.GetRepository(
                    localRepository.Owner,
                    localRepository.Name);

                Repositories = remoteRepository.IsFork ?
                               new[] { remoteRepository.Parent, remoteRepository } :
                new[] { remoteRepository };
                SelectedRepository = Repositories[0];
            }

            PullRequests = modelService.GetPullRequests(SelectedRepository, pullRequests);
            pullRequests.Subscribe(pr =>
            {
                trackingAssignees.AddItem(pr.Assignee);
                trackingAuthors.AddItem(pr.Author);
            }, () => { });

            pullRequests.OriginalCompleted
            .ObserveOn(RxApp.MainThreadScheduler)
            ////.Catch<System.Reactive.Unit, Octokit.AuthorizationException>(ex =>
            ////{
            ////    log.Info("Received AuthorizationException reading pull requests", ex);
            ////    return repositoryHost.LogOut();
            ////})
            .Catch <System.Reactive.Unit, Exception>(ex =>
            {
                // Occurs on network error, when the repository was deleted on GitHub etc.
                log.Error(ex, "Received Exception reading pull requests");
                return(Observable.Empty <System.Reactive.Unit>());
            })
            .Subscribe(_ =>
            {
                if (listSettings.SelectedAuthor != null)
                {
                    SelectedAuthor = Authors.FirstOrDefault(x => x.Login == listSettings.SelectedAuthor);
                }

                if (listSettings.SelectedAssignee != null)
                {
                    SelectedAssignee = Assignees.FirstOrDefault(x => x.Login == listSettings.SelectedAssignee);
                }

                IsBusy = false;
                UpdateFilter(SelectedState, SelectedAssignee, SelectedAuthor, SearchQuery);
            });
        }
    public void OrderByUpdatedNoFilter()
    {
        var count = 6;
        ITrackingCollection<Thing> col = new TrackingCollection<Thing>(
            Observable.Never<Thing>(),
            OrderedComparer<Thing>.OrderBy(x => x.UpdatedAt).Compare);
        col.NewerComparer = OrderedComparer<Thing>.OrderByDescending(x => x.UpdatedAt).Compare;
        col.ProcessingDelay = TimeSpan.Zero;

        var list1 = new List<Thing>(Enumerable.Range(1, count).Select(i => GetThing(i, i, count - i, "Run 1")).ToList());
        var list2 = new List<Thing>(Enumerable.Range(1, count).Select(i => GetThing(i, i, i + count, "Run 2")).ToList());

        var evt = new ManualResetEvent(false);
        col.Subscribe(t =>
        {
            if (++count == list1.Count)
                evt.Set();
        }, () => { });

        count = 0;
        // add first items
        foreach (var l in list1)
            col.AddItem(l);

        evt.WaitOne();
        evt.Reset();

        Assert.AreEqual(list1.Count, col.Count);
        list1.Sort(new LambdaComparer<Thing>(OrderedComparer<Thing>.OrderByDescending(x => x.CreatedAt).Compare));
        CollectionAssert.AreEqual(col, list1);

        count = 0;
        // replace items
        foreach (var l in list2)
            col.AddItem(l);

        evt.WaitOne();
        evt.Reset();

        Assert.AreEqual(list2.Count, col.Count);
        CollectionAssert.AreEqual(col, list2);

        col.Dispose();
    }
        public override void Initialize([AllowNull] ViewWithData data)
        {
            base.Initialize(data);

            repositoryHost.ModelService.GetPullRequests(repository, pullRequests);
            pullRequests.Subscribe(pr =>
            {
                trackingAssignees.AddItem(pr.Assignee);
                trackingAuthors.AddItem(pr.Author);
            }, () => { });
        }
示例#6
0
        public override void Initialize([AllowNull] ViewWithData data)
        {
            base.Initialize(data);

            IsLoaded = false;

            PullRequests = repositoryHost.ModelService.GetPullRequests(repository, pullRequests);
            pullRequests.Subscribe(pr =>
            {
                trackingAssignees.AddItem(pr.Assignee);
                trackingAuthors.AddItem(pr.Author);
            }, () => { });

            pullRequests.OriginalCompleted
            .ObserveOn(RxApp.MainThreadScheduler)
            .Catch <System.Reactive.Unit, Octokit.AuthorizationException>(ex =>
            {
                log.Info("Received AuthorizationException reading pull requests", ex);
                return(repositoryHost.LogOut());
            })
            .Catch <System.Reactive.Unit, Exception>(ex =>
            {
                // Occurs on network error, when the repository was deleted on GitHub etc.
                log.Info("Received Exception reading pull requests", ex);
                return(Observable.Empty <System.Reactive.Unit>());
            })
            .Subscribe(_ =>
            {
                if (listSettings.SelectedAuthor != null)
                {
                    SelectedAuthor = Authors.FirstOrDefault(x => x.Login == listSettings.SelectedAuthor);
                }

                if (listSettings.SelectedAssignee != null)
                {
                    SelectedAssignee = Assignees.FirstOrDefault(x => x.Login == listSettings.SelectedAssignee);
                }

                IsLoaded = true;
                UpdateFilter(SelectedState, SelectedAssignee, SelectedAuthor);
            });
        }
示例#7
0
        public override void Initialize([AllowNull] ViewWithData data)
        {
            base.Initialize(data);

            IsLoaded = false;

            PullRequests = repositoryHost.ModelService.GetPullRequests(repository, pullRequests);
            pullRequests.Subscribe(pr =>
            {
                trackingAssignees.AddItem(pr.Assignee);
                trackingAuthors.AddItem(pr.Author);
            }, () => { });

            pullRequests.OriginalCompleted
            .ObserveOn(RxApp.MainThreadScheduler)
            .Catch <System.Reactive.Unit, Octokit.AuthorizationException>(ex =>
            {
                // TODO: Do some decent logging here
                return(repositoryHost.LogOut());
            })
            .Catch <System.Reactive.Unit, Octokit.NotFoundException>(ex =>
            {
                //this is caused when repository was deleted on github
                return(Observable.Empty <System.Reactive.Unit>());
            })
            .Subscribe(_ =>
            {
                if (listSettings.SelectedAuthor != null)
                {
                    SelectedAuthor = Authors.FirstOrDefault(x => x.Login == listSettings.SelectedAuthor);
                }

                if (listSettings.SelectedAssignee != null)
                {
                    SelectedAssignee = Assignees.FirstOrDefault(x => x.Login == listSettings.SelectedAssignee);
                }

                IsLoaded = true;
                UpdateFilter(SelectedState, SelectedAssignee, SelectedAuthor);
            });
        }
        Task Load()
        {
            IsBusy = true;

            PullRequests = modelService.GetPullRequests(SelectedRepository, pullRequests);
            pullRequests.Subscribe(pr =>
            {
                trackingAssignees.AddItem(pr.Assignee);
                trackingAuthors.AddItem(pr.Author);
            }, () => { });

            pullRequests.OriginalCompleted
            .ObserveOn(RxApp.MainThreadScheduler)
            .Catch <System.Reactive.Unit, Exception>(ex =>
            {
                // Occurs on network error, when the repository was deleted on GitHub etc.
                log.Error(ex, "Received Exception reading pull requests");
                return(Observable.Empty <System.Reactive.Unit>());
            })
            .Subscribe(_ =>
            {
                if (listSettings.SelectedAuthor != null)
                {
                    SelectedAuthor = Authors.FirstOrDefault(x => x.Login == listSettings.SelectedAuthor);
                }

                if (listSettings.SelectedAssignee != null)
                {
                    SelectedAssignee = Assignees.FirstOrDefault(x => x.Login == listSettings.SelectedAssignee);
                }

                IsBusy = false;
                UpdateFilter(SelectedState, SelectedAssignee, SelectedAuthor, SearchQuery);
            });
            return(Task.CompletedTask);
        }
    public void OrderByMatchesOriginalOrder()
    {
        var count = 0;
        var total = 1000;

        var list1 = new List<Thing>(Enumerable.Range(1, total).Select(i => GetThing(i, i, total - i, "Run 1")).ToList());
        var list2 = new List<Thing>(Enumerable.Range(1, total).Select(i => GetThing(i, i, total - i, "Run 2")).ToList());

        var col = new TrackingCollection<Thing>(
            list1.ToObservable(),
            OrderedComparer<Thing>.OrderByDescending(x => x.UpdatedAt).Compare,
            (item, position, list) => item.Title.Equals("Run 2"));
        col.ProcessingDelay = TimeSpan.Zero;

        count = 0;
        var evt = new ManualResetEvent(false);
        col.Subscribe(t =>
        {
            if (++count == list1.Count)
                evt.Set();
        }, () => { });

        evt.WaitOne();
        evt.Reset();

        Assert.AreEqual(total, count);
        Assert.AreEqual(0, col.Count);

        count = 0;

        // add new items
        foreach (var l in list2)
            col.AddItem(l);

        evt.WaitOne();
        evt.Reset();

        Assert.AreEqual(total, count);
        Assert.AreEqual(total, col.Count);
        CollectionAssert.AreEqual(col, list2);

        col.Dispose();
    }
 public void DisposingThrows()
 {
     var col = new TrackingCollection<Thing>(Observable.Empty<Thing>());
     col.Dispose();
     Assert.Throws<ObjectDisposedException>(() => col.SetFilter(null));
     Assert.Throws<ObjectDisposedException>(() => col.SetComparer(null));
     Assert.Throws<ObjectDisposedException>(() => col.Subscribe());
     Assert.Throws<ObjectDisposedException>(() => col.AddItem(GetThing(1)));
     Assert.Throws<ObjectDisposedException>(() => col.RemoveItem(GetThing(1)));
 }
 public void NoChangingAfterDisposed1()
 {
     var col = new TrackingCollection<Thing>(Observable.Never<Thing>(), OrderedComparer<Thing>.OrderByDescending(x => x.UpdatedAt).Compare);
     col.Dispose();
     Assert.Throws<ObjectDisposedException>(() => col.AddItem(new Thing()));
 }
示例#12
0
        public void CopyFromDoesNotLoseAvatar()
        {
            var userImage = AvatarProvider.CreateBitmapImage("pack://application:,,,/GitHub.App;component/Images/default_user_avatar.png");
            var orgImage  = AvatarProvider.CreateBitmapImage("pack://application:,,,/GitHub.App;component/Images/default_org_avatar.png");

            var initialBitmapImageSubject = new Subject <BitmapImage>();

            var collectionEvent     = new ManualResetEvent(false);
            var avatarPropertyEvent = new ManualResetEvent(false);

            //Creating an initial account with an observable that returns immediately
            const string login = "******";
            const int    initialOwnedPrivateRepositoryCount = 1;

            var initialAccount = new Account(login, true, false, initialOwnedPrivateRepositoryCount, 0, initialBitmapImageSubject);

            //Creating the test collection
            var col = new TrackingCollection <IAccount>(Observable.Empty <IAccount>(), OrderedComparer <IAccount> .OrderByDescending(x => x.Login).Compare);

            col.Subscribe(account =>
            {
                collectionEvent.Set();
            }, () => { });

            //Adding that account to the TrackingCollection
            col.AddItem(initialAccount);

            //Waiting for the collection add the item
            collectionEvent.WaitOne();
            collectionEvent.Reset();

            //Checking some initial properties
            Assert.That(login, Is.EqualTo(col[0].Login));
            Assert.That(initialOwnedPrivateRepositoryCount, Is.EqualTo(col[0].OwnedPrivateRepos));

            //Demonstrating that the avatar is not yet present
            Assert.That(col[0].Avatar, Is.Null);

            //Adding a listener to check for the changing of the Avatar property
            initialAccount.Changed.Subscribe(args =>
            {
                if (args.PropertyName == "Avatar")
                {
                    avatarPropertyEvent.Set();
                }
            });

            //Providing the first avatar
            initialBitmapImageSubject.OnNext(userImage);
            initialBitmapImageSubject.OnCompleted();

            //Waiting for the avatar to be added
            avatarPropertyEvent.WaitOne();
            avatarPropertyEvent.Reset();

            //Demonstrating that the avatar is present
            Assert.That(col[0].Avatar, Is.Not.Null);
            Assert.True(BitmapSourcesAreEqual(col[0].Avatar, userImage));
            Assert.False(BitmapSourcesAreEqual(col[0].Avatar, orgImage));

            //Creating an account update
            const int updatedOwnedPrivateRepositoryCount = 2;
            var       updatedBitmapImageSubject          = new Subject <BitmapImage>();
            var       updatedAccount = new Account(login, true, false, updatedOwnedPrivateRepositoryCount, 0, updatedBitmapImageSubject);

            //Updating the account in the collection
            col.AddItem(updatedAccount);

            //Waiting for the collection to process the update
            collectionEvent.WaitOne();
            collectionEvent.Reset();

            //Providing the second avatar
            updatedBitmapImageSubject.OnNext(orgImage);
            updatedBitmapImageSubject.OnCompleted();

            //Waiting for the delayed bitmap image observable
            avatarPropertyEvent.WaitOne();
            avatarPropertyEvent.Reset();

            //Login is the id, so that should be the same
            Assert.That(login, Is.EqualTo(col[0].Login));

            //CopyFrom() should have updated this field
            Assert.That(updatedOwnedPrivateRepositoryCount, Is.EqualTo(col[0].OwnedPrivateRepos));

            //CopyFrom() should not cause a race condition here
            Assert.That(col[0].Avatar, Is.Not.Null);
            Assert.True(BitmapSourcesAreEqual(col[0].Avatar, orgImage));
            Assert.False(BitmapSourcesAreEqual(col[0].Avatar, userImage));
        }
    public void OnlyTimesEqualOrHigherThan3Minutes()
    {
        var count = 6;

        var list1 = new List<Thing>(Enumerable.Range(1, count).Select(i => GetThing(i, i, count - i, "Run 1")).ToList());

        var col = new TrackingCollection<Thing>(
            Observable.Never<Thing>(),
            OrderedComparer<Thing>.OrderBy(x => x.UpdatedAt).Compare,
            (item, position, list) => item.UpdatedAt >= Now + TimeSpan.FromMinutes(3) && item.UpdatedAt <= Now + TimeSpan.FromMinutes(5));
        col.ProcessingDelay = TimeSpan.Zero;

        var evt = new ManualResetEvent(false);
        col.Subscribe(t =>
        {
            if (++count == list1.Count)
                evt.Set();
        }, () => { });

        count = 0;
        // add first items
        foreach (var l in list1)
            col.AddItem(l);

        evt.WaitOne();
        evt.Reset();

        Assert.AreEqual(3, col.Count);

#if DEBUG
        CollectionAssert.AreEqual(list1.Reverse<Thing>(), col.DebugInternalList);
#endif
        CollectionAssert.AreEqual(col, new List<Thing>() { list1[2], list1[1], list1[0] });
        col.Dispose();
    }
    public void OrderByDoesntMatchOriginalOrderTimings()
    {
        var count = 0;
        var total = 1000;

        var list1 = new List<Thing>(Enumerable.Range(1, total).Select(i => GetThing(i, i, i, "Run 1")).ToList());
        var list2 = new List<Thing>(Enumerable.Range(1, total).Select(i => GetThing(i, i, i, "Run 2")).ToList());

        ITrackingCollection<Thing> col = new TrackingCollection<Thing>(
            list1.ToObservable(),
            OrderedComparer<Thing>.OrderByDescending(x => x.UpdatedAt).Compare,
            (item, position, list) => item.Title.Equals("Run 2"));
        col.NewerComparer = OrderedComparer<Thing>.OrderByDescending(x => x.UpdatedAt).Compare;
        col.ProcessingDelay = TimeSpan.Zero;

        var evt = new ManualResetEvent(false);
        var start = DateTimeOffset.UtcNow;

        col.Subscribe(t =>
        {
            if (++count == list1.Count)
                evt.Set();
        }, () => { });

        evt.WaitOne();
        var time = (DateTimeOffset.UtcNow - start).TotalMilliseconds;
        Assert.LessOrEqual(time, 100);
        evt.Reset();

        Assert.AreEqual(total, count);
        Assert.AreEqual(0, col.Count);

        count = 0;

        start = DateTimeOffset.UtcNow;
        // add new items
        foreach (var l in list2)
            col.AddItem(l);

        evt.WaitOne();
        time = (DateTimeOffset.UtcNow - start).TotalMilliseconds;
        Assert.LessOrEqual(time, 200);
        evt.Reset();

        Assert.AreEqual(total, count);
        Assert.AreEqual(total, col.Count);
        CollectionAssert.AreEqual(col, list2.Reverse<Thing>());

        col.Dispose();
    }
示例#15
0
        public void CopyFromDoesNotLoseAvatar()
        {
            var userImage = AvatarProvider.CreateBitmapImage("pack://application:,,,/GitHub.App;component/Images/default_user_avatar.png");
            var orgImage = AvatarProvider.CreateBitmapImage("pack://application:,,,/GitHub.App;component/Images/default_org_avatar.png");

            var initialBitmapImageSubject = new Subject<BitmapImage>();

            var collectionEvent = new ManualResetEvent(false);
            var avatarPropertyEvent = new ManualResetEvent(false);

            //Creating an initial account with an observable that returns immediately
            const string login = "******";
            const int initialOwnedPrivateRepositoryCount = 1;

            var initialAccount = new Account(login, true, false, initialOwnedPrivateRepositoryCount, 0, initialBitmapImageSubject);

            //Creating the test collection
            var col = new TrackingCollection<IAccount>(Observable.Empty<IAccount>(), OrderedComparer<IAccount>.OrderByDescending(x => x.Login).Compare);
            col.Subscribe(account =>
            {
                collectionEvent.Set();
            }, () => { });

            //Adding that account to the TrackingCollection
            col.AddItem(initialAccount);

            //Waiting for the collection add the item
            collectionEvent.WaitOne();
            collectionEvent.Reset();

            //Checking some initial properties
            Assert.Equal(login, col[0].Login);
            Assert.Equal(initialOwnedPrivateRepositoryCount, col[0].OwnedPrivateRepos);

            //Demonstrating that the avatar is not yet present
            Assert.Null(col[0].Avatar);

            //Adding a listener to check for the changing of the Avatar property
            initialAccount.Changed.Subscribe(args =>
            {
                if (args.PropertyName == "Avatar")
                {
                    avatarPropertyEvent.Set();
                }
            });

            //Providing the first avatar
            initialBitmapImageSubject.OnNext(userImage);
            initialBitmapImageSubject.OnCompleted();

            //Waiting for the avatar to be added
            avatarPropertyEvent.WaitOne();
            avatarPropertyEvent.Reset();

            //Demonstrating that the avatar is present
            Assert.NotNull(col[0].Avatar);
            Assert.True(BitmapSourcesAreEqual(col[0].Avatar, userImage));
            Assert.False(BitmapSourcesAreEqual(col[0].Avatar, orgImage));

            //Creating an account update
            const int updatedOwnedPrivateRepositoryCount = 2;
            var updatedBitmapImageSubject = new Subject<BitmapImage>();
            var updatedAccount = new Account(login, true, false, updatedOwnedPrivateRepositoryCount, 0, updatedBitmapImageSubject);

            //Updating the account in the collection
            col.AddItem(updatedAccount);

            //Waiting for the collection to process the update
            collectionEvent.WaitOne();
            collectionEvent.Reset();

            //Providing the second avatar
            updatedBitmapImageSubject.OnNext(orgImage);
            updatedBitmapImageSubject.OnCompleted();

            //Waiting for the delayed bitmap image observable
            avatarPropertyEvent.WaitOne();
            avatarPropertyEvent.Reset();

            //Login is the id, so that should be the same
            Assert.Equal(login, col[0].Login);

            //CopyFrom() should have updated this field
            Assert.Equal(updatedOwnedPrivateRepositoryCount, col[0].OwnedPrivateRepos);

            //CopyFrom() should not cause a race condition here
            Assert.NotNull(col[0].Avatar);
            Assert.True(BitmapSourcesAreEqual(col[0].Avatar, orgImage));
            Assert.False(BitmapSourcesAreEqual(col[0].Avatar, userImage));
        }
 public void AddingWithNoObservableSetThrows()
 {
     ITrackingCollection<Thing> col = new TrackingCollection<Thing>();
     Assert.Throws<InvalidOperationException>(() => col.AddItem(new Thing()));
 }
    public void OnlyIndexes2To4()
    {
        var count = 6;

        var list1 = new List<Thing>(Enumerable.Range(1, count).Select(i => GetThing(i, i, count - i, "Run 1")).ToList());

        ITrackingCollection<Thing> col = new TrackingCollection<Thing>(
            Observable.Never<Thing>(),
            OrderedComparer<Thing>.OrderBy(x => x.UpdatedAt).Compare,
            (item, position, list) => position >= 2 && position <= 4);
        col.NewerComparer = OrderedComparer<Thing>.OrderByDescending(x => x.UpdatedAt).Compare;
        col.ProcessingDelay = TimeSpan.Zero;

        var evt = new ManualResetEvent(false);
        col.Subscribe(t =>
        {
            if (++count == list1.Count)
                evt.Set();
        }, () => { });

        count = 0;
        // add first items
        foreach (var l in list1)
            col.AddItem(l);

        evt.WaitOne();
        evt.Reset();

        Assert.AreEqual(3, col.Count);

#if DEBUG
        CollectionAssert.AreEqual(list1.Reverse<Thing>(), (col as TrackingCollection<Thing>).DebugInternalList);
#endif

        CollectionAssert.AreEqual(col, new List<Thing>() { list1[3], list1[2], list1[1] });

        col.Dispose();
    }
    public async void AddingBeforeSubscribingWorks()
    {
        ITrackingCollection<Thing> col = new TrackingCollection<Thing>(Observable.Empty<Thing>());
        ReplaySubject<Thing> done = new ReplaySubject<Thing>();
        col.AddItem(GetThing(1));
        col.AddItem(GetThing(2));
        var count = 0;
        done.OnNext(null);
        col.Subscribe(t =>
        {
            done.OnNext(t);
            if (++count == 2)
                done.OnCompleted();
        }, () => {});

        await Observable.Timeout(done, TimeSpan.FromMilliseconds(500));
        Assert.AreEqual(2, col.Count);
    }
    public void DoesUpdateThingIfTimeIsNewer()
    {
        ITrackingCollection<Thing> col = new TrackingCollection<Thing>(
            Observable.Never<Thing>(),
            OrderedComparer<Thing>.OrderBy(x => x.UpdatedAt).Compare);
        col.NewerComparer = OrderedComparer<Thing>.OrderByDescending(x => x.UpdatedAt).Compare;
        col.ProcessingDelay = TimeSpan.Zero;

        var evt = new ManualResetEvent(false);
        col.Subscribe(t =>
        {
            evt.Set();
        }, () => { });

        var createdAndUpdatedTime = DateTimeOffset.Now;
        var newerUpdateTime = createdAndUpdatedTime.Add(TimeSpan.FromMinutes(1));

        const string originalTitle = "Original Thing";

        var originalThing = new Thing(1, originalTitle, createdAndUpdatedTime, createdAndUpdatedTime);
        col.AddItem(originalThing);

        evt.WaitOne();
        evt.Reset();

        Assert.AreEqual(originalTitle, col[0].Title);

        const string updatedTitle = "Updated Thing";

        var updatedThing = new Thing(1, updatedTitle, createdAndUpdatedTime, newerUpdateTime);
        col.AddItem(updatedThing);

        evt.WaitOne();
        evt.Reset();

        Assert.AreEqual(updatedTitle, col[0].Title);

        col.Dispose();
    }
 TrackingCollection<Thing> CreateSource()
 {
     var result = new TrackingCollection<Thing>(Observable.Empty<Thing>());
     result.Subscribe();
     result.AddItem(new Thing(1, "item1", DateTimeOffset.MinValue));
     result.AddItem(new Thing(2, "item2", DateTimeOffset.MinValue));
     result.AddItem(new Thing(3, "item3", DateTimeOffset.MinValue));
     return result;
 }