1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.tradefed.util; 17 18 import static org.junit.Assert.assertEquals; 19 import static org.junit.Assert.assertFalse; 20 import static org.junit.Assert.assertNotNull; 21 import static org.junit.Assert.assertTrue; 22 import static org.junit.Assert.fail; 23 import static org.mockito.Mockito.doNothing; 24 import static org.mockito.Mockito.doReturn; 25 import static org.mockito.Mockito.doThrow; 26 import static org.mockito.Mockito.mock; 27 import static org.mockito.Mockito.verify; 28 import static org.mockito.Mockito.when; 29 30 import com.android.tradefed.command.CommandInterrupter; 31 import com.android.tradefed.result.error.InfraErrorIdentifier; 32 import com.android.tradefed.util.IRunUtil.EnvPriority; 33 import com.android.tradefed.util.IRunUtil.IRunnableResult; 34 import com.android.tradefed.util.RunUtil.RunnableResult; 35 36 import org.junit.After; 37 import org.junit.Before; 38 import org.junit.Test; 39 import org.junit.runner.RunWith; 40 import org.junit.runners.JUnit4; 41 import org.mockito.Mockito; 42 43 import java.io.ByteArrayInputStream; 44 import java.io.File; 45 import java.io.FileOutputStream; 46 import java.io.IOException; 47 import java.io.InputStream; 48 import java.io.OutputStream; 49 import java.util.ArrayList; 50 import java.util.concurrent.TimeUnit; 51 52 /** Unit tests for {@link RunUtil} */ 53 @RunWith(JUnit4.class) 54 public class RunUtilTest { 55 56 private RunUtil mRunUtil; 57 private RunnableResult mMockRunnableResult; 58 private long mSleepTime = 0L; 59 private File mWorkingDir; 60 private static final long VERY_SHORT_TIMEOUT_MS = 10L; 61 private static final long SHORT_TIMEOUT_MS = 200L; 62 private static final long LONG_TIMEOUT_MS = 1000L; 63 // Timeout to ensure that IO depend tests have enough time to finish. They should not use the 64 // full duration in most cases. 65 private static final long VERY_LONG_TIMEOUT_MS = 5000L; 66 67 @Before setUp()68 public void setUp() throws Exception { 69 mRunUtil = new RunUtil(new CommandInterrupter()); 70 mRunUtil.setPollingInterval(SHORT_TIMEOUT_MS); 71 mMockRunnableResult = null; 72 mWorkingDir = FileUtil.createTempDir("working_dir_"); 73 } 74 75 @After tearDown()76 public void tearDown() { 77 // clear interrupted status 78 Thread.interrupted(); 79 FileUtil.recursiveDelete(mWorkingDir); 80 } 81 82 /** Test class on {@link RunUtil} in order to avoid creating a real process. */ 83 class SpyRunUtil extends RunUtil { 84 private boolean mShouldThrow = false; 85 SpyRunUtil(boolean shouldThrow)86 public SpyRunUtil(boolean shouldThrow) { 87 mShouldThrow = shouldThrow; 88 } 89 90 @Override createRunnableResult( OutputStream stdout, OutputStream stderr, ProcessBuilder processBuilder)91 RunnableResult createRunnableResult( 92 OutputStream stdout, OutputStream stderr, ProcessBuilder processBuilder) { 93 RunnableResult real = super.createRunnableResult(stdout, stderr, processBuilder); 94 mMockRunnableResult = Mockito.spy(real); 95 try { 96 if (mShouldThrow) { 97 // Test if the binary does not exists, startProcess throws directly in this case 98 doThrow( 99 new RuntimeException( 100 String.format( 101 "Cannot run program \"%s\": error=2," 102 + "No such file or directory", 103 processBuilder.command().get(0)))) 104 .when(mMockRunnableResult) 105 .startProcess(); 106 } else { 107 doReturn(new FakeProcess()).when(mMockRunnableResult).startProcess(); 108 } 109 } catch (Exception e) { 110 throw new RuntimeException(e); 111 } 112 return mMockRunnableResult; 113 } 114 } 115 116 /** Test class on {@link RunUtil} in order to monitor the real process. */ 117 class MonitoredRunUtil extends RunUtil { 118 public ProcessBuilder processBuilder; 119 MonitoredRunUtil(boolean inheritEnvVars)120 public MonitoredRunUtil(boolean inheritEnvVars) { 121 super(inheritEnvVars); 122 } 123 124 @Override createRunnableResult( OutputStream stdout, OutputStream stderr, ProcessBuilder processBuilder)125 RunnableResult createRunnableResult( 126 OutputStream stdout, OutputStream stderr, ProcessBuilder processBuilder) { 127 this.processBuilder = processBuilder; 128 return super.createRunnableResult(stdout, stderr, processBuilder); 129 } 130 } 131 132 /** Test success case for {@link RunUtil#runTimed(long, IRunnableResult, boolean)}. */ 133 @Test testRunTimed()134 public void testRunTimed() throws Exception { 135 IRunUtil.IRunnableResult mockRunnable = mock(IRunUtil.IRunnableResult.class); 136 when(mockRunnable.getCommand()).thenReturn(new ArrayList<>()); 137 when(mockRunnable.run()).thenReturn(Boolean.TRUE); 138 139 assertEquals( 140 CommandStatus.SUCCESS, mRunUtil.runTimed(SHORT_TIMEOUT_MS, mockRunnable, true)); 141 } 142 143 /** Test failure case for {@link RunUtil#runTimed(long, IRunnableResult, boolean)}. */ 144 @Test testRunTimed_failed()145 public void testRunTimed_failed() throws Exception { 146 IRunUtil.IRunnableResult mockRunnable = mock(IRunUtil.IRunnableResult.class); 147 when(mockRunnable.getCommand()).thenReturn(new ArrayList<>()); 148 when(mockRunnable.run()).thenReturn(Boolean.FALSE); 149 150 assertEquals(CommandStatus.FAILED, mRunUtil.runTimed(SHORT_TIMEOUT_MS, mockRunnable, true)); 151 } 152 153 /** Test exception case for {@link RunUtil#runTimed(long, IRunnableResult, boolean)}. */ 154 @Test testRunTimed_exception()155 public void testRunTimed_exception() throws Exception { 156 IRunUtil.IRunnableResult mockRunnable = mock(IRunUtil.IRunnableResult.class); 157 when(mockRunnable.getCommand()).thenReturn(new ArrayList<>()); 158 when(mockRunnable.run()).thenThrow(new RuntimeException()); 159 when(mockRunnable.getResult()).thenReturn(null); 160 161 assertEquals( 162 CommandStatus.EXCEPTION, 163 mRunUtil.runTimed(VERY_LONG_TIMEOUT_MS, mockRunnable, true)); 164 } 165 166 /** Test interrupted case for {@link RunUtil#runTimed(long, IRunnableResult, boolean)}. */ 167 @Test testRunTimed_interrupted()168 public void testRunTimed_interrupted() { 169 IRunnableResult runnable = Mockito.mock(IRunnableResult.class); 170 CommandInterrupter interrupter = Mockito.mock(CommandInterrupter.class); 171 RunUtil runUtil = new RunUtil(interrupter); 172 173 // interrupted during execution 174 doNothing().doThrow(RunInterruptedException.class).when(interrupter).checkInterrupted(); 175 176 try { 177 runUtil.runTimed(VERY_SHORT_TIMEOUT_MS, runnable, true); 178 fail("RunInterruptedException was expected, but not thrown."); 179 } catch (RunInterruptedException e) { 180 // Execution was cancelled due to interruption 181 Mockito.verify(runnable, Mockito.atLeast(1)).cancel(); 182 } 183 } 184 185 /** Test that {@link RunUtil#runTimedCmd(long, String[])} fails when given a garbage command. */ 186 @Test testRunTimedCmd_failed()187 public void testRunTimedCmd_failed() { 188 RunUtil spyUtil = new SpyRunUtil(true); 189 CommandResult result = spyUtil.runTimedCmd(VERY_LONG_TIMEOUT_MS, "blahggggwarggg"); 190 assertEquals(CommandStatus.EXCEPTION, result.getStatus()); 191 assertEquals("", result.getStdout()); 192 assertTrue(result.getStderr().contains("Cannot run program \"blahggggwarggg\"")); 193 } 194 195 /** 196 * Test {@link RunUtil#runTimedCmd(long, String[])} exits with status SUCCESS since the output 197 * monitor observed output on streams through the command time until finished. 198 */ 199 @Test testRunTimed_output_monitor()200 public void testRunTimed_output_monitor() { 201 // Long-running operation with changing output stream. 202 String[] command = {"/bin/bash", "-c", "for i in {1..5}; do echo hello; sleep 1; done"}; 203 204 // Should succeed and return sooner regardless of timeout. 205 CommandResult result = 206 mRunUtil.runTimedCmdWithOutputMonitor(VERY_LONG_TIMEOUT_MS * 5, 1200, command); 207 assertEquals(CommandStatus.SUCCESS, result.getStatus()); 208 } 209 210 /** 211 * Test {@link RunUtil#runTimedCmd(long, String[])} exits with status FAILED due to the output 212 * monitor not observing any output on the streams. 213 */ 214 @Test testRunTimed_output_monitor_failed()215 public void testRunTimed_output_monitor_failed() { 216 // Long-running operation with no output sent to stream. 217 String[] command = {"sleep", String.valueOf(VERY_LONG_TIMEOUT_MS * 5)}; 218 219 // Should fail and return sooner regardless of timeout. 220 CommandResult result = 221 mRunUtil.runTimedCmdWithOutputMonitor(VERY_LONG_TIMEOUT_MS * 5, 1200, command); 222 assertEquals(CommandStatus.FAILED, result.getStatus()); 223 } 224 225 /** 226 * Test {@link RunUtil#runTimedCmd(long, String[])} exits with status TIMED_OUT even if the 227 * output monitor is observing new output on the output streams since the timeout is short. 228 */ 229 @Test testRunTimed_output_monitor_timeout()230 public void testRunTimed_output_monitor_timeout() { 231 // Long-running operation with no output. 232 String[] command = {"sleep", String.valueOf(VERY_LONG_TIMEOUT_MS * 5)}; 233 234 // Should run out of time and timeout. 235 CommandResult result = 236 mRunUtil.runTimedCmdWithOutputMonitor( 237 SHORT_TIMEOUT_MS, SHORT_TIMEOUT_MS * 2, command); 238 assertEquals(CommandStatus.TIMED_OUT, result.getStatus()); 239 } 240 241 /** 242 * Test that {@link RunUtil#runTimedCmdWithInput(long, String, File, File, String...)} properly 243 * backfill errors. 244 */ 245 @Test testRunTimedCmdWithInput_failed()246 public void testRunTimedCmdWithInput_failed() throws Exception { 247 RunUtil spyUtil = new SpyRunUtil(true); 248 File stdout = FileUtil.createTempFile("stdout-test", "txt"); 249 File stderr = FileUtil.createTempFile("stderr-test", "txt"); 250 try { 251 CommandResult result = 252 spyUtil.runTimedCmdWithInput( 253 VERY_LONG_TIMEOUT_MS, null, stdout, stderr, "blahggggwarggg"); 254 assertEquals(CommandStatus.EXCEPTION, result.getStatus()); 255 assertEquals("", result.getStdout()); 256 assertTrue(result.getStderr().contains("Cannot run program \"blahggggwarggg\"")); 257 // Error was backfilled in stderr file 258 assertTrue( 259 FileUtil.readStringFromFile(stderr) 260 .contains("Cannot run program \"blahggggwarggg\"")); 261 } finally { 262 FileUtil.deleteFile(stdout); 263 FileUtil.deleteFile(stderr); 264 } 265 } 266 267 /** 268 * Test that {@link RunUtil#runTimedCmd(long, String[])} is returning timed out state when the 269 * command does not return in time. 270 */ 271 @Test testRunTimedCmd_timeout()272 public void testRunTimedCmd_timeout() { 273 String[] command = {"sleep", "10000"}; 274 CommandResult result = mRunUtil.runTimedCmd(VERY_SHORT_TIMEOUT_MS, command); 275 assertEquals(CommandStatus.TIMED_OUT, result.getStatus()); 276 assertEquals("", result.getStdout()); 277 assertEquals("", result.getStderr()); 278 } 279 280 /** 281 * Verify that calling {@link RunUtil#setWorkingDir(File)} is not allowed on default instance. 282 */ 283 @Test testSetWorkingDir_default()284 public void testSetWorkingDir_default() { 285 try { 286 RunUtil.getDefault().setWorkingDir(new File("foo")); 287 fail("could set working dir on RunUtil.getDefault()"); 288 } catch (RuntimeException e) { 289 // expected 290 } 291 } 292 293 /** 294 * Verify that calling {@link RunUtil#setEnvVariable(String, String)} is not allowed on default 295 * instance. 296 */ 297 @Test testSetEnvVariable_default()298 public void testSetEnvVariable_default() { 299 try { 300 RunUtil.getDefault().setEnvVariable("foo", "bar"); 301 fail("could set env var on RunUtil.getDefault()"); 302 } catch (RuntimeException e) { 303 // expected 304 } 305 } 306 307 /** 308 * Verify that calling {@link RunUtil#unsetEnvVariable(String)} is not allowed on default 309 * instance. 310 */ 311 @Test testUnsetEnvVariable_default()312 public void testUnsetEnvVariable_default() { 313 try { 314 RunUtil.getDefault().unsetEnvVariable("foo"); 315 fail("could unset env var on RunUtil.getDefault()"); 316 } catch (Exception e) { 317 // expected 318 } 319 } 320 321 /** 322 * Test that {@link RunUtil#runEscalatingTimedRetry(long, long, long, long, IRunnableResult)} 323 * fails when operation continually fails, and that the maxTime variable is respected. 324 */ 325 @Test testRunEscalatingTimedRetry_timeout()326 public void testRunEscalatingTimedRetry_timeout() throws Exception { 327 // create a RunUtil fixture with methods mocked out for 328 // fast execution 329 RunUtil runUtil = 330 new RunUtil() { 331 @Override 332 public void sleep(long time) { 333 mSleepTime += time; 334 } 335 336 @Override 337 long getCurrentTime() { 338 return mSleepTime; 339 } 340 341 @Override 342 public CommandStatus runTimed( 343 long timeout, IRunUtil.IRunnableResult runnable, boolean logErrors) { 344 try { 345 // override parent with simple version that doesn't create a thread 346 return runnable.run() ? CommandStatus.SUCCESS : CommandStatus.FAILED; 347 } catch (Exception e) { 348 return CommandStatus.EXCEPTION; 349 } 350 } 351 }; 352 353 IRunUtil.IRunnableResult mockRunnable = mock(IRunUtil.IRunnableResult.class); 354 // expect a call 4 times, at sleep time 0, 1, 4 and 10 ms 355 when(mockRunnable.run()).thenReturn(Boolean.FALSE); 356 357 long maxTime = 12; 358 assertFalse(runUtil.runEscalatingTimedRetry(1, 1, 512, maxTime, mockRunnable)); 359 assertEquals(maxTime, mSleepTime); 360 verify(mockRunnable, Mockito.atLeast(3)).run(); 361 } 362 363 /** Test a success case for {@link RunUtil#interrupt}. */ 364 @Test testInterrupt()365 public void testInterrupt() { 366 final String message = "it is alright now"; 367 mRunUtil.allowInterrupt(true); 368 try { 369 mRunUtil.interrupt( 370 Thread.currentThread(), message, InfraErrorIdentifier.TRADEFED_SHUTTING_DOWN); 371 fail("RunInterruptedException was expected, but not thrown."); 372 } catch (final RunInterruptedException e) { 373 assertEquals(message, e.getMessage()); 374 } 375 } 376 377 /** 378 * Test whether a {@link RunUtil#interrupt} call is respected when called while interrupts are 379 * not allowed. 380 */ 381 @Test testInterrupt_delayed()382 public void testInterrupt_delayed() { 383 final String message = "it is alright now"; 384 try { 385 mRunUtil.allowInterrupt(false); 386 mRunUtil.interrupt( 387 Thread.currentThread(), message, InfraErrorIdentifier.TRADEFED_SHUTTING_DOWN); 388 mRunUtil.sleep(1); 389 mRunUtil.allowInterrupt(true); 390 mRunUtil.sleep(1); 391 fail("RunInterruptedException was expected, but not thrown."); 392 } catch (final RunInterruptedException e) { 393 assertEquals(message, e.getMessage()); 394 } 395 } 396 397 /** Test whether a {@link RunUtil#interrupt} call is respected when called multiple times. */ 398 @Test testInterrupt_multiple()399 public void testInterrupt_multiple() { 400 final String message1 = "it is alright now"; 401 final String message2 = "without a fight"; 402 final String message3 = "rock this town"; 403 mRunUtil.allowInterrupt(true); 404 try { 405 mRunUtil.interrupt( 406 Thread.currentThread(), message1, InfraErrorIdentifier.TRADEFED_SHUTTING_DOWN); 407 mRunUtil.interrupt( 408 Thread.currentThread(), message2, InfraErrorIdentifier.TRADEFED_SHUTTING_DOWN); 409 mRunUtil.interrupt( 410 Thread.currentThread(), message3, InfraErrorIdentifier.TRADEFED_SHUTTING_DOWN); 411 fail("RunInterruptedException was expected, but not thrown."); 412 } catch (final RunInterruptedException e) { 413 assertEquals(message1, e.getMessage()); 414 } 415 } 416 417 /** 418 * Test whether a {@link RunUtil#runTimedCmd(long, OutputStream, OutputStream, String[])} call 419 * correctly redirect the output to files. 420 */ 421 @Test testRuntimedCmd_withFileOutputStream()422 public void testRuntimedCmd_withFileOutputStream() { 423 File stdout = null; 424 File stderr = null; 425 OutputStream stdoutStream = null; 426 OutputStream stderrStream = null; 427 try { 428 stdout = FileUtil.createTempFile("stdout_subprocess_", ".txt"); 429 stdoutStream = new FileOutputStream(stdout); 430 stderr = FileUtil.createTempFile("stderr_subprocess_", ".txt"); 431 stderrStream = new FileOutputStream(stderr); 432 } catch (IOException e) { 433 fail("Failed to create output files: " + e.getMessage()); 434 } 435 RunUtil spyUtil = new SpyRunUtil(false); 436 String[] command = {"unused", "cmd"}; 437 CommandResult result = 438 spyUtil.runTimedCmd(LONG_TIMEOUT_MS, stdoutStream, stderrStream, command); 439 assertEquals(CommandStatus.SUCCESS, result.getStatus()); 440 assertEquals( 441 result.getStdout(), "redirected to " + stdoutStream.getClass().getSimpleName()); 442 assertEquals( 443 result.getStderr(), "redirected to " + stderrStream.getClass().getSimpleName()); 444 assertTrue(stdout.exists()); 445 assertTrue(stderr.exists()); 446 try { 447 assertEquals("TEST STDOUT\n", FileUtil.readStringFromFile(stdout)); 448 assertEquals("TEST STDERR\n", FileUtil.readStringFromFile(stderr)); 449 } catch (IOException e) { 450 fail(e.getMessage()); 451 } finally { 452 FileUtil.deleteFile(stdout); 453 FileUtil.deleteFile(stderr); 454 } 455 } 456 457 /** 458 * Test whether a {@link RunUtil#runTimedCmd(long, OutputStream, OutputStream, String[])} call 459 * correctly redirect the output to stdout because files are null. Replace the process by a fake 460 * one to avoid waiting on real system IO. 461 */ 462 @Test testRuntimedCmd_regularOutput_fileNull()463 public void testRuntimedCmd_regularOutput_fileNull() { 464 String[] command = {"echo", "TEST STDOUT"}; 465 CommandResult result = mRunUtil.runTimedCmd(VERY_LONG_TIMEOUT_MS, null, null, command); 466 assertEquals(CommandStatus.SUCCESS, result.getStatus()); 467 assertEquals("TEST STDOUT\n", result.getStdout()); 468 assertEquals("", result.getStderr()); 469 } 470 471 /** 472 * Test whether a {@link RunUtil#runTimedCmd(long, OutputStream, OutputStream, String[])} 473 * redirect to the file even if they become non-writable afterward. 474 */ 475 @Test testRuntimedCmd_notWritable()476 public void testRuntimedCmd_notWritable() { 477 File stdout = null; 478 File stderr = null; 479 OutputStream stdoutStream = null; 480 OutputStream stderrStream = null; 481 try { 482 stdout = FileUtil.createTempFile("stdout_subprocess_", ".txt"); 483 stdoutStream = new FileOutputStream(stdout); 484 stdout.setWritable(false); 485 stderr = FileUtil.createTempFile("stderr_subprocess_", ".txt"); 486 stderrStream = new FileOutputStream(stderr); 487 stderr.setWritable(false); 488 } catch (IOException e) { 489 fail("Failed to create output files: " + e.getMessage()); 490 } 491 RunUtil spyUtil = new SpyRunUtil(false); 492 String[] command = {"unused", "cmd"}; 493 CommandResult result = 494 spyUtil.runTimedCmd(LONG_TIMEOUT_MS, stdoutStream, stderrStream, command); 495 try { 496 assertEquals(CommandStatus.SUCCESS, result.getStatus()); 497 assertEquals( 498 result.getStdout(), "redirected to " + stdoutStream.getClass().getSimpleName()); 499 assertEquals( 500 result.getStderr(), "redirected to " + stderrStream.getClass().getSimpleName()); 501 assertTrue(stdout.exists()); 502 assertTrue(stderr.exists()); 503 assertEquals("TEST STDOUT\n", FileUtil.readStringFromFile(stdout)); 504 assertEquals("TEST STDERR\n", FileUtil.readStringFromFile(stderr)); 505 } catch (IOException e) { 506 fail(e.getMessage()); 507 } finally { 508 stdout.setWritable(true); 509 stderr.setWritable(true); 510 FileUtil.deleteFile(stdout); 511 FileUtil.deleteFile(stderr); 512 } 513 } 514 515 /** 516 * Test whether a {@link RunUtil#setInterruptibleInFuture} change properly the interruptible 517 * state. 518 */ 519 @Test testSetInterruptibleInFuture()520 public void testSetInterruptibleInFuture() { 521 CommandInterrupter interrupter = Mockito.mock(CommandInterrupter.class); 522 RunUtil runUtil = new RunUtil(interrupter); 523 524 Thread thread = new Thread(); 525 runUtil.setInterruptibleInFuture(thread, 123L); 526 527 // RunUtil delegates to CommandInterrupter#allowInterruptAsync 528 Mockito.verify(interrupter) 529 .allowInterruptAsync( 530 Mockito.eq(thread), Mockito.eq(123L), Mockito.eq(TimeUnit.MILLISECONDS)); 531 Mockito.verifyNoMoreInteractions(interrupter); 532 } 533 534 /** Test {@link RunUtil#setEnvVariablePriority(EnvPriority)} properly prioritize unset. */ 535 @Test testUnsetPriority()536 public void testUnsetPriority() { 537 final String ENV_NAME = "TF_GLO"; 538 RunUtil testRunUtil = new RunUtil(); 539 testRunUtil.setEnvVariablePriority(EnvPriority.UNSET); 540 testRunUtil.setEnvVariable(ENV_NAME, "initvalue"); 541 testRunUtil.unsetEnvVariable(ENV_NAME); 542 CommandResult result = 543 testRunUtil.runTimedCmd( 544 VERY_LONG_TIMEOUT_MS, "/bin/bash", "-c", "echo $" + ENV_NAME); 545 assertNotNull(result.getStdout()); 546 // Variable should be unset, some echo return empty line break. 547 assertEquals("\n", result.getStdout()); 548 } 549 550 /** Test {@link RunUtil#setEnvVariablePriority(EnvPriority)} properly prioritize set. */ 551 @Test testUnsetPriority_inverted()552 public void testUnsetPriority_inverted() { 553 final String ENV_NAME = "TF_GLO"; 554 final String expected = "initvalue"; 555 RunUtil testRunUtil = new RunUtil(); 556 testRunUtil.setEnvVariablePriority(EnvPriority.SET); 557 testRunUtil.setEnvVariable(ENV_NAME, expected); 558 testRunUtil.unsetEnvVariable(ENV_NAME); 559 CommandResult result = 560 testRunUtil.runTimedCmd( 561 VERY_LONG_TIMEOUT_MS, "/bin/bash", "-c", "echo $" + ENV_NAME); 562 assertNotNull(result.getStdout()); 563 // Variable should be set and returned. 564 assertEquals(expected + "\n", result.getStdout()); 565 } 566 567 @Test testGotExitCodeFromCommand()568 public void testGotExitCodeFromCommand() { 569 RunUtil testRunUtil = new RunUtil(); 570 CommandResult result = 571 testRunUtil.runTimedCmd(VERY_LONG_TIMEOUT_MS, "/bin/bash", "-c", "exit 2"); 572 assertEquals("", result.getStdout()); 573 assertEquals("", result.getStderr()); 574 assertEquals(2, (int) result.getExitCode()); 575 } 576 577 @Test testSetRedirectStderrToStdout()578 public void testSetRedirectStderrToStdout() { 579 RunUtil testRunUtil = new RunUtil(); 580 testRunUtil.setRedirectStderrToStdout(true); 581 CommandResult result = 582 testRunUtil.runTimedCmd( 583 VERY_LONG_TIMEOUT_MS, 584 "/bin/bash", 585 "-c", 586 "echo 'TEST STDOUT'; echo 'TEST STDERR' >&2"); 587 assertEquals("TEST STDOUT\nTEST STDERR\n", result.getStdout()); 588 assertEquals("", result.getStderr()); 589 } 590 591 /** 592 * Implementation of {@link Process} to simulate a success of a command that echos to both 593 * stdout and stderr without actually calling the underlying system. 594 */ 595 private class FakeProcess extends Process { 596 597 @Override waitFor()598 public int waitFor() throws InterruptedException { 599 return 0; 600 } 601 602 @Override getOutputStream()603 public OutputStream getOutputStream() { 604 return null; 605 } 606 607 @Override getInputStream()608 public InputStream getInputStream() { 609 return new ByteArrayInputStream("TEST STDOUT\n".getBytes()); 610 } 611 612 @Override getErrorStream()613 public InputStream getErrorStream() { 614 return new ByteArrayInputStream("TEST STDERR\n".getBytes()); 615 } 616 617 @Override exitValue()618 public int exitValue() { 619 return 0; 620 } 621 622 @Override destroy()623 public void destroy() { 624 // ignore 625 } 626 } 627 } 628