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