diff --git a/GVFS/GVFS.FunctionalTests/Tests/MultiEnlistmentTests/ServiceVerbTests.cs b/GVFS/GVFS.FunctionalTests/Tests/MultiEnlistmentTests/ServiceVerbTests.cs index b2a53cd91..72a049407 100644 --- a/GVFS/GVFS.FunctionalTests/Tests/MultiEnlistmentTests/ServiceVerbTests.cs +++ b/GVFS/GVFS.FunctionalTests/Tests/MultiEnlistmentTests/ServiceVerbTests.cs @@ -1,6 +1,7 @@ using GVFS.FunctionalTests.Tools; using GVFS.Tests.Should; using NUnit.Framework; +using System; namespace GVFS.FunctionalTests.Tests.MultiEnlistmentTests { @@ -9,6 +10,19 @@ namespace GVFS.FunctionalTests.Tests.MultiEnlistmentTests public class ServiceVerbTests : TestsWithMultiEnlistment { private static readonly string[] EmptyRepoList = new string[] { }; + private readonly string fixtureServiceName = "Test.GVFS.Service.ServiceVerbTests." + Guid.NewGuid().ToString("N"); + + [OneTimeSetUp] + public void StartFixtureService() + { + GVFSServiceProcess.InstallService(this.fixtureServiceName); + } + + [OneTimeTearDown] + public void StopFixtureService() + { + GVFSServiceProcess.UninstallService(this.fixtureServiceName); + } [TestCase] public void ServiceCommandsWithNoRepos() @@ -21,20 +35,22 @@ public void ServiceCommandsWithNoRepos() [TestCase] public void ServiceCommandsWithMultipleRepos() { - GVFSFunctionalTestEnlistment enlistment1 = this.CreateNewEnlistment(); - GVFSFunctionalTestEnlistment enlistment2 = this.CreateNewEnlistment(); + GVFSFunctionalTestEnlistment enlistment1 = this.CreateNewEnlistment(serviceName: this.fixtureServiceName); + GVFSFunctionalTestEnlistment enlistment2 = this.CreateNewEnlistment(serviceName: this.fixtureServiceName); string[] repoRootList = new string[] { enlistment1.EnlistmentRoot, enlistment2.EnlistmentRoot }; GVFSProcess gvfsProcess1 = new GVFSProcess( GVFSTestConfig.PathToGVFS, enlistment1.EnlistmentRoot, - enlistment1.LocalCacheRoot); + enlistment1.LocalCacheRoot, + this.fixtureServiceName); GVFSProcess gvfsProcess2 = new GVFSProcess( GVFSTestConfig.PathToGVFS, enlistment2.EnlistmentRoot, - enlistment2.LocalCacheRoot); + enlistment2.LocalCacheRoot, + this.fixtureServiceName); this.RunServiceCommandAndCheckOutput("--list-mounted", expectedRepoRoots: repoRootList); this.RunServiceCommandAndCheckOutput("--unmount-all", expectedRepoRoots: repoRootList); @@ -57,14 +73,15 @@ public void ServiceCommandsWithMultipleRepos() [TestCase] public void ServiceCommandsWithMountAndUnmount() { - GVFSFunctionalTestEnlistment enlistment1 = this.CreateNewEnlistment(); + GVFSFunctionalTestEnlistment enlistment1 = this.CreateNewEnlistment(serviceName: this.fixtureServiceName); string[] repoRootList = new string[] { enlistment1.EnlistmentRoot }; GVFSProcess gvfsProcess1 = new GVFSProcess( GVFSTestConfig.PathToGVFS, enlistment1.EnlistmentRoot, - enlistment1.LocalCacheRoot); + enlistment1.LocalCacheRoot, + this.fixtureServiceName); this.RunServiceCommandAndCheckOutput("--list-mounted", expectedRepoRoots: repoRootList); @@ -88,7 +105,8 @@ private void RunServiceCommandAndCheckOutput(string argument, string[] expectedR GVFSProcess gvfsProcess = new GVFSProcess( GVFSTestConfig.PathToGVFS, enlistmentRoot: null, - localCacheRoot: null); + localCacheRoot: null, + serviceName: this.fixtureServiceName); string result = gvfsProcess.RunServiceVerb(argument); result.ShouldContain(expectedRepoRoots); diff --git a/GVFS/GVFS.FunctionalTests/Tests/MultiEnlistmentTests/TestsWithMultiEnlistment.cs b/GVFS/GVFS.FunctionalTests/Tests/MultiEnlistmentTests/TestsWithMultiEnlistment.cs index 3c9a35327..fd7c37132 100644 --- a/GVFS/GVFS.FunctionalTests/Tests/MultiEnlistmentTests/TestsWithMultiEnlistment.cs +++ b/GVFS/GVFS.FunctionalTests/Tests/MultiEnlistmentTests/TestsWithMultiEnlistment.cs @@ -31,13 +31,15 @@ protected virtual void OnTearDownEnlistmentsDeleted() protected GVFSFunctionalTestEnlistment CreateNewEnlistment( string localCacheRoot = null, string branch = null, - bool skipPrefetch = false) + bool skipPrefetch = false, + string serviceName = null) { GVFSFunctionalTestEnlistment output = GVFSFunctionalTestEnlistment.CloneAndMount( GVFSTestConfig.PathToGVFS, branch, localCacheRoot, - skipPrefetch); + skipPrefetch, + serviceName); this.enlistmentsToDelete.Add(output); return output; } diff --git a/GVFS/GVFS.FunctionalTests/Tools/GVFSFunctionalTestEnlistment.cs b/GVFS/GVFS.FunctionalTests/Tools/GVFSFunctionalTestEnlistment.cs index 15880551b..4d653f7e2 100644 --- a/GVFS/GVFS.FunctionalTests/Tools/GVFSFunctionalTestEnlistment.cs +++ b/GVFS/GVFS.FunctionalTests/Tools/GVFSFunctionalTestEnlistment.cs @@ -20,12 +20,20 @@ public class GVFSFunctionalTestEnlistment private static readonly string ZeroBackgroundOperations = "Background operations: 0" + Environment.NewLine; private GVFSProcess gvfsProcess; + private readonly string serviceName; - private GVFSFunctionalTestEnlistment(string pathToGVFS, string enlistmentRoot, string repoUrl, string commitish, string localCacheRoot = null) + private GVFSFunctionalTestEnlistment( + string pathToGVFS, + string enlistmentRoot, + string repoUrl, + string commitish, + string localCacheRoot = null, + string serviceName = null) { this.EnlistmentRoot = enlistmentRoot; this.RepoUrl = repoUrl; this.Commitish = commitish; + this.serviceName = serviceName; if (localCacheRoot == null) { @@ -43,7 +51,7 @@ private GVFSFunctionalTestEnlistment(string pathToGVFS, string enlistmentRoot, s } this.LocalCacheRoot = localCacheRoot; - this.gvfsProcess = new GVFSProcess(pathToGVFS, this.EnlistmentRoot, this.LocalCacheRoot); + this.gvfsProcess = new GVFSProcess(pathToGVFS, this.EnlistmentRoot, this.LocalCacheRoot, this.serviceName); } public string EnlistmentRoot @@ -57,6 +65,7 @@ public string RepoUrl } public string LocalCacheRoot { get; } + public string ServiceName => this.serviceName; public string RepoBackingRoot { @@ -102,24 +111,25 @@ public static GVFSFunctionalTestEnlistment CloneAndMountWithPerRepoCache(string { string enlistmentRoot = GVFSFunctionalTestEnlistment.GetUniqueEnlistmentRoot(); string localCache = GVFSFunctionalTestEnlistment.GetRepoSpecificLocalCacheRoot(enlistmentRoot); - return CloneAndMount(pathToGvfs, enlistmentRoot, null, localCache, skipPrefetch); + return CloneAndMount(pathToGvfs, enlistmentRoot, null, localCache, skipPrefetch, serviceName: null); } public static GVFSFunctionalTestEnlistment CloneAndMount( string pathToGvfs, string commitish = null, string localCacheRoot = null, - bool skipPrefetch = false) + bool skipPrefetch = false, + string serviceName = null) { string enlistmentRoot = GVFSFunctionalTestEnlistment.GetUniqueEnlistmentRoot(); - return CloneAndMount(pathToGvfs, enlistmentRoot, commitish, localCacheRoot, skipPrefetch); + return CloneAndMount(pathToGvfs, enlistmentRoot, commitish, localCacheRoot, skipPrefetch, serviceName); } - public static GVFSFunctionalTestEnlistment CloneAndMountEnlistmentWithSpacesInPath(string pathToGvfs, string commitish = null) + public static GVFSFunctionalTestEnlistment CloneAndMountEnlistmentWithSpacesInPath(string pathToGvfs, string commitish = null, string serviceName = null) { string enlistmentRoot = GVFSFunctionalTestEnlistment.GetUniqueEnlistmentRootWithSpaces(); string localCache = GVFSFunctionalTestEnlistment.GetRepoSpecificLocalCacheRoot(enlistmentRoot); - return CloneAndMount(pathToGvfs, enlistmentRoot, commitish, localCache); + return CloneAndMount(pathToGvfs, enlistmentRoot, commitish, localCache, skipPrefetch: false, serviceName: serviceName); } public static string GetUniqueEnlistmentRoot() @@ -387,14 +397,21 @@ public string GetObjectPathTo(string objectHash) objectHash.Substring(2)); } - private static GVFSFunctionalTestEnlistment CloneAndMount(string pathToGvfs, string enlistmentRoot, string commitish, string localCacheRoot, bool skipPrefetch = false) + private static GVFSFunctionalTestEnlistment CloneAndMount( + string pathToGvfs, + string enlistmentRoot, + string commitish, + string localCacheRoot, + bool skipPrefetch = false, + string serviceName = null) { GVFSFunctionalTestEnlistment enlistment = new GVFSFunctionalTestEnlistment( pathToGvfs, enlistmentRoot ?? GetUniqueEnlistmentRoot(), GVFSTestConfig.RepoToClone, commitish ?? Properties.Settings.Default.Commitish, - localCacheRoot ?? GVFSTestConfig.LocalCacheRoot); + localCacheRoot ?? GVFSTestConfig.LocalCacheRoot, + serviceName); try { diff --git a/GVFS/GVFS.FunctionalTests/Tools/GVFSHelpers.cs b/GVFS/GVFS.FunctionalTests/Tools/GVFSHelpers.cs index 8fdf7fe7f..d1b356f3d 100644 --- a/GVFS/GVFS.FunctionalTests/Tools/GVFSHelpers.cs +++ b/GVFS/GVFS.FunctionalTests/Tools/GVFSHelpers.cs @@ -197,9 +197,13 @@ public static void ModifiedPathsShouldNotContain(GVFSFunctionalTestEnlistment en } } - public static string GetInternalParameter(string maintenanceJob = "null", string packfileMaintenanceBatchSize = "null") + public static string GetInternalParameter( + string maintenanceJob = "null", + string packfileMaintenanceBatchSize = "null", + string serviceName = null) { - return $"\"{{\\\"ServiceName\\\":\\\"{GVFSServiceProcess.TestServiceName}\\\"," + + string effectiveServiceName = string.IsNullOrWhiteSpace(serviceName) ? GVFSServiceProcess.TestServiceName : serviceName; + return $"\"{{\\\"ServiceName\\\":\\\"{effectiveServiceName}\\\"," + "\\\"StartedByService\\\":false," + $"\\\"MaintenanceJob\\\":{maintenanceJob}," + $"\\\"PackfileMaintenanceBatchSize\\\":{packfileMaintenanceBatchSize}}}\""; diff --git a/GVFS/GVFS.FunctionalTests/Tools/GVFSProcess.cs b/GVFS/GVFS.FunctionalTests/Tools/GVFSProcess.cs index 5d7f415d7..7e632a279 100644 --- a/GVFS/GVFS.FunctionalTests/Tools/GVFSProcess.cs +++ b/GVFS/GVFS.FunctionalTests/Tools/GVFSProcess.cs @@ -14,17 +14,23 @@ public class GVFSProcess private readonly string pathToGVFS; private readonly string enlistmentRoot; private readonly string localCacheRoot; + private readonly string serviceName; public GVFSProcess(GVFSFunctionalTestEnlistment enlistment) - : this(GVFSTestConfig.PathToGVFS, enlistment.EnlistmentRoot, Path.Combine(enlistment.EnlistmentRoot, GVFSTestConfig.DotGVFSRoot)) + : this( + GVFSTestConfig.PathToGVFS, + enlistment.EnlistmentRoot, + Path.Combine(enlistment.EnlistmentRoot, GVFSTestConfig.DotGVFSRoot), + enlistment.ServiceName) { } - public GVFSProcess(string pathToGVFS, string enlistmentRoot, string localCacheRoot) + public GVFSProcess(string pathToGVFS, string enlistmentRoot, string localCacheRoot, string serviceName = null) { this.pathToGVFS = pathToGVFS; this.enlistmentRoot = enlistmentRoot; this.localCacheRoot = localCacheRoot; + this.serviceName = serviceName; } public void Clone(string repositorySource, string branchToCheckout, bool skipPrefetch) @@ -141,13 +147,13 @@ public string LooseObjectStep() return this.CallGVFS( "dehydrate \"" + this.enlistmentRoot + "\"", expectedExitCode: SuccessExitCode, - internalParameter: GVFSHelpers.GetInternalParameter("\\\"LooseObjects\\\"")); + internalParameter: GVFSHelpers.GetInternalParameter("\\\"LooseObjects\\\"", serviceName: this.serviceName)); } public string PackfileMaintenanceStep(long? batchSize) { string sizeString = batchSize.HasValue ? $"\\\"{batchSize.Value}\\\"" : "null"; - string internalParameter = GVFSHelpers.GetInternalParameter("\\\"PackfileMaintenance\\\"", sizeString); + string internalParameter = GVFSHelpers.GetInternalParameter("\\\"PackfileMaintenance\\\"", sizeString, this.serviceName); return this.CallGVFS( "dehydrate \"" + this.enlistmentRoot + "\"", expectedExitCode: SuccessExitCode, @@ -156,7 +162,7 @@ public string PackfileMaintenanceStep(long? batchSize) public string PostFetchStep() { - string internalParameter = GVFSHelpers.GetInternalParameter("\\\"PostFetch\\\""); + string internalParameter = GVFSHelpers.GetInternalParameter("\\\"PostFetch\\\"", serviceName: this.serviceName); return this.CallGVFS( "dehydrate \"" + this.enlistmentRoot + "\"", expectedExitCode: SuccessExitCode, @@ -246,7 +252,7 @@ private string CallGVFS(string args, int expectedExitCode = DoNotCheckExitCode, if (internalParameter == null) { - internalParameter = GVFSHelpers.GetInternalParameter(); + internalParameter = GVFSHelpers.GetInternalParameter(serviceName: this.serviceName); } processInfo.Arguments = args + " " + TestConstants.InternalUseOnlyFlag + " " + internalParameter; diff --git a/GVFS/GVFS.FunctionalTests/Tools/GVFSServiceProcess.cs b/GVFS/GVFS.FunctionalTests/Tools/GVFSServiceProcess.cs index 2ac384629..022504abb 100644 --- a/GVFS/GVFS.FunctionalTests/Tools/GVFSServiceProcess.cs +++ b/GVFS/GVFS.FunctionalTests/Tools/GVFSServiceProcess.cs @@ -2,6 +2,7 @@ using System; using System.Diagnostics; using System.IO; +using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.ServiceProcess; @@ -11,8 +12,8 @@ namespace GVFS.FunctionalTests.Tools { public static class GVFSServiceProcess { - private static readonly string ServiceNameArgument = "--servicename=" + TestServiceName; - private static Process consoleServiceProcess; + private static readonly Dictionary ConsoleServiceProcesses = + new Dictionary(System.StringComparer.OrdinalIgnoreCase); public static string TestServiceName { @@ -24,77 +25,98 @@ public static string TestServiceName } public static void InstallService() + { + InstallService(TestServiceName); + } + + public static void InstallService(string serviceName) { if (GVFSTestConfig.IsDevMode) { - StartServiceAsConsoleProcess(); + StartServiceAsConsoleProcess(serviceName); } else { - InstallWindowsService(); + InstallWindowsService(serviceName); } } public static void UninstallService() + { + UninstallService(TestServiceName); + } + + public static void UninstallService(string serviceName) { if (GVFSTestConfig.IsDevMode) { - StopConsoleServiceProcess(); - CleanupServiceData(); + StopConsoleServiceProcess(serviceName); + CleanupServiceData(serviceName); } else { - UninstallWindowsService(); + UninstallWindowsService(serviceName); } } public static void StartService() + { + StartService(TestServiceName); + } + + public static void StartService(string serviceName) { if (GVFSTestConfig.IsDevMode) { - StartServiceAsConsoleProcess(); + StartServiceAsConsoleProcess(serviceName); } else { - StartWindowsService(); + StartWindowsService(serviceName); } } public static void StopService() + { + StopService(TestServiceName); + } + + public static void StopService(string serviceName) { if (GVFSTestConfig.IsDevMode) { - StopConsoleServiceProcess(); + StopConsoleServiceProcess(serviceName); } else { - StopWindowsService(); + StopWindowsService(serviceName); } } - private static void StartServiceAsConsoleProcess() + private static void StartServiceAsConsoleProcess(string serviceName) { - StopConsoleServiceProcess(); + StopConsoleServiceProcess(serviceName); string pathToService = GetPathToService(); Console.WriteLine("Starting test service in console mode: " + pathToService); ProcessStartInfo startInfo = new ProcessStartInfo(pathToService); - startInfo.Arguments = $"--console {ServiceNameArgument}"; + startInfo.Arguments = $"--console --servicename={serviceName}"; startInfo.UseShellExecute = false; startInfo.CreateNoWindow = true; startInfo.RedirectStandardOutput = true; startInfo.RedirectStandardError = true; - consoleServiceProcess = Process.Start(startInfo); + Process consoleServiceProcess = Process.Start(startInfo); consoleServiceProcess.ShouldNotBeNull("Failed to start test service process"); + ConsoleServiceProcesses[serviceName] = consoleServiceProcess; // Consume output asynchronously to prevent buffer deadlock consoleServiceProcess.BeginOutputReadLine(); consoleServiceProcess.BeginErrorReadLine(); // Wait for the service to start listening on its named pipe - string pipeName = TestServiceName + ".pipe"; + string pipeName = serviceName + ".pipe"; int retries = 50; while (retries-- > 0) { @@ -116,39 +138,43 @@ private static void StartServiceAsConsoleProcess() throw new System.TimeoutException("Timed out waiting for test service pipe: " + pipeName); } - private static void StopConsoleServiceProcess() + private static void StopConsoleServiceProcess(string serviceName) { - if (consoleServiceProcess != null && !consoleServiceProcess.HasExited) + Process consoleServiceProcess; + if (ConsoleServiceProcesses.TryGetValue(serviceName, out consoleServiceProcess)) { - try - { - Console.WriteLine("Stopping test service console process (PID: " + consoleServiceProcess.Id + ")"); - consoleServiceProcess.Kill(); - consoleServiceProcess.WaitForExit(5000); - } - catch (InvalidOperationException) + if (consoleServiceProcess != null && !consoleServiceProcess.HasExited) { - // Process already exited + try + { + Console.WriteLine("Stopping test service console process (PID: " + consoleServiceProcess.Id + ")"); + consoleServiceProcess.Kill(); + consoleServiceProcess.WaitForExit(5000); + } + catch (InvalidOperationException) + { + // Process already exited + } } - consoleServiceProcess = null; + ConsoleServiceProcesses.Remove(serviceName); } } - private static void CleanupServiceData() + private static void CleanupServiceData(string serviceName) { string commonAppDataRoot = Environment.GetEnvironmentVariable("GVFS_COMMON_APPDATA_ROOT"); string serviceData; if (!string.IsNullOrEmpty(commonAppDataRoot)) { - serviceData = Path.Combine(commonAppDataRoot, TestServiceName); + serviceData = Path.Combine(commonAppDataRoot, serviceName); } else { serviceData = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "GVFS", - TestServiceName); + serviceName); } DirectoryInfo serviceDataDir = new DirectoryInfo(serviceData); @@ -158,14 +184,14 @@ private static void CleanupServiceData() } } - private static void InstallWindowsService() + private static void InstallWindowsService(string serviceName) { - Console.WriteLine("Installing " + TestServiceName); + Console.WriteLine("Installing " + serviceName); - UninstallWindowsService(); + UninstallWindowsService(serviceName); // Wait for delete to complete. If the services control panel is open, this will never complete. - while (RunScCommand("query", TestServiceName).ExitCode == 0) + while (RunScCommand("query", serviceName).ExitCode == 0) { Thread.Sleep(1000); } @@ -178,23 +204,23 @@ private static void InstallWindowsService() string createServiceArguments = string.Format( "{0} binPath= \"{1}\"", - TestServiceName, + serviceName, pathToService); ProcessResult result = RunScCommand("create", createServiceArguments); result.ExitCode.ShouldEqual(0, "Failure while running sc create " + createServiceArguments + "\r\n" + result.Output); - StartWindowsService(); + StartWindowsService(serviceName); } - private static void UninstallWindowsService() + private static void UninstallWindowsService(string serviceName) { - StopWindowsService(); + StopWindowsService(serviceName); - RunScCommand("delete", TestServiceName); + RunScCommand("delete", serviceName); // Make sure to delete any test service data state - string serviceData = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "GVFS", TestServiceName); + string serviceData = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "GVFS", serviceName); DirectoryInfo serviceDataDir = new DirectoryInfo(serviceData); if (serviceDataDir.Exists) { @@ -202,24 +228,24 @@ private static void UninstallWindowsService() } } - private static void StartWindowsService() + private static void StartWindowsService(string serviceName) { - ServiceController testService = ServiceController.GetServices().SingleOrDefault(service => service.ServiceName == TestServiceName); - testService.ShouldNotBeNull($"{TestServiceName} does not exist as a service"); + ServiceController testService = ServiceController.GetServices().SingleOrDefault(service => service.ServiceName == serviceName); + testService.ShouldNotBeNull($"{serviceName} does not exist as a service"); - using (ServiceController controller = new ServiceController(TestServiceName)) + using (ServiceController controller = new ServiceController(serviceName)) { - controller.Start(new[] { ServiceNameArgument }); + controller.Start(new[] { "--servicename=" + serviceName }); controller.WaitForStatus(ServiceControllerStatus.Running, TimeSpan.FromSeconds(10)); controller.Status.ShouldEqual(ServiceControllerStatus.Running); } } - private static void StopWindowsService() + private static void StopWindowsService(string serviceName) { try { - ServiceController testService = ServiceController.GetServices().SingleOrDefault(service => service.ServiceName == TestServiceName); + ServiceController testService = ServiceController.GetServices().SingleOrDefault(service => service.ServiceName == serviceName); if (testService != null) { if (testService.Status == ServiceControllerStatus.Running)