2017-10-26 23:12:19 +02:00
|
|
|
# Copyright (c) Microsoft Corporation. All rights reserved.
|
2018-02-13 18:23:53 +01:00
|
|
|
# Licensed under the MIT License.
|
2017-09-30 01:28:15 +02:00
|
|
|
|
|
|
|
Describe 'Positive Parse Properties Tests' -Tags "CI" {
|
|
|
|
It 'PositiveParsePropertiesTest' {
|
|
|
|
# Just a bunch of random basic things here
|
|
|
|
# This test doesn't need to check anything, if there are
|
|
|
|
# any parse errors, the entire suite will fail because the
|
|
|
|
# script will fail to parse.
|
|
|
|
|
|
|
|
# No members
|
|
|
|
class C1 {}
|
|
|
|
|
|
|
|
# Simple field
|
|
|
|
class C2 { $x; }
|
|
|
|
|
|
|
|
# Simple typed field
|
|
|
|
class C3 { [int] $x; }
|
|
|
|
|
|
|
|
# Multiple fields, one line, last w/o semicolon
|
|
|
|
class C4 { $x; $y }
|
|
|
|
|
|
|
|
# Multiple fields, multiple lines
|
|
|
|
class C5
|
|
|
|
{
|
|
|
|
$x
|
|
|
|
$y
|
|
|
|
}
|
|
|
|
|
|
|
|
# Static field
|
|
|
|
class C6 { static $x; }
|
|
|
|
|
|
|
|
# Static field w/ type - order doesn't matter
|
|
|
|
class C7a { static [hashtable] $x; }
|
|
|
|
class C7b { [hashtable] static $x; }
|
|
|
|
|
|
|
|
# Field using type defined in this scope
|
|
|
|
class C8a { [C1] $c1; }
|
|
|
|
class C8b { [c1] $c1; }
|
|
|
|
|
|
|
|
# Field referring to self type
|
|
|
|
class C9 { [C9] $c9; }
|
|
|
|
|
|
|
|
# Hidden fields
|
|
|
|
class C10a { hidden $x }
|
|
|
|
class C10b { hidden [int] $x }
|
|
|
|
class C10c { hidden static $x; static hidden $y }
|
|
|
|
class C10d { hidden static [int] $x; static hidden [int] $y }
|
|
|
|
}
|
|
|
|
|
|
|
|
It 'Positive Parse Methods Tests' {
|
|
|
|
# Just a bunch of random basic things here
|
|
|
|
# This test doesn't need to check anything, if there are
|
|
|
|
# any parse errors, the entire suite will fail because the
|
|
|
|
# script will fail to parse.
|
|
|
|
|
|
|
|
# No members
|
|
|
|
class C1 {}
|
|
|
|
|
|
|
|
# Simple method
|
|
|
|
class C2 { f() {} }
|
|
|
|
|
|
|
|
# Simple method with return type
|
|
|
|
class C3 { [int] f() { return 1 } }
|
|
|
|
|
|
|
|
# Multiple methods, one line
|
|
|
|
class C4 { f() {} f1() {} }
|
|
|
|
|
|
|
|
# Multiple methods w/ overloads
|
|
|
|
class C5
|
|
|
|
{
|
|
|
|
f1() {}
|
|
|
|
f1($a) {}
|
|
|
|
}
|
|
|
|
|
|
|
|
# Static method
|
|
|
|
class C6 { static f() {} }
|
|
|
|
|
|
|
|
# Static method w/ return type
|
|
|
|
class C7 { static [hashtable] f1() { return @{} } }
|
|
|
|
|
|
|
|
# Method using return type defined in this scope
|
|
|
|
class C8a { [C1] f1() { return [C1]::new() } }
|
|
|
|
class C8b { [c1] f1() { return [c1]::new() } }
|
|
|
|
|
|
|
|
# Hidden methods
|
|
|
|
class C10a { hidden F() { } }
|
|
|
|
class C10b { hidden [void] F() { } }
|
|
|
|
class C10c { hidden static F() { } static hidden G() { } }
|
|
|
|
class C10d { hidden static [void] F() { } static hidden [void] G() { } }
|
|
|
|
|
|
|
|
# return analysis
|
|
|
|
class C11a { [int]foo() { try {throw "foo"} catch {throw $_} } }
|
|
|
|
class C11b { [int]foo() { try {throw "foo"} finally {}; try {} catch {} } }
|
|
|
|
class C11c { [int]foo() { try {throw "foo"} catch [ArgumentException] {throw $_} catch {throw $_} } }
|
|
|
|
class C11d { [int]foo() { try {if (1 -eq 2) { throw "1"} else {throw "2"}} finally {} } }
|
|
|
|
class C11e { [int]foo() { try {throw "foo"} catch [ArgumentException] {throw $_} catch {return 1} } }
|
|
|
|
class C11f { [int]foo() { try {} finally {throw "bar"} } }
|
|
|
|
class C11g { [int]foo() { do { return 1 } while ($false) } }
|
|
|
|
class C11h { [int]foo() { try {throw "foo"} finally {} } }
|
|
|
|
|
|
|
|
# local variables
|
|
|
|
class C12a { static f() { [bigint]$foo = 42 } }
|
|
|
|
class C12b { [void] f() { [bigint]$foo = 42 } }
|
|
|
|
class C12c { [void] f() { [System.Management.Automation.Host.Rectangle]$foo = [System.Management.Automation.Host.Rectangle]::new(0, 0, 0, 0) } }
|
|
|
|
}
|
|
|
|
|
|
|
|
context "Positive ParseMethods return type Test" {
|
|
|
|
# Method with return type of self
|
|
|
|
class C9 { [C9] f() { return [C9]::new() } }
|
|
|
|
$c9 = [C9]::new().f()
|
2018-03-21 18:47:08 +01:00
|
|
|
It "Expected a C9 returned" { $c9.GetType().Name | Should -Be C9 }
|
2017-09-30 01:28:15 +02:00
|
|
|
class C9a { [C9a[]] f() { return [C9a]::new() } }
|
|
|
|
$c9a = [C9a]::new().f()
|
2018-03-21 18:47:08 +01:00
|
|
|
It "Expected a C9a[] returned" { $c9a.GetType().Name | Should -Be C9a[] }
|
2017-09-30 01:28:15 +02:00
|
|
|
class C9b { [System.Collections.Generic.List[C9b]] f() { return [C9b]::new() } }
|
|
|
|
$c9b = [C9b]::new().f()
|
2018-03-21 18:47:08 +01:00
|
|
|
It "Expected a System.Collections.Generic.List[C9b] returned" { $c9b -is [System.Collections.Generic.List[C9b]] | Should -BeTrue }
|
2018-07-02 21:58:42 +02:00
|
|
|
It 'Methods returning object should return $null if no output was produced' {
|
|
|
|
class Foo {
|
|
|
|
[object] Bar1() { return & {} }
|
|
|
|
static [object] Bar2() { return & {} }
|
|
|
|
}
|
|
|
|
# Test instance method
|
|
|
|
[Foo]::new().Bar1() | Should -BeNullOrEmpty
|
|
|
|
# Test static method
|
|
|
|
[foo]::Bar2() | Should -BeNullOrEmpty
|
|
|
|
}
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
It 'Positive ParseProperty Attributes Test' {
|
|
|
|
class C1a { [ValidateNotNull()][int]$x; }
|
|
|
|
class C1b
|
|
|
|
{
|
|
|
|
[ValidateNotNull()]
|
|
|
|
[int]
|
|
|
|
$x
|
|
|
|
}
|
|
|
|
class C1c
|
|
|
|
{
|
|
|
|
[ValidateNotNull()]
|
|
|
|
[int]$x
|
|
|
|
}
|
|
|
|
class C1d
|
|
|
|
{
|
|
|
|
[ValidateNotNull()][int]
|
|
|
|
$x
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
It 'PositiveParseMethodAttributesTest' {
|
|
|
|
class C1a { [Obsolete()][int] f() { return 0 } }
|
|
|
|
class C1b
|
|
|
|
{
|
|
|
|
[Obsolete()]
|
|
|
|
[int]
|
|
|
|
f() { return 1 }
|
|
|
|
}
|
|
|
|
class C1c
|
|
|
|
{
|
|
|
|
[Obsolete("Message")]
|
|
|
|
[int] f() { return 0 }
|
|
|
|
}
|
|
|
|
class C1d
|
|
|
|
{
|
|
|
|
[Obsolete()][int]
|
|
|
|
f(){ return -1 }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
It 'Class Method Reference ConstantVars' {
|
|
|
|
class C1
|
|
|
|
{
|
|
|
|
[bool] f1() { return $true }
|
|
|
|
[bool] f2() { return $false }
|
|
|
|
[object] f3() { return $null }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
It 'Positive Parsing Of DscResource' {
|
|
|
|
[DscResource()]
|
|
|
|
class C1
|
|
|
|
{
|
|
|
|
[DscProperty(Key)][string]$Key
|
|
|
|
[bool] Test() { return $false }
|
|
|
|
[C1] Get() { return $this }
|
|
|
|
Set() {}
|
|
|
|
}
|
|
|
|
|
|
|
|
[DscResource()]
|
|
|
|
class C2
|
|
|
|
{
|
|
|
|
[DscProperty(Key)][int]$Key = 1
|
|
|
|
[bool] Test() { return $false }
|
|
|
|
[C2] Get() { return $this }
|
|
|
|
Set() {}
|
|
|
|
}
|
|
|
|
|
|
|
|
[DscResource()]
|
|
|
|
class C4
|
|
|
|
{
|
|
|
|
[DscProperty(Key)][byte]$Key=1
|
|
|
|
C4() { }
|
|
|
|
C4($a) { }
|
|
|
|
[bool] Test() { return $false }
|
|
|
|
[C4] Get() { return $this }
|
|
|
|
Set() {}
|
|
|
|
}
|
|
|
|
|
|
|
|
[DscResource()]
|
|
|
|
class C5
|
|
|
|
{
|
|
|
|
[DscProperty(Key)][int]$Key = 1
|
|
|
|
C5() { }
|
|
|
|
static C5() { }
|
|
|
|
C5($a) { }
|
|
|
|
[bool] Test() { return $false }
|
|
|
|
[C5] Get() { return $this }
|
|
|
|
Set() {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
It 'allows some useful implicit variables inside methods' {
|
|
|
|
class C {
|
|
|
|
[void] m()
|
|
|
|
{
|
|
|
|
$LASTEXITCODE
|
2020-01-13 20:15:45 +01:00
|
|
|
$LASTEXITCODE
|
2018-03-21 18:47:08 +01:00
|
|
|
'111' -Match '1'
|
2017-09-30 01:28:15 +02:00
|
|
|
$Matches
|
2020-01-13 20:15:17 +01:00
|
|
|
$Matches
|
2020-01-13 20:19:39 +01:00
|
|
|
$error[0]
|
2017-09-30 01:28:15 +02:00
|
|
|
$error
|
2020-01-13 20:24:10 +01:00
|
|
|
$PWD
|
2017-09-30 01:28:15 +02:00
|
|
|
foreach ($i in 1..10) {$foreach}
|
|
|
|
switch ($i)
|
|
|
|
{
|
|
|
|
'1' {
|
|
|
|
$switch
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
It 'allowes [ordered] attribute inside methods' {
|
|
|
|
class A
|
|
|
|
{
|
|
|
|
$h
|
|
|
|
A()
|
|
|
|
{
|
|
|
|
$this.h = [ordered] @{}
|
|
|
|
}
|
|
|
|
}
|
2018-03-21 18:47:08 +01:00
|
|
|
[A]::new().h.GetType().Name | Should -BeExactly 'OrderedDictionary'
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Describe 'Negative Parsing Tests' -Tags "CI" {
|
|
|
|
ShouldBeParseError 'class' MissingNameAfterKeyword 5
|
|
|
|
ShouldBeParseError 'class foo' MissingTypeBody 9
|
|
|
|
ShouldBeParseError 'class foo {' MissingEndCurlyBrace 11
|
|
|
|
ShouldBeParseError 'class foo { [int] }' IncompleteMemberDefinition 17
|
|
|
|
ShouldBeParseError 'class foo { $private: }' InvalidVariableReference 12
|
|
|
|
ShouldBeParseError 'class foo { [int]$global: }' InvalidVariableReference 17
|
|
|
|
ShouldBeParseError 'class foo { [Attr()] }' IncompleteMemberDefinition 20
|
|
|
|
ShouldBeParseError 'class foo {} class foo {}' MemberAlreadyDefined 13
|
|
|
|
ShouldBeParseError 'class foo { $x; $x; }' MemberAlreadyDefined 16 -SkipAndCheckRuntimeError
|
|
|
|
ShouldBeParseError 'class foo { [int][string]$x; }' TooManyTypes 17
|
|
|
|
ShouldBeParseError 'class foo { static static $x; }' DuplicateQualifier 19
|
|
|
|
ShouldBeParseError 'class foo { [zz]$x; }' TypeNotFound 13
|
|
|
|
ShouldBeParseError 'class foo { [zz]f() { return 0 } }' TypeNotFound 13
|
|
|
|
ShouldBeParseError 'class foo { f([zz]$x) {} }' TypeNotFound 15
|
|
|
|
|
|
|
|
ShouldBeParseError 'class C {} class C {}' MemberAlreadyDefined 11
|
|
|
|
ShouldBeParseError 'class C { f(){} f(){} }' MemberAlreadyDefined 16 -SkipAndCheckRuntimeError
|
|
|
|
ShouldBeParseError 'class C { F(){} F($o){} [int] F($o) {return 1} }' MemberAlreadyDefined 24 -SkipAndCheckRuntimeError
|
|
|
|
ShouldBeParseError 'class C { f(){} f($a){} f(){} }' MemberAlreadyDefined 24 -SkipAndCheckRuntimeError
|
|
|
|
ShouldBeParseError 'class C { f([int]$a){} f([int]$b){} }' MemberAlreadyDefined 23 -SkipAndCheckRuntimeError
|
|
|
|
ShouldBeParseError 'class C { $x; [int]$x; }' MemberAlreadyDefined 14 -SkipAndCheckRuntimeError
|
|
|
|
ShouldBeParseError 'class C { static C($x) {} }' StaticConstructorCantHaveParameters 19 -SkipAndCheckRuntimeError
|
|
|
|
ShouldBeParseError 'class C { static C([int]$x = 100) {} }' StaticConstructorCantHaveParameters 19 -SkipAndCheckRuntimeError
|
|
|
|
|
|
|
|
ShouldBeParseError 'class C {f(){ return 1} }' VoidMethodHasReturn 14
|
|
|
|
ShouldBeParseError 'class C {[int] f(){ return } }' NonVoidMethodMissingReturnValue 20
|
|
|
|
ShouldBeParseError 'class C {[int] f(){} }' MethodHasCodePathNotReturn 15
|
|
|
|
ShouldBeParseError 'class C {f(){ $x=1; if($x -lt 2){ return } elseif($x -gt 0 ) {return 1} else{return 2} return 3 } }' @("VoidMethodHasReturn", "VoidMethodHasReturn", "VoidMethodHasReturn") @(62,77,87)
|
|
|
|
|
|
|
|
ShouldBeParseError 'class foo { [int] bar() { $y = $z; return $y} }' VariableNotLocal 31
|
|
|
|
ShouldBeParseError 'class foo { bar() { foreach ($zz in $yy) { } } }' VariableNotLocal 36
|
|
|
|
ShouldBeParseError 'class foo { bar() { foreach ($zz in $global:yy) { $abc = $zzzzz } } }' VariableNotLocal 57
|
|
|
|
ShouldBeParseError 'class foo { bar() { try { $zz = 42 } finally { } $zz } }' VariableNotLocal 49
|
|
|
|
ShouldBeParseError 'class foo { bar() { try { $zz = 42 } catch { } $zz } }' VariableNotLocal 47
|
|
|
|
ShouldBeParseError 'class foo { bar() { switch (@()) { default { $aa = 42 } } $aa } }' VariableNotLocal 58
|
|
|
|
ShouldBeParseError 'class C { $x; static bar() { $this.x = 1 } }' NonStaticMemberAccessInStaticMember 29
|
|
|
|
ShouldBeParseError 'class C { $x; static $y = $this.x }' NonStaticMemberAccessInStaticMember 26
|
|
|
|
|
|
|
|
ShouldBeParseError 'class C { [void]foo() { try { throw "foo"} finally { return } } }' ControlLeavingFinally 53
|
|
|
|
ShouldBeParseError 'class C { [int]foo() { return; return 1 } }' NonVoidMethodMissingReturnValue 23
|
|
|
|
ShouldBeParseError 'class C { [int]foo() { try { throw "foo"} catch { } } }' MethodHasCodePathNotReturn 15
|
|
|
|
ShouldBeParseError 'class C { [int]foo() { try { throw "foo"} catch [ArgumentException] {} catch {throw $_} } }' MethodHasCodePathNotReturn 15
|
|
|
|
ShouldBeParseError 'class C { [int]foo() { try { throw "foo"} catch [ArgumentException] {return 1} catch {} } }' MethodHasCodePathNotReturn 15
|
|
|
|
ShouldBeParseError 'class C { [int]foo() { while ($false) { return 1 } } }' MethodHasCodePathNotReturn 15
|
2019-01-04 10:31:24 +01:00
|
|
|
ShouldBeParseError 'class C { [int]foo() { try { New-Item -ItemType Directory foo } finally { rm -rec foo } } }' MethodHasCodePathNotReturn 15
|
|
|
|
ShouldBeParseError 'class C { [int]foo() { try { New-Item -ItemType Directory foo; return 1 } catch { } } }' MethodHasCodePathNotReturn 15
|
2017-09-30 01:28:15 +02:00
|
|
|
ShouldBeParseError 'class C { [bool] Test() { if ($false) { return $true; } } }' MethodHasCodePathNotReturn 17
|
|
|
|
|
|
|
|
ShouldBeParseError 'class C { [int]$i; [void] foo() {$i = 10} }' MissingThis 33
|
|
|
|
ShouldBeParseError 'class C { static [int]$i; [void] foo() {$i = 10} }' MissingTypeInStaticPropertyAssignment 40
|
|
|
|
|
|
|
|
ShouldBeParseError 'class C : B' MissingTypeBody 11
|
|
|
|
|
|
|
|
ShouldBeParseError 'Class foo { q(){} w(){}' MissingEndCurlyBrace 11
|
|
|
|
}
|
|
|
|
|
|
|
|
Describe 'Negative methods Tests' -Tags "CI" {
|
|
|
|
ShouldBeParseError 'class foo { f() { param($x) } }' ParamBlockNotAllowedInMethod 18
|
|
|
|
ShouldBeParseError 'class foo { f() { dynamicparam {} } }' NamedBlockNotAllowedInMethod 18
|
|
|
|
ShouldBeParseError 'class foo { f() { begin {} } }' NamedBlockNotAllowedInMethod 18
|
|
|
|
ShouldBeParseError 'class foo { f() { process {} } }' NamedBlockNotAllowedInMethod 18
|
|
|
|
ShouldBeParseError 'class foo { f() { end {} } }' NamedBlockNotAllowedInMethod 18
|
|
|
|
ShouldBeParseError 'class foo { f([Parameter()]$a) {} }' AttributeNotAllowedOnDeclaration 14
|
|
|
|
ShouldBeParseError 'class foo { [int] foo() { return 1 }}' ConstructorCantHaveReturnType 12
|
|
|
|
ShouldBeParseError 'class foo { [void] bar($a, [string][int]$b, $c) {} }' MultipleTypeConstraintsOnMethodParam 35
|
|
|
|
}
|
|
|
|
|
|
|
|
Describe 'Negative Assignment Tests' -Tags "CI" {
|
|
|
|
ShouldBeParseError 'class foo { [string ]$path; f() { $path="" } }' MissingThis 34
|
|
|
|
ShouldBeParseError 'class foo { [string ]$path; f() { [string] $path="" } }' MissingThis 43
|
|
|
|
ShouldBeParseError 'class foo { [string ]$path; f() { [int] [string] $path="" } }' MissingThis 49
|
|
|
|
}
|
|
|
|
|
|
|
|
Describe 'Negative Assignment Tests' -Tags "CI" {
|
|
|
|
ShouldBeParseError '[DscResource()]class C { [bool] Test() { return $false } [C] Get() { return $this } Set() {} }' DscResourceMissingKeyProperty 0
|
|
|
|
|
|
|
|
# Test method
|
|
|
|
ShouldBeParseError '[DscResource()]class C { [DscProperty(Key)][string]$Key; [C] Get() { return $this } Set() {} }' DscResourceMissingTestMethod 0
|
|
|
|
ShouldBeParseError '[DscResource()]class C { [DscProperty(Key)][string]$Key; [C] Get() { return $this } Set() {} Test() { } }' DscResourceMissingTestMethod 0
|
|
|
|
ShouldBeParseError '[DscResource()]class C { [DscProperty(Key)][string]$Key; [C] Get() { return $this } Set() {} [int] Test() { return 1 } }' DscResourceMissingTestMethod 0
|
|
|
|
ShouldBeParseError '[DscResource()]class C { [DscProperty(Key)][string]$Key; [C] Get() { return $this } Set() {} [bool] Test($a) { return $false } }' DscResourceMissingTestMethod 0
|
|
|
|
|
|
|
|
# Get method
|
|
|
|
ShouldBeParseError '[DscResource()]class C { [DscProperty(Key)][string]$Key; [bool] Test() { return $false } Set() {} }' DscResourceMissingGetMethod 0
|
|
|
|
ShouldBeParseError '[DscResource()]class C { [DscProperty(Key)][string]$Key; [bool] Test() { return $false } Set() {} Get() { } }' DscResourceInvalidGetMethod 98
|
|
|
|
ShouldBeParseError '[DscResource()]class C { [DscProperty(Key)][string]$Key; [bool] Test() { return $false } Set() {} [int] Get() { return 1 } }' DscResourceInvalidGetMethod 98
|
|
|
|
ShouldBeParseError '[DscResource()]class C { [DscProperty(Key)][string]$Key; [bool] Test() { return $false } Set() {} [C] Get($a) { return $this } }' DscResourceMissingGetMethod 0
|
|
|
|
|
|
|
|
# Set method
|
|
|
|
ShouldBeParseError '[DscResource()]class C { [DscProperty(Key)][string]$Key; [bool] Test() { return $false } [C] Get() { return $this } }' DscResourceMissingSetMethod 0
|
|
|
|
ShouldBeParseError '[DscResource()]class C { [DscProperty(Key)][string]$Key; [bool] Test() { return $false } [C] Get() { return $this } [int] Set() { return 1 } }' DscResourceMissingSetMethod 0
|
|
|
|
ShouldBeParseError '[DscResource()]class C { [DscProperty(Key)][string]$Key; [bool] Test() { return $false } [C] Get() { return $this } Set($a) { } }' DscResourceMissingSetMethod 0
|
|
|
|
|
|
|
|
# Default ctor
|
|
|
|
ShouldBeParseError '[DscResource()]class C { [DscProperty(Key)][string]$Key; [bool] Test() { return $false } [C] Get() { return $this } Set() {} C($a) { } }' DscResourceMissingDefaultConstructor 0
|
|
|
|
}
|
|
|
|
|
|
|
|
Describe 'Negative DscResources Tests' -Tags "CI" {
|
|
|
|
# Usage errors
|
|
|
|
ShouldBeParseError '[Flags()]class C{}' AttributeNotAllowedOnDeclaration 0
|
|
|
|
ShouldBeParseError 'class C { [Flags()]$field; }' AttributeNotAllowedOnDeclaration 10
|
|
|
|
ShouldBeParseError 'class C { [Flags()]foo(){} }' AttributeNotAllowedOnDeclaration 10
|
|
|
|
|
|
|
|
# Errors related to construction of the attribute
|
|
|
|
ShouldBeParseError '[UnknownAttr()]class C{}' CustomAttributeTypeNotFound 1
|
|
|
|
ShouldBeParseError '[System.Management.Automation.Cmdlet()]class C{}' MethodCountCouldNotFindBest 0 -SkipAndCheckRuntimeError
|
|
|
|
ShouldBeParseError '[System.Management.Automation.Cmdlet("zz")]class C{}' MethodCountCouldNotFindBest 0 -SkipAndCheckRuntimeError
|
|
|
|
ShouldBeParseError '[System.Management.Automation.Cmdlet("Get", "Thing", Prop=1)]class C{}' PropertyNotFoundForAttribute 53
|
|
|
|
ShouldBeParseError '[System.Management.Automation.Cmdlet("Get", "Thing", ConfirmImpact="foo")]class C{}' CannotConvertValue 67 -SkipAndCheckRuntimeError
|
|
|
|
ShouldBeParseError '[System.Management.Automation.Cmdlet("Get", "Thing", NounName="foo")]class C{}' ReadOnlyProperty 53
|
|
|
|
ShouldBeParseError '[System.Management.Automation.Cmdlet("Get", "Thing", ConfirmImpact=$zed)]class C{}' ParameterAttributeArgumentNeedsToBeConstant 67
|
|
|
|
ShouldBeParseError 'class C{ [ValidateScript({})]$p; }' ParameterAttributeArgumentNeedsToBeConstant 25
|
|
|
|
}
|
|
|
|
|
|
|
|
Describe 'Negative ClassAttributes Tests' -Tags "CI" {
|
|
|
|
[System.Management.Automation.Cmdlet("Get", "Thing")]class C{}
|
|
|
|
$t = [C].GetCustomAttributes($false)
|
|
|
|
|
2018-03-21 18:47:08 +01:00
|
|
|
It "Should have one attribute (class C)" {$t.Count | Should -Be 1}
|
|
|
|
It "Should have instance of CmdletAttribute (class C)" {$t[0] | Should -BeOfType System.Management.Automation.CmdletAttribute }
|
2017-09-30 01:28:15 +02:00
|
|
|
|
|
|
|
[System.Management.Automation.CmdletAttribute]$c = $t[0]
|
2018-03-21 18:47:08 +01:00
|
|
|
It "Verb should be Get (class C)" {$c.VerbName | Should -BeExactly 'Get'}
|
|
|
|
It "Noun should be Thing (class C)" {$c.NounName | Should -BeExactly 'Thing'}
|
2017-09-30 01:28:15 +02:00
|
|
|
|
|
|
|
[System.Management.Automation.Cmdlet("Get", "Thing", SupportsShouldProcess = $true, SupportsPaging = $true)]class C2{}
|
|
|
|
$t = [C2].GetCustomAttributes($false)
|
Update pester syntax to v4 (#11544)
* Capitalize 'Should' command and fix whitespace
```powershell
$_ -ireplace '\s?\|\s?should\b',' | Should'
```
* Capitalise and apply hyphen to 'Not' parameter
```powershell
$_ -ireplace '(\| Should) not\b','$1 -Not'
```
* Capitalise and apply hyphen to 'Be' parameter
```powershell
$_ -ireplace '(\| Should(?: -Not)?) -?be\b','$1 -Be'
```
* Capitalise and apply hyphen to 'BeExactly' parameter
$_ -ireplace '(\| Should(?: -Not)?) -?beexactly\b','$1 -BeExactly'
* Capitalise and apply hyphen to 'BeGreaterThan' parameter
```powershell
$_ -ireplace '(\| Should(?: -Not)?) -?begreaterthan\b','$1 -BeGreaterThan'
```
* Use 'BeTrue' parameter
```powershell
$_ -ireplace '(\| Should(?: -Not)?) -Be\s\$?true\b','$1 -BeTrue'
```
* Use 'BeFalse' parameter
```powershell
$_ -ireplace '(\| Should(?: -Not)?) -Be\s\$?false\b','$1 -BeFalse'
```
* Capitalise and apply hyphen to 'Match' parameter
```powershell
$_ -ireplace '(\| Should(?: -Not)?)\s-?match\b','$1 -Match'
```
* Capitalise and apply hyphen to 'Throw' parameter
```powershell
$_ -ireplace '(\| Should(?: -Not)?)\s-?throw\b','$1 -Throw'
```
* Capitalise and apply hyphen to 'BeNullOrEmpty' parameter
```powershell
$_ -ireplace '(\| Should(?: -Not)?)\s-?benullorempty\b','$1 -BeNullOrEmpty'
```
* Capitalise 'Because' parameter
```powershell
$_ -ireplace '\s-because\b',' -Because'
```
* Fix 'BeNullOrEmpty'
2020-01-11 16:41:59 +01:00
|
|
|
It "Should have one attribute (class C2)" { $t.Count | Should -Be 1 }
|
2018-03-21 18:47:08 +01:00
|
|
|
It "Should have instance of CmdletAttribute (class C2)" { $t[0] | Should -BeOfType System.Management.Automation.CmdletAttribute }
|
2017-09-30 01:28:15 +02:00
|
|
|
[System.Management.Automation.CmdletAttribute]$c = $t[0]
|
2018-03-21 18:47:08 +01:00
|
|
|
It "Verb should be Get (class C2)" {$c.VerbName | Should -BeExactly 'Get'}
|
|
|
|
It "Noun should be Thing (class C2)" {$c.NounName | Should -BeExactly 'Thing'}
|
2017-09-30 01:28:15 +02:00
|
|
|
|
2018-12-20 19:15:42 +01:00
|
|
|
It "SupportsShouldProcess should be $true" { $c.SupportsShouldProcess | Should -BeTrue }
|
2018-03-21 18:47:08 +01:00
|
|
|
It "SupportsPaging should be `$true" { $c.SupportsPaging | Should -BeTrue }
|
2017-09-30 01:28:15 +02:00
|
|
|
Context "Support ConfirmImpact as an attribute" {
|
2018-03-21 18:47:08 +01:00
|
|
|
It "ConfirmImpact should be high" {
|
2018-12-20 19:15:42 +01:00
|
|
|
[System.Management.Automation.Cmdlet("Get", "Thing", SupportsShouldProcess = $true, ConfirmImpact = 'High', SupportsPaging = $true)]class C3{}
|
2017-09-30 01:28:15 +02:00
|
|
|
$t = [C3].GetCustomAttributes($false)
|
2018-03-21 18:47:08 +01:00
|
|
|
$t.Count | Should -Be 1
|
|
|
|
$t[0] | Should -BeOfType System.Management.Automation.CmdletAttribute
|
2017-09-30 01:28:15 +02:00
|
|
|
[System.Management.Automation.CmdletAttribute]$c = $t[0]
|
2018-03-21 18:47:08 +01:00
|
|
|
$c.ConfirmImpact | Should -BeExactly 'High'
|
2017-09-30 01:28:15 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Describe 'Property Attributes Test' -Tags "CI" {
|
|
|
|
class C { [ValidateSet('a', 'b')]$p; }
|
|
|
|
|
|
|
|
$t = [C].GetProperty('p').GetCustomAttributes($false)
|
2018-03-21 18:47:08 +01:00
|
|
|
It "Should have one attribute" { $t.Count | Should -Be 1 }
|
2017-09-30 01:28:15 +02:00
|
|
|
[ValidateSet]$v = $t[0]
|
2018-03-21 18:47:08 +01:00
|
|
|
It "Should have 2 valid values" { $v.ValidValues.Count | Should -Be 2 }
|
|
|
|
It "first value should be a" { $v.ValidValues[0] | Should -Be 'a' }
|
|
|
|
It "second value should be b" { $v.ValidValues[1] | Should -Be 'b' }
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
|
2019-06-25 20:04:00 +02:00
|
|
|
Describe 'Testing Method Names can be Keywords' -Tags "CI" {
|
|
|
|
BeforeAll {
|
|
|
|
[powershell] $script:PowerShell = [powershell]::Create()
|
|
|
|
|
|
|
|
function Invoke-PowerShell {
|
|
|
|
[CmdletBinding()]
|
|
|
|
param([string] $Script)
|
|
|
|
|
|
|
|
try {
|
|
|
|
$script:PowerShell.AddScript($Script).Invoke()
|
|
|
|
}
|
|
|
|
finally {
|
|
|
|
$script:PowerShell.Commands.Clear()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function Reset-PowerShell {
|
|
|
|
if ($script:PowerShell) {
|
|
|
|
$script:PowerShell.Dispose()
|
|
|
|
}
|
|
|
|
|
|
|
|
$script:PowerShell = [powershell]::Create()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
AfterEach {
|
|
|
|
Reset-PowerShell
|
|
|
|
}
|
|
|
|
|
|
|
|
It 'Permits class methods to be named after keywords' {
|
|
|
|
$TestScript = @'
|
|
|
|
class TestMethodNames : IDisposable {
|
|
|
|
[string] Begin() { return "Begin" }
|
|
|
|
|
|
|
|
[string] Process() { return "Process" }
|
|
|
|
|
|
|
|
[string] End() { return "End" }
|
|
|
|
|
|
|
|
hidden $Data = "Secrets"
|
|
|
|
|
|
|
|
Dispose() {
|
|
|
|
$this.Data = [string]::Empty
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$Object = [TestMethodNames]::new()
|
|
|
|
[PSCustomObject]@{
|
|
|
|
BeginTest = $Object.Begin()
|
|
|
|
ProcessTest = $Object.Process()
|
|
|
|
EndTest = $Object.End()
|
|
|
|
CheckData1 = $Object.Data
|
|
|
|
CheckData2 = $( $Object.Dispose(); $Object.Data )
|
|
|
|
}
|
|
|
|
'@
|
|
|
|
$Results = Invoke-PowerShell -Script $TestScript
|
|
|
|
|
|
|
|
$Results.BeginTest | Should -BeExactly 'Begin'
|
|
|
|
$Results.ProcessTest | Should -BeExactly 'Process'
|
|
|
|
$Results.EndTest | Should -BeExactly 'End'
|
|
|
|
$Results.CheckData1 | Should -BeExactly 'Secrets'
|
|
|
|
$Results.CheckData2 | Should -BeNullOrEmpty
|
|
|
|
}
|
|
|
|
|
|
|
|
It 'Permits class methods to be named after DynamicKeywords' {
|
|
|
|
$DefineKeyword = @'
|
|
|
|
function GetData {
|
|
|
|
param(
|
|
|
|
$KeywordData,
|
|
|
|
$Name,
|
|
|
|
$Value,
|
|
|
|
$SourceMetadata
|
|
|
|
)
|
|
|
|
end {
|
|
|
|
return $PSBoundParameters
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$keyword = [System.Management.Automation.Language.DynamicKeyword]::new()
|
|
|
|
$keyword.NameMode = [System.Management.Automation.Language.DynamicKeywordNameMode]::SimpleNameRequired
|
|
|
|
$keyword.Keyword = 'GetData'
|
|
|
|
$keyword.BodyMode = [System.Management.Automation.Language.DynamicKeywordBodyMode]::Hashtable
|
|
|
|
|
|
|
|
$property = [System.Management.Automation.Language.DynamicKeywordProperty]::new()
|
|
|
|
$property.Name = 'Hey'
|
|
|
|
$property.TypeConstraint = 'int'
|
|
|
|
$keyword.Properties.Add('Hey', $property)
|
|
|
|
|
|
|
|
[System.Management.Automation.Language.DynamicKeyword]::AddKeyword($keyword)
|
|
|
|
'@
|
|
|
|
$DefineClass = @'
|
|
|
|
class Test {
|
|
|
|
hidden $Data = 'TOP SECRET'
|
|
|
|
|
|
|
|
[string] GetData() {
|
|
|
|
return $this.Data
|
|
|
|
}
|
|
|
|
}
|
|
|
|
'@
|
|
|
|
$TestScript = @'
|
|
|
|
$Object = [Test]::new()
|
|
|
|
$Object.GetData()
|
|
|
|
'@
|
|
|
|
Invoke-PowerShell -Script $DefineKeyword
|
|
|
|
Invoke-PowerShell -Script $DefineClass
|
|
|
|
|
|
|
|
Invoke-PowerShell -Script $TestScript | Should -BeExactly 'TOP SECRET'
|
|
|
|
}
|
|
|
|
|
|
|
|
AfterAll {
|
|
|
|
# Ensure we don't leave any dynamic keywords in the session.
|
|
|
|
[System.Management.Automation.Language.DynamicKeyword]::Reset()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-30 01:28:15 +02:00
|
|
|
Describe 'Method Attributes Test' -Tags "CI" {
|
|
|
|
class C { [Obsolete("aaa")][int]f() { return 1 } }
|
|
|
|
|
|
|
|
$t = [C].GetMethod('f').GetCustomAttributes($false)
|
2018-03-21 18:47:08 +01:00
|
|
|
It "Should have one attribute" {$t.Count | Should -Be 1 }
|
|
|
|
It "Attribute type should be ObsoleteAttribute" { $t[0].GetType().FullName | Should -Be System.ObsoleteAttribute }
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Describe 'Positive SelfClass Type As Parameter Test' -Tags "CI" {
|
|
|
|
class Point
|
|
|
|
{
|
|
|
|
Point($x, $y) { $this.x = $x; $this.y = $y }
|
|
|
|
Point() {}
|
|
|
|
|
|
|
|
[int] $x = 0
|
|
|
|
[int] $y = 0
|
|
|
|
Add([Point] $val) { $this.x += $val.x; $this.y += $val.y; }
|
|
|
|
|
|
|
|
Print() { Write-Host "[`$x=$($this.x) `$y=$($this.y)]" }
|
|
|
|
Set($x, $y) { $this.x = $x; $this.y = $y }
|
|
|
|
}
|
2018-03-07 19:25:49 +01:00
|
|
|
It "[Point]::Add works construction via ::new" {
|
2017-09-30 01:28:15 +02:00
|
|
|
$point = [Point]::new(100,200)
|
|
|
|
$point2 = [Point]::new(1,2)
|
|
|
|
$point.Add($point2)
|
|
|
|
|
2018-03-21 18:47:08 +01:00
|
|
|
$point.x | Should -Be 101
|
|
|
|
$point.y | Should -Be 202
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
|
2018-03-07 19:25:49 +01:00
|
|
|
It "[Point]::Add works construction via new-object" {
|
2017-09-30 01:28:15 +02:00
|
|
|
$point = New-Object Point 100,200
|
|
|
|
$point2 = New-Object Point 1,2
|
|
|
|
$point.Add($point2)
|
|
|
|
|
2018-03-21 18:47:08 +01:00
|
|
|
$point.x | Should -Be 101
|
|
|
|
$point.y | Should -Be 202
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Describe 'PositiveReturnSelfClassTypeFromMemberFunction Test' -Tags "CI" {
|
|
|
|
class ReturnObjectFromMemberFunctionTest
|
|
|
|
{
|
|
|
|
[ReturnObjectFromMemberFunctionTest] CreateInstance()
|
|
|
|
{
|
|
|
|
return [ReturnObjectFromMemberFunctionTest]::new()
|
|
|
|
}
|
|
|
|
[string] SayHello()
|
|
|
|
{
|
|
|
|
return "Hello1"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$f = [ReturnObjectFromMemberFunctionTest]::new()
|
|
|
|
$z = $f.CreateInstance() # Line 13
|
2018-03-21 18:47:08 +01:00
|
|
|
It "CreateInstance works" { $z.SayHello() | Should -BeExactly 'Hello1' }
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Describe 'TestMultipleArguments Test' -Tags "CI" {
|
|
|
|
if ( $IsCoreCLR ) { $maxCount = 14 } else { $maxCount = 16 }
|
|
|
|
for ($i = 0; $i -lt $maxCount; $i++)
|
|
|
|
{
|
|
|
|
$properties = $(for ($j = 0; $j -le $i; $j++) {
|
|
|
|
" [int]`$Prop$j"
|
|
|
|
}) -join "`n"
|
|
|
|
|
|
|
|
$methodParameters = $(for ($j = 0; $j -le $i; $j++) {
|
|
|
|
"[int]`$arg$j"
|
|
|
|
}) -join ", "
|
|
|
|
|
|
|
|
$ctorAssignments = $(for ($j = 0; $j -le $i; $j++) {
|
|
|
|
" `$this.Prop$j = `$arg$j"
|
|
|
|
}) -join "`n"
|
|
|
|
|
|
|
|
$methodReturnValue = $(for ($j = 0; $j -le $i; $j++) {
|
|
|
|
"`$arg$j"
|
|
|
|
}) -join " + "
|
|
|
|
|
|
|
|
$methodArguments = $(for ($j = 0; $j -le $i; $j++) {
|
|
|
|
$j
|
|
|
|
}) -join ", "
|
|
|
|
|
|
|
|
$addUpProperties = $(for ($j = 0; $j -le $i; $j++) {
|
|
|
|
"`$inst.`Prop$j"
|
|
|
|
}) -join " + "
|
|
|
|
|
|
|
|
$expectedTotal = (0..$i | Measure-Object -Sum).Sum
|
|
|
|
|
|
|
|
$class = @"
|
|
|
|
class Foo
|
|
|
|
{
|
|
|
|
$properties
|
|
|
|
|
|
|
|
Foo($methodParameters)
|
|
|
|
{
|
|
|
|
$ctorAssignments
|
|
|
|
}
|
|
|
|
|
|
|
|
[int] DoSomething($methodParameters)
|
|
|
|
{
|
|
|
|
return $methodReturnValue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
`$inst = [Foo]::new($methodArguments)
|
|
|
|
`$sum = $addUpProperties
|
2018-03-21 18:47:08 +01:00
|
|
|
It "ExpectedTotal: Sum should be $expectedTotal" { `$sum | Should -Be $expectedTotal }
|
|
|
|
It "ExpectedTotal: Invocation should return $expectedTotal" { `$inst.DoSomething($methodArguments) | Should -Be $expectedTotal }
|
2017-09-30 01:28:15 +02:00
|
|
|
"@
|
|
|
|
|
|
|
|
Invoke-Expression $class
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Describe 'Scopes Test' -Tags "CI" {
|
|
|
|
class C1
|
|
|
|
{
|
|
|
|
static C1() {
|
|
|
|
$global:foo = $script:foo
|
|
|
|
}
|
|
|
|
C1() {
|
|
|
|
$script:bar = $global:foo
|
|
|
|
}
|
|
|
|
static [int] f1() {
|
|
|
|
return $script:bar + $global:bar
|
|
|
|
}
|
|
|
|
[int] f2() {
|
|
|
|
return $script:bar + $global:bar
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Describe 'Check PS Class Assembly Test' -Tags "CI" {
|
|
|
|
class C1 {}
|
|
|
|
$assem = [C1].Assembly
|
|
|
|
$attrs = @($assem.GetCustomAttributes($true))
|
|
|
|
$expectedAttr = @($attrs | Where-Object { $_ -is [System.Management.Automation.DynamicClassImplementationAssemblyAttribute] })
|
2018-03-21 18:47:08 +01:00
|
|
|
It "Expected a DynamicClassImplementationAssembly attribute" { $expectedAttr.Length | Should -Be 1}
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Describe 'ScriptScopeAccessFromClassMethod' -Tags "CI" {
|
|
|
|
Import-Module "$PSScriptRoot\MSFT_778492.psm1"
|
|
|
|
try
|
|
|
|
{
|
|
|
|
$c = Get-MSFT_778492
|
2018-03-21 18:47:08 +01:00
|
|
|
It "Method should have found variable in module scope" { $c.F() | Should -BeExactly 'MSFT_778492 script scope'}
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
finally
|
|
|
|
{
|
|
|
|
Remove-Module MSFT_778492
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Describe 'Hidden Members Test ' -Tags "CI" {
|
|
|
|
class C1
|
|
|
|
{
|
|
|
|
[int]$visibleX
|
|
|
|
[int]$visibleY
|
|
|
|
hidden [int]$hiddenZ
|
|
|
|
}
|
|
|
|
|
|
|
|
# Create an instance
|
|
|
|
$instance = [C1]@{ visibleX = 10; visibleY = 12; hiddenZ = 42 }
|
|
|
|
|
2018-03-21 18:47:08 +01:00
|
|
|
It "Access hidden property should still work" { $instance.hiddenZ | Should -Be 42 }
|
2017-09-30 01:28:15 +02:00
|
|
|
|
2018-05-03 01:58:39 +02:00
|
|
|
It "Table formatting should not include hidden member hiddenZ" {
|
|
|
|
$expectedTable = @"
|
|
|
|
|
|
|
|
visibleX visibleY
|
|
|
|
-------- --------
|
|
|
|
10 12
|
|
|
|
|
|
|
|
|
|
|
|
"@
|
|
|
|
|
|
|
|
$tableOutput = $instance | Format-Table -AutoSize | Out-String
|
|
|
|
$tableOutput.Replace("`r","") | Should -BeExactly $expectedTable.Replace("`r","")
|
|
|
|
}
|
2017-09-30 01:28:15 +02:00
|
|
|
|
|
|
|
# Get-Member should not include hidden members by default
|
|
|
|
$member = $instance | Get-Member hiddenZ
|
2018-03-21 18:47:08 +01:00
|
|
|
it "Get-Member should not find hidden member w/o -Force" { $member | Should -BeNullOrEmpty }
|
2017-09-30 01:28:15 +02:00
|
|
|
|
|
|
|
# Get-Member should include hidden members with -Force
|
|
|
|
$member = $instance | Get-Member hiddenZ -Force
|
2018-03-21 18:47:08 +01:00
|
|
|
It "Get-Member should find hidden member w/ -Force" { $member | Should -Not -BeNullOrEmpty }
|
2017-09-30 01:28:15 +02:00
|
|
|
|
|
|
|
# Tab completion should not return a hidden member
|
|
|
|
$line = 'class C2 { hidden [int]$hiddenZ } [C2]::new().h'
|
|
|
|
$completions = [System.Management.Automation.CommandCompletion]::CompleteInput($line, $line.Length, $null)
|
2018-03-21 18:47:08 +01:00
|
|
|
It "Tab completion should not return a hidden member" { $completions.CompletionMatches.Count | Should -Be 0 }
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Describe 'BaseMethodCall Test ' -Tags "CI" {
|
2018-03-21 18:47:08 +01:00
|
|
|
It "Derived class method call" {"abc".ToString() | Should -BeExactly "abc" }
|
2017-09-30 01:28:15 +02:00
|
|
|
# call [object] ToString() method as a base class method.
|
2018-03-21 18:47:08 +01:00
|
|
|
It "Base class method call" {([object]"abc").ToString() | Should -BeExactly "System.String" }
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Describe 'Scoped Types Test' -Tags "CI" {
|
|
|
|
class C1 { [string] GetContext() { return "Test scope" } }
|
|
|
|
|
|
|
|
filter f1
|
|
|
|
{
|
|
|
|
class C1 { [string] GetContext() { return "f1 scope" } }
|
|
|
|
|
|
|
|
return [C1]::new().GetContext()
|
|
|
|
}
|
|
|
|
|
|
|
|
filter f2
|
|
|
|
{
|
|
|
|
class C1 { [string] GetContext() { return "f2 scope" } }
|
|
|
|
|
|
|
|
return (new-object C1).GetContext()
|
|
|
|
}
|
|
|
|
|
2018-03-21 18:47:08 +01:00
|
|
|
It "New-Object at test scope" { (new-object C1).GetContext() | Should -BeExactly "Test scope" }
|
|
|
|
It "[C1]::new() at test scope" { [C1]::new().GetContext() | Should -BeExactly "Test scope" }
|
2017-09-30 01:28:15 +02:00
|
|
|
|
2018-03-21 18:47:08 +01:00
|
|
|
It "[C1]::new() in nested scope" { (f1) | Should -BeExactly "f1 scope" }
|
|
|
|
It "'new-object C1' in nested scope" { (f2) | Should -BeExactly "f2 scope" }
|
2017-09-30 01:28:15 +02:00
|
|
|
|
2018-03-21 18:47:08 +01:00
|
|
|
It "[C1]::new() in nested scope (in pipeline)" { (1 | f1 | f2 | f1) | Should -BeExactly "f1 scope" }
|
|
|
|
It "'new-object C1' in nested scope (in pipeline)" { (1 | f2 | f1 | f2) | Should -BeExactly "f2 scope" }
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Describe 'ParameterOfClassTypeInModule Test' -Tags "CI" {
|
|
|
|
try
|
|
|
|
{
|
|
|
|
$sb = [scriptblock]::Create(@'
|
|
|
|
enum EE {one = 1}
|
|
|
|
function test-it([EE]$ee){$ee}
|
|
|
|
'@)
|
|
|
|
$mod = New-Module $sb -Name MSFT_2081529 | Import-Module
|
|
|
|
$result = test-it -ee one
|
2018-03-21 18:47:08 +01:00
|
|
|
It "Parameter of class/enum type defined in module should work" { $result | Should -Be 1 }
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
finally
|
|
|
|
{
|
2018-05-17 23:42:04 +02:00
|
|
|
Remove-Module -ErrorAction ignore MSFT_2081529
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Describe 'Type building' -Tags "CI" {
|
|
|
|
It 'should build the type only once for scriptblock' {
|
|
|
|
$a = $null
|
|
|
|
1..10 | ForEach-Object {
|
|
|
|
class C {}
|
|
|
|
if ($a) {
|
2018-03-21 18:47:08 +01:00
|
|
|
$a -eq [C] | Should -BeTrue
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
$a = [C]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
It 'should create a new type every time scriptblock executed?' -Pending {
|
|
|
|
$sb = [scriptblock]::Create('class A {static [int] $a }; [A]::new()')
|
|
|
|
1..2 | ForEach-Object {
|
|
|
|
$a = $sb.Invoke()[0]
|
2018-03-21 18:47:08 +01:00
|
|
|
++$a::a | Should -Be 1
|
|
|
|
++$a::a | Should -Be 2
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
}
|
2017-11-02 18:29:10 +01:00
|
|
|
|
|
|
|
It 'should get the script from a class type' {
|
|
|
|
class C {}
|
|
|
|
|
|
|
|
$a = [C].Assembly.GetCustomAttributes($false).Where{
|
|
|
|
$_ -is [System.Management.Automation.DynamicClassImplementationAssemblyAttribute]}
|
2018-03-21 18:47:08 +01:00
|
|
|
$a.ScriptFile | Should -BeExactly $PSCommandPath
|
2017-11-02 18:29:10 +01:00
|
|
|
}
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Describe 'RuntimeType created for TypeDefinitionAst' -Tags "CI" {
|
|
|
|
|
|
|
|
It 'can make cast to the right RuntimeType in two different contexts' -pending {
|
|
|
|
|
|
|
|
$ssfe = [System.Management.Automation.Runspaces.SessionStateFunctionEntry]::new("foo", @'
|
|
|
|
class Base
|
|
|
|
{
|
|
|
|
[int] foo() { return 100 }
|
|
|
|
}
|
|
|
|
|
|
|
|
class Derived : Base
|
|
|
|
{
|
|
|
|
[int] foo() { return 2 * ([Base]$this).foo() }
|
|
|
|
}
|
|
|
|
|
|
|
|
[Derived]::new().foo()
|
|
|
|
'@)
|
|
|
|
|
|
|
|
$iss = [System.Management.Automation.Runspaces.initialsessionstate]::CreateDefault2()
|
|
|
|
$iss.Commands.Add($ssfe)
|
|
|
|
|
|
|
|
$ps = [powershell]::Create($iss)
|
2018-03-21 18:47:08 +01:00
|
|
|
$ps.AddCommand("foo").Invoke() | Should -Be 200
|
|
|
|
$ps.Streams.Error | Should -BeNullOrEmpty
|
2017-09-30 01:28:15 +02:00
|
|
|
|
|
|
|
$ps1 = [powershell]::Create($iss)
|
2018-03-21 18:47:08 +01:00
|
|
|
$ps1.AddCommand("foo").Invoke() | Should -Be 200
|
|
|
|
$ps1.Streams.Error | Should -BeNullOrEmpty
|
2017-09-30 01:28:15 +02:00
|
|
|
|
|
|
|
$ps.Commands.Clear()
|
|
|
|
$ps.Streams.Error.Clear()
|
2018-03-21 18:47:08 +01:00
|
|
|
$ps.AddScript(". foo").Invoke() | Should -Be 200
|
|
|
|
$ps.Streams.Error | Should -BeNullOrEmpty
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Describe 'TypeTable lookups' -Tags "CI" {
|
|
|
|
|
|
|
|
Context 'Call methods from a different thread' {
|
|
|
|
$b = [powershell]::Create().AddScript(
|
|
|
|
@'
|
|
|
|
class A {}
|
|
|
|
class B
|
|
|
|
{
|
|
|
|
[object] getA1() { return New-Object A }
|
|
|
|
[object] getA2() { return [A]::new() }
|
|
|
|
}
|
|
|
|
|
|
|
|
[B]::new()
|
|
|
|
|
|
|
|
'@).Invoke()[0]
|
|
|
|
|
|
|
|
It 'can do type lookup by name' {
|
2018-03-21 18:47:08 +01:00
|
|
|
$b.getA1() | Should -BeExactly 'A'
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
It 'can do type lookup by [type]' {
|
2018-03-21 18:47:08 +01:00
|
|
|
$b.getA2() | Should -BeExactly 'A'
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Describe 'Protected method access' -Tags "CI" {
|
|
|
|
|
|
|
|
Add-Type @'
|
|
|
|
namespace Foo
|
|
|
|
{
|
|
|
|
public class Bar
|
|
|
|
{
|
|
|
|
protected int x {get; set;}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
'@
|
|
|
|
|
2018-03-21 18:47:08 +01:00
|
|
|
It 'doesn''t allow protected methods access outside of inheritance chain' {
|
2017-09-30 01:28:15 +02:00
|
|
|
$a = [scriptblock]::Create(@'
|
|
|
|
class A
|
|
|
|
{
|
|
|
|
SetX([Foo.Bar]$bar, [int]$x)
|
|
|
|
{
|
|
|
|
$bar.x = $x
|
|
|
|
}
|
|
|
|
|
|
|
|
[int] GetX([Foo.Bar]$bar)
|
|
|
|
{
|
|
|
|
Set-StrictMode -Version latest
|
|
|
|
return $bar.x
|
|
|
|
}
|
|
|
|
}
|
|
|
|
[A]::new()
|
|
|
|
|
|
|
|
'@).Invoke()
|
|
|
|
$bar = [Foo.Bar]::new()
|
2018-03-21 18:47:08 +01:00
|
|
|
{ $a.SetX($bar, 42) } | Should -Throw -ErrorId 'PropertyAssignmentException'
|
|
|
|
{ $a.GetX($bar) } | Should -Throw -ErrorId 'PropertyNotFoundStrict'
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
It 'can call protected methods sequentially from two different contexts' {
|
|
|
|
$ssfe = [System.Management.Automation.Runspaces.SessionStateFunctionEntry]::new("foo", @'
|
|
|
|
class A : Foo.Bar
|
|
|
|
{
|
|
|
|
SetX([int]$x)
|
|
|
|
{
|
|
|
|
$this.x = $x
|
|
|
|
}
|
|
|
|
|
|
|
|
[int] GetX()
|
|
|
|
{
|
|
|
|
return $this.x
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return [A]::new()
|
|
|
|
'@)
|
|
|
|
|
|
|
|
$iss = [System.Management.Automation.Runspaces.initialsessionstate]::CreateDefault()
|
|
|
|
$iss.Commands.Add($ssfe)
|
|
|
|
|
|
|
|
$ps = [powershell]::Create($iss)
|
|
|
|
$a = $ps.AddCommand("foo").Invoke()[0]
|
2018-03-21 18:47:08 +01:00
|
|
|
$ps.Streams.Error | Should -BeNullOrEmpty
|
2017-09-30 01:28:15 +02:00
|
|
|
|
|
|
|
$ps1 = [powershell]::Create($iss)
|
|
|
|
$a1 = $ps1.AddCommand("foo").Invoke()[0]
|
2018-03-21 18:47:08 +01:00
|
|
|
$ps1.Streams.Error | Should -BeNullOrEmpty
|
2017-09-30 01:28:15 +02:00
|
|
|
|
|
|
|
$a.SetX(101)
|
|
|
|
$a1.SetX(103)
|
|
|
|
|
2018-03-21 18:47:08 +01:00
|
|
|
$a.GetX() | Should -Be 101
|
|
|
|
$a1.GetX() | Should -Be 103
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Describe 'variable analysis' -Tags "CI" {
|
|
|
|
It 'can specify type construct on the local variables' {
|
|
|
|
class A { [string] getFoo() { return 'foo'} }
|
|
|
|
|
|
|
|
class B
|
|
|
|
{
|
|
|
|
static [A] getA ()
|
|
|
|
{
|
|
|
|
[A] $var = [A]::new()
|
|
|
|
return $var
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-21 18:47:08 +01:00
|
|
|
[B]::getA().getFoo() | Should -BeExactly 'foo'
|
2017-09-30 01:28:15 +02:00
|
|
|
}
|
|
|
|
}
|