Skip to content
Merged
2 changes: 1 addition & 1 deletion GVFS/GVFS.Common/FileSystem/IPlatformFileSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ public interface IPlatformFileSystem
bool SupportsFileMode { get; }
void FlushFileBuffers(string path);
void MoveAndOverwriteFile(string sourceFileName, string destinationFilename);
void CreateHardLink(string newFileName, string existingFileName);
void CreateHardLink(string newLinkFileName, string existingFileName);
bool TryGetNormalizedPath(string path, out string normalizedPath, out string errorMessage);
void ChangeMode(string path, int mode);
}
Expand Down
8 changes: 4 additions & 4 deletions GVFS/GVFS.Common/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,11 @@ public static void MoveFile(string existingFileName, string newFileName, MoveFil
}
}

public static void CreateHardLink(string newFileName, string existingFileName)
public static void CreateHardLink(string newLinkFileName, string existingFileName)
{
if (!CreateHardLink(newFileName, existingFileName, IntPtr.Zero))
if (!CreateHardLink(newLinkFileName, existingFileName, IntPtr.Zero))
{
ThrowLastWin32Exception($"Failed to create hard link from '{newFileName}' to '{existingFileName}'");
ThrowLastWin32Exception($"Failed to create hard link from '{newLinkFileName}' to '{existingFileName}'");
}
}

Expand Down Expand Up @@ -172,7 +172,7 @@ private static extern bool MoveFileEx(

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool CreateHardLink(
string newFileName,
string newLinkFileName,
string existingFileName,
IntPtr securityAttributes);

Expand Down
13 changes: 13 additions & 0 deletions GVFS/GVFS.FunctionalTests/FileSystemRunners/BashRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ public BashRunner()
}
}

public override bool SupportsHardlinkCreation
{
get { return true; }
}

protected override string FileName
{
get
Expand Down Expand Up @@ -158,6 +163,14 @@ public override void CreateEmptyFile(string path)
this.RunProcess(string.Format("-c \"touch {0}\"", bashPath));
}

public override void CreateHardLink(string newLinkFilePath, string existingFilePath)
{
string existingFileBashPath = this.ConvertWinPathToBashPath(existingFilePath);
string newLinkBashPath = this.ConvertWinPathToBashPath(newLinkFilePath);

this.RunProcess(string.Format("-c \"ln {0} {1}\"", existingFileBashPath, newLinkBashPath));
}

public override void WriteAllText(string path, string contents)
{
string bashPath = this.ConvertWinPathToBashPath(path);
Expand Down
10 changes: 10 additions & 0 deletions GVFS/GVFS.FunctionalTests/FileSystemRunners/CmdRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ public class CmdRunner : ShellRunner
"The process cannot access the file because it is being used by another process"
};

public override bool SupportsHardlinkCreation
{
get { return true; }
}

protected override string FileName
{
get
Expand Down Expand Up @@ -108,6 +113,11 @@ public override void CreateEmptyFile(string path)
this.RunProcess(string.Format("/C type NUL > \"{0}\"", path));
}

public override void CreateHardLink(string newLinkFilePath, string existingFilePath)
{
this.RunProcess(string.Format("/C mklink /H \"{0}\" \"{1}\"", newLinkFilePath, existingFilePath));
}

