Merge "Improve some of the messages to make them clearer"
diff --git a/invocation_interfaces/com/android/tradefed/config/ConfigurationDescriptor.java b/invocation_interfaces/com/android/tradefed/config/ConfigurationDescriptor.java
index bc6d30c..e97233f 100644
--- a/invocation_interfaces/com/android/tradefed/config/ConfigurationDescriptor.java
+++ b/invocation_interfaces/com/android/tradefed/config/ConfigurationDescriptor.java
@@ -48,7 +48,7 @@
     /** Metadata key for a config to specify that it was sharded. */
     public static final String LOCAL_SHARDED_KEY = "sharded";
     /** Metadata key for a config parameterization, optional. */
-    public static final String PARAMETER_KEY = "parameter";
+    public static final String ACTIVE_PARAMETER_KEY = "active-parameter";
 
     @Option(name = "test-suite-tag", description = "A membership tag to suite. Can be repeated.")
     private List<String> mSuiteTags = new ArrayList<>();
@@ -136,7 +136,7 @@
      * @param key {@link String} of the key to add values to.
      * @param values a list of {@link String} of the additional values.
      */
-    public void addMetaData(String key, List<String> values) {
+    public void addMetadata(String key, List<String> values) {
         for (String source : values) {
             mMetaData.put(key, source);
         }
diff --git a/invocation_interfaces/com/android/tradefed/invoker/logger/InvocationMetricLogger.java b/invocation_interfaces/com/android/tradefed/invoker/logger/InvocationMetricLogger.java
index a23b5c1..ddad834 100644
--- a/invocation_interfaces/com/android/tradefed/invoker/logger/InvocationMetricLogger.java
+++ b/invocation_interfaces/com/android/tradefed/invoker/logger/InvocationMetricLogger.java
@@ -38,10 +38,12 @@
         SHUTDOWN_HARD_LATENCY("shutdown_hard_latency_ms", false),
         DEVICE_DONE_TIMESTAMP("device_done_timestamp", false),
         DEVICE_RELEASE_STATE("device_release_state", false),
+        DEVICE_LOST_DETECTED("device_lost_detected", false),
         SANDBOX_EXIT_CODE("sandbox_exit_code", false),
         CF_FETCH_ARTIFACT_TIME("cf_fetch_artifact_time_ms", false),
         CF_GCE_CREATE_TIME("cf_gce_create_time_ms", false),
-        CF_LAUNCH_CVD_TIME("cf_launch_cvd_time_ms", false);
+        CF_LAUNCH_CVD_TIME("cf_launch_cvd_time_ms", false),
+        CF_INSTANCE_COUNT("cf_instance_count", false);
 
         private final String mKeyName;
         // Whether or not to add the value when the key is added again.
diff --git a/src/com/android/tradefed/cluster/ClusterCommandScheduler.java b/src/com/android/tradefed/cluster/ClusterCommandScheduler.java
index 052cf17..218f910 100644
--- a/src/com/android/tradefed/cluster/ClusterCommandScheduler.java
+++ b/src/com/android/tradefed/cluster/ClusterCommandScheduler.java
@@ -384,8 +384,9 @@
                             // TODO: retrieve cancel reason from TFC.
                             String cause =
                                     String.format(
-                                            "Command (requestId=%s, commandId=%s) has been marked"
-                                                    + " canceled by the cluster",
+                                            "The cluster client %s has marked command "
+                                                    + "(requestId=%s, commandId=%s) canceled",
+                                            getClusterClient().getClass().getSimpleName(),
                                             mCommandTask.getRequestId(),
                                             mCommandTask.getCommandId());
                             CLog.w("Stop invocation due to: %s", cause);
diff --git a/src/com/android/tradefed/device/cloud/GceAvdInfo.java b/src/com/android/tradefed/device/cloud/GceAvdInfo.java
index 81877f6..4d627f2 100644
--- a/src/com/android/tradefed/device/cloud/GceAvdInfo.java
+++ b/src/com/android/tradefed/device/cloud/GceAvdInfo.java
@@ -251,5 +251,9 @@
                     InvocationMetricKey.CF_LAUNCH_CVD_TIME,
                     Double.valueOf(Double.parseDouble(launch_cvd_time) * 1000).longValue());
         }
+        if (!InvocationMetricLogger.getInvocationMetrics()
+                .containsKey(InvocationMetricKey.CF_INSTANCE_COUNT.toString())) {
+            InvocationMetricLogger.addInvocationMetrics(InvocationMetricKey.CF_INSTANCE_COUNT, 1);
+        }
     }
 }
