Merge "Move LocationListenerActivity to managedprovisioning/"
diff --git a/hostsidetests/appsecurity/OWNERS b/hostsidetests/appsecurity/OWNERS
index cbfad62..927344a 100644
--- a/hostsidetests/appsecurity/OWNERS
+++ b/hostsidetests/appsecurity/OWNERS
@@ -21,7 +21,7 @@
 per-file ExternalStorageHostTest.java = jsharkey@google.com
 per-file InstantAppUserTest.java = toddke@google.com
 per-file InstantCookieHostTest.java = toddke@google.com
-per-file IsolatedSplitsTests.java = toddke@google.com
+per-file IsolatedSplitsTests.java = patb@google.com,toddke@google.com
 per-file KeySetHostTest.java = cbrubaker@google.com
 per-file ListeningPortsTest.java = cbrubaker@google.com
 per-file MajorVersionTest.java = toddke@google.com
@@ -35,7 +35,7 @@
 per-file RequestsOnlyCalendarApp22.java = moltmann@google.com
 per-file ScopedDirectoryAccessTest.java = jsharkey@google.com
 per-file SharedUserIdTest.java = toddke@google.com
-per-file SplitTests.java = toddke@google.com
+per-file SplitTests.java = patb@google.com,toddke@google.com
 per-file StorageHostTest.java = jsharkey@google.com
 per-file UseEmbeddedDexTest.java = victorhsieh@google.com
 # test apps
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/OWNERS b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/OWNERS
index 87e75ec..f17768d 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/OWNERS
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/OWNERS
@@ -1,2 +1,3 @@
 # Bug component: 533114
+patb@google.com
 toddke@google.com
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/OWNERS b/hostsidetests/appsecurity/test-apps/SplitApp/OWNERS
new file mode 100644
index 0000000..f17768d
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 533114
+patb@google.com
+toddke@google.com
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPolicyTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPolicyTests.java
index 9bba49c..3584353 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPolicyTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPolicyTests.java
@@ -150,7 +150,7 @@
 
         // Launch a resizeable activity on new secondary display.
         separateTestJournal();
-        launchActivityOnDisplay(RESIZEABLE_ACTIVITY, newDisplay.mId);
+        launchActivityOnDisplay(RESIZEABLE_ACTIVITY, WINDOWING_MODE_FULLSCREEN, newDisplay.mId);
         waitAndAssertActivityStateOnDisplay(RESIZEABLE_ACTIVITY, STATE_RESUMED, newDisplay.mId,
                 "Launched activity must be resumed");
 
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/itest_impl.h b/tests/tests/binder_ndk/libbinder_ndk_test/itest_impl.h
index 8b1a1c3..413e190 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/itest_impl.h
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/itest_impl.h
@@ -459,7 +459,7 @@
     MyExt ext2;
     ext2.a = ext->a;
     ext2.b = ext->b;
-    out_output->ext.setParcelable(&ext2);
+    out_output->ext.setParcelable(ext2);
     return ::ndk::ScopedAStatus(AStatus_newOk());
   }
 
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/test_native_aidl_client.cpp b/tests/tests/binder_ndk/libbinder_ndk_test/test_native_aidl_client.cpp
index c81f1d4..10d6b65 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/test_native_aidl_client.cpp
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/test_native_aidl_client.cpp
@@ -939,7 +939,7 @@
   MyExt myext1;
   myext1.a = 42;
   myext1.b = "mystr";
-  ep.ext.setParcelable(&myext1);
+  ep.ext.setParcelable(myext1);
   std::unique_ptr<MyExt> myext2 = ep.ext.getParcelable<MyExt>();
   EXPECT_TRUE(myext2);
   EXPECT_EQ(42, myext2->a);
@@ -962,7 +962,7 @@
   MyExt myext1;
   myext1.a = 42;
   myext1.b = "mystr";
-  ep.ext.setParcelable(&myext1);
+  ep.ext.setParcelable(myext1);
 
   ExtendableParcelable ep2;
   EXPECT_OK(iface->RepeatExtendableParcelable(ep, &ep2));
