Merge "Fix some of the logging for basic HelloWorld situation"
diff --git a/device_build_interfaces/com/android/tradefed/device/IDeviceRecovery.java b/device_build_interfaces/com/android/tradefed/device/IDeviceRecovery.java
index a5f096a..cef3097 100644
--- a/device_build_interfaces/com/android/tradefed/device/IDeviceRecovery.java
+++ b/device_build_interfaces/com/android/tradefed/device/IDeviceRecovery.java
@@ -53,6 +53,15 @@
             throws DeviceNotAvailableException;
 
     /**
+     * Attempt to recover the given unresponsive device in fastbootd mode.
+     *
+     * @param monitor the {@link IDeviceStateMonitor} to use.
+     * @throws DeviceNotAvailableException if device could not be recovered
+     */
+    public void recoverDeviceFastbootd(final IDeviceStateMonitor monitor)
+            throws DeviceNotAvailableException;
+
+    /**
      * Sets the path to the fastboot binary to be used.
      *
      * @param fastbootPath a {@link String} defining the path to the fastboot binary.
diff --git a/global_configuration/com/android/tradefed/host/HostOptions.java b/global_configuration/com/android/tradefed/host/HostOptions.java
index f650463..68aa51f 100644
--- a/global_configuration/com/android/tradefed/host/HostOptions.java
+++ b/global_configuration/com/android/tradefed/host/HostOptions.java
@@ -53,6 +53,11 @@
     )
     private File mFastbootTmpDir = null;
 
+    @Option(
+            name = "enable-fastbootd-mode",
+            description = "Feature flag to enable the support for fastbootd.")
+    private boolean mEnableFastbootdMode = false;
+
     @Option(name = "download-cache-dir", description = "the directory for caching downloaded "
             + "flashing files. Should be on the same filesystem as java.io.tmpdir.  Consider "
             + "changing the java.io.tmpdir property if you want to move downloads to a different "
@@ -111,6 +116,12 @@
 
     /** {@inheritDoc} */
     @Override
+    public boolean isFastbootdEnable() {
+        return mEnableFastbootdMode;
+    }
+
+    /** {@inheritDoc} */
+    @Override
     public File getDownloadCacheDir() {
         return mDownloadCacheDir;
     }
diff --git a/global_configuration/com/android/tradefed/host/IHostOptions.java b/global_configuration/com/android/tradefed/host/IHostOptions.java
index 86e7be2..32b8a04 100644
--- a/global_configuration/com/android/tradefed/host/IHostOptions.java
+++ b/global_configuration/com/android/tradefed/host/IHostOptions.java
@@ -47,6 +47,9 @@
     /** Returns the path that fastboot should use as temporary folder. */
     File getFastbootTmpDir();
 
+    /** Returns whether or not fastbootd mode support is enabled. */
+    boolean isFastbootdEnable();
+
     /** Returns the path used for storing downloaded artifacts. */
     File getDownloadCacheDir();
 
diff --git a/src/com/android/tradefed/cluster/ClusterCommandLauncher.java b/src/com/android/tradefed/cluster/ClusterCommandLauncher.java
index 66e0ab1..63e34a3 100644
--- a/src/com/android/tradefed/cluster/ClusterCommandLauncher.java
+++ b/src/com/android/tradefed/cluster/ClusterCommandLauncher.java
@@ -134,14 +134,28 @@
 
     @Override
     public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
-        // Pass jars under TF_PATH via classpath option (-cp)
+        // Get an expanded TF_PATH value.
         String tfPath = getEnvVar(TF_PATH, System.getProperty(TF_JAR_DIR));
         if (tfPath == null) {
             throw new RuntimeException("cannot find TF path!");
         }
+
+        // Construct a Java class path based on TF_PATH value.
+        // This expects TF_PATH to be a colon(:) separated list of paths where each path
+        // points to a specific jar file or folder.
+        // (example: path/to/tradefed.jar:path/to/tradefed/folder:...)
         final Set<String> jars = new LinkedHashSet<>();
         for (final String path : tfPath.split(":")) {
-            jars.add(new File(path, "*").getAbsolutePath());
+            final File jarFile = new File(path);
+            if (!jarFile.exists()) {
+                CLog.w("TF_PATH %s doesn't exist; ignoring", path);
+                continue;
+            }
+            if (jarFile.isFile()) {
+                jars.add(jarFile.getAbsolutePath());
+            } else {
+                jars.add(new File(path, "*").getAbsolutePath());
+            }
         }
 
         IRunUtil runUtil = getRunUtil();
diff --git a/src/com/android/tradefed/device/DeviceManager.java b/src/com/android/tradefed/device/DeviceManager.java
index 485af19..ef7c0e0 100644
--- a/src/com/android/tradefed/device/DeviceManager.java
+++ b/src/com/android/tradefed/device/DeviceManager.java
@@ -273,6 +273,7 @@
             // Populate the fastboot devices
             // TODO: remove when refactoring fastboot handling
             addFastbootDevices();
+            CLog.d("Using Fastboot from: '%s'", getFastbootPath());
         } else {
             CLog.w("Fastboot is not available.");
             mFastbootListeners = null;
@@ -1025,6 +1026,14 @@
             throw new DeviceNotAvailableException("aborted test session",
                     monitor.getSerialNumber());
         }
+
+        /** {@inheritDoc} */
+        @Override
+        public void recoverDeviceFastbootd(IDeviceStateMonitor monitor)
+                throws DeviceNotAvailableException {
+            throw new DeviceNotAvailableException(
+                    "aborted test session", monitor.getSerialNumber());
+        }
     }
 
     /**
diff --git a/src/com/android/tradefed/device/FastbootHelper.java b/src/com/android/tradefed/device/FastbootHelper.java
index 039de7d..2cc287d 100644
--- a/src/com/android/tradefed/device/FastbootHelper.java
+++ b/src/com/android/tradefed/device/FastbootHelper.java
@@ -59,8 +59,8 @@
     public boolean isFastbootAvailable() {
         // Run "fastboot help" to check the existence and the version of fastboot
         // (Old versions do not support "help" command).
-        CommandResult fastbootResult = mRunUtil.runTimedCmdSilently(5000, mFastbootPath, "help");
-        if (fastbootResult.getStatus() == CommandStatus.SUCCESS) {
+        CommandResult fastbootResult = mRunUtil.runTimedCmdSilently(15000, mFastbootPath, "help");
+        if (CommandStatus.SUCCESS.equals(fastbootResult.getStatus())) {
             return true;
         }
         if (fastbootResult.getStderr() != null &&
diff --git a/src/com/android/tradefed/device/NativeDevice.java b/src/com/android/tradefed/device/NativeDevice.java
index e730a83..4358fa1 100644
--- a/src/com/android/tradefed/device/NativeDevice.java
+++ b/src/com/android/tradefed/device/NativeDevice.java
@@ -2965,8 +2965,14 @@
             doAdbReboot(mode, null);
         }
 
-        if (!mStateMonitor.waitForDeviceBootloader(mOptions.getFastbootTimeout())) {
-            recoverDeviceFromBootloader();
+        if (RebootMode.REBOOT_INTO_FASTBOOTD.equals(mode) && getHostOptions().isFastbootdEnable()) {
+            if (!mStateMonitor.waitForDeviceFastbootd(mOptions.getFastbootTimeout())) {
+                recoverDeviceFromBootloader();
+            }
+        } else {
+            if (!mStateMonitor.waitForDeviceBootloader(mOptions.getFastbootTimeout())) {
+                recoverDeviceFromBootloader();
+            }
         }
     }
 
diff --git a/src/com/android/tradefed/device/WaitDeviceRecovery.java b/src/com/android/tradefed/device/WaitDeviceRecovery.java
index e163025..94e9591 100644
--- a/src/com/android/tradefed/device/WaitDeviceRecovery.java
+++ b/src/com/android/tradefed/device/WaitDeviceRecovery.java
@@ -40,6 +40,8 @@
     /** the time in ms to wait before beginning recovery attempts */
     protected static final long INITIAL_PAUSE_TIME = 5 * 1000;
 
