summaryrefslogtreecommitdiffstats
path: root/jack-tests/src/com/android/jack/test/runner/DeviceRunner.java
diff options
context:
space:
mode:
Diffstat (limited to 'jack-tests/src/com/android/jack/test/runner/DeviceRunner.java')
-rw-r--r--jack-tests/src/com/android/jack/test/runner/DeviceRunner.java336
1 files changed, 336 insertions, 0 deletions
diff --git a/jack-tests/src/com/android/jack/test/runner/DeviceRunner.java b/jack-tests/src/com/android/jack/test/runner/DeviceRunner.java
new file mode 100644
index 0000000..dbb9203
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/runner/DeviceRunner.java
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2014 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 com.android.jack.test.runner;
+
+import com.google.common.base.Joiner;
+
+import com.android.ddmlib.AdbCommandRejectedException;
+import com.android.ddmlib.AndroidDebugBridge;
+import com.android.ddmlib.IDevice;
+import com.android.ddmlib.IShellOutputReceiver;
+import com.android.ddmlib.ShellCommandUnresponsiveException;
+import com.android.ddmlib.SyncException;
+import com.android.ddmlib.TimeoutException;
+import com.android.jack.test.toolchain.AbstractTestTools;
+import com.android.jack.test.toolchain.TestConfigurationException;
+import com.android.jack.test.util.ExecFileException;
+import com.android.jack.test.util.ExecuteFile;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+/**
+ * This runner is used to execute tests on a device.
+ */
+public abstract class DeviceRunner extends AbstractRuntimeRunner {
+
+ @Nonnull
+ public static final File ROOT_DIR = new File("/system");
+ @Nonnull
+ public static final File ANDROID_DATA_DIR = new File("/data");
+
+ private static final long ADB_CONNECTION_TIMEOUT = 5000;
+ private static final long ADB_WAIT_STEP = ADB_CONNECTION_TIMEOUT / 10;
+
+ @Nonnull
+ private MyShellOuputReceiver shellOutput = new MyShellOuputReceiver();
+
+ private class MyShellOuputReceiver implements IShellOutputReceiver {
+
+ @Override
+ public void addOutput(@Nonnull byte[] data, int offset, int length) {
+ outRedirectStream.println(new String(Arrays.copyOfRange(data, offset, offset + length)));
+ }
+
+ @Override
+ public void flush() {
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return false;
+ }
+ }
+
+ public DeviceRunner() {
+ super(ROOT_DIR);
+ try {
+ AndroidDebugBridge.init(/* clientSupport */ false);
+ } catch (IllegalStateException ex) {
+ // ADB was already initialized, we're fine, so just ignore.
+ }
+ }
+
+ private class ShellOutputToStringReceiver implements IShellOutputReceiver {
+
+ @Nonnull
+ StringBuffer out = new StringBuffer();
+
+ @Override
+ public void addOutput(@Nonnull byte[] data, int offset, int length) {
+ out.append(new String(Arrays.copyOfRange(data, offset, offset + length)));
+ }
+
+ @Override
+ public void flush() {
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return false;
+ }
+
+ @Nonnull
+ public String getOutput() {
+ return out.toString();
+ }
+ }
+
+ protected int runOnDevice(@Nonnull String[] options, @Nonnull String[] mainClasses,
+ @Nonnull File... classpathFiles)
+ throws RuntimeRunnerException {
+
+ // Assumes adb is in PATH
+ AndroidDebugBridge adb = AndroidDebugBridge.createBridge("adb", false);
+
+ long start = System.currentTimeMillis();
+
+ if (isVerbose) {
+ outRedirectStream.println("Initializing adb...");
+ }
+
+ while (!isAdbInitialized(adb)) {
+ long timeLeft = start + ADB_CONNECTION_TIMEOUT - System.currentTimeMillis();
+ if (timeLeft <= 0) {
+ break;
+ }
+ try {
+ Thread.sleep(ADB_WAIT_STEP);
+ } catch (InterruptedException e) {
+ throw new RuntimeRunnerException(e);
+ }
+ }
+ if (!isAdbInitialized(adb)) {
+ throw new RuntimeRunnerException("adb is not initialized");
+ }
+
+ if (isVerbose) {
+ outRedirectStream.println("Done");
+ }
+
+ IDevice[] connectedDevices = adb.getDevices();
+
+ if (connectedDevices.length == 0) {
+ throw new RuntimeRunnerException("No device found");
+ }
+
+ int exitStatus = -1;
+ for (IDevice device : connectedDevices) {
+
+ checkDeviceRuntime(device);
+
+ if (isVerbose) {
+ outRedirectStream.println("Running on device: " + device.getName());
+ }
+
+ ensureAdbRoot(device);
+
+ File testsRootDir = new File(device.getMountPoint(IDevice.MNT_DATA) + "/jack-tests");
+ File[] desFilePaths = new File[classpathFiles.length];
+ try {
+ if (isVerbose) {
+ outRedirectStream.println("adb shell -s " + device.getSerialNumber() + " mkdir "
+ + testsRootDir.getAbsolutePath());
+ }
+ device.executeShellCommand("mkdir " + testsRootDir.getAbsolutePath(), shellOutput);
+
+ if (isVerbose) {
+ outRedirectStream.println("adb -s " + device.getSerialNumber() + " push "
+ + System.getProperty("user.dir") + File.separator + "test-exit-status.sh "
+ + testsRootDir.getAbsolutePath() + "/test-exit-status.sh");
+ }
+ device.pushFile(System.getProperty("user.dir") + File.separator + "test-exit-status.sh",
+ testsRootDir.getAbsolutePath() + "/test-exit-status.sh");
+
+ if (isVerbose) {
+ outRedirectStream.println("adb -s " + device.getSerialNumber() + " shell chmod 777 "
+ + testsRootDir.getAbsolutePath() + "/test-exit-status.sh");
+ }
+ device.executeShellCommand(
+ "chmod 777 " + testsRootDir.getAbsolutePath() + "/test-exit-status.sh", shellOutput);
+
+ int i = 0;
+ for (File f : classpathFiles) {
+ desFilePaths[i] = new File(testsRootDir, "f" + i + "_" + f.getName());
+
+ if (isVerbose) {
+ outRedirectStream.println("adb -s " + device.getSerialNumber() + " push "
+ + f.getAbsolutePath() + " " + desFilePaths[i].getAbsolutePath());
+ }
+ device.pushFile(f.getAbsolutePath(), desFilePaths[i].getAbsolutePath());
+ i++;
+ }
+ } catch (TimeoutException e) {
+ throw new RuntimeRunnerException(e);
+ } catch (AdbCommandRejectedException e) {
+ throw new RuntimeRunnerException(e);
+ } catch (ShellCommandUnresponsiveException e) {
+ throw new RuntimeRunnerException(e);
+ } catch (IOException e) {
+ throw new RuntimeRunnerException(e);
+ } catch (SyncException e) {
+ throw new RuntimeRunnerException(e);
+ }
+
+ String args = Joiner.on(' ').join(buildCommandLine(options, mainClasses, desFilePaths));
+
+ try {
+ // Bug : exit code return by adb shell is wrong (always 0)
+ // https://code.google.com/p/android/issues/detail?id=3254
+ // Use go team hack to work this around
+ // https://code.google.com/p/go/source/browse/misc/arm/a
+
+ if (isVerbose) {
+ outRedirectStream.println("adb -s " + device.getSerialNumber() + " shell "
+ + testsRootDir.getAbsolutePath() + "/test-exit-status.sh " + args);
+ }
+ device.executeShellCommand(
+ testsRootDir.getAbsolutePath() + "/test-exit-status.sh " + args,
+ shellOutput);
+
+ File exitStatusFile = AbstractTestTools.createTempFile("exitStatus", "");
+ if (isVerbose) {
+ outRedirectStream.println("adb -s " + device.getSerialNumber() + " pull "
+ + testsRootDir.getAbsolutePath() + "/exitStatus " + exitStatusFile.getAbsolutePath());
+ }
+ device.pullFile(testsRootDir.getAbsolutePath() + "/exitStatus",
+ exitStatusFile.getAbsolutePath());
+
+ BufferedReader br = new BufferedReader(new FileReader(exitStatusFile));
+ try {
+ String readLine = br.readLine();
+ if (readLine == null) {
+ throw new RuntimeRunnerException("Exit status not found");
+ }
+ exitStatus = Integer.parseInt(readLine);
+ } finally {
+ br.close();
+ }
+
+ if (isVerbose) {
+ outRedirectStream.println("Exit status: " + exitStatus);
+ }
+
+ for (File pushedFile : desFilePaths) {
+ if (isVerbose) {
+ outRedirectStream.println(
+ "adb -s " + device.getSerialNumber() + "rm " + pushedFile.getAbsolutePath());
+ }
+ device.executeShellCommand("rm " + pushedFile.getAbsolutePath(), shellOutput);
+ }
+
+ if (exitStatus != 0) {
+ errRedirectStream.println("Execution failed on device '" + device.getName() + "'");
+ break;
+ }
+
+ } catch (TimeoutException e) {
+ throw new RuntimeRunnerException(e);
+ } catch (AdbCommandRejectedException e) {
+ throw new RuntimeRunnerException(e);
+ } catch (ShellCommandUnresponsiveException e) {
+ throw new RuntimeRunnerException(e);
+ } catch (IOException e) {
+ throw new RuntimeRunnerException(e);
+ } catch (SyncException e) {
+ throw new RuntimeRunnerException(e);
+ }
+ }
+
+ return exitStatus;
+ }
+
+ @Nonnull
+ protected abstract List<String> buildCommandLine(@Nonnull String[] options,
+ @Nonnull String[] mainClasses, @Nonnull File... classpathFiles);
+
+ private boolean isAdbInitialized(@Nonnull AndroidDebugBridge adb) {
+ return adb.isConnected() && adb.hasInitialDeviceList();
+ }
+
+ private void ensureAdbRoot(@Nonnull IDevice device) throws RuntimeRunnerException {
+ ShellOutputToStringReceiver outputToString = new ShellOutputToStringReceiver();
+ try {
+ device.executeShellCommand("id", outputToString);
+
+ if (!outputToString.getOutput().contains("uid=0(root)")) {
+ ExecuteFile ef;
+
+ ef = new ExecuteFile("adb -s " + device.getSerialNumber() + " root");
+ ef.setOut(System.out);
+ ef.setErr(System.err);
+ ef.setVerbose(isVerbose);
+ ef.run();
+
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e1) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ } catch (TimeoutException e1) {
+ throw new RuntimeRunnerException(e1);
+ } catch (AdbCommandRejectedException e1) {
+ throw new RuntimeRunnerException(e1);
+ } catch (ShellCommandUnresponsiveException e1) {
+ throw new RuntimeRunnerException(e1);
+ } catch (IOException e1) {
+ throw new RuntimeRunnerException(e1);
+ } catch (ExecFileException e) {
+ throw new RuntimeRunnerException("Error while executing 'adb root'", e);
+ }
+ }
+
+ @Nonnull
+ protected abstract String getRuntimeName();
+
+ private void checkDeviceRuntime(@Nonnull IDevice device) throws RuntimeRunnerException {
+ ShellOutputToStringReceiver outputToString = new ShellOutputToStringReceiver();
+ try {
+ device.executeShellCommand("dalvikvm -showversion", outputToString);
+ if (!outputToString.getOutput().contains(getRuntimeName())) {
+ throw new TestConfigurationException(
+ "The plugged device does not run the required runtime: '" + getRuntimeName() + "'");
+ }
+ } catch (TimeoutException e) {
+ throw new RuntimeRunnerException(e);
+ } catch (AdbCommandRejectedException e) {
+ throw new RuntimeRunnerException(e);
+ } catch (ShellCommandUnresponsiveException e) {
+ throw new RuntimeRunnerException(e);
+ } catch (IOException e) {
+ throw new RuntimeRunnerException(e);
+ }
+ }
+
+}