From ea0082ab8d5038ad8f70286463bf69f74e69097c Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Tue, 13 Jun 2017 17:49:56 -0700 Subject: [PATCH] Consolidated the two test exes into one (#3982) Combined the previous echoargs and createchildprocess test exes into a single TestExe that can be extended for other tests. --- build.psm1 | 14 +- .../NativeCommandArguments.Tests.ps1 | 75 +++++----- .../NativeCommandProcessor.Tests.ps1 | 22 +-- .../NativeExecution/NativeStreams.Tests.ps1 | 133 +++++++++--------- .../CreateChildProcess/CreateChildProcess.cs | 25 ---- .../CreateChildProcess.csproj | 11 -- test/tools/EchoArgs/EchoArgs.cs | 25 ---- test/tools/TestExe/TestExe.cs | 64 +++++++++ .../TestExe.csproj} | 22 +-- 9 files changed, 188 insertions(+), 203 deletions(-) delete mode 100644 test/tools/CreateChildProcess/CreateChildProcess.cs delete mode 100644 test/tools/CreateChildProcess/CreateChildProcess.csproj delete mode 100644 test/tools/EchoArgs/EchoArgs.cs create mode 100644 test/tools/TestExe/TestExe.cs rename test/tools/{EchoArgs/EchoArgs.csproj => TestExe/TestExe.csproj} (78%) diff --git a/build.psm1 b/build.psm1 index 10f1f53c50..420bcca352 100644 --- a/build.psm1 +++ b/build.psm1 @@ -696,25 +696,31 @@ function Publish-PSTestTools { Find-Dotnet - $tools = @("$PSScriptRoot/test/tools/EchoArgs", "echoargs"), @("$PSScriptRoot/test/tools/CreateChildProcess", "createchildprocess") + $tools = @( + @{Path="${PSScriptRoot}/test/tools/TestExe";Output="testexe"} + ) if ($Options -eq $null) { $Options = New-PSOptions } - # Publish EchoArgs so it can be run by tests + # Publish tools so it can be run by tests foreach ($tool in $tools) { - Push-Location $tool[0] + Push-Location $tool.Path try { dotnet publish --output bin --configuration $Options.Configuration --framework $Options.Framework --runtime $Options.Runtime + $toolPath = Join-Path -Path $tool.Path -ChildPath "bin" # add 'x' permission when building the standalone application # this is temporary workaround to a bug in dotnet.exe, tracking by dotnet/cli issue #6286 if ($Options.Configuration -eq "Linux") { - $executable = Join-Path -Path $tool[0] -ChildPath "bin/$($tool[1])" + $executable = Join-Path -Path $toolPath -ChildPath "$($tool.Output)" chmod u+x $executable } + if ( $env:PATH -notcontains $toolPath ) { + $env:PATH = $toolPath+$TestModulePathSeparator+$($env:PATH) + } } finally { Pop-Location } diff --git a/test/powershell/Language/Scripting/NativeExecution/NativeCommandArguments.Tests.ps1 b/test/powershell/Language/Scripting/NativeExecution/NativeCommandArguments.Tests.ps1 index a55d56ed44..251e42310f 100644 --- a/test/powershell/Language/Scripting/NativeExecution/NativeCommandArguments.Tests.ps1 +++ b/test/powershell/Language/Scripting/NativeExecution/NativeCommandArguments.Tests.ps1 @@ -1,41 +1,34 @@ -Describe "Native Command Arguments" -tags "CI" { - # Find where test/powershell is so we can find the echoargs command relative to it - $powershellTestDir = $PSScriptRoot - while ($powershellTestDir -notmatch 'test[\\/]powershell$') { - $powershellTestDir = Split-Path $powershellTestDir - } - $echoArgs = Join-Path (Split-Path $powershellTestDir) tools/EchoArgs/bin/echoargs - - # When passing arguments to native commands, quoted segments that contain - # spaces need to be quoted with '"' characters when they are passed to the - # native command (or to bash or sh on Linux). - # - # This test checks that the proper quoting is occuring by passing arguments - # to the echoargs native command and looking at how it got the arguments. - It "Should handle quoted spaces correctly" { - $a = 'a"b c"d' - $lines = & $echoArgs $a 'a"b c"d' a"b c"d - ($lines | measure).Count | Should Be 3 - $lines[0] | Should Be 'Arg 0 is ' - $lines[1] | Should Be 'Arg 1 is ' - $lines[2] | Should Be 'Arg 2 is ' - } - - # In order to pass '"' characters so they are actually part of command line - # arguments for native commands, they need to be escaped with a '\' (this - # is in addition to the '`' escaping needed inside '"' quoted strings in - # PowerShell). - # - # This functionality was broken in PowerShell 5.0 and 5.1, so this test - # will fail on those versions unless the fix is backported to them. - # - # This test checks that the proper quoting and escaping is occurring by - # passing arguments with escaped quotes to the echoargs native command and - # looking at how it got the arguments. - It "Should handle spaces between escaped quotes" { - $lines = & $echoArgs 'a\"b c\"d' "a\`"b c\`"d" - ($lines | measure).Count | Should Be 2 - $lines[0] | Should Be 'Arg 0 is ' - $lines[1] | Should Be 'Arg 1 is ' - } -} +Describe "Native Command Arguments" -tags "CI" { + # When passing arguments to native commands, quoted segments that contain + # spaces need to be quoted with '"' characters when they are passed to the + # native command (or to bash or sh on Linux). + # + # This test checks that the proper quoting is occuring by passing arguments + # to the testexe native command and looking at how it got the arguments. + It "Should handle quoted spaces correctly" { + $a = 'a"b c"d' + $lines = testexe -echoargs $a 'a"b c"d' a"b c"d + ($lines | measure).Count | Should Be 3 + $lines[0] | Should Be 'Arg 0 is ' + $lines[1] | Should Be 'Arg 1 is ' + $lines[2] | Should Be 'Arg 2 is ' + } + + # In order to pass '"' characters so they are actually part of command line + # arguments for native commands, they need to be escaped with a '\' (this + # is in addition to the '`' escaping needed inside '"' quoted strings in + # PowerShell). + # + # This functionality was broken in PowerShell 5.0 and 5.1, so this test + # will fail on those versions unless the fix is backported to them. + # + # This test checks that the proper quoting and escaping is occurring by + # passing arguments with escaped quotes to the testexe native command and + # looking at how it got the arguments. + It "Should handle spaces between escaped quotes" { + $lines = testexe -echoargs 'a\"b c\"d' "a\`"b c\`"d" + ($lines | measure).Count | Should Be 2 + $lines[0] | Should Be 'Arg 0 is ' + $lines[1] | Should Be 'Arg 1 is ' + } +} diff --git a/test/powershell/Language/Scripting/NativeExecution/NativeCommandProcessor.Tests.ps1 b/test/powershell/Language/Scripting/NativeExecution/NativeCommandProcessor.Tests.ps1 index b2a6e1b9a5..8aa219d1fb 100644 --- a/test/powershell/Language/Scripting/NativeExecution/NativeCommandProcessor.Tests.ps1 +++ b/test/powershell/Language/Scripting/NativeExecution/NativeCommandProcessor.Tests.ps1 @@ -27,35 +27,25 @@ Describe 'native commands lifecycle' -tags 'Feature' { Describe "Native Command Processor" -tags "Feature" { - BeforeAll { - # Find where test/powershell is so we can find the createchildprocess command relative to it - $powershellTestDir = $PSScriptRoot - while ($powershellTestDir -notmatch 'test[\\/]powershell$') { - $powershellTestDir = Split-Path $powershellTestDir - } - $createchildprocess = Join-Path (Split-Path $powershellTestDir) tools/CreateChildProcess/bin/createchildprocess - } - # If powershell receives a StopProcessing, it should kill the native process and all child processes - # this test should pass and no longer Pending when #2561 is fixed It "Should kill native process tree" -Pending { # make sure no test processes are running - # on Linux, the Process class truncates the name so filter using Where-Object - Get-Process | Where-Object {$_.Name -like 'createchildproc*'} | Stop-Process + Get-Process testexe -ErrorAction SilentlyContinue | Stop-Process [int] $numToCreate = 2 - $ps = [PowerShell]::Create().AddCommand($createchildprocess) - $ps.AddParameter($numToCreate) + $ps = [PowerShell]::Create().AddCommand("testexe") + $ps.AddArgument("-createchildprocess") + $ps.AddArgument($numToCreate) $async = $ps.BeginInvoke() $ps.InvocationStateInfo.State | Should Be "Running" [bool] $childrenCreated = $false while (-not $childrenCreated) { - $childprocesses = Get-Process | Where-Object {$_.Name -like 'createchildproc*'} + $childprocesses = Get-Process testexe -ErrorAction SilentlyContinue if ($childprocesses.count -eq $numToCreate+1) { $childrenCreated = $true @@ -72,7 +62,7 @@ Describe "Native Command Processor" -tags "Feature" { break } } - $childprocesses = Get-Process | Where-Object {$_.Name -like 'createchildproc*'} + $childprocesses = Get-Process testexe $count = $childprocesses.count $childprocesses | Stop-Process $count | Should Be 0 diff --git a/test/powershell/Language/Scripting/NativeExecution/NativeStreams.Tests.ps1 b/test/powershell/Language/Scripting/NativeExecution/NativeStreams.Tests.ps1 index 523a7439e5..ce4ac23fcd 100644 --- a/test/powershell/Language/Scripting/NativeExecution/NativeStreams.Tests.ps1 +++ b/test/powershell/Language/Scripting/NativeExecution/NativeStreams.Tests.ps1 @@ -1,70 +1,63 @@ -Describe "Native streams behavior with PowerShell" -Tags 'CI' { - $powershell = Join-Path -Path $PsHome -ChildPath "powershell" - - Context "Error stream" { - # we are using powershell itself as an example of a native program. - # we can create a behavior we want on the fly and test complex scenarios. - - $error.Clear() - - $command = [string]::Join('', @( - '[Console]::Error.Write(\"foo`n`nbar`n`nbaz\"); ', - '[Console]::Error.Write(\"middle\"); ', - '[Console]::Error.Write(\"foo`n`nbar`n`nbaz\")' - )) - - $out = & $powershell -noprofile -command $command 2>&1 - - # this check should be the first one, because $error is a global shared variable - It 'should not add records to $error variable' { - # we are keeping existing Windows PS v5.1 behavior for $error variable - $error.Count | Should Be 9 - } - - It 'uses ErrorRecord object to return stderr output' { - ($out | measure).Count | Should BeGreaterThan 1 - - $out[0] | Should BeOfType 'System.Management.Automation.ErrorRecord' - $out[0].FullyQualifiedErrorId | Should Be 'NativeCommandError' - - $out | Select-Object -Skip 1 | % { - $_ | Should BeOfType 'System.Management.Automation.ErrorRecord' - $_.FullyQualifiedErrorId | Should Be 'NativeCommandErrorMessage' - } - } - - It 'uses correct exception messages for error stream' { - ($out | measure).Count | Should Be 9 - $out[0].Exception.Message | Should Be 'foo' - $out[1].Exception.Message | Should Be '' - $out[2].Exception.Message | Should Be 'bar' - $out[3].Exception.Message | Should Be '' - $out[4].Exception.Message | Should Be 'bazmiddlefoo' - $out[5].Exception.Message | Should Be '' - $out[6].Exception.Message | Should Be 'bar' - $out[7].Exception.Message | Should Be '' - $out[8].Exception.Message | Should Be 'baz' - } - - It 'preserves error stream as is with Out-String' { - ($out | Out-String).Replace("`r", '') | Should Be "foo`n`nbar`n`nbazmiddlefoo`n`nbar`n`nbaz`n" - } - } -} - -Describe 'piping powershell objects to finished native executable' -Tags 'CI' { - # Find where test/powershell is so we can find the echoargs command relative to it - $powershellTestDir = $PSScriptRoot - while ($powershellTestDir -notmatch 'test[\\/]powershell$') { - $powershellTestDir = Split-Path $powershellTestDir - } - $echoArgs = Join-Path (Split-Path $powershellTestDir) tools/EchoArgs/bin/echoargs - - It 'doesn''t throw any exceptions, when we are piping to the closed executable' { - 1..3 | % { - Start-Sleep -Milliseconds 100 - # yeild some multi-line formatted object - @{'a' = 'b'} - } | & $echoArgs | Should Be $null - } -} +Describe "Native streams behavior with PowerShell" -Tags 'CI' { + $powershell = Join-Path -Path $PsHome -ChildPath "powershell" + + Context "Error stream" { + # we are using powershell itself as an example of a native program. + # we can create a behavior we want on the fly and test complex scenarios. + + $error.Clear() + + $command = [string]::Join('', @( + '[Console]::Error.Write(\"foo`n`nbar`n`nbaz\"); ', + '[Console]::Error.Write(\"middle\"); ', + '[Console]::Error.Write(\"foo`n`nbar`n`nbaz\")' + )) + + $out = & $powershell -noprofile -command $command 2>&1 + + # this check should be the first one, because $error is a global shared variable + It 'should not add records to $error variable' { + # we are keeping existing Windows PS v5.1 behavior for $error variable + $error.Count | Should Be 9 + } + + It 'uses ErrorRecord object to return stderr output' { + ($out | measure).Count | Should BeGreaterThan 1 + + $out[0] | Should BeOfType 'System.Management.Automation.ErrorRecord' + $out[0].FullyQualifiedErrorId | Should Be 'NativeCommandError' + + $out | Select-Object -Skip 1 | % { + $_ | Should BeOfType 'System.Management.Automation.ErrorRecord' + $_.FullyQualifiedErrorId | Should Be 'NativeCommandErrorMessage' + } + } + + It 'uses correct exception messages for error stream' { + ($out | measure).Count | Should Be 9 + $out[0].Exception.Message | Should Be 'foo' + $out[1].Exception.Message | Should Be '' + $out[2].Exception.Message | Should Be 'bar' + $out[3].Exception.Message | Should Be '' + $out[4].Exception.Message | Should Be 'bazmiddlefoo' + $out[5].Exception.Message | Should Be '' + $out[6].Exception.Message | Should Be 'bar' + $out[7].Exception.Message | Should Be '' + $out[8].Exception.Message | Should Be 'baz' + } + + It 'preserves error stream as is with Out-String' { + ($out | Out-String).Replace("`r", '') | Should Be "foo`n`nbar`n`nbazmiddlefoo`n`nbar`n`nbaz`n" + } + } +} + +Describe 'piping powershell objects to finished native executable' -Tags 'CI' { + It 'doesn''t throw any exceptions, when we are piping to the closed executable' { + 1..3 | % { + Start-Sleep -Milliseconds 100 + # yeild some multi-line formatted object + @{'a' = 'b'} + } | testexe -echoargs | Should Be $null + } +} diff --git a/test/tools/CreateChildProcess/CreateChildProcess.cs b/test/tools/CreateChildProcess/CreateChildProcess.cs deleted file mode 100644 index c37272cf63..0000000000 --- a/test/tools/CreateChildProcess/CreateChildProcess.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Diagnostics; -using System.Threading; - -namespace CreateChildProcess -{ - class Program - { - static void Main(string[] args) - { - if (args.Length > 0) - { - uint num = UInt32.Parse(args[0]); - for (uint i = 0; i < num; i++) - { - Process child = new Process(); - child.StartInfo.FileName = Process.GetCurrentProcess().MainModule.FileName; - child.Start(); - } - } - // sleep is needed so the process doesn't exit before the test case kill it - Thread.Sleep(100000); - } - } -} diff --git a/test/tools/CreateChildProcess/CreateChildProcess.csproj b/test/tools/CreateChildProcess/CreateChildProcess.csproj deleted file mode 100644 index 2f50744943..0000000000 --- a/test/tools/CreateChildProcess/CreateChildProcess.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - - Very simple little console app that creates child processes of itself - netcoreapp2.0 - createchildprocess - Exe - ubuntu.16.04-x64;ubuntu.14.04-x64;debian.8-x64;centos.7-x64;fedora.24-x64;win7-x86;win7-x64;win81-x64;win10-x64;osx.10.11-x64;osx.10.12-x64;opensuse.13.2-x64;opensuse.42.1-x64 - - - diff --git a/test/tools/EchoArgs/EchoArgs.cs b/test/tools/EchoArgs/EchoArgs.cs deleted file mode 100644 index 209d5d8615..0000000000 --- a/test/tools/EchoArgs/EchoArgs.cs +++ /dev/null @@ -1,25 +0,0 @@ -//--------------------------------------------------------------------- -// Author: Keith Hill -// Source: https://github.com/Pscx/Pscx/blob/master/Src/EchoArgs/EchoArgs.cs -// -// Description: Very simple little console class that you can use to see -// how PowerShell is passing parameters to legacy console -// apps. -// -// Creation Date: March 06, 2006 -//--------------------------------------------------------------------- -using System; - -namespace Pscx.Applications -{ - class EchoArgs - { - static void Main(string[] args) - { - for (int i = 0; i < args.Length; i++) - { - Console.WriteLine("Arg {0} is <{1}>", i, args[i]); - } - } - } -} diff --git a/test/tools/TestExe/TestExe.cs b/test/tools/TestExe/TestExe.cs new file mode 100644 index 0000000000..5042ad2c67 --- /dev/null +++ b/test/tools/TestExe/TestExe.cs @@ -0,0 +1,64 @@ +using System; +using System.Threading; +using System.Diagnostics; + +namespace TestExe +{ + class TestExe + { + static void Main(string[] args) + { + if (args.Length > 0) + { + switch(args[0].ToLowerInvariant()) + { + case "-echoargs": + EchoArgs(args); + break; + case "-createchildprocess": + CreateChildProcess(args); + break; + default: + Console.WriteLine("Unknown test {0}", args[0]); + break; + } + } + else + { + Console.WriteLine("Test not specified"); + } + } + + // + // Echos back to stdout the arguments passed in + // + static void EchoArgs(string[] args) + { + for (int i = 1; i < args.Length; i++) + { + Console.WriteLine("Arg {0} is <{1}>", i-1, args[i]); + } + } + + // + // First argument is the number of child processes to create which are instances of itself + // Processes automatically exit after 100 seconds + // + static void CreateChildProcess(string[] args) + { + if (args.Length > 1) + { + uint num = UInt32.Parse(args[1]); + for (uint i = 0; i < num; i++) + { + Process child = new Process(); + child.StartInfo.FileName = Process.GetCurrentProcess().MainModule.FileName; + child.StartInfo.Arguments = "-createchildprocess"; + child.Start(); + } + } + // sleep is needed so the process doesn't exit before the test case kill it + Thread.Sleep(100000); + } + } +} diff --git a/test/tools/EchoArgs/EchoArgs.csproj b/test/tools/TestExe/TestExe.csproj similarity index 78% rename from test/tools/EchoArgs/EchoArgs.csproj rename to test/tools/TestExe/TestExe.csproj index 60566fe6e2..9e5851e1b1 100644 --- a/test/tools/EchoArgs/EchoArgs.csproj +++ b/test/tools/TestExe/TestExe.csproj @@ -1,11 +1,11 @@ - - - - Very simple little console class that you can use to see how PowerShell is passing parameters to legacy console apps. - netcoreapp2.0 - echoargs - Exe - ubuntu.16.04-x64;ubuntu.14.04-x64;debian.8-x64;centos.7-x64;fedora.24-x64;win7-x86;win7-x64;win81-x64;win10-x64;osx.10.11-x64;osx.10.12-x64;opensuse.13.2-x64;opensuse.42.1-x64 - - - + + + + Very simple little console class that you can use to for testing PowerShell interaction with native commands + netcoreapp2.0 + testexe + Exe + ubuntu.16.04-x64;ubuntu.14.04-x64;debian.8-x64;centos.7-x64;fedora.24-x64;win7-x86;win7-x64;win81-x64;win10-x64;osx.10.11-x64;osx.10.12-x64;opensuse.13.2-x64;opensuse.42.1-x64 + + +