Skip to content
Merged
2 changes: 2 additions & 0 deletions GVFS.sln
Original file line number Diff line number Diff line change
Expand Up @@ -554,11 +554,13 @@ Global
{BD7C5776-82F2-40C6-AF01-B3CC1E2D83AF}.Release.Windows|x64.ActiveCfg = Release|x64
{BD7C5776-82F2-40C6-AF01-B3CC1E2D83AF}.Release.Windows|x64.Build.0 = Release|x64
{FA273F69-5762-43D8-AEA1-B4F08090D624}.Debug.Linux|x64.ActiveCfg = Debug|x64
{FA273F69-5762-43D8-AEA1-B4F08090D624}.Debug.Linux|x64.Build.0 = Debug|x64
{FA273F69-5762-43D8-AEA1-B4F08090D624}.Debug.Mac|x64.ActiveCfg = Debug|x64
{FA273F69-5762-43D8-AEA1-B4F08090D624}.Debug.Mac|x64.Build.0 = Debug|x64
{FA273F69-5762-43D8-AEA1-B4F08090D624}.Debug.Windows|x64.ActiveCfg = Debug|x64
{FA273F69-5762-43D8-AEA1-B4F08090D624}.Debug.Windows|x64.Build.0 = Debug|x64
{FA273F69-5762-43D8-AEA1-B4F08090D624}.Release.Linux|x64.ActiveCfg = Release|x64
{FA273F69-5762-43D8-AEA1-B4F08090D624}.Release.Linux|x64.Build.0 = Release|x64
{FA273F69-5762-43D8-AEA1-B4F08090D624}.Release.Mac|x64.ActiveCfg = Release|x64
{FA273F69-5762-43D8-AEA1-B4F08090D624}.Release.Mac|x64.Build.0 = Release|x64
{FA273F69-5762-43D8-AEA1-B4F08090D624}.Release.Windows|x64.ActiveCfg = Release|x64
Expand Down
13 changes: 11 additions & 2 deletions GVFS/GVFS.FunctionalTests.LockHolder/AcquireGVFSLock.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using CommandLine;
using GVFS.Common;
using GVFS.Common.NamedPipes;
using GVFS.Platform.Linux;
using GVFS.Platform.Mac;
using GVFS.Platform.Windows;
using System;
Expand Down Expand Up @@ -43,7 +44,11 @@ public void Execute()

