forked from microsoft/VFSForGit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGVFSFunctionalTestEnlistment.cs
More file actions
314 lines (263 loc) · 12.1 KB
/
GVFSFunctionalTestEnlistment.cs
File metadata and controls
314 lines (263 loc) · 12.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
using GVFS.FunctionalTests.FileSystemRunners;
using GVFS.FunctionalTests.Should;
using GVFS.FunctionalTests.Tests;
using GVFS.Tests.Should;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
namespace GVFS.FunctionalTests.Tools
{
public class GVFSFunctionalTestEnlistment
{
private const string LockHeldByGit = "GVFS Lock: Held by {0}";
private const int SleepMSWaitingForStatusCheck = 100;
private const int DefaultMaxWaitMSForStatusCheck = 5000;
private static readonly string ZeroBackgroundOperations = "Background operations: 0" + Environment.NewLine;
private GVFSProcess gvfsProcess;
private GVFSFunctionalTestEnlistment(string pathToGVFS, string enlistmentRoot, string repoUrl, string commitish, string localCacheRoot = null)
{
this.EnlistmentRoot = enlistmentRoot;
this.RepoUrl = repoUrl;
this.Commitish = commitish;
if (localCacheRoot == null)
{
if (GVFSTestConfig.NoSharedCache)
{
// eg C:\Repos\GVFSFunctionalTests\enlistment\7942ca69d7454acbb45ea39ef5be1d15\.gvfs\.gvfsCache
localCacheRoot = GetRepoSpecificLocalCacheRoot(enlistmentRoot);
}
else
{
// eg C:\Repos\GVFSFunctionalTests\.gvfsCache
// Ensures the general cache is not cleaned up between test runs
localCacheRoot = Path.Combine(Properties.Settings.Default.EnlistmentRoot, "..", ".gvfsCache");
}
}
this.LocalCacheRoot = localCacheRoot;
this.gvfsProcess = new GVFSProcess(pathToGVFS, this.EnlistmentRoot, this.LocalCacheRoot);
}
public string EnlistmentRoot
{
get; private set;
}
public string RepoUrl
{
get; private set;
}
public string LocalCacheRoot { get; }
public string RepoRoot
{
get { return Path.Combine(this.EnlistmentRoot, "src"); }
}
public string DotGVFSRoot
{
get { return Path.Combine(this.EnlistmentRoot, ".gvfs"); }
}
public string GVFSLogsRoot
{
get { return Path.Combine(this.DotGVFSRoot, "logs"); }
}
public string DiagnosticsRoot
{
get { return Path.Combine(this.DotGVFSRoot, "diagnostics"); }
}
public string Commitish
{
get; private set;
}
public static GVFSFunctionalTestEnlistment CloneAndMountWithPerRepoCache(string pathToGvfs, bool skipPrefetch)
{
string enlistmentRoot = GVFSFunctionalTestEnlistment.GetUniqueEnlistmentRoot();
string localCache = GVFSFunctionalTestEnlistment.GetRepoSpecificLocalCacheRoot(enlistmentRoot);
return CloneAndMount(pathToGvfs, enlistmentRoot, null, localCache, skipPrefetch);
}
public static GVFSFunctionalTestEnlistment CloneAndMount(
string pathToGvfs,
string commitish = null,
string localCacheRoot = null,
bool skipPrefetch = false)
{
string enlistmentRoot = GVFSFunctionalTestEnlistment.GetUniqueEnlistmentRoot();
return CloneAndMount(pathToGvfs, enlistmentRoot, commitish, localCacheRoot, skipPrefetch);
}
public static GVFSFunctionalTestEnlistment CloneAndMountEnlistmentWithSpacesInPath(string pathToGvfs, string commitish = null)
{
string enlistmentRoot = GVFSFunctionalTestEnlistment.GetUniqueEnlistmentRootWithSpaces();
string localCache = GVFSFunctionalTestEnlistment.GetRepoSpecificLocalCacheRoot(enlistmentRoot);
return CloneAndMount(pathToGvfs, enlistmentRoot, commitish, localCache);
}
public static string GetUniqueEnlistmentRoot()
{
return Path.Combine(Properties.Settings.Default.EnlistmentRoot, Guid.NewGuid().ToString("N").Substring(0, 20));
}
public static string GetUniqueEnlistmentRootWithSpaces()
{
return Path.Combine(Properties.Settings.Default.EnlistmentRoot, "test " + Guid.NewGuid().ToString("N").Substring(0, 15));
}
public string GetObjectRoot(FileSystemRunner fileSystem)
{
Path.Combine(this.LocalCacheRoot, "mapping.dat").ShouldBeAFile(fileSystem);
HashSet<string> allowedFileNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"mapping.dat",
"mapping.dat.lock" // mapping.dat.lock can be present, but doesn't have to be present
};
this.LocalCacheRoot.ShouldBeADirectory(fileSystem).WithFiles().ShouldNotContain(f => !allowedFileNames.Contains(f.Name));
DirectoryInfo[] directories = this.LocalCacheRoot.ShouldBeADirectory(fileSystem).WithDirectories().ToArray();
directories.Length.ShouldEqual(
1,
this.LocalCacheRoot + " is expected to have only one folder. Actual folders: " + string.Join<DirectoryInfo>(",", directories));
return Path.Combine(directories[0].FullName, "gitObjects");
}
public string GetPackRoot(FileSystemRunner fileSystem)
{
return Path.Combine(this.GetObjectRoot(fileSystem), "pack");
}
public void DeleteEnlistment()
{
TestResultsHelper.OutputGVFSLogs(this);
// TODO(Mac): Figure out why the call to DeleteDirectoryWithRetry is not returning.
// Once this is resolved, we can replace this duplicated code with RepositoryHelpers.DeleteTestDirectory
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
// Use cmd.exe to delete the enlistment as it properly handles tombstones and reparse points
CmdRunner.DeleteDirectoryWithUnlimitedRetries(this.EnlistmentRoot);
}
else
{
// BashRunner.DeleteDirectoryWithUnlimitedRetries(this.EnlistmentRoot);
}
}
public void CloneAndMount(bool skipPrefetch)
{
this.gvfsProcess.Clone(this.RepoUrl, this.Commitish, skipPrefetch);
GitProcess.Invoke(this.RepoRoot, "checkout " + this.Commitish);
GitProcess.Invoke(this.RepoRoot, "branch --unset-upstream");
GitProcess.Invoke(this.RepoRoot, "config core.abbrev 40");
GitProcess.Invoke(this.RepoRoot, "config user.name \"Functional Test User\"");
GitProcess.Invoke(this.RepoRoot, "config user.email \"functional@test.com\"");
// If this repository has a .gitignore file in the root directory, force it to be
// hydrated. This is because if the GitStatusCache feature is enabled, it will run
// a "git status" command asynchronously, which will hydrate the .gitignore file
// as it reads the ignore rules. Hydrate this file here so that it is consistently
// hydrated and there are no race conditions depending on when / if it is hydrated
// as part of an asynchronous status scan to rebuild the GitStatusCache.
string rootGitIgnorePath = Path.Combine(this.RepoRoot, ".gitignore");
if (File.Exists(rootGitIgnorePath))
{
File.ReadAllBytes(rootGitIgnorePath);
}
}
public void MountGVFS()
{
this.gvfsProcess.Mount();
}
public bool TryMountGVFS()
{
string output;
return this.TryMountGVFS(out output);
}
public bool TryMountGVFS(out string output)
{
return this.gvfsProcess.TryMount(out output);
}
public string Prefetch(string args, bool failOnError = true, string standardInput = null)
{
return this.gvfsProcess.Prefetch(args, failOnError, standardInput);
}
public void Repair(bool confirm)
{
this.gvfsProcess.Repair(confirm);
}
public string Diagnose()
{
return this.gvfsProcess.Diagnose();
}
public string Status(string trace = null)
{
return this.gvfsProcess.Status(trace);
}
public bool WaitForBackgroundOperations(int maxWaitMilliseconds = DefaultMaxWaitMSForStatusCheck)
{
return this.WaitForStatus(maxWaitMilliseconds, ZeroBackgroundOperations).ShouldBeTrue("Background operations failed to complete.");
}
public bool WaitForLock(string lockCommand, int maxWaitMilliseconds = DefaultMaxWaitMSForStatusCheck)
{
return this.WaitForStatus(maxWaitMilliseconds, string.Format(LockHeldByGit, lockCommand));
}
public void UnmountGVFS()
{
this.gvfsProcess.Unmount();
}
public string GetCacheServer()
{
return this.gvfsProcess.CacheServer("--get");
}
public string SetCacheServer(string arg)
{
return this.gvfsProcess.CacheServer("--set " + arg);
}
public void UnmountAndDeleteAll()
{
this.UnmountGVFS();
this.DeleteEnlistment();
}
public string GetVirtualPathTo(string path)
{
// Replace '/' with Path.DirectorySeparatorChar to ensure that any
// Git paths are converted to system paths
return Path.Combine(this.RepoRoot, path.Replace('/', Path.DirectorySeparatorChar));
}
public string GetVirtualPathTo(params string[] pathParts)
{
return Path.Combine(this.RepoRoot, Path.Combine(pathParts));
}
public string GetObjectPathTo(string objectHash)
{
return Path.Combine(
this.RepoRoot,
TestConstants.DotGit.Objects.Root,
objectHash.Substring(0, 2),
objectHash.Substring(2));
}
private static GVFSFunctionalTestEnlistment CloneAndMount(string pathToGvfs, string enlistmentRoot, string commitish, string localCacheRoot, bool skipPrefetch = false)
{
GVFSFunctionalTestEnlistment enlistment = new GVFSFunctionalTestEnlistment(
pathToGvfs,
enlistmentRoot ?? GetUniqueEnlistmentRoot(),
GVFSTestConfig.RepoToClone,
commitish ?? Properties.Settings.Default.Commitish,
localCacheRoot ?? GVFSTestConfig.LocalCacheRoot);
try
{
enlistment.CloneAndMount(skipPrefetch);
}
catch (Exception e)
{
Console.WriteLine("Unhandled exception in CloneAndMount: " + e.ToString());
TestResultsHelper.OutputGVFSLogs(enlistment);
throw;
}
return enlistment;
}
private static string GetRepoSpecificLocalCacheRoot(string enlistmentRoot)
{
return Path.Combine(enlistmentRoot, ".gvfs", ".gvfsCache");
}
private bool WaitForStatus(int maxWaitMilliseconds, string statusShouldContain)
{
string status = null;
int totalWaitMilliseconds = 0;
while (totalWaitMilliseconds <= maxWaitMilliseconds && (status == null || !status.Contains(statusShouldContain)))
{
Thread.Sleep(SleepMSWaitingForStatusCheck);
status = this.Status();
totalWaitMilliseconds += SleepMSWaitingForStatusCheck;
}
return totalWaitMilliseconds <= maxWaitMilliseconds;
}
}
}