Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.

Commit 6f99a4c

Browse files
authored
Merge pull request #594 from StanleyGoldman/fixes/408-avatar-disappear-pr-list-attempt-2
Fixing Account's CopyFrom
2 parents 58a5cfb + ce0425d commit 6f99a4c

File tree

3 files changed

+160
-1
lines changed

3 files changed

+160
-1
lines changed

src/GitHub.App/Models/Account.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ namespace GitHub.Models
1414
public class Account : ReactiveObject, IAccount
1515
{
1616
BitmapSource avatar;
17+
IObservable<BitmapSource> bitmapSource;
18+
IDisposable bitmapSourceSubscription;
1719

1820
public Account(
1921
string login,
@@ -30,8 +32,10 @@ public Account(
3032
PrivateReposInPlan = privateRepositoryInPlanCount;
3133
IsOnFreePlan = privateRepositoryInPlanCount == 0;
3234
HasMaximumPrivateRepositories = OwnedPrivateRepos >= PrivateReposInPlan;
35+
this.bitmapSource = bitmapSource;
3336

34-
bitmapSource.ObserveOn(RxApp.MainThreadScheduler)
37+
bitmapSourceSubscription = bitmapSource
38+
.ObserveOn(RxApp.MainThreadScheduler)
3539
.Subscribe(x => Avatar = x);
3640
}
3741

@@ -80,6 +84,16 @@ public void CopyFrom(IAccount other)
8084
IsOnFreePlan = other.IsOnFreePlan;
8185
HasMaximumPrivateRepositories = other.HasMaximumPrivateRepositories;
8286
Avatar = other.Avatar;
87+
88+
var otherAccount = other as Account;
89+
if (otherAccount != null)
90+
{
91+
bitmapSourceSubscription.Dispose();
92+
93+
bitmapSourceSubscription = otherAccount.bitmapSource
94+
.ObserveOn(RxApp.MainThreadScheduler)
95+
.Subscribe(x => Avatar = x);
96+
}
8397
}
8498

8599
public override bool Equals([AllowNull]object obj)
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
using System;
2+
using System.IO;
3+
using System.Linq;
4+
using System.Reactive.Linq;
5+
using System.Reactive.Subjects;
6+
using System.Threading;
7+
using System.Windows.Media.Imaging;
8+
using GitHub.Collections;
9+
using GitHub.Models;
10+
using GitHub.Services;
11+
using ReactiveUI;
12+
using Xunit;
13+
14+
namespace UnitTests.GitHub.App.Models
15+
{
16+
public class AccountModelTests : TestBaseClass
17+
{
18+
[Fact]
19+
public void CopyFromDoesNotLoseAvatar()
20+
{
21+
var userImage = AvatarProvider.CreateBitmapImage("pack://application:,,,/GitHub.App;component/Images/default_user_avatar.png");
22+
var orgImage = AvatarProvider.CreateBitmapImage("pack://application:,,,/GitHub.App;component/Images/default_org_avatar.png");
23+
24+
var initialBitmapImageSubject = new Subject<BitmapImage>();
25+
26+
var collectionEvent = new ManualResetEvent(false);
27+
var avatarPropertyEvent = new ManualResetEvent(false);
28+
29+
//Creating an initial account with an observable that returns immediately
30+
const string login = "foo";
31+
const int initialOwnedPrivateRepositoryCount = 1;
32+
33+
var initialAccount = new Account(login, true, false, initialOwnedPrivateRepositoryCount, 0, initialBitmapImageSubject);
34+
35+
//Creating the test collection
36+
var col = new TrackingCollection<IAccount>(Observable.Empty<IAccount>(), OrderedComparer<IAccount>.OrderByDescending(x => x.Login).Compare);
37+
col.Subscribe(account =>
38+
{
39+
collectionEvent.Set();
40+
}, () => { });
41+
42+
//Adding that account to the TrackingCollection
43+
col.AddItem(initialAccount);
44+
45+
//Waiting for the collection add the item
46+
collectionEvent.WaitOne();
47+
collectionEvent.Reset();
48+
49+
//Checking some initial properties
50+
Assert.Equal(login, col[0].Login);
51+
Assert.Equal(initialOwnedPrivateRepositoryCount, col[0].OwnedPrivateRepos);
52+
53+
//Demonstrating that the avatar is not yet present
54+
Assert.Null(col[0].Avatar);
55+
56+
//Adding a listener to check for the changing of the Avatar property
57+
initialAccount.Changed.Subscribe(args =>
58+
{
59+
if (args.PropertyName == "Avatar")
60+
{
61+
avatarPropertyEvent.Set();
62+
}
63+
});
64+
65+
//Providing the first avatar
66+
initialBitmapImageSubject.OnNext(userImage);
67+
initialBitmapImageSubject.OnCompleted();
68+
69+
//Waiting for the avatar to be added
70+
avatarPropertyEvent.WaitOne();
71+
avatarPropertyEvent.Reset();
72+
73+
//Demonstrating that the avatar is present
74+
Assert.NotNull(col[0].Avatar);
75+
Assert.True(BitmapSourcesAreEqual(col[0].Avatar, userImage));
76+
Assert.False(BitmapSourcesAreEqual(col[0].Avatar, orgImage));
77+
78+
//Creating an account update
79+
const int updatedOwnedPrivateRepositoryCount = 2;
80+
var updatedBitmapImageSubject = new Subject<BitmapImage>();
81+
var updatedAccount = new Account(login, true, false, updatedOwnedPrivateRepositoryCount, 0, updatedBitmapImageSubject);
82+
83+
//Updating the account in the collection
84+
col.AddItem(updatedAccount);
85+
86+
//Waiting for the collection to process the update
87+
collectionEvent.WaitOne();
88+
collectionEvent.Reset();
89+
90+
//Providing the second avatar
91+
updatedBitmapImageSubject.OnNext(orgImage);
92+
updatedBitmapImageSubject.OnCompleted();
93+
94+
//Waiting for the delayed bitmap image observable
95+
avatarPropertyEvent.WaitOne();
96+
avatarPropertyEvent.Reset();
97+
98+
//Login is the id, so that should be the same
99+
Assert.Equal(login, col[0].Login);
100+
101+
//CopyFrom() should have updated this field
102+
Assert.Equal(updatedOwnedPrivateRepositoryCount, col[0].OwnedPrivateRepos);
103+
104+
//CopyFrom() should not cause a race condition here
105+
Assert.NotNull(col[0].Avatar);
106+
Assert.True(BitmapSourcesAreEqual(col[0].Avatar, orgImage));
107+
Assert.False(BitmapSourcesAreEqual(col[0].Avatar, userImage));
108+
}
109+
110+
public static bool BitmapSourcesAreEqual(BitmapSource image1, BitmapSource image2)
111+
{
112+
if (image1 == null || image2 == null)
113+
{
114+
return false;
115+
}
116+
117+
return BitmapSourceToBytes(image1).SequenceEqual(BitmapSourceToBytes(image2));
118+
}
119+
120+
public static byte[] BitmapSourceToBytes(BitmapSource image)
121+
{
122+
byte[] data = new byte[] { };
123+
if (image != null)
124+
{
125+
try
126+
{
127+
var encoder = new BmpBitmapEncoder();
128+
encoder.Frames.Add(BitmapFrame.Create(image));
129+
using (MemoryStream ms = new MemoryStream())
130+
{
131+
encoder.Save(ms);
132+
data = ms.ToArray();
133+
}
134+
return data;
135+
}
136+
catch (Exception ex)
137+
{
138+
}
139+
}
140+
141+
return data;
142+
}
143+
}
144+
}

src/UnitTests/UnitTests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@
160160
<Compile Include="GitHub.App\Caches\CredentialCacheTests.cs" />
161161
<Compile Include="GitHub.App\Caches\ImageCacheTests.cs" />
162162
<Compile Include="GitHub.App\Controllers\UIProviderTests.cs" />
163+
<Compile Include="GitHub.App\Models\AccountModelTests.cs" />
163164
<Compile Include="GitHub.App\Models\ModelServiceTests.cs" />
164165
<Compile Include="GitHub.App\Controllers\UIControllerTests.cs" />
165166
<Compile Include="GitHub.App\Models\PullRequestModelTests.cs" />

0 commit comments

Comments
 (0)