1 /* 2 * Copyright (C) 2016 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.command; 17 18 import static org.junit.Assert.assertEquals; 19 import static org.junit.Assert.assertNull; 20 import static org.junit.Assert.assertTrue; 21 import static org.mockito.Mockito.doNothing; 22 import static org.mockito.Mockito.doReturn; 23 24 import com.android.tradefed.command.CommandRunner.ExitCode; 25 import com.android.tradefed.config.ConfigurationException; 26 import com.android.tradefed.config.GlobalConfiguration; 27 import com.android.tradefed.device.MockDeviceManager; 28 import com.android.tradefed.util.FileUtil; 29 30 import org.junit.After; 31 import org.junit.Before; 32 import org.junit.Test; 33 import org.junit.runner.RunWith; 34 import org.junit.runners.JUnit4; 35 import org.mockito.Mockito; 36 37 import java.io.ByteArrayOutputStream; 38 import java.io.File; 39 import java.io.PrintWriter; 40 41 /** 42 * Unit tests for {@link CommandRunner}. Always attempt to run on null-device (-n) to avoid hanging 43 * if no physical device are available. 44 */ 45 @RunWith(JUnit4.class) 46 public class CommandRunnerTest { 47 48 private static final String FAKE_CONFIG = "doesnotexit"; 49 private String mStackTraceOutput = null; 50 private ICommandScheduler mMockScheduler; 51 52 53 private static final String EMPTY_CONF_CONTENT = 54 "<configuration description=\"Empty Config\" />"; 55 private File mConfig; 56 private File mLogDir; 57 58 @Before 59 public void setUp() throws Exception { 60 mStackTraceOutput = null; 61 mConfig = FileUtil.createTempFile("empty", ".xml"); 62 FileUtil.writeToFile(EMPTY_CONF_CONTENT, mConfig); 63 mLogDir = FileUtil.createTempDir("command-runner-unit-test"); 64 mMockScheduler = 65 new CommandScheduler() { 66 @Override 67 protected void initLogging() {} 68 69 @Override 70 protected void cleanUp() {} 71 72 @Override 73 void initDeviceManager() { 74 try { 75 super.initDeviceManager(); 76 } catch (IllegalStateException e) { 77 // ignore re-init 78 } 79 } 80 }; 81 } 82 83 @After 84 public void tearDown() { 85 GlobalConfiguration.getInstance() 86 .getCommandScheduler() 87 .setLastInvocationExitCode(ExitCode.NO_ERROR, null); 88 FileUtil.deleteFile(mConfig); 89 FileUtil.recursiveDelete(mLogDir); 90 } 91 92 private class TestableCommandRunner extends CommandRunner { 93 94 @Override 95 void initGlobalConfig(String[] args) throws ConfigurationException { 96 try { 97 GlobalConfiguration.createGlobalConfiguration(args); 98 } catch (IllegalStateException e) { 99 // ignore re-init. 100 } 101 } 102 103 @Override 104 ICommandScheduler getCommandScheduler() { 105 return mMockScheduler; 106 } 107 108 /** We capture the stack trace if any. */ 109 @Override 110 void printStackTrace(Throwable e) { 111 ByteArrayOutputStream out = new ByteArrayOutputStream(); 112 PrintWriter pw = new PrintWriter(out); 113 e.printStackTrace(pw); 114 pw.flush(); 115 mStackTraceOutput = out.toString(); 116 } 117 } 118 119 /** 120 * Run with a known empty config, should always pass. 121 */ 122 @Test 123 public void testRun_noError() throws Exception { 124 mStackTraceOutput = null; 125 CommandRunner mRunner = new TestableCommandRunner(); 126 String[] args = { 127 mConfig.getAbsolutePath(), 128 "-n", 129 "--no-return-null", 130 "--no-throw-build-error", 131 "--log-file-path", 132 mLogDir.getAbsolutePath() 133 }; 134 mRunner.run(args); 135 assertEquals(0, mRunner.getErrorCode().getCodeValue()); 136 assertNull(mStackTraceOutput); 137 } 138 139 /** 140 * Run with a known empty config but fake a device unresponsive. 141 */ 142 @Test 143 public void testRun_deviceUnresponsive() { 144 CommandRunner mRunner = new TestableCommandRunner(); 145 String[] args = { 146 mConfig.getAbsolutePath(), 147 "-n", 148 "--test-throw-unresponsive", 149 "--log-file-path", 150 mLogDir.getAbsolutePath() 151 }; 152 mRunner.run(args); 153 assertEquals(ExitCode.DEVICE_UNRESPONSIVE, mRunner.getErrorCode()); 154 assertTrue( 155 String.format("%s does not contains the expected output", mStackTraceOutput), 156 mStackTraceOutput.contains( 157 "com.android.tradefed.device.DeviceUnresponsiveException: " 158 + "StubTest DeviceUnresponsiveException")); 159 } 160 161 /** 162 * Run with a known empty config but fake a device unavailable. 163 */ 164 @Test 165 public void testRun_deviceUnavailable() { 166 CommandRunner mRunner = new TestableCommandRunner(); 167 String[] args = { 168 mConfig.getAbsolutePath(), 169 "-n", 170 "--test-throw-not-available", 171 "--log-file-path", 172 mLogDir.getAbsolutePath() 173 }; 174 mRunner.run(args); 175 assertEquals(ExitCode.DEVICE_UNAVAILABLE, mRunner.getErrorCode()); 176 assertTrue( 177 String.format("%s does not contains the expected output", mStackTraceOutput), 178 mStackTraceOutput.contains( 179 "com.android.tradefed.device.DeviceNotAvailableException: " 180 + "StubTest DeviceNotAvailableException")); 181 } 182 183 /** 184 * Run with a known empty config but a throwable exception is caught. 185 */ 186 @Test 187 public void testRun_throwable() { 188 CommandRunner mRunner = new TestableCommandRunner(); 189 String[] args = { 190 mConfig.getAbsolutePath(), 191 "-n", 192 "--test-throw-runtime", 193 "--log-file-path", 194 mLogDir.getAbsolutePath() 195 }; 196 mRunner.run(args); 197 assertEquals(ExitCode.THROWABLE_EXCEPTION, mRunner.getErrorCode()); 198 assertTrue( 199 String.format("%s does not contains the expected output", mStackTraceOutput), 200 mStackTraceOutput.contains( 201 "java.lang.RuntimeException: StubTest RuntimeException")); 202 } 203 204 /** 205 * Run with a non existant config and expect a configuration exception because of it. 206 */ 207 @Test 208 public void testRun_ConfigError() { 209 String[] args = {FAKE_CONFIG}; 210 CommandRunner mRunner = new TestableCommandRunner(); 211 mRunner.run(args); 212 assertEquals(ExitCode.CONFIG_EXCEPTION, mRunner.getErrorCode()); 213 assertTrue( 214 "Stack does not contain expected message: " + mStackTraceOutput, 215 mStackTraceOutput.contains(FAKE_CONFIG)); 216 } 217 218 /** Test that if the device is not allocated after a timeout, we throw a NoDeviceException. */ 219 @Test 220 public void testRun_noDevice() throws Exception { 221 CommandScheduler mockScheduler = Mockito.spy(CommandScheduler.class); 222 CommandRunner mRunner = 223 new TestableCommandRunner() { 224 @Override 225 long getCheckDeviceTimeout() { 226 return 200l; 227 } 228 229 @Override 230 ICommandScheduler getCommandScheduler() { 231 return mockScheduler; 232 } 233 }; 234 String[] args = { 235 mConfig.getAbsolutePath(), 236 "-s", 237 "impossibleSerialThatWillNotBeFound", 238 "--log-file-path", 239 mLogDir.getAbsolutePath() 240 }; 241 doNothing().when(mockScheduler).initDeviceManager(); 242 doReturn(new MockDeviceManager(1)).when(mockScheduler).getDeviceManager(); 243 doNothing().when(mockScheduler).shutdownOnEmpty(); 244 doNothing().when(mockScheduler).initLogging(); 245 doNothing().when(mockScheduler).cleanUp(); 246 mRunner.run(args); 247 Mockito.verify(mockScheduler).shutdownOnEmpty(); 248 mockScheduler.join(5000); 249 assertEquals(ExitCode.NO_DEVICE_ALLOCATED, mRunner.getErrorCode()); 250 assertTrue( 251 String.format("%s does not contains the expected output", mStackTraceOutput), 252 mStackTraceOutput.contains( 253 "com.android.tradefed.device.NoDeviceException: No device was allocated " 254 + "for the command.")); 255 } 256 } 257