diff --git a/test/powershell/Common/Test.Helpers.psm1 b/test/powershell/Common/Test.Helpers.psm1 new file mode 100644 index 0000000000..a83bae4ef1 --- /dev/null +++ b/test/powershell/Common/Test.Helpers.psm1 @@ -0,0 +1,49 @@ +function Wait-CompleteExecution +{ + [CmdletBinding()] + param ( + [ScriptBlock]$sb, + [int]$TimeoutInMilliseconds = 10000, + [int]$IntervalInMilliseconds = 1000 + ) + # Get the current time + $startTime = [DateTime]::Now + + # Loop until the script block evaluates to true + while (-not ($sb.Invoke())) { + # Sleep for the specified interval + start-sleep -mil $intervalInMilliseconds + + # If the timeout period has passed, throw an exception + if (([DateTime]::Now - $startTime).TotalMilliseconds -gt $timeoutInMilliseconds) + { + return $false + } + } + return $true +} + +function Test-IsElevated +{ + $IsElevated = $False + if ( $IsWindows ) { + # on Windows we can determine whether we're executing in an + # elevated context + $identity = [System.Security.Principal.WindowsIdentity]::GetCurrent() + $windowsPrincipal = new-object 'Security.Principal.WindowsPrincipal' $identity + if ($windowsPrincipal.IsInRole("Administrators") -eq 1) + { + $IsElevated = $true + } + } + else { + # on Linux, tests run via sudo will generally report "root" for whoami + if ( (whoami) -match "root" ) { + $IsElevated = $true + } + } + return $IsElevated +} + +export-modulemember -function Wait-CompleteExecution,Test-IsElevated + diff --git a/test/powershell/Common/TestHostCS.psm1 b/test/powershell/Common/TestHostCS.psm1 index 081c48254c..1496999c1d 100755 --- a/test/powershell/Common/TestHostCS.psm1 +++ b/test/powershell/Common/TestHostCS.psm1 @@ -86,7 +86,6 @@ namespace TestHost public class Streams { public ArrayList ConsoleOutput = new ArrayList(); - public ArrayList Output = new ArrayList(); public ArrayList Input = new ArrayList(); public ArrayList Error = new ArrayList(); public ArrayList Verbose = new ArrayList(); @@ -95,6 +94,17 @@ namespace TestHost public ArrayList Information = new ArrayList(); public ArrayList Progress = new ArrayList(); public ArrayList Prompt = new ArrayList(); + public void Clear() { + ConsoleOutput.Clear(); + Input.Clear(); + Error.Clear(); + Verbose.Clear(); + Debug.Clear(); + Warning.Clear(); + Information.Clear(); + Progress.Clear(); + Prompt.Clear(); + } } public class TestPSHostUserInterface : PSHostUserInterface { @@ -112,7 +122,9 @@ namespace TestHost public override Dictionary Prompt(string caption, string message, Collection descriptions) { - Streams.Prompt.Add(caption + ":" + message); + string s = String.Empty; + if ( descriptions[0] != null ) { s = descriptions[0].Name; } + Streams.Prompt.Add(caption + ":" + message + ":" + s); Dictionary d = new Dictionary(); d.Add(descriptions[0].ToString(), new PSObject(promptResponse)); return d; diff --git a/test/powershell/Language/Scripting/Debugging/Debugging.Tests.ps1 b/test/powershell/Language/Scripting/Debugging/Debugging.Tests.ps1 new file mode 100644 index 0000000000..90ed527648 --- /dev/null +++ b/test/powershell/Language/Scripting/Debugging/Debugging.Tests.ps1 @@ -0,0 +1,95 @@ +$script1 = @' +'aaa'.ToString() > $null +'aa' > $null +"a" 2> $null | % { $_ } +'bb' > $null +'bb'.ToSTring() > $null +'bbb' +'@ +$script2 = @' +"line 1" +"line 2" +"line 3" +'@ + +$testroot = resolve-path (join-path $psscriptroot ../../..) +$common = join-path $testroot Common +$helperModule = join-path $common Test.Helpers.psm1 + +Describe "Breakpoints when set should be hit" -tag "CI" { + BeforeAll { + $path = setup -pass -f TestScript_1.ps1 -content $script1 + $bps = 1..6 | %{ set-psbreakpoint -script $path -line $_ -Action { continue } } + } + AfterAll { + $bps | Remove-PSBreakPoint + } + It "A redirected breakpoint is hit" { + & $path + foreach ( $bp in $bps ) { + $bp.HitCount | should be 1 + } + } +} + +Describe "It should be possible to reset runspace debugging" -tag "Feature" { + BeforeAll { + import-module $helperModule -force + $path = setup -pass -f TestScript_2.ps1 -content $script2 + $scriptPath = "$testdrive/TestScript_2.ps1" + $iss = [initialsessionstate]::CreateDefault2(); + $rs = [runspacefactory]::CreateRunspace($iss) + $rs.Name = "TestRunspaceDebuggerReset" + $rs.Open() + $rs | Enable-RunspaceDebug + + $debuggerBeforeReset = $rs.Debugger + + # Create PowerShell to run script. + $ps = [powershell]::Create() + $ps.Runspace = $rs + + # Set breakpoints in runspace. + $result = $ps.AddScript("Set-PSBreakpoint -Script '$scriptPath' -line 1").Invoke() + $ps.Commands.Clear() + $result = $ps.AddScript("Set-PSBreakpoint -Script '$scriptPath' -line 3").Invoke() + $ps.Commands.Clear() + $breakpoints = $ps.AddScript("Get-PSBreakpoint").Invoke() + + # Run script file until breakpoint hit. + $ar = $ps.AddScript("$scriptPath").BeginInvoke() + $completed = Wait-CompleteExecution { $rs.Debugger.InBreakPoint -eq $true } -timeout 10000 -interval 200 + $ps.Stop() + $rs.ResetRunspaceState() + } + AfterAll { + if ( $ps -ne $null ) { $ps.Dispose() } + if ( $ss -ne $null ) { $rs.Dispose() } + } + It "2 breakpoints should have been set" { + $breakpoints.Count | Should be 2 + } + It "The breakpoint Should have been hit" { + $completed | should be $true + } + It "The reset debugger should not be in a breakpoint" { + $rs.Debugger.InBreakPoint | should be $false + } + It "The reset debugger should not be active" { + $rs.Debugger.IsActive | should be $false + } + It "The reset debugger mode should be set to 'Default'" { + $rs.Debugger.DebugMode | should be "Default" + } + It "The debugger should be the same before and after the reset" { + $rs.Debugger | Should be $debuggerBeforeReset + } + It "The breakpoints should be gone after reset" { + $ps.Commands.clear() + $ps.AddCommand("Get-PSBreakpoint").Invoke() | Should BeNullOrEmpty + } + It "The script should run without a break" { + $ps.Commands.Clear() + $ps.addscript("$testdrive/TestScript_2.ps1").Invoke().Count | should be 3 + } +} diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/Start-Process.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/Start-Process.Tests.ps1 index e00c5b6015..65d03c8308 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Start-Process.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Start-Process.Tests.ps1 @@ -14,7 +14,7 @@ Describe "Start-Process" -Tags @("CI","SLOW") { # This has been fixed on Linux, but not on OS X It "Should process arguments without error" { - $process = Start-Process ping -ArgumentList $pingParam -PassThru + $process = Start-Process ping -ArgumentList $pingParam -PassThru -RedirectStandardOutput "$TESTDRIVE/output" $process.Length | Should Be 1 $process.Id | Should BeGreaterThan 1 @@ -22,7 +22,7 @@ Describe "Start-Process" -Tags @("CI","SLOW") { } It "Should work correctly when used with full path name" { - $process = Start-Process $pingCommand -ArgumentList $pingParam -PassThru + $process = Start-Process $pingCommand -ArgumentList $pingParam -PassThru -RedirectStandardOutput "$TESTDRIVE/output" $process.Length | Should Be 1 $process.Id | Should BeGreaterThan 1 @@ -30,7 +30,7 @@ Describe "Start-Process" -Tags @("CI","SLOW") { } It "Should invoke correct path when used with FilePath argument" { - $process = Start-Process -FilePath $pingCommand -ArgumentList $pingParam -PassThru + $process = Start-Process -FilePath $pingCommand -ArgumentList $pingParam -PassThru -RedirectStandardOutput "$TESTDRIVE/output" $process.Length | Should Be 1 $process.Id | Should BeGreaterThan 1 @@ -38,11 +38,11 @@ Describe "Start-Process" -Tags @("CI","SLOW") { } It "Should wait for command completion if used with Wait argument" { - $process = Start-Process ping -ArgumentList $pingParam -Wait -PassThru + $process = Start-Process ping -ArgumentList $pingParam -Wait -PassThru -RedirectStandardOutput "$TESTDRIVE/output" } It "Should work correctly with WorkingDirectory argument" { - $process = Start-Process ping -WorkingDirectory $pingDirectory -ArgumentList $pingParam -PassThru + $process = Start-Process ping -WorkingDirectory $pingDirectory -ArgumentList $pingParam -PassThru -RedirectStandardOutput "$TESTDRIVE/output" $process.Length | Should Be 1 $process.Id | Should BeGreaterThan 1 @@ -50,7 +50,7 @@ Describe "Start-Process" -Tags @("CI","SLOW") { } It "Should should handle stderr redirection without error" { - $process = Start-Process ping -ArgumentList $pingParam -PassThru -RedirectStandardError $tempFile + $process = Start-Process ping -ArgumentList $pingParam -PassThru -RedirectStandardError $tempFile -RedirectStandardOutput "$TESTDRIVE/output" $process.Length | Should Be 1 $process.Id | Should BeGreaterThan 1 @@ -59,13 +59,13 @@ Describe "Start-Process" -Tags @("CI","SLOW") { It "Should should handle stdout redirection without error" { $process = Start-Process ping -ArgumentList $pingParam -Wait -RedirectStandardOutput $tempFile - $dirEntry = dir $tempFile + $dirEntry = get-childitem $tempFile $dirEntry.Length | Should BeGreaterThan 0 } It "Should should handle stdin redirection without error" { $process = Start-Process sort -Wait -RedirectStandardOutput $tempFile -RedirectStandardInput $assetsFile - $dirEntry = dir $tempFile + $dirEntry = get-childitem $tempFile $dirEntry.Length | Should BeGreaterThan 0 } diff --git a/test/powershell/Modules/Microsoft.PowerShell.Security/SecureString.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Security/SecureString.Tests.ps1 new file mode 100644 index 0000000000..9fc6cfe9f8 --- /dev/null +++ b/test/powershell/Modules/Microsoft.PowerShell.Security/SecureString.Tests.ps1 @@ -0,0 +1,45 @@ +Describe "SecureString conversion tests" -Tags "CI" { + BeforeAll { + $string = "ABCD" + $secureString = [System.Security.SecureString]::New() + $string.ToCharArray() | foreach-object { $securestring.AppendChar($_) } + $defaultParamValues = $PSdefaultParameterValues.Clone() + $PSdefaultParameterValues = @{} + if ( ! $IsWindows ) { $PSdefaultParameterValues["it:pending"] = $true } + } + AfterAll { + $PSdefaultParameterValues = $defaultParamValues + } + + It "using null arguments to ConvertFrom-SecureString produces an exception" { + try { + ConvertFrom-SecureString -secureString $null -key $null + } + catch { + $_.FullyQualifiedErrorId | should be "ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.ConvertFromSecureStringCommand" + } + + } + + It "using a bad key produces an exception" { + try { + $badkey = [byte[]]@(1,2) + ConvertFrom-SecureString -securestring $secureString -key $badkey + throw "Command did not throw exception" + } + catch { + $_.FullyQualifiedErrorId | should be "Argument,Microsoft.PowerShell.Commands.ConvertFromSecureStringCommand" + } + } + + It "Can convert to a secure string" { + $ss = ConvertTo-SecureString -AsPlainText -Force abcd + $ss.GetType().Name | should be "SecureString" + } + It "can convert back from a secure string" { + $secret = "abcd" + $ss1 = ConvertTo-SecureString -AsPlainText -Force $secret + $ss2 = convertfrom-securestring $ss1 | convertto-securestring + [pscredential]::New("user",$ss2).GetNetworkCredential().Password | should be $secret + } +} diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Debug-Runspace.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Debug-Runspace.Tests.ps1 new file mode 100644 index 0000000000..4c5b13fccb --- /dev/null +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Debug-Runspace.Tests.ps1 @@ -0,0 +1,59 @@ +Describe "Debug-Runspace" -tag "CI" { + BeforeAll { + $rs1 = [runspacefactory]::CreateRunspace() + $rs1.Open() + $rs1.Name = "MyRunspace1" + $rs2 = [runspacefactory]::CreateRunspace() + $rs2.Open() + $rs2.Name = "MyRunspace2" + } + AfterAll { + if ( $rs1 ) { $rs1.Dispose() } + if ( $rs2 ) { $rs1.Dispose() } + } + + It "Debugging a runspace should fail if the name is ambiguous" { + try { + Debug-Runspace -Name "My*" -ea stop + throw "Command did not throw exception" + } + catch { + $_.FullyQualifiedErrorId | should be "DebugRunspaceTooManyRunspaceFound,Microsoft.PowerShell.Commands.DebugRunspaceCommand" + } + } + + It "Debugging a runspace should fail if the name is not found" { + try { + Debug-Runspace -Name "dflkjsdkfjldkjssldfj" -ea stop + throw "Command did not throw exception" + } + catch { + $_.FullyQualifiedErrorId | should be "DebugRunspaceNoRunspaceFound,Microsoft.PowerShell.Commands.DebugRunspaceCommand" + } + } + + It "Debugging a runspace should fail if the runspace is not open" { + try { + $rs2.Close() + Debug-Runspace -runspace $rs2 -ea stop + throw "Command did not throw exception" + } + catch { + $_.FullyQualifiedErrorId | should be "InvalidOperation,Microsoft.PowerShell.Commands.DebugRunspaceCommand" + } + } + + It "Debugging a runspace should fail if the runspace has no debugger" { + try { + $rs1.Debugger.SetDebugMode("None") + Debug-Runspace -runspace $rs1 -ea stop + throw "Command did not throw exception" + } + catch { + $_.FullyQualifiedErrorId | should be "InvalidOperation,Microsoft.PowerShell.Commands.DebugRunspaceCommand" + } + } + +} + + diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/EnableDisablePSBreakpoint.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/EnableDisablePSBreakpoint.Tests.ps1 new file mode 100644 index 0000000000..cb8e693737 --- /dev/null +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/EnableDisablePSBreakpoint.Tests.ps1 @@ -0,0 +1,21 @@ +Describe "Enable and Disable PSBreakpoints" -Tag "CI" { + BeforeAll { + $path = Setup -F testbp.ps1 -content "get-command`nget-date`nget-location" -pass + $bp = set-psbreakpoint -script $path -line 2 + disable-psbreakpoint $bp + } + AfterAll { + $bp | remove-psbreakpoint + } + It "Enable-PSBreakpoint should enable the breakpoint" { + $bp.Enabled | should be $false + Enable-PSBreakpoint $bp + $bp.Enabled | Should be $true + } + It "Disable-PSBreakpoint should disable the breakpoint" { + Enable-PSBreakpoint $bp + $bp.Enabled | Should be $true + Disable-PSBreakpoint $bp + $bp.Enabled | Should be $false + } +} diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/PowerShellData.tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/PowerShellData.tests.ps1 index 5bdb795377..0b07ad2940 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/PowerShellData.tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/PowerShellData.tests.ps1 @@ -1,83 +1,63 @@ -Describe "Tests for the Import-PowerShellDataFile cmdlet" -Tags "Feature" { +Describe "Tests for the Import-PowerShellDataFile cmdlet" -Tags "CI" { It "Validates error on a missing path" { - $foundError = "" try { Import-PowerShellDataFile -Path /SomeMissingDirectory -ErrorAction Stop + Throw "Command did not throw exception" } catch { - $foundError = $_.FullyQualifiedErrorId + $_.FullyQualifiedErrorId | Should be "PathNotFound,Microsoft.PowerShell.Commands.ResolvePathCommand" } - - $foundError | Should be "PathNotFound,Microsoft.PowerShell.Commands.ResolvePathCommand" } It "Validates error on a directory" { - $foundError = "" try { Import-PowerShellDataFile ${TESTDRIVE} -ErrorAction Stop + Throw "Command did not throw exception" } catch { - $foundError = $_.FullyQualifiedErrorId + $_.FullyQualifiedErrorId | Should be "CouldNotParseAsPowerShellDataFile,Import-PowerShellDataFile" } - $foundError | Should be "CouldNotParseAsPowerShellDataFile,Import-PowerShellDataFile" } It "Generates a good error on an insecure file" { - $path = New-TemporaryFile - Set-Content $path '@{ Foo = Get-Process }' - - $foundError = "" + $path = Setup -f insecure.psd1 -content '@{ Foo = Get-Process }' -pass try { Import-PowerShellDataFile $path -ErrorAction Stop + Throw "Command did not throw exception" } catch { - $foundError = $_.FullyQualifiedErrorId + $_.FullyQualifiedErrorId | Should be "InvalidOperationException,Import-PowerShellDataFile" } - finally - { - Remove-Item $path - } - - $foundError | Should be "InvalidOperationException,Import-PowerShellDataFile" } It "Generates a good error on a file that isn't a PowerShell Data File (missing the hashtable root)" { - $path = New-TemporaryFile - Set-Content $path '"Hello World"' - - $foundError = "" + $path = setup -f NotAPSDataFile -content '"Hello World"' -Pass try { - Import-PowerShellDataFile $path -ErrorAction Stop + Import-PowerShellDataFile $path -ErrorAction Stop + Throw "Command did not throw exception" } catch { - $foundError = $_.FullyQualifiedErrorId + $_.FullyQualifiedErrorId | Should be "CouldNotParseAsPowerShellDataFileNoHashtableRoot,Import-PowerShellDataFile" } - finally - { - Remove-Item $path - } - - $foundError | Should be "CouldNotParseAsPowerShellDataFileNoHashtableRoot,Import-PowerShellDataFile" } It "Can parse a PowerShell Data File (detailed tests are in AST.SafeGetValue tests)" { - $path = New-TemporaryFile - Set-Content $path '@{ "Hello" = "World" }' + $path = Setup -F gooddatafile -content '@{ "Hello" = "World" }' -pass $result = Import-PowerShellDataFile $path -ErrorAction Stop $result.Hello | Should be "World" diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Read-Host.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Read-Host.Tests.ps1 new file mode 100644 index 0000000000..bf9e624efd --- /dev/null +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Read-Host.Tests.ps1 @@ -0,0 +1,42 @@ +if ( ! (get-module -ea silentlycontinue TestHostCS )) +{ + $root = git rev-parse --show-toplevel + $pestertestroot = join-path $root test/powershell + $common = join-path $pestertestroot Common + $hostmodule = join-path $common TestHostCS.psm1 + import-module $hostmodule +} +Describe "Read-Host Test" -tag "CI" { + BeforeAll { + $th = New-TestHost + $rs = [runspacefactory]::Createrunspace($th) + $rs.open() + $ps = [powershell]::Create() + $ps.Runspace = $rs + $ps.Commands.Clear() + } + AfterEach { + $ps.Commands.Clear() + } + AfterAll { + $rs.Close() + $rs.Dispose() + $ps.Dispose() + } + It "Read-Host returns expected string" { + $result = $ps.AddCommand("Read-Host").Invoke() + $result | Should Be $th.UI.ReadLineData + } + It "Read-Host sets the prompt correctly" { + $result = $ps.AddScript("Read-Host -prompt myprompt").Invoke() + $prompt = $th.ui.streams.prompt[0] + $prompt | should Not BeNullOrEmpty + $prompt.split(":")[-1] | should be myprompt + } + It "Read-Host returns a secure string when using -AsSecureString parameter" { + $result = $ps.AddScript("Read-Host -AsSecureString").Invoke() | select-object -first 1 + $result.GetType().Name | should be "SecureString" + [pscredential]::New("foo",$result).GetNEtworkCredential().Password | should BeExactly TEST + + } +} diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Set-Date.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Set-Date.Tests.ps1 new file mode 100644 index 0000000000..d523146dda --- /dev/null +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Set-Date.Tests.ps1 @@ -0,0 +1,15 @@ +# first check to see which platform we're on. If we're on windows we should be able +# to be sure whether we're running elevated. If we're on Linux, we can use whoami to +# determine whether we're elevated +Describe "Set-Date" -Tag "CI" { + BeforeAll { + Import-Module (join-path $psscriptroot "../../Common/Test.Helpers.psm1") + $IsElevated = Test-IsElevated + } + It "Set-Date should be able to set the date in an elevated context" -Skip:(! $IsElevated) { + { get-date | set-date } | Should not throw + } + It "Set-Date should produce an error in a non-elevated context" -Skip:($IsElevated) { + { get-date |set-date} | should throw + } +}