private static bool TryGetGVFSEnlistmentRootImplementation(string directory, out string enlistmentRoot, out string errorMessage)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return LinuxPlatform.TryGetGVFSEnlistmentRootImplementation(directory, out enlistmentRoot, out errorMessage);
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return MacPlatform.TryGetGVFSEnlistmentRootImplementation(directory, out enlistmentRoot, out errorMessage);
}
Expand All @@ -70,7 +75,11 @@ private static bool TryGetGVFSEnlistmentRootImplementation(string directory, out

private static string GetNamedPipeNameImplementation(string enlistmentRoot)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return LinuxPlatform.GetNamedPipeNameImplementation(enlistmentRoot);
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return MacPlatform.GetNamedPipeNameImplementation(enlistmentRoot);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<!-- see https://github.com/NuGet/Home/issues/4837 for reference to CopyLocalLockFileAssemblies -->
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<RuntimeIdentifiers>win-x64;osx-x64</RuntimeIdentifiers>
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
</PropertyGroup>

<PropertyGroup>
Expand All @@ -25,6 +25,7 @@
<ItemGroup>
<Compile Include="..\GVFS.Platform.POSIX\POSIXFileSystem.Shared.cs" Link="POSIXFileSystem.Shared.cs" />
<Compile Include="..\GVFS.Platform.POSIX\POSIXPlatform.Shared.cs" Link="POSIXPlatform.Shared.cs" />
<Compile Include="..\GVFS.Platform.Linux\LinuxPlatform.Shared.cs" Link="LinuxPlatform.Shared.cs" />
<Compile Include="..\GVFS.Platform.Mac\MacPlatform.Shared.cs" Link="MacPlatform.Shared.cs" />
<Compile Include="..\GVFS.Platform.Windows\WindowsFileSystem.Shared.cs" Link="WindowsFileSystem.Shared.cs" />
</ItemGroup>
Expand Down
20 changes: 16 additions & 4 deletions GVFS/GVFS.FunctionalTests/Categories.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,27 @@ public static class Categories
public const string LinuxOnly = "LinuxOnly";
public const string MacOnly = "MacOnly";

public static class LinuxTODO
{
// Tests that fail due to flock() contention with libprojfs
public const string NeedsContentionFreeFileLock = "NeedsNonContendedFileLock";

// Tests that fail due to FUSE passthrough write buffering behavior
public const string NeedsConsistentBufferedWrites = "NeedsConsistentBufferedWrites";
}

public static class MacTODO
{
// Tests that require #360 (detecting/handling new empty folders)
public const string NeedsNewFolderCreateNotification = "NeedsNewFolderCreateNotification";

// Tests that have been flaky on build servers and need additional logging and\or
// investigation
public const string FlakyTest = "MacFlakyTest";
}

public static class NonWindowsTODO
{
// Tests that require the Status Cache to be built
public const string NeedsStatusCache = "NeedsStatusCache";

Expand All @@ -29,10 +45,6 @@ public static class MacTODO

// Tests requires code updates so that we lock the file instead of looking for a .lock file
public const string TestNeedsToLockFile = "TestNeedsToLockFile";

// Tests that have been flaky on build servers and need additional logging and\or
// investigation
public const string FlakyTest = "MacFlakyTest";
}
}
}
21 changes: 16 additions & 5 deletions GVFS/GVFS.FunctionalTests/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,16 +104,27 @@ public static void Main(string[] args)
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
excludeCategories.Add(Categories.MacTODO.NeedsNewFolderCreateNotification);
excludeCategories.Add(Categories.MacTODO.NeedsGVFSConfig);
excludeCategories.Add(Categories.MacTODO.NeedsDehydrate);
excludeCategories.Add(Categories.MacTODO.NeedsServiceVerb);
excludeCategories.Add(Categories.MacTODO.NeedsStatusCache);
excludeCategories.Add(Categories.MacTODO.TestNeedsToLockFile);

excludeCategories.Add(Categories.NonWindowsTODO.NeedsGVFSConfig);
excludeCategories.Add(Categories.NonWindowsTODO.NeedsDehydrate);
excludeCategories.Add(Categories.NonWindowsTODO.NeedsServiceVerb);
excludeCategories.Add(Categories.NonWindowsTODO.NeedsStatusCache);
excludeCategories.Add(Categories.NonWindowsTODO.TestNeedsToLockFile);

excludeCategories.Add(Categories.WindowsOnly);
excludeCategories.Add(Categories.LinuxOnly);
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
excludeCategories.Add(Categories.LinuxTODO.NeedsContentionFreeFileLock);
excludeCategories.Add(Categories.LinuxTODO.NeedsConsistentBufferedWrites);

excludeCategories.Add(Categories.NonWindowsTODO.NeedsGVFSConfig);
excludeCategories.Add(Categories.NonWindowsTODO.NeedsDehydrate);
excludeCategories.Add(Categories.NonWindowsTODO.NeedsServiceVerb);
excludeCategories.Add(Categories.NonWindowsTODO.NeedsStatusCache);
excludeCategories.Add(Categories.NonWindowsTODO.TestNeedsToLockFile);