public override void AppendAllText(string path, string contents)
{
// Use echo|set /p with "" to avoid adding any trailing whitespace or newline
Expand Down
13 changes: 12 additions & 1 deletion GVFS/GVFS.FunctionalTests/FileSystemRunners/FileSystemRunner.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using NUnit.Framework;
using System;

namespace GVFS.FunctionalTests.FileSystemRunners
{
Expand Down Expand Up @@ -46,6 +47,11 @@ public static FileSystemRunner DefaultRunner
get { return defaultRunner; }
}

public virtual bool SupportsHardlinkCreation
{
get { return false; }
}

// File methods
public abstract bool FileExists(string path);
public abstract string MoveFile(string sourcePath, string targetPath);
Expand All @@ -69,6 +75,11 @@ public static FileSystemRunner DefaultRunner

public abstract void CreateEmptyFile(string path);

public virtual void CreateHardLink(string newLinkFilePath, string existingFilePath)
{
Assert.Fail($"This runner does not support {nameof(this.CreateHardLink)}");
}

/// <summary>
/// Write the specified contents to the specified file. By calling this method the caller is
/// indicating that they expect the write to succeed. However, the caller is responsible for verifying that
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using GVFS.FunctionalTests.Tools;
using GVFS.Tests.Should;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
Expand All @@ -12,7 +11,7 @@

namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
{
[TestFixtureSource(typeof(GitFilesTestsRunners), GitFilesTestsRunners.TestRunners)]
[TestFixtureSource(typeof(FileSystemRunner), FileSystemRunner.TestRunners)]
[Category(Categories.Mac.M2)]
public class GitFilesTests : TestsWithEnlistmentPerFixture
{
Expand All @@ -38,10 +37,35 @@ public void CreateFileTest()
this.fileSystem.CreateEmptyFile(this.Enlistment.GetVirtualPathTo(emptyFileName));
this.Enlistment.WaitForBackgroundOperations().ShouldEqual(true, "Background operations failed to complete.");
GVFSHelpers.ModifiedPathsShouldContain(this.fileSystem, this.Enlistment.DotGVFSRoot, emptyFileName);
this.Enlistment.GetVirtualPathTo(fileName).ShouldBeAFile(this.fileSystem);
this.Enlistment.GetVirtualPathTo(emptyFileName).ShouldBeAFile(this.fileSystem);
}

[TestCase, Order(2)]
public void CreateHardLinkTest()
{
if (!this.fileSystem.SupportsHardlinkCreation)

@wilbaker William Baker (wilbaker) Aug 22, 2018

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is not getting any coverage on Mac as it's not using BashRunner

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test fixture updated to use all runners (whatever used to cause failures with BashRunner is no longer an issue)

{
return;
}

string existingFileName = "fileToLinkTo.txt";
string existingFilePath = this.Enlistment.GetVirtualPathTo(existingFileName);
GVFSHelpers.ModifiedPathsShouldNotContain(this.fileSystem, this.Enlistment.DotGVFSRoot, existingFileName);
this.fileSystem.WriteAllText(existingFilePath, "Some content here");
this.Enlistment.WaitForBackgroundOperations().ShouldEqual(true, "Background operations failed to complete.");
GVFSHelpers.ModifiedPathsShouldContain(this.fileSystem, this.Enlistment.DotGVFSRoot, existingFileName);
existingFilePath.ShouldBeAFile(this.fileSystem).WithContents("Some content here");

string newLinkFileName = "newHardLink.txt";
string newLinkFilePath = this.Enlistment.GetVirtualPathTo(newLinkFileName);
GVFSHelpers.ModifiedPathsShouldNotContain(this.fileSystem, this.Enlistment.DotGVFSRoot, newLinkFileName);
this.fileSystem.CreateHardLink(newLinkFilePath, existingFilePath);
this.Enlistment.WaitForBackgroundOperations().ShouldEqual(true, "Background operations failed to complete.");
GVFSHelpers.ModifiedPathsShouldContain(this.fileSystem, this.Enlistment.DotGVFSRoot, newLinkFileName);
newLinkFilePath.ShouldBeAFile(this.fileSystem).WithContents("Some content here");
}

[TestCase, Order(3)]
[Category(Categories.Mac.M2TODO)]
public void CreateFileInFolderTest()
{
Expand All @@ -62,7 +86,7 @@ public void CreateFileInFolderTest()
GVFSHelpers.ModifiedPathsShouldContain(this.fileSystem, this.Enlistment.DotGVFSRoot, folderName + "/" + fileName);
}

[TestCase, Order(3)]
[TestCase, Order(4)]
[Category(Categories.Mac.M2TODO)]
public void RenameEmptyFolderTest()
{
Expand All @@ -83,7 +107,7 @@ public void RenameEmptyFolderTest()
GVFSHelpers.ModifiedPathsShouldContain(this.fileSystem, this.Enlistment.DotGVFSRoot, expectedModifiedEntries);
}

[TestCase, Order(4)]
[TestCase, Order(5)]
[Category(Categories.Mac.M2TODO)]
public void RenameFolderTest()
{
Expand Down Expand Up @@ -116,7 +140,7 @@ public void RenameFolderTest()
GVFSHelpers.ModifiedPathsShouldContain(this.fileSystem, this.Enlistment.DotGVFSRoot, expectedModifiedEntries);
}

[TestCase, Order(5)]
[TestCase, Order(6)]
[Category(Categories.Mac.M2TODO)]
public void CaseOnlyRenameOfNewFolderKeepsExcludeEntries()
{
Expand All @@ -138,7 +162,7 @@ public void CaseOnlyRenameOfNewFolderKeepsExcludeEntries()
GVFSHelpers.ModifiedPathsShouldContain(this.fileSystem, this.Enlistment.DotGVFSRoot, expectedModifiedPathsEntries);
}

[TestCase, Order(6)]
[TestCase, Order(7)]
public void ReadingFileDoesNotUpdateIndexOrSparseCheckout()
{
string gitFileToCheck = "GVFS/GVFS.FunctionalTests/Category/CategoryConstants.cs";
Expand Down Expand Up @@ -166,7 +190,7 @@ public void ReadingFileDoesNotUpdateIndexOrSparseCheckout()
}

// TODO(Mac): Enable this test once the LockHolder is converted to .NET Core
[TestCase, Order(7)]
[TestCase, Order(8)]
[Category(Categories.Mac.M2TODO)]
public void ModifiedFileWillGetAddedToModifiedPathsFile()
{
Expand All @@ -186,19 +210,16 @@ public void ModifiedFileWillGetAddedToModifiedPathsFile()
this.VerifyWorktreeBit(gitFileToTest, LsFilesStatus.Cached);
}

[TestCase, Order(8)]
[Category(Categories.Mac.M2TODO)]
public void RenamedFileAddedToSparseCheckoutAndSkipWorktreeBitCleared()
[TestCase, Order(9)]
public void RenamedFileAddedToModifiedPathsFile()
{
string fileToRenameEntry = "Test_EPF_MoveRenameFileTests/ChangeUnhydratedFileName/Program.cs";
string fileToRenameTargetEntry = "Test_EPF_MoveRenameFileTests/ChangeUnhydratedFileName/Program2.cs";
string fileToRenameRelativePath = "Test_EPF_MoveRenameFileTests\\ChangeUnhydratedFileName\\Program.cs";
string fileToRenameTargetRelativePath = "Test_EPF_MoveRenameFileTests\\ChangeUnhydratedFileName\\Program2.cs";
this.VerifyWorktreeBit(fileToRenameEntry, LsFilesStatus.SkipWorktree);

this.fileSystem.MoveFile(
this.Enlistment.GetVirtualPathTo(fileToRenameRelativePath),
this.Enlistment.GetVirtualPathTo(fileToRenameTargetRelativePath));
this.Enlistment.GetVirtualPathTo(fileToRenameEntry),
this.Enlistment.GetVirtualPathTo(fileToRenameTargetEntry));
this.Enlistment.WaitForBackgroundOperations().ShouldEqual(true, "Background operations failed to complete.");

GVFSHelpers.ModifiedPathsShouldContain(this.fileSystem, this.Enlistment.DotGVFSRoot, fileToRenameEntry);
Expand All @@ -208,20 +229,17 @@ public void RenamedFileAddedToSparseCheckoutAndSkipWorktreeBitCleared()
this.VerifyWorktreeBit(fileToRenameEntry, LsFilesStatus.Cached);
}

[TestCase, Order(9)]
[Category(Categories.Mac.M2TODO)]
public void RenamedFileAndOverwrittenTargetAddedToSparseCheckoutAndSkipWorktreeBitCleared()
[TestCase, Order(10)]
public void RenamedFileAndOverwrittenTargetAddedToModifiedPathsFile()
{
string fileToRenameEntry = "Test_EPF_MoveRenameFileTests_2/MoveUnhydratedFileToOverwriteUnhydratedFileAndWrite/RunUnitTests.bat";
string fileToRenameTargetEntry = "Test_EPF_MoveRenameFileTests_2/MoveUnhydratedFileToOverwriteUnhydratedFileAndWrite/RunFunctionalTests.bat";
string fileToRenameRelativePath = "Test_EPF_MoveRenameFileTests_2\\MoveUnhydratedFileToOverwriteUnhydratedFileAndWrite\\RunUnitTests.bat";
string fileToRenameTargetRelativePath = "Test_EPF_MoveRenameFileTests_2\\MoveUnhydratedFileToOverwriteUnhydratedFileAndWrite\\RunFunctionalTests.bat";
this.VerifyWorktreeBit(fileToRenameEntry, LsFilesStatus.SkipWorktree);
this.VerifyWorktreeBit(fileToRenameTargetEntry, LsFilesStatus.SkipWorktree);

this.fileSystem.ReplaceFile(
this.Enlistment.GetVirtualPathTo(fileToRenameRelativePath),
this.Enlistment.GetVirtualPathTo(fileToRenameTargetRelativePath));
this.Enlistment.GetVirtualPathTo(fileToRenameEntry),
this.Enlistment.GetVirtualPathTo(fileToRenameTargetEntry));
this.Enlistment.WaitForBackgroundOperations().ShouldEqual(true, "Background operations failed to complete.");

GVFSHelpers.ModifiedPathsShouldContain(this.fileSystem, this.Enlistment.DotGVFSRoot, fileToRenameEntry);
Expand All @@ -232,7 +250,7 @@ public void RenamedFileAndOverwrittenTargetAddedToSparseCheckoutAndSkipWorktreeB
this.VerifyWorktreeBit(fileToRenameTargetEntry, LsFilesStatus.Cached);
}

