1e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu/*
2e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * Copyright (C) 2012 The Android Open Source Project
3e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu *
4e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * Licensed under the Apache License, Version 2.0 (the "License");
5e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * you may not use this file except in compliance with the License.
6e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * You may obtain a copy of the License at
7e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu *
8e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu *      http://www.apache.org/licenses/LICENSE-2.0
9e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu *
10e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * Unless required by applicable law or agreed to in writing, software
11e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * distributed under the License is distributed on an "AS IS" BASIS,
12e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * See the License for the specific language governing permissions and
14e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * limitations under the License.
15e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu */
16e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
17e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhupackage com.android.commands.uiautomator;
18e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
19e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.os.Bundle;
20e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
216ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhuimport com.android.commands.uiautomator.Launcher.Command;
22e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport com.android.uiautomator.testrunner.UiAutomatorTestRunner;
23e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
24e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport java.util.ArrayList;
25e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport java.util.List;
26e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
276ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu/**
286ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu * Implementation of the runtest sub command
296ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu *
306ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu */
316ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhupublic class RunTestCommand extends Command {
32e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
33e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static final String CLASS_PARAM = "class";
34e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static final String DEBUG_PARAM = "debug";
35e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static final String RUNNER_PARAM = "runner";
36e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static final String CLASS_SEPARATOR = ",";
37e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static final int ARG_OK = 0;
38e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static final int ARG_FAIL_INCOMPLETE_E = -1;
39e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static final int ARG_FAIL_INCOMPLETE_C = -2;
40e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static final int ARG_FAIL_NO_CLASS = -3;
41e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static final int ARG_FAIL_RUNNER = -4;
42e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static final int ARG_FAIL_UNSUPPORTED = -99;
43e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
44e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private Bundle mParams = new Bundle();
45e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private List<String> mTestClasses = new ArrayList<String>();
46e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private boolean mDebug;
47e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private String mRunner;
48e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
496ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu    public RunTestCommand() {
506ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu        super("runtest");
51e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
52e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
536ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu    @Override
546ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu    public void run(String[] args) {
55e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        int ret = parseArgs(args);
56e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        switch (ret) {
57e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            case ARG_FAIL_INCOMPLETE_C:
58e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                System.err.println("Incomplete '-c' parameter.");
59e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                System.exit(ARG_FAIL_INCOMPLETE_C);
60e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                break;
61e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            case ARG_FAIL_INCOMPLETE_E:
62e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                System.err.println("Incomplete '-e' parameter.");
63e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                System.exit(ARG_FAIL_INCOMPLETE_E);
64e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                break;
65e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            case ARG_FAIL_UNSUPPORTED:
666ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu                System.err.println("Unsupported standalone parameter.");
67e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                System.exit(ARG_FAIL_UNSUPPORTED);
68e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                break;
69e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            default:
70e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                break;
71e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
72e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (mTestClasses.isEmpty()) {
73e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            System.err.println("Please specify at least one test class to run.");
74e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            System.exit(ARG_FAIL_NO_CLASS);
75e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
76e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        getRunner().run(mTestClasses, mParams, mDebug);
77e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
78e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
79e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private int parseArgs(String[] args) {
80e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // we are parsing for these parameters:
81e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // -e <key> <value>
82e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // key-value pairs
83e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // special ones are:
84e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // key is "class", parameter is passed onto JUnit as class name to run
85e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // key is "debug", parameter will determine whether to wait for debugger
86e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // to attach
87e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // -c <class name>
88e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // equivalent to -e class <class name>, i.e. passed onto JUnit
89e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        for (int i = 0; i < args.length; i++) {
90e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if (args[i].equals("-e")) {
91e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                if (i + 2 < args.length) {
92e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    String key = args[++i];
93e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    String value = args[++i];
94e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    if (CLASS_PARAM.equals(key)) {
95e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                        addTestClasses(value);
96e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    } else if (DEBUG_PARAM.equals(key)) {
97e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                        mDebug = "true".equals(value) || "1".equals(value);
98e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    } else if (RUNNER_PARAM.equals(key)) {
99e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                        mRunner = value;
100e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    } else {
101e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                        mParams.putString(key, value);
102e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    }
103e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                } else {
104e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    return ARG_FAIL_INCOMPLETE_E;
105e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                }
106e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            } else if (args[i].equals("-c")) {
107e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                if (i + 1 < args.length) {
108e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    addTestClasses(args[++i]);
109e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                } else {
110e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    return ARG_FAIL_INCOMPLETE_C;
111e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                }
112e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            } else {
113e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                return ARG_FAIL_UNSUPPORTED;
114e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
115e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
116e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return ARG_OK;
117e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
118e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
119e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    protected UiAutomatorTestRunner getRunner() {
120e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (mRunner == null) {
121e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return new UiAutomatorTestRunner();
122e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
123e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // use reflection to get the runner
124e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Object o = null;
125e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        try {
126e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            Class<?> clazz = Class.forName(mRunner);
127e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            o = clazz.newInstance();
128e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        } catch (ClassNotFoundException cnfe) {
129e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            System.err.println("Cannot find runner: " + mRunner);
130e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            System.exit(ARG_FAIL_RUNNER);
131e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        } catch (InstantiationException ie) {
132e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            System.err.println("Cannot instantiate runner: " + mRunner);
133e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            System.exit(ARG_FAIL_RUNNER);
134e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        } catch (IllegalAccessException iae) {
135e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            System.err.println("Constructor of runner " + mRunner + " is not accessibile");
136e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            System.exit(ARG_FAIL_RUNNER);
137e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
138e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        try {
139e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            UiAutomatorTestRunner runner = (UiAutomatorTestRunner)o;
140e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return runner;
141e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        } catch (ClassCastException cce) {
142e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            System.err.println("Specified runner is not subclass of "
143e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    + UiAutomatorTestRunner.class.getSimpleName());
144e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            System.exit(ARG_FAIL_RUNNER);
145e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
146e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // won't reach here
147e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return null;
148e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
149e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
150e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
151e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Add test classes from a potentially comma separated list
152e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param classes
153e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
154e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private void addTestClasses(String classes) {
155e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        String[] classArray = classes.split(CLASS_SEPARATOR);
156e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        for (String clazz : classArray) {
157e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            mTestClasses.add(clazz);
158e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
159e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
1606ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu
1616ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu    @Override
1626ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu    public String detailedOptions() {
1636ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu        return "    runtest <class spec> [options]\n"
164517824a1704b10944f2fb07e5ea53e85e8b44aa7Guang Zhu            + "    <class spec>: <JARS> < -c <CLASSES> | -e class <CLASSES> >\n"
165517824a1704b10944f2fb07e5ea53e85e8b44aa7Guang Zhu            + "      <JARS>: a list of jar files containing test classes and dependencies. If\n"
1666ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu            + "        the path is relative, it's assumed to be under /data/local/tmp. Use\n"
1676ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu            + "        absolute path if the file is elsewhere. Multiple files can be\n"
1686ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu            + "        specified, separated by space.\n"
1696ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu            + "      <CLASSES>: a list of test class names to run, separated by comma. To\n"
1706ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu            + "        a single method, use TestClass#testMethod format. The -e or -c option\n"
1716ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu            + "        may be repeated.\n"
1726ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu            + "    options:\n"
1736ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu            + "      --nohup: trap SIG_HUP, so test won't terminate even if parent process\n"
1746ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu            + "               is terminated, e.g. USB is disconnected.\n"
1756ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu            + "      -e debug [true|false]: wait for debugger to connect before starting.\n"
1766ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu            + "      -e runner [CLASS]: use specified test runner class instead. If\n"
1776ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu            + "        unspecified, framework default runner will be used.\n"
1786ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu            + "      -e <NAME> <VALUE>: other name-value pairs to be passed to test classes.\n"
1796ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu            + "        May be repeated.\n";
1806ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu    }
1816ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu
1826ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu    @Override
1836ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu    public String shortHelp() {
1846ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu        return "executes UI automation tests";
1856ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu    }
1866ac3f28e72bc05dce35a3f8d2b6b095a3f4056daGuang Zhu
187e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu}