Home | History | Annotate | Download | only in util
      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