enable metadata embedding in CTS module config

Bug: 36140955
Bug: 35360169
Test: added new test cases
Change-Id: I2aa6891522fef3a493c3dd3502397ef51ae57673
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
index 037801e..e0ba310 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
@@ -37,6 +37,8 @@
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.DeviceUnresponsiveException;
 import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.invoker.IInvocationContext;
+import com.android.tradefed.invoker.InvocationContext;
 import com.android.tradefed.log.ITestLogger;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.result.ITestInvocationListener;
@@ -48,6 +50,7 @@
 import com.android.tradefed.testtype.IAbi;
 import com.android.tradefed.testtype.IBuildReceiver;
 import com.android.tradefed.testtype.IDeviceTest;
+import com.android.tradefed.testtype.IInvocationContextReceiver;
 import com.android.tradefed.testtype.IRemoteTest;
 import com.android.tradefed.testtype.IShardableTest;
 import com.android.tradefed.testtype.IStrictShardableTest;
@@ -79,7 +82,8 @@
  */
 @OptionClass(alias = "compatibility")
 public class CompatibilityTest implements IDeviceTest, IShardableTest, IBuildReceiver,
-        IStrictShardableTest, ISystemStatusCheckerReceiver, ITestCollector {
+        IStrictShardableTest, ISystemStatusCheckerReceiver, ITestCollector,
+        IInvocationContextReceiver {
 
     public static final String INCLUDE_FILTER_OPTION = "include-filter";
     public static final String EXCLUDE_FILTER_OPTION = "exclude-filter";
@@ -275,6 +279,8 @@
     private boolean mIsLocalSharding = false;
     private boolean mIsSharded = false;
 
+    private IInvocationContext mInvocationContext;
+
     /**
      * Create a new {@link CompatibilityTest} that will run the default list of
      * modules.
@@ -468,6 +474,12 @@
                 if (checkers != null && !checkers.isEmpty()) {
                     runPreModuleCheck(module.getName(), checkers, mDevice, listener);
                 }
+                IInvocationContext moduleContext = new InvocationContext();
+                moduleContext.setConfigurationDescriptor(module.getConfigurationDescriptor());
+                moduleContext.addInvocationAttribute(IModuleDef.MODULE_NAME, module.getName());
+                moduleContext.addInvocationAttribute(IModuleDef.MODULE_ABI,
+                        module.getAbi().getName());
+                mInvocationContext.setModuleInvocationContext(moduleContext);
                 try {
                     module.run(listener);
                 } catch (DeviceUnresponsiveException due) {
@@ -481,6 +493,10 @@
                             stack.toString());
                     CLog.w("This may be due to incorrect timeout setting on module %s",
                             module.getName());
+                } finally {
+                    // clear out module invocation context since we are now done with module
+                    // execution
+                    mInvocationContext.setModuleInvocationContext(null);
                 }
                 long duration = System.currentTimeMillis() - start;
                 long expected = module.getRuntimeHint();
@@ -808,4 +824,9 @@
     public void setExcludeFilter(Set<String> excludeFilters) {
         mExcludeFilters.addAll(excludeFilters);
     }
+
+    @Override
+    public void setInvocationContext(IInvocationContext invocationContext) {
+        mInvocationContext = invocationContext;
+    }
 }
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleDef.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleDef.java
index edb241a..69e8393 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleDef.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleDef.java
@@ -15,7 +15,9 @@
  */
 package com.android.compatibility.common.tradefed.testtype;
 
+import com.android.tradefed.config.ConfigurationDescriptor;
 import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.invoker.IInvocationContext;
 import com.android.tradefed.testtype.IAbi;
 import com.android.tradefed.testtype.IBuildReceiver;
 import com.android.tradefed.testtype.IDeviceTest;
@@ -32,6 +34,10 @@
 public interface IModuleDef extends Comparable<IModuleDef>, IBuildReceiver, IDeviceTest,
         IRemoteTest, IRuntimeHintProvider, ITestCollector {
 
+    /** key names used for saving module info into {@link IInvocationContext} */
+    public static String MODULE_NAME = "module-name";
+    public static String MODULE_ABI = "module-abi";
+
     /**
      * @return The name of this module.
      */
@@ -75,4 +81,8 @@
     boolean prepare(boolean skipPrep, List<String> preconditionArgs)
             throws DeviceNotAvailableException;
 
+    /**
+     * Retrieves the {@link ConfigurationDescriptor} associated with module config
+     */
+    ConfigurationDescriptor getConfigurationDescriptor();
 }
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleDef.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleDef.java
index 29009f1..5198ec1 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleDef.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleDef.java
@@ -21,6 +21,7 @@
 import com.android.compatibility.common.tradefed.targetprep.PreconditionPreparer;
 import com.android.compatibility.common.tradefed.targetprep.TokenRequirement;
 import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.config.ConfigurationDescriptor;
 import com.android.tradefed.config.ConfigurationException;
 import com.android.tradefed.config.OptionSetter;
 import com.android.tradefed.device.DeviceNotAvailableException;
@@ -66,13 +67,15 @@
     private IBuildInfo mBuild;
     private ITestDevice mDevice;
     private Set<String> mPreparerWhitelist = new HashSet<>();
+    private ConfigurationDescriptor mConfigurationDescriptor;
 
     public ModuleDef(String name, IAbi abi, IRemoteTest test,
-            List<ITargetPreparer> preparers) {
+            List<ITargetPreparer> preparers, ConfigurationDescriptor configurationDescriptor) {
         mId = AbiUtils.createId(abi.getName(), name);
         mName = name;
         mAbi = abi;
         mTest = test;
+        mConfigurationDescriptor = configurationDescriptor;
         boolean hasAbiReceiver = false;
         for (ITargetPreparer preparer : preparers) {
             if (preparer instanceof IAbiReceiver) {
@@ -354,4 +357,9 @@
             }
         }
     }
+
+    @Override
+    public ConfigurationDescriptor getConfigurationDescriptor() {
+        return mConfigurationDescriptor;
+    }
 }
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
index 8bf4054..82b8980 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
@@ -341,7 +341,8 @@
             String[] configPaths) throws ConfigurationException {
         // Invokes parser to process the test module config file
         IConfiguration config = mConfigFactory.createConfigurationFromArgs(configPaths);
-        addModuleDef(new ModuleDef(name, abi, test, config.getTargetPreparers()));
+        addModuleDef(new ModuleDef(name, abi, test, config.getTargetPreparers(),
+                config.getConfigurationDescription()));
     }
 
     private void addModuleDef(IModuleDef moduleDef) {
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/IntegrationTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/IntegrationTest.java
index ce41b47..4d9bfec 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/IntegrationTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/IntegrationTest.java
@@ -22,8 +22,10 @@
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.compatibility.common.tradefed.result.ResultReporter;
 import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
+import com.android.compatibility.common.tradefed.testtype.IModuleDef;
 import com.android.compatibility.common.util.IInvocationResult;
 import com.android.compatibility.common.util.TestStatus;
+import com.android.ddmlib.testrunner.TestIdentifier;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.config.ConfigurationFactory;
 import com.android.tradefed.config.OptionSetter;
@@ -36,6 +38,7 @@
 import com.android.tradefed.result.ResultForwarder;
 import com.android.tradefed.testtype.IBuildReceiver;
 import com.android.tradefed.testtype.IDeviceTest;
+import com.android.tradefed.testtype.IInvocationContextReceiver;
 import com.android.tradefed.testtype.IRemoteTest;
 import com.android.tradefed.util.AbiUtils;
 import com.android.tradefed.util.FileUtil;
@@ -67,6 +70,7 @@
 
     private static final String CONFIG =
             "<configuration description=\"Auto Generated File\">\n" +
+            "<option name=\"config-descriptor:metadata\" key=\"component\" value=\"%s\" />\n" +
             "<test class=\"com.android.compatibility.common.tradefed.testtype.%s\">\n" +
             "    <option name=\"report-test\" value=\"%s\" />\n" +
             "    <option name=\"run-complete\" value=\"%s\" />\n" +
@@ -75,7 +79,8 @@
             "</test>\n" +
             "</configuration>";
     private static final String FILENAME = "%s.config";
-    private static final String TEST_STUB = "TestStub"; // Trivial test stub
+    private static final String TEST_STUB = "TestStub"; // Test stub
+    private static final String SIMPLE_TEST_STUB = "SimpleTestStub"; // Simple test stub
     private static final String TEST_STUB_SHARDABLE = "TestStubShardable";
     private static final String COMMAND_LINE = "run cts";
 
@@ -134,6 +139,7 @@
         mContext = new InvocationContext();
         mContext.addAllocatedDevice("default", mMockDevice);
         mContext.addDeviceBuildInfo("default", mMockBuildInfo);
+        mTest.setInvocationContext(mContext);
     }
 
     @After
