fix set-service failing test (#4802)

* Add '-ErrorAction Stop' in Set-Service.Tests.ps1 to correctly test exceptions.
* Refactor StartupType service code and add a throw for disabled startup types (System, Boot).
* Made StartupType(-1) equivalent to Win32 SERVICE_NO_CHANGE.
This commit is contained in:
Steve Lee 2017-09-15 01:24:40 -07:00 committed by Ilya
parent da49841f16
commit b723d6b7f2
3 changed files with 76 additions and 48 deletions

View file

@ -1511,8 +1511,6 @@ namespace Microsoft.PowerShell.Commands
internal ServiceStartMode startupType = (ServiceStartMode)(-1); internal ServiceStartMode startupType = (ServiceStartMode)(-1);
/// <summary> /// <summary>
/// The following is the definition of the input parameter "Status". /// The following is the definition of the input parameter "Status".
/// This specifies what state the service should be in (e.g. Running, Stopped, /// This specifies what state the service should be in (e.g. Running, Stopped,
@ -1684,22 +1682,13 @@ namespace Microsoft.PowerShell.Commands
|| (ServiceStartMode)(-1) != StartupType) || (ServiceStartMode)(-1) != StartupType)
{ {
DWORD dwStartType = NativeMethods.SERVICE_NO_CHANGE; DWORD dwStartType = NativeMethods.SERVICE_NO_CHANGE;
switch (StartupType) if (!NativeMethods.TryGetNativeStartupType(StartupType, out dwStartType))
{ {
case ServiceStartMode.Automatic: WriteNonTerminatingError(StartupType.ToString(), "Set-Service", Name,
dwStartType = NativeMethods.SERVICE_AUTO_START; new ArgumentException(), "CouldNotSetService",
break; ServiceResources.UnsupportedStartupType,
case ServiceStartMode.Manual: ErrorCategory.InvalidArgument);
dwStartType = NativeMethods.SERVICE_DEMAND_START; return;
break;
case ServiceStartMode.Disabled:
dwStartType = NativeMethods.SERVICE_DISABLED;
break;
default:
Diagnostics.Assert(
((ServiceStartMode)(-1)) == StartupType,
"bad StartupType");
break;
} }
bool succeeded = NativeMethods.ChangeServiceConfigW( bool succeeded = NativeMethods.ChangeServiceConfigW(
hService, hService,
@ -2002,25 +1991,14 @@ namespace Microsoft.PowerShell.Commands
ErrorCategory.PermissionDenied); ErrorCategory.PermissionDenied);
return; return;
} }
DWORD dwStartType = NativeMethods.SERVICE_AUTO_START; if (!NativeMethods.TryGetNativeStartupType(StartupType, out DWORD dwStartType))
switch (StartupType)
{ {
case ServiceStartMode.Automatic: WriteNonTerminatingError(StartupType.ToString(), "New-Service", Name,
dwStartType = NativeMethods.SERVICE_AUTO_START; new ArgumentException(), "CouldNotNewService",
break; ServiceResources.UnsupportedStartupType,
case ServiceStartMode.Manual: ErrorCategory.InvalidArgument);
dwStartType = NativeMethods.SERVICE_DEMAND_START; return;
break;
case ServiceStartMode.Disabled:
dwStartType = NativeMethods.SERVICE_DISABLED;
break;
default:
Diagnostics.Assert(
((ServiceStartMode)(-1)) == StartupType,
"bad StartupType");
break;
} }
// set up the double-null-terminated lpDependencies parameter // set up the double-null-terminated lpDependencies parameter
IntPtr lpDependencies = IntPtr.Zero; IntPtr lpDependencies = IntPtr.Zero;
if (null != DependsOn) if (null != DependsOn)
@ -2410,6 +2388,43 @@ namespace Microsoft.PowerShell.Commands
public static extern bool QueryInformationJobObject(SafeHandle hJob, int JobObjectInfoClass, public static extern bool QueryInformationJobObject(SafeHandle hJob, int JobObjectInfoClass,
ref JOBOBJECT_BASIC_PROCESS_ID_LIST lpJobObjectInfo, ref JOBOBJECT_BASIC_PROCESS_ID_LIST lpJobObjectInfo,
int cbJobObjectLength, IntPtr lpReturnLength); int cbJobObjectLength, IntPtr lpReturnLength);
/// <summary>
/// Get appropriate win32 StartupType
/// </summary>
/// <param name="StartupType">
/// StartupType provided by the user.
/// </param>
/// <param name="dwStartType">
/// Out parameter of the native win32 StartupType
/// </param>
/// <returns>
/// If a supported StartupType is provided, funciton returns true, otherwise false.
/// </returns>
internal static bool TryGetNativeStartupType(ServiceStartMode StartupType, out DWORD dwStartType)
{
bool success = true;
dwStartType = NativeMethods.SERVICE_NO_CHANGE;
switch (StartupType)
{
case ServiceStartMode.Automatic:
dwStartType = NativeMethods.SERVICE_AUTO_START;
break;
case ServiceStartMode.Manual:
dwStartType = NativeMethods.SERVICE_DEMAND_START;
break;
case ServiceStartMode.Disabled:
dwStartType = NativeMethods.SERVICE_DISABLED;
break;
case (ServiceStartMode)(-1):
dwStartType = NativeMethods.SERVICE_NO_CHANGE;
break;
default:
success = false;
break;
}
return success;
}
} }
#endregion NativeMethods #endregion NativeMethods
} }

