Autocorrected CRLF to LF (#4943)

Also, fix `Parser.Tests.ps1` after correcting CRLF.
This commit is contained in:
Steve Lee authored and Dongbo Wang committed 2017-09-29 16:28:15 -07:00
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
}