@@ -155,13 +161,33 @@
     private void createConfig(File testsDir, String name, String moduleClass, boolean reportTest,
             boolean runComplete, boolean doesOneTestFail, boolean internalRetry)
                     throws IOException {
+        createConfig(testsDir, name, moduleClass, reportTest, runComplete, doesOneTestFail,
+                internalRetry, "foo");
+
+    }
+
+    /**
+     * Create a CTS configuration with a fake tests to exercise all cases.
+     *
+     * @param testsDir The testcases/ dir where to put the module
+     * @param name the name of the module.
+     * @param moduleClass the fake test class to use.
+     * @param reportTest True if the test report some tests
+     * @param runComplete True if the test run is complete
+     * @param doesOneTestFail True if one of the test is going to fail
+     * @param internalRetry True if the test will retry the module itself once
+     * @param component the platform component name that the module can be categorized under
+     */
+    private void createConfig(File testsDir, String name, String moduleClass, boolean reportTest,
+            boolean runComplete, boolean doesOneTestFail, boolean internalRetry, String component)
+                    throws IOException {
         File config = new File(testsDir, String.format(FILENAME, name));
         FileUtil.deleteFile(config);
         if (!config.createNewFile()) {
             throw new IOException(String.format("Failed to create '%s'", config.getAbsolutePath()));
         }
 
-        FileUtil.writeToFile(String.format(CONFIG, moduleClass, reportTest, runComplete,
+        FileUtil.writeToFile(String.format(CONFIG, component, moduleClass, reportTest, runComplete,
                 doesOneTestFail, internalRetry), config);
     }
 
@@ -192,6 +218,83 @@
     }
 
     /**
+     * Verify that result reporters test run ended callback can receive component name as configured
+     * in module config metadata field.
+     */
+    @Test
+    public void testSingleModuleRun_checkMetadata() throws Exception {
+        final String moduleName = "AwsomeModule";
+        final String mAbi = "arm64-v8a";
+        final String component = "CriticalComponent";
+        final List<String> receivedComponentsTestEnded = new ArrayList<>();
+        final List<String> receivedModuleNameTestEnded = new ArrayList<>();
+        final List<String> receivedAbiTestEnded = new ArrayList<>();
+        final List<String> receivedComponentsTestRunEnded = new ArrayList<>();
+        final List<String> receivedModuleNameTestRunEnded = new ArrayList<>();
+        final List<String> receivedAbiTestRunEnded = new ArrayList<>();
+        createConfig(mTestDir, moduleName, SIMPLE_TEST_STUB, true, true, true, false, component);
+        EasyMock.expect(mMockDevice.getProperty("ro.product.cpu.abilist")).andReturn(mAbi);
+
+        mMockBuildInfo.addBuildAttribute(EasyMock.eq(CompatibilityBuildHelper.MODULE_IDS),
+                EasyMock.eq(AbiUtils.createId(mAbi, moduleName)));
+        EasyMock.expectLastCall();
+
+        EasyMock.replay(mMockDevice, mMockBuildInfo);
+        ITestInvocationListener myListener = new ITestInvocationListener() {
+            private IInvocationContext myContext;
+            @Override
+            public void invocationStarted(IInvocationContext context) {
+                myContext = context;
+            }
+            @Override
+            public void testRunEnded(long elapsedTimeMillis, Map<String, String> runMetrics) {
+                receivedComponentsTestRunEnded.addAll(myContext.getModuleInvocationContext()
+                        .getConfigurationDescriptor().getMetaData("component"));
+                receivedModuleNameTestRunEnded.addAll(myContext.getModuleInvocationContext()
+                        .getAttributes().get(IModuleDef.MODULE_NAME));
+                receivedAbiTestRunEnded.addAll(myContext.getModuleInvocationContext()
+                        .getAttributes().get(IModuleDef.MODULE_ABI));
+            }
+            @Override
+            public void testEnded(TestIdentifier test, long endTime,
+                    Map<String, String> testMetrics) {
+                receivedComponentsTestEnded.addAll(myContext.getModuleInvocationContext()
+                        .getConfigurationDescriptor().getMetaData("component"));
+                receivedModuleNameTestEnded.addAll(myContext.getModuleInvocationContext()
+                        .getAttributes().get(IModuleDef.MODULE_NAME));
+                receivedAbiTestEnded.addAll(myContext.getModuleInvocationContext()
+                        .getAttributes().get(IModuleDef.MODULE_ABI));
+            }
+        };
+        myListener.invocationStarted(mContext);
+        mTest.run(myListener);
+        myListener.invocationEnded(500);
+        EasyMock.verify(mMockDevice, mMockBuildInfo);
+        // verify metadata was retrieved during testRunEnded callbacks
+        assertEquals("[testRunEnded] wrong number of metadata collected",
+                1, receivedComponentsTestRunEnded.size());
+        assertEquals("[testRunEnded] wrong component metadata field received",
+                component, receivedComponentsTestRunEnded.get(0));
+        assertEquals("[testRunEnded] wrong number of module name collected",
+                1, receivedModuleNameTestRunEnded.size());
+        assertEquals(moduleName, receivedModuleNameTestRunEnded.get(0));
+        assertEquals("[testEnded] wrong number of module abi collected",
+                1, receivedAbiTestRunEnded.size());
+        assertEquals(mAbi, receivedAbiTestRunEnded.get(0));
+        // verify metadata was retrieved during testEnded callbacks
+        assertEquals("[testEnded] wrong number of metadata collected",
+                1, receivedComponentsTestEnded.size());
+        assertEquals("[testEnded] wrong component metadata field received",
+                component, receivedComponentsTestEnded.get(0));
+        assertEquals("[testEnded] wrong number of module name collected",
+                1, receivedModuleNameTestEnded.size());
+        assertEquals(moduleName, receivedModuleNameTestEnded.get(0));
+        assertEquals("[testEnded] wrong number of module abi collected",
+                1, receivedAbiTestEnded.size());
+        assertEquals(mAbi, receivedAbiTestEnded.get(0));
+    }
+
+    /**
      * Simple tests running in one module that run some tests but not all of them.
      */
     @Test
@@ -297,6 +400,7 @@
         };
         mTest.setDevice(mMockDevice);
         mTest.setBuild(mMockBuildInfo);
