mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
344 lines
12 KiB
PowerShell
344 lines
12 KiB
PowerShell
# Copyright (c) Microsoft Corporation.
|
|
# Licensed under the MIT License.
|
|
|
|
Describe 'Generic Method invocation' -Tags 'CI' {
|
|
|
|
BeforeAll {
|
|
$EmptyArrayCases = @(
|
|
@{
|
|
Script = '[Array]::Empty[string]()'
|
|
ExpectedType = [string[]]
|
|
}
|
|
@{
|
|
Script = '[Array]::Empty[System.Collections.Generic.Dictionary[System.Numerics.BigInteger, System.Collections.Generic.List[string[,]]]]()'
|
|
ExpectedType = [System.Collections.Generic.Dictionary[System.Numerics.BigInteger, System.Collections.Generic.List[string[, ]]][]]
|
|
}
|
|
)
|
|
|
|
$IndexingAProperty = @(
|
|
@{
|
|
Script = '[object]::Property[[type]]'
|
|
IndexType = 'System.Management.Automation.Language.TypeExpressionAst'
|
|
IndexString = '[type]'
|
|
}
|
|
@{
|
|
Script = '$object.IPSubnet[[Array]::IndexOf($_.IPAddress, $_.IPAddress[0])]'
|
|
IndexType = 'System.Management.Automation.Language.InvokeMemberExpressionAst'
|
|
IndexString = '[Array]::IndexOf($_.IPAddress, $_.IPAddress[0])'
|
|
}
|
|
@{
|
|
Script = @'
|
|
[IPAddress]::Parse(
|
|
$_.IPSubnet[
|
|
[Array]::IndexOf($_.IPAddress, $_.IPAddress[0])
|
|
]
|
|
)
|
|
'@
|
|
IndexType = 'System.Management.Automation.Language.InvokeMemberExpressionAst'
|
|
IndexString = '[Array]::IndexOf($_.IPAddress, $_.IPAddress[0])'
|
|
}
|
|
@{
|
|
Script = @'
|
|
[IPAddress]::Parse(
|
|
$_.IPSubnet[
|
|
([Array]::IndexOf($_.IPAddress, $_.IPAddress[0]))
|
|
]
|
|
)
|
|
'@
|
|
IndexType = 'System.Management.Automation.Language.ParenExpressionAst'
|
|
IndexString = '([Array]::IndexOf($_.IPAddress, $_.IPAddress[0]))'
|
|
}
|
|
)
|
|
|
|
$ExpectedParseErrors = @(
|
|
@{
|
|
Script = '$object.Method[incompl'
|
|
ExpectedErrors = @('EndSquareBracketExpectedAtEndOfType')
|
|
ErrorCount = 1
|
|
}
|
|
@{
|
|
Script = '[type]::Member[incompl'
|
|
ExpectedErrors = @('EndSquareBracketExpectedAtEndOfType')
|
|
ErrorCount = 1
|
|
}
|
|
@{
|
|
Script = '$object.Method[Type1[Type2'
|
|
ExpectedErrors = @('EndSquareBracketExpectedAtEndOfAttribute','EndSquareBracketExpectedAtEndOfType')
|
|
ErrorCount = 2
|
|
}
|
|
@{
|
|
Script = '[array]::empty[type]]()'
|
|
ExpectedErrors = @('MissingArrayIndexExpression', 'UnexpectedToken', 'ExpectedExpression')
|
|
ErrorCount = 3
|
|
}
|
|
@{
|
|
Script = '$object.Method[type,]()'
|
|
ExpectedErrors = @('MissingTypename')
|
|
ErrorCount = 1
|
|
}
|
|
@{
|
|
Script = '$object.Method[]()'
|
|
ExpectedErrors = @('MissingArrayIndexExpression', 'UnexpectedToken', 'ExpectedExpression')
|
|
ErrorCount = 3
|
|
}
|
|
@{
|
|
Script = '$object.Method[,]()'
|
|
ExpectedErrors = @('MissingExpressionAfterOperator', 'UnexpectedToken', 'ExpectedExpression')
|
|
ErrorCount = 3
|
|
}
|
|
@{
|
|
Script = '$object.Method[,type]()'
|
|
ExpectedErrors = @('MissingExpressionAfterOperator', 'UnexpectedToken', 'ExpectedExpression')
|
|
ErrorCount = 3
|
|
}
|
|
@{
|
|
Script = '$object.Method[type()'
|
|
ExpectedErrors = @('EndSquareBracketExpectedAtEndOfType', 'UnexpectedToken', 'ExpectedExpression')
|
|
ErrorCount = 3
|
|
}
|
|
@{
|
|
Script = '$object.Method[type)'
|
|
ExpectedErrors = @('EndSquareBracketExpectedAtEndOfType', 'UnexpectedToken')
|
|
ErrorCount = 2
|
|
}
|
|
@{
|
|
Script = '$object.Method[[type]]()'
|
|
ExpectedErrors = @('UnexpectedToken', 'ExpectedExpression')
|
|
ErrorCount = 2
|
|
}
|
|
@{
|
|
Script = '[Array]::Empty[[type]]()'
|
|
ExpectedErrors = @('UnexpectedToken', 'ExpectedExpression')
|
|
ErrorCount = 2
|
|
}
|
|
@{
|
|
Script = '$object.Property[type]'
|
|
ExpectedErrors = @('MissingArrayIndexExpression', 'UnexpectedToken')
|
|
ErrorCount = 2
|
|
}
|
|
)
|
|
}
|
|
|
|
It 'does not throw a parse error for "<Script>"' -TestCases $EmptyArrayCases {
|
|
param($Script)
|
|
|
|
{ [scriptblock]::Create($script) } | Should -Not -Throw
|
|
}
|
|
|
|
It "parses fine for indexing a property: <Script>" -TestCases $IndexingAProperty {
|
|
param($Script, $IndexType, $IndexString)
|
|
|
|
$parseErrors = $null
|
|
|
|
$ast = [System.Management.Automation.Language.Parser]::ParseInput($Script, [ref]$null, [ref]$parseErrors)
|
|
$parseErrors | Should -BeNullOrEmpty
|
|
|
|
$cmdExpr = $ast.EndBlock.Statements[0].PipelineElements[0]
|
|
$cmdExpr | Should -BeOfType 'System.Management.Automation.Language.CommandExpressionAst'
|
|
|
|
$indexExpr = $cmdExpr.Expression -is [System.Management.Automation.Language.InvokeMemberExpressionAst] ? $cmdExpr.Expression.Arguments[0] : $cmdExpr.Expression
|
|
|
|
$indexExpr | Should -BeOfType 'System.Management.Automation.Language.IndexExpressionAst'
|
|
$indexExpr.Index | Should -BeOfType $IndexType
|
|
$indexExpr.Index.ToString() | Should -BeExactly $IndexString
|
|
}
|
|
|
|
It 'reports a parse error for "<Script>"' -TestCases $ExpectedParseErrors {
|
|
param($Script, $ExpectedErrors, $ErrorCount)
|
|
|
|
$parseErrors = $null
|
|
[System.Management.Automation.Language.Parser]::ParseInput($Script, [ref]$null, [ref]$parseErrors)
|
|
|
|
$parseErrors.Count | Should -Be $ErrorCount
|
|
|
|
for ($i = 0; $i -lt $ErrorCount; $i++) {
|
|
$parseErrors[$i].ErrorId | Should -BeExactly $ExpectedErrors[$i]
|
|
}
|
|
}
|
|
|
|
It 'can call a generic method "<Script>" with no arguments' -TestCases $EmptyArrayCases {
|
|
param($Script, $ExpectedType)
|
|
|
|
$command = "`$r = $Script; `$r.GetType(); `$r.Length"
|
|
$result = Invoke-Expression -Command $command
|
|
$result[0] | Should -Be $ExpectedType
|
|
$result[1] | Should -Be 0
|
|
}
|
|
|
|
It 'can call generic instance methods' {
|
|
$dictionary = [System.Collections.Concurrent.ConcurrentDictionary[string, int]]::new()
|
|
|
|
$addEntryScript = {
|
|
param($key, $float)
|
|
|
|
if ($float -gt 0.5) {
|
|
return 10
|
|
}
|
|
else {
|
|
return 1
|
|
}
|
|
}
|
|
|
|
$updateEntryScript = {
|
|
param($key, $currentValue, $float)
|
|
|
|
if ($currentValue / $float -gt 2) {
|
|
return 5
|
|
}
|
|
else {
|
|
return 0
|
|
}
|
|
}
|
|
|
|
$FloatValue = 0.4
|
|
$Key = 'Test'
|
|
|
|
# Add entry
|
|
$dictionary.AddOrUpdate[float]($Key, $addEntryScript, $updateEntryScript, $FloatValue)
|
|
$dictionary.$Key | Should -Be 1
|
|
|
|
# Update entry
|
|
$dictionary.AddOrUpdate[float]($Key, $addEntryScript, $updateEntryScript, $FloatValue)
|
|
$dictionary.$Key | Should -Be 5
|
|
}
|
|
|
|
It 'can call generic static methods with arguments' {
|
|
[System.Linq.Enumerable]::Select[int, int](
|
|
[int[]](0..10),
|
|
[func[int, int]] { $args[0] + 2 }
|
|
) | Should -Be @(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
|
|
}
|
|
|
|
It 'gives a runtime error if too many type parameters are given' {
|
|
try {
|
|
[array]::Empty[string, int]()
|
|
}
|
|
catch {
|
|
$_.FullyQualifiedErrorId | Should -BeExactly 'MethodCountCouldNotFindBestGeneric'
|
|
}
|
|
}
|
|
|
|
It 'gives a runtime error if a nonexistent type is specified' {
|
|
try {
|
|
[array]::Empty[thisdoesnotexist]()
|
|
}
|
|
catch {
|
|
$_.Exception.Message | Should -BeLike "*[thisdoesnotexist]*"
|
|
$_.FullyQualifiedErrorId | Should -BeExactly 'TypeNotFound'
|
|
}
|
|
}
|
|
|
|
It 'successfully invokes common Linq generic methods' {
|
|
[System.Collections.Generic.List[int]]$list = @( 1, 2, 3, 4, 5 )
|
|
$result = [System.Linq.Enumerable]::Select[int, float](
|
|
$list,
|
|
[Func[int, float]]{
|
|
param($item)
|
|
[math]::Pow($item, 3)
|
|
}
|
|
)
|
|
|
|
$result.GetType().Name | Should -BeExactly 'ListSelectIterator`2'
|
|
$typeArgs = $result.GetType().GenericTypeArguments
|
|
$typeArgs[0] | Should -Be ([int])
|
|
$typeArgs[1] | Should -Be ([float])
|
|
|
|
$resultList = $result.ToList()
|
|
$resultList.GetType().Name | Should -Be 'List`1'
|
|
$resultList.GetType().GenericTypeArguments | Should -Be ([float])
|
|
$resultList | Should -Be @( 1, 8, 27, 64, 125)
|
|
}
|
|
|
|
It 'can use type that is loaded at runtime' {
|
|
Add-Type -TypeDefinition "public class GenericTypeTest {}"
|
|
$r = [Array]::Empty[GenericTypeTest]()
|
|
|
|
$r.GetType() | Should -Be ([GenericTypeTest[]])
|
|
$r.Length | Should -Be 0
|
|
}
|
|
}
|
|
|
|
Describe "Interface inheritance with remoting proxies" -Tags "CI" {
|
|
|
|
if ( $IsCoreCLR ) {
|
|
Write-Verbose -Verbose "Skip this test because it's .NET Framework dependency."
|
|
return
|
|
}
|
|
|
|
$src = @"
|
|
using System;
|
|
using System.ServiceModel;
|
|
|
|
namespace MSFT_716893
|
|
{
|
|
[ServiceContract]
|
|
public interface IInterface1
|
|
{
|
|
[OperationContract]string BaseOperation(int i);
|
|
}
|
|
|
|
[ServiceContract]
|
|
public interface IInterface2 : IInterface1
|
|
{
|
|
[OperationContract(Name="op1")]string Operation(string a);
|
|
[OperationContract(Name="op2")]string Operation(string a, string b);
|
|
}
|
|
|
|
public class ServiceImplementation : IInterface2
|
|
{
|
|
public string Operation(string a) { return "1 - " + a; }
|
|
public string Operation(string a, string b) { return "2 - " + a + " " + b; }
|
|
public string BaseOperation(int i) { return "3 - " + i; }
|
|
}
|
|
|
|
public static class Service
|
|
{
|
|
static ServiceHost serviceHost;
|
|
|
|
public static void Init()
|
|
{
|
|
Uri baseAddress = new Uri("http://localhost:8080/service");
|
|
serviceHost = new ServiceHost(typeof(ServiceImplementation), baseAddress);
|
|
serviceHost.Open();
|
|
}
|
|
|
|
public static IInterface1 GetProxy()
|
|
{
|
|
ChannelFactory<IInterface2> factory = new ChannelFactory<IInterface2>(
|
|
serviceHost.Description.Endpoints[0].Binding,
|
|
serviceHost.Description.Endpoints[0].Address);
|
|
return factory.CreateChannel();
|
|
}
|
|
|
|
public static void Close()
|
|
{
|
|
serviceHost.Close();
|
|
}
|
|
}
|
|
}
|
|
"@
|
|
|
|
Add-Type -TypeDefinition $src -ReferencedAssemblies System.ServiceModel.dll
|
|
|
|
BeforeEach {
|
|
[MSFT_716893.Service]::Init()
|
|
$proxy = [MSFT_716893.Service]::GetProxy()
|
|
}
|
|
|
|
AfterEach {
|
|
[MSFT_716893.Service]::Close()
|
|
}
|
|
|
|
It "Direct invocation" {
|
|
$proxy.Operation("a") | Should -Be "1 - a"
|
|
$proxy.Operation("a", "b") | Should -Be "2 - a b"
|
|
$proxy.BaseOperation(42) | Should -Be "3 - 42"
|
|
}
|
|
|
|
It "Invocation via method constraints" {
|
|
([MSFT_716893.IInterface2]$proxy).Operation("c") | Should -Be "1 - c"
|
|
([MSFT_716893.IInterface2]$proxy).Operation("d", "e") | Should -Be "2 - d e"
|
|
([MSFT_716893.IInterface1]$proxy).BaseOperation(22) | Should -Be "3 - 22"
|
|
}
|
|
}
|