+    private static final long WAIT_FOR_DEVICE_OFFLINE = 20 * 1000;
+
     /**
      * The number of attempts to check if device is in bootloader.
      * <p/>
@@ -210,7 +212,7 @@
         if (!mDisableUnresponsiveReboot) {
             Log.i(LOG_TAG, String.format(
                     "Device %s unresponsive. Rebooting...", monitor.getSerialNumber()));
-            rebootDevice(device);
+            rebootDevice(device, null);
             IDevice newdevice = monitor.waitForDeviceOnline(mOnlineWaitTime);
             if (newdevice == null) {
                 handleDeviceNotAvailable(monitor, false);
@@ -265,27 +267,60 @@
                 return;
             }
         }
-        handleDeviceBootloaderNotAvailable(monitor);
+        handleDeviceBootloaderOrFastbootNotAvailable(monitor, "bootloader");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void recoverDeviceFastbootd(IDeviceStateMonitor monitor)
+            throws DeviceNotAvailableException {
+        // device may have just gone offline
+        // wait a small amount to give device state a chance to settle
+        // TODO - see if there is better way to handle this
+        Log.i(
+                LOG_TAG,
+                String.format(
+                        "Pausing for %d for %s to recover",
+                        INITIAL_PAUSE_TIME, monitor.getSerialNumber()));
+        getRunUtil().sleep(INITIAL_PAUSE_TIME);
+
+        // poll and wait for device to return to valid state
+        long pollTime = mBootloaderWaitTime / BOOTLOADER_POLL_ATTEMPTS;
+        for (int i = 0; i < BOOTLOADER_POLL_ATTEMPTS; i++) {
+            if (monitor.waitForDeviceFastbootd(pollTime)) {
+                handleDeviceFastbootdUnresponsive(monitor);
+                // passed above check, abort
+                return;
+            } else if (monitor.getDeviceState() == TestDeviceState.ONLINE) {
+                handleDeviceOnlineExpectedFasbootd(monitor);
+                return;
+            }
+        }
+        handleDeviceBootloaderOrFastbootNotAvailable(monitor, "fastbootd");
     }
 
     /**
      * Handle condition where device is online, but should be in bootloader state.
-     * <p/>
-     * If this method
+     *
+     * <p>If this method
+     *
      * @param monitor
      * @throws DeviceNotAvailableException
      */