+        mTest.setInvocationContext(mContext);
         OptionSetter setter = new OptionSetter(mTest, mReporter);
         setter.setOptionValue("retry", "0");
 
@@ -370,6 +474,7 @@
         };
         mTest.setDevice(mMockDevice);
         mTest.setBuild(mMockBuildInfo);
+        mTest.setInvocationContext(mContext);
         OptionSetter setter = new OptionSetter(mTest, mReporter);
         setter.setOptionValue("retry", "0");
 
@@ -426,6 +531,7 @@
             ITestInvocationListener listener = getShardListener(mMasterReporter);
             ((IBuildReceiver)mShardTest).setBuild(mBuild);
             ((IDeviceTest)mShardTest).setDevice(mDevice);
+            ((IInvocationContextReceiver)mShardTest).setInvocationContext(mContext);
             listener.invocationStarted(mShardContext);
             try {
                 mShardTest.run(listener);
@@ -570,6 +676,7 @@
 
         ((IBuildReceiver)tests.get(0)).setBuild(mMockBuildInfo);
         ((IDeviceTest)tests.get(0)).setDevice(mMockDevice);
+        ((IInvocationContextReceiver)tests.get(0)).setInvocationContext(mContext);
         mReporter.invocationStarted(mContext);
         try {
             tests.get(0).run(mReporter);
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleDefTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleDefTest.java
index 356207e..0612e6e 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleDefTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleDefTest.java
@@ -16,6 +16,7 @@
 
 package com.android.compatibility.common.tradefed.testtype;
 
+import com.android.tradefed.config.ConfigurationDescriptor;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.result.ITestInvocationListener;
 import com.android.tradefed.targetprep.ITargetPreparer;
@@ -46,7 +47,8 @@
     public void testAccessors() throws Exception {
         IAbi abi = new Abi(ABI, "");
         MockRemoteTest mockTest = new MockRemoteTest();
-        IModuleDef def = new ModuleDef(NAME, abi, mockTest, new ArrayList<ITargetPreparer>());
+        IModuleDef def = new ModuleDef(NAME, abi, mockTest, new ArrayList<ITargetPreparer>(),
+                new ConfigurationDescriptor());
         assertEquals("Incorrect ID", ID, def.getId());
         assertEquals("Incorrect ABI", ABI, def.getAbi().getName());
         assertEquals("Incorrect Name", NAME, def.getName());
@@ -55,7 +57,8 @@
     public void testModuleFinisher() throws Exception {
         IAbi abi = new Abi(ABI, "");
         MockRemoteTest mockTest = new MockRemoteTest();
-        IModuleDef def = new ModuleDef(NAME, abi, mockTest, new ArrayList<ITargetPreparer>());
+        IModuleDef def = new ModuleDef(NAME, abi, mockTest,
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor());
         ITestInvocationListener mockListener = EasyMock.createMock(ITestInvocationListener.class);
         // listener should receive testRunStarted/testRunEnded events even for no-op run() method
         mockListener.testRunStarted(ID, 0);
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java
index 79f85f9..23fc90c 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java
@@ -19,6 +19,7 @@
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.compatibility.common.tradefed.testtype.ModuleRepo.ConfigFilter;
 import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.config.ConfigurationDescriptor;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.result.ITestInvocationListener;
 import com.android.tradefed.targetprep.ITargetPreparer;
@@ -460,22 +461,22 @@
         TestRuntime test1 = new TestRuntime();
         test1.runtimeHint = 100l;
         IModuleDef mod1 = new ModuleDef("test1", new Abi("arm", "32"), test1,
-                new ArrayList<ITargetPreparer>());
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor());
         testList.add(mod1);
         TestRuntime test2 = new TestRuntime();
         test2.runtimeHint = 100l;
         IModuleDef mod2 = new ModuleDef("test2", new Abi("arm", "32"), test2,
-                new ArrayList<ITargetPreparer>());
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor());
         testList.add(mod2);
         TestRuntime test3 = new TestRuntime();
         test3.runtimeHint = 100l;
         IModuleDef mod3 = new ModuleDef("test3", new Abi("arm", "32"), test3,
-                new ArrayList<ITargetPreparer>());
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor());
         testList.add(mod3);
         TestRuntime test4 = new TestRuntime();
         test4.runtimeHint = 100l;
         IModuleDef mod4 = new ModuleDef("test4", new Abi("arm", "32"), test4,
-                new ArrayList<ITargetPreparer>());
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor());
         testList.add(mod4);
         // if we don't shard everything is in one shard.
         List<IModuleDef> res = mRepo.getShard(testList, 0, 1);