excludeCategories.Add(Categories.WindowsOnly);
excludeCategories.Add(Categories.MacOnly);
}
Expand Down
7 changes: 7 additions & 0 deletions GVFS/GVFS.FunctionalTests/Tests/DiskLayoutVersionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ public class DiskLayoutVersionTests : TestsWithEnlistmentPerTestCase
{
private const int WindowsCurrentDiskLayoutMajorVersion = 19;
private const int MacCurrentDiskLayoutMajorVersion = 19;
private const int LinuxCurrentDiskLayoutMajorVersion = 19;
private const int WindowsCurrentDiskLayoutMinimumMajorVersion = 7;
private const int MacCurrentDiskLayoutMinimumMajorVersion = 18;
private const int LinuxCurrentDiskLayoutMinimumMajorVersion = 19;
private const int CurrentDiskLayoutMinorVersion = 0;
private int currentDiskMajorVersion;
private int currentDiskMinimumMajorVersion;
Expand All @@ -28,6 +30,11 @@ public override void CreateEnlistment()
this.currentDiskMajorVersion = MacCurrentDiskLayoutMajorVersion;
this.currentDiskMinimumMajorVersion = MacCurrentDiskLayoutMinimumMajorVersion;
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
this.currentDiskMajorVersion = LinuxCurrentDiskLayoutMajorVersion;
this.currentDiskMinimumMajorVersion = LinuxCurrentDiskLayoutMinimumMajorVersion;
}
else
{
this.currentDiskMajorVersion = WindowsCurrentDiskLayoutMajorVersion;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public void GrowFileContents(FileSystemRunner fileSystem, string parentFolder)
}

[TestCaseSource(typeof(FileRunnersAndFolders), nameof(FileRunnersAndFolders.Runners))]
[Category(Categories.LinuxTODO.NeedsConsistentBufferedWrites)]
public void FilesAreBufferedAndCanBeFlushed(FileSystemRunner fileSystem, string parentFolder)
{
string filename = Path.Combine(parentFolder, "FilesAreBufferedAndCanBeFlushed");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public void DehydrateShouldExitWithoutConfirm()
}

[TestCase]
[Category(Categories.MacTODO.NeedsDehydrate)] // Dehydrate is not successful
[Category(Categories.NonWindowsTODO.NeedsDehydrate)] // Dehydrate is not successful
public void DehydrateShouldSucceedInCommonCase()
{
this.DehydrateShouldSucceed("The repo was successfully dehydrated and remounted", confirm: true, noStatus: false);
Expand All @@ -46,7 +46,7 @@ public void DehydrateShouldFailOnUnmountedRepoWithStatus()
}

[TestCase]
[Category(Categories.MacTODO.NeedsDehydrate)] // Call CmdRunner which is windows specific
[Category(Categories.NonWindowsTODO.NeedsDehydrate)] // Call CmdRunner which is windows specific
public void DehydrateShouldSucceedEvenIfObjectCacheIsDeleted()
{
this.Enlistment.UnmountGVFS();
Expand All @@ -55,7 +55,7 @@ public void DehydrateShouldSucceedEvenIfObjectCacheIsDeleted()
}

[TestCase]
[Category(Categories.MacTODO.NeedsDehydrate)] // .git / .gvfs are not copied to backup folder on Mac
[Category(Categories.NonWindowsTODO.NeedsDehydrate)] // .git / .gvfs are not copied to backup folder on Mac or Linux
public void DehydrateShouldBackupFiles()
{
this.DehydrateShouldSucceed("The repo was successfully dehydrated and remounted", confirm: true, noStatus: false);
Expand Down Expand Up @@ -130,7 +130,7 @@ public void DehydrateShouldFailIfGitObjectsRootNotInMetadata()
}

[TestCase]
[Category(Categories.MacTODO.NeedsDehydrate)] // Error messages on mac are different, DehydrateShouldFail fails on message checking
[Category(Categories.NonWindowsTODO.NeedsDehydrate)] // Error messages on POSIX are different, DehydrateShouldFail fails on message checking
public void DehydrateShouldFailOnWrongDiskLayoutVersion()
{
this.Enlistment.UnmountGVFS();
Expand Down Expand Up @@ -230,4 +230,4 @@ private string RunHandleProcess(string path)
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using NUnit.Framework;
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;

Expand All @@ -13,6 +14,24 @@ namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
[TestFixture]
public class MultithreadedReadWriteTests : TestsWithEnlistmentPerFixture
{
private int nativeEWouldBlock = 0;

public MultithreadedReadWriteTests()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
// #define EAGAIN 11 /* Resource temporarily unavailable */
// #define EWOULDBLOCK EAGAIN /* Operation would block */
this.nativeEWouldBlock = 11;
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
// #define EAGAIN 35 /* Resource temporarily unavailable */
// #define EWOULDBLOCK EAGAIN /* Operation would block */
this.nativeEWouldBlock = 35;
}
}

