mirror of
https://github.com/aphrody-labs/PowerShell.git
synced 2026-10-17 12:45:47 +00:00
Autocorrected CRLF to LF (#4943)
Also, fix `Parser.Tests.ps1` after correcting CRLF.
This commit is contained in:
1 parent
8fdd68fbaa
commit
2639cd89ce
86 files changed
+13288
-13288
No files matched your search
@@ -1,15 +1,15 @@
|
||||
|
||||
$foo = 'MSFT_778492 script scope'
|
||||
|
||||
class MSFT_778492
|
||||
{
|
||||
[string] F()
|
||||
{
|
||||
return $script:foo
|
||||
}
|
||||
}
|
||||
|
||||
function Get-MSFT_778492
|
||||
{
|
||||
[MSFT_778492]::new()
|
||||
}
|
||||
|
||||
$foo = 'MSFT_778492 script scope'
|
||||
|
||||
class MSFT_778492
|
||||
{
|
||||
[string] F()
|
||||
{
|
||||
return $script:foo
|
||||
}
|
||||
}
|
||||
|
||||
function Get-MSFT_778492
|
||||
{
|
||||
[MSFT_778492]::new()
|
||||
}
|
||||
@@ -1,188 +1,188 @@
|
||||
|
||||
Add-Type -WarningAction Ignore @'
|
||||
public class Base
|
||||
{
|
||||
private int data;
|
||||
|
||||
protected Base()
|
||||
{
|
||||
data = 10;
|
||||
}
|
||||
|
||||
protected Base(int i)
|
||||
{
|
||||
data = i;
|
||||
}
|
||||
|
||||
protected int Field;
|
||||
protected int Property { get; set; }
|
||||
public int Property1 { get; protected set; }
|
||||
public int Property2 { protected get; set; }
|
||||
|
||||
protected int Method()
|
||||
{
|
||||
return 32 + data;
|
||||
}
|
||||
protected int OverloadedMethod1(int i)
|
||||
{
|
||||
return 32 + i + data;
|
||||
}
|
||||
protected int OverloadedMethod1(string i)
|
||||
{
|
||||
return 1 + data;
|
||||
}
|
||||
public int OverloadedMethod2(int i)
|
||||
{
|
||||
return 32 + i + data;
|
||||
}
|
||||
protected int OverloadedMethod2(string i)
|
||||
{
|
||||
return 1 + data;
|
||||
}
|
||||
protected int OverloadedMethod3(int i)
|
||||
{
|
||||
return 32 + i + data;
|
||||
}
|
||||
public int OverloadedMethod3(string i)
|
||||
{
|
||||
return 1 + data;
|
||||
}
|
||||
}
|
||||
'@
|
||||
|
||||
$derived1,$derived2,$derived3 = Invoke-Expression @'
|
||||
class Derived : Base
|
||||
{
|
||||
Derived() : Base() {}
|
||||
Derived([int] $i) : Base($i) {}
|
||||
|
||||
[int] TestPropertyAccess()
|
||||
{
|
||||
$this.Property = 1111
|
||||
return $this.Property
|
||||
}
|
||||
|
||||
[int] TestPropertyAccess1()
|
||||
{
|
||||
$this.Property1 = 2111
|
||||
return $this.Property1
|
||||
}
|
||||
|
||||
[int] TestPropertyAccess2()
|
||||
{
|
||||
$this.Property2 = 3111
|
||||
return $this.Property2
|
||||
}
|
||||
|
||||
[int] TestDynamicPropertyAccess()
|
||||
{
|
||||
$p = 'Property'
|
||||
$this.$p = 1112
|
||||
return $this.$p
|
||||
}
|
||||
|
||||
[int] TestFieldAccess()
|
||||
{
|
||||
$this.Field = 11
|
||||
return $this.Field
|
||||
}
|
||||
|
||||
[int] TestDynamicFieldAccess()
|
||||
{
|
||||
$f = 'Field'
|
||||
$this.$f = 12
|
||||
return $this.$f
|
||||
}
|
||||
|
||||
[int] TestMethodAccess()
|
||||
{
|
||||
return $this.Method()
|
||||
}
|
||||
|
||||
[int] TestDynamicMethodAccess()
|
||||
{
|
||||
$m = 'Method'
|
||||
return $this.$m()
|
||||
}
|
||||
|
||||
[int] TestOverloadedMethodAccess1a()
|
||||
{
|
||||
return $this.OverloadedMethod1(42)
|
||||
}
|
||||
[int] TestOverloadedMethodAccess1b()
|
||||
{
|
||||
return $this.OverloadedMethod1("abc")
|
||||
}
|
||||
[int] TestOverloadedMethodAccess2a()
|
||||
{
|
||||
return $this.OverloadedMethod2(42)
|
||||
}
|
||||
[int] TestOverloadedMethodAccess2b()
|
||||
{
|
||||
return $this.OverloadedMethod2("abc")
|
||||
}
|
||||
[int] TestOverloadedMethodAccess3a()
|
||||
{
|
||||
return $this.OverloadedMethod3(42)
|
||||
}
|
||||
[int] TestOverloadedMethodAccess3b()
|
||||
{
|
||||
return $this.OverloadedMethod3("abc")
|
||||
}
|
||||
}
|
||||
|
||||
class Derived2 : Base {}
|
||||
|
||||
[Derived]::new()
|
||||
[Derived]::new(20)
|
||||
[Derived2]::new()
|
||||
'@
|
||||
|
||||
Describe "Protected Member Access - w/ default ctor" -Tags "CI" {
|
||||
It "Method Access" { $derived1.TestMethodAccess() | Should Be 42 }
|
||||
It "Dynamic Method Access" { $derived1.TestDynamicMethodAccess() | Should Be 42 }
|
||||
It "Field Access" { $derived1.TestFieldAccess() | Should Be 11 }
|
||||
It "Dynamic Field Access" { $derived1.TestDynamicFieldAccess() | Should Be 12 }
|
||||
It "Property Access - protected get/protected set" { $derived1.TestPropertyAccess() | Should Be 1111 }
|
||||
It "Property Access - public get/protected set " { $derived1.TestPropertyAccess1() | Should Be 2111 }
|
||||
It "Property Access - protected get/public set" { $derived1.TestPropertyAccess2() | Should Be 3111 }
|
||||
It "Dynamic Property Access" { $derived1.TestDynamicPropertyAccess() | Should Be 1112 }
|
||||
|
||||
It "Method Access - overloaded 1a" { $derived1.TestOverloadedMethodAccess1a() | Should Be 84 }
|
||||
It "Method Access - overloaded 1b" { $derived1.TestOverloadedMethodAccess1b() | Should Be 11 }
|
||||
It "Method Access - overloaded 2a" { $derived1.TestOverloadedMethodAccess2a() | Should Be 84 }
|
||||
It "Method Access - overloaded 2b" { $derived1.TestOverloadedMethodAccess2b() | Should Be 11 }
|
||||
It "Method Access - overloaded 3a" { $derived1.TestOverloadedMethodAccess3a() | Should Be 84 }
|
||||
It "Method Access - overloaded 3b" { $derived1.TestOverloadedMethodAccess3b() | Should Be 11 }
|
||||
It "Implicit ctor calls protected ctor" { $derived3.OverloadedMethod2(42) | Should Be 84 }
|
||||
}
|
||||
|
||||
Describe "Protected Member Access - w/ non-default ctor" -Tags "CI" {
|
||||
It "Method Access" { $derived2.TestMethodAccess() | Should Be 52 }
|
||||
It "Dynamic Method Access" { $derived2.TestDynamicMethodAccess() | Should Be 52 }
|
||||
It "Field Access" { $derived2.TestFieldAccess() | Should Be 11 }
|
||||
It "Dynamic Field Access" { $derived2.TestDynamicFieldAccess() | Should Be 12 }
|
||||
It "Property Access - protected get/protected set" { $derived1.TestPropertyAccess() | Should Be 1111 }
|
||||
It "Property Access - public get/protected set " { $derived1.TestPropertyAccess1() | Should Be 2111 }
|
||||
It "Property Access - protected get/public set" { $derived1.TestPropertyAccess2() | Should Be 3111 }
|
||||
It "Dynamic Property Access" { $derived2.TestDynamicPropertyAccess() | Should Be 1112 }
|
||||
|
||||
It "Method Access - overloaded 1a" { $derived2.TestOverloadedMethodAccess1a() | Should Be 94 }
|
||||
It "Method Access - overloaded 1b" { $derived2.TestOverloadedMethodAccess1b() | Should Be 21 }
|
||||
It "Method Access - overloaded 2a" { $derived2.TestOverloadedMethodAccess2a() | Should Be 94 }
|
||||
It "Method Access - overloaded 2b" { $derived2.TestOverloadedMethodAccess2b() | Should Be 21 }
|
||||
It "Method Access - overloaded 3a" { $derived2.TestOverloadedMethodAccess3a() | Should Be 94 }
|
||||
It "Method Access - overloaded 3b" { $derived2.TestOverloadedMethodAccess3b() | Should Be 21 }
|
||||
}
|
||||
|
||||
Describe "Protected Member Access - members not visible outside class" -Tags "CI" {
|
||||
Set-StrictMode -v 3
|
||||
It "Invalid protected field Get Access" { { $derived1.Field } | Should Throw }
|
||||
It "Invalid protected property Get Access" { { $derived1.Property } | Should Throw }
|
||||
It "Invalid protected field Set Access" { { $derived1.Field = 1 } | Should Throw }
|
||||
It "Invalid protected property Set Access" { { $derived1.Property = 1 } | Should Throw }
|
||||
|
||||
It "Invalid protected constructor Access" { { [Base]::new() } | Should Throw }
|
||||
It "Invalid protected method Access" { { $derived1.Method() } | Should Throw }
|
||||
}
|
||||
|
||||
|
||||
Add-Type -WarningAction Ignore @'
|
||||
public class Base
|
||||
{
|
||||
private int data;
|
||||
|
||||
protected Base()
|
||||
{
|
||||
data = 10;
|
||||
}
|
||||
|
||||
protected Base(int i)
|
||||
{
|
||||
data = i;
|
||||
}
|
||||
|
||||
protected int Field;
|
||||
protected int Property { get; set; }
|
||||
public int Property1 { get; protected set; }
|
||||
public int Property2 { protected get; set; }
|
||||
|
||||
protected int Method()
|
||||
{
|
||||
return 32 + data;
|
||||
}
|
||||
protected int OverloadedMethod1(int i)
|
||||
{
|
||||
return 32 + i + data;
|
||||
}
|
||||
protected int OverloadedMethod1(string i)
|
||||
{
|
||||
return 1 + data;
|
||||
}
|
||||
public int OverloadedMethod2(int i)
|
||||
{
|
||||
return 32 + i + data;
|
||||
}
|
||||
protected int OverloadedMethod2(string i)
|
||||
{
|
||||
return 1 + data;
|
||||
}
|
||||
protected int OverloadedMethod3(int i)
|
||||
{
|
||||
return 32 + i + data;
|
||||
}
|
||||
public int OverloadedMethod3(string i)
|
||||
{
|
||||
return 1 + data;
|
||||
}
|
||||
}
|
||||
'@
|
||||
|
||||
$derived1,$derived2,$derived3 = Invoke-Expression @'
|
||||
class Derived : Base
|
||||
{
|
||||
Derived() : Base() {}
|
||||
Derived([int] $i) : Base($i) {}
|
||||
|
||||
[int] TestPropertyAccess()
|
||||
{
|
||||
$this.Property = 1111
|
||||
return $this.Property
|
||||
}
|
||||
|
||||
[int] TestPropertyAccess1()
|
||||
{
|
||||
$this.Property1 = 2111
|
||||
return $this.Property1
|
||||
}
|
||||
|
||||
[int] TestPropertyAccess2()
|
||||
{
|
||||
$this.Property2 = 3111
|
||||
return $this.Property2
|
||||
}
|
||||
|
||||
[int] TestDynamicPropertyAccess()
|
||||
{
|
||||
$p = 'Property'
|
||||
$this.$p = 1112
|
||||
return $this.$p
|
||||
}
|
||||
|
||||
[int] TestFieldAccess()
|
||||
{
|
||||
$this.Field = 11
|
||||
return $this.Field
|
||||
}
|
||||
|
||||
[int] TestDynamicFieldAccess()
|
||||
{
|
||||
$f = 'Field'
|
||||
$this.$f = 12
|
||||
return $this.$f
|
||||
}
|
||||
|
||||
[int] TestMethodAccess()
|
||||
{
|
||||
return $this.Method()
|
||||
}
|
||||
|
||||
[int] TestDynamicMethodAccess()
|
||||
{
|
||||
$m = 'Method'
|
||||
return $this.$m()
|
||||
}
|
||||
|
||||
[int] TestOverloadedMethodAccess1a()
|
||||
{
|
||||
return $this.OverloadedMethod1(42)
|
||||
}
|
||||
[int] TestOverloadedMethodAccess1b()
|
||||
{
|
||||
return $this.OverloadedMethod1("abc")
|
||||
}
|
||||
[int] TestOverloadedMethodAccess2a()
|
||||
{
|
||||
return $this.OverloadedMethod2(42)
|
||||
}
|
||||
[int] TestOverloadedMethodAccess2b()
|
||||
{
|
||||
return $this.OverloadedMethod2("abc")
|
||||
}
|
||||
[int] TestOverloadedMethodAccess3a()
|
||||
{
|
||||
return $this.OverloadedMethod3(42)
|
||||
}
|
||||
[int] TestOverloadedMethodAccess3b()
|
||||
{
|
||||
return $this.OverloadedMethod3("abc")
|
||||
}
|
||||
}
|
||||
|
||||
class Derived2 : Base {}
|
||||
|
||||
[Derived]::new()
|
||||
[Derived]::new(20)
|
||||
[Derived2]::new()
|
||||
'@
|
||||
|
||||
Describe "Protected Member Access - w/ default ctor" -Tags "CI" {
|
||||
It "Method Access" { $derived1.TestMethodAccess() | Should Be 42 }
|
||||
It "Dynamic Method Access" { $derived1.TestDynamicMethodAccess() | Should Be 42 }
|
||||
It "Field Access" { $derived1.TestFieldAccess() | Should Be 11 }
|
||||
It "Dynamic Field Access" { $derived1.TestDynamicFieldAccess() | Should Be 12 }
|
||||
It "Property Access - protected get/protected set" { $derived1.TestPropertyAccess() | Should Be 1111 }
|
||||
It "Property Access - public get/protected set " { $derived1.TestPropertyAccess1() | Should Be 2111 }
|
||||
It "Property Access - protected get/public set" { $derived1.TestPropertyAccess2() | Should Be 3111 }
|
||||
It "Dynamic Property Access" { $derived1.TestDynamicPropertyAccess() | Should Be 1112 }
|
||||
|
||||
It "Method Access - overloaded 1a" { $derived1.TestOverloadedMethodAccess1a() | Should Be 84 }
|
||||
It "Method Access - overloaded 1b" { $derived1.TestOverloadedMethodAccess1b() | Should Be 11 }
|
||||
It "Method Access - overloaded 2a" { $derived1.TestOverloadedMethodAccess2a() | Should Be 84 }
|
||||
It "Method Access - overloaded 2b" { $derived1.TestOverloadedMethodAccess2b() | Should Be 11 }
|
||||
It "Method Access - overloaded 3a" { $derived1.TestOverloadedMethodAccess3a() | Should Be 84 }
|
||||
It "Method Access - overloaded 3b" { $derived1.TestOverloadedMethodAccess3b() | Should Be 11 }
|
||||
It "Implicit ctor calls protected ctor" { $derived3.OverloadedMethod2(42) | Should Be 84 }
|
||||
}
|
||||
|
||||
Describe "Protected Member Access - w/ non-default ctor" -Tags "CI" {
|
||||
It "Method Access" { $derived2.TestMethodAccess() | Should Be 52 }
|
||||
It "Dynamic Method Access" { $derived2.TestDynamicMethodAccess() | Should Be 52 }
|
||||
It "Field Access" { $derived2.TestFieldAccess() | Should Be 11 }
|
||||
It "Dynamic Field Access" { $derived2.TestDynamicFieldAccess() | Should Be 12 }
|
||||
It "Property Access - protected get/protected set" { $derived1.TestPropertyAccess() | Should Be 1111 }
|
||||
It "Property Access - public get/protected set " { $derived1.TestPropertyAccess1() | Should Be 2111 }
|
||||
It "Property Access - protected get/public set" { $derived1.TestPropertyAccess2() | Should Be 3111 }
|
||||
It "Dynamic Property Access" { $derived2.TestDynamicPropertyAccess() | Should Be 1112 }
|
||||
|
||||
It "Method Access - overloaded 1a" { $derived2.TestOverloadedMethodAccess1a() | Should Be 94 }
|
||||
It "Method Access - overloaded 1b" { $derived2.TestOverloadedMethodAccess1b() | Should Be 21 }
|
||||
It "Method Access - overloaded 2a" { $derived2.TestOverloadedMethodAccess2a() | Should Be 94 }
|
||||
It "Method Access - overloaded 2b" { $derived2.TestOverloadedMethodAccess2b() | Should Be 21 }
|
||||
It "Method Access - overloaded 3a" { $derived2.TestOverloadedMethodAccess3a() | Should Be 94 }
|
||||
It "Method Access - overloaded 3b" { $derived2.TestOverloadedMethodAccess3b() | Should Be 21 }
|
||||
}
|
||||
|
||||
Describe "Protected Member Access - members not visible outside class" -Tags "CI" {
|
||||
Set-StrictMode -v 3
|
||||
It "Invalid protected field Get Access" { { $derived1.Field } | Should Throw }
|
||||
It "Invalid protected property Get Access" { { $derived1.Property } | Should Throw }
|
||||
It "Invalid protected field Set Access" { { $derived1.Field = 1 } | Should Throw }
|
||||
It "Invalid protected property Set Access" { { $derived1.Property = 1 } | Should Throw }
|
||||
|
||||
It "Invalid protected constructor Access" { { [Base]::new() } | Should Throw }
|
||||
It "Invalid protected method Access" { { $derived1.Method() } | Should Throw }
|
||||
}
|
||||
|
||||
@@ -1,484 +1,484 @@
|
||||
Describe 'Attributes Test' -Tags "CI" {
|
||||
|
||||
BeforeAll {
|
||||
$dummyAttributesSource = @'
|
||||
using System.Management.Automation;
|
||||
namespace Dummy
|
||||
{
|
||||
public class DoubleStringTransformationAttribute : ArgumentTransformationAttribute
|
||||
{
|
||||
public override object Transform(EngineIntrinsics engineIntrinsics, object inputData)
|
||||
{
|
||||
string arg = inputData as string;
|
||||
if (arg != null)
|
||||
{
|
||||
return arg + arg;
|
||||
}
|
||||
return inputData;
|
||||
}
|
||||
}
|
||||
|
||||
public class AppendStringTransformationAttribute : ArgumentTransformationAttribute
|
||||
{
|
||||
public override object Transform(EngineIntrinsics engineIntrinsics, object inputData)
|
||||
{
|
||||
string arg = inputData as string;
|
||||
if (arg != null)
|
||||
{
|
||||
return arg + "___";
|
||||
}
|
||||
return inputData;
|
||||
}
|
||||
}
|
||||
|
||||
public class DoubleInt : ArgumentTransformationAttribute
|
||||
{
|
||||
public override object Transform(EngineIntrinsics engineIntrinsics, object inputData)
|
||||
{
|
||||
int? arg = inputData as int?;
|
||||
if (arg != null)
|
||||
{
|
||||
return arg + arg;
|
||||
}
|
||||
return inputData;
|
||||
}
|
||||
}
|
||||
}
|
||||
'@
|
||||
Add-Type -TypeDefinition $dummyAttributesSource
|
||||
}
|
||||
|
||||
|
||||
|
||||
Context 'Property.Instance.ValidateSet.String' {
|
||||
class C1 { [ValidateSet("Present", "Absent")][string]$Ensure }
|
||||
# This call should not throw exception
|
||||
[C1]::new().Ensure = "Present"
|
||||
|
||||
It 'Error when ValidateSet should be ExceptionWhenSetting' {
|
||||
try
|
||||
{
|
||||
[C1]::new().Ensure = "foo"
|
||||
throw "Exception expected"
|
||||
}
|
||||
catch
|
||||
{
|
||||
$_.FullyQualifiedErrorId | Should be 'ExceptionWhenSetting'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Context 'Property.Static.ValidateSet.String' {
|
||||
class C1 { static [ValidateSet("Present", "Absent")][string]$Ensure }
|
||||
# This call should not throw exception
|
||||
[C1]::Ensure = "Present"
|
||||
It 'Error when ValidateSet should be ExceptionWhenSetting'{
|
||||
try {
|
||||
[C1]::Ensure = "foo"
|
||||
throw "Exception expected"
|
||||
}
|
||||
catch {
|
||||
$_.FullyQualifiedErrorId | Should be 'ExceptionWhenSetting'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Context 'Property.Instance.ValidateRange.Int' {
|
||||
class C1 { [ValidateRange(1, 10)][int]$f }
|
||||
# This call should not throw exception
|
||||
[C1]::new().f = 10
|
||||
[C1]::new().f = 1
|
||||
It 'Error when ValidateSet should be ExceptionWhenSetting'{
|
||||
try {
|
||||
[C1]::new().f = 20
|
||||
throw "Exception expected"
|
||||
}
|
||||
catch {
|
||||
$_.FullyQualifiedErrorId | Should be 'ExceptionWhenSetting'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Context 'Property.Static.ValidateRange.Int' {
|
||||
class C1 { static [ValidateRange(1, 10)][int]$f }
|
||||
# This call should not throw exception
|
||||
[C1]::f = 5
|
||||
It 'Error when ValidateSet should be ExceptionWhenSetting'{
|
||||
try {
|
||||
[C1]::f = 20
|
||||
throw "Exception expected"
|
||||
}
|
||||
catch {
|
||||
$_.FullyQualifiedErrorId | Should be 'ExceptionWhenSetting'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Context 'Property.Static.ValidateSet.ImplicitObject' {
|
||||
class C1 { static [ValidateSet("abc", 5)]$o }
|
||||
# This call should not throw exception
|
||||
[C1]::o = "abc"
|
||||
[C1]::o = 5
|
||||
It 'Error when ValidateSet should be ExceptionWhenSetting'{
|
||||
try {
|
||||
[C1]::o = 1
|
||||
throw "Exception expected"
|
||||
}
|
||||
catch {
|
||||
$_.FullyQualifiedErrorId | Should be 'ExceptionWhenSetting'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# We use [scriptblock]::Create() here to allow SuiteSetup add Dummy.Transformation type to
|
||||
# the scope. Otherwise, we will need to have all classes for attributes in parse time.
|
||||
#
|
||||
# Invoke() returns an array, we need first element of it.
|
||||
#
|
||||
|
||||
Context 'Property.Instance.Transformation.ImplicitObject' {
|
||||
$c = [scriptblock]::Create('class C1 { [Dummy.DoubleStringTransformation()]$arg }; [C1]::new()').Invoke()[0]
|
||||
|
||||
It 'Implicitly Transform to 100' {
|
||||
$c.arg = 100
|
||||
$c.arg | should be 100
|
||||
}
|
||||
It 'Implicitly Transform to foo' {
|
||||
$c.arg = "foo"
|
||||
$c.arg | should be "foofoo"
|
||||
}
|
||||
}
|
||||
|
||||
Context 'Property.Instance.Transformation.String' {
|
||||
$c = [scriptblock]::Create('class C1 { [Dummy.DoubleStringTransformation()][string]$arg }; [C1]::new()').Invoke()[0]
|
||||
It 'set to foo' {
|
||||
$c.arg = "foo"
|
||||
$c.arg | should be "foofoo"
|
||||
}
|
||||
}
|
||||
|
||||
Context Property.Instance.Transformation.Int {
|
||||
$c = [scriptblock]::Create('class C1 { [Dummy.DoubleInt()][int]$arg }; [C1]::new()').Invoke()[0]
|
||||
It 'arg should be 200' {
|
||||
$c.arg = 100
|
||||
$c.arg | should be 200
|
||||
}
|
||||
It 'Set to string should fail with ExceptionWhenSetting' {
|
||||
try {
|
||||
$c.arg = "abc"
|
||||
throw "Exception expected"
|
||||
}
|
||||
catch {
|
||||
$_.FullyQualifiedErrorId | Should be 'ExceptionWhenSetting'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Context Property.Instance.Transformation.Nullable {
|
||||
$c = [scriptblock]::Create('class C1 { [Nullable[int]][Dummy.DoubleStringTransformation()]$arg }; [C1]::new()').Invoke()[0]
|
||||
It 'arg should be 100' {
|
||||
$c.arg = 100
|
||||
$c.arg | should be 100
|
||||
}
|
||||
}
|
||||
|
||||
Context Property.Instance.Transformation.Order {
|
||||
$c = [scriptblock]::Create('class C1 { [Dummy.DoubleStringTransformation()][Dummy.AppendStringTransformation()]$arg }; [C1]::new()').Invoke()[0]
|
||||
It 'arg should be 100' {
|
||||
$c.arg = 100
|
||||
$c.arg | should be 100
|
||||
}
|
||||
|
||||
It 'arg should be foo___foo___g' {
|
||||
$c.arg = "foo"
|
||||
$c.arg | should be "foo___foo___"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Describe 'Type resolution with attributes' -Tag "CI" {
|
||||
# There is kind of a collision between names
|
||||
# System.Diagnostics.Tracing.EventSource
|
||||
# System.Diagnostics.Tracing.EventSourceAttribute
|
||||
# We need to make sure that we resolve type name to the right class at each usage
|
||||
Context 'Name collision' {
|
||||
|
||||
It 'Resolve System.Diagnostics.Tracing.EventSource to Attribute and to Type in the different contexts' {
|
||||
[System.Diagnostics.Tracing.EventSource(Name = "MyPSEventSource")]
|
||||
class MyEventSource : System.Diagnostics.Tracing.EventSource
|
||||
{
|
||||
[void] OnEvent([string]$Message) {}
|
||||
}
|
||||
|
||||
[MyEventSource]::new() | Should Not Be $null
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Describe 'ValidateSet support a dynamically generated set' -Tag "CI" {
|
||||
|
||||
Context 'C# tests' {
|
||||
|
||||
BeforeAll {
|
||||
$a=@'
|
||||
using System;
|
||||
using System.Management.Automation;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Test.Language {
|
||||
|
||||
[Cmdlet(VerbsCommon.Get, "TestValidateSet0")]
|
||||
public class TestValidateSetCommand0 : PSCmdlet
|
||||
{
|
||||
[Parameter]
|
||||
[ValidateSet(typeof(PSCmdlet))]
|
||||
public string Param1;
|
||||
|
||||
protected override void EndProcessing()
|
||||
{
|
||||
WriteObject(Param1);
|
||||
}
|
||||
}
|
||||
|
||||
[Cmdlet(VerbsCommon.Get, "TestValidateSet4")]
|
||||
public class TestValidateSetCommand4 : PSCmdlet
|
||||
{
|
||||
[Parameter]
|
||||
[ValidateSet(typeof(GenValuesForParam))]
|
||||
public string Param1;
|
||||
|
||||
protected override void EndProcessing()
|
||||
{
|
||||
WriteObject(Param1);
|
||||
}
|
||||
}
|
||||
|
||||
[Cmdlet(VerbsCommon.Get, "TestValidateSet5")]
|
||||
public class TestValidateSetCommand5 : PSCmdlet
|
||||
{
|
||||
[Parameter]
|
||||
[ValidateSet(typeof(GenValuesForParamNull))]
|
||||
public string Param1;
|
||||
|
||||
protected override void EndProcessing()
|
||||
{
|
||||
WriteObject(Param1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Implement of test IValidateSetValuesGenerator
|
||||
public class GenValuesForParamNull : IValidateSetValuesGenerator
|
||||
{
|
||||
public string[] GetValidValues()
|
||||
{
|
||||
var testValues = new string[] {"Test1","TestString1","Test2"};
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public class GenValuesForParam : IValidateSetValuesGenerator
|
||||
{
|
||||
public string[] GetValidValues()
|
||||
{
|
||||
var testValues = new string[] {"Test1","TestString1","Test2"};
|
||||
return testValues;
|
||||
}
|
||||
}
|
||||
}
|
||||
'@
|
||||
|
||||
$cls = Add-Type -TypeDefinition $a -PassThru | select -First 1
|
||||
$testModule = Import-Module $cls.Assembly -PassThru
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
Remove-Module -ModuleInfo $testModule
|
||||
}
|
||||
|
||||
It 'Throw if IValidateSetValuesGenerator is not implemented' {
|
||||
{ Get-TestValidateSet0 -Param1 "TestString" -ErrorAction Stop } | ShouldBeErrorId "Argument"
|
||||
}
|
||||
|
||||
It 'Dynamically generated set works in C# with default (immediate) cache expire' {
|
||||
Get-TestValidateSet4 -Param1 "TestString1" -ErrorAction SilentlyContinue | Should BeExactly "TestString1"
|
||||
}
|
||||
|
||||
It 'Empty dynamically generated set throws in C#' {
|
||||
$exc = {
|
||||
Get-TestValidateSet5 -Param1 "TestString1" -ErrorAction Stop
|
||||
} | ShouldBeErrorId "ParameterArgumentValidationError,Test.Language.TestValidateSetCommand5"
|
||||
$exc.Exception.InnerException.ErrorRecord.FullyQualifiedErrorId | Should BeExactly "ValidateSetGeneratedValidValuesListIsNull"
|
||||
}
|
||||
}
|
||||
|
||||
Context 'Powershell tests' {
|
||||
|
||||
BeforeAll {
|
||||
class GenValuesForParam : System.Management.Automation.IValidateSetValuesGenerator {
|
||||
[String[]] GetValidValues() {
|
||||
|
||||
return [string[]]("Test1","TestString1","Test2")
|
||||
}
|
||||
}
|
||||
|
||||
class GenValuesForParamNull : System.Management.Automation.IValidateSetValuesGenerator {
|
||||
[String[]] GetValidValues() {
|
||||
|
||||
return [string[]]$null
|
||||
}
|
||||
}
|
||||
|
||||
# Return '$testValues2' and after 2 seconds after first use return another array '$testValues1'.
|
||||
class GenValuesForParamCache1 : System.Management.Automation.IValidateSetValuesGenerator {
|
||||
[String[]] GetValidValues() {
|
||||
|
||||
$testValues1 = "Test11","TestString11","Test22"
|
||||
$testValues2 = "Test11","TestString22","Test22"
|
||||
|
||||
$currentTime = [DateTime]::Now
|
||||
if ([DateTime]::Compare([GenValuesForParamCache1]::cacheTime, $currentTime) -le 0)
|
||||
{
|
||||
$testValues = $testValues1;
|
||||
}
|
||||
else
|
||||
{
|
||||
$testValues = $testValues2;
|
||||
}
|
||||
return [string[]]$testValues
|
||||
}
|
||||
|
||||
static [DateTime] $cacheTime = [DateTime]::Now.AddSeconds(2);
|
||||
}
|
||||
|
||||
function Get-TestValidateSetPS4
|
||||
{
|
||||
[CmdletBinding()]
|
||||
Param
|
||||
(
|
||||
[ValidateSet([GenValuesForParam])]
|
||||
$Param1
|
||||
)
|
||||
|
||||
$Param1
|
||||
}
|
||||
|
||||
function Get-TestValidateSetPS5
|
||||
{
|
||||
[CmdletBinding()]
|
||||
Param
|
||||
(
|
||||
[ValidateSet([GenValuesForParamNull])]
|
||||
$Param1
|
||||
)
|
||||
|
||||
$Param1
|
||||
}
|
||||
|
||||
function Get-TestValidateSetPS6
|
||||
{
|
||||
[CmdletBinding()]
|
||||
Param
|
||||
(
|
||||
[ValidateSet([UnImplementedGeneratorOfValues])]
|
||||
$Param1
|
||||
)
|
||||
|
||||
$Param1
|
||||
}
|
||||
}
|
||||
|
||||
It 'Dynamically generated set works in PowerShell script with default (immediate) cache expire' {
|
||||
Get-TestValidateSetPS4 -Param1 "TestString1" -ErrorAction SilentlyContinue | Should BeExactly "TestString1"
|
||||
}
|
||||
|
||||
It 'Get the appropriate error message' {
|
||||
{Get-TestValidateSetPS4 -Param1 "TestStringWrong" -ErrorAction Stop} | ShouldBeErrorId "ParameterArgumentValidationError,Get-TestValidateSetPS4"
|
||||
}
|
||||
|
||||
It 'Empty dynamically generated set throws in PowerShell script' {
|
||||
$exc = {
|
||||
Get-TestValidateSetPS5 -Param1 "TestString1" -ErrorAction Stop
|
||||
} | ShouldBeErrorId "ParameterArgumentValidationError,Get-TestValidateSetPS5"
|
||||
$exc.Exception.InnerException.ErrorRecord.FullyQualifiedErrorId | Should BeExactly "ValidateSetGeneratedValidValuesListIsNull"
|
||||
}
|
||||
|
||||
It 'Unimplemented valid values generator type throws in PowerShell script' {
|
||||
{
|
||||
Get-TestValidateSetPS6 -Param1 "AnyTestString" -ErrorAction Stop
|
||||
} | ShouldBeErrorId "TypeNotFound"
|
||||
}
|
||||
}
|
||||
|
||||
Context 'CachedValidValuesGeneratorBase class tests' {
|
||||
|
||||
BeforeAll {
|
||||
class GenValuesForParam : System.Management.Automation.CachedValidValuesGeneratorBase {
|
||||
GenValuesForParam() : base(300) {
|
||||
}
|
||||
|
||||
[String[]] GenerateValidValues() {
|
||||
|
||||
return [string[]]("Test1","TestString1","Test2")
|
||||
}
|
||||
}
|
||||
|
||||
class GenValuesWithExpiration : System.Management.Automation.CachedValidValuesGeneratorBase {
|
||||
GenValuesWithExpiration() : base(2) {
|
||||
}
|
||||
|
||||
Static [bool] $temp = $true;
|
||||
|
||||
[String[]] GenerateValidValues() {
|
||||
|
||||
if ([GenValuesWithExpiration]::temp) {
|
||||
[GenValuesWithExpiration]::temp = $false
|
||||
return [string[]]("Test1","TestString1","Test2")
|
||||
} else {
|
||||
[GenValuesWithExpiration]::temp = $true
|
||||
return [string[]]("Test1","TestString2","Test2")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function Get-TestValidateSetPS4
|
||||
{
|
||||
[CmdletBinding()]
|
||||
Param
|
||||
(
|
||||
[ValidateSet([GenValuesForParam])]
|
||||
$Param1
|
||||
)
|
||||
|
||||
$Param1
|
||||
}
|
||||
|
||||
function Get-TestValidateSetPS5
|
||||
{
|
||||
[CmdletBinding()]
|
||||
Param
|
||||
(
|
||||
[ValidateSet([GenValuesWithExpiration])]
|
||||
$Param1
|
||||
)
|
||||
|
||||
$Param1
|
||||
}
|
||||
}
|
||||
|
||||
It 'Can implement CachedValidValuesGeneratorBase in PowerShell' {
|
||||
Get-TestValidateSetPS4 -Param1 "TestString1" -ErrorAction SilentlyContinue | Should BeExactly "TestString1"
|
||||
}
|
||||
|
||||
It 'Can implement CachedValidValuesGeneratorBase with cache expiration in PowerShell' {
|
||||
Get-TestValidateSetPS5 -Param1 "TestString1" -ErrorAction SilentlyContinue | Should BeExactly "TestString1"
|
||||
Get-TestValidateSetPS5 -Param1 "TestString1" -ErrorAction SilentlyContinue | Should BeExactly "TestString1"
|
||||
Start-Sleep 3
|
||||
Get-TestValidateSetPS5 -Param1 "TestString2" -ErrorAction SilentlyContinue | Should BeExactly "TestString2"
|
||||
}
|
||||
}
|
||||
}
|
||||
Describe 'Attributes Test' -Tags "CI" {
|
||||
|
||||
BeforeAll {
|
||||
$dummyAttributesSource = @'
|
||||
using System.Management.Automation;
|
||||
namespace Dummy
|
||||
{
|
||||
public class DoubleStringTransformationAttribute : ArgumentTransformationAttribute
|
||||
{
|
||||
public override object Transform(EngineIntrinsics engineIntrinsics, object inputData)
|
||||
{
|
||||
string arg = inputData as string;
|
||||
if (arg != null)
|
||||
{
|
||||
return arg + arg;
|
||||
}
|
||||
return inputData;
|
||||
}
|
||||
}
|
||||
|
||||
public class AppendStringTransformationAttribute : ArgumentTransformationAttribute
|
||||
{
|
||||
public override object Transform(EngineIntrinsics engineIntrinsics, object inputData)
|
||||
{
|
||||
string arg = inputData as string;
|
||||
if (arg != null)
|
||||
{
|
||||
return arg + "___";
|
||||
}
|
||||
return inputData;
|
||||
}
|
||||
}
|
||||
|
||||
public class DoubleInt : ArgumentTransformationAttribute
|
||||
{
|
||||
public override object Transform(EngineIntrinsics engineIntrinsics, object inputData)
|
||||
{
|
||||
int? arg = inputData as int?;
|
||||
if (arg != null)
|
||||
{
|
||||
return arg + arg;
|
||||
}
|
||||
return inputData;
|
||||
}
|
||||
}
|
||||
}
|
||||
'@
|
||||
Add-Type -TypeDefinition $dummyAttributesSource
|
||||
}
|
||||
|
||||
|
||||
|
||||
Context 'Property.Instance.ValidateSet.String' {
|
||||
class C1 { [ValidateSet("Present", "Absent")][string]$Ensure }
|
||||
# This call should not throw exception
|
||||
[C1]::new().Ensure = "Present"
|
||||
|
||||
It 'Error when ValidateSet should be ExceptionWhenSetting' {
|
||||
try
|
||||
{
|
||||
[C1]::new().Ensure = "foo"
|
||||
throw "Exception expected"
|
||||
}
|
||||
catch
|
||||
{
|
||||
$_.FullyQualifiedErrorId | Should be 'ExceptionWhenSetting'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Context 'Property.Static.ValidateSet.String' {
|
||||
class C1 { static [ValidateSet("Present", "Absent")][string]$Ensure }
|
||||
# This call should not throw exception
|
||||
[C1]::Ensure = "Present"
|
||||
It 'Error when ValidateSet should be ExceptionWhenSetting'{
|
||||
try {
|
||||
[C1]::Ensure = "foo"
|
||||
throw "Exception expected"
|
||||
}
|
||||
catch {
|
||||
$_.FullyQualifiedErrorId | Should be 'ExceptionWhenSetting'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Context 'Property.Instance.ValidateRange.Int' {
|
||||
class C1 { [ValidateRange(1, 10)][int]$f }
|
||||
# This call should not throw exception
|
||||
[C1]::new().f = 10
|
||||
[C1]::new().f = 1
|
||||
It 'Error when ValidateSet should be ExceptionWhenSetting'{
|
||||
try {
|
||||
[C1]::new().f = 20
|
||||
throw "Exception expected"
|
||||
}
|
||||
catch {
|
||||
$_.FullyQualifiedErrorId | Should be 'ExceptionWhenSetting'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Context 'Property.Static.ValidateRange.Int' {
|
||||
class C1 { static [ValidateRange(1, 10)][int]$f }
|
||||
# This call should not throw exception
|
||||
[C1]::f = 5
|
||||
It 'Error when ValidateSet should be ExceptionWhenSetting'{
|
||||
try {
|
||||
[C1]::f = 20
|
||||
throw "Exception expected"
|
||||
}
|
||||
catch {
|
||||
$_.FullyQualifiedErrorId | Should be 'ExceptionWhenSetting'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Context 'Property.Static.ValidateSet.ImplicitObject' {
|
||||
class C1 { static [ValidateSet("abc", 5)]$o }
|
||||
# This call should not throw exception
|
||||
[C1]::o = "abc"
|
||||
[C1]::o = 5
|
||||
It 'Error when ValidateSet should be ExceptionWhenSetting'{
|
||||
try {
|
||||
[C1]::o = 1
|
||||
throw "Exception expected"
|
||||
}
|
||||
catch {
|
||||
$_.FullyQualifiedErrorId | Should be 'ExceptionWhenSetting'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# We use [scriptblock]::Create() here to allow SuiteSetup add Dummy.Transformation type to
|
||||
# the scope. Otherwise, we will need to have all classes for attributes in parse time.
|
||||
#
|
||||
# Invoke() returns an array, we need first element of it.
|
||||
#
|
||||
|
||||
Context 'Property.Instance.Transformation.ImplicitObject' {
|
||||
$c = [scriptblock]::Create('class C1 { [Dummy.DoubleStringTransformation()]$arg }; [C1]::new()').Invoke()[0]
|
||||
|
||||
It 'Implicitly Transform to 100' {
|
||||
$c.arg = 100
|
||||
$c.arg | should be 100
|
||||
}
|
||||
It 'Implicitly Transform to foo' {
|
||||
$c.arg = "foo"
|
||||
$c.arg | should be "foofoo"
|
||||
}
|
||||
}
|
||||
|
||||
Context 'Property.Instance.Transformation.String' {
|
||||
$c = [scriptblock]::Create('class C1 { [Dummy.DoubleStringTransformation()][string]$arg }; [C1]::new()').Invoke()[0]
|
||||
It 'set to foo' {
|
||||
$c.arg = "foo"
|
||||
$c.arg | should be "foofoo"
|
||||
}
|
||||
}
|
||||
|
||||
Context Property.Instance.Transformation.Int {
|
||||
$c = [scriptblock]::Create('class C1 { [Dummy.DoubleInt()][int]$arg }; [C1]::new()').Invoke()[0]
|
||||
It 'arg should be 200' {
|
||||
$c.arg = 100
|
||||
$c.arg | should be 200
|
||||
}
|
||||
It 'Set to string should fail with ExceptionWhenSetting' {
|
||||
try {
|
||||
$c.arg = "abc"
|
||||
throw "Exception expected"
|
||||
}
|
||||
catch {
|
||||
$_.FullyQualifiedErrorId | Should be 'ExceptionWhenSetting'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Context Property.Instance.Transformation.Nullable {
|
||||
$c = [scriptblock]::Create('class C1 { [Nullable[int]][Dummy.DoubleStringTransformation()]$arg }; [C1]::new()').Invoke()[0]
|
||||
It 'arg should be 100' {
|
||||
$c.arg = 100
|
||||
$c.arg | should be 100
|
||||
}
|
||||
}
|
||||
|
||||
Context Property.Instance.Transformation.Order {
|
||||
$c = [scriptblock]::Create('class C1 { [Dummy.DoubleStringTransformation()][Dummy.AppendStringTransformation()]$arg }; [C1]::new()').Invoke()[0]
|
||||
It 'arg should be 100' {
|
||||
$c.arg = 100
|
||||
$c.arg | should be 100
|
||||
}
|
||||
|
||||
It 'arg should be foo___foo___g' {
|
||||
$c.arg = "foo"
|
||||
$c.arg | should be "foo___foo___"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Describe 'Type resolution with attributes' -Tag "CI" {
|
||||
# There is kind of a collision between names
|
||||
# System.Diagnostics.Tracing.EventSource
|
||||
# System.Diagnostics.Tracing.EventSourceAttribute
|
||||
# We need to make sure that we resolve type name to the right class at each usage
|
||||
Context 'Name collision' {
|
||||
|
||||
It 'Resolve System.Diagnostics.Tracing.EventSource to Attribute and to Type in the different contexts' {
|
||||
[System.Diagnostics.Tracing.EventSource(Name = "MyPSEventSource")]
|
||||
class MyEventSource : System.Diagnostics.Tracing.EventSource
|
||||
{
|
||||
[void] OnEvent([string]$Message) {}
|
||||
}
|
||||
|
||||
[MyEventSource]::new() | Should Not Be $null
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Describe 'ValidateSet support a dynamically generated set' -Tag "CI" {
|
||||
|
||||
Context 'C# tests' {
|
||||
|
||||
BeforeAll {
|
||||
$a=@'
|
||||
using System;
|
||||
using System.Management.Automation;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Test.Language {
|
||||
|
||||
[Cmdlet(VerbsCommon.Get, "TestValidateSet0")]
|
||||
public class TestValidateSetCommand0 : PSCmdlet
|
||||
{
|
||||
[Parameter]
|
||||
[ValidateSet(typeof(PSCmdlet))]
|
||||
public string Param1;
|
||||
|
||||
protected override void EndProcessing()
|
||||
{
|
||||
WriteObject(Param1);
|
||||
}
|
||||
}
|
||||
|
||||
[Cmdlet(VerbsCommon.Get, "TestValidateSet4")]
|
||||
public class TestValidateSetCommand4 : PSCmdlet
|
||||
{
|
||||
[Parameter]
|
||||
[ValidateSet(typeof(GenValuesForParam))]
|
||||
public string Param1;
|
||||
|
||||
protected override void EndProcessing()
|
||||
{
|
||||
WriteObject(Param1);
|
||||
}
|
||||
}
|
||||
|
||||
[Cmdlet(VerbsCommon.Get, "TestValidateSet5")]
|
||||
public class TestValidateSetCommand5 : PSCmdlet
|
||||
{
|
||||
[Parameter]
|
||||
[ValidateSet(typeof(GenValuesForParamNull))]
|
||||
public string Param1;
|
||||
|
||||
protected override void EndProcessing()
|
||||
{
|
||||
WriteObject(Param1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Implement of test IValidateSetValuesGenerator
|
||||
public class GenValuesForParamNull : IValidateSetValuesGenerator
|
||||
{
|
||||
public string[] GetValidValues()
|
||||
{
|
||||
var testValues = new string[] {"Test1","TestString1","Test2"};
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public class GenValuesForParam : IValidateSetValuesGenerator
|
||||
{
|
||||
public string[] GetValidValues()
|
||||
{
|
||||
var testValues = new string[] {"Test1","TestString1","Test2"};
|
||||
return testValues;
|
||||
}
|
||||
}
|
||||
}
|
||||
'@
|
||||
|
||||
$cls = Add-Type -TypeDefinition $a -PassThru | select -First 1
|
||||
$testModule = Import-Module $cls.Assembly -PassThru
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
Remove-Module -ModuleInfo $testModule
|
||||
}
|
||||
|
||||
It 'Throw if IValidateSetValuesGenerator is not implemented' {
|
||||
{ Get-TestValidateSet0 -Param1 "TestString" -ErrorAction Stop } | ShouldBeErrorId "Argument"
|
||||
}
|
||||
|
||||
It 'Dynamically generated set works in C# with default (immediate) cache expire' {
|
||||
Get-TestValidateSet4 -Param1 "TestString1" -ErrorAction SilentlyContinue | Should BeExactly "TestString1"
|
||||
}
|
||||
|
||||
It 'Empty dynamically generated set throws in C#' {
|
||||
$exc = {
|
||||
Get-TestValidateSet5 -Param1 "TestString1" -ErrorAction Stop
|
||||
} | ShouldBeErrorId "ParameterArgumentValidationError,Test.Language.TestValidateSetCommand5"
|
||||
$exc.Exception.InnerException.ErrorRecord.FullyQualifiedErrorId | Should BeExactly "ValidateSetGeneratedValidValuesListIsNull"
|
||||
}
|
||||
}
|
||||
|
||||
Context 'Powershell tests' {
|
||||
|
||||
BeforeAll {
|
||||
class GenValuesForParam : System.Management.Automation.IValidateSetValuesGenerator {
|
||||
[String[]] GetValidValues() {
|
||||
|
||||
return [string[]]("Test1","TestString1","Test2")
|
||||
}
|
||||
}
|
||||
|
||||
class GenValuesForParamNull : System.Management.Automation.IValidateSetValuesGenerator {
|
||||
[String[]] GetValidValues() {
|
||||
|
||||
return [string[]]$null
|
||||
}
|
||||
}
|
||||
|
||||
# Return '$testValues2' and after 2 seconds after first use return another array '$testValues1'.
|
||||
class GenValuesForParamCache1 : System.Management.Automation.IValidateSetValuesGenerator {
|
||||
[String[]] GetValidValues() {
|
||||
|
||||
$testValues1 = "Test11","TestString11","Test22"
|
||||
$testValues2 = "Test11","TestString22","Test22"
|
||||
|
||||
$currentTime = [DateTime]::Now
|
||||
if ([DateTime]::Compare([GenValuesForParamCache1]::cacheTime, $currentTime) -le 0)
|
||||
{
|
||||
$testValues = $testValues1;
|
||||
}
|
||||
else
|
||||
{
|
||||
$testValues = $testValues2;
|
||||
}
|
||||
return [string[]]$testValues
|
||||
}
|
||||
|
||||
static [DateTime] $cacheTime = [DateTime]::Now.AddSeconds(2);
|
||||
}
|
||||
|
||||
function Get-TestValidateSetPS4
|
||||
{
|
||||
[CmdletBinding()]
|
||||
Param
|
||||
(
|
||||
[ValidateSet([GenValuesForParam])]
|
||||
$Param1
|
||||
)
|
||||
|
||||
$Param1
|
||||
}
|
||||
|
||||
function Get-TestValidateSetPS5
|
||||
{
|
||||
[CmdletBinding()]
|
||||
Param
|
||||
(
|
||||
[ValidateSet([GenValuesForParamNull])]
|
||||
$Param1
|
||||
)
|
||||
|
||||
$Param1
|
||||
}
|
||||
|
||||
function Get-TestValidateSetPS6
|
||||
{
|
||||
[CmdletBinding()]
|
||||
Param
|
||||
(
|
||||
[ValidateSet([UnImplementedGeneratorOfValues])]
|
||||
$Param1
|
||||
)
|
||||
|
||||
$Param1
|
||||
}
|
||||
}
|
||||
|
||||
It 'Dynamically generated set works in PowerShell script with default (immediate) cache expire' {
|
||||
Get-TestValidateSetPS4 -Param1 "TestString1" -ErrorAction SilentlyContinue | Should BeExactly "TestString1"
|
||||
}
|
||||
|
||||
It 'Get the appropriate error message' {
|
||||
{Get-TestValidateSetPS4 -Param1 "TestStringWrong" -ErrorAction Stop} | ShouldBeErrorId "ParameterArgumentValidationError,Get-TestValidateSetPS4"
|
||||
}
|
||||
|
||||
It 'Empty dynamically generated set throws in PowerShell script' {
|
||||
$exc = {
|
||||
Get-TestValidateSetPS5 -Param1 "TestString1" -ErrorAction Stop
|
||||
} | ShouldBeErrorId "ParameterArgumentValidationError,Get-TestValidateSetPS5"
|
||||
$exc.Exception.InnerException.ErrorRecord.FullyQualifiedErrorId | Should BeExactly "ValidateSetGeneratedValidValuesListIsNull"
|
||||
}
|
||||
|
||||
It 'Unimplemented valid values generator type throws in PowerShell script' {
|
||||
{
|
||||
Get-TestValidateSetPS6 -Param1 "AnyTestString" -ErrorAction Stop
|
||||
} | ShouldBeErrorId "TypeNotFound"
|
||||
}
|
||||
}
|
||||
|
||||
Context 'CachedValidValuesGeneratorBase class tests' {
|
||||
|
||||
BeforeAll {
|
||||
class GenValuesForParam : System.Management.Automation.CachedValidValuesGeneratorBase {
|
||||
GenValuesForParam() : base(300) {
|
||||
}
|
||||
|
||||
[String[]] GenerateValidValues() {
|
||||
|
||||
return [string[]]("Test1","TestString1","Test2")
|
||||
}
|
||||
}
|
||||
|
||||
class GenValuesWithExpiration : System.Management.Automation.CachedValidValuesGeneratorBase {
|
||||
GenValuesWithExpiration() : base(2) {
|
||||
}
|
||||
|
||||
Static [bool] $temp = $true;
|
||||
|
||||
[String[]] GenerateValidValues() {
|
||||
|
||||
if ([GenValuesWithExpiration]::temp) {
|
||||
[GenValuesWithExpiration]::temp = $false
|
||||
return [string[]]("Test1","TestString1","Test2")
|
||||
} else {
|
||||
[GenValuesWithExpiration]::temp = $true
|
||||
return [string[]]("Test1","TestString2","Test2")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function Get-TestValidateSetPS4
|
||||
{
|
||||
[CmdletBinding()]
|
||||
Param
|
||||
(
|
||||
[ValidateSet([GenValuesForParam])]
|
||||
$Param1
|
||||
)
|
||||
|
||||
$Param1
|
||||
}
|
||||
|
||||
function Get-TestValidateSetPS5
|
||||
{
|
||||
[CmdletBinding()]
|
||||
Param
|
||||
(
|
||||
[ValidateSet([GenValuesWithExpiration])]
|
||||
$Param1
|
||||
)
|
||||
|
||||
$Param1
|
||||
}
|
||||
}
|
||||
|
||||
It 'Can implement CachedValidValuesGeneratorBase in PowerShell' {
|
||||
Get-TestValidateSetPS4 -Param1 "TestString1" -ErrorAction SilentlyContinue | Should BeExactly "TestString1"
|
||||
}
|
||||
|
||||
It 'Can implement CachedValidValuesGeneratorBase with cache expiration in PowerShell' {
|
||||
Get-TestValidateSetPS5 -Param1 "TestString1" -ErrorAction SilentlyContinue | Should BeExactly "TestString1"
|
||||
Get-TestValidateSetPS5 -Param1 "TestString1" -ErrorAction SilentlyContinue | Should BeExactly "TestString1"
|
||||
Start-Sleep 3
|
||||
Get-TestValidateSetPS5 -Param1 "TestString2" -ErrorAction SilentlyContinue | Should BeExactly "TestString2"
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,145 +1,145 @@
|
||||
Describe 'Break statements with classes' -Tags "CI" {
|
||||
|
||||
function Get-Errors([string]$sourceCode) {
|
||||
$tokens = $null
|
||||
$errors = $null
|
||||
$ast = [System.Management.Automation.Language.Parser]::ParseInput($sourceCode, [ref] $tokens, [ref] $errors)
|
||||
return $errors
|
||||
}
|
||||
|
||||
Context 'break is inside a class method' {
|
||||
It 'reports parse error for break on non-existing label' {
|
||||
$errors = Get-Errors @'
|
||||
class A
|
||||
{
|
||||
static [int] foo()
|
||||
{
|
||||
while (1) { break some_label }
|
||||
return 1
|
||||
}
|
||||
}
|
||||
'@
|
||||
$errors.Count | Should be 1
|
||||
$errors[0].ErrorId | Should be 'LabelNotFound'
|
||||
}
|
||||
|
||||
It 'reports parse error for break outside of loop' {
|
||||
$errors = Get-Errors @'
|
||||
class A
|
||||
{
|
||||
static [int] foo()
|
||||
{
|
||||
break some_label
|
||||
return 1
|
||||
}
|
||||
}
|
||||
'@
|
||||
$errors.Count | Should be 1
|
||||
$errors[0].ErrorId | Should be 'LabelNotFound'
|
||||
}
|
||||
|
||||
It 'work fine, when break is legit' {
|
||||
class C
|
||||
{
|
||||
static [int] foo()
|
||||
{
|
||||
foreach ($i in 101..102) {
|
||||
break
|
||||
}
|
||||
return $i
|
||||
}
|
||||
}
|
||||
[C]::foo() | Should be 101
|
||||
}
|
||||
}
|
||||
|
||||
Context 'continue inside a class method' {
|
||||
It 'reports parse error for continue on non-existing label' {
|
||||
$errors = Get-Errors @'
|
||||
class A
|
||||
{
|
||||
static [int] foo()
|
||||
{
|
||||
while (1) { continue some_label }
|
||||
return 1
|
||||
}
|
||||
}
|
||||
'@
|
||||
$errors.Count | Should be 1
|
||||
$errors[0].ErrorId | Should be 'LabelNotFound'
|
||||
}
|
||||
}
|
||||
|
||||
Context 'break is in called function' {
|
||||
It 'doesn''t terminate caller method' -Skip {
|
||||
|
||||
function ImBreak() {
|
||||
break
|
||||
}
|
||||
|
||||
class C
|
||||
{
|
||||
static [int] getInt()
|
||||
{
|
||||
ImBreak
|
||||
return 123
|
||||
}
|
||||
}
|
||||
|
||||
$canary = $false
|
||||
try {
|
||||
[C]::getInt() | Should Be 123
|
||||
$canary = $true
|
||||
} finally {
|
||||
$canary | Should be $true
|
||||
}
|
||||
}
|
||||
|
||||
It 'doesn''t allow goto outside of function with break' -Skip {
|
||||
|
||||
function ImBreak() {
|
||||
break label1
|
||||
}
|
||||
|
||||
class C
|
||||
{
|
||||
static [int] getInt()
|
||||
{
|
||||
$count = 123
|
||||
:label1
|
||||
foreach ($i in 0..3) {
|
||||
foreach ($i in 0..3) {
|
||||
ImBreak
|
||||
$count++
|
||||
}
|
||||
}
|
||||
return $count
|
||||
}
|
||||
}
|
||||
|
||||
$canary = $false
|
||||
try {
|
||||
[C]::getInt() | Should Be (123 + 4*4)
|
||||
$canary = $true
|
||||
} finally {
|
||||
$canary | Should be $true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Context 'no classes involved' {
|
||||
|
||||
It 'doesn''t report parse error for non-existing label' {
|
||||
$errors = Get-Errors @'
|
||||
function foo()
|
||||
{
|
||||
while (1) { break some_label }
|
||||
while (1) { continue another_label }
|
||||
return 1
|
||||
}
|
||||
'@
|
||||
$errors.Count | Should be 0
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Describe 'Break statements with classes' -Tags "CI" {
|
||||
|
||||
function Get-Errors([string]$sourceCode) {
|
||||
$tokens = $null
|
||||
$errors = $null
|
||||
$ast = [System.Management.Automation.Language.Parser]::ParseInput($sourceCode, [ref] $tokens, [ref] $errors)
|
||||
return $errors
|
||||
}
|
||||
|
||||
Context 'break is inside a class method' {
|
||||
It 'reports parse error for break on non-existing label' {
|
||||
$errors = Get-Errors @'
|
||||
class A
|
||||
{
|
||||
static [int] foo()
|
||||
{
|
||||
while (1) { break some_label }
|
||||
return 1
|
||||
}
|
||||
}
|
||||
'@
|
||||
$errors.Count | Should be 1
|
||||
$errors[0].ErrorId | Should be 'LabelNotFound'
|
||||
}
|
||||
|
||||
It 'reports parse error for break outside of loop' {
|
||||
$errors = Get-Errors @'
|
||||
class A
|
||||
{
|
||||
static [int] foo()
|
||||
{
|
||||
break some_label
|
||||
return 1
|
||||
}
|
||||
}
|
||||
'@
|
||||
$errors.Count | Should be 1
|
||||
$errors[0].ErrorId | Should be 'LabelNotFound'
|
||||
}
|
||||
|
||||
It 'work fine, when break is legit' {
|
||||
class C
|
||||
{
|
||||
static [int] foo()
|
||||
{
|
||||
foreach ($i in 101..102) {
|
||||
break
|
||||
}
|
||||
return $i
|
||||
}
|
||||
}
|
||||
[C]::foo() | Should be 101
|
||||
}
|
||||
}
|
||||
|
||||
Context 'continue inside a class method' {
|
||||
It 'reports parse error for continue on non-existing label' {
|
||||
$errors = Get-Errors @'
|
||||
class A
|
||||
{
|
||||
static [int] foo()
|
||||
{
|
||||
while (1) { continue some_label }
|
||||
return 1
|
||||
}
|
||||
}
|
||||
'@
|
||||
$errors.Count | Should be 1
|
||||
$errors[0].ErrorId | Should be 'LabelNotFound'
|
||||
}
|
||||
}
|
||||
|
||||
Context 'break is in called function' {
|
||||
It 'doesn''t terminate caller method' -Skip {
|
||||
|
||||
function ImBreak() {
|
||||
break
|
||||
}
|
||||
|
||||
class C
|
||||
{
|
||||
static [int] getInt()
|
||||
{
|
||||
ImBreak
|
||||
return 123
|
||||
}
|
||||
}
|
||||
|
||||
$canary = $false
|
||||
try {
|
||||
[C]::getInt() | Should Be 123
|
||||
$canary = $true
|
||||
} finally {
|
||||
$canary | Should be $true
|
||||
}
|
||||
}
|
||||
|
||||
It 'doesn''t allow goto outside of function with break' -Skip {
|
||||
|
||||
function ImBreak() {
|
||||
break label1
|
||||
}
|
||||
|
||||
class C
|
||||
{
|
||||
static [int] getInt()
|
||||
{
|
||||
$count = 123
|
||||
:label1
|
||||
foreach ($i in 0..3) {
|
||||
foreach ($i in 0..3) {
|
||||
ImBreak
|
||||
$count++
|
||||
}
|
||||
}
|
||||
return $count
|
||||
}
|
||||
}
|
||||
|
||||
$canary = $false
|
||||
try {
|
||||
[C]::getInt() | Should Be (123 + 4*4)
|
||||
$canary = $true
|
||||
} finally {
|
||||
$canary | Should be $true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Context 'no classes involved' {
|
||||
|
||||
It 'doesn''t report parse error for non-existing label' {
|
||||
$errors = Get-Errors @'
|
||||
function foo()
|
||||
{
|
||||
while (1) { break some_label }
|
||||
while (1) { continue another_label }
|
||||
return 1
|
||||
}
|
||||
'@
|
||||
$errors.Count | Should be 0
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,349 +1,349 @@
|
||||
Describe 'Exceptions flow for classes' -Tags "CI" {
|
||||
|
||||
$canaryHashtable = @{}
|
||||
|
||||
$iss = [initialsessionstate]::CreateDefault()
|
||||
$iss.Variables.Add([System.Management.Automation.Runspaces.SessionStateVariableEntry]::new('canaryHashtable', $canaryHashtable, $null))
|
||||
$iss.Commands.Add([System.Management.Automation.Runspaces.SessionStateFunctionEntry]::new('Get-Canary', '$canaryHashtable'))
|
||||
$ps = [powershell]::Create($iss)
|
||||
|
||||
BeforeEach {
|
||||
$canaryHashtable.Clear()
|
||||
$ps.Commands.Clear()
|
||||
}
|
||||
|
||||
Context 'All calls are inside classes' {
|
||||
|
||||
It 'does not execute statements after instance method with exception' {
|
||||
|
||||
# Put try-catch outside to avoid try-catch logic altering analysis
|
||||
try {
|
||||
|
||||
$ps.AddScript( @'
|
||||
class C
|
||||
{
|
||||
[void] m1()
|
||||
{
|
||||
$canaryHashtable = Get-Canary
|
||||
$canaryHashtable['canary'] = 42
|
||||
$this.ImThrow()
|
||||
$canaryHashtable['canary'] = 100
|
||||
}
|
||||
|
||||
[void] ImThrow()
|
||||
{
|
||||
throw 'I told you'
|
||||
}
|
||||
}
|
||||
[C]::new().m1()
|
||||
'@).Invoke()
|
||||
|
||||
} catch {}
|
||||
|
||||
$canaryHashtable['canary'] | Should Be 42
|
||||
}
|
||||
|
||||
It 'does not execute statements after static method with exception' {
|
||||
|
||||
# Put try-catch outside to avoid try-catch logic altering analysis
|
||||
try {
|
||||
|
||||
$ps.AddScript( @'
|
||||
class C
|
||||
{
|
||||
static [void] s1()
|
||||
{
|
||||
$canaryHashtable = Get-Canary
|
||||
$canaryHashtable['canary'] = 43
|
||||
[C]::ImThrow()
|
||||
$canaryHashtable['canary'] = 100
|
||||
}
|
||||
|
||||
static [void] ImThrow()
|
||||
{
|
||||
1 / 0
|
||||
}
|
||||
}
|
||||
[C]::s1()
|
||||
'@).Invoke()
|
||||
|
||||
} catch {}
|
||||
|
||||
$canaryHashtable['canary'] | Should Be 43
|
||||
}
|
||||
|
||||
It 'does not execute statements after instance method with exception and deep stack' {
|
||||
|
||||
# Put try-catch outside to avoid try-catch logic altering analysis
|
||||
try {
|
||||
|
||||
$ps.AddScript( @'
|
||||
class C
|
||||
{
|
||||
[void] m1()
|
||||
{
|
||||
$canaryHashtable = Get-Canary
|
||||
$canaryHashtable['canary'] = 1
|
||||
$this.m2()
|
||||
$canaryHashtable['canary'] = -6101
|
||||
}
|
||||
|
||||
[void] m2()
|
||||
{
|
||||
$canaryHashtable = Get-Canary
|
||||
$canaryHashtable['canary'] += 10
|
||||
$this.m3()
|
||||
$canaryHashtable['canary'] = -6102
|
||||
}
|
||||
|
||||
[void] m3()
|
||||
{
|
||||
$canaryHashtable = Get-Canary
|
||||
$canaryHashtable['canary'] += 100
|
||||
$this.m4()
|
||||
$canaryHashtable['canary'] = -6103
|
||||
}
|
||||
|
||||
[void] m4()
|
||||
{
|
||||
$canaryHashtable = Get-Canary
|
||||
$canaryHashtable['canary'] += 1000
|
||||
$this.ImThrow()
|
||||
$canaryHashtable['canary'] = -6104
|
||||
}
|
||||
|
||||
[void] ImThrow()
|
||||
{
|
||||
$canaryHashtable = Get-Canary
|
||||
$canaryHashtable['canary'] += 10000
|
||||
|
||||
1 / 0
|
||||
}
|
||||
}
|
||||
[C]::new().m1()
|
||||
'@).Invoke()
|
||||
|
||||
} catch {}
|
||||
|
||||
$canaryHashtable['canary'] | Should Be 11111
|
||||
}
|
||||
}
|
||||
|
||||
Context 'Class method call PS function' {
|
||||
|
||||
$body = @'
|
||||
class C
|
||||
{
|
||||
[void] m1()
|
||||
{
|
||||
m2
|
||||
}
|
||||
|
||||
static [void] s1()
|
||||
{
|
||||
s2
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function m2()
|
||||
{
|
||||
$canary = Get-Canary
|
||||
$canary['canaryM'] = 45
|
||||
ImThrow
|
||||
$canary['canaryM'] = 100
|
||||
}
|
||||
|
||||
function s2()
|
||||
{
|
||||
$canary = Get-Canary
|
||||
$canary['canaryS'] = 46
|
||||
CallImThrow
|
||||
$canary['canaryS'] = 100
|
||||
}
|
||||
|
||||
function CallImThrow()
|
||||
{
|
||||
ImThrow
|
||||
}
|
||||
|
||||
function ImThrow()
|
||||
{
|
||||
1 / 0
|
||||
}
|
||||
|
||||
'@
|
||||
|
||||
It 'does not execute statements after function with exception called from instance method' {
|
||||
|
||||
# Put try-catch outside to avoid try-catch logic altering analysis
|
||||
try {
|
||||
|
||||
$ps.AddScript($body).Invoke()
|
||||
$ps.AddScript('$c = [C]::new(); $c.m1()').Invoke()
|
||||
|
||||
} catch {}
|
||||
|
||||
$canaryHashtable['canaryM'] | Should Be 45
|
||||
}
|
||||
|
||||
It 'does not execute statements after function with exception called from static method' {
|
||||
|
||||
# Put try-catch outside to avoid try-catch logic altering analysis
|
||||
try {
|
||||
|
||||
$ps.AddScript($body).Invoke()
|
||||
$ps.AddScript('[C]::s1()').Invoke()
|
||||
|
||||
} catch {}
|
||||
|
||||
$canaryHashtable['canaryS'] | Should Be 46
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Context "No class is involved" {
|
||||
It "functions calls continue execution by default" {
|
||||
|
||||
try {
|
||||
|
||||
$ps.AddScript( @'
|
||||
|
||||
$canaryHashtable = Get-Canary
|
||||
function foo() { 1 / 0; $canaryHashtable['canary'] += 10 }
|
||||
$canaryHashtable['canary'] = 1
|
||||
foo
|
||||
$canaryHashtable['canary'] += 100
|
||||
|
||||
'@).Invoke()
|
||||
|
||||
} catch {}
|
||||
|
||||
$canaryHashtable['canary'] | Should Be 111
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Describe "Exception error position" -Tags "CI" {
|
||||
class MSFT_3090412
|
||||
{
|
||||
static f1() { [MSFT_3090412]::bar = 42 }
|
||||
static f2() { throw "an error in f2" }
|
||||
static f3() { "".Substring(0, 10) }
|
||||
static f4() { dir nosuchfile -ea Stop }
|
||||
}
|
||||
|
||||
It "Setting a property that doesn't exist" {
|
||||
try {
|
||||
[MSFT_3090412]::f1()
|
||||
throw "f1 should have thrown"
|
||||
} catch {
|
||||
$_.InvocationInfo.Line | Should Match ([regex]::Escape('[MSFT_3090412]::bar = 42'))
|
||||
}
|
||||
}
|
||||
|
||||
It "Throwing an exception" {
|
||||
try {
|
||||
[MSFT_3090412]::f2()
|
||||
throw "f2 should have thrown"
|
||||
} catch {
|
||||
$_.InvocationInfo.Line | Should Match ([regex]::Escape('throw "an error in f2"'))
|
||||
}
|
||||
}
|
||||
|
||||
It "Calling a .Net method that throws" {
|
||||
try {
|
||||
[MSFT_3090412]::f3()
|
||||
throw "f3 should have thrown"
|
||||
} catch {
|
||||
$_.InvocationInfo.Line | Should Match ([regex]::Escape('"".Substring(0, 10)'))
|
||||
}
|
||||
}
|
||||
|
||||
It "Terminating error" {
|
||||
try {
|
||||
[MSFT_3090412]::f4()
|
||||
throw "f4 should have thrown"
|
||||
} catch {
|
||||
$_.InvocationInfo.Line | Should Match ([regex]::Escape('dir nosuchfile -ea Stop'))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Describe "Exception from initializer" -Tags "CI" {
|
||||
class MSFT_6397334a
|
||||
{
|
||||
[int]$a = "zz"
|
||||
MSFT_6397334a() {}
|
||||
}
|
||||
|
||||
class MSFT_6397334b
|
||||
{
|
||||
[int]$a = "zz"
|
||||
}
|
||||
|
||||
class MSFT_6397334c
|
||||
{
|
||||
static [int]$a = "zz"
|
||||
static MSFT_6397334a() {}
|
||||
}
|
||||
|
||||
class MSFT_6397334d
|
||||
{
|
||||
static [int]$a = "zz"
|
||||
}
|
||||
|
||||
It "instance member w/ ctor" {
|
||||
try {
|
||||
[MSFT_6397334a]::new()
|
||||
throw "[MSFT_6397334a]::new() should have thrown"
|
||||
}
|
||||
catch
|
||||
{
|
||||
$e = $_
|
||||
$e.FullyQualifiedErrorId | Should Be InvalidCastFromStringToInteger
|
||||
$e.InvocationInfo.Line | Should Match 'a = "zz"'
|
||||
}
|
||||
}
|
||||
|
||||
It "instance member w/o ctor" {
|
||||
try {
|
||||
[MSFT_6397334b]::new()
|
||||
throw "[MSFT_6397334b]::new() should have thrown"
|
||||
}
|
||||
catch
|
||||
{
|
||||
$e = $_
|
||||
$e.FullyQualifiedErrorId | Should Be InvalidCastFromStringToInteger
|
||||
$e.InvocationInfo.Line | Should Match 'a = "zz"'
|
||||
}
|
||||
}
|
||||
|
||||
It "static member w/ ctor" {
|
||||
try {
|
||||
$null = [MSFT_6397334c]::a
|
||||
throw "No Exception!"
|
||||
}
|
||||
catch
|
||||
{
|
||||
$_.Exception | Should BeOfType System.TypeInitializationException
|
||||
$e = $_.Exception.InnerException.InnerException.ErrorRecord
|
||||
$e.FullyQualifiedErrorId | Should Be InvalidCastFromStringToInteger
|
||||
$e.InvocationInfo.Line | Should Match 'a = "zz"'
|
||||
}
|
||||
}
|
||||
|
||||
It "static member w/o ctor" {
|
||||
try {
|
||||
$null = [MSFT_6397334d]::a
|
||||
throw "No Exception!"
|
||||
}
|
||||
catch
|
||||
{
|
||||
$_.Exception | Should BeOfType System.TypeInitializationException
|
||||
$e = $_.Exception.InnerException.InnerException.ErrorRecord
|
||||
$e.FullyQualifiedErrorId | Should Be InvalidCastFromStringToInteger
|
||||
$e.InvocationInfo.Line | Should Match 'a = "zz"'
|
||||
}
|
||||
}
|
||||
}
|
||||
Describe 'Exceptions flow for classes' -Tags "CI" {
|
||||
|
||||
$canaryHashtable = @{}
|
||||
|
||||
$iss = [initialsessionstate]::CreateDefault()
|
||||
$iss.Variables.Add([System.Management.Automation.Runspaces.SessionStateVariableEntry]::new('canaryHashtable', $canaryHashtable, $null))
|
||||
$iss.Commands.Add([System.Management.Automation.Runspaces.SessionStateFunctionEntry]::new('Get-Canary', '$canaryHashtable'))
|
||||
$ps = [powershell]::Create($iss)
|
||||
|
||||
BeforeEach {
|
||||
$canaryHashtable.Clear()
|
||||
$ps.Commands.Clear()
|
||||
}
|
||||
|
||||
Context 'All calls are inside classes' {
|
||||
|
||||
It 'does not execute statements after instance method with exception' {
|
||||
|
||||
# Put try-catch outside to avoid try-catch logic altering analysis
|
||||
try {
|
||||
|
||||
$ps.AddScript( @'
|
||||
class C
|
||||
{
|
||||
[void] m1()
|
||||
{
|
||||
$canaryHashtable = Get-Canary
|
||||
$canaryHashtable['canary'] = 42
|
||||
$this.ImThrow()
|
||||
$canaryHashtable['canary'] = 100
|
||||
}
|
||||
|
||||
[void] ImThrow()
|
||||
{
|
||||
throw 'I told you'
|
||||
}
|
||||
}
|
||||
[C]::new().m1()
|
||||
'@).Invoke()
|
||||
|
||||
} catch {}
|
||||
|
||||
$canaryHashtable['canary'] | Should Be 42
|
||||
}
|
||||
|
||||
It 'does not execute statements after static method with exception' {
|
||||
|
||||
# Put try-catch outside to avoid try-catch logic altering analysis
|
||||
try {
|
||||
|
||||
$ps.AddScript( @'
|
||||
class C
|
||||
{
|
||||
static [void] s1()
|
||||
{
|
||||
$canaryHashtable = Get-Canary
|
||||
$canaryHashtable['canary'] = 43
|
||||
[C]::ImThrow()
|
||||
$canaryHashtable['canary'] = 100
|
||||
}
|
||||
|
||||
static [void] ImThrow()
|
||||
{
|
||||
1 / 0
|
||||
}
|
||||
}
|
||||
[C]::s1()
|
||||
'@).Invoke()
|
||||
|
||||
} catch {}
|
||||
|
||||
$canaryHashtable['canary'] | Should Be 43
|
||||
}
|
||||
|
||||
It 'does not execute statements after instance method with exception and deep stack' {
|
||||
|
||||
# Put try-catch outside to avoid try-catch logic altering analysis
|
||||
try {
|
||||
|
||||
$ps.AddScript( @'
|
||||
class C
|
||||
{
|
||||
[void] m1()
|
||||
{
|
||||
$canaryHashtable = Get-Canary
|
||||
$canaryHashtable['canary'] = 1
|
||||
$this.m2()
|
||||
$canaryHashtable['canary'] = -6101
|
||||
}
|
||||
|
||||
[void] m2()
|
||||
{
|
||||
$canaryHashtable = Get-Canary
|
||||
$canaryHashtable['canary'] += 10
|
||||
$this.m3()
|
||||
$canaryHashtable['canary'] = -6102
|
||||
}
|
||||
|
||||
[void] m3()
|
||||
{
|
||||
$canaryHashtable = Get-Canary
|
||||
$canaryHashtable['canary'] += 100
|
||||
$this.m4()
|
||||
$canaryHashtable['canary'] = -6103
|
||||
}
|
||||
|
||||
[void] m4()
|
||||
{
|
||||
$canaryHashtable = Get-Canary
|
||||
$canaryHashtable['canary'] += 1000
|
||||
$this.ImThrow()
|
||||
$canaryHashtable['canary'] = -6104
|
||||
}
|
||||
|
||||
[void] ImThrow()
|
||||
{
|
||||
$canaryHashtable = Get-Canary
|
||||
$canaryHashtable['canary'] += 10000
|
||||
|
||||
1 / 0
|
||||
}
|
||||
}
|
||||
[C]::new().m1()
|
||||
'@).Invoke()
|
||||
|
||||
} catch {}
|
||||
|
||||
$canaryHashtable['canary'] | Should Be 11111
|
||||
}
|
||||
}
|
||||
|
||||
Context 'Class method call PS function' {
|
||||
|
||||
$body = @'
|
||||
class C
|
||||
{
|
||||
[void] m1()
|
||||
{
|
||||
m2
|
||||
}
|
||||
|
||||
static [void] s1()
|
||||
{
|
||||
s2
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function m2()
|
||||
{
|
||||
$canary = Get-Canary
|
||||
$canary['canaryM'] = 45
|
||||
ImThrow
|
||||
$canary['canaryM'] = 100
|
||||
}
|
||||
|
||||
function s2()
|
||||
{
|
||||
$canary = Get-Canary
|
||||
$canary['canaryS'] = 46
|
||||
CallImThrow
|
||||
$canary['canaryS'] = 100
|
||||
}
|
||||
|
||||
function CallImThrow()
|
||||
{
|
||||
ImThrow
|
||||
}
|
||||
|
||||
function ImThrow()
|
||||
{
|
||||
1 / 0
|
||||
}
|
||||
|
||||
'@
|
||||
|
||||
It 'does not execute statements after function with exception called from instance method' {
|
||||
|
||||
# Put try-catch outside to avoid try-catch logic altering analysis
|
||||
try {
|
||||
|
||||
$ps.AddScript($body).Invoke()
|
||||
$ps.AddScript('$c = [C]::new(); $c.m1()').Invoke()
|
||||
|
||||
} catch {}
|
||||
|
||||
$canaryHashtable['canaryM'] | Should Be 45
|
||||
}
|
||||
|
||||
It 'does not execute statements after function with exception called from static method' {
|
||||
|
||||
# Put try-catch outside to avoid try-catch logic altering analysis
|
||||
try {
|
||||
|
||||
$ps.AddScript($body).Invoke()
|
||||
$ps.AddScript('[C]::s1()').Invoke()
|
||||
|
||||
} catch {}
|
||||
|
||||
$canaryHashtable['canaryS'] | Should Be 46
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Context "No class is involved" {
|
||||
It "functions calls continue execution by default" {
|
||||
|
||||
try {
|
||||
|
||||
$ps.AddScript( @'
|
||||
|
||||
$canaryHashtable = Get-Canary
|
||||
function foo() { 1 / 0; $canaryHashtable['canary'] += 10 }
|
||||
$canaryHashtable['canary'] = 1
|
||||
foo
|
||||
$canaryHashtable['canary'] += 100
|
||||
|
||||
'@).Invoke()
|
||||
|
||||
} catch {}
|
||||
|
||||
$canaryHashtable['canary'] | Should Be 111
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Describe "Exception error position" -Tags "CI" {
|
||||
class MSFT_3090412
|
||||
{
|
||||
static f1() { [MSFT_3090412]::bar = 42 }
|
||||
static f2() { throw "an error in f2" }
|
||||
static f3() { "".Substring(0, 10) }
|
||||
static f4() { dir nosuchfile -ea Stop }
|
||||
}
|
||||
|
||||
It "Setting a property that doesn't exist" {
|
||||
try {
|
||||
[MSFT_3090412]::f1()
|
||||
throw "f1 should have thrown"
|
||||
} catch {
|
||||
$_.InvocationInfo.Line | Should Match ([regex]::Escape('[MSFT_3090412]::bar = 42'))
|
||||
}
|
||||
}
|
||||
|
||||
It "Throwing an exception" {
|
||||
try {
|
||||
[MSFT_3090412]::f2()
|
||||
throw "f2 should have thrown"
|
||||
} catch {
|
||||
$_.InvocationInfo.Line | Should Match ([regex]::Escape('throw "an error in f2"'))
|
||||
}
|
||||
}
|
||||
|
||||
It "Calling a .Net method that throws" {
|
||||
try {
|
||||
[MSFT_3090412]::f3()
|
||||
throw "f3 should have thrown"
|
||||
} catch {
|
||||
$_.InvocationInfo.Line | Should Match ([regex]::Escape('"".Substring(0, 10)'))
|
||||
}
|
||||
}
|
||||
|
||||
It "Terminating error" {
|
||||
try {
|
||||
[MSFT_3090412]::f4()
|
||||
throw "f4 should have thrown"
|
||||
} catch {
|
||||
$_.InvocationInfo.Line | Should Match ([regex]::Escape('dir nosuchfile -ea Stop'))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Describe "Exception from initializer" -Tags "CI" {
|
||||
class MSFT_6397334a
|
||||
{
|
||||
[int]$a = "zz"
|
||||
MSFT_6397334a() {}
|
||||
}
|
||||
|
||||
class MSFT_6397334b
|
||||
{
|
||||
[int]$a = "zz"
|
||||
}
|
||||
|
||||
class MSFT_6397334c
|
||||
{
|
||||
static [int]$a = "zz"
|
||||
static MSFT_6397334a() {}
|
||||
}
|
||||
|
||||
class MSFT_6397334d
|
||||
{
|
||||
static [int]$a = "zz"
|
||||
}
|
||||
|
||||
It "instance member w/ ctor" {
|
||||
try {
|
||||
[MSFT_6397334a]::new()
|
||||
throw "[MSFT_6397334a]::new() should have thrown"
|
||||
}
|
||||
catch
|
||||
{
|
||||
$e = $_
|
||||
$e.FullyQualifiedErrorId | Should Be InvalidCastFromStringToInteger
|
||||
$e.InvocationInfo.Line | Should Match 'a = "zz"'
|
||||
}
|
||||
}
|
||||
|
||||
It "instance member w/o ctor" {
|
||||
try {
|
||||
[MSFT_6397334b]::new()
|
||||
throw "[MSFT_6397334b]::new() should have thrown"
|
||||
}
|
||||
catch
|
||||
{
|
||||
$e = $_
|
||||
$e.FullyQualifiedErrorId | Should Be InvalidCastFromStringToInteger
|
||||
$e.InvocationInfo.Line | Should Match 'a = "zz"'
|
||||
}
|
||||
}
|
||||
|
||||
It "static member w/ ctor" {
|
||||
try {
|
||||
$null = [MSFT_6397334c]::a
|
||||
throw "No Exception!"
|
||||
}
|
||||
catch
|
||||
{
|
||||
$_.Exception | Should BeOfType System.TypeInitializationException
|
||||
$e = $_.Exception.InnerException.InnerException.ErrorRecord
|
||||
$e.FullyQualifiedErrorId | Should Be InvalidCastFromStringToInteger
|
||||
$e.InvocationInfo.Line | Should Match 'a = "zz"'
|
||||
}
|
||||
}
|
||||
|
||||
It "static member w/o ctor" {
|
||||
try {
|
||||
$null = [MSFT_6397334d]::a
|
||||
throw "No Exception!"
|
||||
}
|
||||
catch
|
||||
{
|
||||
$_.Exception | Should BeOfType System.TypeInitializationException
|
||||
$e = $_.Exception.InnerException.InnerException.ErrorRecord
|
||||
$e.FullyQualifiedErrorId | Should Be InvalidCastFromStringToInteger
|
||||
$e.InvocationInfo.Line | Should Match 'a = "zz"'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,117 +1,117 @@
|
||||
Describe 'Misc Test' -Tags "CI" {
|
||||
|
||||
Context 'Where' {
|
||||
class C1 {
|
||||
[int[]] $Wheels = @(1,2,3);
|
||||
[string] Foo() {
|
||||
return (1..10).Where({ $PSItem -in $this.Wheels; }) -join ';'
|
||||
}
|
||||
|
||||
[string] Bar()
|
||||
{
|
||||
return (1..10 | Where-Object { $PSItem -in $this.Wheels; }) -join ';'
|
||||
}
|
||||
}
|
||||
It 'Invoke Where' {
|
||||
[C1]::new().Foo() | should be "1;2;3"
|
||||
}
|
||||
It 'Pipe to where' {
|
||||
[C1]::new().Bar() | should be "1;2;3"
|
||||
}
|
||||
}
|
||||
|
||||
Context 'ForEach' {
|
||||
class C1 {
|
||||
[int[]] $Wheels = @(1,2,3);
|
||||
[string] Foo() {
|
||||
$ret=""
|
||||
Foreach($PSItem in $this.Wheels) { $ret +="$PSItem;"}
|
||||
return $ret
|
||||
}
|
||||
|
||||
[string] Bar()
|
||||
{
|
||||
$ret = ""
|
||||
$this.Wheels | ForEach-Object { $ret += "$_;" }
|
||||
return $ret
|
||||
}
|
||||
}
|
||||
It 'Invoke Foreach' {
|
||||
[C1]::new().Foo() | should be "1;2;3;"
|
||||
}
|
||||
It 'Pipe to Foreach' {
|
||||
[C1]::new().Bar() | should be "1;2;3;"
|
||||
}
|
||||
}
|
||||
|
||||
Context 'Class instantiation' {
|
||||
Class C1 {
|
||||
[string] Foo() {
|
||||
return (Get-TestText)
|
||||
}
|
||||
}
|
||||
|
||||
BeforeAll {
|
||||
$ExpectedTextFromBoundInstance = "Class C1 was defined in this Runspace"
|
||||
$ExpectedTextFromUnboundInstance = "New Runspace without class C1 defined"
|
||||
|
||||
## Define 'Get-TestText' in the current Runspace
|
||||
function Get-TestText { return $ExpectedTextFromBoundInstance }
|
||||
|
||||
$NewRunspaceFunctionDefinitions = @"
|
||||
## Define 'Get-TestText' in the new Runspace
|
||||
function Get-TestText { return '$ExpectedTextFromUnboundInstance' }
|
||||
|
||||
## Define the function to create an instance of the given type using the default constructor
|
||||
function New-UnboundInstance([Type]`$type) { `$type::new() }
|
||||
|
||||
## Define the function to call 'Foo()' on the given C1 instance, and return the result
|
||||
function Run-Foo(`$C1Instance) { `$C1Instance.Foo() }
|
||||
"@
|
||||
## Create a new Runspace and define helper functions in it
|
||||
$powershell = [powershell]::Create()
|
||||
$powershell.AddScript($NewRunspaceFunctionDefinitions).Invoke() > $null
|
||||
$powershell.Commands.Clear()
|
||||
|
||||
function InstantiateInNewRunspace([Type]$type) {
|
||||
try {
|
||||
$result = $powershell.AddCommand("New-UnboundInstance").AddParameter("type", $type).Invoke()
|
||||
$result.Count | Should Be 1 > $null
|
||||
return $result[0]
|
||||
} finally {
|
||||
$powershell.Commands.Clear()
|
||||
}
|
||||
}
|
||||
|
||||
function RunFooInNewRunspace($instance) {
|
||||
try {
|
||||
$result = $powershell.AddCommand("Run-Foo").AddParameter("C1Instance", $instance).Invoke()
|
||||
$result.Count | Should Be 1 > $null
|
||||
return $result[0]
|
||||
} finally {
|
||||
$powershell.Commands.Clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
$powershell.Dispose()
|
||||
}
|
||||
|
||||
It "Create instance that is bound to a SessionState" {
|
||||
$instance = [C1]::new()
|
||||
## For a bound class instance, the execution of an instance method is
|
||||
## done in the Runspace/SessionState the instance is bound to.
|
||||
$instance.Foo() | Should Be $ExpectedTextFromBoundInstance
|
||||
RunFooInNewRunspace $instance | Should Be $ExpectedTextFromBoundInstance
|
||||
}
|
||||
|
||||
It "Create instance that is NOT bound to a SessionState" {
|
||||
$instance = InstantiateInNewRunspace ([C1])
|
||||
## For an unbound class instance, the execution of an instance method is done in
|
||||
## the Runspace/SessionState where the call to the instance method is made.
|
||||
$instance.Foo() | Should Be $ExpectedTextFromBoundInstance
|
||||
RunFooInNewRunspace $instance | Should Be $ExpectedTextFromUnboundInstance
|
||||
}
|
||||
}
|
||||
}
|
||||
Describe 'Misc Test' -Tags "CI" {
|
||||
|
||||
Context 'Where' {
|
||||
class C1 {
|
||||
[int[]] $Wheels = @(1,2,3);
|
||||
[string] Foo() {
|
||||
return (1..10).Where({ $PSItem -in $this.Wheels; }) -join ';'
|
||||
}
|
||||
|
||||
[string] Bar()
|
||||
{
|
||||
return (1..10 | Where-Object { $PSItem -in $this.Wheels; }) -join ';'
|
||||
}
|
||||
}
|
||||
It 'Invoke Where' {
|
||||
[C1]::new().Foo() | should be "1;2;3"
|
||||
}
|
||||
It 'Pipe to where' {
|
||||
[C1]::new().Bar() | should be "1;2;3"
|
||||
}
|
||||
}
|
||||
|
||||
Context 'ForEach' {
|
||||
class C1 {
|
||||
[int[]] $Wheels = @(1,2,3);
|
||||
[string] Foo() {
|
||||
$ret=""
|
||||
Foreach($PSItem in $this.Wheels) { $ret +="$PSItem;"}
|
||||
return $ret
|
||||
}
|
||||
|
||||
[string] Bar()
|
||||
{
|
||||
$ret = ""
|
||||
$this.Wheels | ForEach-Object { $ret += "$_;" }
|
||||
return $ret
|
||||
}
|
||||
}
|
||||
It 'Invoke Foreach' {
|
||||
[C1]::new().Foo() | should be "1;2;3;"
|
||||
}
|
||||
It 'Pipe to Foreach' {
|
||||
[C1]::new().Bar() | should be "1;2;3;"
|
||||
}
|
||||
}
|
||||
|
||||
Context 'Class instantiation' {
|
||||
Class C1 {
|
||||
[string] Foo() {
|
||||
return (Get-TestText)
|
||||
}
|
||||
}
|
||||
|
||||
BeforeAll {
|
||||
$ExpectedTextFromBoundInstance = "Class C1 was defined in this Runspace"
|
||||
$ExpectedTextFromUnboundInstance = "New Runspace without class C1 defined"
|
||||
|
||||
## Define 'Get-TestText' in the current Runspace
|
||||
function Get-TestText { return $ExpectedTextFromBoundInstance }
|
||||
|
||||
$NewRunspaceFunctionDefinitions = @"
|
||||
## Define 'Get-TestText' in the new Runspace
|
||||
function Get-TestText { return '$ExpectedTextFromUnboundInstance' }
|
||||
|
||||
## Define the function to create an instance of the given type using the default constructor
|
||||
function New-UnboundInstance([Type]`$type) { `$type::new() }
|
||||
|
||||
## Define the function to call 'Foo()' on the given C1 instance, and return the result
|
||||
function Run-Foo(`$C1Instance) { `$C1Instance.Foo() }
|
||||
"@
|
||||
## Create a new Runspace and define helper functions in it
|
||||
$powershell = [powershell]::Create()
|
||||
$powershell.AddScript($NewRunspaceFunctionDefinitions).Invoke() > $null
|
||||
$powershell.Commands.Clear()
|
||||
|
||||
function InstantiateInNewRunspace([Type]$type) {
|
||||
try {
|
||||
$result = $powershell.AddCommand("New-UnboundInstance").AddParameter("type", $type).Invoke()
|
||||
$result.Count | Should Be 1 > $null
|
||||
return $result[0]
|
||||
} finally {
|
||||
$powershell.Commands.Clear()
|
||||
}
|
||||
}
|
||||
|
||||
function RunFooInNewRunspace($instance) {
|
||||
try {
|
||||
$result = $powershell.AddCommand("Run-Foo").AddParameter("C1Instance", $instance).Invoke()
|
||||
$result.Count | Should Be 1 > $null
|
||||
return $result[0]
|
||||
} finally {
|
||||
$powershell.Commands.Clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
$powershell.Dispose()
|
||||
}
|
||||
|
||||
It "Create instance that is bound to a SessionState" {
|
||||
$instance = [C1]::new()
|
||||
## For a bound class instance, the execution of an instance method is
|
||||
## done in the Runspace/SessionState the instance is bound to.
|
||||
$instance.Foo() | Should Be $ExpectedTextFromBoundInstance
|
||||
RunFooInNewRunspace $instance | Should Be $ExpectedTextFromBoundInstance
|
||||
}
|
||||
|
||||
It "Create instance that is NOT bound to a SessionState" {
|
||||
$instance = InstantiateInNewRunspace ([C1])
|
||||
## For an unbound class instance, the execution of an instance method is done in
|
||||
## the Runspace/SessionState where the call to the instance method is made.
|
||||
$instance.Foo() | Should Be $ExpectedTextFromBoundInstance
|
||||
RunFooInNewRunspace $instance | Should Be $ExpectedTextFromUnboundInstance
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,113 +1,113 @@
|
||||
Describe 'PSModuleInfo.GetExportedTypeDefinitions()' -Tags "CI" {
|
||||
It "doesn't throw for any module" {
|
||||
$discard = Get-Module -ListAvailable | ForEach-Object { $_.GetExportedTypeDefinitions() }
|
||||
$true | Should Be $true # we only verify that we didn't throw. This line contains a dummy Should to make pester happy.
|
||||
}
|
||||
}
|
||||
|
||||
Describe 'use of a module from two runspaces' -Tags "CI" {
|
||||
function New-TestModule {
|
||||
param(
|
||||
[string]$Name,
|
||||
[string]$Content
|
||||
)
|
||||
|
||||
$TestModulePath = Join-Path -Path $TestDrive -ChildPath "TestModule"
|
||||
$ModuleFolder = Join-Path -Path $TestModulePath -ChildPath $Name
|
||||
New-Item -Path $ModuleFolder -ItemType Directory -Force > $null
|
||||
|
||||
Set-Content -Path "$ModuleFolder\$Name.psm1" -Value $Content
|
||||
|
||||
$manifestParams = @{
|
||||
Path = "$ModuleFolder\$Name.psd1"
|
||||
RootModule = "$Name.psm1"
|
||||
}
|
||||
New-ModuleManifest @manifestParams
|
||||
|
||||
if ($env:PSModulePath -notlike "*$TestModulePath*") {
|
||||
$env:PSModulePath += "$([System.IO.Path]::PathSeparator)$TestModulePath"
|
||||
}
|
||||
}
|
||||
|
||||
$originalPSModulePath = $env:PSModulePath
|
||||
try {
|
||||
|
||||
New-TestModule -Name 'Random' -Content @'
|
||||
$script:random = Get-Random
|
||||
class RandomWrapper
|
||||
{
|
||||
[int] getRandom()
|
||||
{
|
||||
return $script:random
|
||||
}
|
||||
}
|
||||
'@
|
||||
|
||||
It 'use different sessionStates for different modules' {
|
||||
$ps = 1..2 | ForEach-Object { $p = [powershell]::Create().AddScript(@'
|
||||
Import-Module Random
|
||||
'@)
|
||||
$p.Invoke() > $null
|
||||
$p
|
||||
}
|
||||
$res = 1..2 | ForEach-Object {
|
||||
0..1 | ForEach-Object {
|
||||
$ps[$_].Commands.Clear()
|
||||
# The idea: instance created inside the context, in one runspace.
|
||||
# Method is called on instance in the different runspace, but it should know about the origin.
|
||||
$w = $ps[$_].AddScript('& (Get-Module Random) { [RandomWrapper]::new() }').Invoke()[0]
|
||||
$w.getRandom()
|
||||
}
|
||||
}
|
||||
|
||||
$res.Count | Should Be 4
|
||||
$res[0] | Should Not Be $res[1]
|
||||
$res[0] | Should Be $res[2]
|
||||
$res[1] | Should Be $res[3]
|
||||
}
|
||||
|
||||
} finally {
|
||||
$env:PSModulePath = $originalPSModulePath
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Describe 'Module reloading with Class definition' -Tags "CI" {
|
||||
|
||||
BeforeAll {
|
||||
Set-Content -Path TestDrive:\TestModule.psm1 -Value @'
|
||||
$passedArgs = $args
|
||||
class Root { $passedIn = $passedArgs }
|
||||
function Get-PassedArgsRoot { [Root]::new().passedIn }
|
||||
function Get-PassedArgsNoRoot { $passedArgs }
|
||||
'@
|
||||
$Arg_Hello = 'Hello'
|
||||
$Arg_World = 'World'
|
||||
}
|
||||
|
||||
AfterEach {
|
||||
Remove-Module TestModule -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
It "Class execution reflects changes in module reloading with '-Force'" {
|
||||
Import-Module TestDrive:\TestModule.psm1 -ArgumentList $Arg_Hello
|
||||
Get-PassedArgsRoot | Should Be $Arg_Hello
|
||||
Get-PassedArgsNoRoot | Should Be $Arg_Hello
|
||||
|
||||
Import-Module TestDrive:\TestModule.psm1 -ArgumentList $Arg_World -Force
|
||||
Get-PassedArgsRoot | Should Be $Arg_World
|
||||
Get-PassedArgsNoRoot | Should Be $Arg_World
|
||||
}
|
||||
|
||||
It "Class execution reflects changes in module reloading with 'Remove-Module' and 'Import-Module'" {
|
||||
Import-Module TestDrive:\TestModule.psm1 -ArgumentList $Arg_Hello
|
||||
Get-PassedArgsRoot | Should Be $Arg_Hello
|
||||
Get-PassedArgsNoRoot | Should Be $Arg_Hello
|
||||
|
||||
Remove-Module TestModule
|
||||
|
||||
Import-Module TestDrive:\TestModule.psm1 -ArgumentList $Arg_World
|
||||
Get-PassedArgsRoot | Should Be $Arg_World
|
||||
Get-PassedArgsNoRoot | Should Be $Arg_World
|
||||
}
|
||||
}
|
||||
Describe 'PSModuleInfo.GetExportedTypeDefinitions()' -Tags "CI" {
|
||||
It "doesn't throw for any module" {
|
||||
$discard = Get-Module -ListAvailable | ForEach-Object { $_.GetExportedTypeDefinitions() }
|
||||
$true | Should Be $true # we only verify that we didn't throw. This line contains a dummy Should to make pester happy.
|
||||
}
|
||||
}
|
||||
|
||||
Describe 'use of a module from two runspaces' -Tags "CI" {
|
||||
function New-TestModule {
|
||||
param(
|
||||
[string]$Name,
|
||||
[string]$Content
|
||||
)
|
||||
|
||||
$TestModulePath = Join-Path -Path $TestDrive -ChildPath "TestModule"
|
||||
$ModuleFolder = Join-Path -Path $TestModulePath -ChildPath $Name
|
||||
New-Item -Path $ModuleFolder -ItemType Directory -Force > $null
|
||||
|
||||
Set-Content -Path "$ModuleFolder\$Name.psm1" -Value $Content
|
||||
|
||||
$manifestParams = @{
|
||||
Path = "$ModuleFolder\$Name.psd1"
|
||||
RootModule = "$Name.psm1"
|
||||
}
|
||||
New-ModuleManifest @manifestParams
|
||||
|
||||
if ($env:PSModulePath -notlike "*$TestModulePath*") {
|
||||
$env:PSModulePath += "$([System.IO.Path]::PathSeparator)$TestModulePath"
|
||||
}
|
||||
}
|
||||
|
||||
$originalPSModulePath = $env:PSModulePath
|
||||
try {
|
||||
|
||||
New-TestModule -Name 'Random' -Content @'
|
||||
$script:random = Get-Random
|
||||
class RandomWrapper
|
||||
{
|
||||
[int] getRandom()
|
||||
{
|
||||
return $script:random
|
||||
}
|
||||
}
|
||||
'@
|
||||
|
||||
It 'use different sessionStates for different modules' {
|
||||
$ps = 1..2 | ForEach-Object { $p = [powershell]::Create().AddScript(@'
|
||||
Import-Module Random
|
||||
'@)
|
||||
$p.Invoke() > $null
|
||||
$p
|
||||
}
|
||||
$res = 1..2 | ForEach-Object {
|
||||
0..1 | ForEach-Object {
|
||||
$ps[$_].Commands.Clear()
|
||||
# The idea: instance created inside the context, in one runspace.
|
||||
# Method is called on instance in the different runspace, but it should know about the origin.
|
||||
$w = $ps[$_].AddScript('& (Get-Module Random) { [RandomWrapper]::new() }').Invoke()[0]
|
||||
$w.getRandom()
|
||||
}
|
||||
}
|
||||
|
||||
$res.Count | Should Be 4
|
||||
$res[0] | Should Not Be $res[1]
|
||||
$res[0] | Should Be $res[2]
|
||||
$res[1] | Should Be $res[3]
|
||||
}
|
||||
|
||||
} finally {
|
||||
$env:PSModulePath = $originalPSModulePath
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Describe 'Module reloading with Class definition' -Tags "CI" {
|
||||
|
||||
BeforeAll {
|
||||
Set-Content -Path TestDrive:\TestModule.psm1 -Value @'
|
||||
$passedArgs = $args
|
||||
class Root { $passedIn = $passedArgs }
|
||||
function Get-PassedArgsRoot { [Root]::new().passedIn }
|
||||
function Get-PassedArgsNoRoot { $passedArgs }
|
||||
'@
|
||||
$Arg_Hello = 'Hello'
|
||||
$Arg_World = 'World'
|
||||
}
|
||||
|
||||
AfterEach {
|
||||
Remove-Module TestModule -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
It "Class execution reflects changes in module reloading with '-Force'" {
|
||||
Import-Module TestDrive:\TestModule.psm1 -ArgumentList $Arg_Hello
|
||||
Get-PassedArgsRoot | Should Be $Arg_Hello
|
||||
Get-PassedArgsNoRoot | Should Be $Arg_Hello
|
||||
|
||||
Import-Module TestDrive:\TestModule.psm1 -ArgumentList $Arg_World -Force
|
||||
Get-PassedArgsRoot | Should Be $Arg_World
|
||||
Get-PassedArgsNoRoot | Should Be $Arg_World
|
||||
}
|
||||
|
||||
It "Class execution reflects changes in module reloading with 'Remove-Module' and 'Import-Module'" {
|
||||
Import-Module TestDrive:\TestModule.psm1 -ArgumentList $Arg_Hello
|
||||
Get-PassedArgsRoot | Should Be $Arg_Hello
|
||||
Get-PassedArgsNoRoot | Should Be $Arg_Hello
|
||||
|
||||
Remove-Module TestModule
|
||||
|
||||
Import-Module TestDrive:\TestModule.psm1 -ArgumentList $Arg_World
|
||||
Get-PassedArgsRoot | Should Be $Arg_World
|
||||
Get-PassedArgsNoRoot | Should Be $Arg_World
|
||||
}
|
||||
}
|
||||
@@ -1,124 +1,124 @@
|
||||
Describe 'NestedModules' -Tags "CI" {
|
||||
|
||||
function New-TestModule {
|
||||
param(
|
||||
[string]$Name,
|
||||
[string]$Content,
|
||||
[string[]]$NestedContents
|
||||
)
|
||||
|
||||
new-item -type directory -Force "TestDrive:\$Name" > $null
|
||||
$manifestParams = @{
|
||||
Path = "TestDrive:\$Name\$Name.psd1"
|
||||
}
|
||||
|
||||
if ($Content) {
|
||||
Set-Content -Path "${TestDrive}\$Name\$Name.psm1" -Value $Content
|
||||
$manifestParams['RootModule'] = "$Name.psm1"
|
||||
}
|
||||
|
||||
if ($NestedContents) {
|
||||
$manifestParams['NestedModules'] = 1..$NestedContents.Count | ForEach-Object {
|
||||
$null = new-item -type directory TestDrive:\$Name\Nested$_
|
||||
$null = Set-Content -Path "${TestDrive}\$Name\Nested$_\Nested$_.psm1" -Value $NestedContents[$_ - 1]
|
||||
"Nested$_"
|
||||
}
|
||||
}
|
||||
|
||||
New-ModuleManifest @manifestParams
|
||||
|
||||
$resolvedTestDrivePath = Split-Path ((get-childitem TestDrive:\)[0].FullName)
|
||||
if (-not ($env:PSModulePath -like "*$resolvedTestDrivePath*")) {
|
||||
$env:PSModulePath += "$([System.IO.Path]::PathSeparator)$resolvedTestDrivePath"
|
||||
}
|
||||
}
|
||||
|
||||
$originalPSModulePath = $env:PSModulePath
|
||||
|
||||
try {
|
||||
|
||||
|
||||
# Create modules in TestDrive:\
|
||||
New-TestModule -Name NoRoot -NestedContents @(
|
||||
'class A { [string] foo() { return "A1"} }',
|
||||
'class A { [string] foo() { return "A2"} }'
|
||||
)
|
||||
|
||||
New-TestModule -Name WithRoot -NestedContents @(
|
||||
'class A { [string] foo() { return "A1"} }',
|
||||
'class A { [string] foo() { return "A2"} }'
|
||||
) -Content 'class A { [string] foo() { return "A0"} }'
|
||||
|
||||
New-TestModule -Name ABC -NestedContents @(
|
||||
'class A { [string] foo() { return "A"} }',
|
||||
'class B { [string] foo() { return "B"} }'
|
||||
) -Content 'class C { [string] foo() { return "C"} }'
|
||||
|
||||
It 'Get-Module is able to find types' {
|
||||
$module = Get-Module NoRoot -ListAvailable
|
||||
$module.GetExportedTypeDefinitions().Count | Should Be 1
|
||||
|
||||
$module = Get-Module WithRoot -ListAvailable
|
||||
$module.GetExportedTypeDefinitions().Count | Should Be 1
|
||||
|
||||
$module = Get-Module ABC -ListAvailable
|
||||
$module.GetExportedTypeDefinitions().Count | Should Be 3
|
||||
}
|
||||
|
||||
It 'Import-Module pick the right type' {
|
||||
$module = Import-Module ABC -PassThru
|
||||
$module.GetExportedTypeDefinitions().Count | Should Be 3
|
||||
$module = Import-Module ABC -PassThru -Force
|
||||
$module.GetExportedTypeDefinitions().Count | Should Be 3
|
||||
|
||||
$module = Import-Module NoRoot -PassThru
|
||||
$module.GetExportedTypeDefinitions().Count | Should Be 1
|
||||
$module = Import-Module NoRoot -PassThru -Force
|
||||
$module.GetExportedTypeDefinitions().Count | Should Be 1
|
||||
[scriptblock]::Create(@'
|
||||
using module NoRoot
|
||||
[A]::new().foo()
|
||||
'@
|
||||
).Invoke() | Should Be A2
|
||||
|
||||
$module = Import-Module WithRoot -PassThru
|
||||
$module.GetExportedTypeDefinitions().Count | Should Be 1
|
||||
$module = Import-Module WithRoot -PassThru -Force
|
||||
$module.GetExportedTypeDefinitions().Count | Should Be 1
|
||||
[scriptblock]::Create(@'
|
||||
using module WithRoot
|
||||
[A]::new().foo()
|
||||
'@
|
||||
).Invoke() | Should Be A0
|
||||
}
|
||||
|
||||
Context 'execute type creation in the module context' {
|
||||
|
||||
# let's define types to make it more fun
|
||||
class A { [string] foo() { return "local"} }
|
||||
class B { [string] foo() { return "local"} }
|
||||
class C { [string] foo() { return "local"} }
|
||||
|
||||
# We need to think about it: should it work or not.
|
||||
# Currently, types are resolved in compile-time to the 'local' versions
|
||||
# So at runtime we don't call the module versions.
|
||||
It 'Can execute type creation in the module context with new()' -pending {
|
||||
& (Get-Module ABC) { [C]::new().foo() } | Should Be C
|
||||
& (Get-Module NoRoot) { [A]::new().foo() } | Should Be A2
|
||||
& (Get-Module WithRoot) { [A]::new().foo() } | Should Be A0
|
||||
& (Get-Module ABC) { [A]::new().foo() } | Should Be A
|
||||
}
|
||||
|
||||
It 'Can execute type creation in the module context with New-Object' {
|
||||
& (Get-Module ABC) { (New-Object C).foo() } | Should Be C
|
||||
& (Get-Module NoRoot) { (New-Object A).foo() } | Should Be A2
|
||||
& (Get-Module WithRoot) { (New-Object A).foo() } | Should Be A0
|
||||
& (Get-Module ABC) { (New-Object A).foo() } | Should Be A
|
||||
}
|
||||
}
|
||||
|
||||
} finally {
|
||||
$env:PSModulePath = $originalPSModulePath
|
||||
Get-Module @('ABC', 'NoRoot', 'WithRoot') | Remove-Module
|
||||
}
|
||||
}
|
||||
Describe 'NestedModules' -Tags "CI" {
|
||||
|
||||
function New-TestModule {
|
||||
param(
|
||||
[string]$Name,
|
||||
[string]$Content,
|
||||
[string[]]$NestedContents
|
||||
)
|
||||
|
||||
new-item -type directory -Force "TestDrive:\$Name" > $null
|
||||
$manifestParams = @{
|
||||
Path = "TestDrive:\$Name\$Name.psd1"
|
||||
}
|
||||
|
||||
if ($Content) {
|
||||
Set-Content -Path "${TestDrive}\$Name\$Name.psm1" -Value $Content
|
||||
$manifestParams['RootModule'] = "$Name.psm1"
|
||||
}
|
||||
|
||||
if ($NestedContents) {
|
||||
$manifestParams['NestedModules'] = 1..$NestedContents.Count | ForEach-Object {
|
||||
$null = new-item -type directory TestDrive:\$Name\Nested$_
|
||||
$null = Set-Content -Path "${TestDrive}\$Name\Nested$_\Nested$_.psm1" -Value $NestedContents[$_ - 1]
|
||||
"Nested$_"
|
||||
}
|
||||
}
|
||||
|
||||
New-ModuleManifest @manifestParams
|
||||
|
||||
$resolvedTestDrivePath = Split-Path ((get-childitem TestDrive:\)[0].FullName)
|
||||
if (-not ($env:PSModulePath -like "*$resolvedTestDrivePath*")) {
|
||||
$env:PSModulePath += "$([System.IO.Path]::PathSeparator)$resolvedTestDrivePath"
|
||||
}
|
||||
}
|
||||
|
||||
$originalPSModulePath = $env:PSModulePath
|
||||
|
||||
try {
|
||||
|
||||
|
||||
# Create modules in TestDrive:\
|
||||
New-TestModule -Name NoRoot -NestedContents @(
|
||||
'class A { [string] foo() { return "A1"} }',
|
||||
'class A { [string] foo() { return "A2"} }'
|
||||
)
|
||||
|
||||
New-TestModule -Name WithRoot -NestedContents @(
|
||||
'class A { [string] foo() { return "A1"} }',
|
||||
'class A { [string] foo() { return "A2"} }'
|
||||
) -Content 'class A { [string] foo() { return "A0"} }'
|
||||
|
||||
New-TestModule -Name ABC -NestedContents @(
|
||||
'class A { [string] foo() { return "A"} }',
|
||||
'class B { [string] foo() { return "B"} }'
|
||||
) -Content 'class C { [string] foo() { return "C"} }'
|
||||
|
||||
It 'Get-Module is able to find types' {
|
||||
$module = Get-Module NoRoot -ListAvailable
|
||||
$module.GetExportedTypeDefinitions().Count | Should Be 1
|
||||
|
||||
$module = Get-Module WithRoot -ListAvailable
|
||||
$module.GetExportedTypeDefinitions().Count | Should Be 1
|
||||
|
||||
$module = Get-Module ABC -ListAvailable
|
||||
$module.GetExportedTypeDefinitions().Count | Should Be 3
|
||||
}
|
||||
|
||||
It 'Import-Module pick the right type' {
|
||||
$module = Import-Module ABC -PassThru
|
||||
$module.GetExportedTypeDefinitions().Count | Should Be 3
|
||||
$module = Import-Module ABC -PassThru -Force
|
||||
$module.GetExportedTypeDefinitions().Count | Should Be 3
|
||||
|
||||
$module = Import-Module NoRoot -PassThru
|
||||
$module.GetExportedTypeDefinitions().Count | Should Be 1
|
||||
$module = Import-Module NoRoot -PassThru -Force
|
||||
$module.GetExportedTypeDefinitions().Count | Should Be 1
|
||||
[scriptblock]::Create(@'
|
||||
using module NoRoot
|
||||
[A]::new().foo()
|
||||
'@
|
||||
).Invoke() | Should Be A2
|
||||
|
||||
$module = Import-Module WithRoot -PassThru
|
||||
$module.GetExportedTypeDefinitions().Count | Should Be 1
|
||||
$module = Import-Module WithRoot -PassThru -Force
|
||||
$module.GetExportedTypeDefinitions().Count | Should Be 1
|
||||
[scriptblock]::Create(@'
|
||||
using module WithRoot
|
||||
[A]::new().foo()
|
||||
'@
|
||||
).Invoke() | Should Be A0
|
||||
}
|
||||
|
||||
Context 'execute type creation in the module context' {
|
||||
|
||||
# let's define types to make it more fun
|
||||
class A { [string] foo() { return "local"} }
|
||||
class B { [string] foo() { return "local"} }
|
||||
class C { [string] foo() { return "local"} }
|
||||
|
||||
# We need to think about it: should it work or not.
|
||||
# Currently, types are resolved in compile-time to the 'local' versions
|
||||
# So at runtime we don't call the module versions.
|
||||
It 'Can execute type creation in the module context with new()' -pending {
|
||||
& (Get-Module ABC) { [C]::new().foo() } | Should Be C
|
||||
& (Get-Module NoRoot) { [A]::new().foo() } | Should Be A2
|
||||
& (Get-Module WithRoot) { [A]::new().foo() } | Should Be A0
|
||||
& (Get-Module ABC) { [A]::new().foo() } | Should Be A
|
||||
}
|
||||
|
||||
It 'Can execute type creation in the module context with New-Object' {
|
||||
& (Get-Module ABC) { (New-Object C).foo() } | Should Be C
|
||||
& (Get-Module NoRoot) { (New-Object A).foo() } | Should Be A2
|
||||
& (Get-Module WithRoot) { (New-Object A).foo() } | Should Be A0
|
||||
& (Get-Module ABC) { (New-Object A).foo() } | Should Be A
|
||||
}
|
||||
}
|
||||
|
||||
} finally {
|
||||
$env:PSModulePath = $originalPSModulePath
|
||||
Get-Module @('ABC', 'NoRoot', 'WithRoot') | Remove-Module
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1,91 +1,91 @@
|
||||
#
|
||||
# Copyright (c) Microsoft Corporation, 2015
|
||||
#
|
||||
|
||||
Describe 'enums' -Tags "CI" {
|
||||
Context 'basic enums' {
|
||||
enum E1
|
||||
{
|
||||
e0
|
||||
e1
|
||||
e2
|
||||
}
|
||||
|
||||
It "has correct value 0" { [E1]::e0 | Should Be ([E1]0) }
|
||||
It "has correct value 1" { [E1]::e1 | Should Be ([E1]1) }
|
||||
It "has correct value 2" { [E1]::e2 | Should Be ([E1]2) }
|
||||
It "cast from string" { [E1]::e1 | Should Be 'e1' }
|
||||
It "cast to string" { 'e2' | Should Be ([E1]::e2) }
|
||||
}
|
||||
|
||||
Context 'Basic enum with initial value' {
|
||||
enum E2
|
||||
{
|
||||
e0
|
||||
e1 = 5
|
||||
e2
|
||||
}
|
||||
|
||||
It "has correct value 0" { [E2]::e0 | Should Be ([E2]0) }
|
||||
It "has correct value 5" { [E2]::e1 | Should Be ([E2]5) }
|
||||
It "has correct value 6" { [E2]::e2 | Should Be ([E2]6) }
|
||||
It "cast from string" { [E2]::e1 | Should Be 'e1' }
|
||||
It "cast to string" { 'e2' | Should Be ([E2]::e2) }
|
||||
}
|
||||
|
||||
Context 'Basic enum with initial value expression' {
|
||||
enum E3
|
||||
{
|
||||
e0
|
||||
e1 = 5
|
||||
e2 = [int]::MaxValue
|
||||
e3 = 1 # This shouldn't be an error even though previous member was max int
|
||||
}
|
||||
|
||||
It "has correct value 0" { [E3]::e0 | Should Be ([E3]0) }
|
||||
It "has correct value 5" { [E3]::e1 | Should Be ([E3]5) }
|
||||
It "has correct value [int]::MaxValue" { [E3]::e2 | Should Be ([E3]([int]::MaxValue)) }
|
||||
It "has correct value 1" { [E3]::e3 | Should Be ([E3]1) }
|
||||
It "cast from string" { [E3]::e2 | Should Be 'e2' }
|
||||
It "cast to string" { 'e3' | Should Be ([E3]::e3) }
|
||||
}
|
||||
|
||||
Context 'Enum with complicated initial value' {
|
||||
enum E4
|
||||
{
|
||||
e0 = [E5]::e0 + 2
|
||||
}
|
||||
|
||||
enum E5
|
||||
{
|
||||
e0 = [E6]::e0 + 2
|
||||
}
|
||||
|
||||
# Don't add space after 'e0 ='! Fix #2543
|
||||
enum E6
|
||||
{
|
||||
e0 =38
|
||||
}
|
||||
|
||||
It 'E4 has correct value' { [E4]::e0 | Should Be ([E4]42) }
|
||||
It 'E5 has correct value' { [E5]::e0 | Should Be ([E5]40) }
|
||||
It 'E6 has correct value' { [E6]::e0 | Should Be ([E6]38) }
|
||||
}
|
||||
}
|
||||
|
||||
Describe 'Basic enum errors' -Tags "CI" {
|
||||
ShouldBeParseError 'enum' MissingNameAfterKeyword 4
|
||||
ShouldBeParseError 'enum foo' MissingTypeBody 8
|
||||
ShouldBeParseError 'enum foo {' MissingEndCurlyBrace 10
|
||||
ShouldBeParseError 'enum foo { x = }' ExpectedValueExpression 14
|
||||
ShouldBeParseError 'enum foo { x =' ExpectedValueExpression,MissingEndCurlyBrace 14,10
|
||||
ShouldBeParseError 'enum foo {} enum foo {}' MemberAlreadyDefined 12
|
||||
ShouldBeParseError 'enum foo { x; x }' MemberAlreadyDefined 14 -SkipAndCheckRuntimeError
|
||||
ShouldBeParseError 'enum foo { X; x }' MemberAlreadyDefined 14 -SkipAndCheckRuntimeError
|
||||
ShouldBeParseError 'enum foo1 { x = [foo2]::x } enum foo2 { x = [foo1]::x }' CycleInEnumInitializers,CycleInEnumInitializers 0,28 -SkipAndCheckRuntimeError
|
||||
ShouldBeParseError 'enum foo { e = [int]::MaxValue; e2 }' EnumeratorValueTooLarge 33 -SkipAndCheckRuntimeError
|
||||
ShouldBeParseError 'enum foo { e = [int]::MaxValue + 1 }' EnumeratorValueTooLarge 15 -SkipAndCheckRuntimeError
|
||||
ShouldBeParseError 'enum foo { e = $foo }' EnumeratorValueMustBeConstant 15 -SkipAndCheckRuntimeError
|
||||
ShouldBeParseError 'enum foo { e = "hello" }' CannotConvertValue 15 -SkipAndCheckRuntimeError
|
||||
ShouldBeParseError 'enum foo { a;b;c;' MissingEndCurlyBrace 10
|
||||
}
|
||||
#
|
||||
# Copyright (c) Microsoft Corporation, 2015
|
||||
#
|
||||
|
||||
Describe 'enums' -Tags "CI" {
|
||||
Context 'basic enums' {
|
||||
enum E1
|
||||
{
|
||||
e0
|
||||
e1
|
||||
e2
|
||||
}
|
||||
|
||||
It "has correct value 0" { [E1]::e0 | Should Be ([E1]0) }
|
||||
It "has correct value 1" { [E1]::e1 | Should Be ([E1]1) }
|
||||
It "has correct value 2" { [E1]::e2 | Should Be ([E1]2) }
|
||||
It "cast from string" { [E1]::e1 | Should Be 'e1' }
|
||||
It "cast to string" { 'e2' | Should Be ([E1]::e2) }
|
||||
}
|
||||
|
||||
Context 'Basic enum with initial value' {
|
||||
enum E2
|
||||
{
|
||||
e0
|
||||
e1 = 5
|
||||
e2
|
||||
}
|
||||
|
||||
It "has correct value 0" { [E2]::e0 | Should Be ([E2]0) }
|
||||
It "has correct value 5" { [E2]::e1 | Should Be ([E2]5) }
|
||||
It "has correct value 6" { [E2]::e2 | Should Be ([E2]6) }
|
||||
It "cast from string" { [E2]::e1 | Should Be 'e1' }
|
||||
It "cast to string" { 'e2' | Should Be ([E2]::e2) }
|
||||
}
|
||||
|
||||
Context 'Basic enum with initial value expression' {
|
||||
enum E3
|
||||
{
|
||||
e0
|
||||
e1 = 5
|
||||
e2 = [int]::MaxValue
|
||||
e3 = 1 # This shouldn't be an error even though previous member was max int
|
||||
}
|
||||
|
||||
It "has correct value 0" { [E3]::e0 | Should Be ([E3]0) }
|
||||
It "has correct value 5" { [E3]::e1 | Should Be ([E3]5) }
|
||||
It "has correct value [int]::MaxValue" { [E3]::e2 | Should Be ([E3]([int]::MaxValue)) }
|
||||
It "has correct value 1" { [E3]::e3 | Should Be ([E3]1) }
|
||||
It "cast from string" { [E3]::e2 | Should Be 'e2' }
|
||||
It "cast to string" { 'e3' | Should Be ([E3]::e3) }
|
||||
}
|
||||
|
||||
Context 'Enum with complicated initial value' {
|
||||
enum E4
|
||||
{
|
||||
e0 = [E5]::e0 + 2
|
||||
}
|
||||
|
||||
enum E5
|
||||
{
|
||||
e0 = [E6]::e0 + 2
|
||||
}
|
||||
|
||||
# Don't add space after 'e0 ='! Fix #2543
|
||||
enum E6
|
||||
{
|
||||
e0 =38
|
||||
}
|
||||
|
||||
It 'E4 has correct value' { [E4]::e0 | Should Be ([E4]42) }
|
||||
It 'E5 has correct value' { [E5]::e0 | Should Be ([E5]40) }
|
||||
It 'E6 has correct value' { [E6]::e0 | Should Be ([E6]38) }
|
||||
}
|
||||
}
|
||||
|
||||
Describe 'Basic enum errors' -Tags "CI" {
|
||||
ShouldBeParseError 'enum' MissingNameAfterKeyword 4
|
||||
ShouldBeParseError 'enum foo' MissingTypeBody 8
|
||||
ShouldBeParseError 'enum foo {' MissingEndCurlyBrace 10
|
||||
ShouldBeParseError 'enum foo { x = }' ExpectedValueExpression 14
|
||||
ShouldBeParseError 'enum foo { x =' ExpectedValueExpression,MissingEndCurlyBrace 14,10
|
||||
ShouldBeParseError 'enum foo {} enum foo {}' MemberAlreadyDefined 12
|
||||
ShouldBeParseError 'enum foo { x; x }' MemberAlreadyDefined 14 -SkipAndCheckRuntimeError
|
||||
ShouldBeParseError 'enum foo { X; x }' MemberAlreadyDefined 14 -SkipAndCheckRuntimeError
|
||||
ShouldBeParseError 'enum foo1 { x = [foo2]::x } enum foo2 { x = [foo1]::x }' CycleInEnumInitializers,CycleInEnumInitializers 0,28 -SkipAndCheckRuntimeError
|
||||
ShouldBeParseError 'enum foo { e = [int]::MaxValue; e2 }' EnumeratorValueTooLarge 33 -SkipAndCheckRuntimeError
|
||||
ShouldBeParseError 'enum foo { e = [int]::MaxValue + 1 }' EnumeratorValueTooLarge 15 -SkipAndCheckRuntimeError
|
||||
ShouldBeParseError 'enum foo { e = $foo }' EnumeratorValueMustBeConstant 15 -SkipAndCheckRuntimeError
|
||||
ShouldBeParseError 'enum foo { e = "hello" }' CannotConvertValue 15 -SkipAndCheckRuntimeError
|
||||
ShouldBeParseError 'enum foo { a;b;c;' MissingEndCurlyBrace 10
|
||||
}
|
||||
Reference in new issue
Block a user