1 /* 2 * Copyright (C) 2009 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 17 package com.android.hosttest; 18 19 import java.util.ArrayList; 20 import java.util.List; 21 22 import junit.framework.Test; 23 import junit.framework.TestResult; 24 import junit.textui.TestRunner; 25 26 import com.android.ddmlib.AndroidDebugBridge; 27 import com.android.ddmlib.IDevice; 28 import com.android.ddmlib.Log; 29 import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener; 30 31 /** 32 * Command line interface for running DeviceTest tests. 33 * 34 * Extends junit.textui.TestRunner to handle optional -s (device serial) and -p (test data) 35 * arguments, and then pass their values to the instantiated DeviceTests. 36 * 37 * Provided test class must be a DeviceTest. 38 * 39 * @see junit.textui.TestRunner for more information on command line syntax. 40 */ 41 public class DeviceTestRunner extends TestRunner { 42 43 private static final String LOG_TAG = "DeviceTestRunner"; 44 private String mDeviceSerial = null; 45 private IDevice mDevice = null; 46 private String mTestDataPath = null; 47 DeviceTestRunner()48 private DeviceTestRunner() { 49 } 50 51 /** 52 * Starts the test run. 53 * Extracts out DeviceTestCase specific command line arguments, then passes control to parent 54 * TestRunner. 55 * @param args command line arguments 56 * @return {@link TestResult} 57 */ 58 @Override start(String[] args)59 public TestResult start(String[] args) throws Exception { 60 // holds unprocessed arguments to pass to parent 61 List<String> parentArgs = new ArrayList<String>(); 62 for (int i=0; i < args.length; i++) { 63 if (args[i].equals("-s")) { 64 i++; 65 mDeviceSerial = extractArg(args, i); 66 } else if (args[i].equals("-p")) { 67 i++; 68 mTestDataPath = extractArg(args, i); 69 } else { 70 // unrecognized arg, must be for parent 71 parentArgs.add(args[i]); 72 } 73 } 74 mDevice = connectToDevice(mDeviceSerial); 75 return super.start(parentArgs.toArray(new String[parentArgs.size()])); 76 } 77 extractArg(String[] args, int index)78 private String extractArg(String[] args, int index) { 79 if (args.length <= index) { 80 printUsage(); 81 throw new IllegalArgumentException("Error: not enough arguments"); 82 } 83 return args[index]; 84 } 85 86 /** 87 * Initializes DDMS library, and connects to specified Android device 88 * @param deviceSerial 89 * @return {@link IDevice} 90 * @throws IllegalArgumentException if specified device cannot be found 91 */ connectToDevice(String deviceSerial)92 private IDevice connectToDevice(String deviceSerial) { 93 // initialize DDMS with no clientSupport aka debugger support 94 AndroidDebugBridge.init(false /* clientSupport */); 95 AndroidDebugBridge adbBridge = AndroidDebugBridge.createBridge(); 96 for (IDevice device : adbBridge.getDevices()) { 97 if (deviceSerial == null) { 98 return device; 99 } else if (deviceSerial.equals(device.getSerialNumber())) { 100 return device; 101 } 102 } 103 System.out.println("Waiting for device..."); 104 NewDeviceListener listener = new NewDeviceListener(deviceSerial); 105 AndroidDebugBridge.addDeviceChangeListener(listener); 106 IDevice device = listener.waitForDevice(5000); 107 AndroidDebugBridge.removeDeviceChangeListener(listener); 108 if (device == null) { 109 throw new IllegalArgumentException("Could not connect to device"); 110 } 111 return device; 112 } 113 114 /** 115 * Listener for new Android devices 116 */ 117 private static class NewDeviceListener implements IDeviceChangeListener { 118 private IDevice mDevice; 119 private String mSerial; 120 NewDeviceListener(String serial)121 public NewDeviceListener(String serial) { 122 mSerial = serial; 123 } 124 deviceChanged(IDevice device, int changeMask)125 public void deviceChanged(IDevice device, int changeMask) { 126 } 127 deviceConnected(IDevice device)128 public void deviceConnected(IDevice device) { 129 if (mSerial == null) { 130 setDevice(device); 131 } else if (mSerial.equals(device.getSerialNumber())) { 132 setDevice(device); 133 } 134 } 135 setDevice(IDevice device)136 private synchronized void setDevice(IDevice device) { 137 mDevice = device; 138 notify(); 139 } 140 deviceDisconnected(IDevice device)141 public void deviceDisconnected(IDevice device) { 142 } 143 waitForDevice(long waitTime)144 public IDevice waitForDevice(long waitTime) { 145 synchronized(this) { 146 if (mDevice == null) { 147 try { 148 wait(waitTime); 149 } catch (InterruptedException e) { 150 System.out.println("Waiting for device interrupted"); 151 } 152 } 153 } 154 return mDevice; 155 } 156 } 157 158 /** 159 * Main entry point. 160 * 161 * Establishes connection to provided adb device and runs tests 162 * 163 * @param args expects: 164 * test class to run 165 * optionally, device serial number. If unspecified, will connect to first device found 166 * optionally, file system path to test data files 167 */ main(String[] args)168 public static void main(String[] args) { 169 DeviceTestRunner aTestRunner = new DeviceTestRunner(); 170 try { 171 TestResult r = aTestRunner.start(args); 172 if (!r.wasSuccessful()) 173 System.exit(FAILURE_EXIT); 174 System.exit(SUCCESS_EXIT); 175 } catch(Exception e) { 176 System.err.println(e.getMessage()); 177 System.exit(EXCEPTION_EXIT); 178 } 179 } 180 printUsage()181 private static void printUsage() { 182 System.out.println("Usage: DeviceTestRunner <test_class> [-s device_serial] " + 183 "[-p test_data_path]"); 184 } 185 186 /** 187 * Override parent to set DeviceTest data 188 */ 189 @Override doRun(Test test, boolean wait)190 public TestResult doRun(Test test, boolean wait) { 191 if (test instanceof DeviceTest) { 192 DeviceTest deviceTest = (DeviceTest)test; 193 deviceTest.setDevice(mDevice); 194 deviceTest.setTestAppPath(mTestDataPath); 195 } else { 196 Log.w(LOG_TAG, String.format("%s test class is not a DeviceTest.", 197 test.getClass().getName())); 198 } 199 return super.doRun(test, wait); 200 } 201 202 /** 203 * Override parent to create DeviceTestSuite wrapper, instead of TestSuite 204 */ 205 @Override runSingleMethod(String testCase, String method, boolean wait)206 protected TestResult runSingleMethod(String testCase, String method, boolean wait) 207 throws Exception { 208 Class testClass = loadSuiteClass(testCase); 209 Test test = DeviceTestSuite.createTest(testClass, method); 210 return doRun(test, wait); 211 } 212 } 213