[TestCase, Order(10)]
[TestCase, Order(11)]
public void DeletedFileAddedToModifiedPathsFile()
{
string fileToDeleteEntry = "GVFlt_DeleteFileTest/GVFlt_DeleteFullFileWithoutFileContext_DeleteOnClose/a.txt";
Expand All @@ -247,7 +265,7 @@ public void DeletedFileAddedToModifiedPathsFile()
this.VerifyWorktreeBit(fileToDeleteEntry, LsFilesStatus.Cached);
}

[TestCase, Order(11)]
[TestCase, Order(12)]
public void DeletedFolderAndChildrenAddedToToModifiedPathsFile()
{
string folderToDelete = "Scripts";
Expand Down Expand Up @@ -279,7 +297,7 @@ public void DeletedFolderAndChildrenAddedToToModifiedPathsFile()
}
}

[TestCase, Order(12)]
[TestCase, Order(13)]
public void FileRenamedOutOfRepoAddedToModifiedPathsFile()
{
string fileToRenameEntry = "GVFlt_MoveFileTest/PartialToOutside/from/lessInFrom.txt";
Expand All @@ -298,7 +316,7 @@ public void FileRenamedOutOfRepoAddedToModifiedPathsFile()
this.VerifyWorktreeBit(fileToRenameEntry, LsFilesStatus.Cached);
}

