Skip to content

Commit

Permalink
Fixing error propagation for PowerShell 3,4 ensuring no breakage in P…
Browse files Browse the repository at this point in the history
…owerShell 5
  • Loading branch information
gabloe committed Mar 7, 2018
1 parent 5133350 commit a8364ba
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 18 deletions.
Expand Up @@ -71,7 +71,7 @@ public String getScript() {
if (capturingOutput) {
cmd = String.format(". '%s'; Execute-AndWriteOutput -MainScript '%s' -OutputFile '%s' -LogFile '%s' -ResultFile '%s' -CaptureOutput;",
quote(c.getPowerShellHelperFile(ws)),
quote(c.getPowerShellWrapperFile(ws)),
quote(c.getPowerShellScriptFile(ws)),
quote(c.getOutputFile(ws)),
quote(c.getLogFile(ws)),
quote(c.getResultFile(ws)));
Expand All @@ -95,16 +95,16 @@ public String getScript() {
args.addAll(Arrays.asList(powershellArgs.split(" ")));
args.addAll(Arrays.asList("-Command", cmd));

// Exception propagation does not occur in legacy PowerShell versions. This means that an exception thrown in an inner PowerShell process
// does not propagate to the outer process regardless of $ErrorActionPreference selection. This ensures the exit code is non-zero if an error occurs regardless of PowerShell version.
// Ensure backwards compatibility with PowerShell 3,4 for proper error propagation while also ensuring that output stream designations are present in PowerShell 5+
String scriptWrapper = String.format("[CmdletBinding()]\r\n" +
"param()\r\n" +
"& %s %s -File '%s';\r\n" +
"if ($Error) {\r\n" +
" exit 1;\r\n" +
"$scriptPath = '%s';\r\n" +
"if ($PSVersionTable.PSVersion.Major -ge 5) {\r\n" +
" & " + powershellBinary + " " + powershellArgs + " -File $scriptPath;\r\n" +
"} else {\r\n" +
" exit $LASTEXITCODE;\r\n" +
"}", powershellBinary, powershellArgs, quote(c.getPowerShellScriptFile(ws)));
" & $scriptPath;\r\n" +
"}\r\n" +
"exit $LASTEXITCODE;", quote(c.getPowerShellScriptFile(ws)));

// Add an explicit exit to the end of the script so that exit codes are propagated
String scriptWithExit = script + "\r\nexit $LASTEXITCODE;";
Expand Down
Expand Up @@ -39,6 +39,7 @@ param(
[Parameter(Mandatory=$false)] [switch]$CaptureOutput
)
try {
$exitCode = 0;
$errorCaught = $null;
[System.Text.Encoding] $encoding = New-Object System.Text.UTF8Encoding( $false );
[System.Console]::OutputEncoding = [System.Console]::InputEncoding = $encoding;
Expand All @@ -55,7 +56,6 @@ param(
$errorCaught = $_;
$errorCaught | Out-String -Width 192 | Out-FileNoBom -Writer $LogWriter;
} finally {
$exitCode = 0;
if ($LASTEXITCODE -ne $null) {
if ($LASTEXITCODE -eq 0 -and $errorCaught -ne $null) {
$exitCode = 1;
Expand Down
Expand Up @@ -124,27 +124,27 @@ public class PowershellScriptTest {
}

@Test public void implicitError() throws Exception {
Controller c = new PowershellScript("MyBogus-Cmdlet").launch(new EnvVars(), ws, launcher, listener);
Controller c = new PowershellScript("$ErrorActionPreference = 'Stop'; Write-Error \"Bogus error\"").launch(new EnvVars(), ws, launcher, listener);
while (c.exitStatus(ws, launcher, listener) == null) {
Thread.sleep(100);
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
c.writeLog(ws, baos);
assertTrue(c.exitStatus(ws, launcher).intValue() != 0);
assertThat(baos.toString(), containsString("MyBogus-Cmdlet"));
assertThat(baos.toString(), containsString("Bogus error"));
c.cleanup(ws);
}

@Test public void implicitErrorNegativeTest() throws Exception {
Controller c = new PowershellScript("$ErrorActionPreference = 'SilentlyContinue'; MyBogus-Cmdlet").launch(new EnvVars(), ws, launcher, listener);
Controller c = new PowershellScript("$ErrorActionPreference = 'SilentlyContinue'; Write-Error \"Bogus error\"").launch(new EnvVars(), ws, launcher, listener);
while (c.exitStatus(ws, launcher, listener) == null) {
Thread.sleep(100);
}
assertTrue(c.exitStatus(ws, launcher).intValue() == 0);
c.cleanup(ws);
}

@Test public void explicitError() throws Exception {
@Test public void explicitThrow() throws Exception {
DurableTask task = new PowershellScript("Write-Output \"Hello, World!\"; throw \"explicit error\";");
task.captureOutput();
Controller c = task.launch(new EnvVars(), ws, launcher, listener);
Expand All @@ -155,19 +155,77 @@ public class PowershellScriptTest {
c.writeLog(ws, baos);
assertTrue(c.exitStatus(ws, launcher, listener).intValue() != 0);
assertThat(baos.toString(), containsString("explicit error"));
assertEquals("Hello, World!\r\n", new String(c.getOutput(ws, launcher)));
if (launcher.isUnix()) {
assertEquals("Hello, World!\n", new String(c.getOutput(ws, launcher)));
} else {
assertEquals("Hello, World!\r\n", new String(c.getOutput(ws, launcher)));
}
c.cleanup(ws);
}

@Test public void implicitThrow() throws Exception {
DurableTask task = new PowershellScript("$ErrorActionPreference = 'Stop'; My-BogusCmdlet;");
Controller c = task.launch(new EnvVars(), ws, launcher, listener);
while (c.exitStatus(ws, launcher, listener) == null) {
Thread.sleep(100);
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
c.writeLog(ws, baos);
assertTrue(c.exitStatus(ws, launcher, listener).intValue() != 0);
assertThat(baos.toString(), containsString("My-BogusCmdlet"));
c.cleanup(ws);
}

@Test public void verbose() throws Exception {
DurableTask task = new PowershellScript("$VerbosePreference = \"Continue\"; Write-Verbose \"Hello, World!\"");
@Test public void noStdoutPollution() throws Exception {
DurableTask task = new PowershellScript("$VerbosePreference = \"Continue\"; " +
"$WarningPreference = \"Continue\"; " +
"$DebugPreference = \"Continue\"; " +
"Write-Verbose \"Hello, Verbose!\"; " +
"Write-Warning \"Hello, Warning!\"; " +
"Write-Debug \"Hello, Debug!\"; " +
"Write-Output \"Success\"");
task.captureOutput();
Controller c = task.launch(new EnvVars(), ws, launcher, listener);
while (c.exitStatus(ws, launcher, listener) == null) {
Thread.sleep(100);
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
c.writeLog(ws, baos);
assertTrue(c.exitStatus(ws, launcher, listener).intValue() == 0);
assertThat(baos.toString(), containsString("Hello, Verbose!"));
assertThat(baos.toString(), containsString("Hello, Warning!"));
assertThat(baos.toString(), containsString("Hello, Debug!"));
if (launcher.isUnix()) {
assertEquals("Success\n", new String(c.getOutput(ws, launcher)));
} else {
assertEquals("Success\r\n", new String(c.getOutput(ws, launcher)));
}
c.cleanup(ws);
}

@Test public void specialStreams() throws Exception {
DurableTask task = new PowershellScript("$VerbosePreference = \"Continue\"; " +
"$WarningPreference = \"Continue\"; " +
"$DebugPreference = \"Continue\"; " +
"Write-Verbose \"Hello, Verbose!\"; " +
"Write-Warning \"Hello, Warning!\"; " +
"Write-Debug \"Hello, Debug!\";");
Controller c = task.launch(new EnvVars(), ws, launcher, listener);
while (c.exitStatus(ws, launcher, listener) == null) {
Thread.sleep(100);
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
c.writeLog(ws, baos);
assertEquals(0, c.exitStatus(ws, launcher).intValue());
assertEquals("VERBOSE: Hello, World!\r\n", new String(c.getOutput(ws, launcher)));
if (psVersion >= 5) {
assertThat(baos.toString(), containsString("VERBOSE: Hello, Verbose!"));
assertThat(baos.toString(), containsString("WARNING: Hello, Warning!"));
assertThat(baos.toString(), containsString("DEBUG: Hello, Debug!"));
} else {
assertThat(baos.toString(), containsString("Hello, Verbose!"));
assertThat(baos.toString(), containsString("Hello, Warning!"));
assertThat(baos.toString(), containsString("Hello, Debug!"));
}
c.cleanup(ws);
}

Expand Down Expand Up @@ -205,4 +263,4 @@ public class PowershellScriptTest {
assertTrue(log, log.contains("Helló, Wõrld ®"));
c.cleanup(ws);
}
}
}

0 comments on commit a8364ba

Please sign in to comment.