diff --git a/src/com/android/tradefed/invoker/RemoteInvocationExecution.java b/src/com/android/tradefed/invoker/RemoteInvocationExecution.java
index 56fdb7c..d7843d9 100644
--- a/src/com/android/tradefed/invoker/RemoteInvocationExecution.java
+++ b/src/com/android/tradefed/invoker/RemoteInvocationExecution.java
@@ -36,6 +36,8 @@
 import com.android.tradefed.device.cloud.GceManager;
 import com.android.tradefed.device.cloud.ManagedRemoteDevice;
 import com.android.tradefed.device.cloud.RemoteFileUtil;
+import com.android.tradefed.invoker.logger.InvocationMetricLogger;
+import com.android.tradefed.invoker.logger.InvocationMetricLogger.InvocationMetricKey;
 import com.android.tradefed.log.ITestLogger;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.result.FileInputStreamSource;
@@ -127,8 +129,10 @@
             TestDeviceOptions options = device.getOptions();
             // Trigger the multi-tenant start in the VM
             options.addGceDriverParams("--num-avds-per-instance");
-            options.addGceDriverParams(config.getCommandOptions().getShardCount().toString());
-            // TODO: Track how many instances we created
+            String count = config.getCommandOptions().getShardCount().toString();
+            options.addGceDriverParams(count);
+            InvocationMetricLogger.addInvocationMetrics(
+                    InvocationMetricKey.CF_INSTANCE_COUNT, count);
         }
     }
 
diff --git a/src/com/android/tradefed/invoker/TestInvocation.java b/src/com/android/tradefed/invoker/TestInvocation.java
index 46edad7..2bf69ee 100644
--- a/src/com/android/tradefed/invoker/TestInvocation.java
+++ b/src/com/android/tradefed/invoker/TestInvocation.java
@@ -341,17 +341,26 @@
             }
             mStatus = "done running tests";
             // Track the timestamp when we are done with devices
