1/* 2 * Copyright (C) 2012 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 17package com.android.commands.uiautomator; 18 19import android.os.Bundle; 20 21import com.android.commands.uiautomator.Launcher.Command; 22import com.android.uiautomator.testrunner.UiAutomatorTestRunner; 23 24import java.util.ArrayList; 25import java.util.List; 26 27/** 28 * Implementation of the runtest sub command 29 * 30 */ 31public class RunTestCommand extends Command { 32 33 private static final String CLASS_PARAM = "class"; 34 private static final String DEBUG_PARAM = "debug"; 35 private static final String RUNNER_PARAM = "runner"; 36 private static final String CLASS_SEPARATOR = ","; 37 private static final int ARG_OK = 0; 38 private static final int ARG_FAIL_INCOMPLETE_E = -1; 39 private static final int ARG_FAIL_INCOMPLETE_C = -2; 40 private static final int ARG_FAIL_NO_CLASS = -3; 41 private static final int ARG_FAIL_RUNNER = -4; 42 private static final int ARG_FAIL_UNSUPPORTED = -99; 43 44 private Bundle mParams = new Bundle(); 45 private List<String> mTestClasses = new ArrayList<String>(); 46 private boolean mDebug; 47 private String mRunner; 48 49 public RunTestCommand() { 50 super("runtest"); 51 } 52 53 @Override 54 public void run(String[] args) { 55 int ret = parseArgs(args); 56 switch (ret) { 57 case ARG_FAIL_INCOMPLETE_C: 58 System.err.println("Incomplete '-c' parameter."); 59 System.exit(ARG_FAIL_INCOMPLETE_C); 60 break; 61 case ARG_FAIL_INCOMPLETE_E: 62 System.err.println("Incomplete '-e' parameter."); 63 System.exit(ARG_FAIL_INCOMPLETE_E); 64 break; 65 case ARG_FAIL_UNSUPPORTED: 66 System.err.println("Unsupported standalone parameter."); 67 System.exit(ARG_FAIL_UNSUPPORTED); 68 break; 69 default: 70 break; 71 } 72 if (mTestClasses.isEmpty()) { 73 System.err.println("Please specify at least one test class to run."); 74 System.exit(ARG_FAIL_NO_CLASS); 75 } 76 getRunner().run(mTestClasses, mParams, mDebug); 77 } 78 79 private int parseArgs(String[] args) { 80 // we are parsing for these parameters: 81 // -e <key> <value> 82 // key-value pairs 83 // special ones are: 84 // key is "class", parameter is passed onto JUnit as class name to run 85 // key is "debug", parameter will determine whether to wait for debugger 86 // to attach 87 // -c <class name> 88 // equivalent to -e class <class name>, i.e. passed onto JUnit 89 for (int i = 0; i < args.length; i++) { 90 if (args[i].equals("-e")) { 91 if (i + 2 < args.length) { 92 String key = args[++i]; 93 String value = args[++i]; 94 if (CLASS_PARAM.equals(key)) { 95 addTestClasses(value); 96 } else if (DEBUG_PARAM.equals(key)) { 97 mDebug = "true".equals(value) || "1".equals(value); 98 } else if (RUNNER_PARAM.equals(key)) { 99 mRunner = value; 100 } else { 101 mParams.putString(key, value); 102 } 103 } else { 104 return ARG_FAIL_INCOMPLETE_E; 105 } 106 } else if (args[i].equals("-c")) { 107 if (i + 1 < args.length) { 108 addTestClasses(args[++i]); 109 } else { 110 return ARG_FAIL_INCOMPLETE_C; 111 } 112 } else { 113 return ARG_FAIL_UNSUPPORTED; 114 } 115 } 116 return ARG_OK; 117 } 118 119 protected UiAutomatorTestRunner getRunner() { 120 if (mRunner == null) { 121 return new UiAutomatorTestRunner(); 122 } 123 // use reflection to get the runner 124 Object o = null; 125 try { 126 Class<?> clazz = Class.forName(mRunner); 127 o = clazz.newInstance(); 128 } catch (ClassNotFoundException cnfe) { 129 System.err.println("Cannot find runner: " + mRunner); 130 System.exit(ARG_FAIL_RUNNER); 131 } catch (InstantiationException ie) { 132 System.err.println("Cannot instantiate runner: " + mRunner); 133 System.exit(ARG_FAIL_RUNNER); 134 } catch (IllegalAccessException iae) { 135 System.err.println("Constructor of runner " + mRunner + " is not accessibile"); 136 System.exit(ARG_FAIL_RUNNER); 137 } 138 try { 139 UiAutomatorTestRunner runner = (UiAutomatorTestRunner)o; 140 return runner; 141 } catch (ClassCastException cce) { 142 System.err.println("Specified runner is not subclass of " 143 + UiAutomatorTestRunner.class.getSimpleName()); 144 System.exit(ARG_FAIL_RUNNER); 145 } 146 // won't reach here 147 return null; 148 } 149 150 /** 151 * Add test classes from a potentially comma separated list 152 * @param classes 153 */ 154 private void addTestClasses(String classes) { 155 String[] classArray = classes.split(CLASS_SEPARATOR); 156 for (String clazz : classArray) { 157 mTestClasses.add(clazz); 158 } 159 } 160 161 @Override 162 public String detailedOptions() { 163 return " runtest <class spec> [options]\n" 164 + " <class spec>: <JARS> [-c <CLASSES> | -e class <CLASSES>]\n" 165 + " <JARS>: a list of jar file containing test classes and dependencies. If\n" 166 + " the path is relative, it's assumed to be under /data/local/tmp. Use\n" 167 + " absolute path if the file is elsewhere. Multiple files can be\n" 168 + " specified, separated by space.\n" 169 + " <CLASSES>: a list of test class names to run, separated by comma. To\n" 170 + " a single method, use TestClass#testMethod format. The -e or -c option\n" 171 + " may be repeated.\n" 172 + " options:\n" 173 + " --nohup: trap SIG_HUP, so test won't terminate even if parent process\n" 174 + " is terminated, e.g. USB is disconnected.\n" 175 + " -e debug [true|false]: wait for debugger to connect before starting.\n" 176 + " -e runner [CLASS]: use specified test runner class instead. If\n" 177 + " unspecified, framework default runner will be used.\n" 178 + " -e <NAME> <VALUE>: other name-value pairs to be passed to test classes.\n" 179 + " May be repeated.\n"; 180 } 181 182 @Override 183 public String shortHelp() { 184 return "executes UI automation tests"; 185 } 186 187}