@@ -500,7 +501,7 @@
         TestRuntime test1 = new TestRuntime();
         test1.runtimeHint = 100l;
         IModuleDef mod1 = new ModuleDef("test1", new Abi("arm", "32"), test1,
-                new ArrayList<ITargetPreparer>());
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor());
         testList.add(mod1);
         List<IModuleDef> res = mRepo.getShard(testList, 1, 2);
         assertNull(res);
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/SimpleTestStub.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/SimpleTestStub.java
new file mode 100644
index 0000000..331f978
--- /dev/null
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/SimpleTestStub.java
@@ -0,0 +1,95 @@
+/*
+ * 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.testtype;
+
+import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.testtype.IAbi;
+import com.android.tradefed.testtype.IAbiReceiver;
+import com.android.tradefed.testtype.IRemoteTest;
+import com.android.tradefed.testtype.IRuntimeHintProvider;
+import com.android.tradefed.testtype.ITestCollector;
+import com.android.tradefed.testtype.ITestFilterReceiver;
+
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * A test Stub that can be used to fake some runs.
+ */
+public class SimpleTestStub implements IRemoteTest, IAbiReceiver, IRuntimeHintProvider,
+        ITestCollector, ITestFilterReceiver {
+
+    // options below are unused
+    @Option(name = "report-test")
+    protected boolean mReportTest = false;
+    @Option(name = "run-complete")
+    protected boolean mIsComplete = true;
+    @Option(name = "test-fail")
+    protected boolean mDoesOneTestFail = true;
+    @Option(name = "internal-retry")
+    protected boolean mRetry = false;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
+        // We report 1 passing tes
+        listener.testRunStarted("module-run", 1);
+        TestIdentifier tid = new TestIdentifier("TestStub", "test1");
+        listener.testStarted(tid);
+        listener.testEnded(tid, Collections.emptyMap());
+        listener.testRunEnded(0, Collections.emptyMap());
+    }
+
+    @Override
+    public void setAbi(IAbi abi) {
+        // Do nothing
+    }
+
+    @Override
+    public long getRuntimeHint() {
+        return 1L;
+    }
+
+    @Override
+    public void setCollectTestsOnly(boolean shouldCollectTest) {
+        // Do nothing
+    }
+
+    @Override
+    public void addIncludeFilter(String filter) {
+
+    }
+
+    @Override
+    public void addAllIncludeFilters(Set<String> filters) {
+
+    }
+
+    @Override
+    public void addExcludeFilter(String filter) {
+
+    }
+
+    @Override
+    public void addAllExcludeFilters(Set<String> filters) {
+
+    }
+}
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/util/UniqueModuleCountUtilTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/util/UniqueModuleCountUtilTest.java
index 799cff3..8e20133 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/util/UniqueModuleCountUtilTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/util/UniqueModuleCountUtilTest.java
@@ -20,6 +20,7 @@
 import com.android.compatibility.common.tradefed.testtype.IModuleDef;
 import com.android.compatibility.common.tradefed.testtype.ModuleDef;
 import com.android.compatibility.common.tradefed.testtype.TestStub;
