summaryrefslogtreecommitdiffstats
path: root/tools/runner/java/vogar/Driver.java
diff options
context:
space:
mode:
Diffstat (limited to 'tools/runner/java/vogar/Driver.java')
-rw-r--r--tools/runner/java/vogar/Driver.java265
1 files changed, 265 insertions, 0 deletions
diff --git a/tools/runner/java/vogar/Driver.java b/tools/runner/java/vogar/Driver.java
new file mode 100644
index 0000000..f646fe8
--- /dev/null
+++ b/tools/runner/java/vogar/Driver.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package vogar;
+
+import vogar.commands.Command;
+import vogar.commands.Mkdir;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Logger;
+
+/**
+ * Compiles, installs, runs and reports on actions.
+ */
+final class Driver implements HostMonitor.Handler {
+
+ private static final Logger logger = Logger.getLogger(Driver.class.getName());
+
+ private final File localTemp;
+ private final ExpectationStore expectationStore;
+ private final List<CodeFinder> codeFinders;
+ private final Mode mode;
+ private final XmlReportPrinter reportPrinter;
+ private final Console console;
+ private final int monitorPort;
+ private final HostMonitor monitor;
+ private final long timeoutSeconds;
+ private int successes = 0;
+ private int failures = 0;
+
+ private Timer actionTimeoutTimer = new Timer("action timeout", true);
+
+ private final Map<String, Action> actions = Collections.synchronizedMap(
+ new LinkedHashMap<String, Action>());
+ private final Map<String, Outcome> outcomes = Collections.synchronizedMap(
+ new LinkedHashMap<String, Outcome>());
+
+ /**
+ * The number of tests that weren't run because they aren't supported by
+ * this runner.
+ */
+ private int unsupportedActions = 0;
+
+ public Driver(File localTemp, Mode mode, ExpectationStore expectationStore,
+ List<CodeFinder> codeFinders, XmlReportPrinter reportPrinter,
+ Console console, HostMonitor monitor, int monitorPort, long timeoutSeconds) {
+ this.localTemp = localTemp;
+ this.expectationStore = expectationStore;
+ this.mode = mode;
+ this.console = console;
+ this.codeFinders = codeFinders;
+ this.reportPrinter = reportPrinter;
+ this.monitor = monitor;
+ this.monitorPort = monitorPort;
+ this.timeoutSeconds = timeoutSeconds;
+ }
+
+ /**
+ * Builds and executes the actions in the given files.
+ */
+ public void buildAndRunAllActions(Collection<File> files) {
+ if (!actions.isEmpty()) {
+ throw new IllegalStateException("Drivers are not reusable");
+ }
+
+ new Mkdir().mkdirs(localTemp);
+ for (File file : files) {
+ Set<Action> actionsForFile = Collections.emptySet();
+
+ for (CodeFinder codeFinder : codeFinders) {
+ actionsForFile = codeFinder.findActions(file);
+
+ // break as soon as we find any match. We don't need multiple
+ // matches for the same file, since that would run it twice.
+ if (!actionsForFile.isEmpty()) {
+ break;
+ }
+ }
+
+ for (Action action : actionsForFile) {
+ actions.put(action.getName(), action);
+ }
+ }
+
+ // compute TestRunner java and classpath to pass to mode.prepare
+ Set<File> runnerJava = new HashSet<File>();
+ Classpath runnerClasspath = new Classpath();
+ for (final Action action : actions.values()) {
+ runnerJava.add(action.getRunnerJava());
+ runnerClasspath.addAll(action.getRunnerClasspath());
+ }
+
+ // mode.prepare before mode.buildAndInstall to ensure the runner is
+ // built. packaging of activity APK files needs the runner along with
+ // the action-specific files.
+ mode.prepare(runnerJava, runnerClasspath);
+
+ logger.info("Actions: " + actions.size());
+
+ // build and install actions in a background thread. Using lots of
+ // threads helps for packages that contain many unsupported actions
+ final BlockingQueue<Action> readyToRun = new ArrayBlockingQueue<Action>(4);
+
+ ExecutorService builders = Threads.threadPerCpuExecutor();
+ int t = 0;
+
+ for (final Action action : actions.values()) {
+ final String name = action.getName();
+ final int runIndex = t++;
+ builders.submit(new Runnable() {
+ public void run() {
+ try {
+ logger.fine("installing action " + runIndex + "; "
+ + readyToRun.size() + " are runnable");
+
+ if (expectationStore.get(name).getResult() == Result.UNSUPPORTED) {
+ outcomes.put(name, new Outcome(name, Result.UNSUPPORTED,
+ "Unsupported according to expectations file"));
+
+ } else {
+ Outcome outcome = mode.buildAndInstall(action);
+ if (outcome != null) {
+ outcomes.put(name, outcome);
+ }
+ }
+
+ readyToRun.put(action);
+ } catch (InterruptedException e) {
+ outcomes.put(name, new Outcome(name, Result.ERROR, e));
+ }
+ }
+ });
+ }
+ builders.shutdown();
+
+ for (int i = 0; i < actions.size(); i++) {
+ logger.fine("executing action " + i + "; "
+ + readyToRun.size() + " are ready to run");
+
+ // if it takes 5 minutes for build and install, something is broken
+ Action action;
+ try {
+ action = readyToRun.poll(5 * 60, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Unexpected interruption waiting for build and install", e);
+ }
+
+ if (action == null) {
+ throw new IllegalStateException("Expected " + actions.size()
+ + " actions but found only " + i);
+ }
+
+ execute(action);
+ mode.cleanup(action);
+ }
+
+ if (reportPrinter != null) {
+ logger.info("Printing XML Reports... ");
+ int numFiles = reportPrinter.generateReports(outcomes.values());
+ logger.info(numFiles + " XML files written.");
+ }
+
+ if (failures > 0 || unsupportedActions > 0) {
+ logger.info(String.format("Outcomes: %s. Passed: %d, Failed: %d, Skipped: %d",
+ (successes + failures), successes, failures, unsupportedActions));
+ } else {
+ logger.info(String.format("Outcomes: %s. All successful.",
+ (successes + failures)));
+ }
+ }
+
+ /**
+ * Executes a single action and then prints the result.
+ */
+ private void execute(final Action action) {
+ console.action(action.getName());
+
+ Outcome earlyFailure = outcomes.get(action.getName());
+ if (earlyFailure == null) {
+ final Command command = mode.createActionCommand(action);
+ Future<List<String>> consoleOut = command.executeLater();
+ final AtomicBoolean done = new AtomicBoolean();
+
+ actionTimeoutTimer.schedule(new TimerTask() {
+ @Override public void run() {
+ if (!done.get()) {
+ // TODO: set a "timout" bit somewhere so we know why this failed.
+ // currently we report ERROR for all timeouts.
+ logger.fine("killing " + action.getName() + " because it "
+ + "timed out after " + timeoutSeconds + " seconds");
+ }
+ command.destroy();
+ }
+ }, timeoutSeconds * 1000);
+
+ boolean success = monitor.monitor(monitorPort, this);
+ done.set(true);
+ if (success) {
+ return;
+ }
+
+ try {
+ earlyFailure = new Outcome(action.getName(), action.getName(),
+ Result.ERROR, consoleOut.get());
+ } catch (Exception e) {
+ earlyFailure = new Outcome(action.getName(), Result.ERROR, e);
+ }
+ }
+
+ if (earlyFailure.getResult() == Result.UNSUPPORTED) {
+ logger.fine("skipping " + action.getName());
+ unsupportedActions++;
+ } else {
+ for (String line : earlyFailure.getOutputLines()) {
+ console.streamOutput(line + "\n");
+ }
+ outcome(earlyFailure);
+ }
+ }
+
+ public void outcome(Outcome outcome) {
+ Expectation expectation = expectationStore.get(outcome.getName());
+ boolean ok = expectation.matches(outcome);
+ if (ok) {
+ successes++;
+ } else {
+ failures++;
+ }
+ console.outcome(outcome.getName());
+ console.printResult(outcome.getResult(), ok);
+ }
+
+ public void output(String outcomeName, String output) {
+ console.outcome(outcomeName);
+ console.streamOutput(output);
+ }
+}