2018-02-13 18:23:53 +01:00
|
|
|
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
# Licensed under the MIT License.
|
2017-09-30 01:28:15 +02:00
|
|
|
$powershellexe = (get-process -id $PID).mainmodule.filename
|
|
|
|
|
|
|
|
Describe "Clone array" -Tags "CI" {
|
|
|
|
It "Cast in target expr" {
|
2018-03-21 18:47:08 +01:00
|
|
|
(([int[]](42)).clone()) | Should -Be 42
|
|
|
|
(([int[]](1..5)).clone()).Length | Should -Be 5
|
|
|
|
(([int[]](1..5)).clone()).GetType() | Should -Be ([int[]])
|
2017-09-30 01:28:15 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
It "Cast not in target expr" {
|
|
|
|
$e = [int[]](42)
|
2018-03-21 18:47:08 +01:00
|
|
|
$e.Clone() | Should -Be 42
|
2017-09-30 01:28:15 +02:00
|
|
|
$e = [int[]](1..5)
|
2018-03-21 18:47:08 +01:00
|
|
|
$e.Clone().Length | Should -Be 5
|
|
|
|
$e.Clone().GetType() | Should -Be ([int[]])
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Describe "Set fields through PSMemberInfo" -Tags "CI" {
|
|
|
|
Add-Type @"
|
|
|
|
public struct AStruct { public string s; }
|
|
|
|
"@
|
|
|
|
|
|
|
|
It "via cast" {
|
2018-03-21 18:47:08 +01:00
|
|
|
([AStruct]@{s = "abc" }).s | Should -BeExactly "abc"
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
It "via new-object" {
|
2018-03-21 18:47:08 +01:00
|
|
|
(new-object AStruct -prop @{s="abc"}).s | Should -BeExactly "abc"
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
It "via PSObject" {
|
|
|
|
$x = [AStruct]::new()
|
|
|
|
$x.psobject.properties['s'].Value = 'abc'
|
2018-03-21 18:47:08 +01:00
|
|
|
$x.s | Should -Be "abc"
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Describe "MSFT:3309783" -Tags "CI" {
|
|
|
|
|
|
|
|
It "Run in another process" {
|
|
|
|
# For a reliable test, we must run this in a new process because an earlier binding in this process
|
|
|
|
# could mask the bug/fix.
|
2018-03-21 18:47:08 +01:00
|
|
|
& $powershellexe -noprofile -command "[psobject] | ForEach-Object FullName" | Should -Be System.Management.Automation.PSObject
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
It "Run in current process" {
|
|
|
|
# For good measure, do the same thing in this process
|
2018-03-21 18:47:08 +01:00
|
|
|
[psobject] | ForEach-Object FullName | Should -Be System.Management.Automation.PSObject
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
It "Pipe objects derived from PSObject" {
|
|
|
|
# Related - make sure we can still pipe objects derived from PSObject
|
|
|
|
class MyPsObj : PSObject
|
|
|
|
{
|
|
|
|
MyPsObj($obj) : base($obj) { }
|
|
|
|
[string] ToString() {
|
|
|
|
# Don't change access via .psobject, that was also a bug.
|
|
|
|
return "MyObj: " + $this.psobject.BaseObject
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-21 18:47:08 +01:00
|
|
|
[MyPsObj]::new("abc").psobject.ToString() | Should -Be "MyObj: abc"
|
|
|
|
[MyPsObj]::new("def") | Out-String | ForEach-Object Trim | Should -Be "MyObj: def"
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Describe "ScriptBlockAst.GetScriptBlock throws on error" -Tags "CI" {
|
|
|
|
|
|
|
|
$e = $null
|
|
|
|
|
|
|
|
It "with parse error" {
|
|
|
|
$ast = [System.Management.Automation.Language.Parser]::ParseInput('function ', [ref]$null, [ref]$e)
|
2018-03-21 18:47:08 +01:00
|
|
|
{ $ast.GetScriptBlock() } | Should -Throw -ErrorId "PSInvalidOperationException"
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
It "with semantic errors" {
|
|
|
|
$ast = [System.Management.Automation.Language.Parser]::ParseInput('function foo{param()begin{}end{[ref][ref]1}dynamicparam{}}', [ref]$null, [ref]$e)
|
|
|
|
|
2018-03-21 18:47:08 +01:00
|
|
|
{ $ast.GetScriptBlock() } | Should -Throw -ErrorId "PSInvalidOperationException"
|
|
|
|
{ $ast.EndBlock.Statements[0].Body.GetScriptBlock() } | Should -Throw -ErrorId "PSInvalidOperationException"
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Describe "Hashtable key property syntax" -Tags "CI" {
|
|
|
|
$script = @'
|
|
|
|
# First create a hashtable wrapped in PSObject
|
|
|
|
$hash = New-Object hashtable
|
|
|
|
$key = [ConsoleColor]::Red
|
|
|
|
$null = $hash.$key
|
|
|
|
$hash = @{}
|
|
|
|
$hash.$key = 'Hello'
|
|
|
|
# works in PS 2,3,4. Fails in PS 5:
|
|
|
|
$hash.$key
|
|
|
|
'@
|
|
|
|
|
|
|
|
It "In current process" {
|
|
|
|
# Run in current process, but something that ran earlier could influence
|
|
|
|
# the result
|
2018-03-21 18:47:08 +01:00
|
|
|
Invoke-Expression $script | Should -BeExactly 'Hello'
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
It "In different process" {
|
|
|
|
# So also run in a fresh process
|
|
|
|
$bytes = [System.Text.Encoding]::Unicode.GetBytes($script)
|
2018-03-21 18:47:08 +01:00
|
|
|
& $powershellexe -noprofile -encodedCommand ([Convert]::ToBase64String($bytes)) | Should -BeExactly 'Hello'
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Describe "Assign automatic variables" -Tags "CI" {
|
|
|
|
|
|
|
|
$autos = '_', 'args', 'this', 'input', 'pscmdlet', 'psboundparameters', 'myinvocation', 'psscriptroot', 'pscommandpath'
|
|
|
|
|
|
|
|
foreach ($auto in $autos)
|
|
|
|
{
|
|
|
|
It "Assign auto w/ invalid type constraint - $auto" {
|
2018-03-21 18:47:08 +01:00
|
|
|
{ & ([ScriptBlock]::Create("[datetime]`$$auto = 1")) } | Should -Throw $auto
|
|
|
|
{ . ([ScriptBlock]::Create("[datetime]`$$auto = 1")) } | Should -Throw $auto
|
|
|
|
{ & ([ScriptBlock]::Create("[runspace]`$$auto = 1")) } | Should -Throw $auto
|
|
|
|
{ . ([ScriptBlock]::Create("[runspace]`$$auto = 1")) } | Should -Throw $auto
|
|
|
|
{ & ([ScriptBlock]::Create("[notexist]`$$auto = 1")) } | Should -Throw $auto
|
|
|
|
{ . ([ScriptBlock]::Create("[notexist]`$$auto = 1")) } | Should -Throw $auto
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($auto in $autos)
|
|
|
|
{
|
|
|
|
It "Assign auto w/o type constraint - $auto" {
|
2018-03-21 18:47:08 +01:00
|
|
|
& ([ScriptBlock]::Create("`$$auto = 1; `$$auto")) | Should -Be 1
|
|
|
|
. ([ScriptBlock]::Create("`$$auto = 1; `$$auto")) | Should -Be 1
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
It "Assign auto w/ correct type constraint" {
|
2018-03-21 18:47:08 +01:00
|
|
|
& { [object]$_ = 1; $_ } | Should -Be 1
|
|
|
|
& { [object[]]$args = 1; $args } | Should -Be 1
|
|
|
|
& { [object]$this = 1; $this } | Should -Be 1
|
|
|
|
& { [object]$input = 1; $input } | Should -Be 1
|
2017-09-30 01:28:15 +02:00
|
|
|
# Can't test PSCmdlet or PSBoundParameters, they use an internal type
|
2018-03-21 18:47:08 +01:00
|
|
|
& { [System.Management.Automation.InvocationInfo]$myInvocation = $myInvocation; $myInvocation.Line } | Should -Match Automation.InvocationInfo
|
|
|
|
& { [string]$PSScriptRoot = 'abc'; $PSScriptRoot } | Should -BeExactly 'abc'
|
|
|
|
& { [string]$PSCommandPath = 'abc'; $PSCommandPath } | Should -BeExactly 'abc'
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Describe "Assign readonly/constant variables" -Tags "CI" {
|
|
|
|
|
|
|
|
$testCase = @(
|
|
|
|
@{ sb_wo_conversion = { $? = 1 }; name = '$? = 1' }
|
|
|
|
@{ sb_wo_conversion = { $HOME = 1 }; name = '$HOME = 1' }
|
|
|
|
@{ sb_wo_conversion = { $PID = 1 }; name = '$PID = 1' }
|
|
|
|
)
|
|
|
|
|
|
|
|
It "Assign readonly/constant variables w/o type constraint - '<name>'" -TestCases $testCase {
|
|
|
|
param($sb_wo_conversion)
|
2018-03-21 18:47:08 +01:00
|
|
|
{ & $sb_wo_conversion } | Should -Throw -ErrorId "VariableNotWritable"
|
|
|
|
{ . $sb_wo_conversion } | Should -Throw -ErrorId "VariableNotWritable"
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
$testCase = @(
|
|
|
|
@{ sb_w_conversion = { [datetime]$? = 1 }; name = '[datetime]$? = 1' }
|
|
|
|
@{ sb_w_conversion = { [datetime]$HOME = 1 }; name = '[datetime]$HOME = 1' }
|
|
|
|
@{ sb_w_conversion = { [datetime]$PID = 1 }; name = '[datetime]$PID = 1' }
|
|
|
|
)
|
|
|
|
|
|
|
|
It "Assign readonly/constant variables w/ type constraint - '<name>'" -TestCases $testCase {
|
|
|
|
param($sb_w_conversion)
|
2018-03-21 18:47:08 +01:00
|
|
|
{ & $sb_w_conversion } | Should -Throw -ErrorId "VariableNotWritable"
|
|
|
|
{ . $sb_w_conversion } | Should -Throw -ErrorId "VariableNotWritable"
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Describe "Attribute error position" -Tags "CI" {
|
|
|
|
It "Ambiguous overloads" {
|
2018-05-17 23:42:04 +02:00
|
|
|
$e = {
|
2017-09-30 01:28:15 +02:00
|
|
|
& {
|
|
|
|
param(
|
|
|
|
[ValidateNotNull(1,2,3,4)]
|
|
|
|
$param
|
|
|
|
)
|
|
|
|
}
|
2018-05-17 23:42:04 +02:00
|
|
|
} | Should -Throw -PassThru -ErrorId 'MethodCountCouldNotFindBest'
|
|
|
|
$e.InvocationInfo.Line | Should -Match ValidateNotNull
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Describe "Multiple alias attributes" -Tags "CI" {
|
|
|
|
It "basic test" {
|
|
|
|
function foo {
|
|
|
|
param(
|
|
|
|
[alias('aa')]
|
|
|
|
[alias('bb')]
|
|
|
|
$cc
|
|
|
|
)
|
|
|
|
$cc
|
|
|
|
}
|
|
|
|
|
2018-03-21 18:47:08 +01:00
|
|
|
foo -aa 1 | Should -Be 1
|
|
|
|
foo -bb 2 | Should -Be 2
|
|
|
|
foo -cc 3 | Should -Be 3
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Describe "Members of System.Type" -Tags "CI" {
|
|
|
|
It "Members in public classes derived from System.Type should be found" {
|
|
|
|
class MyType : System.Collections.IEnumerable
|
|
|
|
{
|
|
|
|
[System.Collections.IEnumerator] GetEnumerator() { return $null }
|
|
|
|
}
|
|
|
|
|
2018-03-21 18:47:08 +01:00
|
|
|
[type] | Get-Member ImplementedInterfaces | Should -Be 'System.Collections.Generic.IEnumerable[type] ImplementedInterfaces {get;}'
|
|
|
|
[MyType].ImplementedInterfaces | Should -Be System.Collections.IEnumerable
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
}
|
2018-06-06 18:38:05 +02:00
|
|
|
|
|
|
|
Describe "Hash expression with if statement as value" -Tags "CI" {
|
|
|
|
BeforeAll {
|
|
|
|
# With no extra new lines after if-statement
|
|
|
|
$hash1 = @{
|
|
|
|
a = if (1) {'a'}
|
|
|
|
b = 'b'
|
|
|
|
c = if (0) {2} elseif (1) {'c'}
|
|
|
|
d = 'd'
|
|
|
|
e = if (0) {2} elseif (0) {2} else {'e'}
|
|
|
|
f = 'f'
|
|
|
|
g = if (0) {2} else {'g'}
|
|
|
|
h = 'h'
|
|
|
|
}
|
|
|
|
|
|
|
|
# With extra new lines after if-statement
|
|
|
|
$hash2 = @{
|
|
|
|
a = if (1) {'a'}
|
|
|
|
|
|
|
|
b = 'b'
|
|
|
|
c = if (0) {2} elseif (1) {'c'}
|
|
|
|
|
|
|
|
d = 'd'
|
|
|
|
e = if (0) {2} elseif (0) {2} else {'e'}
|
|
|
|
|
|
|
|
f = 'f'
|
|
|
|
g = if (0) {2} else {'g'}
|
|
|
|
|
|
|
|
h = 'h'
|
|
|
|
}
|
|
|
|
|
|
|
|
# With expanded if-statement
|
|
|
|
$hash3 = @{
|
|
|
|
a = if (1)
|
|
|
|
{
|
|
|
|
'a'
|
|
|
|
}
|
|
|
|
b = 'b'
|
|
|
|
c = if (0)
|
|
|
|
{
|
|
|
|
2
|
|
|
|
}
|
|
|
|
elseif (1)
|
|
|
|
{
|
|
|
|
'c'
|
|
|
|
}
|
|
|
|
d = 'd'
|
|
|
|
e = if (0)
|
|
|
|
{
|
|
|
|
2
|
|
|
|
}
|
|
|
|
elseif (0)
|
|
|
|
{
|
|
|
|
2
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
'e'
|
|
|
|
}
|
|
|
|
f = 'f'
|
|
|
|
g = if (0)
|
|
|
|
{
|
|
|
|
2
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
'g'
|
|
|
|
}
|
|
|
|
h = 'h'
|
|
|
|
}
|
|
|
|
|
|
|
|
$testCases = @(
|
|
|
|
@{ name = "No extra new lines"; hash = $hash1 }
|
|
|
|
@{ name = "With extra new lines"; hash = $hash2 }
|
|
|
|
@{ name = "With expanded if-statement"; hash = $hash3 }
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
It "Key-value pairs after an if-statement-value in a HashExpression should continue to be parsed - <name>" -TestCases $testCases {
|
|
|
|
param($hash)
|
|
|
|
|
|
|
|
$hash['a'] | Should -BeExactly 'a'
|
|
|
|
$hash['b'] | Should -BeExactly 'b'
|
|
|
|
$hash['c'] | Should -BeExactly 'c'
|
|
|
|
$hash['d'] | Should -BeExactly 'd'
|
|
|
|
$hash['e'] | Should -BeExactly 'e'
|
|
|
|
$hash['f'] | Should -BeExactly 'f'
|
|
|
|
$hash['g'] | Should -BeExactly 'g'
|
|
|
|
$hash['h'] | Should -BeExactly 'h'
|
|
|
|
}
|
|
|
|
}
|
2019-01-10 05:11:43 +01:00
|
|
|
|
|
|
|
Describe "Hashtable is case insensitive" -Tag CI {
|
|
|
|
It "When LANG is C.UTF-8" -Skip:($IsWindows) {
|
|
|
|
sh -c 'LANG=C.UTF-8 pwsh -NoProfile -Command ''$h=@{p=1};$h.P''' | Should -Be 1
|
|
|
|
}
|
|
|
|
}
|