17850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com/*
27850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * Copyright (C) 2009 The Android Open Source Project
37850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com *
47850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * Licensed under the Apache License, Version 2.0 (the "License");
57850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * you may not use this file except in compliance with the License.
67850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * You may obtain a copy of the License at
77850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com *
87850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com *      http://www.apache.org/licenses/LICENSE-2.0
97850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com *
107850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * Unless required by applicable law or agreed to in writing, software
117850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * distributed under the License is distributed on an "AS IS" BASIS,
127850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * See the License for the specific language governing permissions and
147850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * limitations under the License.
157850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com */
167850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com
177850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.compackage vogar.target;
187850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com
191bdeb9dd8f1ab3f308c285729af007ae8970425bbdc@google.comimport java.io.File;
207850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.comimport java.io.IOException;
217850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.comimport java.io.InputStream;
227850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.comimport java.io.PrintStream;
235884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.comimport java.util.ArrayList;
24a7a8fe1efa0fb3299d68709d60701a045bcc72f3jsharpe@google.comimport java.util.Arrays;
253c513c52be35990697865d1ad171378565cae1f0jsharpe@google.comimport java.util.HashSet;
265884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.comimport java.util.Iterator;
27a7a8fe1efa0fb3299d68709d60701a045bcc72f3jsharpe@google.comimport java.util.List;
287850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.comimport java.util.Properties;
29a7a8fe1efa0fb3299d68709d60701a045bcc72f3jsharpe@google.comimport java.util.Set;
303cea2f55f3da60eb0c4bdd1616cbfa964ee2cd91jessewilson@google.comimport java.util.concurrent.atomic.AtomicReference;
31400bee347dd7464ecc17dc24c82f59c59645ff44jessewilson@google.comimport vogar.Result;
327850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.comimport vogar.TestProperties;
33c68971a68a4f7fe5a58ca0f59161bb44fcf2833djessewilson@google.comimport vogar.monitor.TargetMonitor;
34bd8bda78cc5740aeae8de67b52f1d86e208f4864jessewilson@google.comimport vogar.target.junit.JUnitRunner;
357850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com
367850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com/**
377850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * Runs an action, in process on the target.
387850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com */
398c03f5a6370cf6521384a268e12808e163ae4339jessewilson@google.compublic final class TestRunner {
407850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com
417850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com    protected final Properties properties;
427850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com
437850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com    protected final String qualifiedName;
4472c9585a2c7535a2a847b8c316d28d875582dc09jsharpe@google.com    protected final String qualifiedClassOrPackageName;
45dbd85addb4e44255c4296b82d3ca45ad33a56649jessewilson@google.com
46dbd85addb4e44255c4296b82d3ca45ad33a56649jessewilson@google.com    /** the monitor port if a monitor is expected, or null for no monitor */
47dbd85addb4e44255c4296b82d3ca45ad33a56649jessewilson@google.com    protected final Integer monitorPort;
483cea2f55f3da60eb0c4bdd1616cbfa964ee2cd91jessewilson@google.com
493cea2f55f3da60eb0c4bdd1616cbfa964ee2cd91jessewilson@google.com    /** use an atomic reference so the runner can null it out when it is encountered. */
503cea2f55f3da60eb0c4bdd1616cbfa964ee2cd91jessewilson@google.com    protected final AtomicReference<String> skipPastReference;
51638d42932a6aca6ddb0454674dc20b96e68faf32enh@google.com    protected final int timeoutSeconds;
52a7a8fe1efa0fb3299d68709d60701a045bcc72f3jsharpe@google.com    protected final List<Runner> runners;
531bdeb9dd8f1ab3f308c285729af007ae8970425bbdc@google.com    private final boolean profile;
541bdeb9dd8f1ab3f308c285729af007ae8970425bbdc@google.com    private final int profileDepth;
551bdeb9dd8f1ab3f308c285729af007ae8970425bbdc@google.com    private final int profileInterval;
561bdeb9dd8f1ab3f308c285729af007ae8970425bbdc@google.com    private final File profileFile;
571bdeb9dd8f1ab3f308c285729af007ae8970425bbdc@google.com    private final boolean profileThreadGroup;
585884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com    protected final String[] args;
59dbd85addb4e44255c4296b82d3ca45ad33a56649jessewilson@google.com    private boolean useSocketMonitor;
607850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com
61c68971a68a4f7fe5a58ca0f59161bb44fcf2833djessewilson@google.com    public TestRunner(List<String> argsList) {
627850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com        properties = loadProperties();
637850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com        qualifiedName = properties.getProperty(TestProperties.QUALIFIED_NAME);
6472c9585a2c7535a2a847b8c316d28d875582dc09jsharpe@google.com        qualifiedClassOrPackageName = properties.getProperty(TestProperties.TEST_CLASS_OR_PACKAGE);
65638d42932a6aca6ddb0454674dc20b96e68faf32enh@google.com        timeoutSeconds = Integer.parseInt(properties.getProperty(TestProperties.TIMEOUT));
665884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com
675884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com        int monitorPort = Integer.parseInt(properties.getProperty(TestProperties.MONITOR_PORT));
683cc430f91313dab5074cffa6508c0b47cd9f2b50jessewilson@google.com        String skipPast = null;
691bdeb9dd8f1ab3f308c285729af007ae8970425bbdc@google.com        boolean profile = Boolean.parseBoolean(properties.getProperty(TestProperties.PROFILE));
701bdeb9dd8f1ab3f308c285729af007ae8970425bbdc@google.com        int profileDepth = Integer.parseInt(properties.getProperty(TestProperties.PROFILE_DEPTH));
711bdeb9dd8f1ab3f308c285729af007ae8970425bbdc@google.com        int profileInterval
721bdeb9dd8f1ab3f308c285729af007ae8970425bbdc@google.com                = Integer.parseInt(properties.getProperty(TestProperties.PROFILE_INTERVAL));
731bdeb9dd8f1ab3f308c285729af007ae8970425bbdc@google.com        File profileFile = new File(properties.getProperty(TestProperties.PROFILE_FILE));
741bdeb9dd8f1ab3f308c285729af007ae8970425bbdc@google.com        boolean profileThreadGroup
751bdeb9dd8f1ab3f308c285729af007ae8970425bbdc@google.com                = Boolean.parseBoolean(properties.getProperty(TestProperties.PROFILE_THREAD_GROUP));
761bdeb9dd8f1ab3f308c285729af007ae8970425bbdc@google.com
77cef780b87ae30112756d40ea3c639a1660fd7c29Nicolas Geoffray        boolean testOnly = Boolean.parseBoolean(properties.getProperty(TestProperties.TEST_ONLY));
78cef780b87ae30112756d40ea3c639a1660fd7c29Nicolas Geoffray        if (testOnly) {
79cef780b87ae30112756d40ea3c639a1660fd7c29Nicolas Geoffray          runners = Arrays.asList((Runner)new JUnitRunner());
80cef780b87ae30112756d40ea3c639a1660fd7c29Nicolas Geoffray        } else {
81cef780b87ae30112756d40ea3c639a1660fd7c29Nicolas Geoffray          runners = Arrays.asList(new JUnitRunner(),
82cef780b87ae30112756d40ea3c639a1660fd7c29Nicolas Geoffray                                  new CaliperRunner(),
83cef780b87ae30112756d40ea3c639a1660fd7c29Nicolas Geoffray                                  new MainRunner());
84cef780b87ae30112756d40ea3c639a1660fd7c29Nicolas Geoffray        }
855884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com        for (Iterator<String> i = argsList.iterator(); i.hasNext(); ) {
863cc430f91313dab5074cffa6508c0b47cd9f2b50jessewilson@google.com            String arg = i.next();
873cc430f91313dab5074cffa6508c0b47cd9f2b50jessewilson@google.com            if (arg.equals("--monitorPort")) {
885884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com                i.remove();
895884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com                monitorPort = Integer.parseInt(i.next());
905884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com                i.remove();
915884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com            }
923cc430f91313dab5074cffa6508c0b47cd9f2b50jessewilson@google.com            if (arg.equals("--skipPast")) {
933cc430f91313dab5074cffa6508c0b47cd9f2b50jessewilson@google.com                i.remove();
943cc430f91313dab5074cffa6508c0b47cd9f2b50jessewilson@google.com                skipPast = i.next();
953cc430f91313dab5074cffa6508c0b47cd9f2b50jessewilson@google.com                i.remove();
963cc430f91313dab5074cffa6508c0b47cd9f2b50jessewilson@google.com            }
975884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com        }
985884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com
995884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com        this.monitorPort = monitorPort;
1003cea2f55f3da60eb0c4bdd1616cbfa964ee2cd91jessewilson@google.com        this.skipPastReference = new AtomicReference<String>(skipPast);
1011bdeb9dd8f1ab3f308c285729af007ae8970425bbdc@google.com        this.profile = profile;
1021bdeb9dd8f1ab3f308c285729af007ae8970425bbdc@google.com        this.profileDepth = profileDepth;
1031bdeb9dd8f1ab3f308c285729af007ae8970425bbdc@google.com        this.profileInterval = profileInterval;
1041bdeb9dd8f1ab3f308c285729af007ae8970425bbdc@google.com        this.profileFile = profileFile;
1051bdeb9dd8f1ab3f308c285729af007ae8970425bbdc@google.com        this.profileThreadGroup = profileThreadGroup;
1065884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com        this.args = argsList.toArray(new String[argsList.size()]);
1077850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com    }
1087850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com
1098c03f5a6370cf6521384a268e12808e163ae4339jessewilson@google.com    private Properties loadProperties() {
1107850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com        try {
111ee67ab77cdaa11c37318aa01c22b7b1cb88c914cjessewilson@google.com            InputStream in = getPropertiesStream();
112ee67ab77cdaa11c37318aa01c22b7b1cb88c914cjessewilson@google.com            Properties properties = new Properties();
113ee67ab77cdaa11c37318aa01c22b7b1cb88c914cjessewilson@google.com            properties.load(in);
114ee67ab77cdaa11c37318aa01c22b7b1cb88c914cjessewilson@google.com            in.close();
1157850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com            return properties;
1167850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com        } catch (IOException e) {
1177850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com            throw new RuntimeException(e);
1187850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com        }
1197850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com    }
1207850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com
121a7a8fe1efa0fb3299d68709d60701a045bcc72f3jsharpe@google.com    /**
122dbd85addb4e44255c4296b82d3ca45ad33a56649jessewilson@google.com     * Configure this test runner to await an incoming socket connection when
123dbd85addb4e44255c4296b82d3ca45ad33a56649jessewilson@google.com     * writing test results. Otherwise all communication happens over
124dbd85addb4e44255c4296b82d3ca45ad33a56649jessewilson@google.com     * System.out.
125dbd85addb4e44255c4296b82d3ca45ad33a56649jessewilson@google.com     */
126dbd85addb4e44255c4296b82d3ca45ad33a56649jessewilson@google.com    public void useSocketMonitor() {
127dbd85addb4e44255c4296b82d3ca45ad33a56649jessewilson@google.com        this.useSocketMonitor = true;
128dbd85addb4e44255c4296b82d3ca45ad33a56649jessewilson@google.com    }
129dbd85addb4e44255c4296b82d3ca45ad33a56649jessewilson@google.com
130dbd85addb4e44255c4296b82d3ca45ad33a56649jessewilson@google.com    /**
1318c03f5a6370cf6521384a268e12808e163ae4339jessewilson@google.com     * Attempt to load the test properties file from both the application and system classloader.
1328c03f5a6370cf6521384a268e12808e163ae4339jessewilson@google.com     * This is necessary because sometimes we run tests from the boot classpath.
1338c03f5a6370cf6521384a268e12808e163ae4339jessewilson@google.com     */
1348c03f5a6370cf6521384a268e12808e163ae4339jessewilson@google.com    private InputStream getPropertiesStream() throws IOException {
1358c03f5a6370cf6521384a268e12808e163ae4339jessewilson@google.com        for (Class<?> classToLoadFrom : new Class<?>[] { TestRunner.class, Object.class }) {
1368c03f5a6370cf6521384a268e12808e163ae4339jessewilson@google.com            InputStream propertiesStream = classToLoadFrom.getResourceAsStream(
1378c03f5a6370cf6521384a268e12808e163ae4339jessewilson@google.com                    "/" + TestProperties.FILE);
1388c03f5a6370cf6521384a268e12808e163ae4339jessewilson@google.com            if (propertiesStream != null) {
1398c03f5a6370cf6521384a268e12808e163ae4339jessewilson@google.com                return propertiesStream;
1408c03f5a6370cf6521384a268e12808e163ae4339jessewilson@google.com            }
1418c03f5a6370cf6521384a268e12808e163ae4339jessewilson@google.com        }
1428c03f5a6370cf6521384a268e12808e163ae4339jessewilson@google.com        throw new IOException(TestProperties.FILE + " missing!");
1438c03f5a6370cf6521384a268e12808e163ae4339jessewilson@google.com    }
1448c03f5a6370cf6521384a268e12808e163ae4339jessewilson@google.com
1458c03f5a6370cf6521384a268e12808e163ae4339jessewilson@google.com    /**
146a7a8fe1efa0fb3299d68709d60701a045bcc72f3jsharpe@google.com     * Returns the class to run the test with based on {@param klass}. For instance, a class
147a7a8fe1efa0fb3299d68709d60701a045bcc72f3jsharpe@google.com     * that extends junit.framework.TestCase should be run with JUnitSpec.
1481d4fef707a383dc57285e6608ca290be48811a85jsharpe@google.com     *
1491d4fef707a383dc57285e6608ca290be48811a85jsharpe@google.com     * Returns null if no such associated runner exists.
150a7a8fe1efa0fb3299d68709d60701a045bcc72f3jsharpe@google.com     */
151a7a8fe1efa0fb3299d68709d60701a045bcc72f3jsharpe@google.com    private Class<?> runnerClass(Class<?> klass) {
152a7a8fe1efa0fb3299d68709d60701a045bcc72f3jsharpe@google.com        for (Runner runner : runners) {
153a7a8fe1efa0fb3299d68709d60701a045bcc72f3jsharpe@google.com            if (runner.supports(klass)) {
154a7a8fe1efa0fb3299d68709d60701a045bcc72f3jsharpe@google.com                return runner.getClass();
1557850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com            }
1567850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com        }
157a7a8fe1efa0fb3299d68709d60701a045bcc72f3jsharpe@google.com
1581d4fef707a383dc57285e6608ca290be48811a85jsharpe@google.com        return null;
1597850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com    }
1607850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com
1611bdeb9dd8f1ab3f308c285729af007ae8970425bbdc@google.com    public void run() throws IOException {
162bd8bda78cc5740aeae8de67b52f1d86e208f4864jessewilson@google.com        final TargetMonitor monitor = useSocketMonitor
163dbd85addb4e44255c4296b82d3ca45ad33a56649jessewilson@google.com                ? TargetMonitor.await(monitorPort)
164dbd85addb4e44255c4296b82d3ca45ad33a56649jessewilson@google.com                : TargetMonitor.forPrintStream(System.out);
1658a9cc00a0d2c02483690de28eabe2b60b5804538jsharpe@google.com
166bd8bda78cc5740aeae8de67b52f1d86e208f4864jessewilson@google.com        PrintStream monitorPrintStream = new PrintStreamDecorator(System.out) {
1677850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com            @Override public void print(String str) {
168c1808e2e978c6f96aacee728d39e1fc6eaad766ejessewilson@google.com                monitor.output(str != null ? str : "null");
1697850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com            }
1707850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com        };
1717850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com        System.setOut(monitorPrintStream);
1727850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com        System.setErr(monitorPrintStream);
1737850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com
174bd8bda78cc5740aeae8de67b52f1d86e208f4864jessewilson@google.com        try {
175bd8bda78cc5740aeae8de67b52f1d86e208f4864jessewilson@google.com            run(monitor);
176bd8bda78cc5740aeae8de67b52f1d86e208f4864jessewilson@google.com        } catch (Throwable internalError) {
177bd8bda78cc5740aeae8de67b52f1d86e208f4864jessewilson@google.com            internalError.printStackTrace(monitorPrintStream);
178bd8bda78cc5740aeae8de67b52f1d86e208f4864jessewilson@google.com        } finally {
179bd8bda78cc5740aeae8de67b52f1d86e208f4864jessewilson@google.com            monitor.close();
180bd8bda78cc5740aeae8de67b52f1d86e208f4864jessewilson@google.com        }
181bd8bda78cc5740aeae8de67b52f1d86e208f4864jessewilson@google.com    }
182bd8bda78cc5740aeae8de67b52f1d86e208f4864jessewilson@google.com
183bd8bda78cc5740aeae8de67b52f1d86e208f4864jessewilson@google.com    public void run(final TargetMonitor monitor) {
18484f59696573fe05a50e77832ee26c5abde46fa9ejessewilson@google.com        TestEnvironment testEnvironment = new TestEnvironment();
18584f59696573fe05a50e77832ee26c5abde46fa9ejessewilson@google.com        testEnvironment.reset();
18684f59696573fe05a50e77832ee26c5abde46fa9ejessewilson@google.com
18772c9585a2c7535a2a847b8c316d28d875582dc09jsharpe@google.com        String classOrPackageName;
18872c9585a2c7535a2a847b8c316d28d875582dc09jsharpe@google.com        String qualification;
18972c9585a2c7535a2a847b8c316d28d875582dc09jsharpe@google.com
19072c9585a2c7535a2a847b8c316d28d875582dc09jsharpe@google.com        // Check whether the class or package is qualified and, if so, strip it off and pass it
19172c9585a2c7535a2a847b8c316d28d875582dc09jsharpe@google.com        // separately to the runners. For instance, may qualify a junit class by appending
19272c9585a2c7535a2a847b8c316d28d875582dc09jsharpe@google.com        // #method_name, where method_name is the name of a single test of the class to run.
19372c9585a2c7535a2a847b8c316d28d875582dc09jsharpe@google.com        int hash_position = qualifiedClassOrPackageName.indexOf("#");
19472c9585a2c7535a2a847b8c316d28d875582dc09jsharpe@google.com        if (hash_position != -1) {
19572c9585a2c7535a2a847b8c316d28d875582dc09jsharpe@google.com            classOrPackageName = qualifiedClassOrPackageName.substring(0, hash_position);
19672c9585a2c7535a2a847b8c316d28d875582dc09jsharpe@google.com            qualification = qualifiedClassOrPackageName.substring(hash_position + 1);
19772c9585a2c7535a2a847b8c316d28d875582dc09jsharpe@google.com        } else {
19872c9585a2c7535a2a847b8c316d28d875582dc09jsharpe@google.com            classOrPackageName = qualifiedClassOrPackageName;
19972c9585a2c7535a2a847b8c316d28d875582dc09jsharpe@google.com            qualification = null;
20072c9585a2c7535a2a847b8c316d28d875582dc09jsharpe@google.com        }
20172c9585a2c7535a2a847b8c316d28d875582dc09jsharpe@google.com
202a7a8fe1efa0fb3299d68709d60701a045bcc72f3jsharpe@google.com        Set<Class<?>> classes = new ClassFinder().find(classOrPackageName);
203a7a8fe1efa0fb3299d68709d60701a045bcc72f3jsharpe@google.com
2043c513c52be35990697865d1ad171378565cae1f0jsharpe@google.com        // if there is more than one class in the set, this must be a package. Since we're
2053c513c52be35990697865d1ad171378565cae1f0jsharpe@google.com        // running everything in the package already, remove any class called AllTests.
2063c513c52be35990697865d1ad171378565cae1f0jsharpe@google.com        if (classes.size() > 1) {
2073c513c52be35990697865d1ad171378565cae1f0jsharpe@google.com            Set<Class<?>> toRemove = new HashSet<Class<?>>();
2083c513c52be35990697865d1ad171378565cae1f0jsharpe@google.com            for (Class<?> klass : classes) {
2093c513c52be35990697865d1ad171378565cae1f0jsharpe@google.com                if (klass.getName().endsWith(".AllTests")) {
2103c513c52be35990697865d1ad171378565cae1f0jsharpe@google.com                    toRemove.add(klass);
2113c513c52be35990697865d1ad171378565cae1f0jsharpe@google.com                }
2123c513c52be35990697865d1ad171378565cae1f0jsharpe@google.com            }
2133c513c52be35990697865d1ad171378565cae1f0jsharpe@google.com            classes.removeAll(toRemove);
2143c513c52be35990697865d1ad171378565cae1f0jsharpe@google.com        }
2153c513c52be35990697865d1ad171378565cae1f0jsharpe@google.com
216edcbbe19c10582d415cf2206eee72e32472e6ed6jessewilson@google.com
217edcbbe19c10582d415cf2206eee72e32472e6ed6jessewilson@google.com        Profiler profiler = null;
218edcbbe19c10582d415cf2206eee72e32472e6ed6jessewilson@google.com        if (profile) {
219edcbbe19c10582d415cf2206eee72e32472e6ed6jessewilson@google.com            try {
220edcbbe19c10582d415cf2206eee72e32472e6ed6jessewilson@google.com                profiler = Profiler.getInstance();
221edcbbe19c10582d415cf2206eee72e32472e6ed6jessewilson@google.com            } catch (Exception e) {
222edcbbe19c10582d415cf2206eee72e32472e6ed6jessewilson@google.com                System.out.println("Profiling is disabled: " + e);
223edcbbe19c10582d415cf2206eee72e32472e6ed6jessewilson@google.com            }
224edcbbe19c10582d415cf2206eee72e32472e6ed6jessewilson@google.com        }
2251bdeb9dd8f1ab3f308c285729af007ae8970425bbdc@google.com        if (profiler != null) {
2266fa1029b5a60514952d707a51253f5929628a4ecbdc@google.com            profiler.setup(profileThreadGroup, profileDepth, profileInterval);
2271bdeb9dd8f1ab3f308c285729af007ae8970425bbdc@google.com        }
228a7a8fe1efa0fb3299d68709d60701a045bcc72f3jsharpe@google.com        for (Class<?> klass : classes) {
229a7a8fe1efa0fb3299d68709d60701a045bcc72f3jsharpe@google.com            Class<?> runnerClass = runnerClass(klass);
230a20d57c3defdf4faad67b052e29aaa8be099cae6jessewilson@google.com            if (runnerClass == null) {
231fcd851661001aec3c2c0db3a72f20c30c934ae44bjdodson@google.com                monitor.outcomeStarted(null, klass.getName(), qualifiedName);
232a20d57c3defdf4faad67b052e29aaa8be099cae6jessewilson@google.com                System.out.println("Skipping " + klass.getName()
233a20d57c3defdf4faad67b052e29aaa8be099cae6jessewilson@google.com                        + ": no associated runner class");
2341d4fef707a383dc57285e6608ca290be48811a85jsharpe@google.com                monitor.outcomeFinished(Result.UNSUPPORTED);
235a20d57c3defdf4faad67b052e29aaa8be099cae6jessewilson@google.com                continue;
236a20d57c3defdf4faad67b052e29aaa8be099cae6jessewilson@google.com            }
237a20d57c3defdf4faad67b052e29aaa8be099cae6jessewilson@google.com
238a20d57c3defdf4faad67b052e29aaa8be099cae6jessewilson@google.com            Runner runner;
239a20d57c3defdf4faad67b052e29aaa8be099cae6jessewilson@google.com            try {
240a20d57c3defdf4faad67b052e29aaa8be099cae6jessewilson@google.com                runner = (Runner) runnerClass.newInstance();
2413cea2f55f3da60eb0c4bdd1616cbfa964ee2cd91jessewilson@google.com                runner.init(monitor, qualifiedName, qualification, klass, skipPastReference,
2423cea2f55f3da60eb0c4bdd1616cbfa964ee2cd91jessewilson@google.com                        testEnvironment, timeoutSeconds, profile);
243a20d57c3defdf4faad67b052e29aaa8be099cae6jessewilson@google.com            } catch (Exception e) {
244a20d57c3defdf4faad67b052e29aaa8be099cae6jessewilson@google.com                monitor.outcomeStarted(null, qualifiedName, qualifiedName);
245a20d57c3defdf4faad67b052e29aaa8be099cae6jessewilson@google.com                e.printStackTrace();
246a20d57c3defdf4faad67b052e29aaa8be099cae6jessewilson@google.com                monitor.outcomeFinished(Result.ERROR);
247a20d57c3defdf4faad67b052e29aaa8be099cae6jessewilson@google.com                return;
248a20d57c3defdf4faad67b052e29aaa8be099cae6jessewilson@google.com            }
2493cea2f55f3da60eb0c4bdd1616cbfa964ee2cd91jessewilson@google.com            boolean completedNormally = runner.run(qualifiedName, profiler, args);
250a20d57c3defdf4faad67b052e29aaa8be099cae6jessewilson@google.com            if (!completedNormally) {
251a20d57c3defdf4faad67b052e29aaa8be099cae6jessewilson@google.com                return; // let the caller start another process
252a7a8fe1efa0fb3299d68709d60701a045bcc72f3jsharpe@google.com            }
2537850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com        }
2541bdeb9dd8f1ab3f308c285729af007ae8970425bbdc@google.com        if (profiler != null) {
2556fa1029b5a60514952d707a51253f5929628a4ecbdc@google.com            profiler.shutdown(profileFile);
2561bdeb9dd8f1ab3f308c285729af007ae8970425bbdc@google.com        }
257a20d57c3defdf4faad67b052e29aaa8be099cae6jessewilson@google.com
258a20d57c3defdf4faad67b052e29aaa8be099cae6jessewilson@google.com        monitor.completedNormally(true);
2597850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com    }
2607850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com
261693bab6f249797311a9c4bcd4c9d9c7cfd5ae8d3jessewilson@google.com    public static void main(String[] args) throws IOException {
2621bdeb9dd8f1ab3f308c285729af007ae8970425bbdc@google.com        new TestRunner(new ArrayList<String>(Arrays.asList(args))).run();
2631d793b53e7df1780e48e2819d204f51c7d4a59afjessewilson@google.com        System.exit(0);
2647850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com    }
265bd8bda78cc5740aeae8de67b52f1d86e208f4864jessewilson@google.com
2667850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com}
267