PowerShell/test/powershell/Language/Scripting/ParameterBinding.Tests.ps1

506 lines
16 KiB
PowerShell
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
Describe "Tests for parameter binding" -Tags "CI" {
Context 'Test of Mandatory parameters' {
BeforeAll {
$f = "function get-foo { param([Parameter(mandatory=`$true)] `$a) `$a };"
}
It 'Mandatory parameters used in non-interactive host' {
$rs = [system.management.automation.runspaces.runspacefactory]::CreateRunspace()
$rs.open()
$ps = [System.Management.Automation.PowerShell]::Create()
$ps.Runspace = $rs
try
{
[void] $ps.AddScript($f + "get-foo")
$asyncResult = $ps.BeginInvoke()
$ps.EndInvoke($asyncResult)
$ps.Streams.Error.Count | Should -Be 1 # the host does not implement it.
$ps.InvocationStateInfo.State | Should -BeExactly 'Completed'
} finally {
$ps.Dispose()
$rs.Dispose()
}
}
It 'Mandatory parameters used in interactive host' {
$th = New-TestHost
$rs = [system.management.automation.runspaces.runspacefactory]::Createrunspace($th)
$rs.open()
$ps = [System.Management.Automation.PowerShell]::Create()
$ps.Runspace = $rs
try
{
$ps.AddScript($f + "get-foo").invoke()
$prompt = $th.ui.streams.prompt[0]
$prompt | Should -Not -BeNullOrEmpty
$result = $prompt.split(":")
$result[0] | Should -Match 'get-foo'
$result[-1] | Should -BeExactly 'a'
} finally {
$rs.Close()
$rs.Dispose()
$ps.Dispose()
}
}
}
It 'Test of positional parameters' {
function get-foo
{
[CmdletBinding()]
param($a)
$a
}
get-foo a | Should -BeExactly 'a'
get-foo -a b | Should -BeExactly 'b'
}
It 'Positional parameters when only one position specified: position = 1' {
function get-foo
{
param([Parameter(position=1)] $a )
$a
}
get-foo b | Should -BeExactly 'b'
}
It 'Positional parameters when only position specified: position = 2' {
function get-foo
{
param([Parameter(position=2)] $a )
$a
}
get-foo b | Should -BeExactly 'b'
}
It 'Multiple positional parameters case 1' {
function get-foo
{
param( [Parameter(position=1)] $a,
[Parameter(position=2)] $b )
$a; $b
}
( get-foo c d ) -join ',' | Should -BeExactly 'c,d'
( get-foo -a c d ) -join ',' | Should -BeExactly 'c,d'
( get-foo -a c -b d ) -join ',' | Should -BeExactly 'c,d'
( get-foo -b d c ) -join ',' | Should -BeExactly 'c,d'
( get-foo c -b d ) -join ',' | Should -BeExactly 'c,d'
}
It 'Multiple positional parameters case 2: the parameters are put in different order?' {
function get-foo
{
# the parameters are purposefully out of order.
param( [Parameter(position=2)] $a,
[Parameter(position=1)] $b )
$a; $b
}
(get-foo c d) -join ',' | Should -BeExactly 'd,c'
}
It 'Value from pipeline' {
function get-foo
{
param( [Parameter(valuefrompipeline=$true)] $a )
process
{
if($a % 2 -eq 0)
{
$a
}
}
}
(1..10 | get-foo) -join ',' | Should -BeExactly '2,4,6,8,10'
}
It 'Value from pipeline by property name' {
function get-foo
{
param( [Parameter(valuefrompipelinebypropertyname=$true)] $foo )
process
{
if($foo % 2 -eq 0)
{
$foo
}
}
}
$b = 1..10 | Select-Object @{name='foo'; expression={$_ * 10}} | get-foo
$b -join ',' | Should -BeExactly '10,20,30,40,50,60,70,80,90,100'
}
It 'Value from remaining arguments' {
function get-foo
{
param(
[Parameter(position=1)] $a,
[Parameter(valuefromremainingarguments=$true)] $foo
)
$foo
}
( get-foo a b c d ) -join ',' | Should -BeExactly 'b,c,d'
( get-foo a b -a c d ) -join ',' | Should -BeExactly 'a,b,d'
( get-foo a b -a c -q d ) -join ',' | Should -BeExactly 'a,b,-q,d'
}
It 'Multiple parameter sets with Value from remaining arguments' {
function get-foo
{
param( [Parameter(parametersetname='set1',position=1)] $a,
[Parameter(parametersetname='set2',position=1)] $b,
[parameter(valuefromremainingarguments=$true)] $foo )
$foo
}
{ get-foo -a a -b b c d } | Should -Throw -ErrorId 'AmbiguousParameterSet,get-foo'
( get-foo -a a b c d ) -join ',' | Should -BeExactly 'b,c,d'
( get-foo -b b a c d ) -join ',' | Should -BeExactly 'a,c,d'
}
It 'Default parameter set with value from remaining arguments case 1' {
function get-foo
{
[CmdletBinding(DefaultParameterSetName="set1")]
param( [Parameter(parametersetname="set1", position=1)] $a,
[Parameter(parametersetname="set2", position=1)] $b,
[parameter(valuefromremainingarguments=$true)] $foo )
$a,$b,$foo
}
$x,$y,$z=get-foo a b c d
$x | Should -BeExactly 'a'
$y | Should -BeNullOrEmpty
$z -join ',' | Should -BeExactly 'b,c,d'
}
It 'Default parameter set with value from remaining argument case 2' {
function get-foo
{
[CmdletBinding(DefaultParameterSetName="set2")]
param( [Parameter(parametersetname="set1", position = 1)] $a,
[Parameter(parametersetname="set2", position = 1)] $b,
[parameter(valuefromremainingarguments=$true)] $foo )
$a,$b,$foo
}
$x,$y,$z=get-foo a b c d
$x | Should -BeNullOrEmpty
$y | Should -BeExactly 'a'
$z -join ',' | Should -BeExactly 'b,c,d'
}
It 'Alias are specified for parameters' {
function get-foo
{
param( [Parameter(mandatory=$true)][alias("foo", "bar")] $a )
$a
}
get-foo -foo b | Should -BeExactly 'b'
}
It 'Invoking with script block' {
$foo = . { param([Parameter(position=2)] $a, [Parameter(position=1)]$b); $a; $b} a b
$foo[0] | Should -BeExactly 'b'
}
It 'Normal functions' {
function foo ($a, $b) {$b, $a}
( foo a b ) -join ',' | Should -BeExactly 'b,a'
}
It 'Null is not Allowed when AllowNull attribute is not set' {
function get-foo
{
param( [Parameter(mandatory=$true)] $a )
$a
}
{ get-foo -a $null } | Should -Throw -ErrorId 'ParameterArgumentValidationErrorNullNotAllowed,get-foo'
}
It 'Null is allowed when Allownull attribute is set' {
function get-foo
{
param( [Parameter(mandatory=$true)][allownull()] $a )
$a
}
(get-foo -a $null) | Should -BeNullOrEmpty
}
It 'Empty string is not allowed AllowEmptyString Attribute is not set' {
function get-foo
{
param( [Parameter(mandatory=$true)][string] $a )
$a
}
{ get-foo -a '' } | Should -Throw -ErrorId 'ParameterArgumentValidationErrorEmptyStringNotAllowed,get-foo'
}
It 'Empty string is allowed when AllowEmptyString Attribute is set' {
function get-foo
{
param( [Parameter(mandatory=$true)][allowemptystring()][string] $a )
$a
}
get-foo -a '' | Should -BeExactly ''
}
It 'Empty collection is not allowed when AllowEmptyCollection it not set' {
function get-foo
{
param( [Parameter(mandatory=$true)][string[]] $a )
$a
}
{ get-foo -a @() } | Should -Throw -ErrorId 'ParameterArgumentValidationErrorEmptyArrayNotAllowed,get-foo'
}
It 'Empty collection is allowed when allowEmptyCollection is set' {
function get-foo
{
param( [Parameter(mandatory=$true)][allowemptycollection()][string[]] $a )
$a
}
get-foo -a @() | Should -BeNullOrEmpty
}
It 'Unspecified non-mandatory bool should not cause exception' {
function get-foo
{
param([Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)] $a,
[System.Boolean] $b)
$a
}
42 | get-foo | Should -Be 42
}
It 'Parameter binding failure on Parameter1 should not cause parameter binding failure on Length' {
function get-foo
{
param( [Parameter(ValueFromPipeline = $true)] [int] $Parameter1 = 10,
[Parameter(ValueFromPipelineByPropertyName = $true)] [int] $Length = 100 )
process { $Length }
}
'abc' | get-foo | Should -Be 3
}
It 'Binding array of string to array of bool should fail (cmdletbinding)' {
function get-foo
{
[cmdletbinding()]
param ([bool[]] $Parameter )
$Parameter
}
{ get-foo 'a','b' } | Should -Throw -ErrorId 'ParameterArgumentTransformationError,get-foo'
}
It "Binding array of string to array of bool should succeed" {
function get-foo
{
param ([bool[]] $Parameter)
$Parameter
}
$x = get-foo 'a','b'
$x[0] | Should -BeTrue
$x[1] | Should -BeTrue
}
Context 'Default value conversion tests' {
It 'Parameter default value is converted correctly to the proper type when nothing is set on parameter' {
function get-fooa
{
param( [System.Reflection.MemberTypes] $memberTypes = $([Enum]::GetNames("System.Reflection.MemberTypes") -join ",") )
$memberTypes | Should -BeOfType System.Reflection.MemberTypes
}
get-fooa
}
It "Parameter default value is converted correctly to the proper type when CmdletBinding is set on param" {
function get-foob
{
[CmdletBinding()]
param( [System.Reflection.MemberTypes] $memberTypes = $([Enum]::GetNames("System.Reflection.MemberTypes") -join ",") )
$memberTypes | Should -BeOfType System.Reflection.MemberTypes
}
get-foob
}
It "No default value specified should not cause error when parameter attribute is set on the parameter" {
function get-fooc
{
param( [Parameter()] [System.Reflection.MemberTypes] $memberTypes )
$memberTypes | Should -BeNullOrEmpty
}
get-fooc
}
It "No default value specified should not cause error when nothing is set on parameter" {
function get-food
{
param( [System.Reflection.MemberTypes] $memberTypes )
$memberTypes | Should -BeNullOrEmpty
}
get-food
}
It "Validation attributes should not run on default values when nothing is set on the parameter" {
function get-fooe
{
param([ValidateRange(1,42)] $p = 55)
$p
}
get-fooe | Should -Be 55
}
It "Validation attributes should not run on default values when CmdletBinding is set on the parameter" {
function get-foof
{
[CmdletBinding()]
param([ValidateRange(1,42)] $p = 55)
$p
}
get-foof | Should -Be 55
}
It "Validation attributes should not run on default values" {
function get-foog
{
param([ValidateRange(1,42)] $p)
$p
}
{ get-foog } | Should -Not -Throw
}
It "Validation attributes should not run on default values when CmdletBinding is set" {
function get-fooh
{
[CmdletBinding()]
param([ValidateRange(1,42)] $p)
$p
}
{ get-fooh } | Should -Not -Throw
}
It "ValidateScript can use custom ErrorMessage" {
function get-fooi {
[CmdletBinding()]
param([ValidateScript({$_ -gt 2}, ErrorMessage = "Item '{0}' failed '{1}' validation")] $p)
$p
}
$err = { get-fooi -p 2 } | Should -Throw -ErrorId 'ParameterArgumentValidationError,get-fooi' -PassThru
$err.Exception.Message | Should -BeExactly "Cannot validate argument on parameter 'p'. Item '2' failed '`$_ -gt 2' validation"
}
It "ValidatePattern can use custom ErrorMessage" {
function get-fooj
{
[CmdletBinding()]
param([ValidatePattern("\s+", ErrorMessage = "Item '{0}' failed '{1}' regex")] $p)
$p
}
$err = { get-fooj -p 2 } | Should -Throw -ErrorId 'ParameterArgumentValidationError,get-fooj' -PassThru
$err.Exception.Message | Should -BeExactly "Cannot validate argument on parameter 'p'. Item '2' failed '\s+' regex"
}
It "ValidateSet can use custom ErrorMessage" {
function get-fook
{
param([ValidateSet('A', 'B', 'C', IgnoreCase=$false, ErrorMessage="Item '{0}' is not in '{1}'")] $p)
}
$err = { get-fook -p 2 } | Should -Throw -ErrorId 'ParameterArgumentValidationError,get-fook' -PassThru
$set = 'A','B','C' -join [Globalization.CultureInfo]::CurrentUICulture.TextInfo.ListSeparator
$err.Exception.Message | Should -BeExactly "Cannot validate argument on parameter 'p'. Item '2' is not in '$set'"
}
}
#known issue 2069
It 'Some conversions should be attempted before trying to encode a collection' -Skip:$IsCoreCLR {
try {
$null = [Test.Language.ParameterBinding.MyClass]
}
catch {
Add-Type -PassThru -TypeDefinition @'
using System.Management.Automation;
using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.IO;
namespace Test.Language.ParameterBinding {
public class MyClass : Collection<string>
{
public MyClass() {}
public MyClass(Hashtable h) {}
}
[Cmdlet("Get", "TestCmdlet")]
public class MyCmdlet : PSCmdlet {
[Parameter]
public MyClass MyParameter
{
get { return myParameter; }
set { myParameter = value; }
}
private MyClass myParameter;
protected override void ProcessRecord()
{
WriteObject((myParameter == null) ? "<null>" : "hashtable");
}
}
}
'@ | ForEach-Object {$_.assembly} | Import-Module
}
Get-TestCmdlet -MyParameter @{ a = 42 } | Should -BeExactly 'hashtable'
}
It 'Parameter pasing is consuming enumerators' {
$a = 1..4
$b = $a.getenumerator()
$null = $b.MoveNext()
$null = $b.current
& { } $b
#The position of the enumerator shouldn't be modified
$b.current | Should -Be 1
}
}