-            InvocationMetricLogger.addInvocationMetrics(
+            addInvocationMetric(
                     InvocationMetricKey.DEVICE_DONE_TIMESTAMP, System.currentTimeMillis());
-            if (config.getCommandOptions().earlyDeviceRelease()) {
-                // Capture the FreeDeviceState of the primary device
-                Map<ITestDevice, FreeDeviceState> devicesStates =
-                        CommandScheduler.createReleaseMap(context, exception);
-                if (devicesStates.size() >= 1) {
-                    InvocationMetricLogger.addInvocationMetrics(
-                            InvocationMetricKey.DEVICE_RELEASE_STATE,
-                            devicesStates.values().iterator().next().toString());
+            // Capture the FreeDeviceState of the primary device
+            Map<ITestDevice, FreeDeviceState> devicesStates =
+                    CommandScheduler.createReleaseMap(context, exception);
+            if (devicesStates.size() >= 1) {
+                addInvocationMetric(
+                        InvocationMetricKey.DEVICE_RELEASE_STATE,
+                        devicesStates.values().iterator().next().toString());
+            }
+            int countLost = 0;
+            for (FreeDeviceState fds : devicesStates.values()) {
+                if (FreeDeviceState.UNAVAILABLE.equals(fds)) {
+                    countLost++;
                 }
+            }
+            if (countLost > 0) {
+                addInvocationMetric(InvocationMetricKey.DEVICE_LOST_DETECTED, countLost);
+            }
+            if (config.getCommandOptions().earlyDeviceRelease()) {
                 for (IScheduledInvocationListener scheduleListener : mSchedulerListeners) {
                     scheduleListener.releaseDevices(context, devicesStates);
                 }
@@ -921,6 +930,14 @@
                 .setLastInvocationExitCode(code, stack);
     }
 
+    protected void addInvocationMetric(InvocationMetricKey key, long value) {
+        InvocationMetricLogger.addInvocationMetrics(key, value);
+    }
+
+    protected void addInvocationMetric(InvocationMetricKey key, String value) {
+        InvocationMetricLogger.addInvocationMetrics(key, value);
+    }
+
     public static String getDeviceLogName(Stage stage) {
         return DEVICE_LOG_NAME_PREFIX + stage.getName();
     }
diff --git a/src/com/android/tradefed/sandbox/TradefedSandbox.java b/src/com/android/tradefed/sandbox/TradefedSandbox.java
index 9718642..e45b91e 100644
--- a/src/com/android/tradefed/sandbox/TradefedSandbox.java
+++ b/src/com/android/tradefed/sandbox/TradefedSandbox.java
@@ -120,7 +120,8 @@
         }
 
         long timeout = config.getCommandOptions().getInvocationTimeout();
-        mRunUtil.allowInterrupt(false);
+        // Allow interruption, subprocess should handle signals itself
+        mRunUtil.allowInterrupt(true);
         CommandResult result =
                 mRunUtil.runTimedCmd(timeout, mStdout, mStderr, mCmdArgs.toArray(new String[0]));
         // Log stdout and stderr
diff --git a/src/com/android/tradefed/testtype/suite/ModuleDefinition.java b/src/com/android/tradefed/testtype/suite/ModuleDefinition.java
index 192aff6..d869243 100644
--- a/src/com/android/tradefed/testtype/suite/ModuleDefinition.java
+++ b/src/com/android/tradefed/testtype/suite/ModuleDefinition.java
@@ -188,7 +188,7 @@
                 configDescriptor
                         .getAllMetaData()
                         .getUniqueMap()
-                        .get(ConfigurationDescriptor.PARAMETER_KEY);
+                        .get(ConfigurationDescriptor.ACTIVE_PARAMETER_KEY);
         if (parameterization != null) {
             mModuleInvocationContext.addInvocationAttribute(
                     MODULE_PARAMETERIZATION, parameterization);
diff --git a/src/com/android/tradefed/testtype/suite/SuiteModuleLoader.java b/src/com/android/tradefed/testtype/suite/SuiteModuleLoader.java
index df958aa..f233b8d 100644
--- a/src/com/android/tradefed/testtype/suite/SuiteModuleLoader.java
+++ b/src/com/android/tradefed/testtype/suite/SuiteModuleLoader.java
@@ -328,7 +328,7 @@
                             paramConfig
                                     .getConfigurationDescription()
                                     .addMetadata(
-                                            ConfigurationDescriptor.PARAMETER_KEY,
+                                            ConfigurationDescriptor.ACTIVE_PARAMETER_KEY,
                                             param.getParameterIdentifier());
                             param.addParameterSpecificConfig(paramConfig);
                             setUpConfig(name, baseId, fullId, paramConfig, abi);
diff --git a/src/com/android/tradefed/testtype/suite/TestMappingSuiteRunner.java b/src/com/android/tradefed/testtype/suite/TestMappingSuiteRunner.java
index 037ceeb..1f3514a 100644
--- a/src/com/android/tradefed/testtype/suite/TestMappingSuiteRunner.java
+++ b/src/com/android/tradefed/testtype/suite/TestMappingSuiteRunner.java
@@ -177,7 +177,7 @@
                 moduleConfig.setTests(allTests);
                 // Set test sources to ConfigurationDescriptor.
                 List<String> testSources = getTestSources(testInfos);
-                configDescriptor.addMetaData(TestMapping.TEST_SOURCES, testSources);
+                configDescriptor.addMetadata(TestMapping.TEST_SOURCES, testSources);
             }
         }
         return testConfigs;
diff --git a/src/com/android/tradefed/util/GCSFileDownloader.java b/src/com/android/tradefed/util/GCSFileDownloader.java
index 303d3b7..08c4990 100644
--- a/src/com/android/tradefed/util/GCSFileDownloader.java
+++ b/src/com/android/tradefed/util/GCSFileDownloader.java
@@ -190,11 +190,16 @@
         }
         for (String subRemoteFolder : subRemoteFolders) {
             String subFolderName = Paths.get(subRemoteFolder).getFileName().toString();
-            if (!recursiveCheckFolderFreshness(
-                    bucketName, subRemoteFolder, new File(localFolder, subFolderName))) {
+            File subFolder = new File(localFolder, subFolderName);
+            if (new File(localFolder, subFolderName).exists()
+                    && !new File(localFolder, subFolderName).isDirectory()) {
+                CLog.w("%s exists as a non-directory.", subFolder);
+                subFolder = new File(localFolder, subFolderName + "_folder");
+            }
+            if (!recursiveCheckFolderFreshness(bucketName, subRemoteFolder, subFolder)) {
                 return false;
             }
-            subFilenames.remove(subFolderName);
+            subFilenames.remove(subFolder.getName());
         }
         return subFilenames.isEmpty();
     }
@@ -310,6 +315,14 @@
         if (!localFolder.exists()) {
             FileUtil.mkdirsRWX(localFolder);
         }
+        if (!localFolder.isDirectory()) {
+            String error =
+                    String.format(
+                            "%s is not a folder. (gs://%s/%s)",
+                            localFolder, bucketName, remoteFolderName);
+            CLog.e(error);
+            throw new IOException(error);
+        }
         Set<String> subFilenames = new HashSet<>(Arrays.asList(localFolder.list()));
         List<String> subRemoteFolders = new ArrayList<>();
         List<StorageObject> subRemoteFiles = new ArrayList<>();
