Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ServiceController test #57116

Merged
merged 1 commit into from
Aug 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ public string ServiceName
}

/// <summary>
/// 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.
/// </summary>
public unsafe ServiceController[] ServicesDependedOn
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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))]
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<InvalidOperationException>(() => 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<InvalidOperationException>(() => prerequisiteServiceController.Stop(stopDependentServices: false));
}

[ConditionalFact(nameof(IsProcessElevated))]
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
{
Expand All @@ -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,
Expand Down Expand Up @@ -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");
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -56,27 +56,30 @@ 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 = ".";
ControlTimeout = TimeSpan.FromSeconds(120);
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();
}
Expand All @@ -94,16 +97,18 @@ public async Task<byte> ReadPipeAsync()

private void CreateTestServices()
{
DebugTrace($"TestServiceProvider: Creating test service {TestServiceName}");
TestServiceInstaller testServiceInstaller = new TestServiceInstaller();

testServiceInstaller.ServiceName = TestServiceName;
testServiceInstaller.DisplayName = TestServiceDisplayName;
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;
Expand All @@ -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()
Expand All @@ -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");
}
}
}
Expand Down