diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Implicit.Remoting.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Implicit.Remoting.Tests.ps1
new file mode 100644
index 0000000000..6f03982280
--- /dev/null
+++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Implicit.Remoting.Tests.ps1
@@ -0,0 +1,2001 @@
+##
+## Remoting Implicit script tests
+##
+
+$path = $PSScriptRoot
+if ($path -eq $null) { $path = Split-Path $MyInvocation.InvocationName }
+if ($path -eq $null) { $path = $pwd }
+
+import-module (join-path $path ".\RemotingCommon.psm1")
+
+
+Describe "Implicit remoting and CIM cmdlets with AllSigned and Restricted policy" -tags 'Innerloop', 'P1' {
+
+ try
+ {
+ ##############################################################################
+ # TEST SETUP - CREATE TEMP DIRECTORY
+ #
+
+ $modulesDir = $env:TEMP -split ';' | select -first 1
+
+ $tempdir = join-path $modulesDir ([IO.Path]::GetRandomFileName())
+ mkdir $tempdir | Out-Null
+
+ $fileName = [io.path]::GetFileName($tempdir)
+
+
+ ##############################################################################
+ # GET CERTIFICATE
+ #
+
+ $tempName = "$env:temp\signedscript_$(get-random).ps1"
+ "123456" >$tempName
+ $cert = $null
+ foreach ($thisCertificate in (dir cert:\ -rec -codesigning))
+ {
+ $null = set-authenticodesignature $tempName -cert $thisCertificate
+ if ((get-authenticodesignature $tempName).Status -eq "Valid")
+ {
+ $cert = $thisCertificate
+ break
+ }
+ }
+
+ if ($cert -eq $null) { return }
+
+ # ensure the cert is trusted
+ if (-not (Test-Path "cert:\currentuser\TrustedPublisher\$($cert.Thumbprint)"))
+ {
+ $store = New-Object System.Security.Cryptography.X509Certificates.X509Store "TrustedPublisher"
+ $store.Open("ReadWrite")
+ $store.Add($cert)
+ $store.Close()
+ }
+
+
+ ##############################################################################
+ # TEST - Verifying that Import-PSSession signs the files
+ #
+
+ $oldExecutionPolicy = Get-ExecutionPolicy -Scope Process
+ Set-ExecutionPolicy AllSigned -Scope Process
+
+ $s = New-PSSession
+
+ $importedModule = Import-PSSession $s Get-Variable -Prefix Remote -Certificate $cert -AllowClobber
+ It "Verifies that Import-PSSession works in AllSigned if Certificate is used" {
+ $importedModule | Should Not Be $null
+ }
+
+ $importedModule | Remove-Module -Force -ErrorAction SilentlyContinue
+
+ $caught = $false
+ try
+ {
+ $importedModule = Import-PSSession $s Get-Variable -Prefix Remote -AllowClobber
+ }
+ catch { $caught = $true; }
+
+ It "Verifies security error when Certificate parameter is not used" {
+ $caught | Should Be $true
+ }
+ }
+ finally
+ {
+ if ($oldExecutionPolicy -ne $null) { Set-ExecutionPolicy $oldExecutionPolicy -Scope Process }
+ if ($s -ne $null) { Remove-PSSession $s -ErrorAction SilentlyContinue }
+ if ($tempdir -ne $null) { remove-item $tempdir -recurse -force -ea silentlycontinue }
+ }
+}
+
+Describe "Tests Import-Proxy cmdlet function works with types unavailable on the client" -tags 'Innerloop', 'P1' {
+
+ $typeDefinition = @"
+ namespace MyTest
+ {
+ public enum MyEnum
+ {
+ Value1 = 1,
+ Value2 = 2
+ }
+ }
+"@
+
+ try
+ {
+ $r = New-PSSession
+ icm -Session $r -Script { Add-Type -TypeDefinition $args[0] } -Args $typeDefinition
+ icm -Session $r -Script { function foo { param([MyTest.MyEnum][Parameter(Mandatory = $true)]$x) $x } }
+
+ $module = Import-PSSession -Session $r -CommandName foo -AllowClobber
+
+ It "Verifies client-side unavailable enum is treated as an int" {
+ (foo -x "Value2") | Should Be 2
+ }
+ It "Verifies client-side unavailable enum is to-string-ed appropriately" {
+ ((foo -x "Value2").ToString()) | Should Be "Value2"
+ }
+ }
+ finally
+ {
+ if ($r -ne $null) { Remove-PSSession $r -ErrorAction SilentlyContinue }
+ if ($module -ne $null) { Remove-Module $module -Force -ErrorAction SilentlyContinue }
+ }
+}
+
+
+Describe "Cmdlet help from remote session" -tags 'Innerloop', 'P1' {
+
+ try
+ {
+ $s = New-PSSession
+ $module = import-pssession $s -name select-object -prefix my -AllowClobber
+ $gcmOutPut = (get-command select-myobject).Name
+ $getHelpOutPut = (get-help select-myobject).Name
+
+ It "Verifies that get-help name for remote proxied commands matches the get-command name" {
+ $gcmOutPut | Should Be $getHelpOutPut
+ }
+ }
+ finally
+ {
+ if ($s -ne $null) { Remove-PSSession $s -ErrorAction SilentlyContinue }
+ if ($module -ne $null) { Remove-Module $module -Force -ErrorAction SilentlyContinue }
+ }
+}
+
+
+Describe "Import-Proxy Cmdlet error handling" -tags 'Innerloop', 'P1' {
+
+ try
+ {
+ $s = New-PSSession
+
+ ##############################################################################
+ # TEST: error message for broken aliases
+
+ Invoke-Command $s { set-alias BrokenAlias NonExistantCommand }
+ $module = Import-PSSession $s -CommandName:BrokenAlias -CommandType:All -ErrorAction SilentlyContinue -ErrorVariable expectedError -AllowClobber
+
+ It "Verifies that broken alias results in one error" {
+ $expectedError | Should Not Be NullOrEmpty
+ }
+ It "Verifies that broken alias error contains expected 'BrokenAlias' name" {
+ $expectedError[0].ToString().Contains("BrokenAlias") | Should Be $true
+ }
+
+ if ($module -ne $null) { Remove-Module $module -Force -ErrorAction SilentlyContinue; $module = $null }
+ Invoke-Command $s { del alias:BrokenAlias }
+
+
+ ##############################################################################
+ # TEST: content and format of proxied error message (Windows 7: #319080)
+
+ $module = Import-PSSession -Session:$s -Name:Get-Variable -Prefix My -AllowClobber
+
+ # non-terminating error
+ $results = Get-MyVariable blah,pid 2>&1
+ It "Verifies that returned PID is not for this session" {
+ ($results[1]).Value | Should Not Be $pid
+ }
+ $errorString = $results[0] | Out-String
+ It "Verifies error message for variable blah" {
+ ($errorString -like "*VariableNotFound*") | Should Be $true
+ }
+
+ #terminating error
+ $results = Get-MyVariable pid -Scope blah 2>&1
+ It "Verifies that remote session pid is not returned" {
+ $results.Count | Should Be 1
+ }
+ $errorString = $results[0] | Out-String
+ It "Verifes error message for incorrect Scope parameter argument" {
+ ($errorString -like "*Argument*") | Should Be $true
+ }
+
+ if ($module -ne $null) { Remove-Module $module -Force -ErrorAction SilentlyContinue; $module = $null }
+
+
+ ##############################################################################
+ # TEST: ordering of a sequence of error and output messages (Windows 7: #405065)
+
+ icm $s {function foo1{1; write-error 2; 3; write-error 4; 5; write-error 6}}
+ $module = Import-PSSession $s -CommandName foo1 -AllowClobber
+
+ $icmErr = $($icmOut = icm $s { foo1 }) 2>&1
+ $proxiedErr = $($proxiedOut = foo1) 2>&1
+ $proxiedOut2 = foo1 2>$null
+
+ $icmOut = "$icmOut"
+ $icmErr = "$icmErr"
+ $proxiedOut = "$proxiedOut"
+ $proxiedOut2 = "$proxiedOut2"
+ $proxiedErr = "$proxiedErr"
+
+ It "Verifies proxied output = proxied output 2" {
+ $proxiedOut2 | Should Be $proxiedOut
+ }
+ It "Verifies proxied output = icm output (for mixed error and output results)" {
+ $icmOut | Should Be $proxiedOut
+ }
+ It "Verifies proxied error = icm error (for mixed error and output results)" {
+ $icmErr | Should Be $proxiedErr
+ }
+
+ $icmOrder = icm $s { foo1 } 2>&1 | out-string
+ $proxiedOrder = foo1 2>&1 | out-string
+
+ It "Verifies proxied order = icm order (for mixed error and output results)" {
+ $icmOrder | Should Be $proxiedOrder
+ }
+
+ if ($module -ne $null) { Remove-Module $module -Force -ErrorAction SilentlyContinue; $module = $null }
+
+
+ ##############################################################################
+ # TEST: is WarningVariable parameter working with implicit remoting (Windows 8: #44861)
+
+ $m = Import-PSSession $s -CommandName Write-Warning -Prefix Remote -AllowClobber
+ $global:myWarningVariable = @()
+ Write-RemoteWarning MyWarning -WarningVariable global:myWarningVariable
+ It "Verifies WarningVariable" {
+ ([string]($myWarningVariable[0])) | Should Be 'MyWarning'
+ }
+ }
+ finally
+ {
+ if ($s -ne $null) { Remove-PSSession $s -ErrorAction SilentlyContinue }
+ if ($module -ne $null) { Remove-Module $module -Force -ErrorAction SilentlyContinue; $module = $null }
+ }
+}
+
+
+Describe "Tests Export-PSSession" -tags 'Innerloop', 'P1' {
+
+ function Get-TempModuleFile
+ {
+ [IO.Path]::Combine([IO.Path]::GetTempPath(), [Guid]::NewGuid().ToString())
+ }
+
+ try
+ {
+ $sessionOption = New-PSSessionOption -ApplicationArguments @{myTest="MyValue"}
+ $s = New-PSSession -SessionOption $sessionOption
+
+ $file = Get-TempModuleFile
+ $results = Export-PSSession -Session $s -CommandName Get-Variable -AllowClobber -ModuleName $file
+ It "Verifies Export-PSSession creates a file/directory" {
+ @(Get-Item $file).Count | Should Be 1
+ }
+ It "Verifies Export-PSSession creates a psd1 file" {
+ ($results | ?{ $_.Name -like "*$(Split-Path -Leaf $file).psd1" }) | Should Be $true
+ }
+ It "Verifies Export-PSSession creates a psm1 file" {
+ ($results | ?{ $_.Name -like "*.psm1" }) | Should Be $true
+ }
+ It "Verifies Export-PSSession creates a ps1xml file" {
+ ($results | ?{ $_.Name -like "*.ps1xml" }) | Should Be $true
+ }
+ $oldTimestamp = $($results | Select -First 1).LastWriteTime
+
+ #
+ # error when trying to overwrite an existing directory
+ #
+
+ $msg = $null
+ try
+ {
+ Export-PSSession -Session $s -CommandName Get-Variable -AllowClobber -ModuleName $file -EA SilentlyContinue -ErrorVariable expectedError
+ }
+ catch { }
+ It "Verifies that Export-PSSession fails when a module directory already exists" {
+ $expectedError | Should Not Be NullOrEmpty
+ }
+ $msg = [string]($expectedError[0])
+ It "Verifies Error contains reference to the directory that already exists" {
+ ($msg -like "*$file*") | Should Be $true
+ }
+
+ $newResults = Export-PSSession -Session $s -CommandName Get-Variable -AllowClobber -ModuleName $file -Force
+ It "Verifies that Export-PSSession returns 4 files" {
+ (@($newResults).Count) | Should Be 4
+ }
+ $newResults | % {
+ It "Verifies that Export-PSSession creates *new* files" {
+ $_.LastWriteTime | Should BeGreaterThan $oldTimestamp
+ }
+ }
+
+ #
+ # the module is usable when the original runspace is still around
+ #
+
+ $module = import-Module $file -PassThru
+ It "Verifies that proxy returns remote pid" {
+ (Get-Variable -Name:pid).Value | Should Not Be $pid
+ }
+ Remove-Module $module -Force -ErrorAction SilentlyContinue
+ It "Verfies Remove-Module doesn't remove user's runspace" {
+ (Get-PSSession -InstanceId $s.InstanceId) | Should Not Be NullOrEmpty
+ }
+
+ #
+ # only explicitly imported/exported commands are modifier
+ #
+ It "Verifies that no Get-Variable function before this test" {
+ ((Get-Item function:Get-Variable -ErrorAction SilentlyContinue) -eq $null) | Should Be $true
+ }
+
+ set-item function:global:Get-Variable { param($Name) Microsoft.PowerShell.Utility\Get-Variable -Name:$Name }
+ $module = import-Module $file -PassThru -Force -Function @()
+ It "Verifes that our global function didn't get overwritten" {
+ (Get-Variable -Name:pid).Value | Should Be $pid
+ }
+ It "Verifies module.ExportedFunctions.Contains('Get-Variable')" {
+ ($module.ExportedFunctions.ContainsKey("Get-Variable")) | Should Be $true
+ }
+ Remove-Module $module
+
+ Remove-Item function:global:Get-Variable -ErrorAction SilentlyContinue
+ Remove-Item function:script:Get-Variable -ErrorAction SilentlyContinue
+ Remove-Item function:Get-Variable -ErrorAction SilentlyContinue
+ It "Verifies no Get-Variable function after this test" {
+ ((Get-Item function:Get-Variable -ErrorAction SilentlyContinue) -eq $null) | Should Be $true
+ }
+ }
+ finally
+ {
+ if ($s -ne $null) { Remove-PSSession $s -ErrorAction SilentlyContinue }
+ if ($module -ne $null) { Remove-Module $module -Force -ErrorAction SilentlyContinue }
+ }
+
+
+ try
+ {
+ ##############################################################################
+ # Export-PSSession tests continued - runspace created by the module
+
+ # the module is usable when the original runspace is no longer around
+
+ $testUI = $null
+ try
+ {
+ if ($host.Name -eq "test host")
+ {
+ $testUI = $host.UI.GetType().GetField("externalUI", "IgnoreCase,Instance,NonPublic").GetValue($host.UI)
+ $oldIgnoreWrites = $testUI.IgnoreWrites
+ $testUI.IgnoreWrites = $true # we expect "Creating a new runspace for implicit remoting of 'Get-Variable' command..." message
+ }
+ $module = import-Module $file -PassThru -Force
+ It "Verifies proxy should return remote pid" {
+ (Get-Variable -Name:pid).Value | Should Not Be $pid
+ }
+ }
+ finally
+ {
+ if ($testUI)
+ {
+ $testUI.IgnoreWrites = $oldIgnoreWrites
+ }
+ }
+
+ # let's verify if ApplicationArguments got preserved correctly
+ $s = & $module { $script:PSSession }
+ It "Verifies ApplicationArguments got preserved correctly" {
+ $(icm $s { $PSSenderInfo.ApplicationArguments.MyTest }) | Should Be "MyValue"
+ }
+
+ # removing the module should remove the implicitly/magically created runspace
+ Remove-Module $module
+ It "Verifies Remove-Module removed automatically created runspace" {
+ ((Get-PSSession -InstanceId $s.InstanceId -ErrorAction SilentlyContinue) -eq $null) | Should Be $true
+ }
+ It "Verifies Runspace is closed after removing module from Export-PSSession that got initialized with an internal r-space" {
+ ($s.Runspace.RunspaceStateInfo.ToString()) | Should Be "Closed"
+ }
+
+ ##############################################################################
+ # Export-PSSession tests continued - runspace created by the module with explicit session options
+
+ try
+ {
+ if ($host.Name -eq "test host")
+ {
+ $testUI = $host.UI.GetType().GetField("externalUI", "IgnoreCase,Instance,NonPublic").GetValue($host.UI)
+ $oldIgnoreWrites = $testUI.IgnoreWrites
+ $testUI.IgnoreWrites = $true # we expect "Creating a new runspace for implicit remoting of 'Get-Variable' command..." message
+ }
+ $explicitSessionOption = New-PSSessionOption -Culture fr-FR -UICulture de-DE
+ $module = import-Module $file -PassThru -Force -Args $null,$explicitSessionOption
+ It "Verifies proxy should return remote pid" {
+ (Get-Variable -Name:pid).Value | Should Not Be $pid
+ }
+ }
+ finally
+ {
+ if ($testUI)
+ {
+ $testUI.IgnoreWrites = $oldIgnoreWrites
+ }
+ }
+
+ # culture settings should be taken from the explicitly passed session options
+ It "Verifies proxy returns modified culture" {
+ (Get-Variable -Name:PSCulture).Value | Should Be "fr-FR"
+ }
+ It "Verifies proxy returns modified culture" {
+ (Get-Variable -Name:PSUICulture).Value | Should Be "de-DE"
+ }
+
+ # removing the module should remove the implicitly/magically created runspace
+ $s = & $module { $script:PSSession }
+ Remove-Module $module
+ It "Verifies Remove-Module removes automatically created runspace" {
+ ((Get-PSSession -InstanceId $s.InstanceId -ErrorAction SilentlyContinue) -eq $null) | Should Be $true
+ }
+ It "Verifies Runspace is closed after removing module from Export-PSSession that got initialized with an internal r-space" {
+ ($s.Runspace.RunspaceStateInfo.ToString()) | Should Be "Closed"
+ }
+
+ ##############################################################################
+ # Export-PSSession tests continued - passing a runspace into module
+
+ $s = New-PSSession #
+ $module = import-Module $file -PassThru -Force -Args $s
+
+ It "Verifies proxy returns remote pid" {
+ (Get-Variable -Name:pid).Value | Should Not Be $pid
+ }
+ It "Verifies switch parameters work" {
+ (Get-Variable -Name:pid -ValueOnly) | Should Not Be $pid
+ }
+ It "Verifies Adding a module affects runspace's state" {
+ ($s.Runspace.RunspaceStateInfo.ToString()) | Should Be "Opened"
+ }
+ Remove-Module $module
+ It "Verifies Runspace stays opened after removing module from Export-PSSession that got initialized with an external runspace" {
+ ($s.Runspace.RunspaceStateInfo.ToString()) | Should Be "Opened"
+ }
+ }
+ finally
+ {
+ if ($s -ne $null) { Remove-PSSession $s -ErrorAction SilentlyContinue }
+ if ($module -ne $null) { Remove-Module $module -Force -ErrorAction SilentlyContinue }
+ if ($file -ne $null) { Remove-Item $file -Force -Recurse -ErrorAction SilentlyContinue }
+ }
+}
+
+
+Describe "Import-Proxy with FormatAndTypes" -tags 'Innerloop', 'P1' {
+
+ function CreateTempPs1xmlFile
+ {
+ do {
+ $tmpFile = [IO.Path]::Combine([IO.Path]::GetTempPath(), [IO.Path]::GetRandomFileName()) + ".ps1xml";
+ } while ([io.file]::exists($tmpFile))
+ $tmpFile
+ }
+
+ function CreateTypeFile {
+ $tmpFile = CreateTempPs1xmlFile
+@"
+
+
+ System.Management.Automation.Host.Coordinates
+
+
+ MyTestLabel
+ 123
+
+
+
+
+ MyTest.Root
+
+
+ PSStandardMembers
+
+
+ SerializationDepth
+ 1
+
+
+
+
+
+
+ MyTest.Son
+
+
+ PSStandardMembers
+
+
+ SerializationDepth
+ 1
+
+
+
+
+
+
+ MyTest.Grandson
+
+
+ PSStandardMembers
+
+
+ SerializationDepth
+ 1
+
+
+
+
+
+
+"@ | set-content $tmpFile
+ $tmpFile
+ }
+
+ function CreateFormatFile {
+ $tmpFile = CreateTempPs1xmlFile
+ @"
+
+
+
+ MySizeView
+
+ System.Management.Automation.Host.Size
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Width
+
+
+ Height
+
+
+
+
+
+
+
+
+"@ | set-content $tmpFile
+ $tmpFile
+ }
+
+ try
+ {
+ $s = New-PSSession
+
+ ##############################################################################
+ # TEST: importing format file works
+
+ $date = Get-Date
+ $formattingScript = { new-object System.Management.Automation.Host.Size | %{ $_.Width = 123; $_.Height = 456; $_ } | Out-String }
+ $typeDefinition = @"
+ namespace MyTest
+ {
+ public enum MyEnum
+ {
+ Value1 = 1,
+ Value2 = 2
+ }
+ }
+"@
+
+ icm -Session $s -Script { Add-Type -TypeDefinition $args[0] } -Args $typeDefinition
+ icm -Session $s -Script { function foo { param([MyTest.MyEnum][Parameter(Mandatory = $true)]$x) $x } }
+
+ $originalLocalFormatting = & $formattingScript
+ $originalRemoteFormatting = icm $s $formattingScript
+ It "original local and remote formatting should be equal (sanity check)" {
+ $originalLocalFormatting | Should Be $originalRemoteFormatting
+ }
+
+ $formatFile = CreateFormatFile
+ icm $s { param($file) update-formatdata $file } -args $formatFile
+ $modifiedRemoteFormatting = icm $s $formattingScript
+ It "original remote and modified remote formatting should not be equal (sanity check)" {
+ $originalRemoteFormatting | Should Not Be $modifiedRemoteFormatting
+ }
+
+ $module = import-pssession -Session $s -CommandName @() -FormatTypeName * -AllowClobber
+ $importedLocalFormatting = & $formattingScript
+ It "modified remote and imported local should be equal" {
+ $modifiedRemoteFormatting | Should Be $importedLocalFormatting
+ }
+
+ Remove-Module $module
+ $unimportedLocalFormatting = & $formattingScript
+ It "original local and unimported local should be equal" {
+ $originalLocalFormatting | Should Be $unimportedLocalFormatting
+ }
+
+ ##############################################################################
+ # TEST: updating type table in a middle of a command has effect on serializer
+
+ $typeFile = CreateTypeFile
+
+ $results = icm $s -args $typeFile { param($file)
+ new-object System.Management.Automation.Host.Coordinates
+ update-typedata $file
+ new-object System.Management.Automation.Host.Coordinates
+ }
+
+ It "Should get 2 deserialized S.M.A.H.Coordinates objects" {
+ ($results.Count) | Should Be 2
+ }
+ It "First object shouldn't have the additional ETS note property" {
+ ($results[0].MyTestLabel -eq $null) | Should Be $true
+ }
+ It "Second object should have the additional ETS note property" {
+ ($results[1].MyTestLabel) | Should Be 123
+ }
+
+ ##############################################################################
+ # TEST: implicit remoting works even when types.ps1xml is missing on the client
+
+ $typeDefinition = @"
+ namespace MyTest
+ {
+ public class Root
+ {
+ public Root(string s) { text = s; }
+ public Son Son = new Son();
+ public string text;
+ }
+
+ public class Son
+ {
+ public Grandson Grandson = new Grandson();
+ }
+
+ public class Grandson
+ {
+ public string text = "Grandson";
+ }
+ }
+"@
+
+ icm -Session $s -Script { Add-Type -TypeDefinition $args[0] } -Args $typeDefinition
+ icm -Session $s -Script { function foo { new-object MyTest.Root "root" } }
+ icm -Session $s -Script { function bar { param([Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]$Son) $Son.Grandson.text } }
+
+ $m = import-pssession $s foo,bar -AllowClobber
+
+ $x = foo
+ It "Serialization works for top-level properties" {
+ ($x.text) | Should Be "root"
+ }
+ It "Serialization settings works for deep properties" {
+ ($x.Son.Grandson.text) | Should Be "Grandson"
+ }
+ $y = foo | bar
+ It "Serialization settings are preserved even if types.ps1xml is missing on the client" {
+ $y | Should Be "Grandson"
+ }
+ }
+ finally
+ {
+ if ($s -ne $null) { Remove-PSSession $s -ErrorAction SilentlyContinue }
+ if ($formatFile -ne $null) { Remove-Item $formatFile -Force -ErrorAction SilentlyContinue }
+ if ($typeFile -ne $null) { Remove-Item $typeFile -Force -ErrorAction SilentlyContinue }
+ if ($m -ne $null) { Remove-Module $m -Force -ErrorAction SilentlyContinue }
+ }
+}
+
+
+Describe "Import-PSSession functional tests" -tags 'Innerloop', 'P1' {
+
+ try
+ {
+ $s = New-PSSession
+
+ # define a remote function
+ Invoke-Command -Session $s { function MyFunction { param($x) "x = '$x'; args = '$args'" } }
+
+ # define a remote proxy script cmdlet
+ $remoteCommandType = $ExecutionContext.InvokeCommand.GetCommand('Get-Variable', [System.Management.Automation.CommandTypes]::Cmdlet)
+ $remoteProxyBody = [System.Management.Automation.ProxyCommand]::Create($remoteCommandType)
+ $remoteProxyDeclaration = "function Get-VariableProxy { $remoteProxyBody }"
+ Invoke-Command -Session $s { param($x) Invoke-Expression $x } -Arg $remoteProxyDeclaration
+ $remoteAliasDeclaration = "set-alias gvalias Get-Variable"
+ Invoke-Command -Session $s { param($x) Invoke-Expression $x } -Arg $remoteAliasDeclaration
+ del alias:gvalias -force -ea silentlycontinue
+
+ # import a remote function, script cmdlet, cmdlet, native application, alias
+ $module = Import-PSSession -Session $s -Name MyFunction,Get-VariableProxy,Get-Variable,gvalias,cmd -AllowClobber -Type All
+ It "Import-PSSession should return a PSModuleInfo object" {
+ $module | Should Not Be NullOrEmpty
+ }
+ It "Import-PSSession should return a PSModuleInfo object" {
+ ($module -is [System.Management.Automation.PSModuleInfo]) | Should Not Be NullOrEmpty
+ }
+ It "Helper functions should not be imported" {
+ ((Get-Item function:*PSImplicitRemoting* -ErrorAction SilentlyContinue) -eq $null) | Should Be $true
+ }
+
+ # test calling implicit remoting proxies
+ It "NoName-ef2e1dbb-6278-4c1f-99b8-5edd68aa1679" {
+ (MyFunction 1 2 3) | Should Be "x = '1'; args = '2 3'"
+ }
+
+ It "proxy should return remote pid" {
+ (Get-VariableProxy -Name:pid).Value | Should Not Be $pid
+ }
+ It "proxy should return remote pid" {
+ (Get-Variable -Name:pid).Value | Should Not Be $pid
+ }
+ It "proxy should return remote pid" {
+ $(& (Get-Command gvalias -Type alias) -Name:pid).Value | Should Not Be $pid
+ }
+
+ Invoke-Command -Session $s { $env:TestImplicitRemotingVariable = 123 }
+ It "NoName-c8aeb5c8-2388-4d64-98c1-a9c6c218d404" {
+ (cmd.exe /c "echo TestImplicitRemotingVariable=%TestImplicitRemotingVariable%") | Should Be "TestImplicitRemotingVariable=123"
+ }
+
+ # test what happens after the runspace is closed
+ Remove-PSSession $s
+ $s = $null
+
+ # The loop below works around the fact that PSEventManager uses threadpool worker to queue event handler actions to process later
+ # Usage of threadpool means that it is impossible to predict when the event handler will run (this is Windows 8 Bugs: #882977)
+ $i = 0
+ while ( ($i -lt 20) -and ($null -ne (Get-Module | ?{ $_.Path -eq $module.Path })) )
+ {
+ $i++
+ Start-Sleep -Milliseconds 50
+ }
+ Write-Host "Workaround for bug 882977 used $i iterations"
+ It "Temporary module should be automatically removed after runspace is closed" {
+ ((Get-Module | ?{ $_.Path -eq $module.Path }) -eq $null) | Should Be $true
+ }
+
+ It "Temporary psm1 file should be automatically removed after runspace is closed" {
+ ((Get-Item $module.Path -ErrorAction SilentlyContinue) -eq $null) | Should Be $true
+ }
+
+ # Check that the implicit remoting event has been removed.
+ $implicitEventCount = 0
+ foreach ($item in $ExecutionContext.Events.Subscribers)
+ {
+ if ($item.SourceIdentifier -match "Implicit remoting event") { $implicitEventCount++ }
+ }
+ It "Event should be unregistered when the runspace is closed" {
+ 0 | Should Be ($implicitEventCount)
+ }
+
+ It "Private functions from the implicit remoting module shouldn't get imported into global scope" {
+ 0 | Should Be @(dir function:*Implicit* -ErrorAction SilentlyContinue).Count
+ }
+ }
+ finally
+ {
+ if ($s -ne $null) { Remove-PSSession $s -ErrorAction SilentlyContinue }
+ if ($module -ne $null) { Remove-Module $module -ErrorAction SilentlyContinue }
+ }
+}
+
+
+Describe "Implicit remoting parameter binding" -tags 'Innerloop', 'P1' {
+
+ try
+ {
+ $s = New-PSSession
+
+ ##############################################################################
+ # TEST: work-around for dynamic parameters via -ArgumentList parameter
+
+ $dynamicParametersTest = @'
+ function Get-DynamicParameters
+ {
+ [CmdletBinding(DefaultParameterSetName='DefaultParameterSet')]
+
+ param(
+ [Parameter(ParameterSetName = "DefaultParameterSet")]
+ $staticParameter
+ )
+
+ dynamicParam
+ {
+ $attributes = new-object System.Management.Automation.ParameterAttribute
+ $attributes.ParameterSetName = 'DefaultParameterSet'
+ $attributes.Mandatory = $false
+
+ $attributeCollection = new-object -Type System.Collections.ObjectModel.Collection``1[System.Attribute]
+ $attributeCollection.Add($attributes)
+
+ $dynParam1 = new-object System.Management.Automation.RuntimeDefinedParameter("dynamicParameter", [int], $attributeCollection)
+
+ $paramDictionary = new-object System.Management.Automation.RuntimeDefinedParameterDictionary
+ $paramDictionary.Add("dynamicParameter", $dynParam1)
+
+ return $paramDictionary
+ }
+
+ begin
+ {
+ "static=$staticParameter; dynamic=$($paramDictionary.dynamicParameter.Value)"
+ }
+ process {}
+ end {}
+ }
+'@
+
+ # unfortunately -ArgumentList works only for cmdlets, not for script cmdlets
+ Invoke-Command -Session $s { param($x) Invoke-Expression $x } -Arg $dynamicParametersTest
+
+ ##############################################################################
+ # TEST: binding of ValueFromPipeline type of parameters
+
+ $module = Import-PSSession -Session:$s -Name:Get-Random -AllowClobber
+ $x = 1..20 | Get-Random -Count 5
+ It "Binding of ValueFromPipeline should work" {
+ $x.Count | Should Be 5
+ }
+ Remove-Module $module
+
+ ##############################################################################
+ # TEST: pipeline-based parameter binding works even when client has no type constraints (Windows 7: #391157)
+
+ icm $s {
+ function foo {
+ [cmdletbinding(defaultparametersetname="string")]
+ param(
+ [string]
+ [parameter(ParameterSetName="string", ValueFromPipeline = $true)]
+ $string,
+
+ [ipaddress]
+ [parameter(ParameterSetName="ipaddress", ValueFromPipeline = $true)]
+ $ipaddress
+ )
+
+ "Bound parameter: $($myInvocation.BoundParameters.Keys | sort)"
+ }
+ }
+
+ It "Sanity check (no remoting). Pipeline binding works even if it relies on type constraints" {
+ $(icm $s {"s" | foo}) | Should Be "Bound parameter: string"
+ }
+ It "Sanity check (no remoting). Pipeline binding works even if it relies on type constraints" {
+ $(icm $s {[ipaddress]::parse("127.0.0.1") | foo}) | Should Be "Bound parameter: ipaddress"
+ }
+
+ $module = Import-PSSession $s foo -AllowClobber
+ It "Pipeline binding works even if it relies on type constraints" {
+ $("s" | foo) | Should Be "Bound parameter: string"
+ }
+ It "Pipeline binding works even if it relies on type constraints" {
+ $([ipaddress]::parse("127.0.0.1") | foo) | Should Be "Bound parameter: ipaddress"
+ }
+ Remove-Module $module
+
+ ##############################################################################
+ # TEST: pipeline-based parameter binding works even when client has no type constraints and parameterset is ambiguous (Windows 7: #430379)
+
+ icm $s {
+ function foo {
+ param(
+ [string]
+ [parameter(ParameterSetName="string", ValueFromPipeline = $true)]
+ $string,
+
+ [ipaddress]
+ [parameter(ParameterSetName="ipaddress", ValueFromPipeline = $true)]
+ $ipaddress
+ )
+
+ "Bound parameter: $($myInvocation.BoundParameters.Keys)"
+ }
+ }
+
+ It "Sanity check (no remoting). Pipeline binding works even if it relies on type constraints and parameter set is ambiguous" {
+ $(icm $s {"s" | foo}) | Should Be "Bound parameter: string"
+ }
+ It "Sanity check (no remoting). Pipeline binding works even if it relies on type constraints and parameter set is ambiguous" {
+ $(icm $s {[ipaddress]::parse("127.0.0.1") | foo}) | Should Be "Bound parameter: ipaddress"
+ }
+
+ $module = Import-PSSession $s foo -AllowClobber
+ It "Pipeline binding works even if it relies on type constraints and parameter set is ambiguous" {
+ $("s" | foo) | Should Be "Bound parameter: string"
+ }
+ It "Pipeline binding works even if it relies on type constraints and parameter set is ambiguous" {
+ $([ipaddress]::parse("127.0.0.1") | foo) | Should Be "Bound parameter: ipaddress"
+ }
+ Remove-Module $module
+
+ ##############################################################################
+ # TEST: pipeline-based parameter binding works even when one of parameters
+ # that can be bound by pipeline gets bound by name
+
+ icm $s {
+ function foo {
+ param(
+ [DateTime]
+ [parameter(ValueFromPipeline = $true)]
+ $date,
+
+ [ipaddress]
+ [parameter(ValueFromPipeline = $true)]
+ $ipaddress
+ )
+
+ "Bound parameter: $($myInvocation.BoundParameters.Keys | sort)"
+ }
+ }
+
+ It "Sanity check (no remoting)" {
+ $( icm $s { get-date | foo } ) | Should Be "Bound parameter: date"
+ }
+ It "Sanity check (no remoting)" {
+ $( icm $s { [ipaddress]::parse("127.0.0.1") | foo } ) | Should Be "Bound parameter: ipaddress"
+ }
+ It "Sanity check (no remoting)" {
+ $( icm $s { [ipaddress]::parse("127.0.0.1") | foo -date $(get-date) } ) | Should Be "Bound parameter: date ipaddress"
+ }
+ It "Sanity check (no remoting)" {
+ $( icm $s { get-date | foo -ipaddress ([ipaddress]::parse("127.0.0.1")) } ) | Should Be "Bound parameter: date ipaddress"
+ }
+
+ $module = Import-PSSession $s foo -AllowClobber
+ It "Pipeline binding works even when also binding by name" {
+ $( get-date | foo ) | Should Be "Bound parameter: date"
+ }
+ It "Pipeline binding works even when also binding by name" {
+ $( [ipaddress]::parse("127.0.0.1") | foo ) | Should Be "Bound parameter: ipaddress"
+ }
+ It "Pipeline binding works even when also binding by name" {
+ $( [ipaddress]::parse("127.0.0.1") | foo -date $(get-date) ) | Should Be "Bound parameter: date ipaddress"
+ }
+ It "Pipeline binding works even when also binding by name" {
+ $( get-date | foo -ipaddress ([ipaddress]::parse("127.0.0.1")) ) | Should Be "Bound parameter: date ipaddress"
+ }
+ Remove-Module $module
+
+ ##############################################################################
+ # TEST: value from pipeline by property name - multiple parameters
+
+ icm $s {
+ function foo {
+ param(
+ [System.TimeSpan]
+ [parameter(ValueFromPipelineByPropertyName = $true)]
+ $TotalProcessorTime,
+
+ [System.Diagnostics.ProcessPriorityClass]
+ [parameter(ValueFromPipelineByPropertyName = $true)]
+ $PriorityClass
+ )
+
+ "Bound parameter: $($myInvocation.BoundParameters.Keys | sort)"
+ }
+ }
+
+ It "Sanity check (no remoting)." {
+ $(icm $s { gps -pid $pid | foo }) | Should Be "Bound parameter: PriorityClass TotalProcessorTime"
+ }
+ It "Sanity check (no remoting)." {
+ $(icm $s { gps -pid $pid | foo -Total 5 }) | Should Be "Bound parameter: PriorityClass TotalProcessorTime"
+ }
+ It "Sanity check (no remoting)." {
+ $(icm $s { gps -pid $pid | foo -Priority normal }) | Should Be "Bound parameter: PriorityClass TotalProcessorTime"
+ }
+
+ $module = Import-PSSession $s foo -AllowClobber
+ It "Pipeline binding works by property name" {
+ $( gps -id $pid | foo ) | Should Be "Bound parameter: PriorityClass TotalProcessorTime"
+ }
+ It "Pipeline binding works by property name" {
+ $( gps -id $pid | foo -Total 5 ) | Should Be "Bound parameter: PriorityClass TotalProcessorTime"
+ }
+ It "Pipeline binding works by property name" {
+ $( gps -id $pid | foo -Priority normal ) | Should Be "Bound parameter: PriorityClass TotalProcessorTime"
+ }
+ Remove-Module $module
+
+ ##############################################################################
+ # TEST: 2 parameters on the same position
+ #
+
+ icm $s {
+ function foo {
+ param(
+ [string]
+ [parameter(Position = 0, parametersetname = 'set1', mandatory = $true)]
+ $string,
+
+ [ipaddress]
+ [parameter(Position = 0, parametersetname = 'set2', mandatory = $true)]
+ $ipaddress
+ )
+
+ "Bound parameter: $($myInvocation.BoundParameters.Keys | sort)"
+ }
+ }
+
+ It "Sanity check (no remoting)." {
+ $(icm $s { foo ([ipaddress]::parse("127.0.0.1")) }) | Should Be "Bound parameter: ipaddress"
+ }
+ It "Sanity check (no remoting)." {
+ $(icm $s { foo "blah" }) | Should Be "Bound parameter: string"
+ }
+
+ $module = Import-PSSession $s foo -AllowClobber
+ It "Positional binding works" {
+ $( foo "blah" ) | Should Be "Bound parameter: string"
+ }
+ It "Positional binding works" {
+ $( foo ([ipaddress]::parse("127.0.0.1")) ) | Should Be "Bound parameter: ipaddress"
+ }
+ Remove-Module $module
+
+ ##############################################################################
+ # TEST: positional binding and array argument value
+ #
+
+ icm $s {
+ function foo {
+ param(
+ [object]
+ [parameter(Position = 0, mandatory = $true)]
+ $p1,
+
+ [object]
+ [parameter(Position = 1)]
+ $p2
+ )
+
+ "$p1 : $p2"
+ }
+ }
+
+ It "Sanity check (no remoting)." {
+ $(icm $s { foo 1,2,3 }) | Should Be "1 2 3 : "
+ }
+ It "Sanity check (no remoting)." {
+ $(icm $s { foo 1,2,3 4 }) | Should Be "1 2 3 : 4"
+ }
+ It "Sanity check (no remoting)." {
+ $(icm $s { foo -p2 4 1,2,3 }) | Should Be "1 2 3 : 4"
+ }
+ It "Sanity check (no remoting)." {
+ $(icm $s { foo 1 4 }) | Should Be "1 : 4"
+ }
+ It "Sanity check (no remoting)." {
+ $(icm $s { foo -p2 4 1 }) | Should Be "1 : 4"
+ }
+
+ $module = Import-PSSession $s foo -AllowClobber
+ It "Positional binding works when binding an array value" {
+ $( foo 1,2,3 ) | Should Be "1 2 3 : "
+ }
+ It "Positional binding works when binding an array value" {
+ $( foo 1,2,3 4 ) | Should Be "1 2 3 : 4"
+ }
+ It "Positional binding works when binding an array value" {
+ $( foo -p2 4 1,2,3 ) | Should Be "1 2 3 : 4"
+ }
+ It "Positional binding works when binding an array value" {
+ $( foo 1 4 ) | Should Be "1 : 4"
+ }
+ It "Positional binding works when binding an array value" {
+ $( foo -p2 4 1 ) | Should Be "1 : 4"
+ }
+ Remove-Module $module
+
+
+ ##############################################################################
+ # TEST: value from remaining arguments
+ #
+
+ icm $s {
+ function foo {
+ param(
+ [string]
+ [parameter(Position = 0)]
+ $firstArg,
+
+ [string[]]
+ [parameter(ValueFromRemainingArguments = $true)]
+ $remainingArgs
+ )
+
+ "$firstArg : $remainingArgs"
+ }
+ }
+
+ It "Sanity check (no remoting)." {
+ $(icm $s { foo }) | Should Be " : "
+ }
+ It "Sanity check (no remoting)." {
+ $(icm $s { foo 1 }) | Should Be "1 : "
+ }
+ It "Sanity check (no remoting)." {
+ $(icm $s { foo -first 1 }) | Should Be "1 : "
+ }
+ It "Sanity check (no remoting)." {
+ $(icm $s { foo 1 2 3 }) | Should Be "1 : 2 3"
+ }
+ It "Sanity check (no remoting)." {
+ $(icm $s { foo -first 1 2 3 }) | Should Be "1 : 2 3"
+ }
+ It "Sanity check (no remoting)." {
+ $(icm $s { foo 2 3 -first 1 4 5 }) | Should Be "1 : 2 3 4 5"
+ }
+ It "Sanity check (no remoting)." {
+ $(icm $s { foo -remainingArgs 2,3 1 }) | Should Be "1 : 2 3"
+ }
+
+ $module = Import-PSSession $s foo -AllowClobber
+ It "Value from remaining arguments works" {
+ $( foo ) | Should Be " : "
+ }
+ It "Value from remaining arguments works" {
+ $( foo 1 ) | Should Be "1 : "
+ }
+ It "Value from remaining arguments works" {
+ $( foo -first 1 ) | Should Be "1 : "
+ }
+ It "Value from remaining arguments works" {
+ $( foo 1 2 3 ) | Should Be "1 : 2 3"
+ }
+ It "Value from remaining arguments works" {
+ $( foo -first 1 2 3 ) | Should Be "1 : 2 3"
+ }
+ It "Value from remaining arguments works" {
+ $( foo 2 3 -first 1 4 5 ) | Should Be "1 : 2 3 4 5"
+ }
+ It "Value from remaining arguments works" {
+ $( foo -remainingArgs 2,3 1 ) | Should Be "1 : 2 3"
+ }
+
+ Remove-Module $module
+
+ ##############################################################################
+ # TEST: non cmdlet-based binding
+ #
+
+ icm $s {
+ function foo {
+ param(
+ $firstArg,
+ $secondArg
+ )
+
+ "$firstArg : $secondArg : $args"
+ }
+ }
+
+ It "Sanity check (no remoting)." {
+ $(icm $s { foo }) | Should Be " : : "
+ }
+ It "Sanity check (no remoting)." {
+ $(icm $s { foo 1 }) | Should Be "1 : : "
+ }
+ It "Sanity check (no remoting)." {
+ $(icm $s { foo -first 1 }) | Should Be "1 : : "
+ }
+ It "Sanity check (no remoting)." {
+ $(icm $s { foo 1 2 }) | Should Be "1 : 2 : "
+ }
+ It "Sanity check (no remoting)." {
+ $(icm $s { foo 1 -second 2 }) | Should Be "1 : 2 : "
+ }
+ It "Sanity check (no remoting)." {
+ $(icm $s { foo -first 1 -second 2 }) | Should Be "1 : 2 : "
+ }
+ It "Sanity check (no remoting)." {
+ $(icm $s { foo 1 2 3 4 }) | Should Be "1 : 2 : 3 4"
+ }
+ It "Sanity check (no remoting)." {
+ $(icm $s { foo -first 1 2 3 4 }) | Should Be "1 : 2 : 3 4"
+ }
+ It "Sanity check (no remoting)." {
+ $(icm $s { foo 1 -second 2 3 4 }) | Should Be "1 : 2 : 3 4"
+ }
+ It "Sanity check (no remoting)." {
+ $(icm $s { foo 1 3 -second 2 4 }) | Should Be "1 : 2 : 3 4"
+ }
+ It "Sanity check (no remoting)." {
+ $(icm $s { foo -first 1 -second 2 3 4 }) | Should Be "1 : 2 : 3 4"
+ }
+
+ $module = Import-PSSession $s foo -AllowClobber
+ It "Non cmdlet-based binding works." {
+ $( foo ) | Should Be " : : "
+ }
+
+ It "Non cmdlet-based binding works." {
+ $( foo 1 ) | Should Be "1 : : "
+ }
+ It "Non cmdlet-based binding works." {
+ $( foo -first 1 ) | Should Be "1 : : "
+ }
+ It "Non cmdlet-based binding works." {
+ $( foo 1 2 ) | Should Be "1 : 2 : "
+ }
+ It "Non cmdlet-based binding works." {
+ $( foo 1 -second 2 ) | Should Be "1 : 2 : "
+ }
+ It "Non cmdlet-based binding works." {
+ $( foo -first 1 -second 2 ) | Should Be "1 : 2 : "
+ }
+ It "Non cmdlet-based binding works." {
+ $( foo 1 2 3 4 ) | Should Be "1 : 2 : 3 4"
+ }
+ It "Non cmdlet-based binding works." {
+ $( foo -first 1 2 3 4 ) | Should Be "1 : 2 : 3 4"
+ }
+ It "Non cmdlet-based binding works." {
+ $( foo 1 -second 2 3 4 ) | Should Be "1 : 2 : 3 4"
+ }
+ It "Non cmdlet-based binding works." {
+ $( foo 1 3 -second 2 4 ) | Should Be "1 : 2 : 3 4"
+ }
+ It "Non cmdlet-based binding works." {
+ $( foo -first 1 -second 2 3 4 ) | Should Be "1 : 2 : 3 4"
+ }
+ Remove-Module $module
+
+ ##############################################################################
+ # TEST: default parameter initialization should be executed on the server
+
+ Invoke-Command -Session $s { param($x) IEx $x } -Args 'function MyInitializerFunction { param($x = $($pid)) $x }'
+ $module = Import-PSSession -Session:$s -Name:MyInitializerFunction -AllowClobber
+ $localPid = $pid
+ $remotePid = icm $s { $pid }
+ It "Sanity check - remotePid != localPid" {
+ $localPid | Should Not Be $remotePid
+ }
+ It "Initializer run on the remote server" {
+ (MyInitializerFunction) | Should Be $remotePid
+ }
+ It "Initializer not run when value provided" {
+ (MyInitializerFunction 123) | Should Be 123
+ }
+ Remove-Module $module
+
+ ##############################################################################
+ # TEST: client-side parameters - cmdlet case
+
+ $module = Import-PSSession -Session:$s -Name:Get-Variable -Type:cmdlet -AllowClobber
+ It "Importing by name/type should work" {
+ (Get-Variable -Name:pid).Value | Should Not Be $pid
+ }
+ $remotePid = (Get-Variable -Name:pid).Value
+
+ $job = Get-Variable -Name:pid -AsJob
+ It "-AsJob should return something" {
+ $job | Should Not Be NullOrEmpty
+ }
+ It "-AsJob returns the right type of object" {
+ ($job -is [System.Management.Automation.Job]) | Should Be $true
+ }
+ It "Job completes within reasonable time" {
+ ($job.Finished.WaitOne([TimeSpan]::FromSeconds(10), $false)) | Should Be $true
+ }
+ It "AsJob: $job.JobStateInfo.State" {
+ $job.JobStateInfo.State | Should Be 'Completed'
+ }
+ $childJob = $job.ChildJobs[0]
+ It "AsJob: $childJob.Output.Count" {
+ $childJob.Output.Count | Should Be 1
+ }
+ It "AsJob: $childJob.Output[0].Value" {
+ $childJob.Output[0].Value | Should Be $remotePid
+ }
+ Remove-Job $job
+
+ $result1 = Get-Variable -Name:pid -OutVariable global:result2
+ It "OutVariable: $result1.Value" {
+ $result1.Value | Should Be $remotePid
+ }
+ It "OutVariable: $result2[0].Value" {
+ $global:result2[0].Value | Should Be $remotePid
+ }
+
+ Remove-Module $module
+
+ ##############################################################################
+ # TEST: client-side parameters - Windows 7 bug #759434
+
+ $module = Import-PSSession -Session:$s -Name:Write-Warning -Type:cmdlet -Prefix Remote -AllowClobber
+
+ $jobWithWarnings = write-remotewarning foo -warningaction continue -asjob
+ $null = Wait-Job $jobWithWarnings
+ It "Warnings present if -WarningAction Continue" {
+ $jobWithWarnings.ChildJobs[0].Warning.Count | Should Be 1
+ }
+ Remove-Job $jobWithWarnings
+
+ $jobWithoutWarnings = write-remotewarning foo -warningaction silentlycontinue -asjob
+ $null = Wait-Job $jobWithoutWarnings
+ It "No warnings if -WarningAction SilentlyContinue" {
+ 0 | Should Be ($jobWithoutWarnings.ChildJobs[0].Warning.Count)
+ }
+ Remove-Job $jobWithoutWarnings
+
+ Remove-Module $module
+
+ ##############################################################################
+ # TEST: client-side parameters - non-cmdlet case
+
+ icm $s { function foo { param($OutVariable) "OutVariable = $OutVariable" } }
+ It "Sanity check: OutVariable is not intercepted for non-cmdlet-bound functions" {
+ $(icm $s { foo -OutVariable x }) | Should Be "OutVariable = x"
+ }
+ $module = Import-PSSession -Session:$s -Name:foo -Type:function -AllowClobber
+ It "Implicit remoting: OutVariable is not intercepted for non-cmdlet-bound functions" {
+ $( foo -OutVariable x ) | Should Be "OutVariable = x"
+ }
+ Remove-Module $module
+
+ ##############################################################################
+ # TEST: switch and positional parameters
+
+ $module = Import-PSSession -Session $s -Name Get-Variable -Type cmdlet -Prefix Remote -AllowClobber
+ $remotePid = ICm $s { $pid }
+ It "Sanity check: remote pid != local pid" {
+ $remotePid | Should Not Be $pid
+ }
+
+ # switch
+ $proxiedPid = Get-RemoteVariable -Name pid -ValueOnly
+ It "Switch parameters work fine" {
+ $remotePid | Should Be $proxiedPid
+ }
+
+ # positional
+ $proxiedPid = Get-RemoteVariable pid
+ It "Positional parameters work fine" {
+ $remotePid | Should Be ($proxiedPid.Value)
+ }
+ }
+ finally
+ {
+ if ($s -ne $null) { Remove-PSSession $s -ErrorAction SilentlyContinue }
+ if ($module -ne $null) { Remove-Module $module -Force -ErrorAction SilentlyContinue }
+ }
+}
+
+
+Describe "Implicit remoting on restricted ISS" -tags 'Innerloop', 'P1' {
+
+ if (${env:PROCESSOR_ARCHITECTURE} -eq 'ARM')
+ {
+ Write-Warning "Skipping the test on ARM"
+ return
+ }
+
+ ##############################################################################
+ # SETUP: create a remote end-point
+
+ try
+ {
+ $sessionConfigurationDll = [IO.Path]::Combine([IO.Path]::GetTempPath(), "ImplicitRemotingRestrictedConfiguration$(Get-Random).dll")
+
+ Add-Type -OutputAssembly $sessionConfigurationDll -TypeDefinition @"
+
+ using System;
+ using System.Collections.Generic;
+ using System.Management.Automation;
+ using System.Management.Automation.Runspaces;
+ using System.Management.Automation.Remoting;
+
+ namespace MySessionConfiguration
+ {
+ public class MySessionConfiguration : PSSessionConfiguration
+ {
+ public override InitialSessionState GetInitialSessionState(PSSenderInfo senderInfo)
+ {
+ //System.Diagnostics.Debugger.Launch();
+ //System.Diagnostics.Debugger.Break();
+
+ InitialSessionState iss = InitialSessionState.CreateRestricted(System.Management.Automation.SessionCapabilities.RemoteServer);
+
+ // add Out-String for testing stuff
+ iss.Commands["Out-String"][0].Visibility = SessionStateEntryVisibility.Public;
+
+ // remove all commands that are not public
+ List commandsToRemove = new List();
+ foreach (SessionStateCommandEntry entry in iss.Commands)
+ {
+ List sameNameEntries = new List(iss.Commands[entry.Name]);
+ if (!sameNameEntries.Exists(delegate(SessionStateCommandEntry e) { return e.Visibility == SessionStateEntryVisibility.Public; }))
+ {
+ commandsToRemove.Add(entry.Name);
+ }
+ }
+ foreach (string commandToRemove in commandsToRemove)
+ {
+ iss.Commands.Remove(commandToRemove, null /* all types */);
+ }
+
+ return iss;
+ }
+ }
+ }
+
+"@
+
+ Get-PSSessionConfiguration ImplicitRemotingRestrictedConfiguration* | Unregister-PSSessionConfiguration -Force
+
+ $myConfiguration = Register-PSSessionConfiguration `
+ -Name ImplicitRemotingRestrictedConfiguration `
+ -ApplicationBase (Split-Path $sessionConfigurationDll) `
+ -AssemblyName (Split-Path $sessionConfigurationDll -Leaf) `
+ -ConfigurationTypeName "MySessionConfiguration.MySessionConfiguration" `
+ -Force `
+
+ $s = New-PSSession -Cn "localhost" -ConfigurationName $myConfiguration.Name
+ It "Verifies that created PSSession is not null" {
+ $s | Should Not Be $null
+ }
+
+ ##############################################################################
+ # TEST: restrictions work
+ #
+
+ It "Get-Variable is private" {
+ (@(ICm $s { Get-Command -Name Get-Variabl* }).Count) | Should Be 0
+ }
+ It "Only 9 commands are public" {
+ (@(ICm $s { Get-Command }).Count) | Should Be 9
+ }
+
+ ##############################################################################
+ # TEST: basic functionality of Import-PSSession works (against a directly exposed cmdlet and against a proxy function)
+
+ $m = Import-PSSession $s Out-Strin*,Measure-Object -Type Cmdlet,Function -ArgumentList 123 -AllowClobber
+
+ It "Import-PSSession works against the ISS-restricted runspace (Out-String)" {
+ (@(Get-Command Out-String -Type Function).Count) | Should Be 1
+ }
+ It "Import-PSSession works against the ISS-restricted runspace (Measure-Object)" {
+ (@(Get-Command Measure-Object -Type Function).Count) | Should Be 1
+ }
+
+ $remoteResult = Out-String -input ("blah " * 10) -Width 10
+ $localResult = Microsoft.PowerShell.Utility\Out-String -input ("blah " * 10) -Width 10
+ It "Invoking an implicit remoting proxy works against the ISS-restricted runspace (Out-String)" {
+ $localResult | Should Be $remoteResult
+ }
+
+ $remoteResult = 1..10 | Measure-Object
+ $localResult = 1..10 | Microsoft.PowerShell.Utility\Measure-Object
+ It "Invoking an implicit remoting proxy works against the ISS-restricted runspace (Measure-Object)" {
+ ($localResult.Count) | Should Be ($remoteResult.Count)
+ }
+ }
+ finally
+ {
+ if ($m -ne $null) { Remove-Module $m -Force -ErrorAction SilentlyContinue }
+ if ($s -ne $null) { Remove-PSSession $s -ErrorAction SilentlyContinue }
+ if ($myConfiguration -ne $null) { Unregister-PSSessionConfiguration -Name ($myConfiguration.Name) -Force -ErrorAction SilentlyContinue }
+ if ($sessionConfigurationDll -ne $null) { Remove-Item $sessionConfigurationDll -Force -ErrorAction SilentlyContinue }
+ }
+}
+
+
+Describe "Implicit remoting tests" -tags 'Innerloop', 'P1' {
+
+ try
+ {
+ $s = New-PSSession
+
+ ##############################################################################
+ # TEST: Get-Command $m and $m.Name work (Windows 7: #334112)
+
+ $m = Import-PSSession $s Get-Variable -Prefix My -AllowClobber
+
+ It "PSModuleInfo.Name shouldn't contain a psd1 extension" {
+ ($m.Name -notlike '*.psd1') | Should Be $true
+ }
+ It "PSModuleInfo.Name shouldn't contain a psm1 extension" {
+ ($m.Name -notlike '*.psm1') | Should Be $true
+ }
+ It "PSModuleInfo.Name shouldn't contain a path" {
+ ($m.Name -notlike "${env:TMP}*") | Should Be $true
+ }
+
+ $c = @(Get-Command -Module $m)
+ It "Get-Command returns only 1 public command from implicit remoting module (1)" {
+ $c.Count | Should Be 1
+ }
+ It "Get-Command returns the right public command from implicit remoting module (1)" {
+ $c[0].Name | Should Be "Get-MyVariable"
+ }
+
+ $c = @(Get-Command -Module $m.Name)
+ It "Get-Command returns only 1 public command from implicit remoting module (2)" {
+ $c.Count | Should Be 1
+ }
+ It "Get-Command returns the right public command from implicit remoting module (2)" {
+ $c[0].Name | Should Be "Get-MyVariable"
+ }
+
+ Remove-Module $m
+
+ ##############################################################################
+ # TEST: progress bar should be 1) present and 2) completed also
+
+ function Get-TempModuleFile
+ {
+ [IO.Path]::Combine([IO.Path]::GetTempPath(), [Guid]::NewGuid().ToString())
+ }
+
+ $file = Get-TempModuleFile
+
+ $powerShell = [PowerShell]::Create().AddCommand("Export-PSSession").AddParameter("Session", $s).AddParameter("ModuleName", $file).AddParameter("CommandName", "Get-Process").AddParameter("AllowClobber")
+ $powerShell.Invoke() | Out-Null
+ It "'Completed' progress record should be present" {
+ ($powerShell.Streams.Progress | select -last 1).RecordType.ToString() | Should Be "Completed"
+ }
+ $powerShell.Dispose()
+
+ ##############################################################################
+ # TEST: display of property-less objects (not sure if this test belongs here) (Windows 7: #248499)
+
+ $x = new-object random
+ $expected = $x.ToString()
+ # Since New-PSSession now only loads Microsoft.PowerShell.Core and for the session in the test, Autoloading is disabled, engine cannot find New-Object as it is part of Microsoft.PowerShell.Utility module.
+ # The fix is to import this module before running the command.
+ It "Display of local property-less objects" {
+ $expected | Should Be ($($x | out-string).Trim())
+ }
+ It "Display of remote property-less objects" {
+ $expected | Should Be ($(ICm $s { Import-Module Microsoft.PowerShell.Utility; new-object random } | out-string).Trim())
+ }
+
+ ##############################################################################
+ # TEST: piping between remoting proxies should work
+
+ $module = Import-PSSession -Session:$s -Name:Write-Output -AllowClobber
+ $result = Write-Output 123 | Write-Output
+ It "piping between remoting proxies should work" {
+ $result | Should Be 123
+ }
+ Remove-Module $module
+
+ ##############################################################################
+ # TEST: BUG: Windows 7: #269467: Security: Server can inject code that will be executed on a client during implicit remoting call.
+
+ icm $s { function attack(${foo="$(calc)"}){echo "It is done."}}
+ $m = Import-PSSession -Session $s -CommandName attack -EA SilentlyContinue -ErrorVariable expectedError -AllowClobber
+ It "Strange parameter names should trigger an error" {
+ $expectedError | Should Not Be NullOrEmpty
+ }
+ Remove-Module $m
+
+ ##############################################################################
+ # TEST: Non-terminating error from a remote command
+
+ icm $s { $oldGetCommand = ${function:Get-Command} }
+ icm $s { function get-command { write-error blah } }
+
+ $module = Import-PSSession -Session:$s -EA SilentlyContinue -ErrorVariable expectedError -AllowClobber
+ It "Non-terminating error from remote end got duplicated locally" {
+ $expectedError | Should Not Be NullOrEmpty
+ }
+ $msg = [string]($expectedError[0])
+ It "Error message got duplicated correctly" {
+ ($msg.Contains("blah")) | Should Be $true
+ }
+
+ Remove-Module $module
+ icm $s { ${function:Get-Command} = $oldGetCommand }
+
+ ##############################################################################
+ # TEST: Get-Command returns something that wasn't asked for
+
+ icm $s { $oldGetCommand = ${function:Get-Command} }
+ icm $s { function notRequested { "notRequested" }; function get-command { Microsoft.PowerShell.Core\Get-Command Get-Variable,notRequested } }
+
+ $module = Import-PSSession -Session:$s Get-Variable -AllowClobber -EA SilentlyContinue -ErrorVariable expectedError
+ It "We get an error if remote server returns something that wasn't asked for" {
+ $expectedError | Should Not Be NullOrEmpty
+ }
+ $msg = [string]($expectedError[0])
+ It "Error message contains reference to the command that wasn't asked for" {
+ ($msg.Contains("notRequested")) | Should Be $true
+ }
+
+ Remove-Module $module
+ icm $s { ${function:Get-Command} = $oldGetCommand }
+
+ ##############################################################################
+ # TEST: Get-Command returns something that is not CommandInfo
+
+ icm $s { $oldGetCommand = ${function:Get-Command} }
+ icm $s { function get-command { Microsoft.PowerShell.Utility\Get-Variable } }
+
+ $expectedError = $null
+ try
+ {
+ $module = Import-PSSession -Session:$s -AllowClobber
+ }
+ catch
+ {
+ $expectedError = $_
+ }
+ It "Got terminating error for malformed data" {
+ ($expectedError) | Should Not Be $null
+ }
+ $msg = [string]($expectedError)
+ It "Error message contains reference to Get-Command" {
+ ($msg.Contains("Get-Command")) | Should Be $true
+ }
+
+ Remove-Module $module
+ icm $s { ${function:Get-Command} = $oldGetCommand }
+
+ ##############################################################################
+ # TEST: order of remote commands (alias > function > cmdlet > external script)
+
+ $tempdir = join-path $env:TEMP ([IO.Path]::GetRandomFileName())
+ $null = mkdir $tempdir
+ $oldpath = $env:PATH
+ try
+ {
+ 'param([Parameter(Mandatory=$true)]$scriptParam) "external script / $scriptParam"' >$tempdir\myOrder.ps1
+ icm $s { param($x) $env:PATH = $env:PATH + ";" + $x } -Args $tempDir
+ icm $s { function myOrder { param([Parameter(Mandatory=$true)]$functionParam) "function / $functionParam" } }
+ icm $s { function helper { param([Parameter(Mandatory=$true)]$aliasParam) "alias / $aliasParam" }; set-alias myOrder helper }
+
+ $expectedResult = icm $s { myOrder -aliasParam 123 }
+
+ $m = Import-PSSession $s myOrder -CommandType All -AllowClobber
+ $actualResult = myOrder -aliasParam 123
+ It "Command resolution myOrder should be respected by implicit remoting" {
+ $expectedResult | Should Be $actualResult
+ }
+ Remove-Module $m
+ }
+ finally
+ {
+ $env:PATH = $oldpath
+ del $tempDir -Force -Recurse -EA SilentlyContinue
+ }
+
+
+ ##############################################################################
+ # TEST: -Prefix parameter
+
+ $module = Import-PSSession -Session:$s -Name:Get-Variable -Type:cmdlet -Prefix My -AllowClobber
+ It "proxy should return remote pid" {
+ (Get-MyVariable -Name:pid).Value | Should Not Be $pid
+ }
+ Remove-Module $module
+ It "Prefixed commands are removed correctly" {
+ ((Get-Item function:Get-MyVariable -ErrorAction SilentlyContinue) -eq $null) | Should Be $true
+ }
+
+ ##############################################################################
+ # TEST: BadVerbs of functions should trigger a warning
+
+ icm $s { function BadVerb-Variable { param($name) Get-Variable $name } }
+
+ $ps = [powershell]::Create().AddCommand("Import-PSSession", $true).AddParameter("Session", $s).AddParameter("CommandName", "BadVerb-Variable")
+ $ps.Invoke() | out-null
+
+ It "No errors from importing a function with a bad verb" {
+ $ps.Streams.Error.Count | Should Be 0
+ }
+ It "Warnings should be emitted when importing a function with a bad verb" {
+ $ps.Streams.Warning.Count | Should Not Be 0
+ }
+
+ $m = Import-PSSession $s BadVerb-Variable -WarningAction SilentlyContinue -AllowClobber
+
+ $remotePid = icm $s { $pid }
+ $badVerbVariablePid = (BadVerb-Variable -Name:pid).Value
+ $getVariablePid = icm $s { (Get-Variable -Name:pid).Value }
+ It "Importing function with bad verb should work" {
+ $badVerbVariablePid | Should Be $remotePid
+ }
+ It "Importing function with bad verb should work" {
+ $getVariablePid | Should Be $remotePid
+ }
+
+ It "Get-Variable function should be not exported when importing a BadVerb-Variable function" {
+ ((Get-Item function:Get-Variable -ErrorAction SilentlyContinue) -eq $null) | Should Be $true
+ }
+ It "BadVerb-Variable should be a function, not an alias (1)" {
+ ((Get-Item function:BadVerb-Variable -ErrorAction SilentlyContinue) -eq $null) | Should Not Be $true
+ }
+ It "BadVerb-Variable should be a function, not an alias (2)" {
+ ((Get-Item alias:BadVerb-Variable -ErrorAction SilentlyContinue) -eq $null) | Should Be $true
+ }
+
+ Remove-Module $m
+ icm $s { del function:BadVerb-Variable }
+
+ ##############################################################################
+ # TEST: BadVerbs of functions shouldn't trigger a warning when -DisableNameChecking is used
+
+ icm $s { function BadVerb-Variable { param($name) Get-Variable $name } }
+
+ $ps = [powershell]::Create().AddCommand("Import-PSSession", $true).AddParameter("Session", $s).AddParameter("CommandName", "BadVerb-Variable").AddParameter("DisableNameChecking", $true)
+ $ps.Invoke() | out-null
+
+ It "No errors from importing a function with a bad verb + -DisableNameChecking" {
+ $ps.Streams.Error.Count | Should Be 0
+ }
+ It "No warnings from importing a function with a bad verb + -DisableNameChecking" {
+ $ps.Streams.Warning.Count | Should Be 0
+ }
+
+ $m = Import-PSSession $s BadVerb-Variable -DisableNameChecking -AllowClobber
+
+ $remotePid = icm $s { $pid }
+ $badVerbVariablePid = (BadVerb-Variable -Name:pid).Value
+ $getVariablePid = icm $s { (Get-Variable -Name:pid).Value }
+ It "Importing function with bad verb should work" {
+ $badVerbVariablePid | Should Be $remotePid
+ }
+ It "Importing function with bad verb should work" {
+ $getVariablePid | Should Be $remotePid
+ }
+
+ It "Get-Variable function should be not exported when importing a BadVerb-Variable function" {
+ ((Get-Item function:Get-Variable -ErrorAction SilentlyContinue) -eq $Null ) | Should Be $true
+ }
+ It "BadVerb-Variable should be a function, not an alias (1)" {
+ ((Get-Item function:BadVerb-Variable -ErrorAction SilentlyContinue) -ne $null) | Should Be $true
+ }
+ It "BadVerb-Variable should be a function, not an alias (2)" {
+ ((Get-Item alias:BadVerb-Variable -ErrorAction SilentlyContinue) -eq $null) | Should Be $true
+ }
+
+ Remove-Module $m
+ icm $s { del function:BadVerb-Variable }
+
+ ##############################################################################
+ # TEST: BadVerbs of aliases shouldn't trigger a warning
+ # (+ can import an alias without saying -CommandType Alias)
+
+ icm $s { set-alias BadVerb-Variable Get-Variable }
+
+ $ps = [powershell]::Create().AddCommand("Import-PSSession", $true).AddParameter("Session", $s).AddParameter("CommandName", "BadVerb-Variable")
+ $ps.Invoke() | out-null
+
+ It "No errors from importing an alias with a bad verb" {
+ $ps.Streams.Error.Count | Should Be 0
+ }
+ It "No warnings from importing an alias with a bad verb" {
+ $ps.Streams.Warning.Count | Should Be 0
+ }
+
+ $m = Import-PSSession $s BadVerb-Variable -AllowClobber
+
+ $remotePid = icm $s { $pid }
+ $badVerbVariablePid = (BadVerb-Variable -Name:pid).Value
+ $getVariablePid = icm $s { (Get-Variable -Name:pid).Value }
+ It "Importing alias with bad verb should work" {
+ $badVerbVariablePid | Should Be $remotePid
+ }
+ It "Importing alias with bad verb should work" {
+ $getVariablePid | Should Be $remotePid
+ }
+
+ Remove-Module $m
+ icm $s { del alias:BadVerb-Variable }
+
+ ##############################################################################
+ # TEST: removing a module should clean-up event handlers (Windows 7: #268819)
+
+ $oldNumberOfHandlers = $executionContext.GetType().GetProperty("Events").GetValue($executionContext, $null).Subscribers.Count
+ $module = Import-PSSession -Session:$s -Name:Get-Random -AllowClobber
+ Remove-Module $module
+ $newNumberOfHandlers = $executionContext.GetType().GetProperty("Events").GetValue($executionContext, $null).Subscribers.Count
+ It "Event should be unregistered when the module is removed" {
+ $oldNumberOfHandlers | Should Be $newNumberOfHandlers
+ }
+
+ It "Private functions from the implicit remoting module shouldn't get imported into global scope" {
+ 0 | Should Be @(dir function:*Implicit* -ErrorAction SilentlyContinue).Count
+ }
+ }
+ finally
+ {
+ if ($s -ne $null) { Remove-PSSession $s -ErrorAction SilentlyContinue }
+ if ($m -ne $null) { Remove-Module $m -Force -ErrorAction SilentlyContinue }
+ if ($module -ne $null) { Remove-Module $module -Force -ErrorAction SilentlyContinue }
+ if ($file -ne $null) { Remove-item $file -Force -Recurse -ErrorAction SilentlyContinue }
+ }
+}
+
+
+Describe "Export-PSSession function" -tags 'Innerloop', 'P1' {
+
+ try
+ {
+ ##############################################################################
+ # TEST SETUP - CREATE TEMP DIRECTORY
+ #
+
+ $modulesDir = $env:TEMP -split ';' | select -first 1
+
+ $tempdir = join-path $modulesDir ([IO.Path]::GetRandomFileName())
+ mkdir $tempdir | Out-Null
+
+ $fileName = [io.path]::GetFileName($tempdir)
+
+ ##############################################################################
+ # TEST: basic functionality of Export-PSSession
+
+ $s = New-PSSession
+ Invoke-Command -Session $s {Import-Module PSDiagnostics}
+ Export-PSSession -Session $s -OutputModule $tempdir\Diag -CommandName Start-Trace -AllowClobber | Out-Null
+
+ @"
+ Import-Module `"$tempdir\Diag`"
+ `$mod = Get-Module Diag
+ Return `$mod
+"@ > $tempdir\TestBug450687.ps1
+
+ # Only the snapin Microsoft.PowerShell.Core is loaded
+ $iss = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault2()
+ $ps = [PowerShell]::Create($iss)
+ $result = $ps.AddScript(" & $tempdir\TestBug450687.ps1").Invoke()
+
+ It "The module created by Export-PSSession is imported successfully" {
+ ($result -ne $null -and $result.Count -eq 1 -and $result[0].Name -eq "Diag") | Should Be $true
+ }
+ $c = $result[0].ExportedCommands["Start-Trace"]
+ It "The command Add-BitsFile is imported successfully" {
+ ($c -ne $null -and $c.CommandType -eq "Function") | Should Be $true
+ }
+ }
+ finally
+ {
+ if ($s -ne $null) { Remove-PSSession $s -ErrorAction SilentlyContinue }
+ if ($tempdir -ne $null) { Remove-Item $tempDir -Force -Recurse -ErrorAction SilentlyContinue }
+ }
+}
+
+
+Describe "Implicit remoting with disconnected session" -tags 'Innerloop', 'P1' {
+
+ try
+ {
+ ## Create session for import.
+ $s = nsn -Name Session102
+ $m = Import-PSSession $s Get-Variable -prefix remote -AllowClobber
+
+ ## Check local and remote versions of process Id variable.
+ $thisPid = Get-Variable pid
+ $sessionPid = Get-RemoteVariable pid
+ It "This and remote session process ids should be different." {
+ $thisPid.Value | Should Not Be $sessionPid.Value
+ }
+
+ ## Disconnect session and use imported command.
+ Disconnect-PSSession $s
+ $dSessionPid = Get-RemoteVariable pid
+ It "Session process id should be same as before, with connected session." {
+ $dSessionPid.Value | Should Be $sessionPid.Value
+ }
+ It "Session should be reconnected." {
+ $s.State | Should Be 'Opened'
+ }
+
+ ## Disconnect session and make it un-connectable.
+ Disconnect-PSSession $s
+ start powershell -arg 'Get-PSSession -cn localhost -name Session102 | Connect-PSSession' -Wait
+
+ sleep 3
+
+ ## This time a new session is created because the old one is unavailable.
+ $dSessionPid = Get-RemoteVariable pid
+ It "Should have a new session process id because old session is unavailable." {
+ $dSessionPid.Value | Should Not Be $sessionPid.Value
+ }
+ }
+ finally
+ {
+ if ($s -ne $null) { Remove-PSSession $s -ErrorAction SilentlyContinue }
+ if ($m -ne $null) { Remove-Module $m -Force -ErrorAction SilentlyContinue }
+ }
+}
+
+
+Describe "Select-Object with implicit remoting" -tags 'Innerloop', 'P1' {
+
+ try
+ {
+
+ $session = New-PSSession localhost
+ icm $session { function foo { "a","b","c" } }
+ $module = Import-PSSession $session foo -AllowClobber
+ $bar = foo | select -First 2; "here"
+
+ It "Select -First failed with implicit remoting" {
+ $bar | Should Not Be NullOrEmpty
+ }
+ It "Select -First failed with implicit remoting" {
+ $bar.Count | Should Be 2
+ }
+ It "Select -First failed with implicit remoting" {
+ $bar[0] | Should Be "a"
+ }
+ It "Select -First failed with implicit remoting" {
+ $bar[1] | Should Be "b"
+ }
+ }
+ finally
+ {
+ if ($module -ne $null) { Remove-Module $module -Force -ErrorAction SilentlyContinue }
+ if ($session -ne $null) { Remove-PSSession $session -ErrorAction SilentlyContinue }
+ }
+}
+
+Describe "Get-FormatData used in Export-PSSession should work on DL targets" -Tags 'Innerloop','P1' {
+
+ # Only run these tests if .NET 2.0 and PS 2.0 is installed on the machine
+ if (! (test-path 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v2.0.50727') -or
+ ! (test-path 'HKLM:\SOFTWARE\Microsoft\PowerShell\1\PowerShellEngine')
+ )
+ {
+ It -SKip "PS 2.0 not installed. Skipping test."
+ return
+ }
+
+ $configName = "DLConfigTest"
+
+ try
+ {
+ $null = Register-PSSessionConfiguration -Name $configName -PSVersion 2.0 -Force
+ $s = New-PSSession -ComputerName . -ConfigurationName $configName
+
+ $results = Export-PSSession -Session $s -OutputModule tempTest -CommandName Get-Process `
+ -AllowClobber -FormatTypeName * -Force -ErrorAction Stop
+
+ It "Verifies that Export-PSSession with PS 2.0 session and format type names succeeds" {
+ $results.Count | Should Not Be 0
+ }
+ }
+ finally
+ {
+ Unregister-PSSessionConfiguration -Name $configName -Force -ErrorAction SilentlyContinue
+ if ($s -ne $null) { Remove-PSSession $s -ErrorAction SilentlyContinue }
+ if ($results.Count -gt 0)
+ {
+ Remove-Item -Path $results[0].DirectoryName -Recurse -Force -ErrorAction SilentlyContinue
+ }
+ }
+}
+
+Describe "GetCommand locally and remotely" -Tags 'Innerloop','P1' {
+
+ BeforeAll {
+ $s = New-PSSession -cn localhost
+ }
+
+ AfterAll {
+ if ($s -ne $null) { Remove-PSSession $s -ErrorAction SilentlyContinue }
+ }
+
+ $localCommandCount = (Get-Command -Type Cmdlet).Count
+ $remoteCommandCount = Invoke-Command { (Get-Command -Type Cmdlet).Count }
+
+ It "Verifies that the number of local cmdlet command count is the same as remote cmdlet command count." {
+ $localCommandCount | Should Be $remoteCommandCount
+ }
+}