@@ -322,9 +335,14 @@
         }
         for (String subRemoteFolder : subRemoteFolders) {
             String subFolderName = Paths.get(subRemoteFolder).getFileName().toString();
-            recursiveDownloadFolder(
-                    bucketName, subRemoteFolder, new File(localFolder, subFolderName));
-            subFilenames.remove(subFolderName);
+            File subFolder = new File(localFolder, subFolderName);
+            if (new File(localFolder, subFolderName).exists()
+                    && !new File(localFolder, subFolderName).isDirectory()) {
+                CLog.w("%s exists as a non-directory.", subFolder);
+                subFolder = new File(localFolder, subFolderName + "_folder");
+            }
+            recursiveDownloadFolder(bucketName, subRemoteFolder, subFolder);
+            subFilenames.remove(subFolder.getName());
         }
         for (String subFilename : subFilenames) {
             FileUtil.recursiveDelete(new File(localFolder, subFilename));
diff --git a/src/com/android/tradefed/util/LocalRunInstructionBuilder.java b/src/com/android/tradefed/util/LocalRunInstructionBuilder.java
index 63c72f0..3550df9 100644
--- a/src/com/android/tradefed/util/LocalRunInstructionBuilder.java
+++ b/src/com/android/tradefed/util/LocalRunInstructionBuilder.java
@@ -130,7 +130,7 @@
         }
         // Ensure repro is aligned with parameterized modules.
         List<String> paramMetadata =
