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); }