View file

@ -201,4 +201,7 @@
<data name="ComputerAccessDenied" xml:space="preserve"> <data name="ComputerAccessDenied" xml:space="preserve">
<value>The command cannot be used to configure the service '{0}' because access to computer '{1}' is denied. Run PowerShell as admin and run your command again.</value> <value>The command cannot be used to configure the service '{0}' because access to computer '{1}' is denied. Run PowerShell as admin and run your command again.</value>
</data> </data>
<data name="UnsupportedStartupType" xml:space="preserve">
<value>The startup type '{0}' is not supported by {1}.</value>
</data>
</root> </root>

View file

@ -22,15 +22,18 @@ Describe "Set/New-Service cmdlet tests" -Tags "Feature", "RequireAdminOnWindows"
@{parameter = "Status" ; value = "Running"}, @{parameter = "Status" ; value = "Running"},
@{parameter = "Status" ; value = "Stopped"}, @{parameter = "Status" ; value = "Stopped"},
@{parameter = "Status" ; value = "Paused"}, @{parameter = "Status" ; value = "Paused"},
@{parameter = "InputObject" ; value = (Get-Service | Select-Object -First 1)}, @{parameter = "InputObject" ; script = {Get-Service | Select-Object -First 1}},
# cmdlet inherits this property, but it's not exposed as parameter so it should be $null # cmdlet inherits this property, but it's not exposed as parameter so it should be $null
@{parameter = "Include" ; value = "foo", "bar" ; expectedNull = $true}, @{parameter = "Include" ; value = "foo", "bar" ; expectedNull = $true},
# cmdlet inherits this property, but it's not exposed as parameter so it should be $null # cmdlet inherits this property, but it's not exposed as parameter so it should be $null
@{parameter = "Exclude" ; value = "foo", "bar" ; expectedNull = $true} @{parameter = "Exclude" ; value = "foo", "bar" ; expectedNull = $true}
) { ) {
param($parameter, $value, $expectedNull) param($parameter, $value, $script, $expectedNull)
$setServiceCommand = [Microsoft.PowerShell.Commands.SetServiceCommand]::new() $setServiceCommand = [Microsoft.PowerShell.Commands.SetServiceCommand]::new()
if ($script -ne $Null) {
$value = & $script
}
$setServiceCommand.$parameter = $value $setServiceCommand.$parameter = $value
if ($expectedNull -eq $true) { if ($expectedNull -eq $true) {
$setServiceCommand.$parameter | Should BeNullOrEmpty $setServiceCommand.$parameter | Should BeNullOrEmpty
@ -42,7 +45,7 @@ Describe "Set/New-Service cmdlet tests" -Tags "Feature", "RequireAdminOnWindows"
It "Set-Service parameter validation for invalid values: <script>" -TestCases @( It "Set-Service parameter validation for invalid values: <script>" -TestCases @(
@{ @{
script = {Set-Service foo -StartupType bar}; script = {Set-Service foo -StartupType bar -ErrorAction Stop};
errorid = "CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.SetServiceCommand" errorid = "CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.SetServiceCommand"
} }
) { ) {
@ -58,6 +61,7 @@ Describe "Set/New-Service cmdlet tests" -Tags "Feature", "RequireAdminOnWindows"
) { ) {
param($parameter, $value, $expected) param($parameter, $value, $expected)
$currentService = Get-CimInstance -ClassName Win32_Service -Filter "Name='spooler'" $currentService = Get-CimInstance -ClassName Win32_Service -Filter "Name='spooler'"
$originalStartupType = (Get-Service -Name spooler).StartType
try { try {
$setServiceCommand = [Microsoft.PowerShell.Commands.SetServiceCommand]::new() $setServiceCommand = [Microsoft.PowerShell.Commands.SetServiceCommand]::new()
$setServiceCommand.Name = "Spooler" $setServiceCommand.Name = "Spooler"
@ -76,7 +80,7 @@ Describe "Set/New-Service cmdlet tests" -Tags "Feature", "RequireAdminOnWindows"
} }
finally { finally {
if ($parameter -eq "StartupType") { if ($parameter -eq "StartupType") {
$setServiceCommand.StartupType = $currentService.StartMode $setServiceCommand.StartupType = $originalStartupType
} }
else { else {
$setServiceCommand.$parameter = $currentService.$parameter $setServiceCommand.$parameter = $currentService.$parameter
@ -158,17 +162,23 @@ Describe "Set/New-Service cmdlet tests" -Tags "Feature", "RequireAdminOnWindows"
} }
} }
It "New-Service with bad parameters will fail for '<name>' where '<parameter>' = '<value>'" -TestCases @( It "Using bad parameters will fail for '<name>' where '<parameter>' = '<value>'" -TestCases @(
@{name = 'credtest' ; parameter = "Credential" ; value = ( @{cmdlet="New-Service"; name = 'credtest' ; parameter = "Credential" ; value = (
[System.Management.Automation.PSCredential]::new("username", [System.Management.Automation.PSCredential]::new("username",
(ConvertTo-SecureString "PlainTextPassword" -AsPlainText -Force))) (ConvertTo-SecureString "PlainTextPassword" -AsPlainText -Force)));
}, errorid = "CouldNotNewService,Microsoft.PowerShell.Commands.NewServiceCommand"},
# This test case fails due to #4803. Disabled for now. @{cmdlet="New-Service"; name = 'badstarttype'; parameter = "StartupType"; value = "System";
# @{name = 'badstarttype'; parameter = "StartupType"; value = "System"}, errorid = "CouldNotNewService,Microsoft.PowerShell.Commands.NewServiceCommand"},
@{name = 'winmgmt' ; parameter = "DisplayName"; value = "foo"} @{cmdlet="New-Service"; name = 'winmgmt' ; parameter = "DisplayName"; value = "foo";
errorid = "CouldNotNewService,Microsoft.PowerShell.Commands.NewServiceCommand"},
@{cmdlet="Set-Service"; name = 'winmgmt' ; parameter = "StartupType"; value = "Boot";
errorid = "CouldNotSetService,Microsoft.PowerShell.Commands.SetServiceCommand"}
) { ) {
param($name, $parameter, $value) param($cmdlet, $name, $parameter, $value, $errorid)
$parameters = @{$parameter = $value; Name = $name; Binary = "$PSHOME\powershell.exe"; ErrorAction = "Stop"} $parameters = @{$parameter = $value; Name = $name; ErrorAction = "Stop"}
{ New-Service @parameters } | ShouldBeErrorId "CouldNotNewService,Microsoft.PowerShell.Commands.NewServiceCommand" if ($cmdlet -eq "New-Service") {
$parameters += @{Binary = "$PSHOME\powershell.exe"};
}
{ & $cmdlet @parameters } | ShouldBeErrorId $errorid
} }
} }