diff --git a/Scalar.FunctionalTests/Tests/DiskLayoutVersionTests.cs b/Scalar.FunctionalTests/Tests/DiskLayoutVersionTests.cs deleted file mode 100644 index e3bacd1c56..0000000000 --- a/Scalar.FunctionalTests/Tests/DiskLayoutVersionTests.cs +++ /dev/null @@ -1,71 +0,0 @@ -using NUnit.Framework; -using Scalar.FunctionalTests.Tests.EnlistmentPerTestCase; -using Scalar.FunctionalTests.Tools; -using Scalar.Tests.Should; -using System.Runtime.InteropServices; - -namespace Scalar.FunctionalTests.Tests -{ - [TestFixture] - [Category(Categories.ExtraCoverage)] - [Category(Categories.NeedsUpdatesForNonVirtualizedMode)] - public class DiskLayoutVersionTests : TestsWithEnlistmentPerTestCase - { - private const int WindowsCurrentDiskLayoutMajorVersion = 0; - private const int MacCurrentDiskLayoutMajorVersion = 0; - private const int WindowsCurrentDiskLayoutMinimumMajorVersion = 0; - private const int MacCurrentDiskLayoutMinimumMajorVersion = 0; - private const int CurrentDiskLayoutMinorVersion = 0; - private int currentDiskMajorVersion; - private int currentDiskMinimumMajorVersion; - - [SetUp] - public override void CreateEnlistment() - { - base.CreateEnlistment(); - - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - this.currentDiskMajorVersion = MacCurrentDiskLayoutMajorVersion; - this.currentDiskMinimumMajorVersion = MacCurrentDiskLayoutMinimumMajorVersion; - } - else - { - this.currentDiskMajorVersion = WindowsCurrentDiskLayoutMajorVersion; - this.currentDiskMinimumMajorVersion = WindowsCurrentDiskLayoutMinimumMajorVersion; - } - } - - [TestCase] - public void MountSucceedsIfMinorVersionHasAdvancedButNotMajorVersion() - { - // Advance the minor version, mount should still work - this.Enlistment.UnmountScalar(); - ScalarHelpers.SaveDiskLayoutVersion( - this.Enlistment.DotScalarRoot, - this.currentDiskMajorVersion.ToString(), - (CurrentDiskLayoutMinorVersion + 1).ToString()); - this.Enlistment.TryMountScalar().ShouldBeTrue("Mount should succeed because only the minor version advanced"); - - // Advance the major version, mount should fail - this.Enlistment.UnmountScalar(); - ScalarHelpers.SaveDiskLayoutVersion( - this.Enlistment.DotScalarRoot, - (this.currentDiskMajorVersion + 1).ToString(), - CurrentDiskLayoutMinorVersion.ToString()); - this.Enlistment.TryMountScalar().ShouldBeFalse("Mount should fail because the major version has advanced"); - } - - [TestCase] - public void MountFailsIfBeforeMinimumVersion() - { - // Mount should fail if on disk version is below minimum supported version - this.Enlistment.UnmountScalar(); - ScalarHelpers.SaveDiskLayoutVersion( - this.Enlistment.DotScalarRoot, - (this.currentDiskMinimumMajorVersion - 1).ToString(), - CurrentDiskLayoutMinorVersion.ToString()); - this.Enlistment.TryMountScalar().ShouldBeFalse("Mount should fail because we are before minimum version"); - } - } -} diff --git a/Scalar.FunctionalTests/Tests/EnlistmentPerFixture/CloneTests.cs b/Scalar.FunctionalTests/Tests/EnlistmentPerFixture/CloneTests.cs index 90c5594b43..b2f4ee27a8 100644 --- a/Scalar.FunctionalTests/Tests/EnlistmentPerFixture/CloneTests.cs +++ b/Scalar.FunctionalTests/Tests/EnlistmentPerFixture/CloneTests.cs @@ -15,19 +15,11 @@ public class CloneTests : TestsWithEnlistmentPerFixture private const int ScalarGenericError = 3; [TestCase] - public void CloneInsideMountedEnlistment() + public void CloneInsideExistingEnlistment() { this.SubfolderCloneShouldFail(); } - [TestCase] - public void CloneInsideUnmountedEnlistment() - { - this.Enlistment.UnmountScalar(); - this.SubfolderCloneShouldFail(); - this.Enlistment.MountScalar(); - } - [TestCase] public void CloneWithLocalCachePathWithinSrc() { @@ -53,20 +45,19 @@ public void SparseCloneWithNoFetchOfCommitsAndTreesSucceeds() try { - enlistment = ScalarFunctionalTestEnlistment.CloneAndMountWithPerRepoCache(ScalarTestConfig.PathToScalar, skipFetchCommitsAndTrees: true); + enlistment = ScalarFunctionalTestEnlistment.CloneWithPerRepoCache(ScalarTestConfig.PathToScalar, skipFetchCommitsAndTrees: true); ProcessResult result = GitProcess.InvokeProcess(enlistment.RepoRoot, "status"); result.ExitCode.ShouldEqual(0, result.Errors); } finally { - enlistment?.UnmountAndDeleteAll(); + enlistment?.DeleteAll(); } } [TestCase] [Category(Categories.MacOnly)] - [Category(Categories.NeedsUpdatesForNonVirtualizedMode)] public void CloneWithDefaultLocalCacheLocation() { FileSystemRunner fileSystem = FileSystemRunner.DefaultRunner; @@ -77,8 +68,6 @@ public void CloneWithDefaultLocalCacheLocation() ProcessStartInfo processInfo = new ProcessStartInfo(ScalarTestConfig.PathToScalar); - // Needs update for non-virtualized mode: this used to have --no-mount to avoid an issue - // with registering the mount with the service. processInfo.Arguments = $"clone {Properties.Settings.Default.RepoToClone} {newEnlistmentRoot} --no-fetch-commits-and-trees"; processInfo.WindowStyle = ProcessWindowStyle.Hidden; processInfo.CreateNoWindow = true; @@ -102,14 +91,14 @@ public void CloneWithDefaultLocalCacheLocation() [TestCase] public void CloneToPathWithSpaces() { - ScalarFunctionalTestEnlistment enlistment = ScalarFunctionalTestEnlistment.CloneAndMountEnlistmentWithSpacesInPath(ScalarTestConfig.PathToScalar); - enlistment.UnmountAndDeleteAll(); + ScalarFunctionalTestEnlistment enlistment = ScalarFunctionalTestEnlistment.CloneEnlistmentWithSpacesInPath(ScalarTestConfig.PathToScalar); + enlistment.DeleteAll(); } [TestCase] public void CloneCreatesCorrectFilesInRoot() { - ScalarFunctionalTestEnlistment enlistment = ScalarFunctionalTestEnlistment.CloneAndMount(ScalarTestConfig.PathToScalar); + ScalarFunctionalTestEnlistment enlistment = ScalarFunctionalTestEnlistment.Clone(ScalarTestConfig.PathToScalar); try { Directory.GetFiles(enlistment.EnlistmentRoot).ShouldBeEmpty("There should be no files in the enlistment root after cloning"); @@ -120,7 +109,7 @@ public void CloneCreatesCorrectFilesInRoot() } finally { - enlistment.UnmountAndDeleteAll(); + enlistment.DeleteAll(); } } diff --git a/Scalar.FunctionalTests/Tests/EnlistmentPerFixture/FetchCommitsAndTreesWithoutSharedCacheTests.cs b/Scalar.FunctionalTests/Tests/EnlistmentPerFixture/FetchCommitsAndTreesWithoutSharedCacheTests.cs index e8506ab9f7..39b0b8f1fe 100644 --- a/Scalar.FunctionalTests/Tests/EnlistmentPerFixture/FetchCommitsAndTreesWithoutSharedCacheTests.cs +++ b/Scalar.FunctionalTests/Tests/EnlistmentPerFixture/FetchCommitsAndTreesWithoutSharedCacheTests.cs @@ -104,7 +104,7 @@ public void FetchCommitsAndTreesCleansUpBadPrefetchPack() [TestCase, Order(4)] public void FetchCommitsAndTreesCleansUpOldPrefetchPack() { - this.Enlistment.UnmountScalar(); + this.Enlistment.UnregisterRepo(); string[] prefetchPacks = this.ReadPrefetchPackFileNames(); long oldestPackTimestamp = this.GetOldestPackTimestamp(prefetchPacks); @@ -135,7 +135,7 @@ public void FetchCommitsAndTreesCleansUpOldPrefetchPack() [Category(Categories.MacTODO.TestNeedsToLockFile)] public void FetchCommitsAndTreesFailsWhenItCannotRemoveABadPrefetchPack() { - this.Enlistment.UnmountScalar(); + this.Enlistment.UnregisterRepo(); string[] prefetchPacks = this.ReadPrefetchPackFileNames(); long mostRecentPackTimestamp = this.GetMostRecentPackTimestamp(prefetchPacks); @@ -168,7 +168,7 @@ public void FetchCommitsAndTreesFailsWhenItCannotRemoveABadPrefetchPack() [Category(Categories.MacTODO.TestNeedsToLockFile)] public void FetchCommitsAndTreesFailsWhenItCannotRemoveAPrefetchPackNewerThanBadPrefetchPack() { - this.Enlistment.UnmountScalar(); + this.Enlistment.UnregisterRepo(); string[] prefetchPacks = this.ReadPrefetchPackFileNames(); long oldestPackTimestamp = this.GetOldestPackTimestamp(prefetchPacks); @@ -202,7 +202,7 @@ public void FetchCommitsAndTreesFailsWhenItCannotRemoveAPrefetchPackNewerThanBad [Category(Categories.MacTODO.TestNeedsToLockFile)] public void FetchCommitsAndTreesFailsWhenItCannotRemoveAPrefetchIdxNewerThanBadPrefetchPack() { - this.Enlistment.UnmountScalar(); + this.Enlistment.UnregisterRepo(); string[] prefetchPacks = this.ReadPrefetchPackFileNames(); long oldestPackTimestamp = this.GetOldestPackTimestamp(prefetchPacks); @@ -239,7 +239,7 @@ public void FetchCommitsAndTreesFailsWhenItCannotRemoveAPrefetchIdxNewerThanBadP [TestCase, Order(8)] public void FetchCommitsAndTreesCleansUpStaleTempPrefetchPacks() { - this.Enlistment.UnmountScalar(); + this.Enlistment.UnregisterRepo(); // Create stale packs and idxs in the temp folder string stalePackContents = "StalePack"; diff --git a/Scalar.FunctionalTests/Tests/EnlistmentPerFixture/LooseObjectStepTests.cs b/Scalar.FunctionalTests/Tests/EnlistmentPerFixture/LooseObjectStepTests.cs index ff35b1d85e..b99d787abc 100644 --- a/Scalar.FunctionalTests/Tests/EnlistmentPerFixture/LooseObjectStepTests.cs +++ b/Scalar.FunctionalTests/Tests/EnlistmentPerFixture/LooseObjectStepTests.cs @@ -31,7 +31,7 @@ public LooseObjectStepTests() [Order(1)] public void NoLooseObjectsDoesNothing() { - this.Enlistment.UnmountScalar(); + this.Enlistment.UnregisterRepo(); this.DeleteFiles(this.GetLooseObjectFiles()); this.DeleteFiles(this.GetLooseObjectFiles()); @@ -133,7 +133,7 @@ public void CorruptLooseObjectIsDeleted() private void ClearAllObjects() { - this.Enlistment.UnmountScalar(); + this.Enlistment.UnregisterRepo(); // Delete/Move any starting loose objects and packfiles this.DeleteFiles(this.GetLooseObjectFiles()); diff --git a/Scalar.FunctionalTests/Tests/EnlistmentPerFixture/MountTests.cs b/Scalar.FunctionalTests/Tests/EnlistmentPerFixture/MountTests.cs deleted file mode 100644 index 5ce991fa3c..0000000000 --- a/Scalar.FunctionalTests/Tests/EnlistmentPerFixture/MountTests.cs +++ /dev/null @@ -1,250 +0,0 @@ -using NUnit.Framework; -using Scalar.FunctionalTests.FileSystemRunners; -using Scalar.FunctionalTests.Properties; -using Scalar.FunctionalTests.Should; -using Scalar.FunctionalTests.Tools; -using Scalar.Tests.Should; -using System.Diagnostics; -using System.IO; - -namespace Scalar.FunctionalTests.Tests.EnlistmentPerFixture -{ - [TestFixture] - [Category(Categories.ExtraCoverage)] - [Category(Categories.NeedsUpdatesForNonVirtualizedMode)] - public class MountTests : TestsWithEnlistmentPerFixture - { - private const int ScalarGenericError = 3; - private const uint GenericRead = 2147483648; - private const uint FileFlagBackupSemantics = 3355443; - - private FileSystemRunner fileSystem; - - public MountTests() - { - this.fileSystem = new SystemIORunner(); - } - - [TestCaseSource(typeof(MountSubfolders), MountSubfolders.MountFolders)] - public void SecondMountAttemptFails(string mountSubfolder) - { - this.MountShouldFail(0, "already mounted", this.Enlistment.GetSourcePath(mountSubfolder)); - } - - [TestCase] - public void MountFailsOutsideEnlistment() - { - this.MountShouldFail("is not a valid Scalar enlistment", Path.GetDirectoryName(this.Enlistment.EnlistmentRoot)); - } - - [TestCase] - public void MountSetsCoreHooksPath() - { - this.Enlistment.UnmountScalar(); - - GitProcess.Invoke(this.Enlistment.RepoRoot, "config --unset core.hookspath"); - string.IsNullOrWhiteSpace( - GitProcess.Invoke(this.Enlistment.RepoRoot, "config core.hookspath")) - .ShouldBeTrue(); - - this.Enlistment.MountScalar(); - string expectedHooksPath = Path.Combine(this.Enlistment.RepoRoot, ".git", "hooks"); - expectedHooksPath = GitHelpers.ConvertPathToGitFormat(expectedHooksPath); - - GitProcess.Invoke( - this.Enlistment.RepoRoot, "config core.hookspath") - .Trim('\n') - .ShouldEqual(expectedHooksPath); - } - - [TestCase] - public void MountChangesMountId() - { - string mountId = GitProcess.Invoke(this.Enlistment.RepoRoot, "config scalar.mount-id") - .Trim('\n'); - this.Enlistment.UnmountScalar(); - this.Enlistment.MountScalar(); - GitProcess.Invoke(this.Enlistment.RepoRoot, "config scalar.mount-id") - .Trim('\n') - .ShouldNotEqual(mountId, "scalar.mount-id should change on every mount"); - } - - [TestCase] - public void MountFailsWhenNoOnDiskVersion() - { - this.Enlistment.UnmountScalar(); - - // Get the current disk layout version - string majorVersion; - string minorVersion; - ScalarHelpers.GetPersistedDiskLayoutVersion(this.Enlistment.DotScalarRoot, out majorVersion, out minorVersion); - - int majorVersionNum; - int minorVersionNum; - int.TryParse(majorVersion.ShouldNotBeNull(), out majorVersionNum).ShouldEqual(true); - int.TryParse(minorVersion.ShouldNotBeNull(), out minorVersionNum).ShouldEqual(true); - - // Move the RepoMetadata database to a temp file - string versionDatabasePath = Path.Combine(this.Enlistment.DotScalarRoot, ScalarHelpers.RepoMetadataName); - versionDatabasePath.ShouldBeAFile(this.fileSystem); - - string tempDatabasePath = versionDatabasePath + "_MountFailsWhenNoOnDiskVersion"; - tempDatabasePath.ShouldNotExistOnDisk(this.fileSystem); - - this.fileSystem.MoveFile(versionDatabasePath, tempDatabasePath); - versionDatabasePath.ShouldNotExistOnDisk(this.fileSystem); - - this.MountShouldFail("Failed to upgrade repo disk layout"); - - // Move the RepoMetadata database back - this.fileSystem.DeleteFile(versionDatabasePath); - this.fileSystem.MoveFile(tempDatabasePath, versionDatabasePath); - tempDatabasePath.ShouldNotExistOnDisk(this.fileSystem); - versionDatabasePath.ShouldBeAFile(this.fileSystem); - - this.Enlistment.MountScalar(); - } - - [TestCase] - public void MountFailsWhenNoGitObjectsRootInGitConfig() - { - this.Enlistment.UnmountScalar(); - string gitObjectsRoot = ScalarHelpers.GetObjectsRootFromGitConfig(this.Enlistment.RepoRoot); - - GitProcess.Invoke(this.Enlistment.RepoRoot, $"config --local --unset-all {ScalarHelpers.GitConfigObjectCache}"); - this.MountShouldFail("Failed to determine git objects root from git config"); - GitProcess.Invoke(this.Enlistment.RepoRoot, $"config--local {ScalarHelpers.GitConfigObjectCache} {gitObjectsRoot}"); - this.Enlistment.MountScalar(); - } - - [TestCase] - public void MountRegeneratesAlternatesFileWhenMissingGitObjectsRoot() - { - this.Enlistment.UnmountScalar(); - - string objectsRoot = ScalarHelpers.GetObjectsRootFromGitConfig(this.Enlistment.RepoRoot); - - string alternatesFilePath = Path.Combine(this.Enlistment.RepoRoot, ".git", "objects", "info", "alternates"); - alternatesFilePath.ShouldBeAFile(this.fileSystem).WithContents(objectsRoot); - this.fileSystem.WriteAllText(alternatesFilePath, "Z:\\invalidPath"); - - this.Enlistment.MountScalar(); - - alternatesFilePath.ShouldBeAFile(this.fileSystem).WithContents(objectsRoot); - } - - [TestCase] - public void MountRegeneratesAlternatesFileWhenMissingFromDisk() - { - this.Enlistment.UnmountScalar(); - - string objectsRoot = ScalarHelpers.GetObjectsRootFromGitConfig(this.Enlistment.RepoRoot); - - string alternatesFilePath = Path.Combine(this.Enlistment.RepoRoot, ".git", "objects", "info", "alternates"); - alternatesFilePath.ShouldBeAFile(this.fileSystem).WithContents(objectsRoot); - this.fileSystem.DeleteFile(alternatesFilePath); - - this.Enlistment.MountScalar(); - - alternatesFilePath.ShouldBeAFile(this.fileSystem).WithContents(objectsRoot); - } - - [TestCaseSource(typeof(MountSubfolders), MountSubfolders.MountFolders)] - public void MountFailsAfterBreakingDowngrade(string mountSubfolder) - { - MountSubfolders.EnsureSubfoldersOnDisk(this.Enlistment, this.fileSystem); - this.Enlistment.UnmountScalar(); - - string majorVersion; - string minorVersion; - ScalarHelpers.GetPersistedDiskLayoutVersion(this.Enlistment.DotScalarRoot, out majorVersion, out minorVersion); - - int majorVersionNum; - int minorVersionNum; - int.TryParse(majorVersion.ShouldNotBeNull(), out majorVersionNum).ShouldEqual(true); - int.TryParse(minorVersion.ShouldNotBeNull(), out minorVersionNum).ShouldEqual(true); - - ScalarHelpers.SaveDiskLayoutVersion(this.Enlistment.DotScalarRoot, (majorVersionNum + 1).ToString(), "0"); - - this.MountShouldFail("do not allow mounting after downgrade", this.Enlistment.GetSourcePath(mountSubfolder)); - - ScalarHelpers.SaveDiskLayoutVersion(this.Enlistment.DotScalarRoot, majorVersionNum.ToString(), minorVersionNum.ToString()); - this.Enlistment.MountScalar(); - } - - [TestCaseSource(typeof(MountSubfolders), MountSubfolders.MountFolders)] - public void MountFailsUpgradingFromInvalidUpgradePath(string mountSubfolder) - { - MountSubfolders.EnsureSubfoldersOnDisk(this.Enlistment, this.fileSystem); - string headCommitId = GitProcess.Invoke(this.Enlistment.RepoRoot, "rev-parse HEAD"); - - this.Enlistment.UnmountScalar(); - - string majorVersion; - string minorVersion; - ScalarHelpers.GetPersistedDiskLayoutVersion(this.Enlistment.DotScalarRoot, out majorVersion, out minorVersion); - - int majorVersionNum; - int minorVersionNum; - int.TryParse(majorVersion.ShouldNotBeNull(), out majorVersionNum).ShouldEqual(true); - int.TryParse(minorVersion.ShouldNotBeNull(), out minorVersionNum).ShouldEqual(true); - - // 1 will always be below the minumum support version number - ScalarHelpers.SaveDiskLayoutVersion(this.Enlistment.DotScalarRoot, "1", "0"); - this.MountShouldFail("Breaking change to Scalar disk layout has been made since cloning", this.Enlistment.GetSourcePath(mountSubfolder)); - - ScalarHelpers.SaveDiskLayoutVersion(this.Enlistment.DotScalarRoot, majorVersionNum.ToString(), minorVersionNum.ToString()); - this.Enlistment.MountScalar(); - } - - private void MountShouldFail(int expectedExitCode, string expectedErrorMessage, string mountWorkingDirectory = null) - { - string enlistmentRoot = this.Enlistment.EnlistmentRoot; - - // TODO: 865304 Use app.config instead of --internal* arguments - ProcessStartInfo processInfo = new ProcessStartInfo(ScalarTestConfig.PathToScalar); - processInfo.Arguments = "mount " + TestConstants.InternalUseOnlyFlag + " " + ScalarHelpers.GetInternalParameter(); - processInfo.WindowStyle = ProcessWindowStyle.Hidden; - processInfo.WorkingDirectory = string.IsNullOrEmpty(mountWorkingDirectory) ? enlistmentRoot : mountWorkingDirectory; - processInfo.UseShellExecute = false; - processInfo.RedirectStandardOutput = true; - - ProcessResult result = ProcessHelper.Run(processInfo); - result.ExitCode.ShouldEqual(expectedExitCode, $"mount exit code was not {expectedExitCode}. Output: {result.Output}"); - result.Output.ShouldContain(expectedErrorMessage); - } - - private void MountShouldFail(string expectedErrorMessage, string mountWorkingDirectory = null) - { - this.MountShouldFail(ScalarGenericError, expectedErrorMessage, mountWorkingDirectory); - } - - private class MountSubfolders - { - public const string MountFolders = "Folders"; - private static object[] mountFolders = - { - new object[] { string.Empty }, - new object[] { "GVFS" }, - }; - - public static object[] Folders - { - get - { - return mountFolders; - } - } - - public static void EnsureSubfoldersOnDisk(ScalarFunctionalTestEnlistment enlistment, FileSystemRunner fileSystem) - { - // Enumerate the directory to ensure that the folder is on disk after Scalar is unmounted - foreach (object[] folder in Folders) - { - string folderPath = enlistment.GetSourcePath((string)folder[0]); - folderPath.ShouldBeADirectory(fileSystem).WithItems(); - } - } - } - } -} diff --git a/Scalar.FunctionalTests/Tests/EnlistmentPerFixture/TestsWithEnlistmentPerFixture.cs b/Scalar.FunctionalTests/Tests/EnlistmentPerFixture/TestsWithEnlistmentPerFixture.cs index 40b2ad867f..00d632d562 100644 --- a/Scalar.FunctionalTests/Tests/EnlistmentPerFixture/TestsWithEnlistmentPerFixture.cs +++ b/Scalar.FunctionalTests/Tests/EnlistmentPerFixture/TestsWithEnlistmentPerFixture.cs @@ -27,13 +27,13 @@ public virtual void CreateEnlistment() { if (this.forcePerRepoObjectCache) { - this.Enlistment = ScalarFunctionalTestEnlistment.CloneAndMountWithPerRepoCache( + this.Enlistment = ScalarFunctionalTestEnlistment.CloneWithPerRepoCache( ScalarTestConfig.PathToScalar, this.skipFetchCommitsAndTreesDuringClone); } else { - this.Enlistment = ScalarFunctionalTestEnlistment.CloneAndMount(ScalarTestConfig.PathToScalar, fullClone: this.fullClone); + this.Enlistment = ScalarFunctionalTestEnlistment.Clone(ScalarTestConfig.PathToScalar, fullClone: this.fullClone); } } @@ -42,7 +42,7 @@ public virtual void DeleteEnlistment() { if (this.Enlistment != null) { - this.Enlistment.UnmountAndDeleteAll(); + this.Enlistment.DeleteAll(); } } } diff --git a/Scalar.FunctionalTests/Tests/EnlistmentPerFixture/UnmountTests.cs b/Scalar.FunctionalTests/Tests/EnlistmentPerFixture/UnmountTests.cs deleted file mode 100644 index d01dd6d5f9..0000000000 --- a/Scalar.FunctionalTests/Tests/EnlistmentPerFixture/UnmountTests.cs +++ /dev/null @@ -1,85 +0,0 @@ -using NUnit.Framework; -using Scalar.FunctionalTests.FileSystemRunners; -using Scalar.FunctionalTests.Tools; -using Scalar.Tests.Should; -using System.Diagnostics; -using System.IO; -using System.Threading; - -namespace Scalar.FunctionalTests.Tests.EnlistmentPerFixture -{ - [TestFixture] - [Category(Categories.ExtraCoverage)] - [Category(Categories.NeedsUpdatesForNonVirtualizedMode)] - public class UnmountTests : TestsWithEnlistmentPerFixture - { - private FileSystemRunner fileSystem; - - public UnmountTests() - { - this.fileSystem = new SystemIORunner(); - } - - [SetUp] - public void SetupTest() - { - ScalarProcess scalarProcess = new ScalarProcess( - ScalarTestConfig.PathToScalar, - this.Enlistment.EnlistmentRoot, - Path.Combine(this.Enlistment.EnlistmentRoot, ScalarTestConfig.DotScalarRoot)); - - if (!scalarProcess.IsEnlistmentMounted()) - { - scalarProcess.Mount(); - } - } - - [TestCase] - public void UnmountWaitsForLock() - { - ManualResetEventSlim lockHolder = GitHelpers.AcquireScalarLock(this.Enlistment, out _); - - using (Process unmountingProcess = this.StartUnmount()) - { - unmountingProcess.WaitForExit(3000).ShouldEqual(false, "Unmount completed while lock was acquired."); - - // Release the lock. - lockHolder.Set(); - - unmountingProcess.WaitForExit(10000).ShouldEqual(true, "Unmount didn't complete as expected."); - } - } - - [TestCase] - public void UnmountSkipLock() - { - ManualResetEventSlim lockHolder = GitHelpers.AcquireScalarLock(this.Enlistment, out _, Timeout.Infinite, true); - - using (Process unmountingProcess = this.StartUnmount("--skip-wait-for-lock")) - { - unmountingProcess.WaitForExit(10000).ShouldEqual(true, "Unmount didn't complete as expected."); - } - - // Signal process holding lock to terminate and release lock. - lockHolder.Set(); - } - - private Process StartUnmount(string extraParams = "") - { - string enlistmentRoot = this.Enlistment.EnlistmentRoot; - - // TODO: 865304 Use app.config instead of --internal* arguments - ProcessStartInfo processInfo = new ProcessStartInfo(ScalarTestConfig.PathToScalar); - processInfo.Arguments = "unmount " + extraParams + " " + TestConstants.InternalUseOnlyFlag + " " + ScalarHelpers.GetInternalParameter(); - processInfo.WindowStyle = ProcessWindowStyle.Hidden; - processInfo.WorkingDirectory = enlistmentRoot; - processInfo.UseShellExecute = false; - - Process executingProcess = new Process(); - executingProcess.StartInfo = processInfo; - executingProcess.Start(); - - return executingProcess; - } - } -} diff --git a/Scalar.FunctionalTests/Tests/EnlistmentPerTestCase/CaseOnlyFolderRenameTests.cs b/Scalar.FunctionalTests/Tests/EnlistmentPerTestCase/CaseOnlyFolderRenameTests.cs deleted file mode 100644 index 335d4f9563..0000000000 --- a/Scalar.FunctionalTests/Tests/EnlistmentPerTestCase/CaseOnlyFolderRenameTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -using NUnit.Framework; -using Scalar.FunctionalTests.FileSystemRunners; -using Scalar.FunctionalTests.Should; -using Scalar.Tests.Should; -using System.IO; - -namespace Scalar.FunctionalTests.Tests.EnlistmentPerTestCase -{ - [TestFixture] - [Category(Categories.NeedsUpdatesForNonVirtualizedMode)] - public class CaseOnlyFolderRenameTests : TestsWithEnlistmentPerTestCase - { - private FileSystemRunner fileSystem; - - public CaseOnlyFolderRenameTests() - { - this.fileSystem = new BashRunner(); - } - - // MacOnly because renames of partial folders are blocked on Windows - [TestCase] - [Category(Categories.MacOnly)] - public void CaseRenameFoldersAndRemountAndRenameAgain() - { - // Projected folder without a physical folder - string parentFolderName = "Scalar"; - string oldScalarSubFolderName = "Scalar"; - string oldScalarSubFolderPath = Path.Combine(parentFolderName, oldScalarSubFolderName); - string newScalarSubFolderName = "scalar"; - string newScalarSubFolderPath = Path.Combine(parentFolderName, newScalarSubFolderName); - - this.Enlistment.GetSourcePath(oldScalarSubFolderPath).ShouldBeADirectory(this.fileSystem).WithCaseMatchingName(oldScalarSubFolderName); - - this.fileSystem.MoveFile(this.Enlistment.GetSourcePath(oldScalarSubFolderPath), this.Enlistment.GetSourcePath(newScalarSubFolderPath)); - - this.Enlistment.GetSourcePath(newScalarSubFolderPath).ShouldBeADirectory(this.fileSystem).WithCaseMatchingName(newScalarSubFolderName); - - // Projected folder with a physical folder - string oldTestsSubFolderName = "Scalar.FunctionalTests"; - string oldTestsSubFolderPath = Path.Combine(parentFolderName, oldTestsSubFolderName); - string newTestsSubFolderName = "scalar.functionaltests"; - string newTestsSubFolderPath = Path.Combine(parentFolderName, newTestsSubFolderName); - - string fileToAdd = "NewFile.txt"; - string fileToAddContent = "This is new file text."; - string fileToAddPath = this.Enlistment.GetSourcePath(Path.Combine(oldTestsSubFolderPath, fileToAdd)); - this.fileSystem.WriteAllText(fileToAddPath, fileToAddContent); - - this.Enlistment.GetSourcePath(oldTestsSubFolderPath).ShouldBeADirectory(this.fileSystem).WithCaseMatchingName(oldTestsSubFolderName); - - this.fileSystem.MoveFile(this.Enlistment.GetSourcePath(oldTestsSubFolderPath), this.Enlistment.GetSourcePath(newTestsSubFolderPath)); - - this.Enlistment.GetSourcePath(newTestsSubFolderPath).ShouldBeADirectory(this.fileSystem).WithCaseMatchingName(newTestsSubFolderName); - - // Remount - this.Enlistment.UnmountScalar(); - this.Enlistment.MountScalar(); - - this.Enlistment.GetSourcePath(newScalarSubFolderPath).ShouldBeADirectory(this.fileSystem).WithCaseMatchingName(newScalarSubFolderName); - this.Enlistment.GetSourcePath(newTestsSubFolderPath).ShouldBeADirectory(this.fileSystem).WithCaseMatchingName(newTestsSubFolderName); - this.Enlistment.GetSourcePath(Path.Combine(newTestsSubFolderPath, fileToAdd)).ShouldBeAFile(this.fileSystem).WithContents().ShouldEqual(fileToAddContent); - - // Rename each folder again - string finalScalarSubFolderName = "gvFS"; - string finalScalarSubFolderPath = Path.Combine(parentFolderName, finalScalarSubFolderName); - this.fileSystem.MoveFile(this.Enlistment.GetSourcePath(newScalarSubFolderPath), this.Enlistment.GetSourcePath(finalScalarSubFolderPath)); - this.Enlistment.GetSourcePath(finalScalarSubFolderPath).ShouldBeADirectory(this.fileSystem).WithCaseMatchingName(finalScalarSubFolderName); - - string finalTestsSubFolderName = "scalar.FunctionalTESTS"; - string finalTestsSubFolderPath = Path.Combine(parentFolderName, finalTestsSubFolderName); - this.fileSystem.MoveFile(this.Enlistment.GetSourcePath(newTestsSubFolderPath), this.Enlistment.GetSourcePath(finalTestsSubFolderPath)); - this.Enlistment.GetSourcePath(finalTestsSubFolderPath).ShouldBeADirectory(this.fileSystem).WithCaseMatchingName(finalTestsSubFolderName); - this.Enlistment.GetSourcePath(Path.Combine(finalTestsSubFolderPath, fileToAdd)).ShouldBeAFile(this.fileSystem).WithContents().ShouldEqual(fileToAddContent); - } - } -} diff --git a/Scalar.FunctionalTests/Tests/EnlistmentPerTestCase/TestsWithEnlistmentPerTestCase.cs b/Scalar.FunctionalTests/Tests/EnlistmentPerTestCase/TestsWithEnlistmentPerTestCase.cs index d84e0a796c..214a4a89eb 100644 --- a/Scalar.FunctionalTests/Tests/EnlistmentPerTestCase/TestsWithEnlistmentPerTestCase.cs +++ b/Scalar.FunctionalTests/Tests/EnlistmentPerTestCase/TestsWithEnlistmentPerTestCase.cs @@ -23,13 +23,13 @@ public virtual void CreateEnlistment() { if (this.forcePerRepoObjectCache) { - this.Enlistment = ScalarFunctionalTestEnlistment.CloneAndMountWithPerRepoCache( + this.Enlistment = ScalarFunctionalTestEnlistment.CloneWithPerRepoCache( ScalarTestConfig.PathToScalar, skipFetchCommitsAndTrees: false); } else { - this.Enlistment = ScalarFunctionalTestEnlistment.CloneAndMount(ScalarTestConfig.PathToScalar); + this.Enlistment = ScalarFunctionalTestEnlistment.Clone(ScalarTestConfig.PathToScalar); } } @@ -38,7 +38,7 @@ public virtual void DeleteEnlistment() { if (this.Enlistment != null) { - this.Enlistment.UnmountAndDeleteAll(); + this.Enlistment.DeleteAll(); } } } diff --git a/Scalar.FunctionalTests/Tests/GitCommands/AddStageTests.cs b/Scalar.FunctionalTests/Tests/GitCommands/AddStageTests.cs index 5434d6522f..e5b879a816 100644 --- a/Scalar.FunctionalTests/Tests/GitCommands/AddStageTests.cs +++ b/Scalar.FunctionalTests/Tests/GitCommands/AddStageTests.cs @@ -1,8 +1,5 @@ using NUnit.Framework; using Scalar.FunctionalTests.Properties; -using Scalar.FunctionalTests.Tools; -using System.IO; -using System.Threading; namespace Scalar.FunctionalTests.Tests.GitCommands { @@ -42,28 +39,5 @@ public void AddAndStageHardLinksTest() this.ValidateGitCommand("stage AuthoringTestsLink.md"); this.RunGitCommand("commit -m \"Created AuthoringTestsLink.md\""); } - - [TestCase, Order(4)] - public void AddAllowsPlaceholderCreation() - { - this.CommandAllowsPlaceholderCreation("add", "GVFS", "GVFS", "Program.cs"); - } - - [TestCase, Order(5)] - public void StageAllowsPlaceholderCreation() - { - this.CommandAllowsPlaceholderCreation("stage", "GVFS", "GVFS", "App.config"); - } - - private void CommandAllowsPlaceholderCreation(string command, params string[] fileToReadPathParts) - { - string fileToRead = Path.Combine(fileToReadPathParts); - this.EditFile($"Some new content for {command}.", "Protocol.md"); - ManualResetEventSlim resetEvent = GitHelpers.RunGitCommandWithWaitAndStdIn(this.Enlistment, resetTimeout: 3000, command: $"{command} -p", stdinToQuit: "q", processId: out _); - this.FileContentsShouldMatch(fileToRead); - this.ValidateGitCommand("--no-optional-locks status"); - resetEvent.Wait(); - this.RunGitCommand("reset --hard"); - } } } diff --git a/Scalar.FunctionalTests/Tests/GitCommands/GitRepoTests.cs b/Scalar.FunctionalTests/Tests/GitCommands/GitRepoTests.cs index bf543f0e30..15321a48b7 100644 --- a/Scalar.FunctionalTests/Tests/GitCommands/GitRepoTests.cs +++ b/Scalar.FunctionalTests/Tests/GitCommands/GitRepoTests.cs @@ -206,7 +206,7 @@ protected virtual void CreateEnlistment() protected void CreateEnlistment(string commitish = null) { - this.Enlistment = ScalarFunctionalTestEnlistment.CloneAndMount( + this.Enlistment = ScalarFunctionalTestEnlistment.Clone( ScalarTestConfig.PathToScalar, commitish: commitish, fullClone: this.validateWorkingTree != Settings.ValidateWorkingTreeMode.SparseMode); @@ -219,7 +219,7 @@ protected virtual void DeleteEnlistment() { if (this.Enlistment != null) { - this.Enlistment.UnmountAndDeleteAll(); + this.Enlistment.DeleteAll(); } if (this.ControlGitRepo != null) diff --git a/Scalar.FunctionalTests/Tests/GitCommands/ResetMixedTests.cs b/Scalar.FunctionalTests/Tests/GitCommands/ResetMixedTests.cs index 74c35941ab..c66fe90b8a 100644 --- a/Scalar.FunctionalTests/Tests/GitCommands/ResetMixedTests.cs +++ b/Scalar.FunctionalTests/Tests/GitCommands/ResetMixedTests.cs @@ -43,19 +43,6 @@ public void ResetMixedAndCheckoutOrphanBranch() this.FilesShouldMatchCheckoutOfTargetBranch(); } - [TestCase] - public void ResetMixedAndRemount() - { - this.ValidateGitCommand("checkout " + GitRepoTests.ConflictTargetBranch); - this.ValidateGitCommand("reset --mixed HEAD~1"); - this.FilesShouldMatchCheckoutOfTargetBranch(); - - this.Enlistment.UnmountScalar(); - this.Enlistment.MountScalar(); - this.ValidateGitCommand("status"); - this.FilesShouldMatchCheckoutOfTargetBranch(); - } - [TestCase] public void ResetMixedThenCheckoutWithConflicts() { diff --git a/Scalar.FunctionalTests/Tests/GitCommands/ResetSoftTests.cs b/Scalar.FunctionalTests/Tests/GitCommands/ResetSoftTests.cs index c7d96c5a8b..80770282b7 100644 --- a/Scalar.FunctionalTests/Tests/GitCommands/ResetSoftTests.cs +++ b/Scalar.FunctionalTests/Tests/GitCommands/ResetSoftTests.cs @@ -20,19 +20,6 @@ public void ResetSoft() this.FilesShouldMatchCheckoutOfTargetBranch(); } - [TestCase] - public void ResetSoftThenRemount() - { - this.ValidateGitCommand("checkout " + GitRepoTests.ConflictTargetBranch); - this.ValidateGitCommand("reset --soft HEAD~1"); - this.FilesShouldMatchCheckoutOfTargetBranch(); - - this.Enlistment.UnmountScalar(); - this.Enlistment.MountScalar(); - this.ValidateGitCommand("status"); - this.FilesShouldMatchCheckoutOfTargetBranch(); - } - [TestCase] public void ResetSoftThenCheckoutWithConflicts() { diff --git a/Scalar.FunctionalTests/Tests/GitCommands/StatusTests.cs b/Scalar.FunctionalTests/Tests/GitCommands/StatusTests.cs index 5190390d8c..c727744371 100644 --- a/Scalar.FunctionalTests/Tests/GitCommands/StatusTests.cs +++ b/Scalar.FunctionalTests/Tests/GitCommands/StatusTests.cs @@ -210,7 +210,6 @@ private void WaitForStatusCacheToBeGenerated(bool waitForNewFile = true) private void ValidGitStatusWithRetry(string srcPath) { - this.Enlistment.WaitForBackgroundOperations(); try { this.ValidateGitCommand("status"); diff --git a/Scalar.FunctionalTests/Tests/MultiEnlistmentTests/SharedCacheTests.cs b/Scalar.FunctionalTests/Tests/MultiEnlistmentTests/SharedCacheTests.cs index 72dd404bc4..c928f3b240 100644 --- a/Scalar.FunctionalTests/Tests/MultiEnlistmentTests/SharedCacheTests.cs +++ b/Scalar.FunctionalTests/Tests/MultiEnlistmentTests/SharedCacheTests.cs @@ -38,13 +38,13 @@ public void SetCacheLocation() [TestCase] public void ParallelDownloadsInSharedCache() { - ScalarFunctionalTestEnlistment enlistment1 = this.CloneAndMountEnlistment(); - ScalarFunctionalTestEnlistment enlistment2 = this.CloneAndMountEnlistment(); + ScalarFunctionalTestEnlistment enlistment1 = this.CloneEnlistment(); + ScalarFunctionalTestEnlistment enlistment2 = this.CloneEnlistment(); ScalarFunctionalTestEnlistment enlistment3 = null; Task task1 = Task.Run(() => this.LoadBlobsViaGit(enlistment1)); Task task2 = Task.Run(() => this.LoadBlobsViaGit(enlistment2)); - Task task3 = Task.Run(() => enlistment3 = this.CloneAndMountEnlistment()); + Task task3 = Task.Run(() => enlistment3 = this.CloneEnlistment()); task1.Wait(); task2.Wait(); @@ -54,67 +54,30 @@ public void ParallelDownloadsInSharedCache() task2.Exception.ShouldBeNull(); task3.Exception.ShouldBeNull(); - enlistment1.Status().ShouldContain("Mount status: Ready"); - enlistment2.Status().ShouldContain("Mount status: Ready"); - enlistment3.Status().ShouldContain("Mount status: Ready"); - this.AlternatesFileShouldHaveGitObjectsRoot(enlistment1); this.AlternatesFileShouldHaveGitObjectsRoot(enlistment2); this.AlternatesFileShouldHaveGitObjectsRoot(enlistment3); } - [TestCase] - [Category(Categories.NeedsUpdatesForNonVirtualizedMode)] - public void DeleteObjectsCacheBeforeMount() - { - ScalarFunctionalTestEnlistment enlistment1 = this.CloneAndMountEnlistment(); - ScalarFunctionalTestEnlistment enlistment2 = this.CloneAndMountEnlistment(); - - enlistment1.UnmountScalar(); - - string objectsRoot = ScalarHelpers.GetObjectsRootFromGitConfig(enlistment1.RepoRoot); - objectsRoot.ShouldBeADirectory(this.fileSystem); - RepositoryHelpers.DeleteTestDirectory(objectsRoot); - - enlistment1.MountScalar(); - - Task task1 = Task.Run(() => this.LoadBlobsViaGit(enlistment1)); - Task task2 = Task.Run(() => this.LoadBlobsViaGit(enlistment2)); - task1.Wait(); - task2.Wait(); - task1.Exception.ShouldBeNull(); - task2.Exception.ShouldBeNull(); - - enlistment1.Status().ShouldContain("Mount status: Ready"); - enlistment2.Status().ShouldContain("Mount status: Ready"); - - this.AlternatesFileShouldHaveGitObjectsRoot(enlistment1); - this.AlternatesFileShouldHaveGitObjectsRoot(enlistment2); - } - [TestCase] public void DownloadingACommitWithoutTreesDoesntBreakNextClone() { - ScalarFunctionalTestEnlistment enlistment1 = this.CloneAndMountEnlistment(); + ScalarFunctionalTestEnlistment enlistment1 = this.CloneEnlistment(); GitProcess.Invoke(enlistment1.RepoRoot, "cat-file -s " + WellKnownCommitSha).ShouldEqual("293\n"); - ScalarFunctionalTestEnlistment enlistment2 = this.CloneAndMountEnlistment(WellKnownBranch); - enlistment2.Status().ShouldContain("Mount status: Ready"); + ScalarFunctionalTestEnlistment enlistment2 = this.CloneEnlistment(WellKnownBranch); } [TestCase] - public void MountReusesLocalCacheKeyWhenGitObjectsRootDeleted() + public void GitObjectsRecreatedWhenDownloadingObjects() { - ScalarFunctionalTestEnlistment enlistment = this.CloneAndMountEnlistment(); - - enlistment.UnmountScalar(); + ScalarFunctionalTestEnlistment enlistment = this.CloneEnlistment(); // Find the current git objects root and ensure it's on disk string objectsRoot = ScalarHelpers.GetObjectsRootFromGitConfig(enlistment.RepoRoot); objectsRoot.ShouldBeADirectory(this.fileSystem); RepositoryHelpers.DeleteTestDirectory(objectsRoot); - enlistment.MountScalar(); ScalarHelpers.GetObjectsRootFromGitConfig(enlistment.RepoRoot).ShouldEqual(objectsRoot); @@ -155,7 +118,7 @@ protected override void OnTearDownEnlistmentsDeleted() RepositoryHelpers.DeleteTestDirectory(this.localCacheParentPath); } - private ScalarFunctionalTestEnlistment CloneAndMountEnlistment(string branch = null) + private ScalarFunctionalTestEnlistment CloneEnlistment(string branch = null) { return this.CreateNewEnlistment(this.localCachePath, branch); } diff --git a/Scalar.FunctionalTests/Tests/MultiEnlistmentTests/TestsWithMultiEnlistment.cs b/Scalar.FunctionalTests/Tests/MultiEnlistmentTests/TestsWithMultiEnlistment.cs index 46df05792d..d283f428f0 100644 --- a/Scalar.FunctionalTests/Tests/MultiEnlistmentTests/TestsWithMultiEnlistment.cs +++ b/Scalar.FunctionalTests/Tests/MultiEnlistmentTests/TestsWithMultiEnlistment.cs @@ -13,7 +13,7 @@ public void DeleteEnlistments() { foreach (ScalarFunctionalTestEnlistment enlistment in this.enlistmentsToDelete) { - enlistment.UnmountAndDeleteAll(); + enlistment.DeleteAll(); } this.OnTearDownEnlistmentsDeleted(); @@ -33,7 +33,7 @@ protected ScalarFunctionalTestEnlistment CreateNewEnlistment( string branch = null, bool skipFetchCommitsAndTrees = false) { - ScalarFunctionalTestEnlistment output = ScalarFunctionalTestEnlistment.CloneAndMount( + ScalarFunctionalTestEnlistment output = ScalarFunctionalTestEnlistment.Clone( ScalarTestConfig.PathToScalar, branch, localCacheRoot, diff --git a/Scalar.FunctionalTests/Tools/GitHelpers.cs b/Scalar.FunctionalTests/Tools/GitHelpers.cs index 9f218bce0e..64a65e8050 100644 --- a/Scalar.FunctionalTests/Tools/GitHelpers.cs +++ b/Scalar.FunctionalTests/Tools/GitHelpers.cs @@ -7,49 +7,13 @@ using System.IO; using System.Linq; using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; - namespace Scalar.FunctionalTests.Tools { public static class GitHelpers { - /// - /// This string must match the command name provided in the - /// Scalar.FunctionalTests.LockHolder program. - /// - private const string LockHolderCommandName = @"Scalar.FunctionalTests.LockHolder"; - private const string LockHolderCommand = @"Scalar.FunctionalTests.LockHolder.dll"; - private const string WindowsPathSeparator = "\\"; private const string GitPathSeparator = "/"; - private static string LockHolderCommandPath - { - get - { - // On OSX functional tests are run from inside Publish directory. Dependent - // assemblies including LockHolder test are available at the same level in - // the same directory. - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - return Path.Combine( - Settings.Default.CurrentDirectory, - LockHolderCommand); - } - else - { - // On Windows, FT is run from the Output directory of Scalar.FunctionalTest project. - // LockHolder is a .netcore assembly and can be found inside netcoreapp2.1 - // subdirectory of Scalar.FunctionalTest Output directory. - return Path.Combine( - Settings.Default.CurrentDirectory, - "netcoreapp2.1", - LockHolderCommand); - } - } - } - public static string ConvertPathToGitFormat(string relativePath) { return relativePath.Replace(WindowsPathSeparator, GitPathSeparator); @@ -138,133 +102,12 @@ public static void ValidateGitCommand( } } - /// - /// Acquire the ScalarLock. This method will return once the ScalarLock has been acquired. - /// - /// The ID of the process that acquired the lock. - /// that can be signaled to exit the lock acquisition program. - public static ManualResetEventSlim AcquireScalarLock( - ScalarFunctionalTestEnlistment enlistment, - out int processId, - int resetTimeout = Timeout.Infinite, - bool skipReleaseLock = false) - { - string args = LockHolderCommandPath; - if (skipReleaseLock) - { - args += " --skip-release-lock"; - } - - return RunCommandWithWaitAndStdIn( - enlistment, - resetTimeout, - "dotnet", - args, - GitHelpers.LockHolderCommandName, - "done", - out processId); - } - - /// - /// Run the specified Git command. This method will return once the ScalarLock has been acquired. - /// - /// The ID of the process that acquired the lock. - /// that can be signaled to exit the lock acquisition program. - public static ManualResetEventSlim RunGitCommandWithWaitAndStdIn( - ScalarFunctionalTestEnlistment enlistment, - int resetTimeout, - string command, - string stdinToQuit, - out int processId) - { - return - RunCommandWithWaitAndStdIn( - enlistment, - resetTimeout, - Properties.Settings.Default.PathToGit, - command, - "git " + command, - stdinToQuit, - out processId); - } - public static void ErrorsShouldMatch(string command, ProcessResult expectedResult, ProcessResult actualResult) { actualResult.Errors.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries) .ShouldMatchInOrder(expectedResult.Errors.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries), LinesAreEqual, command + " Errors Lines"); } - /// - /// Run the specified command as an external program. This method will return once the ScalarLock has been acquired. - /// - /// The ID of the process that acquired the lock. - /// that can be signaled to exit the lock acquisition program. - private static ManualResetEventSlim RunCommandWithWaitAndStdIn( - ScalarFunctionalTestEnlistment enlistment, - int resetTimeout, - string pathToCommand, - string args, - string lockingProcessCommandName, - string stdinToQuit, - out int processId) - { - ManualResetEventSlim resetEvent = new ManualResetEventSlim(initialState: false); - - ProcessStartInfo processInfo = new ProcessStartInfo(pathToCommand); - processInfo.WorkingDirectory = enlistment.RepoRoot; - processInfo.UseShellExecute = false; - processInfo.RedirectStandardOutput = true; - processInfo.RedirectStandardError = true; - processInfo.RedirectStandardInput = true; - processInfo.Arguments = args; - - Process holdingProcess = Process.Start(processInfo); - StreamWriter stdin = holdingProcess.StandardInput; - processId = holdingProcess.Id; - - enlistment.WaitForLock(lockingProcessCommandName); - - Task.Run( - () => - { - resetEvent.Wait(resetTimeout); - - try - { - // Make sure to let the holding process end. - if (stdin != null) - { - stdin.WriteLine(stdinToQuit); - stdin.Close(); - } - - if (holdingProcess != null) - { - bool holdingProcessHasExited = holdingProcess.WaitForExit(10000); - - if (!holdingProcess.HasExited) - { - holdingProcess.Kill(); - } - - holdingProcess.Dispose(); - - holdingProcessHasExited.ShouldBeTrue("Locking process did not exit in time."); - } - } - catch (Exception ex) - { - Assert.Fail($"{nameof(RunCommandWithWaitAndStdIn)} exception closing stdin {ex.ToString()}"); - } - finally - { - resetEvent.Set(); - } - }); - - return resetEvent; - } - private static bool LinesAreEqual(string actualLine, string expectedLine) { return actualLine.Equals(expectedLine); diff --git a/Scalar.FunctionalTests/Tools/ScalarFunctionalTestEnlistment.cs b/Scalar.FunctionalTests/Tools/ScalarFunctionalTestEnlistment.cs index 03e5e83b05..100e48a59d 100644 --- a/Scalar.FunctionalTests/Tools/ScalarFunctionalTestEnlistment.cs +++ b/Scalar.FunctionalTests/Tools/ScalarFunctionalTestEnlistment.cs @@ -80,14 +80,14 @@ public string Commitish get; private set; } - public static ScalarFunctionalTestEnlistment CloneAndMountWithPerRepoCache(string pathToGvfs, bool skipFetchCommitsAndTrees) + public static ScalarFunctionalTestEnlistment CloneWithPerRepoCache(string pathToGvfs, bool skipFetchCommitsAndTrees) { string enlistmentRoot = ScalarFunctionalTestEnlistment.GetUniqueEnlistmentRoot(); string localCache = ScalarFunctionalTestEnlistment.GetRepoSpecificLocalCacheRoot(enlistmentRoot); - return CloneAndMount(pathToGvfs, enlistmentRoot, null, localCacheRoot: localCache, skipFetchCommitsAndTrees: skipFetchCommitsAndTrees); + return Clone(pathToGvfs, enlistmentRoot, null, localCacheRoot: localCache, skipFetchCommitsAndTrees: skipFetchCommitsAndTrees); } - public static ScalarFunctionalTestEnlistment CloneAndMount( + public static ScalarFunctionalTestEnlistment Clone( string pathToGvfs, string commitish = null, string localCacheRoot = null, @@ -95,14 +95,14 @@ public static ScalarFunctionalTestEnlistment CloneAndMount( bool fullClone = true) { string enlistmentRoot = ScalarFunctionalTestEnlistment.GetUniqueEnlistmentRoot(); - return CloneAndMount(pathToGvfs, enlistmentRoot, commitish, localCacheRoot, skipFetchCommitsAndTrees, fullClone); + return Clone(pathToGvfs, enlistmentRoot, commitish, localCacheRoot, skipFetchCommitsAndTrees, fullClone); } - public static ScalarFunctionalTestEnlistment CloneAndMountEnlistmentWithSpacesInPath(string pathToGvfs, string commitish = null) + public static ScalarFunctionalTestEnlistment CloneEnlistmentWithSpacesInPath(string pathToGvfs, string commitish = null) { string enlistmentRoot = ScalarFunctionalTestEnlistment.GetUniqueEnlistmentRootWithSpaces(); string localCache = ScalarFunctionalTestEnlistment.GetRepoSpecificLocalCacheRoot(enlistmentRoot); - return CloneAndMount(pathToGvfs, enlistmentRoot, commitish, localCache); + return Clone(pathToGvfs, enlistmentRoot, commitish, localCache); } public static string GetUniqueEnlistmentRoot() @@ -126,7 +126,7 @@ public void DeleteEnlistment() RepositoryHelpers.DeleteTestDirectory(this.EnlistmentRoot); } - public void CloneAndMount(bool skipFetchCommitsAndTrees) + public void Clone(bool skipFetchCommitsAndTrees) { this.scalarProcess.Clone(this.RepoUrl, this.Commitish, skipFetchCommitsAndTrees, fullClone: this.fullClone); @@ -149,25 +149,14 @@ public void CloneAndMount(bool skipFetchCommitsAndTrees) } } - public void MountScalar() - { - this.scalarProcess.Mount(); - } - - public bool TryMountScalar() - { - string output; - return this.TryMountScalar(out output); - } - - public bool TryMountScalar(out string output) + public string FetchCommitsAndTrees(bool failOnError = true, string standardInput = null) { - return this.scalarProcess.TryMount(out output); + return this.scalarProcess.FetchCommitsAndTrees(failOnError, standardInput); } - public string FetchCommitsAndTrees(bool failOnError = true, string standardInput = null) + public void UnregisterRepo() { - return this.scalarProcess.FetchCommitsAndTrees(failOnError, standardInput); + // TODO: #111: Unregister the repo from the service } public void Repair(bool confirm) @@ -200,21 +189,6 @@ public string Status(string trace = null) return this.scalarProcess.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 UnmountScalar() - { - this.scalarProcess.Unmount(); - } - public string GetCacheServer() { return this.scalarProcess.CacheServer("--get"); @@ -225,9 +199,8 @@ public string SetCacheServer(string arg) return this.scalarProcess.CacheServer("--set " + arg); } - public void UnmountAndDeleteAll() + public void DeleteAll() { - this.UnmountScalar(); this.DeleteEnlistment(); } @@ -252,7 +225,7 @@ public string GetObjectPathTo(string objectHash) objectHash.Substring(2)); } - private static ScalarFunctionalTestEnlistment CloneAndMount( + private static ScalarFunctionalTestEnlistment Clone( string pathToGvfs, string enlistmentRoot, string commitish, @@ -270,11 +243,11 @@ private static ScalarFunctionalTestEnlistment CloneAndMount( try { - enlistment.CloneAndMount(skipFetchCommitsAndTrees); + enlistment.Clone(skipFetchCommitsAndTrees); } catch (Exception e) { - Console.WriteLine("Unhandled exception in CloneAndMount: " + e.ToString()); + Console.WriteLine($"Unhandled exception in {nameof(ScalarFunctionalTestEnlistment.Clone)}: " + e.ToString()); TestResultsHelper.OutputScalarLogs(enlistment); throw; } @@ -286,19 +259,5 @@ private static string GetRepoSpecificLocalCacheRoot(string enlistmentRoot) { return Path.Combine(enlistmentRoot, ScalarTestConfig.DotScalarRoot, ".scalarCache"); } - - 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; - } } } diff --git a/Scalar.Installer.Mac/scripts/preinstall b/Scalar.Installer.Mac/scripts/preinstall index 926d22c6be..205e27c904 100755 --- a/Scalar.Installer.Mac/scripts/preinstall +++ b/Scalar.Installer.Mac/scripts/preinstall @@ -2,7 +2,7 @@ SCALARBINPATH="/usr/local/scalar/scalar" if [ -f "${SCALARBINPATH}" ]; then - unmountCmd="${SCALARBINPATH} service --unmount-all" + unmountCmd="${SCALARBINPATH} service --help" echo $unmountCmd eval $unmountCmd || exit 1 fi diff --git a/Scalar.Installer.Mac/uninstall_scalar.sh b/Scalar.Installer.Mac/uninstall_scalar.sh index ed356d3330..78f36e1e70 100755 --- a/Scalar.Installer.Mac/uninstall_scalar.sh +++ b/Scalar.Installer.Mac/uninstall_scalar.sh @@ -9,16 +9,6 @@ ScalarCOMMANDPATH="/usr/local/bin/scalar" UNINSTALLERCOMMANDPATH="/usr/local/bin/uninstall_scalar.sh" INSTALLERPACKAGEID="com.scalar.pkg" -function UnloadKext() -{ - kextLoaded=`/usr/sbin/kextstat -b "$KEXTID" | wc -l` - if [ $kextLoaded -eq "2" ]; then - unloadCmd="sudo /sbin/kextunload -b $KEXTID" - echo "$unloadCmd..." - eval $unloadCmd || exit 1 - fi -} - function UnInstallScalar() { if [ -f "${LAUNCHDAEMONDIRECTORY}/$LOGDAEMONLAUNCHDFILENAME" ]; then @@ -82,7 +72,6 @@ function ForgetPackage() function Run() { - UnloadKext UnInstallScalar ForgetPackage echo "Successfully uninstalled Scalar" diff --git a/Scalar.Installer.Windows/Setup.iss b/Scalar.Installer.Windows/Setup.iss index b900711b63..71ec1740e6 100644 --- a/Scalar.Installer.Windows/Setup.iss +++ b/Scalar.Installer.Windows/Setup.iss @@ -330,69 +330,39 @@ begin DeleteFile(TempFilename); end; -procedure UnmountRepos(); +procedure StopMaintenanceTasks(); var ResultCode: integer; begin - Exec('scalar.exe', 'service --unmount-all', '', SW_HIDE, ewWaitUntilTerminated, ResultCode); + // TODO: #185 Instead of calling --help, use the correct action for stopping the + // maintenance task + Exec('scalar.exe', 'service --help', '', SW_HIDE, ewWaitUntilTerminated, ResultCode); end; -procedure MountRepos(); +procedure RegisterUserWithService(); var StatusText: string; - MountOutput: ansiString; + RegisterOutput: ansiString; ResultCode: integer; MsgBoxText: string; begin StatusText := WizardForm.StatusLabel.Caption; - WizardForm.StatusLabel.Caption := 'Mounting Repos.'; + WizardForm.StatusLabel.Caption := 'Registering with service.'; WizardForm.ProgressGauge.Style := npbstMarquee; - ExecWithResult(ExpandConstant('{app}') + '\scalar.exe', 'service --mount-all', '', SW_HIDE, ewWaitUntilTerminated, ResultCode, MountOutput); + // TODO: #190 Instead of --help use the service action that will register the user with the service + ExecWithResult(ExpandConstant('{app}') + '\scalar.exe', 'service --help', '', SW_HIDE, ewWaitUntilTerminated, ResultCode, RegisterOutput); WizardForm.StatusLabel.Caption := StatusText; WizardForm.ProgressGauge.Style := npbstNormal; if (ResultCode <> 0) then begin - MsgBoxText := 'Mounting one or more repos failed:' + #13#10 + MountOutput; + MsgBoxText := 'Registering with service failed:' + #13#10 + RegisterOutput; SuppressibleMsgBox(MsgBoxText, mbConfirmation, MB_OK, IDOK); ExitCode := 17; end; end; -function ConfirmUnmountAll(): Boolean; -var - MsgBoxResult: integer; - Repos: ansiString; - ResultCode: integer; - MsgBoxText: string; -begin - Result := False; - if ExecWithResult('scalar.exe', 'service --list-mounted', '', SW_HIDE, ewWaitUntilTerminated, ResultCode, Repos) then - begin - if Repos = '' then - begin - Result := False; - end - else - begin - if ResultCode = 0 then - begin - MsgBoxText := 'The following repos are currently mounted:' + #13#10 + Repos + #13#10 + 'Setup needs to unmount all repos before it can proceed, and those repos will be unavailable while setup is running. Do you want to continue?'; - MsgBoxResult := SuppressibleMsgBox(MsgBoxText, mbConfirmation, MB_OKCANCEL, IDOK); - if (MsgBoxResult = IDOK) then - begin - Result := True; - end - else - begin - Abort(); - end; - end; - end; - end; -end; - function EnsureScalarNotRunning(): Boolean; var MsgBoxResult: integer; @@ -419,7 +389,7 @@ end; function InitializeUninstall(): Boolean; begin - UnmountRepos(); + StopMaintenanceTasks(); Result := EnsureScalarNotRunning(); end; @@ -443,11 +413,7 @@ begin end; ssPostInstall: begin - if ExpandConstant('{param:REMOUNTREPOS|true}') = 'true' then - begin - StartScalarServiceUI(); - MountRepos(); - end + RegisterUserWithService(); end; end; end; @@ -473,13 +439,7 @@ function PrepareToInstall(var NeedsRestart: Boolean): String; begin NeedsRestart := False; Result := ''; - if ConfirmUnmountAll() then - begin - if ExpandConstant('{param:REMOUNTREPOS|true}') = 'true' then - begin - UnmountRepos(); - end - end; + StopMaintenanceTasks(); if not EnsureScalarNotRunning() then begin Abort(); diff --git a/Scalar/CommandLine/CloneVerb.cs b/Scalar/CommandLine/CloneVerb.cs index 042ad61657..63c912ef01 100644 --- a/Scalar/CommandLine/CloneVerb.cs +++ b/Scalar/CommandLine/CloneVerb.cs @@ -3,6 +3,7 @@ using Scalar.Common.FileSystem; using Scalar.Common.Git; using Scalar.Common.Http; +using Scalar.Common.NamedPipes; using Scalar.Common.Tracing; using System; using System.Diagnostics; @@ -11,7 +12,7 @@ namespace Scalar.CommandLine { - [Verb(CloneVerb.CloneVerbName, HelpText = "Clone a git repo and mount it as a Scalar virtual repo")] + [Verb(CloneVerb.CloneVerbName, HelpText = "Clone a git repo and register it with the service as a Scalar repo")] public class CloneVerb : ScalarVerb { private const string CloneVerbName = "clone"; @@ -278,17 +279,9 @@ private Result DoClone(string fullEnlistmentRootPathParameter, string normalized this.ConfigureWatchmanIntegration(); - this.Execute( - this.enlistment, - verb => - { - verb.SkipMountedCheck = true; - verb.SkipVersionCheck = true; - verb.ResolvedCacheServer = this.cacheServer; - verb.DownloadedScalarConfig = this.serverScalarConfig; - }); - cloneResult = this.CheckoutRepo(); + + this.RegisterWithService(); } return cloneResult; @@ -471,11 +464,6 @@ private void CheckNotInsideExistingRepo(string normalizedEnlistmentRootPath) { this.ReportErrorAndExit("Error: You can't clone inside an existing Scalar repo ({0})", existingEnlistmentRoot); } - - if (this.IsExistingPipeListening(normalizedEnlistmentRootPath)) - { - this.ReportErrorAndExit($"Error: There is currently a Scalar.Mount process running for '{normalizedEnlistmentRootPath}'. This process must be stopped before cloning."); - } } private bool TryDetermineLocalCacheAndInitializePaths(string localCacheRoot, out string errorMessage) @@ -587,6 +575,82 @@ private Result CreateClone() return new Result(true); } + private void RegisterWithService() + { + if (!this.Unattended) + { + this.tracer.RelatedInfo($"{nameof(this.Execute)}: Registering with service"); + + string errorMessage = string.Empty; + if (this.ShowStatusWhileRunning( + () => { return this.RegisterRepoWithService(out errorMessage); }, + "Registering with service")) + { + this.tracer.RelatedInfo($"{nameof(this.Execute)}: Registered with service"); + } + else + { + this.Output.WriteLine(" WARNING: " + errorMessage); + this.tracer.RelatedInfo($"{nameof(this.Execute)}: Failed to register with service"); + } + } + } + + private bool RegisterRepoWithService(out string errorMessage) + { + errorMessage = string.Empty; + + NamedPipeMessages.RegisterRepoRequest request = new NamedPipeMessages.RegisterRepoRequest(); + request.EnlistmentRoot = this.enlistment.EnlistmentRoot; + + request.OwnerSID = ScalarPlatform.Instance.GetCurrentUser(); + + using (NamedPipeClient client = new NamedPipeClient(this.ServicePipeName)) + { + if (!client.Connect()) + { + errorMessage = "Unable to register repo because Scalar.Service is not responding."; + return false; + } + + try + { + client.SendRequest(request.ToMessage()); + NamedPipeMessages.Message response = client.ReadResponse(); + if (response.Header == NamedPipeMessages.RegisterRepoRequest.Response.Header) + { + NamedPipeMessages.RegisterRepoRequest.Response message = NamedPipeMessages.RegisterRepoRequest.Response.FromMessage(response); + + if (!string.IsNullOrEmpty(message.ErrorMessage)) + { + errorMessage = message.ErrorMessage; + return false; + } + + if (message.State != NamedPipeMessages.CompletionState.Success) + { + errorMessage = "Unable to register repo. " + errorMessage; + return false; + } + else + { + return true; + } + } + else + { + errorMessage = string.Format("Scalar.Service responded with unexpected message: {0}", response); + return false; + } + } + catch (BrokenPipeException e) + { + errorMessage = "Unable to communicate with Scalar.Service: " + e.ToString(); + return false; + } + } + } + private Result TryInitRepo() { string repoPath = this.enlistment.WorkingDirectoryBackingRoot; diff --git a/Scalar/CommandLine/DiagnoseVerb.cs b/Scalar/CommandLine/DiagnoseVerb.cs index bd25d4d1cd..fd085f289d 100644 --- a/Scalar/CommandLine/DiagnoseVerb.cs +++ b/Scalar/CommandLine/DiagnoseVerb.cs @@ -78,12 +78,7 @@ protected override void Execute(ScalarEnlistment enlistment) this.RecordVersionInformation(); - this.ShowStatusWhileRunning( - () => - this.RunAndRecordScalarVerb(archiveFolderPath, "scalar_status.txt") != ReturnCode.Success || - this.RunAndRecordScalarVerb(archiveFolderPath, "scalar_unmount.txt", verb => verb.SkipLock = true) == ReturnCode.Success, - "Unmounting", - suppressGvfsLogMessage: true); + this.RunAndRecordScalarVerb(archiveFolderPath, "scalar_status.txt"); this.ShowStatusWhileRunning( () => @@ -157,11 +152,6 @@ protected override void Execute(ScalarEnlistment enlistment) }, "Copying logs"); - this.ShowStatusWhileRunning( - () => this.RunAndRecordScalarVerb(archiveFolderPath, "scalar_mount.txt") == ReturnCode.Success, - "Mounting", - suppressGvfsLogMessage: true); - this.CopyAllFiles(enlistment.DotScalarRoot, Path.Combine(archiveFolderPath, ScalarPlatform.Instance.Constants.DotScalarRoot), "logs", copySubFolders: false); } diff --git a/Scalar/CommandLine/MountVerb.cs b/Scalar/CommandLine/MountVerb.cs deleted file mode 100644 index d3a257c0ba..0000000000 --- a/Scalar/CommandLine/MountVerb.cs +++ /dev/null @@ -1,323 +0,0 @@ -using CommandLine; -using Scalar.Common; -using Scalar.Common.FileSystem; -using Scalar.Common.Git; -using Scalar.Common.Http; -using Scalar.Common.NamedPipes; -using Scalar.Common.Tracing; -using Scalar.DiskLayoutUpgrades; -using System; -using System.IO; - -namespace Scalar.CommandLine -{ - [Verb(MountVerb.MountVerbName, HelpText = "Mount a Scalar virtual repo")] - public class MountVerb : ScalarVerb.ForExistingEnlistment - { - private const string MountVerbName = "mount"; - - [Option( - 'v', - ScalarConstants.VerbParameters.Mount.Verbosity, - Default = ScalarConstants.VerbParameters.Mount.DefaultVerbosity, - Required = false, - HelpText = "Sets the verbosity of console logging. Accepts: Verbose, Informational, Warning, Error")] - public string Verbosity { get; set; } - - [Option( - 'k', - ScalarConstants.VerbParameters.Mount.Keywords, - Default = ScalarConstants.VerbParameters.Mount.DefaultKeywords, - Required = false, - HelpText = "A CSV list of logging filter keywords. Accepts: Any, Network")] - public string KeywordsCsv { get; set; } - - public bool SkipMountedCheck { get; set; } - public bool SkipVersionCheck { get; set; } - public CacheServerInfo ResolvedCacheServer { get; set; } - public ServerScalarConfig DownloadedScalarConfig { get; set; } - - protected override string VerbName - { - get { return MountVerbName; } - } - - public override void InitializeDefaultParameterValues() - { - this.Verbosity = ScalarConstants.VerbParameters.Mount.DefaultVerbosity; - this.KeywordsCsv = ScalarConstants.VerbParameters.Mount.DefaultKeywords; - } - - protected override void PreCreateEnlistment() - { - string errorMessage; - string enlistmentRoot; - if (!ScalarPlatform.Instance.TryGetScalarEnlistmentRoot(this.EnlistmentRootPathParameter, out enlistmentRoot, out errorMessage)) - { - this.ReportErrorAndExit("Error: '{0}' is not a valid Scalar enlistment", this.EnlistmentRootPathParameter); - } - - if (!this.SkipMountedCheck) - { - if (this.IsExistingPipeListening(enlistmentRoot)) - { - this.ReportErrorAndExit(tracer: null, exitCode: ReturnCode.Success, error: $"The repo at '{enlistmentRoot}' is already mounted."); - } - } - - if (!DiskLayoutUpgrade.TryRunAllUpgrades(enlistmentRoot)) - { - this.ReportErrorAndExit("Failed to upgrade repo disk layout. " + ConsoleHelper.GetScalarLogMessage(enlistmentRoot)); - } - - string error; - if (!DiskLayoutUpgrade.TryCheckDiskLayoutVersion(tracer: null, enlistmentRoot: enlistmentRoot, error: out error)) - { - this.ReportErrorAndExit("Error: " + error); - } - } - - protected override void Execute(ScalarEnlistment enlistment) - { - string errorMessage = null; - string mountExecutableLocation = null; - using (JsonTracer tracer = new JsonTracer(ScalarConstants.ScalarEtwProviderName, "ExecuteMount")) - { - PhysicalFileSystem fileSystem = new PhysicalFileSystem(); - ScalarContext context = new ScalarContext(tracer, fileSystem, enlistment); - - CacheServerInfo cacheServer = this.ResolvedCacheServer ?? CacheServerResolver.GetCacheServerFromConfig(enlistment); - - tracer.AddLogFileEventListener( - ScalarEnlistment.GetNewScalarLogFileName(enlistment.ScalarLogsRoot, ScalarConstants.LogFileTypes.MountVerb), - EventLevel.Verbose, - Keywords.Any); - tracer.WriteStartEvent( - enlistment.EnlistmentRoot, - enlistment.RepoUrl, - cacheServer.Url, - this.AddVerbDataToMetadata(new EventMetadata - { - { "Unattended", this.Unattended }, - { "IsElevated", ScalarPlatform.Instance.IsElevated() }, - { "NamedPipeName", enlistment.NamedPipeName }, - { nameof(this.EnlistmentRootPathParameter), this.EnlistmentRootPathParameter }, - })); - - RetryConfig retryConfig = null; - ServerScalarConfig serverScalarConfig = this.DownloadedScalarConfig; - if (!this.SkipVersionCheck) - { - string authErrorMessage; - if (!this.TryAuthenticate(tracer, enlistment, out authErrorMessage)) - { - this.Output.WriteLine(" WARNING: " + authErrorMessage); - this.Output.WriteLine(" Mount will proceed, but new files cannot be accessed until Scalar can authenticate."); - } - - if (serverScalarConfig == null) - { - if (retryConfig == null) - { - retryConfig = this.GetRetryConfig(tracer, enlistment); - } - - serverScalarConfig = this.QueryScalarConfig(tracer, enlistment, retryConfig); - } - - this.ValidateClientVersions(tracer, enlistment, serverScalarConfig, showWarnings: true); - - CacheServerResolver cacheServerResolver = new CacheServerResolver(tracer, enlistment); - cacheServer = cacheServerResolver.ResolveNameFromRemote(cacheServer.Url, serverScalarConfig); - this.Output.WriteLine("Configured cache server: " + cacheServer); - } - - this.InitializeLocalCacheAndObjectsPaths(tracer, enlistment, retryConfig, serverScalarConfig, cacheServer); - - if (!this.ShowStatusWhileRunning( - () => { return this.PerformPreMountValidation(tracer, enlistment, out mountExecutableLocation, out errorMessage); }, - "Validating repo")) - { - this.ReportErrorAndExit(tracer, errorMessage); - } - - if (!this.SkipVersionCheck) - { - string error; - if (!RepoMetadata.TryInitialize(tracer, enlistment.DotScalarRoot, out error)) - { - this.ReportErrorAndExit(tracer, error); - } - - try - { - GitProcess git = new GitProcess(enlistment); - this.LogEnlistmentInfoAndSetConfigValues(tracer, git, enlistment); - } - finally - { - RepoMetadata.Shutdown(); - } - } - - if (!this.ShowStatusWhileRunning( - () => { return this.TryMount(tracer, enlistment, mountExecutableLocation, out errorMessage); }, - "Mounting")) - { - this.ReportErrorAndExit(tracer, errorMessage); - } - - if (!this.Unattended) - { - tracer.RelatedInfo($"{nameof(this.Execute)}: Registering for automount"); - - if (this.ShowStatusWhileRunning( - () => { return this.RegisterMount(enlistment, out errorMessage); }, - "Registering for automount")) - { - tracer.RelatedInfo($"{nameof(this.Execute)}: Registered for automount"); - } - else - { - this.Output.WriteLine(" WARNING: " + errorMessage); - tracer.RelatedInfo($"{nameof(this.Execute)}: Failed to register for automount"); - } - } - } - } - - private bool PerformPreMountValidation(ITracer tracer, ScalarEnlistment enlistment, out string mountExecutableLocation, out string errorMessage) - { - errorMessage = string.Empty; - mountExecutableLocation = string.Empty; - - // We have to parse these parameters here to make sure they are valid before - // handing them to the background process which cannot tell the user when they are bad - EventLevel verbosity; - Keywords keywords; - this.ParseEnumArgs(out verbosity, out keywords); - - mountExecutableLocation = Path.Combine(ProcessHelper.GetCurrentProcessLocation(), ScalarPlatform.Instance.Constants.MountExecutableName); - if (!File.Exists(mountExecutableLocation)) - { - errorMessage = $"Could not find {ScalarPlatform.Instance.Constants.MountExecutableName}. You may need to reinstall Scalar."; - return false; - } - - GitProcess git = new GitProcess(enlistment); - if (!git.IsValidRepo()) - { - errorMessage = "The .git folder is missing or has invalid contents"; - return false; - } - - if (!ScalarPlatform.Instance.FileSystem.IsFileSystemSupported(enlistment.EnlistmentRoot, out string error)) - { - errorMessage = $"FileSystem unsupported: {error}"; - return false; - } - - return true; - } - - private bool TryMount(ITracer tracer, ScalarEnlistment enlistment, string mountExecutableLocation, out string errorMessage) - { - if (!ScalarVerb.TrySetRequiredGitConfigSettings(enlistment)) - { - errorMessage = "Unable to configure git repo"; - return false; - } - - const string ParamPrefix = "--"; - - tracer.RelatedInfo($"{nameof(this.TryMount)}: Launching background process('{mountExecutableLocation}') for {enlistment.EnlistmentRoot}"); - - ScalarPlatform.Instance.StartBackgroundScalarProcess( - tracer, - mountExecutableLocation, - new[] - { - enlistment.EnlistmentRoot, - ParamPrefix + ScalarConstants.VerbParameters.Mount.Verbosity, - this.Verbosity, - ParamPrefix + ScalarConstants.VerbParameters.Mount.Keywords, - this.KeywordsCsv, - ParamPrefix + ScalarConstants.VerbParameters.Mount.StartedByService, - this.StartedByService.ToString(), - ParamPrefix + ScalarConstants.VerbParameters.Mount.StartedByVerb, - true.ToString() - }); - - tracer.RelatedInfo($"{nameof(this.TryMount)}: Waiting for repo to be mounted"); - return ScalarEnlistment.WaitUntilMounted(tracer, enlistment.EnlistmentRoot, this.Unattended, out errorMessage); - } - - private bool RegisterMount(ScalarEnlistment enlistment, out string errorMessage) - { - errorMessage = string.Empty; - - NamedPipeMessages.RegisterRepoRequest request = new NamedPipeMessages.RegisterRepoRequest(); - request.EnlistmentRoot = enlistment.EnlistmentRoot; - - request.OwnerSID = ScalarPlatform.Instance.GetCurrentUser(); - - using (NamedPipeClient client = new NamedPipeClient(this.ServicePipeName)) - { - if (!client.Connect()) - { - errorMessage = "Unable to register repo because Scalar.Service is not responding."; - return false; - } - - try - { - client.SendRequest(request.ToMessage()); - NamedPipeMessages.Message response = client.ReadResponse(); - if (response.Header == NamedPipeMessages.RegisterRepoRequest.Response.Header) - { - NamedPipeMessages.RegisterRepoRequest.Response message = NamedPipeMessages.RegisterRepoRequest.Response.FromMessage(response); - - if (!string.IsNullOrEmpty(message.ErrorMessage)) - { - errorMessage = message.ErrorMessage; - return false; - } - - if (message.State != NamedPipeMessages.CompletionState.Success) - { - errorMessage = "Unable to register repo. " + errorMessage; - return false; - } - else - { - return true; - } - } - else - { - errorMessage = string.Format("Scalar.Service responded with unexpected message: {0}", response); - return false; - } - } - catch (BrokenPipeException e) - { - errorMessage = "Unable to communicate with Scalar.Service: " + e.ToString(); - return false; - } - } - } - - private void ParseEnumArgs(out EventLevel verbosity, out Keywords keywords) - { - if (!Enum.TryParse(this.KeywordsCsv, out keywords)) - { - this.ReportErrorAndExit("Error: Invalid logging filter keywords: " + this.KeywordsCsv); - } - - if (!Enum.TryParse(this.Verbosity, out verbosity)) - { - this.ReportErrorAndExit("Error: Invalid logging verbosity: " + this.Verbosity); - } - } - } -} diff --git a/Scalar/CommandLine/ScalarVerb.cs b/Scalar/CommandLine/ScalarVerb.cs index 60ac12e0e1..c7a1bb5adb 100644 --- a/Scalar/CommandLine/ScalarVerb.cs +++ b/Scalar/CommandLine/ScalarVerb.cs @@ -436,19 +436,6 @@ protected VstsInfoData QueryVstsInfo(ITracer tracer, ScalarEnlistment enlistment return vstsInfo; } - protected bool IsExistingPipeListening(string enlistmentRoot) - { - using (NamedPipeClient pipeClient = new NamedPipeClient(ScalarPlatform.Instance.GetNamedPipeName(enlistmentRoot))) - { - if (pipeClient.Connect(500)) - { - return true; - } - } - - return false; - } - protected void ValidateClientVersions(ITracer tracer, ScalarEnlistment enlistment, ServerScalarConfig scalarConfig, bool showWarnings) { this.CheckGitVersion(tracer, enlistment, out string gitVersion); diff --git a/Scalar/CommandLine/ServiceVerb.cs b/Scalar/CommandLine/ServiceVerb.cs index 9f290d7622..5336720828 100644 --- a/Scalar/CommandLine/ServiceVerb.cs +++ b/Scalar/CommandLine/ServiceVerb.cs @@ -14,24 +14,10 @@ public class ServiceVerb : ScalarVerb.ForNoEnlistment private const string ServiceVerbName = "service"; [Option( - "mount-all", + "list-registered", Default = false, Required = false, - HelpText = "Mounts all repos")] - public bool MountAll { get; set; } - - [Option( - "unmount-all", - Default = false, - Required = false, - HelpText = "Unmounts all repos")] - public bool UnmountAll { get; set; } - - [Option( - "list-mounted", - Default = false, - Required = false, - HelpText = "Prints a list of all mounted repos")] + HelpText = "Prints a list of all repos registered with the service")] public bool List { get; set; } protected override string VerbName @@ -41,7 +27,7 @@ protected override string VerbName public override void Execute() { - int optionCount = new[] { this.MountAll, this.UnmountAll, this.List }.Count(flag => flag); + int optionCount = new[] { this.List }.Count(flag => flag); if (optionCount == 0) { this.ReportErrorAndExit($"Error: You must specify an argument. Run 'scalar {ServiceVerbName} --help' for details."); @@ -62,66 +48,7 @@ public override void Execute() { foreach (string repoRoot in repoList) { - if (this.IsRepoMounted(repoRoot)) - { - this.Output.WriteLine(repoRoot); - } - } - } - else if (this.MountAll) - { - List failedRepoRoots = new List(); - - foreach (string repoRoot in repoList) - { - if (!this.IsRepoMounted(repoRoot)) - { - this.Output.WriteLine("\r\nMounting repo at " + repoRoot); - ReturnCode result = this.Execute(repoRoot); - - if (result != ReturnCode.Success) - { - failedRepoRoots.Add(repoRoot); - } - } - } - - if (failedRepoRoots.Count() > 0) - { - string errorString = $"The following repos failed to mount:{Environment.NewLine}{string.Join("\r\n", failedRepoRoots.ToArray())}"; - Console.Error.WriteLine(errorString); - this.ReportErrorAndExit(Environment.NewLine + errorString); - } - } - else if (this.UnmountAll) - { - List failedRepoRoots = new List(); - - foreach (string repoRoot in repoList) - { - if (this.IsRepoMounted(repoRoot)) - { - this.Output.WriteLine("\r\nUnmounting repo at " + repoRoot); - ReturnCode result = this.Execute( - repoRoot, - verb => - { - verb.SkipUnregister = true; - verb.SkipLock = true; - }); - - if (result != ReturnCode.Success) - { - failedRepoRoots.Add(repoRoot); - } - } - } - - if (failedRepoRoots.Count() > 0) - { - string errorString = $"The following repos failed to unmount:{Environment.NewLine}{string.Join(Environment.NewLine, failedRepoRoots.ToArray())}"; - Console.Error.WriteLine(errorString); - this.ReportErrorAndExit(Environment.NewLine + errorString); + this.Output.WriteLine(repoRoot); } } } @@ -179,24 +106,5 @@ private bool TryGetRepoList(out List repoList, out string errorMessage) return false; } } - - private bool IsRepoMounted(string repoRoot) - { - // Hide the output of status - StringWriter statusOutput = new StringWriter(); - ReturnCode result = this.Execute( - repoRoot, - verb => - { - verb.Output = statusOutput; - }); - - if (result == ReturnCode.Success) - { - return true; - } - - return false; - } } } diff --git a/Scalar/CommandLine/UnmountVerb.cs b/Scalar/CommandLine/UnmountVerb.cs deleted file mode 100644 index 9209a1042b..0000000000 --- a/Scalar/CommandLine/UnmountVerb.cs +++ /dev/null @@ -1,194 +0,0 @@ -using CommandLine; -using Scalar.Common; -using Scalar.Common.NamedPipes; - -namespace Scalar.CommandLine -{ - [Verb(UnmountVerb.UnmountVerbName, HelpText = "Unmount a Scalar virtual repo")] - public class UnmountVerb : ScalarVerb - { - private const string UnmountVerbName = "unmount"; - - [Value( - 0, - Required = false, - Default = "", - MetaName = "Enlistment Root Path", - HelpText = "Full or relative path to the Scalar enlistment root")] - public override string EnlistmentRootPathParameter { get; set; } - - [Option( - ScalarConstants.VerbParameters.Unmount.SkipLock, - Default = false, - Required = false, - HelpText = "Force unmount even if the lock is not available.")] - public bool SkipLock { get; set; } - - public bool SkipUnregister { get; set; } - - protected override string VerbName - { - get { return UnmountVerbName; } - } - - public override void Execute() - { - this.ValidatePathParameter(this.EnlistmentRootPathParameter); - - string errorMessage; - string root; - if (!ScalarPlatform.Instance.TryGetScalarEnlistmentRoot(this.EnlistmentRootPathParameter, out root, out errorMessage)) - { - this.ReportErrorAndExit( - "Error: '{0}' is not a valid Scalar enlistment", - this.EnlistmentRootPathParameter); - } - - if (!this.ShowStatusWhileRunning( - () => { return this.Unmount(root, out errorMessage); }, - "Unmounting")) - { - this.ReportErrorAndExit(errorMessage); - } - - if (!this.Unattended && !this.SkipUnregister) - { - if (!this.ShowStatusWhileRunning( - () => { return this.UnregisterRepo(root, out errorMessage); }, - "Unregistering automount")) - { - this.Output.WriteLine(" WARNING: " + errorMessage); - } - } - } - - private bool Unmount(string enlistmentRoot, out string errorMessage) - { - errorMessage = string.Empty; - - string pipeName = ScalarPlatform.Instance.GetNamedPipeName(enlistmentRoot); - string rawGetStatusResponse = string.Empty; - - try - { - using (NamedPipeClient pipeClient = new NamedPipeClient(pipeName)) - { - if (!pipeClient.Connect()) - { - errorMessage = "Unable to connect to Scalar.Mount"; - return false; - } - - pipeClient.SendRequest(NamedPipeMessages.GetStatus.Request); - rawGetStatusResponse = pipeClient.ReadRawResponse(); - NamedPipeMessages.GetStatus.Response getStatusResponse = - NamedPipeMessages.GetStatus.Response.FromJson(rawGetStatusResponse); - - switch (getStatusResponse.MountStatus) - { - case NamedPipeMessages.GetStatus.Mounting: - errorMessage = "Still mounting, please try again later"; - return false; - - case NamedPipeMessages.GetStatus.Unmounting: - errorMessage = "Already unmounting, please wait"; - return false; - - case NamedPipeMessages.GetStatus.Ready: - break; - - case NamedPipeMessages.GetStatus.MountFailed: - break; - - default: - errorMessage = "Unrecognized response to GetStatus: " + rawGetStatusResponse; - return false; - } - - pipeClient.SendRequest(NamedPipeMessages.Unmount.Request); - string unmountResponse = pipeClient.ReadRawResponse(); - - switch (unmountResponse) - { - case NamedPipeMessages.Unmount.Acknowledged: - string finalResponse = pipeClient.ReadRawResponse(); - if (finalResponse == NamedPipeMessages.Unmount.Completed) - { - errorMessage = string.Empty; - return true; - } - else - { - errorMessage = "Unrecognized final response to unmount: " + finalResponse; - return false; - } - - case NamedPipeMessages.Unmount.NotMounted: - errorMessage = "Unable to unmount, repo was not mounted"; - return false; - - case NamedPipeMessages.Unmount.MountFailed: - errorMessage = "Unable to unmount, previous mount attempt failed"; - return false; - - default: - errorMessage = "Unrecognized response to unmount: " + unmountResponse; - return false; - } - } - } - catch (BrokenPipeException e) - { - errorMessage = "Unable to communicate with Scalar: " + e.ToString(); - return false; - } - } - - private bool UnregisterRepo(string rootPath, out string errorMessage) - { - errorMessage = string.Empty; - NamedPipeMessages.UnregisterRepoRequest request = new NamedPipeMessages.UnregisterRepoRequest(); - request.EnlistmentRoot = rootPath; - - using (NamedPipeClient client = new NamedPipeClient(this.ServicePipeName)) - { - if (!client.Connect()) - { - errorMessage = "Unable to unregister repo because Scalar.Service is not responding. " + ScalarVerb.StartServiceInstructions; - return false; - } - - try - { - client.SendRequest(request.ToMessage()); - NamedPipeMessages.Message response = client.ReadResponse(); - if (response.Header == NamedPipeMessages.UnregisterRepoRequest.Response.Header) - { - NamedPipeMessages.UnregisterRepoRequest.Response message = NamedPipeMessages.UnregisterRepoRequest.Response.FromMessage(response); - - if (message.State != NamedPipeMessages.CompletionState.Success) - { - errorMessage = message.ErrorMessage; - return false; - } - else - { - errorMessage = string.Empty; - return true; - } - } - else - { - errorMessage = string.Format("Scalar.Service responded with unexpected message: {0}", response); - return false; - } - } - catch (BrokenPipeException e) - { - errorMessage = "Unable to communicate with Scalar.Service: " + e.ToString(); - return false; - } - } - } - } -} diff --git a/Scalar/Program.cs b/Scalar/Program.cs index 4222af4019..faeea75eaf 100644 --- a/Scalar/Program.cs +++ b/Scalar/Program.cs @@ -21,10 +21,8 @@ public static void Main(string[] args) typeof(ConfigVerb), typeof(MaintenanceVerb), typeof(DiagnoseVerb), - typeof(MountVerb), typeof(ServiceVerb), typeof(StatusVerb), - typeof(UnmountVerb), typeof(UpgradeVerb), }; diff --git a/Scalar/Scalar.Windows.csproj b/Scalar/Scalar.Windows.csproj index 795b7dda26..3857f8303c 100644 --- a/Scalar/Scalar.Windows.csproj +++ b/Scalar/Scalar.Windows.csproj @@ -116,11 +116,9 @@ - - PlatformLoader.Windows.cs