Add tests for the Language (#2118)

DeserializedMethods, DeserializedTypeConversion,
DynamicParameters, Generics, Indexer, LindEndings, MyInvocation,
OrderedAttributeForHashTable.
This commit is contained in:
Yanbing
2016-09-15 11:27:44 -07:00
committed by Jason Shirk
parent 3d43053e3a
commit edb292face
8 changed files with 754 additions and 0 deletions
@@ -0,0 +1,19 @@
Describe "DeserializedMethods" -Tags "CI" {
It "Deserialized objects shouldn't ever have any methods (unless they are primitive known types)" {
$a = [collections.arraylist]::new()
$null = $a.Add(1)
$null = $a.Add(2)
$null = $a.Add(3)
# using linkedlist that implements IEnumerable,
# but doesn't implement IList or IList<T>
$x = [collections.generic.linkedlist[int]]::new()
$null = $x.Add(123)
$null = $x.Add(456)
$s = [System.Management.Automation.PSSerializer]::Serialize($x)
$d = [System.Management.Automation.PSSerializer]::Deserialize($s)
$d | Get-Member -MemberType *Method* Add | Should Be $null
}
}
@@ -0,0 +1,272 @@
Describe "Tests conversion of deserialized types to original type using object properties." -Tags "CI" {
BeforeAll {
# Create new types and test functions.
$type1,$type2,$type3,$type4 = Add-Type -PassThru -TypeDefinition @'
public class test1
{
public string name;
public int port;
public string scriptText;
}
public class test2
{
private string name;
private int port;
private string scriptText;
public string Name
{
get { return name; }
set { name = value; }
}
public int Port
{
get { return port; }
set { port = value; }
}
public string ScriptText
{
get { return scriptText; }
set { scriptText = value; }
}
}
public class test3
{
private string name = "default";
private int port = 80;
private string scriptText = "1..6";
public string Name
{
get { return name; }
set { name = value; }
}
public int Port
{
get { return port; }
set { port = value; }
}
public string ScriptText
{
get { return scriptText; }
}
}
public class test4
{
private string name = "default";
private int port = 80;
private string scriptText = "1..6";
public string Name
{
get { return name; }
set { name = value; }
}
public int Port
{
get { return port; }
set { port = value; }
}
internal void Compute()
{
scriptText = scriptText + " Computed";
}
}
'@
}
Context 'Type conversion and parameter binding of deserialized Type case 1: type definition contains public fields' {
BeforeAll {
$t1 = new-object test1 -Property @{name="TestName1";port=80;scriptText="1..5"}
$s = [System.Management.Automation.PSSerializer]::Serialize($t1)
$dst1 = [System.Management.Automation.PSSerializer]::Deserialize($s)
}
It 'Type casts should succeed.' {
{ $tc1 = [test1]$dst1 }| Should not Throw
}
It 'Parameter bindings should succeed.' {
function test-1
{
param(
[parameter(position=0, mandatory=1)]
[test1] $test
)
$test | Format-List | Out-String
}
{ test-1 $dst1 } | Should Not Throw
}
}
Context 'Type conversion and parameter binding of deserialized Type case 2: type definition contains public properties' {
BeforeAll {
$t2 = new-object test2 -Property @{Name="TestName2";Port=80;ScriptText="1..5"}
$s = [System.Management.Automation.PSSerializer]::Serialize($t2)
$dst2 = [System.Management.Automation.PSSerializer]::Deserialize($s)
}
It 'Type casts should succeed.' {
{ $tc2 = [test2]$dst2 } | Should Not Throw
}
It 'Parameter bindings should succeed.' {
function test-2
{
param(
[parameter(position=0, mandatory=1)]
[test2] $test
)
$test | Format-List | Out-String
}
{ test-2 $dst2 } | Should Not Throw
}
}
Context 'Type conversion and parameter binding of deserialized Type case 1: type definition contains 2 public properties and 1 read only property' {
BeforeAll {
$t3 = new-object test3 -Property @{Name="TestName3";Port=80}
$s = [System.Management.Automation.PSSerializer]::Serialize($t3)
$dst3 = [System.Management.Automation.PSSerializer]::Deserialize($s)
}
It 'Type casts should fail.' {
try
{
$tc3 = [test3]$dst3
Throw "Exception expected, execution should not have reached here"
}
catch {
$_.FullyQualifiedErrorId | Should Be 'InvalidCastConstructorException'
}
}
It 'Parameter bindings should fail.' {
function test-3
{
param(
[parameter(position=0, mandatory=1)]
[test3] $test
)
$test | Format-List | Out-String
}
try
{
test-3 $dst3
Throw "Exception expected, execution should not have reached here"
}
catch {
$_.FullyQualifiedErrorId | Should Be 'ParameterArgumentTransformationError,test-3'
}
}
}
Context 'Type conversion and parameter binding of deserialized Type case 1: type definition contains 2 public properties' {
BeforeAll {
$t4 = new-object test4 -Property @{Name="TestName4";Port=80}
$s = [System.Management.Automation.PSSerializer]::Serialize($t4)
$dst4 = [System.Management.Automation.PSSerializer]::Deserialize($s)
}
It 'Type casts should succeed.' {
{ $tc4 = [test4]$dst4 } | Should Not Throw
}
It 'Parameter bindings should succeed.' {
function test-4
{
param(
[parameter(position=0, mandatory=1)]
[test4] $test
)
$test | Format-List | Out-String
}
{ test-4 $dst4 } | Should Not Throw
}
}
Context 'Type conversion and parameter binding of deserialized Powershell class with default constructor' {
BeforeAll {
class PSClass1 {
[string] $name = "PSClassName1"
[int] $port = 80
[string] $scriptText = "1..6"
}
$t5 = [PSClass1]::new()
$s = [System.Management.Automation.PSSerializer]::Serialize($t5)
$dst5 = [System.Management.Automation.PSSerializer]::Deserialize($s)
}
It 'Type casts should succeed.' {
{ $tc5 = [PSClass1]$dst5 } | Should Not Throw
}
It 'Parameter bindings should succeed.' {
function test-PSClass1
{
param(
[parameter(position=0, mandatory=1)]
[PSClass1] $test
)
$test | Format-List | Out-String
}
{ test-PSClass1 $dst5 } | Should Not Throw
}
}
Context 'Type conversion and parameter binding of deserialized Powershell class with a defualt constructor and a constructor' {
BeforeAll {
class PSClass2 {
[string] $name = "default"
[int] $port = 80
[string] $scriptText = "1..6"
PSClass2() {}
PSClass2([string] $name1, [int] $port1, [string] $scriptText1)
{
$this.name = $name1
$this.port = $port1
$this.scriptText = $scriptText1
}
}
$t6 = [PSClass2]::new("PSClassName2", 80, "1..5")
$s = [System.Management.Automation.PSSerializer]::Serialize($t6)
$dst6 = [System.Management.Automation.PSSerializer]::Deserialize($s)
}
It 'Type casts should succeed.' {
{ $tc6 = [PSClass2]$dst6 } | Should Not Throw
}
It 'Parameter bindings should succeed.' {
function test-PSClass2
{
param(
[parameter(position=0, mandatory=1)]
[PSClass2] $test
)
$test | Format-List | Out-String
}
{ test-PSClass2 $dst6 } | Should Not Throw
}
}
}
@@ -0,0 +1,84 @@
Describe "Dynamic parameter support in script cmdlets." -Tags "CI" {
BeforeAll {
Class MyTestParameter {
[parameter(ParameterSetName = 'pset1', position=0, mandatory=1)]
[string] $name
}
function foo-bar
{
[CmdletBinding()]
param($path)
dynamicparam {
if ($PSBoundParameters["path"] -contains "abc") {
$attributes = [System.Management.Automation.ParameterAttribute]::New()
$attributes.ParameterSetName = 'pset1'
$attributes.Mandatory = $false
$attributeCollection = [System.Collections.ObjectModel.Collection``1[System.Attribute]]::new()
$attributeCollection.Add($attributes)
$dynParam1 = [System.Management.Automation.RuntimeDefinedParameter]::new("dp1", [Int32], $attributeCollection)
$paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
$paramDictionary.Add("dp1", $dynParam1)
return $paramDictionary
}
elseif($PSBoundParameters["path"] -contains "class") {
$paramDictionary = [MyTestParameter]::new()
return $paramDictionary
}
$paramDictionary = $null
return $null
}
begin {
if(($paramDictionary -ne $null) -and ($paramDictionary -is [MyTestParameter]) ) {
$paramDictionary.name
}
elseif ($paramDictionary -ne $null) {
if ($paramDictionary.dp1.Value -ne $null) {
$paramDictionary.dp1.Value
}
else {
"dynamic parameters not passed"
}
}
else {
"no dynamic parameters"
}
}
process {}
end {}
}
}
It "The dynamic parameter is enabled and bound" {
foo-bar -path abc -dp1 42 | Should Be 42
}
It "When the dynamic parameter is not available, and raises an error when specified" {
try {
foo-bar -path def -dp1 42
Throw "Exception expected, execution should not have reached here"
} catch {
$_.FullyQualifiedErrorId | Should Be "NamedParameterNotFound,foo-bar"
}
}
It "No dynamic parameter shouldn't cause an errr " {
foo-bar -path def | Should Be 'no dynamic parameters'
}
It "Not specifying dynamic parameter shouldn't cause an error" {
foo-bar -path abc | Should Be 'dynamic parameters not passed'
}
It "Parameter is defined in Class" {
foo-bar -path class -name "myName" | Should Be 'myName'
}
}
@@ -0,0 +1,98 @@
using namespace system.collections.generic
using namespace System.Management.Automation
Describe "Generics support" -Tags "CI" {
# list and stack are in different assemblies, and dictionary
# takes more than one type parameter.
It 'Type list[Int] works properly' {
$x = [list[int]]::New()
$x.Add(42)
$x.Add(40)
$x.count | Should Be 2
}
It 'Type stack[Int] works properly' {
$x = [stack[int]]::New()
$x.Push(42)
$x.Push(40)
$x.count | Should Be 2
}
It 'Type dictionary[string, Int] works properly' {
$x = [dictionary[string, int]]::New()
$x.foo = 42
$x.foo | Should Be 42
}
It 'Type list[[Int]] works properly' {
$x = [list[[int]]]::New()
$x.Add(42)
$x.Add(40)
$x.count | Should Be 2
}
It 'Type stack[[Int]] works properly' {
$x = [stack[[int]]]::New()
$x.Push(42)
$x.Push(40)
$x.count | Should Be 2
}
It 'Type dictionary[[string], [Int]] works properly' {
$x = [dictionary[[string], [int]]]::New()
$x.foo = 42
$x.foo | Should Be 42
}
It 'Type dictionary[dictionary[list[int],string], stack[double]] works properly' {
$x = [dictionary[dictionary[list[int],string], stack[double]]]::new()
$x.gettype().fullname | should match "double"
$y = new-object "dictionary[dictionary[list[int],string], stack[double]]"
$y.gettype().fullname | should match "double"
}
It 'non-generic EventHandler works properly' {
# EventHandler has a generic and a non-generic. This code caused an exception trying to
# use the non-generic.
$x = [System.EventHandler[PSInvocationStateChangedEventArgs]]
# The error message for a generic that doesn't meet the constraints should mention which
# argument failed.
$ex = $null
try {
[nullable[object]]
Throw "Exception expected, execution should not have reached here"
} catch {
$_.FullyQualifiedErrorId | Should be 'TypeNotFoundWithMessage'
$_ | should match "\[T\]"
}
}
It 'Array type works properly' -skip:$IsCoreCLR{
$x = [system.array]::ConvertAll.OverloadDefinitions
$x | Should match "static\s+TOutput\[\]\s+ConvertAll\[TInput,\s+TOutput\]\("
}
It 'Class type works properly' {
class TestClass {
[string] $name = "default"
[int] $port = 80
[string] $scriptText = "1..6"
TestClass([string] $name1, [int] $port1, [string] $scriptText1)
{
$this.name = $name1
$this.port = $port1
$this.scriptText = $scriptText1
}
}
$x = [TestClass]::New("default1", 90, "1...5")
$x.scriptText = "1...4"
$x.name | Should Be 'default1'
$x.port | Should Be 90
$x.scriptText | Should Be "1...4"
}
}
@@ -0,0 +1,25 @@
Describe 'Tests for indexers' -Tags "CI" {
It 'Indexer in dictionary' {
$hashtable = @{ "Hello"="There" }
$hashtable["Hello"] | Should Be "There"
}
It 'Accessing a Indexed property of a dictionary that does not exist should return $NULL' {
$hashtable = @{ "Hello"="There" }
$hashtable["Hello There"] | Should Be $null
}
It 'Wmi object implements an indexer' -Skip:$IsCoreCLR {
$service = Get-WmiObject -List -Amended Win32_Service
$service.Properties["DisplayName"].Name | Should Be 'DisplayName'
}
It 'Accessing a Indexed property of a wmi object that does not exist should return $NULL' -skip:$IsCoreCLR {
$service = Get-WmiObject -List -Amended Win32_Service
$service.Properties["Hello There"] | Should Be $null
}
}
@@ -0,0 +1,141 @@
Describe 'Line endings' -Tags "CI" {
BeforeAll {
$lf = "`n"
$cr = "`r"
$crlf="`r`n"
$dq = "`""
$sq = "'"
$hereDB = "@`""
$hereDE = "`"@"
$hereSB = "@'"
$hereSE = "'@"
}
$testData = @(
@{
Name = 'CR in single quotes here-string';
NewLine = $cr
Begin = $hereSB+$cr;
End = $cr + $hereSE
},
@{
Name = 'LF in single quotes here-string';
NewLine = $lf
Begin = $hereSB+$lf;
End = $lf + $hereSE
},
@{
Name = 'CRLF in single quotes here-string';
NewLine = $cr+$lf
Begin = $hereSB+$cr+$lf;
End = $cr+$lf + $hereSE
},
@{
Name = 'CR in double quotes here string';
Begin = $hereDB + $cr;
NewLine = $cr
End = $cr + $hereDE
},
@{
Name = 'LF in double quotes here string';
Begin = $hereDB + $lf;
NewLine = $lf
End = $lf + $hereDE
},
@{
Name = 'CRLF in double quotes here string';
Begin = $hereDB + $cr + $lf;
NewLine = $cr + $lf
End = $cr + $lf + $hereDE
},
@{
Name = 'CR in double quotes here string';
Begin = $hereDB + $cr;
NewLine = $cr
End = $cr + $hereDE
},
@{
Name = 'Lf in double quotes here string';
Begin = $hereDB + $lf;
NewLine = $lf
End = $lf + $hereDE
},
@{
Name = 'CRLF in double quotes here string';
Begin = $hereDB + $cr + $lf;
NewLine = $cr + $lf
End = $cr + $lf + $hereDE
},
@{
Name = 'CR in single quotes string';
Begin = $sq;
NewLine = $cr
End = $sq
},
@{
Name = 'LF in single quotes string';
Begin = $sq;
NewLine = $lf
End = $sq
},
@{
Name = 'CRLF in single quotes string';
Begin = $sq;
NewLine = $cr + $lf
End = $sq
},
@{
Name = 'CR in double quotes string';
Begin = $dq;
NewLine = $cr
End = $dq
},
@{
Name = 'LF in double quotes string';
Begin = $dq;
NewLine = $lf
End = $dq
},
@{
Name = 'CRLF in double quotes string';
Begin = $dq;
NewLine = $cr + $lf
End = $dq
}
)
It '<Name> in expression' -TestCases:$testData {
param([string]$Name, $Begin, $End, $NewLine)
# build the content string
$expected = "This$($newline)is$($newline)a$($newline)multi$($newline)line$($newline)string"
# wrap the content in the specified begin and end quoting characters.
$content = "$($Begin)$($expected)$($End)"
$actual = iex $content
# $actual should be the content string ($expected) without the begin and end quoting characters.
$actual | Should Be $expected
}
It '<Name> in ps file' -TestCases:$testData {
param([string]$Name, $Begin, $End)
$fileName = $Name.Replace(' ', '') + '.ps1'
# build the content string
$expected = "This$($newline)is$($newline)a$($newline)multi$($newline)line$($newline)string"
# wrap the content in the specified begin and end quoting characters.
$content = "$($Begin)$($expected)$($End)"
# BUG: Set-Content is failing on linux if the file does not exit.
$null = New-item -path TESTDRIVE:$fileName -force
$content | Set-content -NoNewline -Encoding ascii -Path TESTDRIVE:\$fileName
$actual = &( "TESTDRIVE:\$fileName")
# $actual should be the content string ($expected) without the begin and end quoting characters.
$actual | Should Be $expected
}
}
$test = @"
"@
@@ -0,0 +1,45 @@
Describe 'Testing of MyInvocation' -Tags "CI" {
It 'MyInvocation works in Function' {
function myfunc
{
$MyInvocation.Line.IndexOf("myfunc") | Should BeGreaterThan -1
}
{ . myfunc } | Should Not Throw
{& myfunc } | Should Not Throw
}
It 'MyInvocation works in Filter' {
filter myfilter
{
$MyInvocation.Line.IndexOf("myfilter") | Should BeGreaterThan -1
}
{. myfilter } | Should Not Throw
{ & myfilter } | Should Not Throw
}
Context 'MyInvocation works in Script block' {
It 'MyInvocation works in dot sourced Script block' {
$a = . {$MyInvocation.Line}
$a.IndexOf('$a = . {$MyInvocation.Line}') | Should BeGreaterThan -1
}
It 'MyInvocation works in & Script block2' {
$a = & {$MyInvocation.Line}
$a.IndexOf('$a = & {$MyInvocation.Line}') | Should BeGreaterThan -1
}
It 'MyInvocation works when run Script file' {
$a = & {$MyInvocation.ScriptName}
$a.ToLower().IndexOf("myinvocation.tests.ps1") | Should BeGreaterThan -1
}
It 'MyInvocation works when dot source Script file' {
$a = . {$MyInvocation.ScriptName}
$a.ToLower().IndexOf("myinvocation.tests.ps1") | Should BeGreaterThan -1
}
}
}
@@ -0,0 +1,70 @@
Describe 'Test for cmdlet to support Ordered Attribute on hash literal nodes' -Tags "CI" {
BeforeAll {
If (-not $IsCoreCLR) {
Get-WmiObject -Query "select * from win32_environment where name='TestWmiInstance'" | Remove-WmiObject
}
}
AfterAll {
If (-not $IsCoreCLR) {
Get-WmiObject -Query "select * from win32_environment where name='TestWmiInstance'" | Remove-WmiObject
}
}
It 'New-Object - Property Parameter Must take IDictionary' {
$a = new-object psobject -property ([ordered]@{one=1;two=2})
$a | Should Not Be $null
$a.one | Should Be 1
}
Context 'Select-Xml cmdlet - Namespace parameter must take IDictionary' {
$script:a = $null
$helpXml = @'
<?xml version="1.0" encoding="utf-8" ?>
<helpItems schema="maml">
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
<command:details>
<command:name>
Stop-Transcript
</command:name>
</command:details>
</command:command>
</helpItems>
'@
{ $script:a = select-xml -content $helpXml -xpath "//command:name" -namespace (
[ordered]@{command="http://schemas.microsoft.com/maml/dev/command/2004/10";
maml="http://schemas.microsoft.com/maml/2004/10";
dev="http://schemas.microsoft.com/maml/dev/2004/10"}) } | Should Not Throw
It '$a should not be $null' { $script:a | Should Not Be $null }
}
It 'Set-WmiInstance cmdlet - Argument parameter must take IDictionary' -skip:$IsCoreCLR {
$script:a = $null
{ $script:a = set-wmiinstance -class win32_environment -argument ([ordered]@{Name="TestWmiInstance";
VariableValue="testvalu234e";
UserName="<SYSTEM>"}) } | Should Not Throw
$script:a | Should Not Be $null
$script:a.Name | Should Be "TestWmiInstance"
}
Context 'Select-Object cmdlet - Property parameter (Calculated properties) must take IDictionary' {
$script:a = $null
{$script:a = dir | select-object -property Name, (
[ordered]@{Name="IsDirectory";
Expression ={$_.PSIsContainer}})} | Should Not Throw
It '$a should not be $null' { $script:a | Should Not Be $null }
}
}