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 com.android.tradefed.log.LogUtil.CLog;
19 import com.android.tradefed.util.IRunUtil.IRunnableResult;
20 
21 import junit.framework.TestCase;
22 
23 import java.io.BufferedWriter;
24 import java.io.File;
25 import java.io.FileWriter;
26 import java.io.IOException;
27 import java.io.Writer;
28 
29 /**
30  * Longer running tests for {@link RunUtilFuncTest}
31  */
32 public class RunUtilFuncTest extends TestCase {
33 
34     private static final long VERY_SHORT_TIMEOUT_MS = 10l;
35     private static final long SHORT_TIMEOUT_MS = 500l;
36     private static final long LONG_TIMEOUT_MS = 5000l;
37 
38     private abstract class MyRunnable implements IRunUtil.IRunnableResult {
39         boolean mCanceled = false;
40 
41         @Override
cancel()42         public void cancel() {
43             mCanceled = true;
44         }
45     }
46 
47     /**
48      * Test timeout case for {@link RunUtil#runTimed(long, IRunnableResult, boolean)}.
49      */
testRunTimed_timeout()50     public void testRunTimed_timeout() {
51         MyRunnable mockRunnable = new MyRunnable() {
52             @Override
53             public boolean run() {
54                 try {
55                     Thread.sleep(SHORT_TIMEOUT_MS * 5);
56                 } catch (InterruptedException e) {
57                     // ignore
58                 }
59                 return true;
60             }
61         };
62         assertEquals(CommandStatus.TIMED_OUT, RunUtil.getDefault().runTimed(SHORT_TIMEOUT_MS,
63                 mockRunnable, true));
64         assertTrue(mockRunnable.mCanceled);
65     }
66 
67     /**
68      * Test method for {@link RunUtil#runTimedRetry(long, long, int, IRunnableResult)}.
69      * Verify that multiple attempts are made.
70      */
testRunTimedRetry()71     public void testRunTimedRetry() {
72         final int maxAttempts = 5;
73         IRunUtil.IRunnableResult mockRunnable = new IRunUtil.IRunnableResult() {
74             int attempts = 0;
75             @Override
76             public boolean run() {
77                 attempts++;
78                 return attempts == maxAttempts;
79             }
80             @Override
81             public void cancel() {
82                 // ignore
83             }
84         };
85         final long startTime = System.currentTimeMillis();
86         assertTrue(RunUtil.getDefault().runTimedRetry(100, SHORT_TIMEOUT_MS, maxAttempts,
87                 mockRunnable));
88         final long actualTime = System.currentTimeMillis() - startTime;
89         // assert that time actually taken is at least, and no more than twice expected
90         final long expectedPollTime = SHORT_TIMEOUT_MS * (maxAttempts-1);
91         assertTrue(String.format("Expected poll time %d, got %d", expectedPollTime, actualTime),
92                 expectedPollTime <= actualTime && actualTime <= (2 * expectedPollTime));
93     }
94 
95     /**
96      * Test timeout case for {@link RunUtil#runTimedCmd(long, String...)} and ensure we
97      * consistently get the right stdout for a fast running command.
98      */
testRunTimedCmd_repeatedOutput()99     public void testRunTimedCmd_repeatedOutput() {
100         for (int i = 0; i < 1000; i++) {
101             CommandResult result =
102                     RunUtil.getDefault().runTimedCmd(LONG_TIMEOUT_MS, "echo", "hello");
103             assertTrue("Failed at iteration " + i,
104                     CommandStatus.SUCCESS.equals(result.getStatus()));
105             CLog.d(result.getStdout());
106             assertTrue(result.getStdout().trim().equals("hello"));
107         }
108     }
109 
110     /**
111      * Test that that running a command with a 0 timeout results in no timeout being applied to it.
112      */
testRunTimedCmd_noTimeout()113     public void testRunTimedCmd_noTimeout() {
114         // When there is no timeout, max_poll interval will be 30sec so we need a test with more
115         // than 30sec
116         CommandResult result = RunUtil.getDefault().runTimedCmd(0l, "sleep", "35");
117         assertTrue(CommandStatus.SUCCESS.equals(result.getStatus()));
118         assertTrue(result.getStdout().isEmpty());
119     }
120 
121     /**
122      * Test case for {@link RunUtil#runTimedCmd(long, String...)} for a command that produces
123      * a large amount of output
124      * @throws IOException
125      */
testRunTimedCmd_largeOutput()126     public void testRunTimedCmd_largeOutput() throws IOException {
127         // 1M  chars
128         int dataSize = 1000000;
129         File f = FileUtil.createTempFile("foo", ".txt");
130         Writer s = null;
131         try {
132             s = new BufferedWriter(new FileWriter(f));
133             for (int i=0; i < dataSize; i++) {
134                 s.write('a');
135             }
136             s.close();
137 
138             // FIXME: this test case is not ideal, as it will only work on platforms that support
139             // cat command.
140             CommandResult result =
141                     RunUtil.getDefault()
142                             .runTimedCmd(3 * LONG_TIMEOUT_MS, "cat", f.getAbsolutePath());
143             assertEquals(
144                     String.format(
145                             "We expected SUCCESS but got %s, with stdout: '%s'\nstderr: %s",
146                             result.getStatus(), result.getStdout(), result.getStderr()),
147                     CommandStatus.SUCCESS,
148                     result.getStatus());
149             assertTrue(result.getStdout().length() == dataSize);
150         } finally {
151             f.delete();
152             StreamUtil.close(s);
153         }
154     }
155 
156     /**
157      * Test case for {@link RunUtil#unsetEnvVariable(String key)}
158      */
testUnsetEnvVariable()159     public void testUnsetEnvVariable() {
160         RunUtil runUtil = new RunUtil();
161         runUtil.setEnvVariable("bar", "foo");
162         // FIXME: this test case is not ideal, as it will only work on platforms that support
163         // printenv
164         CommandResult result =
165                 runUtil.runTimedCmdRetry(SHORT_TIMEOUT_MS, SHORT_TIMEOUT_MS, 3, "printenv", "bar");
166         assertEquals(
167                 String.format(
168                         "We expected SUCCESS but got %s, with stdout: '%s'\nstderr: %s",
169                         result.getStatus(), result.getStdout(), result.getStderr()),
170                 CommandStatus.SUCCESS,
171                 result.getStatus());
172         assertEquals("foo", result.getStdout().trim());
173 
174         // remove env variable
175         runUtil.unsetEnvVariable("bar");
176         // printenv with non-exist variable will fail
177         result = runUtil.runTimedCmd(SHORT_TIMEOUT_MS, "printenv", "bar");
178         assertEquals(CommandStatus.FAILED, result.getStatus());
179         assertEquals("", result.getStdout().trim());
180     }
181 
182     /**
183      * Test that {@link RunUtil#runTimedCmd(long, String[])} returns timeout when the command is too
184      * short and also clean up all its thread.
185      */
testRunTimedCmd_timeout()186     public void testRunTimedCmd_timeout() throws InterruptedException {
187         RunUtil runUtil = new RunUtil();
188         String[] command = {"sleep", "10000"};
189         CommandResult result = runUtil.runTimedCmd(VERY_SHORT_TIMEOUT_MS, command);
190         assertEquals(
191                 String.format(
192                         "We expected TIMED_OUT but got %s, with stdout: '%s'\nstderr: %s",
193                         result.getStatus(), result.getStdout(), result.getStderr()),
194                 CommandStatus.TIMED_OUT,
195                 result.getStatus());
196         assertEquals("", result.getStdout());
197         assertEquals("", result.getStderr());
198         // We give it some times to clean up the process
199         Thread.sleep(5000);
200         Thread[] list = new Thread[Thread.currentThread().getThreadGroup().activeCount()];
201         Thread.currentThread().getThreadGroup().enumerate(list);
202         // Ensure the list of Threads does not contain the RunnableNotifier or InheritIO threads.
203         for (Thread t : list) {
204             assertFalse(
205                     String.format("We found a thread: %s", t.getName()),
206                     t.getName().contains(RunUtil.RUNNABLE_NOTIFIER_NAME));
207             assertFalse(
208                     String.format("We found a thread: %s", t.getName()),
209                     t.getName().contains(RunUtil.INHERITIO_PREFIX));
210         }
211     }
212 }
213