-                configDescriptor.getMetaData(ConfigurationDescriptor.PARAMETER_KEY);
+                configDescriptor.getMetaData(ConfigurationDescriptor.ACTIVE_PARAMETER_KEY);
         if (paramMetadata != null
                 && paramMetadata.size() > 0
                 && "instant".equals(paramMetadata.get(0))) {
diff --git a/test_framework/com/android/tradefed/targetprep/RootTargetPreparer.java b/test_framework/com/android/tradefed/targetprep/RootTargetPreparer.java
index 50ddaae..8d6031b 100644
--- a/test_framework/com/android/tradefed/targetprep/RootTargetPreparer.java
+++ b/test_framework/com/android/tradefed/targetprep/RootTargetPreparer.java
@@ -15,12 +15,14 @@
  */
 package com.android.tradefed.targetprep;
 
+import com.android.tradefed.command.remote.DeviceDescriptor;
 import com.android.tradefed.config.Option;
 import com.android.tradefed.config.OptionClass;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.device.StubDevice;
 import com.android.tradefed.invoker.TestInformation;
+import com.android.tradefed.log.LogUtil.CLog;
 
 /**
  * Target preparer that performs "adb root" or "adb unroot" based on option "force-root".
@@ -39,6 +41,11 @@
                             + "root during setup.")
     private boolean mForceRoot = true;
 
+    @Option(
+            name = "throw-on-error",
+            description = "Throws TargetSetupError if adb root/unroot fails")
+    private boolean mThrowOnError = true;
+
     @Override
     public void setUp(TestInformation testInfo)
             throws TargetSetupError, BuildError, DeviceNotAvailableException {
@@ -49,9 +56,9 @@
         }
         mWasRoot = device.isAdbRoot();
         if (!mWasRoot && mForceRoot && !device.enableAdbRoot()) {
-            throw new TargetSetupError("Failed to adb root device", device.getDeviceDescriptor());
+            throwOrLog("Failed to adb root device", device.getDeviceDescriptor());
         } else if (mWasRoot && !mForceRoot && !device.disableAdbRoot()) {
-            throw new TargetSetupError("Failed to adb unroot device", device.getDeviceDescriptor());
+            throwOrLog("Failed to adb unroot device", device.getDeviceDescriptor());
         }
     }
 
@@ -68,4 +75,13 @@
             device.enableAdbRoot();
         }
     }
+
+    private void throwOrLog(String message, DeviceDescriptor deviceDescriptor)
+            throws TargetSetupError {
+        if (mThrowOnError) {
+            throw new TargetSetupError(message, deviceDescriptor);
+        } else {
+            CLog.w(message + " " + deviceDescriptor);
+        }
+    }
 }
diff --git a/test_framework/com/android/tradefed/testtype/python/PythonBinaryHostTest.java b/test_framework/com/android/tradefed/testtype/python/PythonBinaryHostTest.java
index cdd11be..1d23bfe 100644
--- a/test_framework/com/android/tradefed/testtype/python/PythonBinaryHostTest.java
+++ b/test_framework/com/android/tradefed/testtype/python/PythonBinaryHostTest.java
@@ -42,6 +42,8 @@
 import com.android.tradefed.util.RunUtil;
 import com.android.tradefed.util.SubprocessTestResultsParser;
 
+import com.google.common.base.Joiner;
+
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -64,6 +66,7 @@
 public class PythonBinaryHostTest implements IRemoteTest, ITestFilterReceiver {
 
     protected static final String ANDROID_SERIAL_VAR = "ANDROID_SERIAL";
+    protected static final String LD_LIBRARY_PATH = "LD_LIBRARY_PATH";
     protected static final String PATH_VAR = "PATH";
     protected static final long PATH_TIMEOUT_MS = 60000L;
 
@@ -75,6 +78,7 @@
 
     private Set<String> mIncludeFilters = new LinkedHashSet<>();
     private Set<String> mExcludeFilters = new LinkedHashSet<>();
+    private String mLdLibraryPath = null;
 
     @Option(name = "par-file-name", description = "The binary names inside the build info to run.")
     private Set<String> mBinaryNames = new HashSet<>();
@@ -174,6 +178,22 @@
     public final void run(TestInformation testInfo, ITestInvocationListener listener)
             throws DeviceNotAvailableException {
         mTestInfo = testInfo;
+        File hostTestDir = mTestInfo.executionFiles().get(FilesKey.HOST_TESTS_DIRECTORY);
+        if (hostTestDir.exists()) {
+            File libDir = new File(hostTestDir, "lib");
+            List<String> ldLibraryPath = new ArrayList<>();
+            if (libDir.exists()) {
+                ldLibraryPath.add(libDir.getAbsolutePath());
+            }
+
+            File lib64Dir = new File(hostTestDir, "lib64");
+            if (lib64Dir.exists()) {
+                ldLibraryPath.add(lib64Dir.getAbsolutePath());
+            }
+            if (!ldLibraryPath.isEmpty()) {
+                mLdLibraryPath = Joiner.on(":").join(ldLibraryPath);
+            }
+        }
         List<File> pythonFilesList = findParFiles();
         for (File pyFile : pythonFilesList) {
             if (!pyFile.exists()) {
@@ -222,6 +242,9 @@
             commandLine.add(mTestInfo.getDevice().getSerialNumber());
         }
 
+        if (mLdLibraryPath != null) {
+            getRunUtil().setEnvVariable(LD_LIBRARY_PATH, mLdLibraryPath);
+        }
         if (mInjectAndroidSerialVar) {
             getRunUtil()
                     .setEnvVariable(ANDROID_SERIAL_VAR, mTestInfo.getDevice().getSerialNumber());
diff --git a/tests/src/com/android/tradefed/invoker/TestInvocationTest.java b/tests/src/com/android/tradefed/invoker/TestInvocationTest.java
index 6b01fc4..48a2590 100644
--- a/tests/src/com/android/tradefed/invoker/TestInvocationTest.java
+++ b/tests/src/com/android/tradefed/invoker/TestInvocationTest.java
@@ -51,6 +51,7 @@
 import com.android.tradefed.device.StubDevice;
 import com.android.tradefed.device.TcpDevice;
 import com.android.tradefed.device.TestDeviceOptions;
+import com.android.tradefed.device.TestDeviceState;
 import com.android.tradefed.device.metric.BaseDeviceMetricCollector;
 import com.android.tradefed.device.metric.DeviceMetricData;
 import com.android.tradefed.device.metric.IMetricCollector;
@@ -217,6 +218,8 @@
         EasyMock.expect(mMockDevice.getSerialNumber()).andStubReturn(SERIAL);
         EasyMock.expect(mMockDevice.getIDevice()).andStubReturn(null);
         EasyMock.expect(mMockDevice.getBattery()).andStubReturn(null);
+        EasyMock.expect(mMockDevice.getDeviceState()).andStubReturn(TestDeviceState.NOT_AVAILABLE);
+        mMockDevice.setRecoveryMode(RecoveryMode.AVAILABLE);
         mMockDevice.setRecovery(mMockRecovery);
         mMockDevice.preInvocationSetup(
                 (IBuildInfo) EasyMock.anyObject(), EasyMock.<List<IBuildInfo>>anyObject());
@@ -307,6 +310,12 @@
                     public void registerExecutionFiles(ExecutionFiles executionFiles) {
                         // Empty of purpose
                     }
+
+                    @Override
+                    protected void addInvocationMetric(InvocationMetricKey key, long value) {}
+
+                    @Override
+                    protected void addInvocationMetric(InvocationMetricKey key, String value) {}
                 };
     }
 
@@ -1565,6 +1574,12 @@
                         // Avoid re-entry in the current TF invocation scope for unit tests.
                         return new InvocationScope();
                     }
+
+                    @Override
+                    protected void addInvocationMetric(InvocationMetricKey key, long value) {}
+
+                    @Override
+                    protected void addInvocationMetric(InvocationMetricKey key, String value) {}
                 };
         mMockBuildInfo = EasyMock.createMock(IDeviceBuildInfo.class);
         EasyMock.expect(mMockBuildInfo.getProperties()).andStubReturn(new HashSet<>());
@@ -1645,6 +1660,12 @@
                             // Avoid re-entry in the current TF invocation scope for unit tests.
                             return new InvocationScope();
                         }
+
+                        @Override
+                        protected void addInvocationMetric(InvocationMetricKey key, long value) {}
+
+                        @Override
+                        protected void addInvocationMetric(InvocationMetricKey key, String value) {}
                     };
             mMockBuildInfo = EasyMock.createMock(IDeviceBuildInfo.class);
             IRemoteTest test = EasyMock.createNiceMock(IRemoteTest.class);
@@ -1743,6 +1764,12 @@
                             // Avoid re-entry in the current TF invocation scope for unit tests.
                             return new InvocationScope();
                         }
+
+                        @Override
+                        protected void addInvocationMetric(InvocationMetricKey key, long value) {}
+
+                        @Override
+                        protected void addInvocationMetric(InvocationMetricKey key, String value) {}
                     };
             mMockBuildInfo = EasyMock.createMock(IDeviceBuildInfo.class);
             IRemoteTest test = EasyMock.createNiceMock(IRemoteTest.class);
diff --git a/tests/src/com/android/tradefed/targetprep/RootTargetPreparerTest.java b/tests/src/com/android/tradefed/targetprep/RootTargetPreparerTest.java
index e28d420..22c74cc 100644
--- a/tests/src/com/android/tradefed/targetprep/RootTargetPreparerTest.java
+++ b/tests/src/com/android/tradefed/targetprep/RootTargetPreparerTest.java
@@ -124,4 +124,36 @@
 
         mRootTargetPreparer.setUp(mTestInfo);
     }
+
+    @Test
+    public void testSetUpFail_forceRoot_ignoresFailure() throws Exception {
+        OptionSetter setter = new OptionSetter(mRootTargetPreparer);
+        setter.setOptionValue("throw-on-error", "false");
+
+        EasyMock.expect(mMockDevice.isAdbRoot()).andReturn(false).once();
+        EasyMock.expect(mMockDevice.enableAdbRoot()).andReturn(false).once();
+        EasyMock.expect(mMockDevice.getDeviceDescriptor()).andReturn(null).once();
+        EasyMock.expect(mMockDevice.disableAdbRoot()).andReturn(true).once();
+        EasyMock.replay(mMockDevice, mMockBuildInfo);
+
+        mRootTargetPreparer.setUp(mTestInfo);
+        mRootTargetPreparer.tearDown(mTestInfo, null);
+    }
+
+    @Test
+    public void testSetUpFail_forceUnroot_ignoresFailure() throws Exception {
+        OptionSetter setter = new OptionSetter(mRootTargetPreparer);
+        setter.setOptionValue("force-root", "false");
+        setter.setOptionValue("throw-on-error", "false");
+
+        EasyMock.expect(mMockDevice.isAdbRoot()).andReturn(true).once();
+        EasyMock.expect(mMockDevice.disableAdbRoot()).andReturn(false).once();
+        EasyMock.expect(mMockDevice.getDeviceDescriptor()).andReturn(null).once();
+        EasyMock.expect(mMockDevice.enableAdbRoot()).andReturn(true).once();
+        EasyMock.replay(mMockDevice, mMockBuildInfo);
+
+        mRootTargetPreparer.setUp(mTestInfo);
+        mRootTargetPreparer.tearDown(mTestInfo, null);
+        EasyMock.verify(mMockDevice, mMockBuildInfo);
+    }
 }
diff --git a/tests/src/com/android/tradefed/testtype/python/PythonBinaryHostTestTest.java b/tests/src/com/android/tradefed/testtype/python/PythonBinaryHostTestTest.java
index f9cf4e1..2c32f13 100644
--- a/tests/src/com/android/tradefed/testtype/python/PythonBinaryHostTestTest.java
+++ b/tests/src/com/android/tradefed/testtype/python/PythonBinaryHostTestTest.java
@@ -101,6 +101,7 @@
         mMockRunUtil.setEnvVariable(PythonBinaryHostTest.ANDROID_SERIAL_VAR, "SERIAL");
 
         mPythonBinary = FileUtil.createTempFile("python-dir", "");
+        mTestInfo.executionFiles().put(FilesKey.HOST_TESTS_DIRECTORY, new File("/path-not-exist"));
     }
 
     @After
@@ -312,6 +313,7 @@
     public void testRun_withAdbPath() throws Exception {
         mTestInfo.executionFiles().put(FilesKey.ADB_BINARY, new File("/test/adb"));
         File binary = FileUtil.createTempFile("python-dir", "");
+
         try {
             OptionSetter setter = new OptionSetter(mTest);
             setter.setOptionValue("python-binaries", binary.getAbsolutePath());
@@ -344,6 +346,51 @@
         }
     }
 
+    /** Test running the python tests when shared lib is available in HOST_TESTS_DIRECTORY. */
+    @Test
+    public void testRun_withSharedLib() throws Exception {
+        File hostTestsDir = FileUtil.createTempDir("host-test-cases");
+        mTestInfo.executionFiles().put(FilesKey.HOST_TESTS_DIRECTORY, hostTestsDir);
+        File binary = FileUtil.createTempFile("python-dir", "", hostTestsDir);
+        File lib = new File(hostTestsDir, "lib");
+        lib.mkdirs();
+        File lib64 = new File(hostTestsDir, "lib64");
+        lib64.mkdirs();
+
+        try {
+            OptionSetter setter = new OptionSetter(mTest);
+            setter.setOptionValue("python-binaries", binary.getAbsolutePath());
+            mMockRunUtil.setEnvVariable(
+                    PythonBinaryHostTest.LD_LIBRARY_PATH,
+                    lib.getAbsolutePath() + ":" + lib64.getAbsolutePath());
+            expectedAdbPath(mFakeAdb);
+
+            CommandResult res = new CommandResult();
+            res.setStatus(CommandStatus.SUCCESS);
+            res.setStderr("TEST_RUN_STARTED {\"testCount\": 5, \"runName\": \"TestSuite\"}");
+            EasyMock.expect(
+                            mMockRunUtil.runTimedCmd(
+                                    EasyMock.anyLong(), EasyMock.eq(binary.getAbsolutePath())))
+                    .andReturn(res);
+            mMockListener.testRunStarted(
+                    EasyMock.eq(binary.getName()),
+                    EasyMock.eq(5),
+                    EasyMock.eq(0),
+                    EasyMock.anyLong());
+            mMockListener.testLog(
+                    EasyMock.eq(binary.getName() + "-stderr"),
+                    EasyMock.eq(LogDataType.TEXT),
+                    EasyMock.anyObject());
+            EasyMock.expect(mMockDevice.getIDevice()).andReturn(new StubDevice("serial"));
+
+            EasyMock.replay(mMockRunUtil, mMockBuildInfo, mMockListener, mMockDevice);
+            mTest.run(mTestInfo, mMockListener);
+            EasyMock.verify(mMockRunUtil, mMockBuildInfo, mMockListener, mMockDevice);
+        } finally {
+            FileUtil.recursiveDelete(hostTestsDir);
+        }
+    }
+
     /**
      * If the binary returns an exception status, we should throw a runtime exception since
      * something went wrong with the binary setup.
diff --git a/tests/src/com/android/tradefed/testtype/suite/ModuleDefinitionTest.java b/tests/src/com/android/tradefed/testtype/suite/ModuleDefinitionTest.java
index 7430069..3a4ff57 100644
--- a/tests/src/com/android/tradefed/testtype/suite/ModuleDefinitionTest.java
+++ b/tests/src/com/android/tradefed/testtype/suite/ModuleDefinitionTest.java
@@ -16,6 +16,8 @@
 package com.android.tradefed.testtype.suite;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -358,6 +360,51 @@
         }
     }
 
+    @Test
+    public void testCreateModule() {
+        IConfiguration config = new Configuration("", "");
+        ConfigurationDescriptor descriptor = config.getConfigurationDescription();
+        descriptor.setAbi(new Abi("armeabi-v7a", "32"));
+        descriptor.addMetadata(ITestSuite.PARAMETER_KEY, Arrays.asList("instant_app", "multi_abi"));
+        mModule =
+                new ModuleDefinition(
+                        MODULE_NAME,
+                        mTestList,
+                        mMapDeviceTargetPreparer,
+                        mMultiTargetPrepList,
+                        config);
+        assertNotNull(mModule.getModuleInvocationContext());
+        IInvocationContext moduleContext = mModule.getModuleInvocationContext();
+        assertNull(moduleContext.getAttributes().get(ModuleDefinition.MODULE_PARAMETERIZATION));
+    }
+
+    @Test
+    public void testCreateModule_withParams() {
+        IConfiguration config = new Configuration("", "");
+        ConfigurationDescriptor descriptor = config.getConfigurationDescription();
+        descriptor.setAbi(new Abi("armeabi-v7a", "32"));
+        descriptor.addMetadata(
+                ConfigurationDescriptor.ACTIVE_PARAMETER_KEY, Arrays.asList("instant"));
+        mModule =
+                new ModuleDefinition(
+                        MODULE_NAME,
+                        mTestList,
+                        mMapDeviceTargetPreparer,
+                        mMultiTargetPrepList,
+                        config);
+        assertNotNull(mModule.getModuleInvocationContext());
+        IInvocationContext moduleContext = mModule.getModuleInvocationContext();
+        assertEquals(
+                1,
+                moduleContext.getAttributes().get(ModuleDefinition.MODULE_PARAMETERIZATION).size());
+        assertEquals(
+                "instant",
+                moduleContext
+                        .getAttributes()
+                        .getUniqueMap()
+                        .get(ModuleDefinition.MODULE_PARAMETERIZATION));
+    }
+
     /**
      * Test that {@link ModuleDefinition#run(TestInformation, ITestInvocationListener)} is properly
      * going through the execution flow.
@@ -506,7 +553,7 @@
     public void testParseTokens() throws Exception {
         Configuration config = new Configuration("", "");
         ConfigurationDescriptor descriptor = config.getConfigurationDescription();
-        descriptor.addMetaData(ITestSuite.TOKEN_KEY, Arrays.asList("SIM_CARD"));
+        descriptor.addMetadata(ITestSuite.TOKEN_KEY, Arrays.asList("SIM_CARD"));
         mModule =
                 new ModuleDefinition(
                         MODULE_NAME,
diff --git a/tests/src/com/android/tradefed/testtype/suite/SuiteModuleLoaderTest.java b/tests/src/com/android/tradefed/testtype/suite/SuiteModuleLoaderTest.java
index 3df2d4b..921f675 100644
--- a/tests/src/com/android/tradefed/testtype/suite/SuiteModuleLoaderTest.java
+++ b/tests/src/com/android/tradefed/testtype/suite/SuiteModuleLoaderTest.java
@@ -311,11 +311,17 @@
         // Ensure that appropriate metadata are set on the module config descriptor
         ConfigurationDescriptor descriptor = instantModule.getConfigurationDescription();
         assertEquals(
-                "instant_app",
+                1,
+                descriptor
+                        .getAllMetaData()
+                        .get(ConfigurationDescriptor.ACTIVE_PARAMETER_KEY)
+                        .size());
+        assertEquals(
+                "instant",
                 descriptor
                         .getAllMetaData()
                         .getUniqueMap()
-                        .get(ConfigurationDescriptor.PARAMETER_KEY));
+                        .get(ConfigurationDescriptor.ACTIVE_PARAMETER_KEY));
         assertEquals("armeabi-v7a", descriptor.getAbi().getName());
     }
 
diff --git a/tests/src/com/android/tradefed/util/GCSFileDownloaderFuncTest.java b/tests/src/com/android/tradefed/util/GCSFileDownloaderFuncTest.java
index c0c5d05..8d97efb 100644
--- a/tests/src/com/android/tradefed/util/GCSFileDownloaderFuncTest.java
+++ b/tests/src/com/android/tradefed/util/GCSFileDownloaderFuncTest.java
@@ -102,6 +102,8 @@
         createFile(mStorage, FILE_CONTENT, BUCKET_NAME, mRemoteRoot, FILE_NAME1);
         createFile(mStorage, FILE_NAME2, BUCKET_NAME, mRemoteRoot, FOLDER_NAME1, FILE_NAME2);
         createFile(mStorage, FILE_NAME3, BUCKET_NAME, mRemoteRoot, FOLDER_NAME1, FILE_NAME3);
+        // Create a special case condition where folder name is also a file name.
+        createFile(mStorage, FILE_NAME3, BUCKET_NAME, mRemoteRoot, FOLDER_NAME1, FOLDER_NAME2);
         createFile(
                 mStorage,
                 FILE_NAME4,
@@ -230,7 +232,7 @@
 
     private void checkDownloadedFolder(File localFile) throws Exception {
         Assert.assertTrue(localFile.isDirectory());
-        Assert.assertEquals(3, localFile.list().length);
+        Assert.assertEquals(4, localFile.list().length);
         for (String filename : localFile.list()) {
             if (filename.equals(FILE_NAME2)) {
                 Assert.assertEquals(
@@ -242,7 +244,7 @@
                         FILE_NAME3,
                         FileUtil.readStringFromFile(
                                 new File(localFile.getAbsolutePath(), filename)));
-            } else if (filename.equals(FOLDER_NAME2)) {
+            } else if (filename.equals(FOLDER_NAME2 + "_folder")) {
                 File subFolder = new File(localFile.getAbsolutePath(), filename);
                 Assert.assertTrue(subFolder.isDirectory());
                 Assert.assertEquals(1, subFolder.list().length);
@@ -250,6 +252,9 @@
                         FILE_NAME4,
                         FileUtil.readStringFromFile(
                                 new File(subFolder.getAbsolutePath(), subFolder.list()[0])));
+            } else if (filename.equals(FOLDER_NAME2)) {
+                File fileWithFolderName = new File(localFile.getAbsolutePath(), filename);
+                Assert.assertTrue(fileWithFolderName.isFile());
             } else {
                 Assert.assertTrue(String.format("Unknonwn file %s", filename), false);
             }
diff --git a/tests/src/com/android/tradefed/util/LocalRunInstructionBuilderTest.java b/tests/src/com/android/tradefed/util/LocalRunInstructionBuilderTest.java
index 4a9d110..e861154 100644
--- a/tests/src/com/android/tradefed/util/LocalRunInstructionBuilderTest.java
+++ b/tests/src/com/android/tradefed/util/LocalRunInstructionBuilderTest.java
@@ -110,7 +110,7 @@
         ConfigurationDescriptor configDescriptor = new ConfigurationDescriptor();
         configDescriptor.setAbi(new Abi(ABI_NAME, "32"));
         configDescriptor.setModuleName(OPTION_SOURCE);
-        configDescriptor.addMetadata(ConfigurationDescriptor.PARAMETER_KEY, "instant");
+        configDescriptor.addMetadata(ConfigurationDescriptor.ACTIVE_PARAMETER_KEY, "instant");
         String instruction =
                 LocalRunInstructionBuilder.getInstruction(
                         configDescriptor, LocalTestRunner.ATEST, null);