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