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.mockito.Mockito.doReturn; 19 import static org.mockito.Mockito.doThrow; 20 21 import com.android.tradefed.util.IRunUtil.EnvPriority; 22 import com.android.tradefed.util.IRunUtil.IRunnableResult; 23 import com.android.tradefed.util.RunUtil.RunnableResult; 24 25 import junit.framework.TestCase; 26 27 import org.easymock.EasyMock; 28 import org.mockito.Mockito; 29 30 import java.io.ByteArrayInputStream; 31 import java.io.File; 32 import java.io.FileOutputStream; 33 import java.io.IOException; 34 import java.io.InputStream; 35 import java.io.OutputStream; 36 37 /** Unit tests for {@link RunUtil} */ 38 public class RunUtilTest extends TestCase { 39 40 private RunUtil mRunUtil; 41 private RunnableResult mMockRunnableResult; 42 private long mSleepTime = 0L; 43 private boolean success = false; 44 private static final long VERY_SHORT_TIMEOUT_MS = 10L; 45 private static final long SHORT_TIMEOUT_MS = 200L; 46 private static final long LONG_TIMEOUT_MS = 1000L; 47 // Timeout to ensure that IO depend tests have enough time to finish. They should not use the 48 // full duration in most cases. 49 private static final long VERY_LONG_TIMEOUT_MS = 5000L; 50 51 @Override 52 protected void setUp() throws Exception { 53 super.setUp(); 54 mRunUtil = new RunUtil(); 55 mMockRunnableResult = null; 56 } 57 58 /** Test class on {@link RunUtil} in order to avoid creating a real process. */ 59 class SpyRunUtil extends RunUtil { 60 private boolean mShouldThrow = false; 61 62 public SpyRunUtil(boolean shouldThrow) { 63 mShouldThrow = shouldThrow; 64 } 65 66 @Override 67 IRunnableResult createRunnableResult( 68 CommandResult result, 69 OutputStream stdout, 70 OutputStream stderr, 71 boolean closeStreamAfterRun, 72 String... command) { 73 IRunnableResult real = 74 super.createRunnableResult( 75 result, stdout, stderr, closeStreamAfterRun, command); 76 mMockRunnableResult = (RunnableResult) Mockito.spy(real); 77 try { 78 if (mShouldThrow) { 79 // Test if the binary does not exists, startProcess throws directly in this case 80 doThrow( 81 new RuntimeException( 82 "Cannot run program \"\": error=2," 83 + "No such file or directory")) 84 .when(mMockRunnableResult) 85 .startProcess(); 86 } else { 87 doReturn(new FakeProcess()).when(mMockRunnableResult).startProcess(); 88 } 89 } catch (Exception e) { 90 throw new RuntimeException(e); 91 } 92 return mMockRunnableResult; 93 } 94 } 95 96 /** 97 * Test success case for {@link RunUtil#runTimed(long, IRunnableResult, boolean)}. 98 */ 99 public void testRunTimed() throws Exception { 100 IRunUtil.IRunnableResult mockRunnable = EasyMock.createStrictMock( 101 IRunUtil.IRunnableResult.class); 102 EasyMock.expect(mockRunnable.run()).andReturn(Boolean.TRUE); 103 EasyMock.replay(mockRunnable); 104 assertEquals(CommandStatus.SUCCESS, 105 mRunUtil.runTimed(SHORT_TIMEOUT_MS, mockRunnable, true)); 106 } 107 108 /** 109 * Test failure case for {@link RunUtil#runTimed(long, IRunnableResult, boolean)}. 110 */ 111 public void testRunTimed_failed() throws Exception { 112 IRunUtil.IRunnableResult mockRunnable = EasyMock.createStrictMock( 113 IRunUtil.IRunnableResult.class); 114 EasyMock.expect(mockRunnable.run()).andReturn(Boolean.FALSE); 115 EasyMock.replay(mockRunnable); 116 assertEquals(CommandStatus.FAILED, 117 mRunUtil.runTimed(SHORT_TIMEOUT_MS, mockRunnable, true)); 118 } 119 120 /** 121 * Test exception case for {@link RunUtil#runTimed(long, IRunnableResult, boolean)}. 122 */ 123 public void testRunTimed_exception() throws Exception { 124 IRunUtil.IRunnableResult mockRunnable = EasyMock.createStrictMock( 125 IRunUtil.IRunnableResult.class); 126 EasyMock.expect(mockRunnable.run()).andThrow(new RuntimeException()); 127 mockRunnable.cancel(); 128 EasyMock.replay(mockRunnable); 129 assertEquals(CommandStatus.EXCEPTION, 130 mRunUtil.runTimed(SHORT_TIMEOUT_MS, mockRunnable, true)); 131 } 132 133 /** 134 * Test that {@link RunUtil#runTimedCmd(long, String[])} fails when given a garbage command. 135 */ 136 public void testRunTimedCmd_failed() { 137 RunUtil spyUtil = new SpyRunUtil(true); 138 CommandResult result = spyUtil.runTimedCmd(1000, "blahggggwarggg"); 139 assertEquals(CommandStatus.EXCEPTION, result.getStatus()); 140 assertEquals("", result.getStdout()); 141 assertEquals("", result.getStderr()); 142 } 143 144 /** 145 * Test that {@link RunUtil#runTimedCmd(long, String[])} is returning timed out state when the 146 * command does not return in time. 147 */ 148 public void testRunTimedCmd_timeout() { 149 String[] command = {"sleep", "10000"}; 150 CommandResult result = mRunUtil.runTimedCmd(VERY_SHORT_TIMEOUT_MS, command); 151 assertEquals(CommandStatus.TIMED_OUT, result.getStatus()); 152 assertEquals("", result.getStdout()); 153 assertEquals("", result.getStderr()); 154 } 155 156 /** 157 * Verify that calling {@link RunUtil#setWorkingDir(File)} is not allowed on default instance. 158 */ 159 public void testSetWorkingDir_default() { 160 try { 161 RunUtil.getDefault().setWorkingDir(new File("foo")); 162 fail("could set working dir on RunUtil.getDefault()"); 163 } catch (RuntimeException e) { 164 // expected 165 } 166 } 167 168 /** 169 * Verify that calling {@link RunUtil#setEnvVariable(String, String)} is not allowed on default 170 * instance. 171 */ 172 public void testSetEnvVariable_default() { 173 try { 174 RunUtil.getDefault().setEnvVariable("foo", "bar"); 175 fail("could set env var on RunUtil.getDefault()"); 176 } catch (RuntimeException e) { 177 // expected 178 } 179 } 180 181 /** 182 * Verify that calling {@link RunUtil#unsetEnvVariable(String)} is not allowed on default 183 * instance. 184 */ 185 public void testUnsetEnvVariable_default() { 186 try { 187 RunUtil.getDefault().unsetEnvVariable("foo"); 188 fail("could unset env var on RunUtil.getDefault()"); 189 } catch (Exception e) { 190 // expected 191 } 192 } 193 194 /** 195 * Test that {@link RunUtil#runEscalatingTimedRetry(long, long, long, long, IRunnableResult)} 196 * fails when operation continually fails, and that the maxTime variable is respected. 197 */ 198 public void testRunEscalatingTimedRetry_timeout() throws Exception { 199 // create a RunUtil fixture with methods mocked out for 200 // fast execution 201 202 RunUtil runUtil = new RunUtil() { 203 @Override 204 public void sleep(long time) { 205 mSleepTime += time; 206 } 207 208 @Override 209 long getCurrentTime() { 210 return mSleepTime; 211 } 212 213 @Override 214 public CommandStatus runTimed(long timeout, IRunUtil.IRunnableResult runnable, 215 boolean logErrors) { 216 try { 217 // override parent with simple version that doesn't create a thread 218 return runnable.run() ? CommandStatus.SUCCESS : CommandStatus.FAILED; 219 } catch (Exception e) { 220 return CommandStatus.EXCEPTION; 221 } 222 } 223 }; 224 225 IRunUtil.IRunnableResult mockRunnable = EasyMock.createStrictMock( 226 IRunUtil.IRunnableResult.class); 227 // expect a call 4 times, at sleep time 0, 1, 4 and 10 ms 228 EasyMock.expect(mockRunnable.run()).andReturn(Boolean.FALSE).times(4); 229 EasyMock.replay(mockRunnable); 230 long maxTime = 10; 231 assertFalse(runUtil.runEscalatingTimedRetry(1, 1, 512, maxTime, mockRunnable)); 232 assertEquals(maxTime, mSleepTime); 233 EasyMock.verify(mockRunnable); 234 } 235 236 /** 237 * Test a success case for {@link RunUtil#interrupt}. 238 */ 239 public void testInterrupt() { 240 final String message = "it is alright now"; 241 mRunUtil.allowInterrupt(true); 242 mRunUtil.interrupt(Thread.currentThread(), message); 243 try{ 244 mRunUtil.sleep(1); 245 fail("RunInterruptedException was expected, but not thrown."); 246 } catch (final RunInterruptedException e) { 247 assertEquals(message, e.getMessage()); 248 } 249 } 250 251 /** 252 * Test whether a {@link RunUtil#interrupt} call is respected when called while interrupts are 253 * not allowed. 254 */ 255 public void testInterrupt_delayed() { 256 final String message = "it is alright now"; 257 mRunUtil.allowInterrupt(false); 258 mRunUtil.interrupt(Thread.currentThread(), message); 259 mRunUtil.sleep(1); 260 try{ 261 mRunUtil.allowInterrupt(true); 262 mRunUtil.sleep(1); 263 fail("RunInterruptedException was expected, but not thrown."); 264 } catch (final RunInterruptedException e) { 265 assertEquals(message, e.getMessage()); 266 } 267 } 268 269 /** 270 * Test whether a {@link RunUtil#interrupt} call is respected when called multiple times. 271 */ 272 public void testInterrupt_multiple() { 273 final String message1 = "it is alright now"; 274 final String message2 = "without a fight"; 275 final String message3 = "rock this town"; 276 mRunUtil.allowInterrupt(true); 277 mRunUtil.interrupt(Thread.currentThread(), message1); 278 mRunUtil.interrupt(Thread.currentThread(), message2); 279 mRunUtil.interrupt(Thread.currentThread(), message3); 280 try{ 281 mRunUtil.sleep(1); 282 fail("RunInterruptedException was expected, but not thrown."); 283 } catch (final RunInterruptedException e) { 284 assertEquals(message3, e.getMessage()); 285 } 286 } 287 288 /** 289 * Test whether a {@link RunUtil#runTimedCmd(long, OutputStream, OutputStream, String[])} 290 * call correctly redirect the output to files. 291 */ 292 public void testRuntimedCmd_withFileOutputStream() { 293 File stdout = null; 294 File stderr = null; 295 OutputStream stdoutStream = null; 296 OutputStream stderrStream = null; 297 try { 298 stdout = FileUtil.createTempFile("stdout_subprocess_", ".txt"); 299 stdoutStream = new FileOutputStream(stdout); 300 stderr = FileUtil.createTempFile("stderr_subprocess_", ".txt"); 301 stderrStream = new FileOutputStream(stderr); 302 } catch (IOException e) { 303 fail("Failed to create output files: " + e.getMessage()); 304 } 305 RunUtil spyUtil = new SpyRunUtil(false); 306 String[] command = {"echo", "TEST"}; 307 CommandResult result = 308 spyUtil.runTimedCmd(LONG_TIMEOUT_MS, stdoutStream, stderrStream, command); 309 assertEquals(CommandStatus.SUCCESS, result.getStatus()); 310 assertEquals(result.getStdout(), 311 "redirected to " + stdoutStream.getClass().getSimpleName()); 312 assertEquals(result.getStderr(), 313 "redirected to " + stderrStream.getClass().getSimpleName()); 314 assertTrue(stdout.exists()); 315 assertTrue(stderr.exists()); 316 try { 317 assertEquals("TEST\n", FileUtil.readStringFromFile(stdout)); 318 assertEquals("", FileUtil.readStringFromFile(stderr)); 319 } catch (IOException e) { 320 fail(e.getMessage()); 321 } finally { 322 FileUtil.deleteFile(stdout); 323 FileUtil.deleteFile(stderr); 324 } 325 } 326 327 /** 328 * Test whether a {@link RunUtil#runTimedCmd(long, OutputStream, OutputStream, String[])} call 329 * correctly redirect the output to stdout because files are null. Replace the process by a fake 330 * one to avoid waiting on real system IO. 331 */ 332 public void testRuntimedCmd_regularOutput_fileNull() { 333 RunUtil spyUtil = new SpyRunUtil(false); 334 String[] command = {"echo", "TEST"}; 335 CommandResult result = spyUtil.runTimedCmd(LONG_TIMEOUT_MS, null, null, command); 336 assertEquals(CommandStatus.SUCCESS, result.getStatus()); 337 assertEquals(result.getStdout(), "TEST\n"); 338 assertEquals(result.getStderr(), ""); 339 } 340 341 /** 342 * Test whether a {@link RunUtil#runTimedCmd(long, OutputStream, OutputStream, String[])} 343 * redirect to the file even if they become non-writable afterward. 344 */ 345 public void testRuntimedCmd_notWritable() { 346 File stdout = null; 347 File stderr = null; 348 OutputStream stdoutStream = null; 349 OutputStream stderrStream = null; 350 try { 351 stdout = FileUtil.createTempFile("stdout_subprocess_", ".txt"); 352 stdoutStream = new FileOutputStream(stdout); 353 stdout.setWritable(false); 354 stderr = FileUtil.createTempFile("stderr_subprocess_", ".txt"); 355 stderrStream = new FileOutputStream(stderr); 356 stderr.setWritable(false); 357 } catch (IOException e) { 358 fail("Failed to create output files: " + e.getMessage()); 359 } 360 RunUtil spyUtil = new SpyRunUtil(false); 361 String[] command = {"echo", "TEST"}; 362 CommandResult result = 363 spyUtil.runTimedCmd(SHORT_TIMEOUT_MS, stdoutStream, stderrStream, command); 364 assertEquals(CommandStatus.SUCCESS, result.getStatus()); 365 assertEquals(result.getStdout(), 366 "redirected to " + stdoutStream.getClass().getSimpleName()); 367 assertEquals(result.getStderr(), 368 "redirected to " + stderrStream.getClass().getSimpleName()); 369 assertTrue(stdout.exists()); 370 assertTrue(stderr.exists()); 371 try { 372 assertEquals("TEST\n", FileUtil.readStringFromFile(stdout)); 373 assertEquals("", FileUtil.readStringFromFile(stderr)); 374 } catch (IOException e) { 375 fail(e.getMessage()); 376 } finally { 377 stdout.setWritable(true); 378 stderr.setWritable(true); 379 FileUtil.deleteFile(stdout); 380 FileUtil.deleteFile(stderr); 381 } 382 } 383 384 /** 385 * Test whether a {@link RunUtil#setInterruptibleInFuture} change properly the interruptible 386 * state. 387 */ 388 public void testSetInterruptibleInFuture() { 389 final Thread test = 390 new Thread( 391 new Runnable() { 392 @Override 393 public void run() { 394 mRunUtil.allowInterrupt(false); 395 assertFalse(mRunUtil.isInterruptAllowed()); 396 mRunUtil.setInterruptibleInFuture(Thread.currentThread(), 10); 397 try { 398 mRunUtil.sleep(25); 399 mRunUtil.sleep(25); 400 fail(); 401 } catch (RunInterruptedException rie) { 402 assertEquals("TEST", rie.getMessage()); 403 } 404 success = mRunUtil.isInterruptAllowed(); 405 mRunUtil.terminateTimer(); 406 } 407 }); 408 mRunUtil.interrupt(test, "TEST"); 409 test.start(); 410 try { 411 test.join(); 412 } catch (InterruptedException e) { 413 // Ignore 414 } 415 assertTrue(success); 416 } 417 418 /** 419 * Test whether a {@link RunUtil#setInterruptibleInFuture} has not change the state yet. 420 */ 421 public void testSetInterruptibleInFuture_beforeTimeout() { 422 mRunUtil.allowInterrupt(false); 423 assertFalse(mRunUtil.isInterruptAllowed()); 424 mRunUtil.setInterruptibleInFuture(Thread.currentThread(), LONG_TIMEOUT_MS); 425 mRunUtil.sleep(10); 426 // Should still be false 427 assertFalse(mRunUtil.isInterruptAllowed()); 428 mRunUtil.terminateTimer(); 429 } 430 431 /** 432 * Test {@link RunUtil#setEnvVariablePriority(EnvPriority)} properly prioritize unset. 433 */ 434 public void testUnsetPriority() { 435 final String ENV_NAME = "TF_GLO"; 436 RunUtil testRunUtil = new RunUtil(); 437 testRunUtil.setEnvVariablePriority(EnvPriority.UNSET); 438 testRunUtil.setEnvVariable(ENV_NAME, "initvalue"); 439 testRunUtil.unsetEnvVariable(ENV_NAME); 440 CommandResult result = 441 testRunUtil.runTimedCmd( 442 VERY_LONG_TIMEOUT_MS, "/bin/bash", "-c", "echo $" + ENV_NAME); 443 assertNotNull(result.getStdout()); 444 // Variable should be unset, some echo return empty line break. 445 assertEquals("\n", result.getStdout()); 446 } 447 448 /** 449 * Test {@link RunUtil#setEnvVariablePriority(EnvPriority)} properly prioritize set. 450 */ 451 public void testUnsetPriority_inverted() { 452 final String ENV_NAME = "TF_GLO"; 453 final String expected = "initvalue"; 454 RunUtil testRunUtil = new RunUtil(); 455 testRunUtil.setEnvVariablePriority(EnvPriority.SET); 456 testRunUtil.setEnvVariable(ENV_NAME, expected); 457 testRunUtil.unsetEnvVariable(ENV_NAME); 458 CommandResult result = 459 testRunUtil.runTimedCmd(LONG_TIMEOUT_MS, "/bin/bash", "-c", "echo $" + ENV_NAME); 460 assertNotNull(result.getStdout()); 461 // Variable should be set and returned. 462 assertEquals(expected + "\n", result.getStdout()); 463 } 464 465 /** 466 * Implementation of {@link Process} to simulate a success of 'echo Test' without actually 467 * calling the underlying system. 468 */ 469 private class FakeProcess extends Process { 470 471 @Override 472 public int waitFor() throws InterruptedException { 473 return 0; 474 } 475 476 @Override 477 public OutputStream getOutputStream() { 478 return null; 479 } 480 481 @Override 482 public InputStream getInputStream() { 483 ByteArrayInputStream stream = new ByteArrayInputStream("TEST\n".getBytes()); 484 return stream; 485 } 486 487 @Override 488 public InputStream getErrorStream() { 489 return new ByteArrayInputStream("".getBytes()); 490 } 491 492 @Override 493 public int exitValue() { 494 return 0; 495 } 496 497 @Override 498 public void destroy() { 499 // ignore 500 } 501 } 502 } 503