From f02d9ffa2ad17f69971d7774d1439ae9aa72e520 Mon Sep 17 00:00:00 2001 From: Dan Moseley Date: Tue, 10 Aug 2021 07:47:40 -0600 Subject: [PATCH] Fix ServiceController test (#57116) --- .../ServiceProcess/ServiceController.cs | 2 +- .../tests/ServiceBaseTests.cs | 2 +- .../tests/ServiceControllerTests.cs | 14 ++++------ .../ServiceControllerTests.netcoreapp.cs | 12 ++++++-- .../TestService.cs | 2 +- .../TestServiceInstaller.cs | 10 ++++++- .../tests/TestServiceProvider.cs | 28 ++++++++++++------- 7 files changed, 46 insertions(+), 24 deletions(-) diff --git a/src/libraries/System.ServiceProcess.ServiceController/src/System/ServiceProcess/ServiceController.cs b/src/libraries/System.ServiceProcess.ServiceController/src/System/ServiceProcess/ServiceController.cs index 64725f9f910cf8..e72f8827aaf9ef 100644 --- a/src/libraries/System.ServiceProcess.ServiceController/src/System/ServiceProcess/ServiceController.cs +++ b/src/libraries/System.ServiceProcess.ServiceController/src/System/ServiceProcess/ServiceController.cs @@ -283,7 +283,7 @@ public string ServiceName } /// - /// A set of services on which the given service object is depend upon. + /// A set of services on which the given service object is dependent upon. /// public unsafe ServiceController[] ServicesDependedOn { diff --git a/src/libraries/System.ServiceProcess.ServiceController/tests/ServiceBaseTests.cs b/src/libraries/System.ServiceProcess.ServiceController/tests/ServiceBaseTests.cs index 00bcc3869bf298..c2af5df2f70593 100644 --- a/src/libraries/System.ServiceProcess.ServiceController/tests/ServiceBaseTests.cs +++ b/src/libraries/System.ServiceProcess.ServiceController/tests/ServiceBaseTests.cs @@ -183,7 +183,7 @@ public void TestOnContinueBeforePause() public void LogWritten() { string serviceName = Guid.NewGuid().ToString(); - // If the username is null, then the service is created under LocalSystem Account which have access to EventLog. + // If the username is null, then the service is created under LocalSystem Account which has access to EventLog. var testService = new TestServiceProvider(serviceName); Assert.True(EventLog.SourceExists(serviceName)); testService.DeleteTestServices(); diff --git a/src/libraries/System.ServiceProcess.ServiceController/tests/ServiceControllerTests.cs b/src/libraries/System.ServiceProcess.ServiceController/tests/ServiceControllerTests.cs index 1f0ead84e8753d..8a73b2dc41ddd6 100644 --- a/src/libraries/System.ServiceProcess.ServiceController/tests/ServiceControllerTests.cs +++ b/src/libraries/System.ServiceProcess.ServiceController/tests/ServiceControllerTests.cs @@ -148,18 +148,16 @@ public void GetServices_FindSelf() [ConditionalFact(nameof(IsProcessElevated))] public void Dependencies() { - // The test service creates a number of dependent services, each of which is depended on - // by all the services created after it. var controller = new ServiceController(_testService.TestServiceName); Assert.Equal(0, controller.DependentServices.Length); Assert.Equal(1, controller.ServicesDependedOn.Length); - var dependentController = new ServiceController(_testService.TestServiceName + ".Dependent"); - Assert.Equal(1, dependentController.DependentServices.Length); - Assert.Equal(0, dependentController.ServicesDependedOn.Length); + var prerequisiteServiceController = new ServiceController(_testService.TestServiceName + ".Prerequisite"); + Assert.Equal(1, prerequisiteServiceController.DependentServices.Length); + Assert.Equal(0, prerequisiteServiceController.ServicesDependedOn.Length); - Assert.Equal(controller.ServicesDependedOn[0].ServiceName, dependentController.ServiceName); - Assert.Equal(dependentController.DependentServices[0].ServiceName, controller.ServiceName); + Assert.Equal(controller.ServicesDependedOn[0].ServiceName, prerequisiteServiceController.ServiceName); + Assert.Equal(prerequisiteServiceController.DependentServices[0].ServiceName, controller.ServiceName); } [ConditionalFact(nameof(IsProcessElevated))] @@ -168,7 +166,7 @@ public void ServicesStartMode() var controller = new ServiceController(_testService.TestServiceName); Assert.Equal(ServiceStartMode.Manual, controller.StartType); - // Check for the startType of the dependent services. + // Check for the startType of the services that depend on the test service for (int i = 0; i < controller.DependentServices.Length; i++) { Assert.Equal(ServiceStartMode.Disabled, controller.DependentServices[i].StartType); diff --git a/src/libraries/System.ServiceProcess.ServiceController/tests/ServiceControllerTests.netcoreapp.cs b/src/libraries/System.ServiceProcess.ServiceController/tests/ServiceControllerTests.netcoreapp.cs index 9b78dfe61a4703..687fd2654b5335 100644 --- a/src/libraries/System.ServiceProcess.ServiceController/tests/ServiceControllerTests.netcoreapp.cs +++ b/src/libraries/System.ServiceProcess.ServiceController/tests/ServiceControllerTests.netcoreapp.cs @@ -12,8 +12,15 @@ public partial class ServiceControllerTests : IDisposable public void Stop_FalseArg_WithDependentServices_ThrowsInvalidOperationException() { var controller = new ServiceController(_testService.TestServiceName); - controller.WaitForStatus(ServiceControllerStatus.Running, _testService.ControlTimeout); - Assert.Throws(() => controller.Stop(stopDependentServices: false)); + Assert.Equal(0, controller.DependentServices.Length); + Assert.Equal(1, controller.ServicesDependedOn.Length); + + var prerequisiteServiceController = new ServiceController(_testService.TestServiceName + ".Prerequisite"); + Assert.Equal(1, prerequisiteServiceController.DependentServices.Length); + Assert.Equal(0, prerequisiteServiceController.ServicesDependedOn.Length); + + prerequisiteServiceController.WaitForStatus(ServiceControllerStatus.Running, _testService.ControlTimeout); + Assert.Throws(() => prerequisiteServiceController.Stop(stopDependentServices: false)); } [ConditionalFact(nameof(IsProcessElevated))] @@ -35,6 +42,7 @@ public void StopTheServiceAndItsDependentsManually() var controller = new ServiceController(_testService.TestServiceName); controller.WaitForStatus(ServiceControllerStatus.Running, _testService.ControlTimeout); + // stop the services that depend on this service foreach (var dependentService in controller.DependentServices) { dependentService.Stop(stopDependentServices: false); diff --git a/src/libraries/System.ServiceProcess.ServiceController/tests/System.ServiceProcess.ServiceController.TestService/TestService.cs b/src/libraries/System.ServiceProcess.ServiceController/tests/System.ServiceProcess.ServiceController.TestService/TestService.cs index 26aea983efe77d..5f0cad1e794c65 100644 --- a/src/libraries/System.ServiceProcess.ServiceController/tests/System.ServiceProcess.ServiceController.TestService/TestService.cs +++ b/src/libraries/System.ServiceProcess.ServiceController/tests/System.ServiceProcess.ServiceController.TestService/TestService.cs @@ -12,7 +12,7 @@ public class TestService : ServiceBase // To view tracing, use DbgView from sysinternals.com; // run it elevated, check "Capture>Global Win32" and "Capture>Win32", // and filter to just messages beginning with "##" - internal const bool DebugTracing = false; + internal const bool DebugTracing = false; // toggle in TestServiceProvider.cs as well private bool _disposed; private Task _waitClientConnect; diff --git a/src/libraries/System.ServiceProcess.ServiceController/tests/System.ServiceProcess.ServiceController.TestService/TestServiceInstaller.cs b/src/libraries/System.ServiceProcess.ServiceController/tests/System.ServiceProcess.ServiceController.TestService/TestServiceInstaller.cs index daad7d2af0ac23..5ae0d0692e5c4e 100644 --- a/src/libraries/System.ServiceProcess.ServiceController/tests/System.ServiceProcess.ServiceController.TestService/TestServiceInstaller.cs +++ b/src/libraries/System.ServiceProcess.ServiceController/tests/System.ServiceProcess.ServiceController.TestService/TestServiceInstaller.cs @@ -31,6 +31,7 @@ public TestServiceInstaller() public string ServiceCommandLine { get; set; } + // Install and start the test service, after starting any prerequisite services it depends on public unsafe void Install() { string username = Username; @@ -53,6 +54,7 @@ public unsafe void Install() } // Build servicesDependedOn string + // These are prerequisite services that must be started before this service string servicesDependedOn = null; if (ServicesDependedOn.Length > 0) { @@ -75,6 +77,8 @@ public unsafe void Install() if (serviceManagerHandle.IsInvalid) throw new InvalidOperationException("Cannot open Service Control Manager"); + TestService.DebugTrace($"TestServiceInstaller: creating service {ServiceName} with prerequisite services {servicesDependedOn}"); + // Install the service using (var serviceHandle = new SafeServiceHandle(Interop.Advapi32.CreateService(serviceManagerHandle, ServiceName, DisplayName, Interop.Advapi32.ServiceAccessOptions.ACCESS_TYPE_ALL, Interop.Advapi32.ServiceTypeOptions.SERVICE_TYPE_WIN32_OWN_PROCESS, @@ -102,11 +106,15 @@ public unsafe void Install() { if (svc.Status != ServiceControllerStatus.Running) { - TestService.DebugTrace("TestServiceInstaller: instructing ServiceController to Start service " + ServiceName); + TestService.DebugTrace($"TestServiceInstaller: instructing ServiceController to start service {ServiceName}"); svc.Start(); if (!ServiceName.StartsWith("PropagateExceptionFromOnStart")) svc.WaitForStatus(ServiceControllerStatus.Running, TimeSpan.FromSeconds(120)); } + else + { + TestService.DebugTrace("TestServiceInstaller: service {ServiceName} already running"); + } } } } diff --git a/src/libraries/System.ServiceProcess.ServiceController/tests/TestServiceProvider.cs b/src/libraries/System.ServiceProcess.ServiceController/tests/TestServiceProvider.cs index 1128d2b29e8337..4ea0bda4fd470b 100644 --- a/src/libraries/System.ServiceProcess.ServiceController/tests/TestServiceProvider.cs +++ b/src/libraries/System.ServiceProcess.ServiceController/tests/TestServiceProvider.cs @@ -13,7 +13,7 @@ internal sealed class TestServiceProvider // To view tracing, use DbgView from sysinternals.com; // run it elevated, check "Capture>Global Win32" and "Capture>Win32", // and filter to just messages beginning with "##" - internal const bool DebugTracing = false; + internal const bool DebugTracing = false; // toggle in TestService.cs as well private const int readTimeout = 60000; @@ -56,7 +56,9 @@ public NamedPipeClientStream Client public readonly string TestServiceName; public readonly string TestServiceDisplayName; - private readonly TestServiceProvider _dependentServices; + private readonly TestServiceProvider _prerequisiteServices; + + // Creates a test service with a prerequisite service public TestServiceProvider() { TestMachineName = "."; @@ -64,19 +66,20 @@ public TestServiceProvider() TestServiceName = Guid.NewGuid().ToString(); TestServiceDisplayName = "Test Service " + TestServiceName; - _dependentServices = new TestServiceProvider(TestServiceName + ".Dependent"); + _prerequisiteServices = new TestServiceProvider(TestServiceName + ".Prerequisite"); // Create the service CreateTestServices(); } + // Creates a test service with no prerequisite services public TestServiceProvider(string serviceName) { TestMachineName = "."; ControlTimeout = TimeSpan.FromSeconds(120); TestServiceName = serviceName; TestServiceDisplayName = "Test Service " + TestServiceName; - + // Create the service CreateTestServices(); } @@ -94,6 +97,7 @@ public async Task ReadPipeAsync() private void CreateTestServices() { + DebugTrace($"TestServiceProvider: Creating test service {TestServiceName}"); TestServiceInstaller testServiceInstaller = new TestServiceInstaller(); testServiceInstaller.ServiceName = TestServiceName; @@ -101,9 +105,10 @@ private void CreateTestServices() testServiceInstaller.Description = "__Dummy Test Service__"; testServiceInstaller.Username = null; - if (_dependentServices != null) + if (_prerequisiteServices != null) { - testServiceInstaller.ServicesDependedOn = new string[] { _dependentServices.TestServiceName }; + DebugTrace($"TestServiceProvider: .. with prequisite services {_prerequisiteServices.TestServiceName}"); + testServiceInstaller.ServicesDependedOn = new string[] { _prerequisiteServices.TestServiceName }; } string processName = Process.GetCurrentProcess().MainModule.FileName; @@ -122,8 +127,9 @@ private void CreateTestServices() } testServiceInstaller.ServiceCommandLine = $"\"{processName}\" {arguments}"; - + DebugTrace($"TestServiceProvider: Executing {testServiceInstaller.ServiceCommandLine}"); testServiceInstaller.Install(); + DebugTrace("TestServiceProvider: Completed install of test service"); } public void DeleteTestServices() @@ -140,14 +146,16 @@ public void DeleteTestServices() TestServiceInstaller testServiceInstaller = new TestServiceInstaller(); testServiceInstaller.ServiceName = TestServiceName; testServiceInstaller.RemoveService(); + DebugTrace("TestServiceProvider: Removed test service"); } finally { - // Lets be sure to try and clean up dependenct services even if something goes + // Lets be sure to try and clean up prerequisite services even if something goes // wrong with the full removal of the other service. - if (_dependentServices != null) + if (_prerequisiteServices != null) { - _dependentServices.DeleteTestServices(); + _prerequisiteServices.DeleteTestServices(); + DebugTrace("TestServiceProvider: Deleted test services"); } } }