[TestCase, Order(1)]
public void CanReadVirtualFileInParallel()
{
Expand All @@ -31,13 +50,22 @@ public void CanReadVirtualFileInParallel()
{
threads[i] = new Thread(() =>
{
try
{
FileSystemRunner.DefaultRunner.ReadAllText(virtualPath).ShouldBeNonEmpty();
}
catch (Exception e)
while (true)
{
readException = e;
try
{
FileSystemRunner.DefaultRunner.ReadAllText(virtualPath).ShouldBeNonEmpty();
break;
}
catch (IOException) when (Marshal.GetLastWin32Error() == this.nativeEWouldBlock && this.nativeEWouldBlock > 0)
{
// ignore EAGAIN
}
catch (Exception e)
{
readException = e;
break;
}
}
});

Expand Down Expand Up @@ -110,6 +138,7 @@ public void CanReadHydratedPlaceholderInParallel()
}

[TestCaseSource(typeof(FileSystemRunner), nameof(FileSystemRunner.Runners))]
[Category(Categories.LinuxTODO.NeedsConsistentBufferedWrites)]
[Order(3)]
public void CanReadWriteAFileInParallel(FileSystemRunner fileSystem)
{
Expand All @@ -124,6 +153,8 @@ public void CanReadWriteAFileInParallel(FileSystemRunner fileSystem)
Thread[] threads = new Thread[4];
StringBuilder[] fileContents = new StringBuilder[4];

Exception writeException = null;

// Writer
fileContents[0] = new StringBuilder();
threads[0] = new Thread(() =>
Expand All @@ -133,32 +164,70 @@ public void CanReadWriteAFileInParallel(FileSystemRunner fileSystem)
while ((DateTime.Now - start).TotalSeconds < 2.5)
{
string newChar = r.Next(10).ToString();
fileSystem.AppendAllText(virtualPath, newChar);
fileContents[0].Append(newChar);
try
{
fileSystem.AppendAllText(virtualPath, newChar);
fileContents[0].Append(newChar);
}
catch (IOException) when (Marshal.GetLastWin32Error() == this.nativeEWouldBlock && this.nativeEWouldBlock > 0)
{
// ignore EAGAIN
}
catch (Exception e)
{
writeException = e;
}
Thread.Yield();
}

keepRunning = false;
});

Exception readException = null;

// Readers
for (int i = 1; i < threads.Length; ++i)
{
int myIndex = i;
fileContents[i] = new StringBuilder();
threads[i] = new Thread(() =>
{
using (Stream readStream = File.Open(virtualPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (StreamReader reader = new StreamReader(readStream, true))
bool retry = true;
while (retry)
{
while (keepRunning)
Stream readStream = null;
try
{
Thread.Yield();
fileContents[myIndex].Append(reader.ReadToEnd());
}
readStream = File.Open(virtualPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
retry = false;
Comment thread
derrickstolee marked this conversation as resolved.
using (StreamReader reader = new StreamReader(readStream, true))
{
while (keepRunning)
{
Thread.Yield();
fileContents[myIndex].Append(reader.ReadToEnd());
}

// Catch the last write that might have escaped us
fileContents[myIndex].Append(reader.ReadToEnd());
// Catch the last write that might have escaped us
fileContents[myIndex].Append(reader.ReadToEnd());
}
}
catch (IOException) when (Marshal.GetLastWin32Error() == this.nativeEWouldBlock && this.nativeEWouldBlock > 0)
{
// ignore EAGAIN
}
catch (Exception e)
{
readException = e;
retry = false;
}
finally
{
if (readStream != null)
{
((IDisposable)readStream).Dispose();
}
}
}
});
}
Expand All @@ -179,6 +248,9 @@ public void CanReadWriteAFileInParallel(FileSystemRunner fileSystem)
}

fileSystem.DeleteFile(virtualPath);

readException.ShouldBeNull("At least one of the reads failed");
writeException.ShouldBeNull("At least one of the writes failed");
}
}
}
Loading