+import com.android.tradefed.config.ConfigurationDescriptor;
 import com.android.tradefed.targetprep.ITargetPreparer;
 import com.android.tradefed.testtype.Abi;
 
@@ -43,9 +44,9 @@
     public void testCount_2uniquesModules() {
         List<IModuleDef> list = new ArrayList<>();
         list.add(new ModuleDef("moduleA", new Abi("arm64", "64"), new TestStub(),
-                new ArrayList<ITargetPreparer>()));
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor()));
         list.add(new ModuleDef("moduleA", new Abi("arm32", "32"), new TestStub(),
-                new ArrayList<ITargetPreparer>()));
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor()));
         assertEquals(2, UniqueModuleCountUtil.countUniqueModules(list));
     }
 
@@ -53,9 +54,9 @@
     public void testCount_2subModules() {
         List<IModuleDef> list = new ArrayList<>();
         list.add(new ModuleDef("moduleA", new Abi("arm32", "32"), new TestStub(),
-                new ArrayList<ITargetPreparer>()));
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor()));
         list.add(new ModuleDef("moduleA", new Abi("arm32", "32"), new TestStub(),
-                new ArrayList<ITargetPreparer>()));
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor()));
         assertEquals(1, UniqueModuleCountUtil.countUniqueModules(list));
     }
 
