public PullRequestListViewModel( IRepositoryHost repositoryHost, ILocalRepositoryModel repository, IPackageSettings settings) { this.repositoryHost = repositoryHost; this.repository = repository; this.settings = settings; this.listSettings = settings.UIState .GetOrCreateRepositoryState(repository.CloneUrl) .PullRequests; openPullRequestCommand = ReactiveCommand.Create(); openPullRequestCommand.Subscribe(_ => { VisualStudio.Services.DefaultExportProvider.GetExportedValue <IVisualStudioBrowser>().OpenUrl(repositoryHost.Address.WebUri); }); States = new List <PullRequestState> { new PullRequestState { IsOpen = true, Name = "Open" }, new PullRequestState { IsOpen = false, Name = "Closed" }, new PullRequestState { Name = "All" } }; trackingAuthors = new TrackingCollection <IAccount>(Observable.Empty <IAccount>(), OrderedComparer <IAccount> .OrderByDescending(x => x.Login).Compare); trackingAssignees = new TrackingCollection <IAccount>(Observable.Empty <IAccount>(), OrderedComparer <IAccount> .OrderByDescending(x => x.Login).Compare); trackingAuthors.Subscribe(); trackingAssignees.Subscribe(); Authors = trackingAuthors.CreateListenerCollection(EmptyUser, this.WhenAnyValue(x => x.SelectedAuthor)); Assignees = trackingAssignees.CreateListenerCollection(EmptyUser, this.WhenAnyValue(x => x.SelectedAssignee)); PullRequests = new TrackingCollection <IPullRequestModel>(); pullRequests.Comparer = OrderedComparer <IPullRequestModel> .OrderByDescending(x => x.UpdatedAt).Compare; pullRequests.NewerComparer = OrderedComparer <IPullRequestModel> .OrderByDescending(x => x.UpdatedAt).Compare; this.WhenAny(x => x.SelectedState, x => x.Value) .Where(x => PullRequests != null) .Subscribe(s => UpdateFilter(s, SelectedAssignee, SelectedAuthor)); this.WhenAny(x => x.SelectedAssignee, x => x.Value) .Where(x => PullRequests != null && x != EmptyUser && IsLoaded) .Subscribe(a => UpdateFilter(SelectedState, a, SelectedAuthor)); this.WhenAny(x => x.SelectedAuthor, x => x.Value) .Where(x => PullRequests != null && x != EmptyUser && IsLoaded) .Subscribe(a => UpdateFilter(SelectedState, SelectedAssignee, a)); SelectedState = States.FirstOrDefault(x => x.Name == listSettings.SelectedState) ?? States[0]; }
public PullRequestListViewModel( IRepositoryHost repositoryHost, ILocalRepositoryModel repository, IPackageSettings settings) { this.repositoryHost = repositoryHost; this.repository = repository; this.settings = settings; Title = Resources.PullRequestsNavigationItemText; this.listSettings = settings.UIState .GetOrCreateRepositoryState(repository.CloneUrl) .PullRequests; States = new List <PullRequestState> { new PullRequestState { IsOpen = true, Name = "Open" }, new PullRequestState { IsOpen = false, Name = "Closed" }, new PullRequestState { Name = "All" } }; trackingAuthors = new TrackingCollection <IAccount>(Observable.Empty <IAccount>(), OrderedComparer <IAccount> .OrderByDescending(x => x.Login).Compare); trackingAssignees = new TrackingCollection <IAccount>(Observable.Empty <IAccount>(), OrderedComparer <IAccount> .OrderByDescending(x => x.Login).Compare); trackingAuthors.Subscribe(); trackingAssignees.Subscribe(); Authors = trackingAuthors.CreateListenerCollection(EmptyUser, this.WhenAnyValue(x => x.SelectedAuthor)); Assignees = trackingAssignees.CreateListenerCollection(EmptyUser, this.WhenAnyValue(x => x.SelectedAssignee)); PullRequests = new TrackingCollection <IPullRequestModel>(); pullRequests.Comparer = OrderedComparer <IPullRequestModel> .OrderByDescending(x => x.UpdatedAt).Compare; pullRequests.NewerComparer = OrderedComparer <IPullRequestModel> .OrderByDescending(x => x.UpdatedAt).Compare; this.WhenAny(x => x.SelectedState, x => x.Value) .Where(x => PullRequests != null) .Subscribe(s => UpdateFilter(s, SelectedAssignee, SelectedAuthor)); this.WhenAny(x => x.SelectedAssignee, x => x.Value) .Where(x => PullRequests != null && x != EmptyUser && IsLoaded) .Subscribe(a => UpdateFilter(SelectedState, a, SelectedAuthor)); this.WhenAny(x => x.SelectedAuthor, x => x.Value) .Where(x => PullRequests != null && x != EmptyUser && IsLoaded) .Subscribe(a => UpdateFilter(SelectedState, SelectedAssignee, a)); SelectedState = States.FirstOrDefault(x => x.Name == listSettings.SelectedState) ?? States[0]; OpenPullRequest = ReactiveCommand.Create(); OpenPullRequest.Subscribe(DoOpenPullRequest); CreatePullRequest = ReactiveCommand.Create(); CreatePullRequest.Subscribe(_ => DoCreatePullRequest()); }
public PullRequestListViewModel(IRepositoryHost repositoryHost, ISimpleRepositoryModel repository) { this.repositoryHost = repositoryHost; this.repository = repository; openPullRequestCommand = ReactiveCommand.Create(); openPullRequestCommand.Subscribe(_ => { VisualStudio.Services.DefaultExportProvider.GetExportedValue <IVisualStudioBrowser>().OpenUrl(repositoryHost.Address.WebUri); }); States = new List <PullRequestState> { new PullRequestState { IsOpen = true, Name = "Open" }, new PullRequestState { IsOpen = false, Name = "Closed" }, new PullRequestState { Name = "All" } }; SelectedState = States[0]; this.WhenAny(x => x.SelectedState, x => x.Value) .Where(x => PullRequests != null) .Subscribe(s => UpdateFilter(s, SelectedAssignee, SelectedAuthor)); this.WhenAny(x => x.SelectedAssignee, x => x.Value) .Where(x => PullRequests != null) .Subscribe(a => UpdateFilter(SelectedState, a, SelectedAuthor)); this.WhenAny(x => x.SelectedAuthor, x => x.Value) .Where(x => PullRequests != null) .Subscribe(a => UpdateFilter(SelectedState, SelectedAssignee, a)); trackingAuthors = new TrackingCollection <IAccount>(Observable.Empty <IAccount>(), OrderedComparer <IAccount> .OrderByDescending(x => x.Login).Compare); trackingAssignees = new TrackingCollection <IAccount>(Observable.Empty <IAccount>(), OrderedComparer <IAccount> .OrderByDescending(x => x.Login).Compare); trackingAuthors.Subscribe(); trackingAssignees.Subscribe(); Authors = trackingAuthors.CreateListenerCollection(new List <IAccount> { EmptyUser }); Assignees = trackingAssignees.CreateListenerCollection(new List <IAccount> { EmptyUser }); PullRequests = new TrackingCollection <IPullRequestModel>(); pullRequests.Comparer = OrderedComparer <IPullRequestModel> .OrderByDescending(x => x.UpdatedAt).Compare; pullRequests.Filter = (pr, i, l) => pr.IsOpen; pullRequests.NewerComparer = OrderedComparer <IPullRequestModel> .OrderByDescending(x => x.UpdatedAt).Compare; }
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 PullRequestListViewModel( IRepositoryHost repositoryHost, ILocalRepositoryModel repository, IPackageSettings settings) { this.repositoryHost = repositoryHost; this.repository = repository; this.settings = settings; Title = Resources.PullRequestsNavigationItemText; this.listSettings = settings.UIState .GetOrCreateRepositoryState(repository.CloneUrl) .PullRequests; openPullRequestCommand = ReactiveCommand.Create(); openPullRequestCommand.Subscribe(_ => { VisualStudio.Services.DefaultExportProvider.GetExportedValue<IVisualStudioBrowser>().OpenUrl(repositoryHost.Address.WebUri); }); States = new List<PullRequestState> { new PullRequestState { IsOpen = true, Name = "Open" }, new PullRequestState { IsOpen = false, Name = "Closed" }, new PullRequestState { Name = "All" } }; trackingAuthors = new TrackingCollection<IAccount>(Observable.Empty<IAccount>(), OrderedComparer<IAccount>.OrderByDescending(x => x.Login).Compare); trackingAssignees = new TrackingCollection<IAccount>(Observable.Empty<IAccount>(), OrderedComparer<IAccount>.OrderByDescending(x => x.Login).Compare); trackingAuthors.Subscribe(); trackingAssignees.Subscribe(); Authors = trackingAuthors.CreateListenerCollection(EmptyUser, this.WhenAnyValue(x => x.SelectedAuthor)); Assignees = trackingAssignees.CreateListenerCollection(EmptyUser, this.WhenAnyValue(x => x.SelectedAssignee)); PullRequests = new TrackingCollection<IPullRequestModel>(); pullRequests.Comparer = OrderedComparer<IPullRequestModel>.OrderByDescending(x => x.UpdatedAt).Compare; pullRequests.NewerComparer = OrderedComparer<IPullRequestModel>.OrderByDescending(x => x.UpdatedAt).Compare; this.WhenAny(x => x.SelectedState, x => x.Value) .Where(x => PullRequests != null) .Subscribe(s => UpdateFilter(s, SelectedAssignee, SelectedAuthor)); this.WhenAny(x => x.SelectedAssignee, x => x.Value) .Where(x => PullRequests != null && x != EmptyUser && IsLoaded) .Subscribe(a => UpdateFilter(SelectedState, a, SelectedAuthor)); this.WhenAny(x => x.SelectedAuthor, x => x.Value) .Where(x => PullRequests != null && x != EmptyUser && IsLoaded) .Subscribe(a => UpdateFilter(SelectedState, SelectedAssignee, a)); SelectedState = States.FirstOrDefault(x => x.Name == listSettings.SelectedState) ?? States[0]; }
public async Task NonExpiredIndexReturnsCacheAsync() { var expected = 5; var username = "******"; var reponame = "repo"; var cache = new InMemoryBlobCache(); var apiClient = Substitute.For <IApiClient>(); var modelService = CreateTarget(apiClient: apiClient, hostCache: cache); var user = CreateOctokitUser(username); apiClient.GetUser().Returns(Observable.Return(user)); apiClient.GetOrganizations().Returns(Observable.Empty <Organization>()); var act = modelService.GetAccounts().ToEnumerable().First().First(); var repo = Substitute.For <LocalRepositoryModel>(); repo.Name.Returns(reponame); repo.CloneUrl.Returns(new UriString("https://github.com/" + username + "/" + reponame)); var indexKey = string.Format(CultureInfo.InvariantCulture, "{0}|{1}:{2}", CacheIndex.PRPrefix, user.Login, repo.Name); var prcache = Enumerable.Range(1, expected) .Select(id => CreatePullRequest(user, id, ItemState.Open, "Cache " + id, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow)); // seed the cache prcache .Select(item => new PullRequestCacheItem(item)) .Select(item => item.Save <PullRequestCacheItem>(cache, indexKey).ToEnumerable().First()) .SelectMany(item => CacheIndex.AddAndSaveToIndex(cache, indexKey, item).ToEnumerable()) .ToList(); var prlive = Observable.Range(1, expected) .Select(id => CreatePullRequest(user, id, ItemState.Open, "Live " + id, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow)) .DelaySubscription(TimeSpan.FromMilliseconds(10)); apiClient.GetPullRequestsForRepository(user.Login, repo.Name).Returns(prlive); await modelService.InsertUser(new AccountCacheItem(user)); ITrackingCollection <IPullRequestModel> col = new TrackingCollection <IPullRequestModel>(); modelService.GetPullRequests(repo, col); col.ProcessingDelay = TimeSpan.Zero; col.Subscribe(); await col.OriginalCompleted.Timeout(TimeSpan.FromMilliseconds(Timeout));; Assert.That(expected, Is.EqualTo(col.Count)); //Assert.Collection(col, col.Select(x => new Action<IPullRequestModel>(t => Assert.That("Cache", StartsWith(x.Title)))).ToArray()); }
public void OrderByUpdatedFilter() { var count = 3; var col = new TrackingCollection<Thing>( Observable.Never<Thing>(), OrderedComparer<Thing>.OrderBy(x => x.UpdatedAt).Compare, (item, position, list) => true); 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 void ListeningTwiceWorks() { var count = 10; ITrackingCollection<Thing> col = new TrackingCollection<Thing>(); 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 subj = new ReplaySubject<Unit>(); subj.OnNext(Unit.Default); var disp = col.OriginalCompleted.Subscribe(x => subj.OnCompleted()); col.Listen(list1.ToObservable()); col.Subscribe(); subj.Wait(); disp.Dispose(); col.Listen(list2.ToObservable()); subj = new ReplaySubject<Unit>(); subj.OnNext(Unit.Default); disp = col.OriginalCompleted.Subscribe(x => subj.OnCompleted()); col.Subscribe(); subj.Wait(); disp.Dispose(); CollectionAssert.AreEqual(list2, col); }
public async Task MultipleSortingAndFiltering() { var expectedTotal = 20; var rnd = new Random(214748364); var titles1 = Enumerable.Range(1, expectedTotal).Select(x => ((char)('a' + x)).ToString()).ToList(); var dates1 = Enumerable.Range(1, expectedTotal).Select(x => Now + TimeSpan.FromMinutes(x)).ToList(); var idstack1 = new Stack<int>(Enumerable.Range(1, expectedTotal).OrderBy(rnd.Next)); var datestack1 = new Stack<DateTimeOffset>(dates1); var titlestack1 = new Stack<string>(titles1.OrderBy(_ => rnd.Next())); var titles2 = Enumerable.Range(1, expectedTotal).Select(x => ((char)('c' + x)).ToString()).ToList(); var dates2 = Enumerable.Range(1, expectedTotal).Select(x => Now + TimeSpan.FromMinutes(x)).ToList(); var dates2mixed = new List<DateTimeOffset>() { dates2[2], dates2[0], dates2[1], dates2[3], dates2[5], dates2[9], dates2[15], dates2[6], dates2[7], dates2[8], dates2[13], dates2[10], dates2[16], dates2[11], dates2[12], dates2[14], dates2[17], dates2[18], dates2[19], dates2[4], }; var idstack2 = new Stack<int>(Enumerable.Range(1, expectedTotal).OrderBy(rnd.Next)); var datestack2 = new Stack<DateTimeOffset>(dates2mixed); var titlestack2 = new Stack<string>(titles2.OrderBy(_ => rnd.Next())); var datestack3 = new Stack<DateTimeOffset>(); for (int i = 0; i < datestack1.Count; i++) datestack3.Push(new DateTimeOffset(Math.Max(dates1[i].Ticks, dates2mixed[i].Ticks), TimeSpan.Zero)); var list1 = Observable.Defer(() => Enumerable.Range(1, expectedTotal) .OrderBy(rnd.Next) .Select(x => new Thing(idstack1.Pop(), titlestack1.Pop(), datestack1.Pop())) .ToObservable()) .Replay() .RefCount(); var list2 = Observable.Defer(() => Enumerable.Range(1, expectedTotal) .OrderBy(rnd.Next) .Select(x => new Thing(idstack2.Pop(), titlestack2.Pop(), datestack2.Pop())) .ToObservable()) .Replay() .RefCount(); ITrackingCollection<Thing> col = new TrackingCollection<Thing>( list1.Concat(list2), OrderedComparer<Thing>.OrderByDescending(x => x.CreatedAt).Compare, (item, idx, list) => idx < 5 ); col.NewerComparer = OrderedComparer<Thing>.OrderByDescending(x => x.UpdatedAt).Compare; col.Subscribe(); await col.OriginalCompleted; // it's initially sorted by date, so id list should not match CollectionAssert.AreNotEqual(list1.Select(x => x.Number).ToEnumerable(), list2.Select(x => x.Number).ToEnumerable()); var sortlist = col.ToArray(); Array.Sort(sortlist, new LambdaComparer<Thing>(OrderedComparer<Thing>.OrderByDescending(x => x.CreatedAt).Compare)); CollectionAssert.AreEqual(sortlist.Take(5), col); col.Comparer = OrderedComparer<Thing>.OrderBy(x => x.Number).Compare; sortlist = col.ToArray(); Array.Sort(sortlist, new LambdaComparer<Thing>(OrderedComparer<Thing>.OrderBy(x => x.Number).Compare)); CollectionAssert.AreEqual(sortlist.Take(5), col); col.Comparer = OrderedComparer<Thing>.OrderBy(x => x.CreatedAt).Compare; sortlist = col.ToArray(); Array.Sort(sortlist, new LambdaComparer<Thing>(OrderedComparer<Thing>.OrderBy(x => x.CreatedAt).Compare)); CollectionAssert.AreEqual(sortlist.Take(5), col); col.Comparer = OrderedComparer<Thing>.OrderByDescending(x => x.Title).Compare; sortlist = col.ToArray(); Array.Sort(sortlist, new LambdaComparer<Thing>(OrderedComparer<Thing>.OrderByDescending(x => x.Title).Compare)); CollectionAssert.AreEqual(sortlist.Take(5), col); col.Comparer = OrderedComparer<Thing>.OrderBy(x => x.Title).Compare; sortlist = col.ToArray(); Array.Sort(sortlist, new LambdaComparer<Thing>(OrderedComparer<Thing>.OrderBy(x => x.Title).Compare)); CollectionAssert.AreEqual(sortlist.Take(5), col); col.Dispose(); }
public void ChangingSortUpdatesCollection() { var source = new Subject<Thing>(); var col = new TrackingCollection<Thing>( source, OrderedComparer<Thing>.OrderBy(x => x.UpdatedAt).Compare, (item, position, list) => item.UpdatedAt < Now + TimeSpan.FromMinutes(10)) { ProcessingDelay = TimeSpan.Zero }; var count = 0; var evt = new ManualResetEvent(false); var list1 = new List<Thing> { GetThing(1, 1), GetThing(2, 2), GetThing(3, 3), GetThing(4, 4), GetThing(5, 5), GetThing(6, 6), GetThing(7, 7), GetThing(8, 8), GetThing(9, 9), }; col.Subscribe(t => { if (++count == list1.Count) evt.Set(); }, () => { }); foreach (var l in list1) Add(source, l); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(col, list1); col.SetComparer(OrderedComparer<Thing>.OrderByDescending(x => x.UpdatedAt).Compare); CollectionAssert.AreEqual(col, list1.Reverse<Thing>().ToArray()); 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 SortingTestWithFilterMoves() { var source = new Subject<Thing>(); var col = new TrackingCollection<Thing>( source, OrderedComparer<Thing>.OrderBy(x => x.UpdatedAt).Compare, (item, position, list) => (position >= 1 && position <= 2) || (position >= 5 && position <= 7)) { ProcessingDelay = TimeSpan.Zero }; var count = 0; var expectedCount = 0; var evt = new ManualResetEvent(false); col.Subscribe(t => { if (++count == expectedCount) evt.Set(); }, () => { }); expectedCount = 9; Add(source, GetThing(1, 1)); Add(source, GetThing(2, 3)); Add(source, GetThing(3, 5)); Add(source, GetThing(4, 7)); Add(source, GetThing(5, 9)); Add(source, GetThing(6, 11)); Add(source, GetThing(7, 13)); Add(source, GetThing(8, 15)); Add(source, GetThing(9, 17)); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(col, new List<Thing> { GetThing(2, 3), GetThing(3, 5), GetThing(6, 11), GetThing(7, 13), GetThing(8, 5), }); expectedCount = 10; Add(source, GetThing(7, 4)); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(col, new List<Thing> { GetThing(2, 3), GetThing(7, 4), GetThing(5, 9), GetThing(6, 11), GetThing(8, 5), }); expectedCount = 11; Add(source, GetThing(9, 2)); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(col, new List<Thing> { GetThing(9, 2), GetThing(2, 3), GetThing(4, 7), GetThing(5, 9), GetThing(6, 11), }); col.Dispose(); }
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(); }
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 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)); }
void Run2() { var count = 10; var col = new TrackingCollection<Thing>(); col.ProcessingDelay = TimeSpan.FromMilliseconds(20); col.NewerComparer = OrderedComparer<Thing>.OrderByDescending(x => x.UpdatedAt).Compare; list.ItemsSource = col; var source = Observable.Merge( Observable.Generate(0, i => i < count, i => i + 1, i => i, i => TimeSpan.FromMilliseconds(5)) .Select(i => GetThing(i, i, i, "Run 1")), Observable.Generate(0, i => i < count, i => i + 1, i => i, i => TimeSpan.FromMilliseconds(7)) .Select(i => GetThing(i, i, i + 1, "Run 2")) ); col.Listen(source); col.Subscribe(); }
public PullRequestListViewModel( IModelServiceFactory modelServiceFactory, IPackageSettings settings, IPullRequestSessionManager sessionManager, IVisualStudioBrowser visualStudioBrowser) { Guard.ArgumentNotNull(modelServiceFactory, nameof(modelServiceFactory)); Guard.ArgumentNotNull(settings, nameof(settings)); Guard.ArgumentNotNull(sessionManager, nameof(sessionManager)); Guard.ArgumentNotNull(visualStudioBrowser, nameof(visualStudioBrowser)); constructing = true; this.modelServiceFactory = modelServiceFactory; this.settings = settings; this.visualStudioBrowser = visualStudioBrowser; Title = Resources.PullRequestsNavigationItemText; States = new List <PullRequestState> { new PullRequestState { IsOpen = true, Name = "Open" }, new PullRequestState { IsOpen = false, Name = "Closed" }, new PullRequestState { Name = "All" } }; trackingAuthors = new TrackingCollection <IAccount>(Observable.Empty <IAccount>(), OrderedComparer <IAccount> .OrderByDescending(x => x.Login).Compare); trackingAssignees = new TrackingCollection <IAccount>(Observable.Empty <IAccount>(), OrderedComparer <IAccount> .OrderByDescending(x => x.Login).Compare); trackingAuthors.Subscribe(); trackingAssignees.Subscribe(); Authors = trackingAuthors.CreateListenerCollection(EmptyUser, this.WhenAnyValue(x => x.SelectedAuthor)); Assignees = trackingAssignees.CreateListenerCollection(EmptyUser, this.WhenAnyValue(x => x.SelectedAssignee)); CreatePullRequests(); this.WhenAny(x => x.SelectedState, x => x.Value) .Where(x => PullRequests != null) .Subscribe(s => UpdateFilter(s, SelectedAssignee, SelectedAuthor, SearchQuery)); this.WhenAny(x => x.SelectedAssignee, x => x.Value) .Where(x => PullRequests != null && x != EmptyUser) .Subscribe(a => UpdateFilter(SelectedState, a, SelectedAuthor, SearchQuery)); this.WhenAny(x => x.SelectedAuthor, x => x.Value) .Where(x => PullRequests != null && x != EmptyUser) .Subscribe(a => UpdateFilter(SelectedState, SelectedAssignee, a, SearchQuery)); this.WhenAny(x => x.SearchQuery, x => x.Value) .Where(x => PullRequests != null) .Subscribe(f => UpdateFilter(SelectedState, SelectedAssignee, SelectedAuthor, f)); this.WhenAnyValue(x => x.SelectedRepository) .Skip(1) .Subscribe(_ => ResetAndLoad()); OpenPullRequest = ReactiveCommand.Create(); OpenPullRequest.Subscribe(DoOpenPullRequest); CreatePullRequest = ReactiveCommand.Create(); CreatePullRequest.Subscribe(_ => DoCreatePullRequest()); OpenPullRequestOnGitHub = ReactiveCommand.Create(); OpenPullRequestOnGitHub.Subscribe(x => DoOpenPullRequestOnGitHub((int)x)); // Get the current pull request session and the selected repository. When the session's // repository is the same as our selected repository set CheckedOutPullRequest to the // current session's model, so that the checked out PR can be highlighted. Observable.CombineLatest( sessionManager.WhenAnyValue(x => x.CurrentSession), this.WhenAnyValue(x => x.SelectedRepository), (s, r) => new { Session = s, Repository = r }) .Subscribe(x => { CheckedOutPullRequest = x.Session?.RepositoryOwner == x.Repository?.Owner ? x.Session?.PullRequest : null; }); constructing = false; }
public void RemovingFirstItemWithFilterWorks() { var source = new Subject<Thing>(); ITrackingCollection<Thing> col = new TrackingCollection<Thing>( Observable.Range(0, 5).Select(x => GetThing(x, x)), OrderedComparer<Thing>.OrderBy(x => x.UpdatedAt).Compare, (item, position, list) => true); col.ProcessingDelay = TimeSpan.Zero; var count = 0; var expectedCount = 5; var evt = new ManualResetEvent(false); col.Subscribe(t => { if (++count == expectedCount) evt.Set(); }, () => { }); Assert.True(evt.WaitOne(40)); evt.Reset(); expectedCount = 6; col.RemoveItem(GetThing(0)); Assert.True(evt.WaitOne(40)); evt.Reset(); CollectionAssert.AreEqual(col, Enumerable.Range(1, 4).Select(x => GetThing(x, x))); col.Dispose(); }
public void ChangingSortingAndUpdatingItemsUpdatesSortCorrectly() { var source = new Subject<Thing>(); ITrackingCollection<Thing> col = new TrackingCollection<Thing>( source); col.Comparer = OrderedComparer<Thing>.OrderBy(x => x.UpdatedAt).Compare; col.NewerComparer = OrderedComparer<Thing>.OrderByDescending(x => x.UpdatedAt).Compare; col.Filter = (item, position, list) => position == 2 || position == 3 || position == 5 || position == 7; col.ProcessingDelay = TimeSpan.Zero; var count = 0; var expectedCount = 0; var evt = new ManualResetEvent(false); col.Subscribe(t => { if (++count == expectedCount) evt.Set(); }, () => { }); expectedCount = 9; Enumerable.Range(0, expectedCount) .Select(x => GetThing(x, x)) .ForEach(x => Add(source, x)); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(new List<Thing> { GetThing(2, 2), GetThing(3, 3), GetThing(5, 5), GetThing(7, 7), }, col); expectedCount = 10; Add(source, GetThing(3, 3, 2)); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(new List<Thing> { GetThing(2, 2), GetThing(3, 3), GetThing(5, 5), GetThing(7, 7), }, col); expectedCount = 11; Add(source, GetThing(3, 3, 4)); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(new List<Thing> { GetThing(2, 2), GetThing(3, 3, 4), GetThing(5, 5), GetThing(7, 7), }, col); expectedCount = 12; Add(source, GetThing(3, 3, 6)); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(new List<Thing> { GetThing(2, 2), GetThing(4, 4), GetThing(3, 3, 6), GetThing(7, 7), }, col); col.Comparer = OrderedComparer<Thing>.OrderByDescending(x => x.UpdatedAt).Compare; CollectionAssert.AreEqual(new List<Thing> { GetThing(3, 3, 6), GetThing(6, 6), GetThing(4, 4), GetThing(1, 1), }, col); expectedCount = 13; Add(source, GetThing(4, 4)); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(new List<Thing> { GetThing(3, 3, 6), GetThing(6, 6), GetThing(4, 4), GetThing(1, 1), }, col); expectedCount = 14; Add(source, GetThing(4, 4, 6)); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(new List<Thing> { GetThing(3, 3, 6), GetThing(6, 6), GetThing(5, 5), GetThing(1, 1), }, col); expectedCount = 15; Add(source, GetThing(5, 5, 6)); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(new List<Thing> { GetThing(3, 3, 6), GetThing(6, 6), GetThing(5, 5, 6), GetThing(1, 1), }, col); col.Dispose(); }
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(); }
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(); }
public void ChangingComparers() { var source = new Subject<Thing>(); var col = new TrackingCollection<Thing>(source, OrderedComparer<Thing>.OrderBy(x => x.CreatedAt).Compare) { ProcessingDelay = TimeSpan.Zero }; var count = 0; var evt = new ManualResetEvent(false); var list1 = new List<Thing> { GetThing(1, 1, 9), GetThing(2, 2, 8), GetThing(3, 3, 7), GetThing(4, 4, 6), GetThing(5, 5, 5), GetThing(6, 6, 4), GetThing(7, 7, 3), GetThing(8, 8, 2), GetThing(9, 9, 1), }; col.Subscribe(t => { if (++count == list1.Count) evt.Set(); }, () => { }); foreach (var l in list1) Add(source, l); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(col, list1); col.SetComparer(null); CollectionAssert.AreEqual(col, list1.Reverse<Thing>().ToArray()); col.Dispose(); }
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 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 ChangingItemContentRemovesItFromFilteredList2() { var source = new Subject<Thing>(); var now = new DateTimeOffset(0, TimeSpan.FromTicks(0)); var col = new TrackingCollection<Thing>( source, OrderedComparer<Thing>.OrderBy(x => x.CreatedAt).Compare, (item, position, list) => item.UpdatedAt > now + TimeSpan.FromMinutes(2) && item.UpdatedAt < now + TimeSpan.FromMinutes(8)) { ProcessingDelay = TimeSpan.Zero }; var count = 0; var expectedCount = 0; var evt = new ManualResetEvent(false); col.Subscribe(t => { if (++count == expectedCount) evt.Set(); }, () => { }); expectedCount = 5; Add(source, GetThing(1, 1, 1)); Add(source, GetThing(3, 3, 3)); Add(source, GetThing(5, 5, 5)); Add(source, GetThing(7, 7, 7)); Add(source, GetThing(9, 9, 9)); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(col, new List<Thing> { GetThing(3, 3, 3), GetThing(5, 5, 5), GetThing(7, 7, 7), }); expectedCount = 6; Add(source, GetThing(7, 7, 8)); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(col, new List<Thing> { GetThing(3, 3, 3), GetThing(5, 5, 5), }); expectedCount = 7; Add(source, GetThing(7, 7, 7)); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(col, new List<Thing> { GetThing(3, 3, 3), GetThing(5, 5, 5), GetThing(7, 7, 7), }); expectedCount = 8; Add(source, GetThing(3, 3, 2)); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(col, new List<Thing> { GetThing(5, 5, 5), GetThing(7, 7, 7), }); expectedCount = 9; Add(source, GetThing(3, 3, 3)); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(col, new List<Thing> { GetThing(3, 3, 3), GetThing(5, 5, 5), GetThing(7, 7, 7), }); expectedCount = 10; Add(source, GetThing(5, 5, 1)); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(col, new List<Thing> { GetThing(3, 3, 3), GetThing(7, 7, 7), }); col.Dispose(); }
public void OrderByDescendingNoFilter() { var count = 6; 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, "Run 2")).ToList()); var col = new TrackingCollection<Thing>( Observable.Never<Thing>(), 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(6, col.Count); #if DEBUG CollectionAssert.AreEqual(list1, col.DebugInternalList); #endif CollectionAssert.AreEqual(col, list1); count = 0; // add first items foreach (var l in list2) col.AddItem(l); evt.WaitOne(); evt.Reset(); Assert.AreEqual(6, col.Count); col.Dispose(); }
public void ChangingFilterUpdatesCollection() { var source = new Subject<Thing>(); var col = new TrackingCollection<Thing>( source, OrderedComparer<Thing>.OrderBy(x => x.UpdatedAt).Compare, (item, position, list) => item.UpdatedAt < Now + TimeSpan.FromMinutes(10)) { ProcessingDelay = TimeSpan.Zero }; var count = 0; var expectedCount = 0; var evt = new ManualResetEvent(false); col.Subscribe(t => { if (++count == expectedCount) evt.Set(); }, () => { }); expectedCount = 9; Add(source, GetThing(1, 1)); Add(source, GetThing(2, 2)); Add(source, GetThing(3, 3)); Add(source, GetThing(4, 4)); Add(source, GetThing(5, 5)); Add(source, GetThing(6, 6)); Add(source, GetThing(7, 7)); Add(source, GetThing(8, 8)); Add(source, GetThing(9, 9)); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(col, new List<Thing> { GetThing(1, 1), GetThing(2, 2), GetThing(3, 3), GetThing(4, 4), GetThing(5, 5), GetThing(6, 6), GetThing(7, 7), GetThing(8, 8), GetThing(9, 9), }); col.SetFilter((item, position, list) => item.UpdatedAt < Now + TimeSpan.FromMinutes(8)); CollectionAssert.AreEqual(col, new List<Thing> { GetThing(1, 1), GetThing(2, 2), GetThing(3, 3), GetThing(4, 4), GetThing(5, 5), GetThing(6, 6), GetThing(7, 7), }); col.Dispose(); }
public void NotInitializedCorrectlyThrows2() { var col = new TrackingCollection<Thing>(OrderedComparer<Thing>.OrderByDescending(x => x.UpdatedAt).Compare); Assert.Throws<InvalidOperationException>(() => col.Subscribe(_ => { }, () => { })); }
public void RemovingItemsFromCollectionManuallyThrows2() { var source = new Subject<Thing>(); var col = new TrackingCollection<Thing>(source) { ProcessingDelay = TimeSpan.Zero }; var count = 0; var expectedCount = 2; var evt = new ManualResetEvent(false); col.Subscribe(t => { if (++count == expectedCount) evt.Set(); }, () => { }); Add(source, GetThing(1, 1)); Add(source, GetThing(2, 2)); evt.WaitOne(); evt.Reset(); Assert.Throws<InvalidOperationException>(() => col.RemoveAt(0)); col.Dispose(); }
public async Task MultipleSortingAndFiltering() { var expectedTotal = 20; var rnd = new Random(214748364); var updatedAtMinutesStack = new Stack<int>(Enumerable.Range(1, expectedTotal).OrderBy(rnd.Next)); var list1 = Observable.Defer(() => Enumerable.Range(1, expectedTotal) .OrderBy(rnd.Next) .Select(x => GetThing(x, x, x, ((char)('a' + x)).ToString())) .ToObservable()) .Replay() .RefCount(); var list2 = Observable.Defer(() => Enumerable.Range(1, expectedTotal) .OrderBy(rnd.Next) .Select(x => GetThing(x, x, updatedAtMinutesStack.Pop(), ((char)('c' + x)).ToString())) .ToObservable()) .Replay() .RefCount(); ITrackingCollection<Thing> col = new TrackingCollection<Thing>( list1.Concat(list2), OrderedComparer<Thing>.OrderByDescending(x => x.UpdatedAt).Compare, (item, idx, list) => idx < 5 ); col.NewerComparer = OrderedComparer<Thing>.OrderByDescending(x => x.UpdatedAt).Compare; col.Subscribe(); await col.OriginalCompleted; // it's initially sorted by date, so id list should not match CollectionAssert.AreNotEqual(list1.Select(x => x.Number).ToEnumerable(), list2.Select(x => x.Number).ToEnumerable()); var sortlist = col.ToArray(); Array.Sort(sortlist, new LambdaComparer<Thing>(OrderedComparer<Thing> .OrderByDescending(x => x.UpdatedAt) .ThenByDescending(x => x.CreatedAt).Compare)); CollectionAssert.AreEqual(sortlist.Take(5), col); col.Comparer = OrderedComparer<Thing>.OrderBy(x => x.Number).Compare; sortlist = col.ToArray(); Array.Sort(sortlist, new LambdaComparer<Thing>(OrderedComparer<Thing>.OrderBy(x => x.Number).Compare)); CollectionAssert.AreEqual(sortlist.Take(5), col); col.Comparer = OrderedComparer<Thing>.OrderBy(x => x.UpdatedAt).Compare; sortlist = col.ToArray(); Array.Sort(sortlist, new LambdaComparer<Thing>(OrderedComparer<Thing> .OrderBy(x => x.UpdatedAt) .ThenBy(x => x.CreatedAt).Compare)); CollectionAssert.AreEqual(sortlist.Take(5), col); col.Comparer = OrderedComparer<Thing>.OrderByDescending(x => x.Title).Compare; sortlist = col.ToArray(); Array.Sort(sortlist, new LambdaComparer<Thing>(OrderedComparer<Thing>.OrderByDescending(x => x.Title).Compare)); CollectionAssert.AreEqual(sortlist.Take(5), col); col.Comparer = OrderedComparer<Thing>.OrderBy(x => x.Title).Compare; sortlist = col.ToArray(); Array.Sort(sortlist, new LambdaComparer<Thing>(OrderedComparer<Thing>.OrderBy(x => x.Title).Compare)); CollectionAssert.AreEqual(sortlist.Take(5), col); col.Dispose(); }
public void Removing() { var source = new Subject<Thing>(); var col = new TrackingCollection<Thing>( source, OrderedComparer<Thing>.OrderBy(x => x.UpdatedAt).Compare, (item, position, list) => (position > 2 && position < 5) || (position > 6 && position < 8)) { ProcessingDelay = TimeSpan.Zero }; var count = 0; var expectedCount = 0; var evt = new ManualResetEvent(false); col.Subscribe(t => { if (++count == expectedCount) evt.Set(); }, () => { }); expectedCount = 11; Add(source, GetThing(0, 0)); Add(source, GetThing(1, 1)); Add(source, GetThing(2, 2)); Add(source, GetThing(3, 3)); Add(source, GetThing(4, 4)); Add(source, GetThing(5, 5)); Add(source, GetThing(6, 6)); Add(source, GetThing(7, 7)); Add(source, GetThing(8, 8)); Add(source, GetThing(9, 9)); Add(source, GetThing(10, 10)); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(col, new List<Thing> { GetThing(3, 3), GetThing(4, 4), GetThing(7, 7), }); expectedCount = 12; col.RemoveItem(GetThing(2)); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(col, new List<Thing> { GetThing(4, 4), GetThing(5, 5), GetThing(8, 8), }); expectedCount = 13; col.RemoveItem(GetThing(5)); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(col, new List<Thing> { GetThing(4, 4), GetThing(6, 6), GetThing(9, 9), }); col.SetFilter(null); expectedCount = 14; col.RemoveItem(GetThing(100)); // this one won't result in a new element from the observable col.RemoveItem(GetThing(10)); evt.WaitOne(); evt.Reset(); Assert.AreEqual(8, col.Count); CollectionAssert.AreEqual(col, new List<Thing> { GetThing(0, 0), GetThing(1, 1), GetThing(3, 3), GetThing(4, 4), GetThing(6, 6), GetThing(7, 7), GetThing(8, 8), GetThing(9, 9), }); col.Dispose(); }
public PullRequestListViewModel( IModelServiceFactory modelServiceFactory, IPackageSettings settings, IVisualStudioBrowser visualStudioBrowser) { Guard.ArgumentNotNull(modelServiceFactory, nameof(modelServiceFactory)); Guard.ArgumentNotNull(settings, nameof(settings)); Guard.ArgumentNotNull(visualStudioBrowser, nameof(visualStudioBrowser)); constructing = true; this.modelServiceFactory = modelServiceFactory; this.settings = settings; this.visualStudioBrowser = visualStudioBrowser; Title = Resources.PullRequestsNavigationItemText; States = new List <PullRequestState> { new PullRequestState { IsOpen = true, Name = "Open" }, new PullRequestState { IsOpen = false, Name = "Closed" }, new PullRequestState { Name = "All" } }; trackingAuthors = new TrackingCollection <IAccount>(Observable.Empty <IAccount>(), OrderedComparer <IAccount> .OrderByDescending(x => x.Login).Compare); trackingAssignees = new TrackingCollection <IAccount>(Observable.Empty <IAccount>(), OrderedComparer <IAccount> .OrderByDescending(x => x.Login).Compare); trackingAuthors.Subscribe(); trackingAssignees.Subscribe(); Authors = trackingAuthors.CreateListenerCollection(EmptyUser, this.WhenAnyValue(x => x.SelectedAuthor)); Assignees = trackingAssignees.CreateListenerCollection(EmptyUser, this.WhenAnyValue(x => x.SelectedAssignee)); CreatePullRequests(); this.WhenAny(x => x.SelectedState, x => x.Value) .Where(x => PullRequests != null) .Subscribe(s => UpdateFilter(s, SelectedAssignee, SelectedAuthor, SearchQuery)); this.WhenAny(x => x.SelectedAssignee, x => x.Value) .Where(x => PullRequests != null && x != EmptyUser) .Subscribe(a => UpdateFilter(SelectedState, a, SelectedAuthor, SearchQuery)); this.WhenAny(x => x.SelectedAuthor, x => x.Value) .Where(x => PullRequests != null && x != EmptyUser) .Subscribe(a => UpdateFilter(SelectedState, SelectedAssignee, a, SearchQuery)); this.WhenAny(x => x.SearchQuery, x => x.Value) .Where(x => PullRequests != null) .Subscribe(f => UpdateFilter(SelectedState, SelectedAssignee, SelectedAuthor, f)); this.WhenAnyValue(x => x.SelectedRepository) .Skip(1) .Subscribe(_ => ResetAndLoad()); OpenPullRequest = ReactiveCommand.Create(); OpenPullRequest.Subscribe(DoOpenPullRequest); CreatePullRequest = ReactiveCommand.Create(); CreatePullRequest.Subscribe(_ => DoCreatePullRequest()); OpenPullRequestOnGitHub = ReactiveCommand.Create(); OpenPullRequestOnGitHub.Subscribe(x => DoOpenPullRequestOnGitHub((int)x)); constructing = false; }
public void MultipleSortingAndFiltering() { var expectedTotal = 20; var rnd = new Random(214748364); var titles1 = Enumerable.Range(1, expectedTotal).Select(x => ((char)('a' + x)).ToString()).ToList(); var dates1 = Enumerable.Range(1, expectedTotal).Select(x => Now + TimeSpan.FromMinutes(x)).ToList(); var idstack1 = new Stack<int>(Enumerable.Range(1, expectedTotal).OrderBy(rnd.Next)); var datestack1 = new Stack<DateTimeOffset>(dates1); var titlestack1 = new Stack<string>(titles1.OrderBy(_ => rnd.Next())); var titles2 = Enumerable.Range(1, expectedTotal).Select(x => ((char)('c' + x)).ToString()).ToList(); var dates2 = Enumerable.Range(1, expectedTotal).Select(x => Now + TimeSpan.FromMinutes(x)).ToList(); var idstack2 = new Stack<int>(Enumerable.Range(1, expectedTotal).OrderBy(rnd.Next)); var datestack2 = new Stack<DateTimeOffset>(new List<DateTimeOffset>() { dates2[2], dates2[0], dates2[1], dates2[3], dates2[5], dates2[9], dates2[15], dates2[6], dates2[7], dates2[8], dates2[13], dates2[10], dates2[16], dates2[11], dates2[12], dates2[14], dates2[17], dates2[18], dates2[19], dates2[4], }); var titlestack2 = new Stack<string>(titles2.OrderBy(_ => rnd.Next())); var list1 = Observable.Defer(() => Enumerable.Range(1, expectedTotal) .OrderBy(rnd.Next) .Select(x => new Thing(idstack1.Pop(), titlestack1.Pop(), datestack1.Pop())) .ToObservable()) .Replay() .RefCount(); var list2 = Observable.Defer(() => Enumerable.Range(1, expectedTotal) .OrderBy(rnd.Next) .Select(x => new Thing(idstack2.Pop(), titlestack2.Pop(), datestack2.Pop())) .ToObservable()) .Replay() .RefCount(); var col = new TrackingCollection<Thing>( list1.Concat(list2), OrderedComparer<Thing>.OrderByDescending(x => x.CreatedAt).Compare, (item, idx, list) => idx < 5 ) { ProcessingDelay = TimeSpan.Zero }; var count = 0; var evt = new ManualResetEvent(false); col.Subscribe(t => { if (++count == expectedTotal * 2) evt.Set(); }, () => { }); evt.WaitOne(); evt.Reset(); // it's initially sorted by date, so id list should not match CollectionAssert.AreNotEqual(list1.Select(x => x.Number).ToEnumerable(), list2.Select(x => x.Number).ToEnumerable()); var sortlist = list1.ToEnumerable().ToArray(); Array.Sort(sortlist, new LambdaComparer<Thing>(OrderedComparer<Thing>.OrderByDescending(x => x.CreatedAt).Compare)); CollectionAssert.AreEqual(sortlist.Take(5), col); col.SetComparer(OrderedComparer<Thing>.OrderBy(x => x.Number).Compare); sortlist = list1.ToEnumerable().ToArray(); Array.Sort(sortlist, new LambdaComparer<Thing>(OrderedComparer<Thing>.OrderBy(x => x.Number).Compare)); CollectionAssert.AreEqual(sortlist.Take(5), col); col.SetComparer(OrderedComparer<Thing>.OrderBy(x => x.CreatedAt).Compare); sortlist = list1.ToEnumerable().ToArray(); Array.Sort(sortlist, new LambdaComparer<Thing>(OrderedComparer<Thing>.OrderBy(x => x.CreatedAt).Compare)); CollectionAssert.AreEqual(sortlist.Take(5), col); col.SetComparer(OrderedComparer<Thing>.OrderByDescending(x => x.Title).Compare); sortlist = list1.ToEnumerable().ToArray(); Array.Sort(sortlist, new LambdaComparer<Thing>(OrderedComparer<Thing>.OrderByDescending(x => x.Title).Compare)); CollectionAssert.AreEqual(sortlist.Take(5), col); col.SetComparer(OrderedComparer<Thing>.OrderBy(x => x.Title).Compare); sortlist = list1.ToEnumerable().ToArray(); Array.Sort(sortlist, new LambdaComparer<Thing>(OrderedComparer<Thing>.OrderBy(x => x.Title).Compare)); CollectionAssert.AreEqual(sortlist.Take(5), col); col.Dispose(); }
public async Task ExpiredIndexReturnsLive() { var expected = 5; var username = "******"; var reponame = "repo"; var cache = new InMemoryBlobCache(); var apiClient = Substitute.For <IApiClient>(); var modelService = new ModelService(apiClient, cache, Substitute.For <IAvatarProvider>()); var user = CreateOctokitUser(username); apiClient.GetUser().Returns(Observable.Return(new UserAndScopes(user, null))); apiClient.GetOrganizations().Returns(Observable.Empty <Organization>()); var act = modelService.GetAccounts().ToEnumerable().First().First(); var repo = Substitute.For <ILocalRepositoryModel>(); repo.Name.Returns(reponame); repo.CloneUrl.Returns(new UriString("https://github.com/" + username + "/" + reponame)); var indexKey = string.Format(CultureInfo.InvariantCulture, "{0}|{1}:{2}", CacheIndex.PRPrefix, user.Login, repo.Name); var prcache = Enumerable.Range(1, expected) .Select(id => CreatePullRequest(user, id, ItemState.Open, "Cache " + id, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow)); // seed the cache prcache .Select(item => new ModelService.PullRequestCacheItem(item)) .Select(item => item.Save <ModelService.PullRequestCacheItem>(cache, indexKey).ToEnumerable().First()) .SelectMany(item => CacheIndex.AddAndSaveToIndex(cache, indexKey, item).ToEnumerable()) .ToList(); // expire the index var indexobj = await cache.GetObject <CacheIndex>(indexKey); indexobj.UpdatedAt = DateTimeOffset.UtcNow - TimeSpan.FromMinutes(6); await cache.InsertObject(indexKey, indexobj); var prlive = Observable.Range(1, expected) .Select(id => CreatePullRequest(user, id, ItemState.Open, "Live " + id, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow)) .DelaySubscription(TimeSpan.FromMilliseconds(10)); apiClient.GetPullRequestsForRepository(user.Login, repo.Name).Returns(prlive); await modelService.InsertUser(new AccountCacheItem(user)); ITrackingCollection <IPullRequestModel> col = new TrackingCollection <IPullRequestModel>(); modelService.GetPullRequests(repo, col); col.ProcessingDelay = TimeSpan.Zero; var count = 0; var done = new ReplaySubject <Unit>(); done.OnNext(Unit.Default); done.Subscribe(); col.Subscribe(t => { if (++count == expected * 2) { done.OnCompleted(); } }, () => { }); await done; Assert.Collection(col, col.Select(x => new Action <IPullRequestModel>(t => Assert.True(x.Title.StartsWith("Live")))).ToArray()); }
public void ProcessingDelayPingsRegularly() { int count, total; count = total = 400; var list1 = new List<Thing>(Enumerable.Range(1, count).Select(i => GetThing(i, i, count - i)).ToList()); var col = new TrackingCollection<Thing>( list1.ToObservable().Delay(TimeSpan.FromMilliseconds(10)), OrderedComparer<Thing>.OrderByDescending(x => x.UpdatedAt).Compare); col.ProcessingDelay = TimeSpan.FromMilliseconds(10); var sub = new Subject<Thing>(); var times = new List<DateTimeOffset>(); sub.Subscribe(t => { times.Add(DateTimeOffset.UtcNow); }); count = 0; var evt = new ManualResetEvent(false); col.Subscribe(t => { sub.OnNext(t); if (++count == list1.Count) { sub.OnCompleted(); evt.Set(); } }, () => { }); evt.WaitOne(); evt.Reset(); Assert.AreEqual(total, col.Count); CollectionAssert.AreEqual(col, list1); long totalTime = 0; for (var j = 1; j < times.Count; j++) totalTime += (times[j] - times[j - 1]).Ticks; var avg = TimeSpan.FromTicks(totalTime / times.Count).TotalMilliseconds; Assert.GreaterOrEqual(avg, 9); Assert.LessOrEqual(avg, 12); col.Dispose(); }
public async Task ExpiredIndexClearsItemsAsync() { var expected = 5; var username = "******"; var reponame = "repo"; var cache = new InMemoryBlobCache(); var apiClient = Substitute.For <IApiClient>(); var modelService = CreateTarget(apiClient: apiClient, hostCache: cache); var user = CreateOctokitUser(username); apiClient.GetUser().Returns(Observable.Return(user)); apiClient.GetOrganizations().Returns(Observable.Empty <Organization>()); var act = modelService.GetAccounts().ToEnumerable().First().First(); var repo = Substitute.For <LocalRepositoryModel>(); repo.Name.Returns(reponame); repo.Owner.Returns(user.Login); repo.CloneUrl.Returns(new UriString("https://github.com/" + username + "/" + reponame)); var indexKey = string.Format(CultureInfo.InvariantCulture, "{0}|{1}:{2}", CacheIndex.PRPrefix, user.Login, repo.Name); var prcache = Enumerable.Range(1, expected) .Select(id => CreatePullRequest(user, id, ItemState.Open, "Cache " + id, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow)); // seed the cache prcache .Select(item => new ModelService.PullRequestCacheItem(item)) .Select(item => item.Save <ModelService.PullRequestCacheItem>(cache, indexKey).ToEnumerable().First()) .SelectMany(item => CacheIndex.AddAndSaveToIndex(cache, indexKey, item).ToEnumerable()) .ToList(); // expire the index var indexobj = await cache.GetObject <CacheIndex>(indexKey); indexobj.UpdatedAt = DateTimeOffset.UtcNow - TimeSpan.FromMinutes(6); await cache.InsertObject(indexKey, indexobj); var prlive = Observable.Range(5, expected) .Select(id => CreatePullRequest(user, id, ItemState.Open, "Live " + id, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow, 0)) .DelaySubscription(TimeSpan.FromMilliseconds(10)); apiClient.GetPullRequestsForRepository(user.Login, repo.Name).Returns(prlive); await modelService.InsertUser(new AccountCacheItem(user)); ITrackingCollection <IPullRequestModel> col = new TrackingCollection <IPullRequestModel>(); modelService.GetPullRequests(repo, col); col.ProcessingDelay = TimeSpan.Zero; var count = 0; var done = new ReplaySubject <Unit>(); done.OnNext(Unit.Default); done.Subscribe(); col.Subscribe(t => { // we get all the items from the cache (items 1-5), all the items from the live (items 5-9), // and 4 deletions (items 1-4) because the cache expired the items that were not // a part of the live data if (++count == 14) { done.OnCompleted(); } }, () => { }); await done; Assert.That(5, Is.EqualTo(col.Count)); /**Assert.Collection(col, * t => { Assert.StartsWith("Live", t.Title); Assert.Equal(5, t.Number); }, * t => { Assert.StartsWith("Live", t.Title); Assert.Equal(6, t.Number); }, * t => { Assert.StartsWith("Live", t.Title); Assert.Equal(7, t.Number); }, * t => { Assert.StartsWith("Live", t.Title); Assert.Equal(8, t.Number); }, * t => { Assert.StartsWith("Live", t.Title); Assert.Equal(9, t.Number); } * );*/ }
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 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 void SortingTestWithFilterBetween6And12() { var source = new Subject<Thing>(); var col = new TrackingCollection<Thing>( source, OrderedComparer<Thing>.OrderByDescending(x => x.UpdatedAt).Compare, (item, position, list) => item.UpdatedAt.Minute >= 6 && item.UpdatedAt.Minute <= 12); col.ProcessingDelay = TimeSpan.Zero; var count = 0; var expectedCount = 0; var evt = new ManualResetEvent(false); col.Subscribe(t => { if (++count == expectedCount) evt.Set(); }, () => { }); // testing ADD expectedCount = 1; // add a thing with UpdatedAt=0:0:10 Add(source, GetThing(1, 10)); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(col, new List<Thing> { GetThing(1, 10), }); // testing ADD // add another thing with UpdatedAt=0:0:2 expectedCount = 2; Add(source, GetThing(2, 2)); evt.WaitOne(); evt.Reset(); // check that list has {0:0:10,0:0:2} CollectionAssert.AreEqual(col, new List<Thing> { GetThing(1, 10), }); // testing MOVE // replace thing with UpdatedAt=0:0:2 to UpdatedAt=0:0:12 expectedCount = 3; Add(source, GetThing(2, 12)); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(col, new List<Thing> { GetThing(2, 12), GetThing(1, 10), }); // testing INSERT expectedCount = 4; Add(source, GetThing(3, 11)); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(col, new List<Thing> { GetThing(2, 12), GetThing(3, 11), GetThing(1, 10), }); // testing INSERT expectedCount = 7; Add(source, GetThing(4, 5)); Add(source, GetThing(5, 14)); Add(source, GetThing(6, 13)); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(col, new List<Thing> { GetThing(2, 12), GetThing(3, 11), GetThing(1, 10), }); // testing MOVE from top to middle expectedCount = 8; Add(source, GetThing(5, 5)); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(col, new List<Thing> { GetThing(2, 12), GetThing(3, 11), GetThing(1, 10), }); // testing MOVE from top to bottom expectedCount = 9; Add(source, GetThing(6, 4)); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(col, new List<Thing> { GetThing(2, 12), GetThing(3, 11), GetThing(1, 10), }); // testing MOVE from bottom to top expectedCount = 10; Add(source, GetThing(6, 14)); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(col, new List<Thing> { GetThing(2, 12), GetThing(3, 11), GetThing(1, 10), }); // testing MOVE from middle bottom to middle top expectedCount = 11; Add(source, GetThing(3, 14)); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(col, new List<Thing> { GetThing(2, 12), GetThing(1, 10), }); // testing MOVE from middle top to middle bottom expectedCount = 12; Add(source, GetThing(2, 9)); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(col, new List<Thing> { GetThing(1, 10), GetThing(2, 9), }); // testing MOVE from middle bottom to middle top more than 1 position expectedCount = 13; Add(source, GetThing(5, 12)); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(col, new List<Thing> { GetThing(5, 12), GetThing(1, 10), GetThing(2, 9), }); expectedCount = 14; col.RemoveItem(GetThing(1, 10)); evt.WaitOne(); evt.Reset(); CollectionAssert.AreEqual(col, new List<Thing> { GetThing(5, 12), GetThing(2, 9), }); col.Dispose(); }
public async Task ExpiredIndexClearsItems() { var expected = 5; var username = "******"; var reponame = "repo"; var cache = new InMemoryBlobCache(); var apiClient = Substitute.For<IApiClient>(); var modelService = new ModelService(apiClient, cache, Substitute.For<IAvatarProvider>()); var user = CreateOctokitUser(username); apiClient.GetUser().Returns(Observable.Return(new UserAndScopes(user, null))); apiClient.GetOrganizations().Returns(Observable.Empty<Organization>()); var act = modelService.GetAccounts().ToEnumerable().First().First(); var repo = Substitute.For<ILocalRepositoryModel>(); repo.Name.Returns(reponame); repo.CloneUrl.Returns(new UriString("https://github.com/" + username + "/" + reponame)); var indexKey = string.Format(CultureInfo.InvariantCulture, "{0}|{1}:{2}", CacheIndex.PRPrefix, user.Login, repo.Name); var prcache = Enumerable.Range(1, expected) .Select(id => CreatePullRequest(user, id, ItemState.Open, "Cache " + id, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow)); // seed the cache prcache .Select(item => new ModelService.PullRequestCacheItem(item)) .Select(item => item.Save<ModelService.PullRequestCacheItem>(cache, indexKey).ToEnumerable().First()) .SelectMany(item => CacheIndex.AddAndSaveToIndex(cache, indexKey, item).ToEnumerable()) .ToList(); // expire the index var indexobj = await cache.GetObject<CacheIndex>(indexKey); indexobj.UpdatedAt = DateTimeOffset.UtcNow - TimeSpan.FromMinutes(6); await cache.InsertObject(indexKey, indexobj); var prlive = Observable.Range(5, expected) .Select(id => CreatePullRequest(user, id, ItemState.Open, "Live " + id, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow, 0)) .DelaySubscription(TimeSpan.FromMilliseconds(10)); apiClient.GetPullRequestsForRepository(user.Login, repo.Name).Returns(prlive); await modelService.InsertUser(new AccountCacheItem(user)); ITrackingCollection<IPullRequestModel> col = new TrackingCollection<IPullRequestModel>(); modelService.GetPullRequests(repo, col); col.ProcessingDelay = TimeSpan.Zero; var count = 0; var done = new ReplaySubject<Unit>(); done.OnNext(Unit.Default); done.Subscribe(); col.Subscribe(t => { // we get all the items from the cache (items 1-5), all the items from the live (items 5-9), // and 4 deletions (items 1-4) because the cache expired the items that were not // a part of the live data if (++count == 14) { done.OnCompleted(); } }, () => { }); await done; Assert.Equal(5, col.Count); Assert.Collection(col, t => { Assert.True(t.Title.StartsWith("Live")); Assert.Equal(5, t.Number); }, t => { Assert.True(t.Title.StartsWith("Live")); Assert.Equal(6, t.Number); }, t => { Assert.True(t.Title.StartsWith("Live")); Assert.Equal(7, t.Number); }, t => { Assert.True(t.Title.StartsWith("Live")); Assert.Equal(8, t.Number); }, t => { Assert.True(t.Title.StartsWith("Live")); Assert.Equal(9, t.Number); } ); }