diff --git a/tools/cts-tradefed/OWNERS b/tools/cts-tradefed/OWNERS
new file mode 100644
index 0000000..5f93ef3
--- /dev/null
+++ b/tools/cts-tradefed/OWNERS
@@ -0,0 +1,17 @@
+#  Android EngProd Approvers
+guangzhu@google.com
+fdeng@google.com
+moonk@google.com
+jdesprez@google.com
+
+# Android Partner Eng Approvers
+aaronholden@google.com
+yuji@google.com
+nickrose@google.com
+
+# File Specific Approvers
+per-file Backup* = aabhinav@google.com, jstemmer@google.com, nathch@google.com, niagra@google.com, niamhfw@google.com, philippov@google.com, rthakohov@google.com, tobiast@google.com
+per-file cts-meerkat.xml = alanstokes@google.com, brufino@google.com, lus@google.com, rickywai@google.com
+per-file cts-on-csi*.xml = ycchen@google.com, hsinyichen@google.com, tyanh@google.com
+per-file csi-*.xml = ycchen@google.com, hsinyichen@google.com, tyanh@google.com
+
diff --git a/tools/cts-tradefed/tests/.classpath b/tools/cts-tradefed/tests/.classpath
new file mode 100644
index 0000000..1c238bc
--- /dev/null
+++ b/tools/cts-tradefed/tests/.classpath
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-9">
+		<attributes>
+			<attribute name="module" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry combineaccessrules="false" kind="src" path="/tradefederation">
+		<attributes>
+			<attribute name="module" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry combineaccessrules="false" kind="src" path="/cts-tradefed">
+		<attributes>
+			<attribute name="module" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/tools/cts-tradefed/tests/.project b/tools/cts-tradefed/tests/.project
new file mode 100644
index 0000000..2f00b36
--- /dev/null
+++ b/tools/cts-tradefed/tests/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>cts-tradefed-tests</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/ApkPackageNameCheck.java b/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/ApkPackageNameCheck.java
new file mode 100644
index 0000000..b50c906
--- /dev/null
+++ b/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/ApkPackageNameCheck.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2017 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.compatibility.common.tradefed.presubmit;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.compatibility.common.tradefed.targetprep.FilePusher;
+import com.android.tradefed.config.ConfigurationException;
+import com.android.tradefed.config.ConfigurationFactory;
+import com.android.tradefed.config.IConfiguration;
+import com.android.tradefed.targetprep.ITargetPreparer;
+import com.android.tradefed.targetprep.PushFilePreparer;
+import com.android.tradefed.targetprep.TestAppInstallSetup;
+import com.android.tradefed.testtype.IRemoteTest;
+import com.android.tradefed.testtype.InstrumentationTest;
+import com.android.tradefed.util.AaptParser;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Class to validate tests Apks in testcases/
+ */
+@RunWith(JUnit4.class)
+public class ApkPackageNameCheck {
+
+    private static final Set<String> EXCEPTION_LIST = new HashSet<>();
+    static {
+        // TODO: Remove exception when their package have been fixed.
+        EXCEPTION_LIST.add("android.app.cts");
+        EXCEPTION_LIST.add("android.systemui.cts");
+    }
+
+    /**
+     * We ensure that no apk with same package names may be installed. Otherwise it may results in
+     * conflicts.
+     */
+    @Test
+    public void testApkPackageNames() throws Exception {
+        String ctsRoot = System.getProperty("CTS_ROOT");
+        File testcases = new File(ctsRoot, "/android-cts/testcases/");
+        if (!testcases.exists()) {
+            fail(String.format("%s does not exists", testcases));
+            return;
+        }
+        File[] listConfig = testcases.listFiles(new FilenameFilter() {
+            @Override
+            public boolean accept(File dir, String name) {
+                if (name.endsWith(".config")) {
+                    return true;
+                }
+                return false;
+            }
+        });
+        assertTrue(listConfig.length > 0);
+        // We check all apk installed by all modules
+        Map<String, String> packageNames = new HashMap<>();
+
+        for (File config : listConfig) {
+            IConfiguration c = ConfigurationFactory.getInstance()
+                    .createConfigurationFromArgs(new String[] {config.getAbsolutePath()});
+            // For each config, we check all the apk it's going to install
+            List<File> apkNames = new ArrayList<>();
+            List<String> packageListNames = new ArrayList<>();
+            for (ITargetPreparer prep : c.getTargetPreparers()) {
+                if (prep instanceof TestAppInstallSetup) {
+                    apkNames.addAll(((TestAppInstallSetup) prep).getTestsFileName());
+                }
+                // Ensure the files requested to be pushed exist.
+                if (prep instanceof FilePusher && ((FilePusher) prep).shouldAppendBitness()) {
+                    for (File f : ((PushFilePreparer) prep).getPushSpecs(null).values()) {
+                        String path = f.getPath();
+                        if (!new File(testcases, path + "32").exists()
+                                || !new File(testcases, path + "64").exists()) {
+                            // TODO: Enforce should abort on failure is True in CTS
+                            if (((FilePusher) prep).shouldAbortOnFailure()) {
+                                fail(
+                                        String.format(
+                                                "File %s[32/64] wasn't found in testcases/ while "
+                                                        + "it's expected to be pushed as part of "
+                                                        + "%s",
+                                                path, config.getName()));
+                            }
+                        }
+                    }
+                } else if (prep instanceof PushFilePreparer) {
+                    for (File f : ((PushFilePreparer) prep).getPushSpecs(null).values()) {
+                        String path = f.getPath();
+                        if (!new File(testcases, path).exists()) {
+                            // TODO: Enforce should abort on failure is True in CTS
+                            if (((PushFilePreparer) prep).shouldAbortOnFailure()) {
+                                fail(
+                                        String.format(
+                                                "File %s wasn't found in testcases/ while it's "
+                                                        + "expected to be pushed as part of %s",
+                                                path, config.getName()));
+                            }
+                        }
+                    }
+                }
+            }
+
+            for (File apk : apkNames) {
+                String apkName = apk.getName();
+                File apkFile = new File(testcases, apkName);
+                if (!apkFile.exists()) {
+                    fail(String.format("Module %s is trying to install %s which does not "
+                            + "exists in testcases/", config.getName(), apkFile));
+                }
+                AaptParser res = AaptParser.parse(apkFile);
+                assertNotNull(res);
+                String packageName = res.getPackageName();
+                String put = packageNames.put(packageName, apkName);
+                packageListNames.add(packageName);
+                // The package already exists and it's a different apk
+                if (put != null && !apkName.equals(put) && !EXCEPTION_LIST.contains(packageName)) {
+                    fail(String.format("Module %s: Package name '%s' from apk '%s' was already "
+                            + "added by previous apk '%s'.",
+                            config.getName(), packageName, apkName, put));
+                }
+            }
+
+            // Catch a test trying to run something it doesn't install.
+            List<IRemoteTest> tests = c.getTests();
+            for (IRemoteTest test : tests) {
+                if (test instanceof InstrumentationTest) {
+                    InstrumentationTest instrumentationTest = (InstrumentationTest) test;
+                    if (instrumentationTest.getPackageName() != null) {
+                        if (!packageListNames.contains(instrumentationTest.getPackageName())) {
+                            throw new ConfigurationException(
+                                    String.format("Module %s requests to run '%s' but it's not "
+                                        + "part of any apks.",
+                                        config.getName(), instrumentationTest.getPackageName()));
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/CtsConfigLoadingTest.java b/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/CtsConfigLoadingTest.java
new file mode 100644
index 0000000..625da2f
--- /dev/null
+++ b/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/CtsConfigLoadingTest.java
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2017 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.compatibility.common.tradefed.presubmit;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.compatibility.common.tradefed.targetprep.ApkInstaller;
+import com.android.compatibility.common.tradefed.targetprep.PreconditionPreparer;
+import com.android.compatibility.common.tradefed.testtype.JarHostTest;
+import com.android.tradefed.build.FolderBuildInfo;
+import com.android.tradefed.config.ConfigurationDescriptor;
+import com.android.tradefed.config.ConfigurationException;
+import com.android.tradefed.config.ConfigurationFactory;
+import com.android.tradefed.config.IConfiguration;
+import com.android.tradefed.invoker.ExecutionFiles.FilesKey;
+import com.android.tradefed.invoker.TestInformation;
+import com.android.tradefed.invoker.shard.token.TokenProperty;
+import com.android.tradefed.targetprep.ITargetPreparer;
+import com.android.tradefed.testtype.AndroidJUnitTest;
+import com.android.tradefed.testtype.HostTest;
+import com.android.tradefed.testtype.IRemoteTest;
+import com.android.tradefed.testtype.ITestFilterReceiver;
+import com.android.tradefed.testtype.suite.ITestSuite;
+import com.android.tradefed.testtype.suite.params.ModuleParameters;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Test that configuration in CTS can load and have expected properties.
+ */
+@RunWith(JUnit4.class)
+public class CtsConfigLoadingTest {
+
+    private static final String METADATA_COMPONENT = "component";
+    private static final Set<String> KNOWN_COMPONENTS =
+            new HashSet<>(
+                    Arrays.asList(
+                            // modifications to the list below must be reviewed
+                            "abuse",
+                            "art",
+                            "auth",
+                            "auto",
+                            "autofill",
+                            "backup",
+                            "bionic",
+                            "bluetooth",
+                            "camera",
+                            "contentcapture",
+                            "deviceinfo",
+                            "deqp",
+                            "devtools",
+                            "framework",
+                            "graphics",
+                            "hdmi",
+                            "inputmethod",
+                            "libcore",
+                            "location",
+                            "media",
+                            "metrics",
+                            "misc",
+                            "mocking",
+                            "networking",
+                            "neuralnetworks",
+                            "print",
+                            "renderscript",
+                            "security",
+                            "statsd",
+                            "systems",
+                            "sysui",
+                            "telecom",
+                            "tv",
+                            "uitoolkit",
+                            "vr",
+                            "webview",
+                            "wifi"));
+    private static final Set<String> KNOWN_MISC_MODULES =
+            new HashSet<>(
+                    Arrays.asList(
+                            // Modifications to the list below must be approved by someone in
+                            // test/suite_harness/OWNERS.
+                            "CtsSliceTestCases.config",
+                            "CtsSampleDeviceTestCases.config",
+                            "CtsUsbTests.config",
+                            "CtsGpuToolsHostTestCases.config",
+                            "CtsEdiHostTestCases.config",
+                            "CtsClassLoaderFactoryPathClassLoaderTestCases.config",
+                            "CtsSampleHostTestCases.config",
+                            "CtsHardwareTestCases.config",
+                            "CtsMonkeyTestCases.config",
+                            "CtsAndroidAppTestCases.config",
+                            "CtsClassLoaderFactoryInMemoryDexClassLoaderTestCases.config",
+                            "CtsAppComponentFactoryTestCases.config",
+                            "CtsSeccompHostTestCases.config"));
+
+    /**
+     * List of the officially supported runners in CTS, they meet all the interfaces criteria as
+     * well as support sharding very well. Any new addition should go through a review.
+     */
+    private static final Set<String> SUPPORTED_CTS_TEST_TYPE = new HashSet<>(Arrays.asList(
+            // Cts runners
+            "com.android.compatibility.common.tradefed.testtype.JarHostTest",
+            "com.android.compatibility.testtype.DalvikTest",
+            "com.android.compatibility.testtype.LibcoreTest",
+            "com.drawelements.deqp.runner.DeqpTestRunner",
+            // Tradefed runners
+            "com.android.tradefed.testtype.AndroidJUnitTest",
+            "com.android.tradefed.testtype.HostTest",
+            "com.android.tradefed.testtype.GTest"
+    ));
+
+    /**
+     * In Most cases we impose the usage of the AndroidJUnitRunner because it supports all the
+     * features required (filtering, sharding, etc.). We do not typically expect people to need a
+     * different runner.
+     */
+    private static final Set<String> ALLOWED_INSTRUMENTATION_RUNNER_NAME = new HashSet<>();
+    static {
+        ALLOWED_INSTRUMENTATION_RUNNER_NAME.add("android.support.test.runner.AndroidJUnitRunner");
+        ALLOWED_INSTRUMENTATION_RUNNER_NAME.add("androidx.test.runner.AndroidJUnitRunner");
+    }
+    private static final Set<String> RUNNER_EXCEPTION = new HashSet<>();
+    static {
+        // Used for a bunch of system-api cts tests
+        RUNNER_EXCEPTION.add("repackaged.android.test.InstrumentationTestRunner");
+        // Used by a UiRendering scenario where an activity is persisted between tests
+        RUNNER_EXCEPTION.add("android.uirendering.cts.runner.UiRenderingRunner");
+    }
+
+    /**
+     * Families of module parameterization that MUST be specified explicitly in the module
+     * AndroidTest.xml.
+     */
+    private static final Set<String> MANDATORY_PARAMETERS_FAMILY = new HashSet<>();
+
+    static {
+        MANDATORY_PARAMETERS_FAMILY.add(ModuleParameters.INSTANT_APP_FAMILY);
+        MANDATORY_PARAMETERS_FAMILY.add(ModuleParameters.MULTI_ABI_FAMILY);
+        MANDATORY_PARAMETERS_FAMILY.add(ModuleParameters.SECONDARY_USER_FAMILY);
+    }
+
+    /**
+     * AllowList to start enforcing metadata on modules. No additional entry will be allowed! This
+     * is meant to burn down the remaining modules definition.
+     */
+    private static final Set<String> ALLOWLIST_MODULE_PARAMETERS = new HashSet<>();
+
+    static {
+        ALLOWLIST_MODULE_PARAMETERS.add("CtsAccessibilityServiceTestCases.config");
+        ALLOWLIST_MODULE_PARAMETERS.add("CtsCarrierApiTestCases.config");
+        ALLOWLIST_MODULE_PARAMETERS.add("CtsMediaTestCases.config");
+        ALLOWLIST_MODULE_PARAMETERS.add("CtsMediaV2TestCases.config");
+        ALLOWLIST_MODULE_PARAMETERS.add("CtsOpenGlPerfTestCases.config");
+        ALLOWLIST_MODULE_PARAMETERS.add("CtsOsTestCases.config");
+        ALLOWLIST_MODULE_PARAMETERS.add("CtsPermission2TestCases.config");
+        ALLOWLIST_MODULE_PARAMETERS.add("CtsPermissionTestCases.config");
+        ALLOWLIST_MODULE_PARAMETERS.add("CtsProviderUiTestCases.config");
+    }
+
+    /**
+     * Test that configuration shipped in Tradefed can be parsed.
+     * -> Exclude deprecated ApkInstaller.
+     * -> Check if host-side tests are non empty.
+     */
+    @Test
+    public void testConfigurationLoad() throws Exception {
+        String ctsRoot = System.getProperty("CTS_ROOT");
+        File testcases = new File(ctsRoot, "/android-cts/testcases/");
+        if (!testcases.exists()) {
+            fail(String.format("%s does not exists", testcases));
+            return;
+        }
+        File[] listConfig = testcases.listFiles(new FilenameFilter() {
+            @Override
+            public boolean accept(File dir, String name) {
+                if (name.endsWith(".config")) {
+                    return true;
+                }
+                return false;
+            }
+        });
+        assertTrue(listConfig.length > 0);
+        // Create a FolderBuildInfo to similate the CompatibilityBuildProvider
+        FolderBuildInfo stubFolder = new FolderBuildInfo("-1", "-1");
+        stubFolder.setRootDir(new File(ctsRoot));
+        stubFolder.addBuildAttribute(CompatibilityBuildHelper.SUITE_NAME, "CTS");
+        stubFolder.addBuildAttribute("ROOT_DIR", ctsRoot);
+        TestInformation stubTestInfo = TestInformation.newBuilder().build();
+        stubTestInfo.executionFiles().put(FilesKey.TESTS_DIRECTORY, new File(ctsRoot));
+
+        List<String> missingMandatoryParameters = new ArrayList<>();
+        // We expect to be able to load every single config in testcases/
+        for (File config : listConfig) {
+            IConfiguration c = ConfigurationFactory.getInstance()
+                    .createConfigurationFromArgs(new String[] {config.getAbsolutePath()});
+            // Ensure the deprecated ApkInstaller is not used anymore.
+            for (ITargetPreparer prep : c.getTargetPreparers()) {
+                if (prep.getClass().isAssignableFrom(ApkInstaller.class)) {
+                    throw new ConfigurationException(
+                            String.format("%s: Use com.android.tradefed.targetprep.suite."
+                                    + "SuiteApkInstaller instead of com.android.compatibility."
+                                    + "common.tradefed.targetprep.ApkInstaller, options will be "
+                                    + "the same.", config));
+                }
+                if (prep.getClass().isAssignableFrom(PreconditionPreparer.class)) {
+                    throw new ConfigurationException(
+                            String.format(
+                                    "%s: includes a PreconditionPreparer (%s) which is not allowed"
+                                            + " in modules.",
+                                    config.getName(), prep.getClass()));
+                }
+            }
+            // We can ensure that Host side tests are not empty.
+            for (IRemoteTest test : c.getTests()) {
+                // Check that all the tests runners are well supported.
+                if (!SUPPORTED_CTS_TEST_TYPE.contains(test.getClass().getCanonicalName())) {
+                    throw new ConfigurationException(
+                            String.format(
+                                    "testtype %s is not officially supported by CTS. "
+                                            + "The supported ones are: %s",
+                                    test.getClass().getCanonicalName(), SUPPORTED_CTS_TEST_TYPE));
+                }
+                if (test instanceof HostTest) {
+                    HostTest hostTest = (HostTest) test;
+                    // We inject a made up folder so that it can find the tests.
+                    hostTest.setBuild(stubFolder);
+                    hostTest.setTestInformation(stubTestInfo);
+                    int testCount = hostTest.countTestCases();
+                    if (testCount == 0) {
+                        throw new ConfigurationException(
+                                String.format("%s: %s reports 0 test cases.",
+                                        config.getName(), test));
+                    }
+                }
+                // Tests are expected to implement that interface.
+                if (!(test instanceof ITestFilterReceiver)) {
+                    throw new IllegalArgumentException(String.format(
+                            "Test in module %s must implement ITestFilterReceiver.",
+                            config.getName()));
+                }
+                // Ensure that the device runner is the AJUR one if explicitly specified.
+                if (test instanceof AndroidJUnitTest) {
+                    AndroidJUnitTest instru = (AndroidJUnitTest) test;
+                    if (instru.getRunnerName() != null &&
+                            !ALLOWED_INSTRUMENTATION_RUNNER_NAME.contains(instru.getRunnerName())) {
+                        // Some runner are exempt
+                        if (!RUNNER_EXCEPTION.contains(instru.getRunnerName())) {
+                            throw new ConfigurationException(
+                                    String.format("%s: uses '%s' instead of on of '%s' that are "
+                                            + "expected", config.getName(), instru.getRunnerName(),
+                                            ALLOWED_INSTRUMENTATION_RUNNER_NAME));
+                        }
+                    }
+                }
+            }
+            ConfigurationDescriptor cd = c.getConfigurationDescription();
+            Assert.assertNotNull(config + ": configuration descriptor is null", cd);
+            List<String> component = cd.getMetaData(METADATA_COMPONENT);
+            Assert.assertNotNull(String.format("Missing module metadata field \"component\", "
+                    + "please add the following line to your AndroidTest.xml:\n"
+                    + "<option name=\"config-descriptor:metadata\" key=\"component\" "
+                    + "value=\"...\" />\nwhere \"value\" must be one of: %s\n"
+                    + "config: %s", KNOWN_COMPONENTS, config),
+                    component);
+            Assert.assertEquals(String.format("Module config contains more than one \"component\" "
+                    + "metadata field: %s\nconfig: %s", component, config),
+                    1, component.size());
+            String cmp = component.get(0);
+            Assert.assertTrue(String.format("Module config contains unknown \"component\" metadata "
+                    + "field \"%s\", supported ones are: %s\nconfig: %s",
+                    cmp, KNOWN_COMPONENTS, config), KNOWN_COMPONENTS.contains(cmp));
+
+            if ("misc".equals(cmp)) {
+                String configFileName = config.getName();
+                Assert.assertTrue(
+                        String.format(
+                                "Adding new module %s to \"misc\" component is restricted, "
+                                        + "please pick a component that your module fits in",
+                                configFileName),
+                        KNOWN_MISC_MODULES.contains(configFileName));
+            }
+
+            // Check that specified parameters are expected
+            boolean res =
+                    checkModuleParameters(
+                            config.getName(), cd.getMetaData(ITestSuite.PARAMETER_KEY));
+            if (!res) {
+                missingMandatoryParameters.add(config.getName());
+            }
+            // Check that specified tokens are expected
+            checkTokens(config.getName(), cd.getMetaData(ITestSuite.TOKEN_KEY));
+
+            // Ensure each CTS module is tagged with <option name="test-suite-tag" value="cts" />
+            Assert.assertTrue(String.format(
+                    "Module config %s does not contains "
+                    + "'<option name=\"test-suite-tag\" value=\"cts\" />'", config.getName()),
+                    cd.getSuiteTags().contains("cts"));
+
+            // Check not-shardable: JarHostTest cannot create empty shards so it should never need
+            // to be not-shardable.
+            if (cd.isNotShardable()) {
+                for (IRemoteTest test : c.getTests()) {
+                    if (test.getClass().isAssignableFrom(JarHostTest.class)) {
+                        throw new ConfigurationException(
+                                String.format("config: %s. JarHostTest does not need the "
+                                    + "not-shardable option.", config.getName()));
+                    }
+                }
+            }
+            // Ensure options have been set
+            c.validateOptions();
+        }
+
+        // Exempt the allow list
+        missingMandatoryParameters.removeAll(ALLOWLIST_MODULE_PARAMETERS);
+        // Ensure the mandatory fields are filled
+        if (!missingMandatoryParameters.isEmpty()) {
+            String msg =
+                    String.format(
+                            "The following %s modules are missing some of the mandatory "
+                                    + "parameters [instant_app, not_instant_app, "
+                                    + "multi_abi, not_multi_abi, "
+                                    + "secondary_user, not_secondary_user]: '%s'",
+                            missingMandatoryParameters.size(), missingMandatoryParameters);
+            throw new ConfigurationException(msg);
+        }
+    }
+
+    /** Test that all parameter metadata can be resolved. */
+    private boolean checkModuleParameters(String configName, List<String> parameters)
+            throws ConfigurationException {
+        if (parameters == null) {
+            return false;
+        }
+        Map<String, Boolean> families = createFamilyCheckMap();
+        for (String param : parameters) {
+            try {
+                ModuleParameters p = ModuleParameters.valueOf(param.toUpperCase());
+                if (families.containsKey(p.getFamily())) {
+                    families.put(p.getFamily(), true);
+                }
+            } catch (IllegalArgumentException e) {
+                throw new ConfigurationException(
+                        String.format("Config: %s includes an unknown parameter '%s'.",
+                                configName, param));
+            }
+        }
+        if (families.containsValue(false)) {
+            return false;
+        }
+        return true;
+    }
+
+    /** Test that all tokens can be resolved. */
+    private void checkTokens(String configName, List<String> tokens) throws ConfigurationException {
+        if (tokens == null) {
+            return;
+        }
+        for (String token : tokens) {
+            try {
+                TokenProperty.valueOf(token.toUpperCase());
+            } catch (IllegalArgumentException e) {
+                throw new ConfigurationException(
+                        String.format(
+                                "Config: %s includes an unknown token '%s'.", configName, token));
+            }
+        }
+    }
+
+    private Map<String, Boolean> createFamilyCheckMap() {
+        Map<String, Boolean> families = new HashMap<>();
+        for (String family : MANDATORY_PARAMETERS_FAMILY) {
+            families.put(family, false);
+        }
+        return families;
+    }
+}
diff --git a/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/DupFileTest.java b/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/DupFileTest.java
new file mode 100644
index 0000000..7701279
--- /dev/null
+++ b/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/DupFileTest.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2018 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.compatibility.common.tradefed.presubmit;
+
+import static org.junit.Assert.fail;
+
+import com.android.tradefed.config.ConfigurationException;
+
+import com.google.common.collect.ImmutableSet;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+ * Test to check for duplicate files in different jars and prevent the same dependencies of being
+ * included several time (which might result in version conflicts).
+ */
+@RunWith(JUnit4.class)
+public class DupFileTest {
+
+    // We ignore directories part of the common java and google packages.
+    private static final String[] IGNORE_DIRS =
+            new String[] {
+                "android/",
+                "javax/annotation/",
+                "com/google/protobuf/",
+                "kotlin/",
+                "perfetto/protos/"
+            };
+    // Temporarily exclude some Tradefed jar while we work on unbundling them.
+    private static final Set<String> IGNORE_JARS =
+            ImmutableSet.of("tradefed-no-fwk.jar", "tradefed-test-framework.jar",
+                    "compatibility-tradefed-tests.jar",
+                    "compatibility-common-util-tests.jar", "compatibility-host-util-tests.jar");
+
+    /** test if there are duplicate files in different jars. */
+    @Test
+    public void testDupFilesExist() throws Exception {
+        // Get list of jars.
+        List<File> jars = getListOfBuiltJars();
+
+        // Create map of files to jars.
+        Map<String, List<String>> filesToJars = getMapOfFilesAndJars(jars);
+
+        // Check if there are any files with the same name in diff jars.
+        int dupedFiles = 0;
+        StringBuilder dupedFilesSummary = new StringBuilder();
+        for (Map.Entry<String, List<String>> entry : filesToJars.entrySet()) {
+            String file = entry.getKey();
+            List<String> jarFiles = entry.getValue();
+
+            if (jarFiles.size() != 1) {
+                dupedFiles++;
+                dupedFilesSummary.append(file + ": " + jarFiles.toString() + "\n");
+            }
+        }
+
+        if (dupedFiles != 0) {
+            fail(
+                    String.format(
+                            "%d files are duplicated in different jars:\n%s",
+                            dupedFiles, dupedFilesSummary.toString()));
+        }
+    }
+
+    /** Create map of file to jars */
+    private Map<String, List<String>> getMapOfFilesAndJars(List<File> jars) throws IOException {
+        Map<String, List<String>> map = new LinkedHashMap<String, List<String>>();
+        JarFile jarFile;
+        List<String> jarFileList;
+        // Map all the files from all the jars.
+        for (File jar : jars) {
+            if (IGNORE_JARS.contains(jar.getName())) {
+                continue;
+            }
+            jarFile = new JarFile(jar);
+            jarFileList = getListOfFiles(jarFile);
+            jarFile.close();
+
+            // Add in the jar file to the map.
+            for (String file : jarFileList) {
+                if (!map.containsKey(file)) {
+                    map.put(file, new LinkedList<String>());
+                }
+
+                map.get(file).add(jar.getName());
+            }
+        }
+        return map;
+    }
+
+    /** Get the list of jars specified in the path. */
+    private List<File> getListOfBuiltJars() throws ConfigurationException {
+        String classpathStr = System.getProperty("java.class.path");
+        if (classpathStr == null) {
+            throw new ConfigurationException(
+                    "Could not find the classpath property: java.class.path");
+        }
+        List<File> listOfJars = new ArrayList<File>();
+        for (String jar : classpathStr.split(":")) {
+            File jarFile = new File(jar);
+            if (jarFile.exists()) {
+                listOfJars.add(jarFile);
+            }
+        }
+        return listOfJars;
+    }
+
+    /** Return the list of files in the jar. */
+    private List<String> getListOfFiles(JarFile jar) {
+        List<String> files = new ArrayList<String>();
+        Enumeration<JarEntry> e = jar.entries();
+        while (e.hasMoreElements()) {
+            JarEntry entry = e.nextElement();
+            String filename = entry.getName();
+            if (checkThisFile(filename)) {
+                files.add(filename);
+            }
+        }
+        return files;
+    }
+
+    /** Check if we should add this file to list of files. We only want to check for classes. */
+    private Boolean checkThisFile(String filename) {
+        if (!filename.endsWith(".class")) {
+            return false;
+        }
+
+        for (String skipDir : IGNORE_DIRS) {
+            if (filename.startsWith(skipDir)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/PresubmitSetupValidation.java b/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/PresubmitSetupValidation.java
new file mode 100644
index 0000000..4eb48fc
--- /dev/null
+++ b/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/PresubmitSetupValidation.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2016 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.compatibility.common.tradefed.presubmit;
+
+import com.android.tradefed.config.ConfigurationException;
+import com.android.tradefed.config.ConfigurationFactory;
+import com.android.tradefed.config.IConfigurationFactory;
+import com.android.tradefed.log.LogUtil.CLog;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests that validate the CTS presubmit setup to ensure no CL will break the presubmit setup
+ * itself.
+ */
+public class PresubmitSetupValidation extends TestCase {
+    private static final String PRESUBMIT_CTS_UNIT_TESTS = "cts-unit-tests";
+
+    /**
+     * Test that the base cts unit tests configuration is still working, and has a reporter
+     * template placeholder.
+     */
+    public void testCtsPresubmit_unit_tests() {
+        IConfigurationFactory factory = ConfigurationFactory.getInstance();
+        String[] presubmitCommand = {PRESUBMIT_CTS_UNIT_TESTS, "--template:map", "reporters=empty"};
+        try {
+            factory.createConfigurationFromArgs(presubmitCommand);
+        } catch (ConfigurationException e) {
+            CLog.e(e);
+            fail(String.format("ConfigException '%s': One of your change is breaking the presubmit "
+                    + "CTS unit tests configuration.", e.getMessage()));
+        }
+    }
+
+    /**
+     * Test to ensure that Zip dependency on the Apache Commons Compress coming from TradeFed is
+     * properly setup. This dependency is required for some utilities of TradeFed to work.
+     */
+    public void testDependencyCommonsCompress() throws Exception {
+        ClassLoader loader = ClassLoader.getSystemClassLoader();
+        // This will throw an exception if dependency isn't met.
+        loader.loadClass("org.apache.commons.compress.archivers.zip.ZipFile");
+    }
+}
diff --git a/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/ValidateTestsAbi.java b/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/ValidateTestsAbi.java
new file mode 100644
index 0000000..9fd38ad
--- /dev/null
+++ b/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/ValidateTestsAbi.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2017 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.compatibility.common.tradefed.presubmit;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.tradefed.testtype.suite.TestSuiteInfo;
+import com.android.tradefed.util.AaptParser;
+import com.android.tradefed.util.AbiUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * Tests to validate that the build is containing usable test artifact.
+ */
+@RunWith(JUnit4.class)
+public class ValidateTestsAbi {
+
+    private static final Set<String> MODULE_EXCEPTIONS = new HashSet<>();
+    static {
+        /**
+         *  This particular module is shipping all its dependencies in all abis with prebuilt stuff.
+         *  Excluding it for now to have the test setup.
+         */
+        MODULE_EXCEPTIONS.add("CtsSplitApp");
+
+        /**
+         *  This module tests for security vulnerabilities when installing attacker-devised APKs.
+         */
+        MODULE_EXCEPTIONS.add("CtsCorruptApkTests");
+
+        /**
+         * This module tests for installations of packages that have only 32-bit native libraries
+         * and extract native libraries.
+         */
+        MODULE_EXCEPTIONS.add("CtsExtractNativeLibsAppTrue32");
+
+        /**
+         * This module tests for installations of packages that have only 64-bit native libraries
+         * and extract native libraries.
+         */
+        MODULE_EXCEPTIONS.add("CtsExtractNativeLibsAppTrue64");
+        /**
+         * This module tests for installations of packages that have only 32-bit native libraries
+         * and embed native libraries.
+         */
+        MODULE_EXCEPTIONS.add("CtsExtractNativeLibsAppFalse32");
+
+        /**
+         * This module tests for installations of packages that have only 64-bit native libraries
+         * and embed native libraries.
+         */
+        MODULE_EXCEPTIONS.add("CtsExtractNativeLibsAppFalse64");
+    }
+
+    private static final Set<String> BINARY_EXCEPTIONS = new HashSet<>();
+    static {
+        /**
+         * This binary is a host side helper, so we do not need to check it.
+         */
+        BINARY_EXCEPTIONS.add("sepolicy-analyze");
+    }
+
+    /**
+     * Test that all apks have the same supported abis.
+     * Sometimes, if a module is missing LOCAL_MULTILIB := both, we will end up with only one of
+     * the two abis required and the second one will fail.
+     */
+    @Test
+    public void testApksAbis() {
+        String ctsRoot = System.getProperty("CTS_ROOT");
+        File testcases = new File(ctsRoot, "/android-cts/testcases/");
+        if (!testcases.exists()) {
+            fail(String.format("%s does not exists", testcases));
+            return;
+        }
+        File[] listApks = testcases.listFiles(new FilenameFilter() {
+            @Override
+            public boolean accept(File dir, String name) {
+                for (String module : MODULE_EXCEPTIONS) {
+                    if (name.startsWith(module)) {
+                        return false;
+                    }
+                }
+
+                return name.endsWith(".apk");
+            }
+        });
+        assertTrue(listApks.length > 0);
+        int maxAbi = 0;
+        Map<String, Integer> apkToAbi = new HashMap<>();
+
+        for (File testApk : listApks) {
+            AaptParser result = AaptParser.parse(testApk);
+            // Retry as we have seen flake with aapt sometimes.
+            if (result == null) {
+                for (int i = 0; i < 2; i++) {
+                    result = AaptParser.parse(testApk);
+                    if (result != null) {
+                        break;
+                    }
+                }
+                // If still couldn't parse the apk
+                if (result == null) {
+                    fail(String.format("Fail to run 'aapt dump badging %s'",
+                            testApk.getAbsolutePath()));
+                }
+            }
+            // We only check the apk that have native code
+            if (!result.getNativeCode().isEmpty()) {
+                List<String> supportedAbiApk = result.getNativeCode();
+                Set<String> buildTarget = AbiUtils.getAbisForArch(
+                        TestSuiteInfo.getInstance().getTargetArchs().get(0));
+                // first check, all the abis are supported
+                for (String abi : supportedAbiApk) {
+                    if (!buildTarget.contains(abi)) {
+                        fail(String.format("apk %s %s does not support our abis [%s]",
+                                testApk.getName(), supportedAbiApk, buildTarget));
+                    }
+                }
+                apkToAbi.put(testApk.getName(), supportedAbiApk.size());
+                maxAbi = Math.max(maxAbi, supportedAbiApk.size());
+            }
+        }
+
+        // We do a second pass to make sure nobody is short on abi
+        for (Entry<String, Integer> apk : apkToAbi.entrySet()) {
+            if (apk.getValue() < maxAbi) {
+                fail(String.format("apk %s only has %s abi when it should have %s", apk.getKey(),
+                        apk.getValue(), maxAbi));
+            }
+        }
+    }
+
+    /**
+     * Test that when CTS has multiple abis, we have binary for each ABI. In this case the abi will
+     * be the same with different bitness (only case supported by build system).
+     * <p/>
+     * If there is only one bitness, then we check that it's the right one.
+     */
+    @Test
+    public void testBinariesAbis() {
+        String ctsRoot = System.getProperty("CTS_ROOT");
+        File testcases = new File(ctsRoot, "/android-cts/testcases/");
+        if (!testcases.exists()) {
+            fail(String.format("%s does not exist", testcases));
+            return;
+        }
+        String[] listBinaries = testcases.list(new FilenameFilter() {
+            @Override
+            public boolean accept(File dir, String name) {
+                if (name.contains(".")) {
+                    return false;
+                }
+                if (BINARY_EXCEPTIONS.contains(name)) {
+                    return false;
+                }
+                File file = new File(dir, name);
+                if (file.isDirectory()) {
+                    return false;
+                }
+                if (!file.canExecute()) {
+                    return false;
+                }
+                return true;
+            }
+        });
+        assertTrue(listBinaries.length > 0);
+        List<String> orderedList = Arrays.asList(listBinaries);
+        // we sort to have binary starting with same name, next to each other. The last two
+        // characters of their name with be the bitness (32 or 64).
+        Collections.sort(orderedList);
+        Set<String> buildTarget = AbiUtils.getAbisForArch(
+                TestSuiteInfo.getInstance().getTargetArchs().get(0));
+        // We expect one binary per abi of CTS, they should be appended with 32 or 64
+        for (int i = 0; i < orderedList.size(); i=i + buildTarget.size()) {
+            List<String> subSet = orderedList.subList(i, i + buildTarget.size());
+            if (subSet.size() > 1) {
+                String base = subSet.get(0).substring(0, subSet.get(0).length() - 2);
+                for (int j = 0; j < subSet.size(); j++) {
+                    assertEquals(base, subSet.get(j).substring(0, subSet.get(j).length() - 2));
+                }
+            } else {
+                String bitness = AbiUtils.getBitness(buildTarget.iterator().next());
+                assertTrue(subSet.get(i).endsWith(bitness));
+            }
+        }
+    }
+}
diff --git a/tools/cts-tradefed/tests/src/com/android/compatibility/tradefed/CtsTradefedTest.java b/tools/cts-tradefed/tests/src/com/android/compatibility/tradefed/CtsTradefedTest.java
index 5d5df59..8bab842 100644
--- a/tools/cts-tradefed/tests/src/com/android/compatibility/tradefed/CtsTradefedTest.java
+++ b/tools/cts-tradefed/tests/src/com/android/compatibility/tradefed/CtsTradefedTest.java
@@ -15,20 +15,27 @@
  */
 package com.android.compatibility.tradefed;
 
+import static org.junit.Assert.assertEquals;
+
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildProvider;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.config.OptionSetter;
 import com.android.tradefed.util.FileUtil;
 
-import junit.framework.TestCase;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 
 import java.io.File;
 
 /**
  * Tests for cts-tradefed.
  */
-public class CtsTradefedTest extends TestCase {
+@RunWith(JUnit4.class)
+public class CtsTradefedTest {
 
     private static final String PROPERTY_NAME = "CTS_ROOT";
     private static final String SUITE_FULL_NAME = "Compatibility Test Suite";
@@ -38,20 +45,19 @@
 
     private String mOriginalProperty = null;
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
+    @Before
+    public void setUp() throws Exception {
         mOriginalProperty = System.getProperty(PROPERTY_NAME);
     }
 
-    @Override
-    protected void tearDown() throws Exception {
+    @After
+    public void tearDown() throws Exception {
         if (mOriginalProperty != null) {
             System.setProperty(PROPERTY_NAME, mOriginalProperty);
         }
-        super.tearDown();
     }
 
+    @Test
     public void testSuiteInfoLoad() throws Exception {
         // Test the values in the manifest can be loaded
         File root = FileUtil.createTempDir("root");
diff --git a/tools/cts-tradefed/tests/src/com/android/compatibility/tradefed/CtsUnitTests.java b/tools/cts-tradefed/tests/src/com/android/compatibility/tradefed/CtsUnitTests.java
new file mode 100644
index 0000000..d989014
--- /dev/null
+++ b/tools/cts-tradefed/tests/src/com/android/compatibility/tradefed/CtsUnitTests.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 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.compatibility.tradefed;
+
+import com.android.compatibility.common.tradefed.presubmit.ApkPackageNameCheck;
+import com.android.compatibility.common.tradefed.presubmit.CtsConfigLoadingTest;
+import com.android.compatibility.common.tradefed.presubmit.DupFileTest;
+import com.android.compatibility.common.tradefed.presubmit.PresubmitSetupValidation;
+import com.android.compatibility.common.tradefed.presubmit.ValidateTestsAbi;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+
+/**
+ * A test suite for all compatibility tradefed unit tests.
+ *
+ * <p>All tests listed here should be self-contained, and do not require any external dependencies.
+ */
+@RunWith(Suite.class)
+@SuiteClasses({
+    // base
+    CtsTradefedTest.class,
+
+    // presubmit
+    ApkPackageNameCheck.class,
+    CtsConfigLoadingTest.class,
+    DupFileTest.class,
+    PresubmitSetupValidation.class,
+    ValidateTestsAbi.class,
+})
+public class CtsUnitTests {
+    // Empty on purpose
+}