99using NUnit . Framework ;
1010using NSubstitute ;
1111using Microsoft . VisualStudio . TeamFoundation . Git . Extensibility ;
12+ using System . Threading . Tasks ;
13+ using System . Linq ;
14+ using GitHub . TeamFoundation . Services ;
1215
1316public class VSGitExtTests
1417{
@@ -171,9 +174,32 @@ public void ExceptionRefreshingRepositories_ReturnsEmptyList()
171174 repoFactory . Received ( 1 ) . Create ( repoPath ) ;
172175 Assert . That ( activeRepositories . Count , Is . EqualTo ( 0 ) ) ;
173176 }
177+
178+ [ Test ]
179+ public async Task ThreadingStressTest ( )
180+ {
181+ for ( var i = 0 ; i < 100 ; ++ i )
182+ {
183+ var gitExt = new MockGitExt ( ) ;
184+ var repoFactory = new MockRepositoryFactory ( ) ;
185+ var target = CreateVSGitExt ( gitExt : gitExt , repoFactory : repoFactory ) ;
186+ var activeRepositories1 = CreateActiveRepositories ( "repo1" ) ;
187+ var activeRepositories2 = CreateActiveRepositories ( "repo2" ) ;
188+ var task1 = Task . Run ( ( ) => gitExt . ActiveRepositories = activeRepositories1 ) ;
189+ await Task . Delay ( 1 ) ;
190+ var task2 = Task . Run ( ( ) => gitExt . ActiveRepositories = activeRepositories2 ) ;
191+
192+ await Task . WhenAll ( task1 , task2 ) ;
193+
194+ Assert . That (
195+ target . ActiveRepositories . Single ( ) . LocalPath ,
196+ Is . EqualTo ( "repo2" ) ,
197+ $ "Failed at iteration { i } ") ;
198+ }
199+ }
174200 }
175201
176- static IReadOnlyList < IGitRepositoryInfo > CreateActiveRepositories ( IList < string > repositoryPaths )
202+ static IReadOnlyList < IGitRepositoryInfo > CreateActiveRepositories ( params string [ ] repositoryPaths )
177203 {
178204 var repositories = new List < IGitRepositoryInfo > ( ) ;
179205 foreach ( var repositoryPath in repositoryPaths )
@@ -203,9 +229,8 @@ static VSGitExt CreateVSGitExt(IVSUIContext context = null, IGitExt gitExt = nul
203229 return vsGitExt ;
204230 }
205231
206- static IGitExt CreateGitExt ( IList < string > repositoryPaths = null )
232+ static IGitExt CreateGitExt ( params string [ ] repositoryPaths )
207233 {
208- repositoryPaths = repositoryPaths ?? Array . Empty < string > ( ) ;
209234 var gitExt = Substitute . For < IGitExt > ( ) ;
210235 var repoList = CreateActiveRepositories ( repositoryPaths ) ;
211236 gitExt . ActiveRepositories . Returns ( repoList ) ;
@@ -218,4 +243,34 @@ static IVSUIContext CreateVSUIContext(bool isActive)
218243 context . IsActive . Returns ( isActive ) ;
219244 return context ;
220245 }
246+
247+ class MockGitExt : IGitExt
248+ {
249+ IReadOnlyList < IGitRepositoryInfo > activeRepositories = new IGitRepositoryInfo [ 0 ] ;
250+
251+ public IReadOnlyList < IGitRepositoryInfo > ActiveRepositories
252+ {
253+ get { return activeRepositories ; }
254+ set
255+ {
256+ if ( activeRepositories != value )
257+ {
258+ activeRepositories = value ;
259+ PropertyChanged ? . Invoke ( this , new PropertyChangedEventArgs ( nameof ( ActiveRepositories ) ) ) ;
260+ }
261+ }
262+ }
263+
264+ public event PropertyChangedEventHandler PropertyChanged ;
265+ }
266+
267+ class MockRepositoryFactory : ILocalRepositoryModelFactory
268+ {
269+ public ILocalRepositoryModel Create ( string localPath )
270+ {
271+ var result = Substitute . For < ILocalRepositoryModel > ( ) ;
272+ result . LocalPath . Returns ( localPath ) ;
273+ return result ;
274+ }
275+ }
221276}
0 commit comments