@@ -63,21 +64,21 @@
     public void testCount_mix() {
         List<IModuleDef> list = new ArrayList<>();
         list.add(new ModuleDef("moduleA", new Abi("arm64", "64"), new TestStub(),
-                new ArrayList<ITargetPreparer>()));
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor()));
         list.add(new ModuleDef("moduleA", new Abi("arm32", "32"), new TestStub(),
-                new ArrayList<ITargetPreparer>()));
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor()));
         list.add(new ModuleDef("moduleC", new Abi("arm32", "32"), new TestStub(),
-                new ArrayList<ITargetPreparer>()));
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor()));
         list.add(new ModuleDef("moduleB", new Abi("arm64", "64"), new TestStub(),
-                new ArrayList<ITargetPreparer>()));
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor()));
         list.add(new ModuleDef("moduleB", new Abi("arm32", "32"), new TestStub(),
-                new ArrayList<ITargetPreparer>()));
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor()));
         list.add(new ModuleDef("moduleC", new Abi("arm64", "64"), new TestStub(),
-                new ArrayList<ITargetPreparer>()));
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor()));
         list.add(new ModuleDef("moduleA", new Abi("arm32", "32"), new TestStub(),
-                new ArrayList<ITargetPreparer>()));
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor()));
         list.add(new ModuleDef("moduleC", new Abi("arm32", "32"), new TestStub(),
-                new ArrayList<ITargetPreparer>()));
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor()));
         assertEquals(6, UniqueModuleCountUtil.countUniqueModules(list));
     }
 }
diff --git a/tests/acceleration/AndroidTest.xml b/tests/acceleration/AndroidTest.xml
index 03be9fd..f8df29a 100644
--- a/tests/acceleration/AndroidTest.xml
+++ b/tests/acceleration/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Acceleration test cases">
+    <option name="config-descriptor:metadata" key="component" value="framework" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsAccelerationTestCases.apk" />
diff --git a/tests/camera/AndroidTest.xml b/tests/camera/AndroidTest.xml
index 274daf0..869d2c3 100644
--- a/tests/camera/AndroidTest.xml
+++ b/tests/camera/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Camera test cases">
+    <option name="config-descriptor:metadata" key="component" value="camera" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsCameraTestCases.apk" />