[TestCase, Order(13)]
[TestCase, Order(14)]
public void OverwrittenFileAddedToSparseCheckoutAndSkipWorktreeBitCleared()
{
string fileToOverwriteEntry = "Test_EPF_WorkingDirectoryTests/1/2/3/4/ReadDeepProjectedFile.cpp";
Expand All @@ -318,7 +336,7 @@ public void OverwrittenFileAddedToSparseCheckoutAndSkipWorktreeBitCleared()
this.VerifyWorktreeBit(fileToOverwriteEntry, LsFilesStatus.Cached);
}

[TestCase, Order(14)]
[TestCase, Order(15)]
[Category(Categories.Mac.M2TODO)]
public void SupersededFileAddedToSparseCheckoutAndSkipWorktreeBitCleared()
{
Expand Down Expand Up @@ -357,29 +375,5 @@ private static class LsFilesStatus
public const char Cached = 'H';
public const char SkipWorktree = 'S';
}

private class GitFilesTestsRunners
{
public const string TestRunners = "Runners";

public static object[] Runners
{
get
{
// Don't use the BashRunner for GitFilesTests as the BashRunner always strips off the last trailing newline (\n)
// and we expect there to be a trailing new line
List<object[]> runners = new List<object[]>();
foreach (object[] runner in FileSystemRunner.Runners.ToList())
{
if (!(runner.ToList().First() is BashRunner))
{
runners.Add(new object[] { runner.ToList().First() });
}
}

return runners.ToArray();
}
}
}
}
}
Loading