Fix ValueFromRemainingArguments to have consistent behavior between script and C# (#2038)

This commit is contained in:
Dave Wyatt
2017-10-11 17:09:59 -07:00
committed by Dongbo Wang
parent 2a9cd7211f
commit 94a71b05d4
3 changed files with 112 additions and 22 deletions
@@ -55,10 +55,8 @@ namespace Microsoft.PowerShell.Commands
{
enumerate = false;
}
foreach (PSObject inputObject in _inputObjects) // compensate for ValueFromRemainingArguments
{
WriteObject(inputObject, enumerate);
}
WriteObject(_inputObjects, enumerate);
}//processrecord
}//WriteOutputCommand
#endregion
@@ -1684,33 +1684,31 @@ namespace System.Management.Automation
var cpi = CommandParameterInternal.CreateParameterWithArgument(
PositionUtilities.EmptyExtent, varargsParameter.Parameter.Name, "-" + varargsParameter.Parameter.Name + ":",
argumentExtent, valueFromRemainingArguments, false);
// To make all of the following work similarly (the first is handled elsewhere, but second and third are
// handled here):
// Set-ClusterOwnerNode -Owners foo,bar
// Set-ClusterOwnerNode foo bar
// Set-ClusterOwnerNode foo,bar
// we unwrap our List, but only if there is a single argument of type object[].
if (valueFromRemainingArguments.Count == 1 && valueFromRemainingArguments[0] is object[])
{
cpi.SetArgumentValue(UnboundArguments[0].ArgumentExtent, valueFromRemainingArguments[0]);
}
try
{
BindParameter(cpi, varargsParameter, ParameterBindingFlags.ShouldCoerceType);
}
catch (ParameterBindingException pbex)
{
// To make all of the following work similarly (the first is handled elsewhere, but second and third are
// handled here):
// Set-ClusterOwnerNode -Owners foo,bar
// Set-ClusterOwnerNode foo bar
// Set-ClusterOwnerNode foo,bar
// we make one additional attempt at converting, but only if there is a single argument of type object[].
if (valueFromRemainingArguments.Count == 1 && valueFromRemainingArguments[0] is object[])
if (!DefaultParameterBindingInUse)
{
cpi.SetArgumentValue(UnboundArguments[0].ArgumentExtent, valueFromRemainingArguments[0]);
BindParameter(cpi, varargsParameter, ParameterBindingFlags.ShouldCoerceType);
throw;
}
else
{
if (!DefaultParameterBindingInUse)
{
throw;
}
else
{
ThrowElaboratedBindingException(pbex);
}
ThrowElaboratedBindingException(pbex);
}
}
UnboundArguments.Clear();
@@ -228,7 +228,7 @@
[CmdletBinding()]
param (
[array]$Parameter1,
[int[]]$Parameter2
[int[]]$Parameter2
)
Process {
@@ -329,4 +329,98 @@
$result | Should Be $expected
}
}
Context "ValueFromRemainingArguments" {
BeforeAll {
function Test-BindingFunction {
param (
[Parameter(ValueFromRemainingArguments)]
[object[]] $Parameter
)
return [pscustomobject] @{
ArgumentCount = $Parameter.Count
Value = $Parameter
}
}
# Deliberately not using TestDrive:\ here because Pester will fail to clean it up due to the
# assembly being loaded in our process.
if ($IsWindows)
{
$tempDir = $env:temp
}
else
{
$tempDir = '/tmp'
}
$dllPath = Join-Path $tempDir TestBindingCmdlet.dll
Add-Type -OutputAssembly $dllPath -TypeDefinition '
using System;
using System.Management.Automation;
[Cmdlet("Test", "BindingCmdlet")]
public class TestBindingCommand : PSCmdlet
{
[Parameter(Position = 0, ValueFromRemainingArguments = true)]
public string[] Parameter { get; set; }
protected override void ProcessRecord()
{
PSObject obj = new PSObject();
obj.Properties.Add(new PSNoteProperty("ArgumentCount", Parameter.Length));
obj.Properties.Add(new PSNoteProperty("Value", Parameter));
WriteObject(obj);
}
}
'
Import-Module $dllPath
}
AfterAll {
Get-Module TestBindingCmdlet | Remove-Module -Force
}
It "Binds properly when passing an explicit array to an advanced function" {
$result = Test-BindingFunction 1,2,3
$result.ArgumentCount | Should Be 3
$result.Value[0] | Should Be 1
$result.Value[1] | Should Be 2
$result.Value[2] | Should Be 3
}
It "Binds properly when passing multiple arguments to an advanced function" {
$result = Test-BindingFunction 1 2 3
$result.ArgumentCount | Should Be 3
$result.Value[0] | Should Be 1
$result.Value[1] | Should Be 2
$result.Value[2] | Should Be 3
}
It "Binds properly when passing an explicit array to a cmdlet" {
$result = Test-BindingCmdlet 1,2,3
$result.ArgumentCount | Should Be 3
$result.Value[0] | Should Be 1
$result.Value[1] | Should Be 2
$result.Value[2] | Should Be 3
}
It "Binds properly when passing multiple arguments to a cmdlet" {
$result = Test-BindingCmdlet 1 2 3
$result.ArgumentCount | Should Be 3
$result.Value[0] | Should Be 1
$result.Value[1] | Should Be 2
$result.Value[2] | Should Be 3
}
}
}