1- using GVFS . Common . Prefetch ;
1+ using GVFS . Common ;
2+ using GVFS . Common . Prefetch ;
23using GVFS . Tests . Should ;
4+ using GVFS . UnitTests . Mock ;
5+ using GVFS . UnitTests . Mock . Common ;
36using GVFS . UnitTests . Mock . FileSystem ;
47using NUnit . Framework ;
8+ using System . Collections . Generic ;
59using System . IO ;
610
711namespace GVFS . UnitTests . Prefetch
812{
913 [ TestFixture ]
1014 public class BlobPrefetcherTests
1115 {
16+ private const string MockCacheFileName = "mock:\\ prefetch-cache.dat" ;
17+
1218 [ TestCase ]
1319 public void AppendToNewlineSeparatedFileTests ( )
1420 {
@@ -29,5 +35,207 @@ public void AppendToNewlineSeparatedFileTests()
2935 BlobPrefetcher . AppendToNewlineSeparatedFile ( fileSystem , testFileName , "expected line 2" ) ;
3036 fileSystem . ReadAllText ( testFileName ) . ShouldEqual ( "existing content\n expected line 2\n " ) ;
3137 }
38+
39+ [ TestCase ]
40+ public void ComputeCacheKeyIsDeterministic ( )
41+ {
42+ List < string > files = new List < string > { "src/a.cs" , "src/b.cs" } ;
43+ List < string > folders = new List < string > { "src/dir1" , "src/dir2" } ;
44+
45+ string key1 = BlobPrefetcher . ComputeCacheKey ( files , folders , hydrate : false ) ;
46+ string key2 = BlobPrefetcher . ComputeCacheKey ( files , folders , hydrate : false ) ;
47+
48+ key1 . ShouldEqual ( key2 ) ;
49+ }
50+
51+ [ TestCase ]
52+ public void ComputeCacheKeyDiffersForDifferentFiles ( )
53+ {
54+ List < string > files1 = new List < string > { "src/a.cs" } ;
55+ List < string > files2 = new List < string > { "src/b.cs" } ;
56+ List < string > folders = new List < string > { "src/dir1" } ;
57+
58+ string key1 = BlobPrefetcher . ComputeCacheKey ( files1 , folders , hydrate : false ) ;
59+ string key2 = BlobPrefetcher . ComputeCacheKey ( files2 , folders , hydrate : false ) ;
60+
61+ key1 . ShouldNotEqual ( key2 ) ;
62+ }
63+
64+ [ TestCase ]
65+ public void ComputeCacheKeyDiffersForDifferentFolders ( )
66+ {
67+ List < string > files = new List < string > { "src/a.cs" } ;
68+ List < string > folders1 = new List < string > { "src/dir1" } ;
69+ List < string > folders2 = new List < string > { "src/dir2" } ;
70+
71+ string key1 = BlobPrefetcher . ComputeCacheKey ( files , folders1 , hydrate : false ) ;
72+ string key2 = BlobPrefetcher . ComputeCacheKey ( files , folders2 , hydrate : false ) ;
73+
74+ key1 . ShouldNotEqual ( key2 ) ;
75+ }
76+
77+ [ TestCase ]
78+ public void ComputeCacheKeyDiffersForHydrateFlag ( )
79+ {
80+ List < string > files = new List < string > { "src/a.cs" } ;
81+ List < string > folders = new List < string > { "src/dir1" } ;
82+
83+ string key1 = BlobPrefetcher . ComputeCacheKey ( files , folders , hydrate : false ) ;
84+ string key2 = BlobPrefetcher . ComputeCacheKey ( files , folders , hydrate : true ) ;
85+
86+ key1 . ShouldNotEqual ( key2 ) ;
87+ }
88+
89+ [ TestCase ]
90+ public void ComputeCacheKeyIsOrderIndependent ( )
91+ {
92+ List < string > filesA = new List < string > { "src/b.cs" , "src/a.cs" } ;
93+ List < string > filesB = new List < string > { "src/a.cs" , "src/b.cs" } ;
94+ List < string > folders = new List < string > { "src/dir1" } ;
95+
96+ string key1 = BlobPrefetcher . ComputeCacheKey ( filesA , folders , hydrate : false ) ;
97+ string key2 = BlobPrefetcher . ComputeCacheKey ( filesB , folders , hydrate : false ) ;
98+
99+ key1 . ShouldEqual ( key2 ) ;
100+ }
101+
102+ [ TestCase ]
103+ public void ComputeCacheKeyFolderOrderIndependent ( )
104+ {
105+ List < string > files = new List < string > { "src/a.cs" } ;
106+ List < string > foldersA = new List < string > { "src/dir2" , "src/dir1" } ;
107+ List < string > foldersB = new List < string > { "src/dir1" , "src/dir2" } ;
108+
109+ string key1 = BlobPrefetcher . ComputeCacheKey ( files , foldersA , hydrate : false ) ;
110+ string key2 = BlobPrefetcher . ComputeCacheKey ( files , foldersB , hydrate : false ) ;
111+
112+ key1 . ShouldEqual ( key2 ) ;
113+ }
114+
115+ [ TestCase ]
116+ public void IsNoopPrefetchReturnsFalseWhenCacheIsNull ( )
117+ {
118+ MockTracer tracer = new MockTracer ( ) ;
119+ List < string > files = new List < string > { "src/a.cs" } ;
120+ List < string > folders = new List < string > { "src/dir1" } ;
121+
122+ BlobPrefetcher . IsNoopPrefetch ( tracer , null , "abc123" , files , folders , false ) . ShouldEqual ( false ) ;
123+ }
124+
125+ [ TestCase ]
126+ public void IsNoopPrefetchReturnsFalseWhenCacheIsEmpty ( )
127+ {
128+ MockTracer tracer = new MockTracer ( ) ;
129+ List < string > files = new List < string > { "src/a.cs" } ;
130+ List < string > folders = new List < string > { "src/dir1" } ;
131+ FileBasedDictionary < string , string > cache = CreateEmptyCache ( ) ;
132+
133+ BlobPrefetcher . IsNoopPrefetch ( tracer , cache , "abc123" , files , folders , false ) . ShouldEqual ( false ) ;
134+ }
135+
136+ [ TestCase ]
137+ public void IsNoopPrefetchReturnsTrueOnCacheHit ( )
138+ {
139+ MockTracer tracer = new MockTracer ( ) ;
140+ List < string > files = new List < string > { "src/a.cs" } ;
141+ List < string > folders = new List < string > { "src/dir1" } ;
142+ string commitId = "abc123" ;
143+
144+ FileBasedDictionary < string , string > cache = CreateEmptyCache ( ) ;
145+ string cacheKey = BlobPrefetcher . ComputeCacheKey ( files , folders , hydrate : false ) ;
146+ cache . SetValueAndFlush ( cacheKey , commitId ) ;
147+
148+ BlobPrefetcher . IsNoopPrefetch ( tracer , cache , commitId , files , folders , false ) . ShouldEqual ( true ) ;
149+ }
150+
151+ [ TestCase ]
152+ public void IsNoopPrefetchReturnsFalseWhenCommitIdChanged ( )
153+ {
154+ MockTracer tracer = new MockTracer ( ) ;
155+ List < string > files = new List < string > { "src/a.cs" } ;
156+ List < string > folders = new List < string > { "src/dir1" } ;
157+
158+ FileBasedDictionary < string , string > cache = CreateEmptyCache ( ) ;
159+ string cacheKey = BlobPrefetcher . ComputeCacheKey ( files , folders , hydrate : false ) ;
160+ cache . SetValueAndFlush ( cacheKey , "oldcommit" ) ;
161+
162+ BlobPrefetcher . IsNoopPrefetch ( tracer , cache , "newcommit" , files , folders , false ) . ShouldEqual ( false ) ;
163+ }
164+
165+ [ TestCase ]
166+ public void IsNoopPrefetchSupportsMultipleEntries ( )
167+ {
168+ MockTracer tracer = new MockTracer ( ) ;
169+ List < string > filesA = new List < string > { "src/a.cs" } ;
170+ List < string > filesB = new List < string > { "src/b.cs" } ;
171+ List < string > folders = new List < string > { "src/dir1" } ;
172+ string commitId = "abc123" ;
173+
174+ FileBasedDictionary < string , string > cache = CreateEmptyCache ( ) ;
175+
176+ string keyA = BlobPrefetcher . ComputeCacheKey ( filesA , folders , hydrate : false ) ;
177+ cache . SetValueAndFlush ( keyA , commitId ) ;
178+
179+ string keyB = BlobPrefetcher . ComputeCacheKey ( filesB , folders , hydrate : false ) ;
180+ cache . SetValueAndFlush ( keyB , commitId ) ;
181+
182+ // Both should hit
183+ BlobPrefetcher . IsNoopPrefetch ( tracer , cache , commitId , filesA , folders , false ) . ShouldEqual ( true ) ;
184+ BlobPrefetcher . IsNoopPrefetch ( tracer , cache , commitId , filesB , folders , false ) . ShouldEqual ( true ) ;
185+
186+ // A third pattern should miss
187+ List < string > filesC = new List < string > { "src/c.cs" } ;
188+ BlobPrefetcher . IsNoopPrefetch ( tracer , cache , commitId , filesC , folders , false ) . ShouldEqual ( false ) ;
189+ }
190+
191+ private static FileBasedDictionary < string , string > CreateEmptyCache ( )
192+ {
193+ CacheFileSystem fs = new CacheFileSystem ( ) ;
194+ fs . ExpectedFiles . Add ( MockCacheFileName , new ReusableMemoryStream ( string . Empty ) ) ;
195+ fs . ExpectedOpenFileStreams . Add ( MockCacheFileName + ".tmp" , new ReusableMemoryStream ( string . Empty ) ) ;
196+ fs . ExpectedOpenFileStreams . Add ( MockCacheFileName , fs . ExpectedFiles [ MockCacheFileName ] ) ;
197+
198+ FileBasedDictionary < string , string > . TryCreate (
199+ null ,
200+ MockCacheFileName ,
201+ fs ,
202+ out FileBasedDictionary < string , string > cache ,
203+ out string error ) . ShouldEqual ( true , error ) ;
204+
205+ fs . ExpectedOpenFileStreams . Remove ( MockCacheFileName ) ;
206+ return cache ;
207+ }
208+
209+ private class CacheFileSystem : ConfigurableFileSystem
210+ {
211+ public CacheFileSystem ( )
212+ {
213+ this . ExpectedOpenFileStreams = new Dictionary < string , ReusableMemoryStream > ( ) ;
214+ }
215+
216+ public Dictionary < string , ReusableMemoryStream > ExpectedOpenFileStreams { get ; }
217+
218+ public override Stream OpenFileStream ( string path , FileMode fileMode , FileAccess fileAccess , FileShare shareMode , FileOptions options , bool flushesToDisk )
219+ {
220+ this . ExpectedOpenFileStreams . TryGetValue ( path , out ReusableMemoryStream stream ) ;
221+
222+ if ( fileMode == FileMode . Create )
223+ {
224+ this . ExpectedFiles [ path ] = new ReusableMemoryStream ( string . Empty ) ;
225+ }
226+
227+ this . ExpectedFiles . TryGetValue ( path , out stream ) . ShouldEqual ( true , "Unexpected access of file: " + path ) ;
228+ return stream ;
229+ }
230+
231+ public override void MoveAndOverwriteFile ( string sourceFileName , string destinationFilename )
232+ {
233+ this . ExpectedFiles . TryGetValue ( sourceFileName , out ReusableMemoryStream source ) . ShouldEqual ( true , "Source file does not exist: " + sourceFileName ) ;
234+ this . ExpectedFiles . ContainsKey ( destinationFilename ) . ShouldEqual ( true , "MoveAndOverwriteFile expects the destination file to exist: " + destinationFilename ) ;
235+
236+ this . ExpectedFiles . Remove ( sourceFileName ) ;
237+ this . ExpectedFiles [ destinationFilename ] = source ;
238+ }
239+ }
32240 }
33241}
0 commit comments