-    protected void handleDeviceOnlineExpectedBootloader(final IDeviceStateMonitor monitor)
+    private void handleDeviceOnlineExpectedBootloader(final IDeviceStateMonitor monitor)
             throws DeviceNotAvailableException {
-        Log.i(LOG_TAG, String.format("Found device %s online but expected fastboot.",
-            monitor.getSerialNumber()));
+        Log.i(
+                LOG_TAG,
+                String.format(
+                        "Found device %s online but expected bootloader.",
+                        monitor.getSerialNumber()));
         // call waitForDeviceOnline to get handle to IDevice
         IDevice device = monitor.waitForDeviceOnline(mOnlineWaitTime);
         if (device == null) {
-            handleDeviceBootloaderNotAvailable(monitor);
+            handleDeviceBootloaderOrFastbootNotAvailable(monitor, "bootloader");
             return;
         }
-        rebootDeviceIntoBootloader(device);
+        rebootDevice(device, "bootloader");
         if (!monitor.waitForDeviceBootloader(mBootloaderWaitTime)) {
             throw new DeviceNotAvailableException(String.format(
                     "Device %s not in bootloader after reboot", monitor.getSerialNumber()),
@@ -293,11 +328,74 @@
         }
     }
 
+    private void handleDeviceOnlineExpectedFasbootd(final IDeviceStateMonitor monitor)
+            throws DeviceNotAvailableException {
+        Log.i(
+                LOG_TAG,
+                String.format(
+                        "Found device %s online but expected fastbootd.",
+                        monitor.getSerialNumber()));
+        // call waitForDeviceOnline to get handle to IDevice
+        IDevice device = monitor.waitForDeviceOnline(mOnlineWaitTime);
+        if (device == null) {
+            handleDeviceBootloaderOrFastbootNotAvailable(monitor, "fastbootd");
+            return;
+        }
+        rebootDevice(device, "fastboot");
+        if (!monitor.waitForDeviceFastbootd(mBootloaderWaitTime)) {
+            throw new DeviceNotAvailableException(
+                    String.format(
+                            "Device %s not in fastbootd after reboot", monitor.getSerialNumber()),
+                    monitor.getSerialNumber());
+        }
+    }
+
+    private void handleDeviceFastbootdUnresponsive(IDeviceStateMonitor monitor)
+            throws DeviceNotAvailableException {
+        CLog.i(
+                "Found device %s in fastbootd but potentially unresponsive.",
+                monitor.getSerialNumber());
+        // TODO: retry reboot
+        getRunUtil()
+                .runTimedCmd(
+                        mFastbootWaitTime,
+                        mFastbootPath,
+                        "-s",
+                        monitor.getSerialNumber(),
+                        "reboot",
+                        "fastboot");
+        // wait for device to reboot
+        monitor.waitForDeviceNotAvailable(WAIT_FOR_DEVICE_OFFLINE);
+        if (!monitor.waitForDeviceFastbootd(mBootloaderWaitTime)) {
+            throw new DeviceNotAvailableException(
+                    String.format(
+                            "Device %s not in fastbootd after reboot", monitor.getSerialNumber()),
+                    monitor.getSerialNumber());
+        }
+        // running a meaningless command just to see whether the device is responsive.
+        CommandResult result =
+                getRunUtil()
+                        .runTimedCmd(
+                                mFastbootWaitTime,
+                                mFastbootPath,
+                                "-s",
+                                monitor.getSerialNumber(),
+                                "getvar",
+                                "product");
+        if (result.getStatus().equals(CommandStatus.TIMED_OUT)) {
+            throw new DeviceNotAvailableException(
+                    String.format(
+                            "Device %s is in fastbootd but unresponsive",
+                            monitor.getSerialNumber()),
+                    monitor.getSerialNumber());
+        }
+    }
+
     /**
      * @param monitor
      * @throws DeviceNotAvailableException
      */
-    protected void handleDeviceBootloaderUnresponsive(IDeviceStateMonitor monitor)
+    private void handleDeviceBootloaderUnresponsive(IDeviceStateMonitor monitor)
             throws DeviceNotAvailableException {
         CLog.i("Found device %s in fastboot but potentially unresponsive.",
                 monitor.getSerialNumber());
@@ -322,32 +420,14 @@
     }
 
     /**
-     * Reboot device into bootloader.
+     * Reboot device into given mode.
      *
      * @param device the {@link IDevice} to reboot.
+     * @param mode The mode into which to reboot the device. null being regular reboot.
      */
-    protected void rebootDeviceIntoBootloader(IDevice device) {
+    private void rebootDevice(IDevice device, String mode) {
         try {
-            device.reboot("bootloader");
-        } catch (IOException e) {
-            Log.w(LOG_TAG, String.format("failed to reboot %s: %s", device.getSerialNumber(),
-                    e.getMessage()));
-        } catch (TimeoutException e) {
-            Log.w(LOG_TAG, String.format("failed to reboot %s: timeout", device.getSerialNumber()));
-        } catch (AdbCommandRejectedException e) {
-            Log.w(LOG_TAG, String.format("failed to reboot %s: %s", device.getSerialNumber(),
-                    e.getMessage()));
-        }
-    }
-
-    /**
-     * Reboot device into bootloader.
-     *
-     * @param device the {@link IDevice} to reboot.
-     */
-    protected void rebootDevice(IDevice device) {
-        try {
-            device.reboot(null);
+            device.reboot(mode);
         } catch (IOException e) {
             Log.w(LOG_TAG, String.format("failed to reboot %s: %s", device.getSerialNumber(),
                     e.getMessage()));
@@ -365,10 +445,10 @@
      * @param monitor the {@link IDeviceStateMonitor}
      * @throws DeviceNotAvailableException
      */
-    protected void handleDeviceBootloaderNotAvailable(final IDeviceStateMonitor monitor)
-            throws DeviceNotAvailableException {
-        throw new DeviceNotAvailableException(String.format(
-                "Could not find device %s in bootloader", monitor.getSerialNumber()),
+    private void handleDeviceBootloaderOrFastbootNotAvailable(
+            final IDeviceStateMonitor monitor, String mode) throws DeviceNotAvailableException {
+        throw new DeviceNotAvailableException(
+                String.format("Could not find device %s in %s", monitor.getSerialNumber(), mode),
                 monitor.getSerialNumber());
     }
 
diff --git a/src/com/android/tradefed/device/recovery/UsbResetRunConfigRecovery.java b/src/com/android/tradefed/device/recovery/UsbResetRunConfigRecovery.java
index c683e27..51b3553 100644
--- a/src/com/android/tradefed/device/recovery/UsbResetRunConfigRecovery.java
+++ b/src/com/android/tradefed/device/recovery/UsbResetRunConfigRecovery.java
@@ -16,6 +16,7 @@
 package com.android.tradefed.device.recovery;
 
 import com.android.tradefed.config.OptionClass;
+import com.android.tradefed.device.DeviceAllocationState;
 import com.android.tradefed.device.IManagedTestDevice;
 
 import java.util.HashSet;
@@ -30,15 +31,24 @@
     @Override
     public boolean shouldSkip(IManagedTestDevice device) {
         boolean res = device.isStateBootloaderOrFastbootd();
-        if (!res) {
-            String serial = device.getSerialNumber();
-            // Avoid running the same device twice in row of recovery request to give a chance to
-            // others recovery to start too.
-            if (!mLastInvoked.add(serial)) {
-                mLastInvoked.remove(serial);
-                return true;
-            }
+        // Do not reset available devices
+        if (!res && DeviceAllocationState.Available.equals(device.getAllocationState())) {
+            res = true;
         }
-        return res;
+        if (!res) {
+            return checkRanBefore(device);
+        }
+        return false;
+    }
+
+    private boolean checkRanBefore(IManagedTestDevice device) {
+        String serial = device.getSerialNumber();
+        // Avoid running the same device twice in row of recovery request to give a chance to
+        // others recovery to start too.
+        if (!mLastInvoked.add(serial)) {
+            mLastInvoked.remove(serial);
+            return true;
+        }
+        return false;
     }
 }
diff --git a/src/com/android/tradefed/invoker/InvocationExecution.java b/src/com/android/tradefed/invoker/InvocationExecution.java
index f16b41b..b27cbb3 100644
--- a/src/com/android/tradefed/invoker/InvocationExecution.java
+++ b/src/com/android/tradefed/invoker/InvocationExecution.java
@@ -171,6 +171,13 @@
                 testInfo.getContext().addDeviceBuildInfo(currentDeviceName, errorBuild);
             }
             throw e;
+        } catch (RuntimeException re) {
+            if (currentDeviceName != null) {
+                IBuildInfo errorBuild = new BuildInfo();
+                updateBuild(errorBuild, config);
+                testInfo.getContext().addDeviceBuildInfo(currentDeviceName, errorBuild);
+            }
+            throw re;
         }
         setBinariesVersion(testInfo.getContext());
         return true;
diff --git a/src/com/android/tradefed/invoker/TestInvocation.java b/src/com/android/tradefed/invoker/TestInvocation.java
index 0efc7a4..f6d5b06 100644
--- a/src/com/android/tradefed/invoker/TestInvocation.java
+++ b/src/com/android/tradefed/invoker/TestInvocation.java
@@ -586,7 +586,7 @@
             // Set the exit code to error
             buildException = new BuildRetrievalError("No build found to test.");
             setExitCode(ExitCode.NO_BUILD, buildException);
-        } catch (BuildRetrievalError e) {
+        } catch (BuildRetrievalError | RuntimeException e) {
             buildException = e;
         }
         // Report an empty invocation, so this error is sent to listeners
diff --git a/test_framework/com/android/tradefed/testtype/NativeCodeCoverageListener.java b/test_framework/com/android/tradefed/testtype/NativeCodeCoverageListener.java
index 0f1e3ef..5420079 100644
--- a/test_framework/com/android/tradefed/testtype/NativeCodeCoverageListener.java
+++ b/test_framework/com/android/tradefed/testtype/NativeCodeCoverageListener.java
@@ -47,13 +47,13 @@
 
     private static final String NATIVE_COVERAGE_DEVICE_PATH = "/data/misc/trace";
     private static final String COVERAGE_TAR_PATH =
-            String.format("%s/coverage.tar.gz", NATIVE_COVERAGE_DEVICE_PATH);
+            String.format("%s/coverage.tar", NATIVE_COVERAGE_DEVICE_PATH);
 
     // Finds .gcda files in /data/misc/trace and compresses those files only. Stores the full
     // path of the file on the device.
     private static final String ZIP_COVERAGE_FILES_COMMAND =
             String.format(
-                    "find %s -name '*.gcda' | tar -cvzf %s -T -",
+                    "find %s -name '*.gcda' | tar -cvf %s -T -",
                     NATIVE_COVERAGE_DEVICE_PATH, COVERAGE_TAR_PATH);
 
     // Deletes .gcda files in /data/misc/trace.
@@ -113,7 +113,7 @@
 
     /** Pulls native coverage measurements from the device and logs them. */
     public void logCoverageMeasurements(String runName) {
-        File coverageTarGz = null;
+        File coverageTar = null;
         File coverageZip = null;
         try {
             // Enable abd root on the device, otherwise the following commands will fail.
@@ -126,11 +126,11 @@
 
             // Compress coverage measurements on the device before pulling.
             mDevice.executeShellCommand(ZIP_COVERAGE_FILES_COMMAND);
-            coverageTarGz = mDevice.pullFile(COVERAGE_TAR_PATH);
-            verifyNotNull(coverageTarGz, "Failed to pull the coverage file %s", COVERAGE_TAR_PATH);
+            coverageTar = mDevice.pullFile(COVERAGE_TAR_PATH);
+            verifyNotNull(coverageTar, "Failed to pull the coverage file %s", COVERAGE_TAR_PATH);
             mDevice.deleteFile(COVERAGE_TAR_PATH);
 
-            coverageZip = convertTarGzToZip(coverageTarGz);
+            coverageZip = convertTarToZip(coverageTar);
 
             try (FileInputStreamSource source = new FileInputStreamSource(coverageZip, true)) {
                 testLog(runName + "_native_runtime_coverage", LogDataType.NATIVE_COVERAGE, source);
@@ -141,22 +141,23 @@
         } catch (DeviceNotAvailableException | IOException e) {
             throw new RuntimeException(e);
         } finally {
-            FileUtil.deleteFile(coverageTarGz);
+            FileUtil.deleteFile(coverageTar);
             FileUtil.deleteFile(coverageZip);
         }
     }
 
     /**
-     * Converts a .tar.gz file to a .zip file.
+     * Converts a .tar file to a .zip file.
      *
-     * @param tarGz the .tar.gz file to convert
+     * @param tar the .tar file to convert
      * @return a .zip file with the same contents
      * @throws IOException
      */
-    private File convertTarGzToZip(File tarGz) throws IOException {
+    private File convertTarToZip(File tar) throws IOException {
         File untarDir = null;
         try {
-            untarDir = TarUtil.extractTarGzipToTemp(tarGz, "native_coverage");
+            untarDir = FileUtil.createTempDir("gcov_coverage");
+            TarUtil.unTar(tar, untarDir);
             return ZipUtil.createZip(Arrays.asList(untarDir.listFiles()), "native_coverage");
         } finally {
             FileUtil.recursiveDelete(untarDir);
diff --git a/test_framework/com/android/tradefed/testtype/binary/ExecutableBaseTest.java b/test_framework/com/android/tradefed/testtype/binary/ExecutableBaseTest.java
index 074a9bb..ed238fe 100644
--- a/test_framework/com/android/tradefed/testtype/binary/ExecutableBaseTest.java
+++ b/test_framework/com/android/tradefed/testtype/binary/ExecutableBaseTest.java
@@ -192,7 +192,7 @@
         }
         if (!mIncludeFilters.isEmpty()) {
             return !mIncludeFilters.contains(testName)
-                    && !mExcludeFilters.contains(description.toString());
+                    && !mIncludeFilters.contains(description.toString());
         }
         return false;
     }
diff --git a/tests/src/com/android/tradefed/cluster/ClusterCommandLauncherTest.java b/tests/src/com/android/tradefed/cluster/ClusterCommandLauncherTest.java
index 70250c7..c6dbe31 100644
--- a/tests/src/com/android/tradefed/cluster/ClusterCommandLauncherTest.java
+++ b/tests/src/com/android/tradefed/cluster/ClusterCommandLauncherTest.java
@@ -105,23 +105,31 @@
     }
 
     @Test
-    public void testRun() throws DeviceNotAvailableException, ConfigurationException {
+    public void testRun() throws DeviceNotAvailableException, ConfigurationException, IOException {
         mInvocationContext.addAllocatedDevice("foo", mMockTestDevice);
+        final File tfJar = new File(mRootDir, "foo.jar");
+        tfJar.createNewFile();
+
+        final String tfPathValue =
+                String.format(
+                        "${TF_WORK_DIR}/%s:${TF_WORK_DIR}/%s:${TF_WORK_DIR}/%s",
+                        tfJar.getName(), mTfPath.getName(), mTfLibDir.getName());
         final List<String> jars = new ArrayList<>();
+        jars.add(tfJar.getAbsolutePath());
         jars.add(String.format("%s/*", mTfPath));
         jars.add(String.format("%s/*", mTfLibDir));
         final String classPath = ArrayUtil.join(":", jars);
-        final String tfPathValue =
-                String.format(
-                        "${TF_WORK_DIR}/%s:${TF_WORK_DIR}/%s",
-                        mTfPath.getName(), mTfLibDir.getName());
         mOptionSetter.setOptionValue("cluster:jvm-option", "-Xmx1g");
         mOptionSetter.setOptionValue("cluster:env-var", "TF_PATH", tfPathValue);
         mOptionSetter.setOptionValue("cluster:java-property", "FOO", "${TF_WORK_DIR}/foo");
         mOptionSetter.setOptionValue("cluster:original-command-line", "original-command-line");
         mOptionSetter.setOptionValue("cluster:command-line", "command-line");
         final String expandedTfPathValue =
-                String.format("%s:%s", mTfPath.getAbsolutePath(), mTfLibDir.getAbsolutePath());
+                String.format(
+                        "%s:%s:%s",
+                        tfJar.getAbsolutePath(),
+                        mTfPath.getAbsolutePath(),
+                        mTfLibDir.getAbsolutePath());
         final CommandResult mockCommandResult = new CommandResult(CommandStatus.SUCCESS);
         when(mMockRunUtil.runTimedCmd(
                         Mockito.anyLong(),
diff --git a/tests/src/com/android/tradefed/device/NativeDeviceTest.java b/tests/src/com/android/tradefed/device/NativeDeviceTest.java
index e8992d5..67cfbbd 100644
--- a/tests/src/com/android/tradefed/device/NativeDeviceTest.java
+++ b/tests/src/com/android/tradefed/device/NativeDeviceTest.java
@@ -40,6 +40,8 @@
 import com.android.tradefed.config.ConfigurationException;
 import com.android.tradefed.config.OptionSetter;
 import com.android.tradefed.device.NativeDevice.RebootMode;
+import com.android.tradefed.host.HostOptions;
+import com.android.tradefed.host.IHostOptions;
 import com.android.tradefed.log.ITestLogger;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.result.ByteArrayInputStreamSource;
@@ -93,6 +95,7 @@
     private IRunUtil mMockRunUtil;
     private IWifiHelper mMockWifi;
     private IDeviceMonitor mMockDvcMonitor;
+    private IHostOptions mHostOptions;
 
     /**
      * A {@link TestDevice} that is suitable for running tests against
@@ -114,10 +117,16 @@
         protected IRunUtil getRunUtil() {
             return mMockRunUtil;
         }
+
+        @Override
+        IHostOptions getHostOptions() {
+            return mHostOptions;
+        }
     }
 
     @Before
     public void setUp() throws Exception {
+        mHostOptions = new HostOptions();
         mMockIDevice = EasyMock.createMock(IDevice.class);
         EasyMock.expect(mMockIDevice.getSerialNumber()).andReturn(MOCK_DEVICE_SERIAL).anyTimes();
         mMockRecovery = EasyMock.createMock(IDeviceRecovery.class);
@@ -1658,7 +1667,7 @@
     @Test
     public void testRebootIntoBootloader() throws Exception {
         NativeDevice testDevice =
-                new NativeDevice(mMockIDevice, mMockStateMonitor, mMockDvcMonitor) {
+                new TestableAndroidNativeDevice() {
                     @Override
                     public TestDeviceState getDeviceState() {
                         return TestDeviceState.ONLINE;
@@ -1708,7 +1717,7 @@
     @Test
     public void testRebootIntoFastbootd() throws Exception {
         NativeDevice testDevice =
-                new NativeDevice(mMockIDevice, mMockStateMonitor, mMockDvcMonitor) {
+                new TestableAndroidNativeDevice() {
                     @Override
                     public TestDeviceState getDeviceState() {
                         return TestDeviceState.ONLINE;
diff --git a/tests/src/com/android/tradefed/device/TestDeviceTest.java b/tests/src/com/android/tradefed/device/TestDeviceTest.java
index 3590005..7c9e610 100644
--- a/tests/src/com/android/tradefed/device/TestDeviceTest.java
+++ b/tests/src/com/android/tradefed/device/TestDeviceTest.java
@@ -454,26 +454,34 @@
                 return true;
             }
         };
-        testDevice.setRecovery(new IDeviceRecovery() {
+        testDevice.setRecovery(
+                new IDeviceRecovery() {
 
-            @Override
-            public void recoverDeviceRecovery(IDeviceStateMonitor monitor)
-                    throws DeviceNotAvailableException {
-                throw new DeviceNotAvailableException();
-            }
+                    @Override
+                    public void recoverDeviceRecovery(IDeviceStateMonitor monitor)
+                            throws DeviceNotAvailableException {
+                        throw new DeviceNotAvailableException();
+                    }
 
-            @Override
-            public void recoverDeviceBootloader(IDeviceStateMonitor monitor)
-                    throws DeviceNotAvailableException {
-                throw new DeviceNotAvailableException();
-            }
+                    @Override
+                    public void recoverDeviceBootloader(IDeviceStateMonitor monitor)
+                            throws DeviceNotAvailableException {
+                        throw new DeviceNotAvailableException();
+                    }
 
-            @Override
-            public void recoverDevice(IDeviceStateMonitor monitor, boolean recoverUntilOnline)
-                    throws DeviceNotAvailableException {
-                throw new DeviceUnresponsiveException();
-            }
-        });
+                    @Override
+                    public void recoverDevice(
+                            IDeviceStateMonitor monitor, boolean recoverUntilOnline)
+                            throws DeviceNotAvailableException {
+                        throw new DeviceUnresponsiveException();
+                    }
+
+                    @Override
+                    public void recoverDeviceFastbootd(IDeviceStateMonitor monitor)
+                            throws DeviceNotAvailableException {
+                        throw new DeviceUnresponsiveException();
+                    }
+                });
         testDevice.setRecoveryMode(RecoveryMode.AVAILABLE);
         mMockIDevice.executeShellCommand((String) EasyMock.anyObject(),
                 (CollectingOutputReceiver)EasyMock.anyObject(), EasyMock.anyLong(),
diff --git a/tests/src/com/android/tradefed/device/WaitDeviceRecoveryTest.java b/tests/src/com/android/tradefed/device/WaitDeviceRecoveryTest.java
index c4e75ac..c68a08b 100644
--- a/tests/src/com/android/tradefed/device/WaitDeviceRecoveryTest.java
+++ b/tests/src/com/android/tradefed/device/WaitDeviceRecoveryTest.java
@@ -15,37 +15,34 @@
  */
 package com.android.tradefed.device;
 
-import com.google.common.util.concurrent.SettableFuture;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
 
 import com.android.ddmlib.IDevice;
-import com.android.ddmlib.TimeoutException;
 import com.android.tradefed.config.OptionSetter;
 import com.android.tradefed.util.CommandResult;
 import com.android.tradefed.util.CommandStatus;
 import com.android.tradefed.util.IRunUtil;
 
-import junit.framework.TestCase;
+import com.google.common.util.concurrent.SettableFuture;
 
 import org.easymock.EasyMock;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 
-import java.io.IOException;
-
-/**
- * Unit tests for {@link WaitDeviceRecovery}.
- */
-public class WaitDeviceRecoveryTest extends TestCase {
+/** Unit tests for {@link WaitDeviceRecovery}. */
+@RunWith(JUnit4.class)
+public class WaitDeviceRecoveryTest {
 
     private IRunUtil mMockRunUtil;
     private WaitDeviceRecovery mRecovery;
     private IDeviceStateMonitor mMockMonitor;
     private IDevice mMockDevice;
 
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
+    @Before
+    public void setUp() throws Exception {
         mMockRunUtil = EasyMock.createMock(IRunUtil.class);
         mRecovery = new WaitDeviceRecovery() {
             @Override
@@ -59,9 +56,10 @@
     }
 
     /**
-     * Test {@link WaitDeviceRecovery#recoverDevice(IDeviceStateMonitor, boolean)}
-     * when devices comes back online on its own accord.
+     * Test {@link WaitDeviceRecovery#recoverDevice(IDeviceStateMonitor, boolean)} when devices
+     * comes back online on its own accord.
      */
+    @Test
     public void testRecoverDevice_success() throws DeviceNotAvailableException {
         // expect initial sleep
         mMockRunUtil.sleep(EasyMock.anyLong());
@@ -81,7 +79,8 @@
      * Test {@link WaitDeviceRecovery#recoverDevice(IDeviceStateMonitor, boolean)} when device is
      * not available.
      */
-    public void testRecoverDevice_unavailable()  {
+    @Test
+    public void testRecoverDevice_unavailable() {
         // expect initial sleep
         mMockRunUtil.sleep(EasyMock.anyLong());
         mMockMonitor.waitForDeviceBootloaderStateUpdate();
@@ -101,6 +100,7 @@
      * Test {@link WaitDeviceRecovery#recoverDevice(IDeviceStateMonitor, boolean)} when device is
      * not responsive.
      */
+    @Test
     public void testRecoverDevice_unresponsive() throws Exception {
         // expect initial sleep
         mMockRunUtil.sleep(EasyMock.anyLong());
@@ -123,9 +123,10 @@
     }
 
     /**
-     * Test {@link WaitDeviceRecovery#recoverDevice(IDeviceStateMonitor, boolean)} when device is
-     * in fastboot.
+     * Test {@link WaitDeviceRecovery#recoverDevice(IDeviceStateMonitor, boolean)} when device is in
+     * fastboot.
      */
+    @Test
     public void testRecoverDevice_fastboot() throws DeviceNotAvailableException {
         // expect initial sleep
         mMockRunUtil.sleep(EasyMock.anyLong());
@@ -153,6 +154,7 @@
      * Test {@link WaitDeviceRecovery#recoverDeviceBootloader(IDeviceStateMonitor)} when device is
      * already in bootloader
      */
+    @Test
     public void testRecoverDeviceBootloader_fastboot() throws DeviceNotAvailableException {
         mMockRunUtil.sleep(EasyMock.anyLong());
         // expect reboot
@@ -176,6 +178,7 @@
      * Test {@link WaitDeviceRecovery#recoverDeviceBootloader(IDeviceStateMonitor)} when device is
      * unavailable but comes back to bootloader on its own
      */
+    @Test
     public void testRecoverDeviceBootloader_unavailable() throws DeviceNotAvailableException {
         mMockRunUtil.sleep(EasyMock.anyLong());
         EasyMock.expect(mMockMonitor.waitForDeviceBootloader(EasyMock.anyLong())).andReturn(
@@ -202,6 +205,7 @@
      * Test {@link WaitDeviceRecovery#recoverDeviceBootloader(IDeviceStateMonitor)} when device is
      * online when bootloader is expected
      */
+    @Test
     public void testRecoverDeviceBootloader_online() throws Exception {
         mMockRunUtil.sleep(EasyMock.anyLong());
         EasyMock.expect(mMockMonitor.waitForDeviceBootloader(EasyMock.anyLong())).andReturn(
@@ -221,6 +225,7 @@
      * Test {@link WaitDeviceRecovery#recoverDeviceBootloader(IDeviceStateMonitor)} when device is
      * initially unavailable, then comes online when bootloader is expected
      */
+    @Test
     public void testRecoverDeviceBootloader_unavailable_online() throws Exception {
         mMockRunUtil.sleep(EasyMock.anyLong());
         EasyMock.expect(mMockMonitor.waitForDeviceBootloader(EasyMock.anyLong())).andReturn(
@@ -243,6 +248,7 @@
      * Test {@link WaitDeviceRecovery#recoverDeviceBootloader(IDeviceStateMonitor)} when device is
      * unavailable
      */
+    @Test
     public void testRecoverDeviceBootloader_unavailable_failure() throws Exception {
         mMockRunUtil.sleep(EasyMock.anyLong());
         EasyMock.expect(mMockMonitor.waitForDeviceBootloader(EasyMock.anyLong())).andStubReturn(
@@ -262,6 +268,7 @@
      * Test {@link WaitDeviceRecovery#checkMinBatteryLevel(IDevice)} throws an exception if battery
      * level is not readable.
      */
+    @Test
     public void testCheckMinBatteryLevel_unreadable() throws Exception {
         OptionSetter setter = new OptionSetter(mRecovery);
         setter.setOptionValue("min-battery-after-recovery", "50");
@@ -283,6 +290,7 @@
      * Test {@link WaitDeviceRecovery#checkMinBatteryLevel(IDevice)} throws an exception if battery
      * level is below the minimal expected.
      */
+    @Test
     public void testCheckMinBatteryLevel_belowLevel() throws Exception {
         OptionSetter setter = new OptionSetter(mRecovery);
         setter.setOptionValue("min-battery-after-recovery", "50");
@@ -305,6 +313,7 @@
      * Test {@link WaitDeviceRecovery#checkMinBatteryLevel(IDevice)} returns without exception when
      * battery level after recovery is above or equals minimum expected.
      */
+    @Test
     public void testCheckMinBatteryLevel() throws Exception {
         OptionSetter setter = new OptionSetter(mRecovery);
         setter.setOptionValue("min-battery-after-recovery", "50");
@@ -317,58 +326,6 @@
     }
 
     /**
-     * Test {@link WaitDeviceRecovery#rebootDeviceIntoBootloader(IDevice)} does throw when reboot
-     * bootloader throws an IO exception.
-     */
-    public void testRebootDeviceIntoBootloader_IOException() throws Exception {
-        mMockDevice.reboot("bootloader");
-        EasyMock.expectLastCall().andThrow(new IOException());
-        EasyMock.expect(mMockDevice.getSerialNumber()).andReturn("SERIAL");
-        replayMocks();
-        mRecovery.rebootDeviceIntoBootloader(mMockDevice);
-        verifyMocks();
-    }
-
-    /**
-     * Test {@link WaitDeviceRecovery#rebootDeviceIntoBootloader(IDevice)} does throw when reboot
-     * bootloader throws an timeout exception.
-     */
-    public void testRebootDeviceIntoBootloader_timeoutException() throws Exception {
-        mMockDevice.reboot("bootloader");
-        EasyMock.expectLastCall().andThrow(new TimeoutException());
-        EasyMock.expect(mMockDevice.getSerialNumber()).andReturn("SERIAL");
-        replayMocks();
-        mRecovery.rebootDeviceIntoBootloader(mMockDevice);
-        verifyMocks();
-    }
-
-    /**
-     * Test {@link WaitDeviceRecovery#rebootDevice(IDevice)} does throw when reboot
-     * throws an IO exception.
-     */
-    public void testReboot_IOException() throws Exception {
-        mMockDevice.reboot(null);
-        EasyMock.expectLastCall().andThrow(new IOException());
-        EasyMock.expect(mMockDevice.getSerialNumber()).andReturn("SERIAL");
-        replayMocks();
-        mRecovery.rebootDevice(mMockDevice);
-        verifyMocks();
-    }
-
-    /**
-     * Test {@link WaitDeviceRecovery#rebootDevice(IDevice)} does throw when reboot
-     * throws an IO exception.
-     */
-    public void testReboot_timeoutException() throws Exception {
-        mMockDevice.reboot(null);
-        EasyMock.expectLastCall().andThrow(new TimeoutException());
-        EasyMock.expect(mMockDevice.getSerialNumber()).andReturn("SERIAL");
-        replayMocks();
-        mRecovery.rebootDevice(mMockDevice);
-        verifyMocks();
-    }
-
-    /**
      * Verify all the mock objects
      */
     private void verifyMocks() {
diff --git a/tests/src/com/android/tradefed/invoker/TestInvocationTest.java b/tests/src/com/android/tradefed/invoker/TestInvocationTest.java
index 3520f5b..8aa9448 100644
--- a/tests/src/com/android/tradefed/invoker/TestInvocationTest.java
+++ b/tests/src/com/android/tradefed/invoker/TestInvocationTest.java
@@ -415,6 +415,39 @@
         assertEquals(expectedTestTag, mStubInvocationMetadata.getTestTag());
     }
 
+    /** Ensure we get a build info when we get a runtime exception in fetch build. */
+    @Test
+    public void testInvoke_buildFailed_runtimeException() throws Throwable {
+        RuntimeException runtimeException = new RuntimeException("failed to get build.");
+        EasyMock.expect(mMockBuildProvider.getBuild()).andThrow(runtimeException);
+        setupInvoke();
+        // For the mocks to be properly done, stub a BuildRetrievalError.
+        setupMockFailureListenersAny(new BuildRetrievalError("fake"), true);
+
+        EasyMock.reset(mMockLogger, mMockLogRegistry);
+        mMockLogRegistry.registerLogger(mMockLogger);
+        mMockLogger.init();
+        mMockLogger.closeLog();
+        mMockLogRegistry.unregisterLogger();
+
+        EasyMock.expect(mMockLogger.getLog()).andReturn(EMPTY_STREAM_SOURCE);
+        EasyMock.expect(mMockDevice.getLogcat()).andReturn(EMPTY_STREAM_SOURCE).times(2);
+        mMockDevice.clearLogcat();
+        EasyMock.expectLastCall().times(2);
+        Capture<IBuildInfo> captured = new Capture<>();
+        mMockBuildProvider.cleanUp(EasyMock.capture(captured));
+        mMockLogRegistry.unregisterLogger();
+        mMockLogRegistry.dumpToGlobalLog(mMockLogger);
+        mMockLogger.closeLog();
+        replayMocks(mockRescheduler);
+        mTestInvocation.invoke(mStubInvocationMetadata, mStubConfiguration, mockRescheduler);
+        verifyMocks();
+
+        IBuildInfo stubBuild = captured.getValue();
+        assertEquals(BuildInfo.UNKNOWN_BUILD_ID, stubBuild.getBuildId());
+        stubBuild.cleanUp();
+    }
+
     /** Test the invoke scenario where there is no build to test. */
     @Test
     public void testInvoke_noBuild() throws Throwable {
@@ -428,8 +461,6 @@
         mMockLogger.closeLog();
         mMockLogRegistry.unregisterLogger();
 
-        IRemoteTest test = EasyMock.createMock(IRemoteTest.class);
-        mStubConfiguration.setTest(test);
         EasyMock.expect(mMockLogger.getLog()).andReturn(EMPTY_STREAM_SOURCE);
         EasyMock.expect(mMockDevice.getLogcat()).andReturn(EMPTY_STREAM_SOURCE).times(2);
         mMockDevice.clearLogcat();
@@ -439,9 +470,9 @@
         mMockLogRegistry.unregisterLogger();
         mMockLogRegistry.dumpToGlobalLog(mMockLogger);
         mMockLogger.closeLog();
-        replayMocks(test, mockRescheduler);
+        replayMocks(mockRescheduler);
         mTestInvocation.invoke(mStubInvocationMetadata, mStubConfiguration, mockRescheduler);
-        verifyMocks(test);
+        verifyMocks();
 
         IBuildInfo stubBuild = captured.getValue();
         assertEquals(BuildInfo.UNKNOWN_BUILD_ID, stubBuild.getBuildId());
diff --git a/tests/src/com/android/tradefed/presubmit/TestMappingsValidation.java b/tests/src/com/android/tradefed/presubmit/TestMappingsValidation.java
index 222654d..12884f1 100644
--- a/tests/src/com/android/tradefed/presubmit/TestMappingsValidation.java
+++ b/tests/src/com/android/tradefed/presubmit/TestMappingsValidation.java
@@ -66,7 +66,6 @@
     private static final Pattern REGULAR_EXPRESSION = Pattern.compile("(\\?+)|(\\*+)");
     private static final String MODULE_INFO = "module-info.json";
     private static final String TEST_MAPPINGS_ZIP = "test_mappings.zip";
-    private static final String GENERAL_TESTS_ZIP = "general-tests.zip";
     private static final String INCLUDE_FILTER = "include-filter";
     private static final String EXCLUDE_FILTER = "exclude-filter";
     private static final String LOCAL_COMPATIBILITY_SUITES = "compatibility_suites";
@@ -75,8 +74,6 @@
     // Only Check the tests with group in presubmit or postsubmit.
     private static final Set<String> TEST_GROUPS_TO_VALIDATE =
             new HashSet<>(Arrays.asList("presubmit", "postsubmit"));
-    private static final long GIGABYTE = 1024L * 1024L * 1024L;
-    private static final long SIZE_LIMITATION = 4L;
 
     private File testMappingsDir = null;
     private IDeviceBuildInfo deviceBuildInfo = null;
@@ -115,27 +112,6 @@
         FileUtil.recursiveDelete(testMappingsDir);
     }
 
-    /** Test the size of general-tests.zip to ensure it doesn't exceed the size limitation. */
-    @Test
-    public void testGeneralTestsSize() {
-        File generalTestsZip = deviceBuildInfo.getFile(GENERAL_TESTS_ZIP);
-        String error = null;
-        if (generalTestsZip == null || !generalTestsZip.exists()) {
-            error = String.format("general-tests.zip is null or doesn't exist");
-        } else {
-            if (generalTestsZip.length() / GIGABYTE >= SIZE_LIMITATION) {
-                error =
-                        String.format(
-                                "The size of general-tests.zip is: %s Bytes, which "
-                                        + "exceeds the limitation of %s GB",
-                                generalTestsZip.length(), SIZE_LIMITATION);
-            }
-        }
-        if (error != null) {
-            fail(String.format("Fail size check for general-tests.zip:\n%s", error));
-        }
-    }
-
     /**
      * Test all the TEST_MAPPING files and make sure they contain the suite setting in
      * module-info.json.
diff --git a/tests/src/com/android/tradefed/testtype/NativeCodeCoverageListenerTest.java b/tests/src/com/android/tradefed/testtype/NativeCodeCoverageListenerTest.java
index 3860b51..6d7e90e 100644
--- a/tests/src/com/android/tradefed/testtype/NativeCodeCoverageListenerTest.java
+++ b/tests/src/com/android/tradefed/testtype/NativeCodeCoverageListenerTest.java
@@ -32,7 +32,6 @@
 import com.android.tradefed.result.LogDataType;
 import com.android.tradefed.testtype.coverage.CoverageOptions;
 import com.android.tradefed.util.proto.TfMetricProtoUtil;
-import com.android.tradefed.util.TarUtil;
 
 import com.google.common.base.VerifyException;
 import com.google.common.collect.ImmutableMap;
@@ -101,14 +100,14 @@
 
         // Setup mocks to write the coverage measurement to the file.
         doReturn(true).when(mMockDevice).enableAdbRoot();
-        File tarGz =
-                createTarGz(
+        File tar =
+                createTar(
                         ImmutableMap.of(
                                 "path/to/coverage.gcda",
                                 ByteString.copyFromUtf8("coverage.gcda"),
                                 "path/to/.hidden/coverage2.gcda",
                                 ByteString.copyFromUtf8("coverage2.gcda")));
-        doReturn(tarGz).when(mMockDevice).pullFile(anyString());
+        doReturn(tar).when(mMockDevice).pullFile(anyString());
 
         // Simulate a test run.
         mCodeCoverageListener.testRunStarted(RUN_NAME, TEST_COUNT);
@@ -140,7 +139,7 @@
         mCodeCoverageListener = new NativeCodeCoverageListener(mMockDevice, mFakeListener);
 
         doReturn(true).when(mMockDevice).enableAdbRoot();
-        doReturn(createTarGz(ImmutableMap.of())).when(mMockDevice).pullFile(anyString());
+        doReturn(createTar(ImmutableMap.of())).when(mMockDevice).pullFile(anyString());
 
         // Simulate a test run.
         mCodeCoverageListener.testRunStarted(RUN_NAME, TEST_COUNT);
@@ -170,7 +169,7 @@
 
         doReturn(true).when(mMockDevice).enableAdbRoot();
         doReturn(true).when(mMockDevice).isAdbRoot();
-        doReturn(createTarGz(ImmutableMap.of())).when(mMockDevice).pullFile(anyString());
+        doReturn(createTar(ImmutableMap.of())).when(mMockDevice).pullFile(anyString());
 
         // Simulate a test run.
         mCodeCoverageListener.testRunStarted(RUN_NAME, TEST_COUNT);
@@ -195,7 +194,7 @@
         doReturn(true).when(mMockDevice).isAdbRoot();
         doReturn("123").when(mMockDevice).getProcessPid("mediaserver");
         doReturn("56789").when(mMockDevice).getProcessPid("adbd");
-        doReturn(createTarGz(ImmutableMap.of())).when(mMockDevice).pullFile(anyString());
+        doReturn(createTar(ImmutableMap.of())).when(mMockDevice).pullFile(anyString());
 
         // Simulate a test run.
         mCodeCoverageListener.testRunStarted(RUN_NAME, TEST_COUNT);
@@ -244,11 +243,11 @@
 
         // Setup mocks to write the coverage measurement to the file.
         doReturn(true).when(mMockDevice).enableAdbRoot();
-        File tarGz =
-                createTarGz(
+        File tar =
+                createTar(
                         ImmutableMap.of(
                                 "path/to/coverage.gcda", ByteString.copyFromUtf8("coverage.gcda")));
-        doReturn(tarGz).when(mMockDevice).pullFile(anyString());
+        doReturn(tar).when(mMockDevice).pullFile(anyString());
 
         // Manually call logCoverageMeasurements().
         mCodeCoverageListener.logCoverageMeasurements("manual");
@@ -288,8 +287,8 @@
         }
     }
 
-    /** Utility method to create .tar.gz files. */
-    private File createTarGz(Map<String, ByteString> fileContents) throws IOException {
+    /** Utility method to create .tar files. */
+    private File createTar(Map<String, ByteString> fileContents) throws IOException {
         File tarFile = folder.newFile("coverage.tar");
         try (TarArchiveOutputStream out =
                 new TarArchiveOutputStream(new FileOutputStream(tarFile))) {
@@ -302,6 +301,6 @@
                 out.closeArchiveEntry();
             }
         }
-        return TarUtil.gzip(tarFile);
+        return tarFile;
     }
 }
diff --git a/tests/src/com/android/tradefed/testtype/binary/ExecutableTargetTestTest.java b/tests/src/com/android/tradefed/testtype/binary/ExecutableTargetTestTest.java
index 69bbd96..05cf59a 100644
--- a/tests/src/com/android/tradefed/testtype/binary/ExecutableTargetTestTest.java
+++ b/tests/src/com/android/tradefed/testtype/binary/ExecutableTargetTestTest.java
@@ -303,4 +303,59 @@
                         Mockito.eq(testDescription3),
                         Mockito.eq(new HashMap<String, MetricMeasurement.Metric>()));
     }
+
+    /** Test the run method for a couple commands with IncludeFilter */
+    @Test
+    public void testRun_add_description_IncludeFilter()
+            throws DeviceNotAvailableException, ConfigurationException {
+        mExecutableTargetTest =
+                new ExecutableTargetTest() {
+                    @Override
+                    public String findBinary(String binary) {
+                        return binary;
+                    }
+
+                    @Override
+                    protected void checkCommandResult(
+                            CommandResult result,
+                            ITestInvocationListener listener,
+                            TestDescription description) {}
+                };
+        mExecutableTargetTest.setDevice(mMockITestDevice);
+        // Set test commands
+        OptionSetter setter = new OptionSetter(mExecutableTargetTest);
+        setter.setOptionValue("test-command-line", testName1, testCmd1);
+        setter.setOptionValue("test-command-line", testName2, testCmd2);
+        setter.setOptionValue("test-command-line", testName3, testCmd3);
+        TestDescription testDescription = new TestDescription(testName1, testName1);
+        TestDescription testDescription2 = new TestDescription(testName2, testName2);
+        TestDescription testDescription3 = new TestDescription(testName3, testName3);
+        mExecutableTargetTest.addIncludeFilter(testDescription2.toString());
+        mExecutableTargetTest.run(mTestInfo, mListener);
+        // testName1 should NOT run.
+        Mockito.verify(mListener, Mockito.never()).testRunStarted(eq(testName1), eq(1));
+        Mockito.verify(mListener, Mockito.never()).testStarted(Mockito.eq(testDescription));
+        Mockito.verify(mListener, Mockito.never())
+                .testEnded(
+                        Mockito.eq(testDescription),
+                        Mockito.eq(new HashMap<String, MetricMeasurement.Metric>()));
+        // run cmd2 test
+        Mockito.verify(mListener, Mockito.times(1)).testRunStarted(eq(testName2), eq(1));
+        Mockito.verify(mListener, Mockito.times(1)).testStarted(Mockito.eq(testDescription2));
+        Mockito.verify(mListener, Mockito.times(1))
+                .testEnded(
+                        Mockito.eq(testDescription2),
+                        Mockito.eq(new HashMap<String, MetricMeasurement.Metric>()));
+        Mockito.verify(mListener, Mockito.times(1))
+                .testRunEnded(
+                        Mockito.anyLong(),
+                        Mockito.<HashMap<String, MetricMeasurement.Metric>>any());
+        // testName3 should NOT run.
+        Mockito.verify(mListener, Mockito.never()).testRunStarted(eq(testName3), eq(1));
+        Mockito.verify(mListener, Mockito.never()).testStarted(Mockito.eq(testDescription3));
+        Mockito.verify(mListener, Mockito.never())
+                .testEnded(
+                        Mockito.eq(testDescription3),
+                        Mockito.eq(new HashMap<String, MetricMeasurement.Metric>()));
+    }
 }