diff --git a/GVFS/GVFS.FunctionalTests/Program.cs b/GVFS/GVFS.FunctionalTests/Program.cs
index f00d9496a..56940a2b7 100644
--- a/GVFS/GVFS.FunctionalTests/Program.cs
+++ b/GVFS/GVFS.FunctionalTests/Program.cs
@@ -21,6 +21,11 @@ public static void Main(string[] args)
NUnitRunner runner = new NUnitRunner(args);
runner.AddGlobalSetupIfNeeded("GVFS.FunctionalTests.GlobalSetup");
+ if (runner.HasCustomArg("--debug"))
+ {
+ Debugger.Launch();
+ }
+
if (runner.HasCustomArg("--no-shared-gvfs-cache"))
{
Console.WriteLine("Running without a shared git object cache");
diff --git a/GVFS/GVFS.FunctionalTests/Tests/GitCommands/CorruptionScenarioTests.cs b/GVFS/GVFS.FunctionalTests/Tests/GitCommands/CorruptionScenarioTests.cs
new file mode 100644
index 000000000..e4a08d123
--- /dev/null
+++ b/GVFS/GVFS.FunctionalTests/Tests/GitCommands/CorruptionScenarioTests.cs
@@ -0,0 +1,35 @@
+using GVFS.FunctionalTests.Properties;
+using NUnit.Framework;
+
+namespace GVFS.FunctionalTests.Tests.GitCommands
+{
+ ///
+ /// This class is used to reproduce corruption scenarios in the GVFS virtual projection.
+ ///
+ [Category(Categories.GitCommands)]
+ [TestFixtureSource(typeof(GitRepoTests), nameof(GitRepoTests.ValidateWorkingTree))]
+ public class CorruptionReproTests : GitRepoTests
+ {
+ public CorruptionReproTests(Settings.ValidateWorkingTreeMode validateWorkingTree)
+ : base(enlistmentPerTest: true, validateWorkingTree: validateWorkingTree)
+ {
+ }
+
+ ///
+ /// Reproduction of a reported issue:
+ /// Restoring a file after its parent directory was deleted fails with
+ /// "fatal: could not unlink 'path\to\': Directory not empty"
+ ///
+ [TestCase]
+ public void RestoreAfterDeleteNesteredDirectory()
+ {
+ // Delete a directory with nested subdirectories and files.
+ this.ValidateNonGitCommand("cmd.exe", "/c \"rmdir /s /q GVFlt_DeleteFileTest\"");
+
+ // Restore the working directory.
+ this.ValidateGitCommand("restore .");
+
+ this.FilesShouldMatchCheckoutOfSourceBranch();
+ }
+ }
+}
\ No newline at end of file
diff --git a/GVFS/GVFS.FunctionalTests/Tests/GitCommands/GitRepoTests.cs b/GVFS/GVFS.FunctionalTests/Tests/GitCommands/GitRepoTests.cs
index 2b902117f..d7a22fa28 100644
--- a/GVFS/GVFS.FunctionalTests/Tests/GitCommands/GitRepoTests.cs
+++ b/GVFS/GVFS.FunctionalTests/Tests/GitCommands/GitRepoTests.cs
@@ -269,6 +269,23 @@ protected void ValidateGitCommand(string command, params object[] args)
args);
}
+ protected void ValidateNonGitCommand(string command, string args = "", bool ignoreErrors = false, bool checkStatus = true)
+ {
+ string controlRepoRoot = this.ControlGitRepo.RootPath;
+ string gvfsRepoRoot = this.Enlistment.RepoRoot;
+
+ ProcessResult expectedResult = ProcessHelper.Run(command, args, controlRepoRoot);
+ ProcessResult actualResult = ProcessHelper.Run(command, args, gvfsRepoRoot);
+ if (!ignoreErrors)
+ {
+ GitHelpers.ErrorsShouldMatch(command, expectedResult, actualResult);
+ }
+ if (checkStatus)
+ {
+ this.ValidateGitCommand("status");
+ }
+ }
+
protected void ChangeMode(string filePath, ushort mode)
{
string virtualFile = Path.Combine(this.Enlistment.RepoRoot, filePath);
diff --git a/GVFS/GVFS.FunctionalTests/Tools/ProcessHelper.cs b/GVFS/GVFS.FunctionalTests/Tools/ProcessHelper.cs
index 664c1e254..539c5cc82 100644
--- a/GVFS/GVFS.FunctionalTests/Tools/ProcessHelper.cs
+++ b/GVFS/GVFS.FunctionalTests/Tools/ProcessHelper.cs
@@ -6,6 +6,11 @@ namespace GVFS.FunctionalTests.Tools
public static class ProcessHelper
{
public static ProcessResult Run(string fileName, string arguments)
+ {
+ return Run(fileName, arguments, null);
+ }
+
+ public static ProcessResult Run(string fileName, string arguments, string workingDirectory)
{
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.UseShellExecute = false;
@@ -14,6 +19,10 @@ public static ProcessResult Run(string fileName, string arguments)
startInfo.CreateNoWindow = true;
startInfo.FileName = fileName;
startInfo.Arguments = arguments;
+ if (!string.IsNullOrEmpty(workingDirectory))
+ {
+ startInfo.WorkingDirectory = workingDirectory;
+ }
return Run(startInfo);
}