Add a sample showing how to use filtering.

Bug:21762834
Change-Id: Iaa4455bb5aea4794709f9fb613c6c980be2cce79
diff --git a/common/host-side/tradefed/res/config/everything.xml b/common/host-side/tradefed/res/config/everything.xml
index b267032..d1ac900 100644
--- a/common/host-side/tradefed/res/config/everything.xml
+++ b/common/host-side/tradefed/res/config/everything.xml
@@ -16,6 +16,6 @@
 <configuration description="Common config for Compatibility suites to run all modules">
 
     <include name="common-compatibility-config" />
-    <option name="compatibility-test:plan" value="everything" />
+    <option name="compatibility:plan" value="everything" />
 
 </configuration>
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 40bace9..1e94a88 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
@@ -55,7 +55,7 @@
 /**
  * A Test for running Compatibility Suites
  */
-@OptionClass(alias = "compatibility-test")
+@OptionClass(alias = "compatibility")
 public class CompatibilityTest implements IDeviceTest, IShardableTest, IBuildReceiver {
 
     public static final String INCLUDE_FILTER_OPTION = "include-filter";
@@ -63,6 +63,8 @@
     private static final String PLAN_OPTION = "plan";
     private static final String MODULE_OPTION = "module";
     private static final String TEST_OPTION = "test";
+    private static final String MODULE_ARG_OPTION = "module-arg";
+    private static final String TEST_ARG_OPTION = "test-arg";
     public static final String RETRY_OPTION = "retry";
     private static final String ABI_OPTION = "abi";
     private static final String SHARD_OPTION = "shard";
@@ -103,6 +105,18 @@
             importance = Importance.IF_UNSET)
     private String mTestName = null;
 
+    @Option(name = MODULE_ARG_OPTION,
+            description = "the arguments to pass to a module. The expected format is"
+                    + "\"<module-name>:<arg-name>:<arg-value>\"",
+            importance = Importance.ALWAYS)
+    private List<String> mModuleArgs = new ArrayList<>();
+
+    @Option(name = TEST_ARG_OPTION,
+            description = "the arguments to pass to a test. The expected format is"
+                    + "\"<test-class>:<arg-name>:<arg-value>\"",
+            importance = Importance.ALWAYS)
+    private List<String> mTestArgs = new ArrayList<>();
+
     @Option(name = RETRY_OPTION,
             shortName = 'r',
             description = "retry a previous session.",
@@ -216,7 +230,8 @@
                     // Initialize the repository, {@link CompatibilityBuildHelper#getTestsDir} can
                     // throw a {@link FileNotFoundException}
                     moduleRepo.initialize(mTotalShards, mBuildHelper.getTestsDir(), getAbis(),
-                            mDeviceTokens, mIncludeFilters, mExcludeFilters);
+                            mDeviceTokens, mTestArgs, mModuleArgs, mIncludeFilters,
+                            mExcludeFilters);
                 }
             }
             // Get the tests to run in this shard
@@ -315,8 +330,7 @@
                     }
                 }
             }
-        }
-        if (mModuleName != null) {
+        } else if (mModuleName != null) {
             mIncludeFilters.clear();
             try {
                 List<String> modules = ModuleRepo.getModuleNamesMatching(
@@ -352,6 +366,11 @@
             } catch (FileNotFoundException e) {
                 e.printStackTrace();
             }
+        } else {
+            // If a module has an arg, assume it's included
+            for (String arg : mModuleArgs) {
+                mIncludeFilters.add(arg.split(":")[0]);
+            }
         }
     }
 
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 ee9295e..f8520b2 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
@@ -49,6 +49,11 @@
     Set<String> getTokens();
 
     /**
+     * @return the {@link IRemoteTest} that runs the tests.
+     */
+    IRemoteTest getTest();
+
+    /**
      * Adds a filter to include a specific test
      *
      * @param name the name of the test. Can be <package>, <package>.<class>,
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleRepo.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleRepo.java
index 5e06b1c..6b023aa 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleRepo.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleRepo.java
@@ -36,7 +36,8 @@
      * Initializes the repository.
      */
     void initialize(int shards, File testsDir, Set<IAbi> abis, List<String> deviceTokens,
-            List<String> includeFilters, List<String> excludeFilters);
+            List<String> testArgs, List<String> moduleArgs, List<String> mIncludeFilters,
+            List<String> mExcludeFilters);
 
     /**
      * @return a {@link Map} of all modules to run on the device referenced by the given serial.
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 54bba26..9930f33 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
@@ -129,6 +129,14 @@
      * {@inheritDoc}
      */
     @Override
+    public IRemoteTest getTest() {
+        return mTest;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
     public void addIncludeFilter(String filter) {
         mIncludeFilters.add(filter);
     }
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 fdd157e..b9f6e82 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
@@ -23,6 +23,7 @@
 import com.android.tradefed.config.IConfiguration;
 import com.android.tradefed.config.IConfigurationFactory;
 import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.targetprep.ITargetPreparer;
 import com.android.tradefed.testtype.IAbi;
 import com.android.tradefed.testtype.IRemoteTest;
 import com.android.tradefed.testtype.IShardableTest;
@@ -35,6 +36,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 
 /**
@@ -50,6 +52,8 @@
     private int mModulesPerShard;
     private Set<String> mSerials = new HashSet<>();
     private Map<String, Set<String>> mDeviceTokens = new HashMap<>();
+    private Map<String, Map<String, String>> mTestArgs = new HashMap<>();
+    private Map<String, Map<String, String>> mModuleArgs = new HashMap<>();
     private boolean mIncludeAll;
     private Map<String, List<TestFilter>> mIncludeFilters = new HashMap<>();
     private Map<String, List<TestFilter>> mExcludeFilters = new HashMap<>();
@@ -92,7 +96,7 @@
     public Map<String, Set<String>> getDeviceTokens() {
         return mDeviceTokens;
     }
-
+        
     /**
      * A {@link FilenameFilter} to find all modules in a directory who match the given pattern.
      */
@@ -150,7 +154,8 @@
      */
     @Override
     public void initialize(int shards, File testsDir, Set<IAbi> abis, List<String> deviceTokens,
-            List<String> includeFilters, List<String> excludeFilters) {
+            List<String> testArgs, List<String> moduleArgs, List<String> includeFilters,
+            List<String> excludeFilters) {
         mInitialized = true;
         mShards = shards;
         for (String line : deviceTokens) {
@@ -169,6 +174,8 @@
                         String.format("Could not parse device token: %s", line));
             }
         }
+        putArgs(testArgs, mTestArgs);
+        putArgs(moduleArgs, mModuleArgs);
         mIncludeAll = includeFilters.isEmpty();
         // Include all the inclusions
         addFilters(includeFilters, mIncludeFilters, abis);
@@ -185,7 +192,34 @@
                 // configs are idempotent. This however means we parse the same file multiple times
                 for (IAbi abi : abis) {
                     IConfiguration config = mConfigFactory.createConfigurationFromArgs(pathArg);
+                    String id = AbiUtils.createId(abi.getName(), name);
+                    {
+                        Map<String, String> args = new HashMap<>();
+                        if (mModuleArgs.containsKey(name)) {
+                            args.putAll(mModuleArgs.get(name));
+                        }
+                        if (mModuleArgs.containsKey(id)) {
+                            args.putAll(mModuleArgs.get(id));
+                        }
+                        if (args != null && args.size() > 0) {
+                            for (Entry<String, String> entry : args.entrySet()) {
+                                config.injectOptionValue(entry.getKey(), entry.getValue());
+                            }
+                        }
+                    }
                     List<IRemoteTest> tests = config.getTests();
+                    for (IRemoteTest test : tests) {
+                        String className = test.getClass().getName();
+                        Map<String, String> args = new HashMap<>();
+                        if (mTestArgs.containsKey(className)) {
+                            args.putAll(mTestArgs.get(className));
+                        }
+                        if (args != null && args.size() > 0) {
+                            for (Entry<String, String> entry : args.entrySet()) {
+                                config.injectOptionValue(entry.getKey(), entry.getValue());
+                            }
+                        }
+                    }
                     int testCount = tests.size();
                     for (int i = 0; i < testCount; i++) {
                         IRemoteTest test = tests.get(i);
@@ -270,7 +304,7 @@
             } else {
                 mRemainingWithTokens.add(moduleDef);
             }
-            int numModules = mRemainingModules.size() + mRemainingWithTokens.size();
+            float numModules = mRemainingModules.size() + mRemainingWithTokens.size();
             mModulesPerShard = (int) ((numModules / mShards) + 0.5f); // Round up
         }
     }
@@ -378,4 +412,19 @@
         }
         return modules;
     }
+
+    private static void putArgs(List<String> args, Map<String, Map<String, String>> argsMap) {
+        for (String arg : args) {
+            String[] parts = arg.split(":");
+            String target = parts[0];
+            String key = parts[1];
+            String value = parts[2];
+            Map<String, String> map = argsMap.get(target);
+            if (map == null) {
+                map = new HashMap<>();
+                argsMap.put(target, map);
+            }
+            map.put(key, value);
+        }
+    }
 }
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 4922081..88cbe3e 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
@@ -17,14 +17,15 @@
 package com.android.compatibility.common.tradefed.testtype;
 
 import com.android.compatibility.common.tradefed.testtype.ModuleRepo.ConfigFilter;
+import com.android.compatibility.common.util.AbiUtils;
 import com.android.tradefed.testtype.IAbi;
-import com.android.tradefed.util.FileUtil;
+import com.android.tradefed.testtype.IRemoteTest;
+import com.android.tradefed.util.FileUtil;  
 
 import junit.framework.TestCase;
 
 import java.io.File;
 import java.io.IOException;
-import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
@@ -38,10 +39,12 @@
             + "<option name=\"token\" value=\"%s\" />\n"
             + "</target_preparer>\n";
     private static final String CONFIG =
-            "<configuration description=\"Auto Generated File\">\n"
-            + "%s"
-            + "<test class=\"com.android.tradefed.testtype.AndroidJUnitTest\" />\n"
-            + "</configuration>";
+            "<configuration description=\"Auto Generated File\">\n" +
+            "%s" +
+            "<test class=\"com.android.compatibility.common.tradefed.testtype.TestStub\">\n" +
+            "<option name=\"module\" value=\"%s\" />" +
+            "</test>\n" +
+            "</configuration>";
     private static final String FOOBAR_TOKEN = "foobar";
     private static final String SERIAL1 = "abc";
     private static final String SERIAL2 = "def";
@@ -49,28 +52,65 @@
     private static final Set<String> SERIALS = new HashSet<>();
     private static final Set<IAbi> ABIS = new HashSet<>();
     private static final List<String> DEVICE_TOKENS = new ArrayList<>();
+    private static final List<String> TEST_ARGS= new ArrayList<>();
+    private static final List<String> MODULE_ARGS = new ArrayList<>();
     private static final List<String> INCLUDES = new ArrayList<>();
     private static final List<String> EXCLUDES = new ArrayList<>();
     private static final Set<String> FILES = new HashSet<>();
+    private static final String FILENAME = "%s.config";
+    private static final String ABI_32 = "armeabi-v7a";
+    private static final String ABI_64 = "arm64-v8a";
+    private static final String MODULE_NAME_A = "FooModuleA";
+    private static final String MODULE_NAME_B = "FooModuleB";
+    private static final String MODULE_NAME_C = "FooModuleC";
+    private static final String ID_A_32 = AbiUtils.createId(ABI_32, MODULE_NAME_A);
+    private static final String ID_A_64 = AbiUtils.createId(ABI_64, MODULE_NAME_A);
+    private static final String ID_B_32 = AbiUtils.createId(ABI_32, MODULE_NAME_B);
+    private static final String ID_B_64 = AbiUtils.createId(ABI_64, MODULE_NAME_B);
+    private static final String ID_C_32 = AbiUtils.createId(ABI_32, MODULE_NAME_C);
+    private static final String ID_C_64 = AbiUtils.createId(ABI_64, MODULE_NAME_C);
+    private static final String TEST_ARG = TestStub.class.getName() + ":foo:bar";
+    private static final String MODULE_ARG = "%s:blah:foobar";
     static {
         SERIALS.add(SERIAL1);
         SERIALS.add(SERIAL2);
         SERIALS.add(SERIAL3);
-        ABIS.add(new Abi("armeabi-v7a", "32"));
-        ABIS.add(new Abi("arm64-v8a", "64"));
+        ABIS.add(new Abi(ABI_32, "32"));
+        ABIS.add(new Abi(ABI_64, "64"));
         DEVICE_TOKENS.add(String.format("%s:%s", SERIAL3, FOOBAR_TOKEN));
-        FILES.add("One.config");
-        FILES.add("Two.config");
-        FILES.add("Three.config");
+        TEST_ARGS.add(TEST_ARG);
+        MODULE_ARGS.add(String.format(MODULE_ARG, MODULE_NAME_A));
+        MODULE_ARGS.add(String.format(MODULE_ARG, MODULE_NAME_B));
+        MODULE_ARGS.add(String.format(MODULE_ARG, MODULE_NAME_C));
+        FILES.add(String.format(FILENAME, MODULE_NAME_A));
+        FILES.add(String.format(FILENAME, MODULE_NAME_B));
+        FILES.add(String.format(FILENAME, MODULE_NAME_C));
     }
-    private IModuleRepo repo;
+    private IModuleRepo mRepo;
     private File mTestsDir;
 
     @Override
     public void setUp() throws Exception {
         mTestsDir = setUpConfigs();
         ModuleRepo.sInstance = null;// Clear the instance so it gets recreated.
-        repo = ModuleRepo.getInstance();
+        mRepo = ModuleRepo.getInstance();
+    }
+
+    private File setUpConfigs() throws IOException {
+        File testsDir = FileUtil.createNamedTempDir("testcases");
+        createConfig(testsDir, MODULE_NAME_A, null);
+        createConfig(testsDir, MODULE_NAME_B, null);
+        createConfig(testsDir, MODULE_NAME_C, FOOBAR_TOKEN);
+        return testsDir;
+    }
+
+    private void createConfig(File testsDir, String name, String token) throws IOException {
+        File config = new File(testsDir, String.format(FILENAME, name));
+        String preparer = "";
+        if (token != null) {
+            preparer = String.format(TOKEN, token);
+        }
+        FileUtil.writeToFile(String.format(CONFIG, preparer, name), config);
     }
 
     @Override
@@ -78,34 +118,40 @@
         tearDownConfigs(mTestsDir);
     }
 
+    private void tearDownConfigs(File testsDir) {
+        FileUtil.recursiveDelete(testsDir);
+    }
+
     public void testInitialization() throws Exception {
-        repo.initialize(3, mTestsDir, ABIS, DEVICE_TOKENS, INCLUDES, EXCLUDES);
-        assertTrue("Should be initialized", repo.isInitialized());
-        assertEquals("Wrong number of shards", 3, repo.getNumberOfShards());
-        assertEquals("Wrong number of modules per shard", 2, repo.getModulesPerShard());
-        Set<IModuleDef> modules = repo.getRemainingModules();
-        Map<String, Set<String>> deviceTokens = repo.getDeviceTokens();
+        mRepo.initialize(3, mTestsDir, ABIS, DEVICE_TOKENS, TEST_ARGS, MODULE_ARGS, INCLUDES,
+                EXCLUDES);
+        assertTrue("Should be initialized", mRepo.isInitialized());
+        assertEquals("Wrong number of shards", 3, mRepo.getNumberOfShards());
+        assertEquals("Wrong number of modules per shard", 2, mRepo.getModulesPerShard());
+        Set<IModuleDef> modules = mRepo.getRemainingModules();
+        Map<String, Set<String>> deviceTokens = mRepo.getDeviceTokens();
         assertEquals("Wrong number of devices with tokens", 1, deviceTokens.size());
         Set<String> tokens = deviceTokens.get(SERIAL3);
         assertEquals("Wrong number of tokens", 1, tokens.size());
         assertTrue("Unexpected device token", tokens.contains(FOOBAR_TOKEN));
         assertEquals("Wrong number of modules", 4, modules.size());
-        Set<IModuleDef> tokenModules = repo.getRemainingWithTokens();
+        Set<IModuleDef> tokenModules = mRepo.getRemainingWithTokens();
         assertEquals("Wrong number of modules with tokens", 2, tokenModules.size());
-        List<IModuleDef> serial1Modules = repo.getModules(SERIAL1);
+        List<IModuleDef> serial1Modules = mRepo.getModules(SERIAL1);
         assertEquals("Wrong number of modules", 2, serial1Modules.size());
-        List<IModuleDef> serial2Modules = repo.getModules(SERIAL2);
+        List<IModuleDef> serial2Modules = mRepo.getModules(SERIAL2);
         assertEquals("Wrong number of modules", 2, serial2Modules.size());
-        List<IModuleDef> serial3Modules = repo.getModules(SERIAL3);
+        List<IModuleDef> serial3Modules = mRepo.getModules(SERIAL3);
         assertEquals("Wrong number of modules", 2, serial3Modules.size());
         // Serial 3 should have the modules with tokens
         for (IModuleDef module : serial3Modules) {
-            assertEquals("Wrong module", "Three", module.getName());
+            assertEquals("Wrong module", MODULE_NAME_C, module.getName());
         }
-        Set<String> serials = repo.getSerials();
+        Set<String> serials = mRepo.getSerials();
         assertEquals("Wrong number of serials", 3, serials.size());
         assertTrue("Unexpected device serial", serials.containsAll(SERIALS));
     }
+
     public void testConfigFilter() throws Exception {
         File[] configFiles = mTestsDir.listFiles(new ConfigFilter());
         assertEquals("Wrong number of config files found.", 3, configFiles.length);
@@ -115,33 +161,46 @@
         }
     }
 
-    private File setUpConfigs() throws IOException {
-        File testsDir = FileUtil.createNamedTempDir("testcases");
-        createConfig(testsDir, "One", null);
-        createConfig(testsDir, "Two", null);
-        createConfig(testsDir, "Three", FOOBAR_TOKEN);
-        return testsDir;
+    public void testFiltering() throws Exception {
+        List<String> includeFilters = new ArrayList<>();
+        includeFilters.add(MODULE_NAME_A);
+        List<String> excludeFilters = new ArrayList<>();
+        excludeFilters.add(ID_A_32);
+        excludeFilters.add(MODULE_NAME_B);
+        mRepo.initialize(1, mTestsDir, ABIS, DEVICE_TOKENS, TEST_ARGS, MODULE_ARGS, includeFilters,
+                excludeFilters);
+        List<IModuleDef> modules = mRepo.getModules(SERIAL1);
+        assertEquals("Incorrect number of modules", 1, modules.size());
+        IModuleDef module = modules.get(0);
+        assertEquals("Incorrect ID", ID_A_64, module.getId());
+        checkArgs(module);
     }
 
-    private void tearDownConfigs(File testsDir) {
-        FileUtil.recursiveDelete(testsDir);
+    public void testParsing() throws Exception {
+        mRepo.initialize(1, mTestsDir, ABIS, DEVICE_TOKENS, TEST_ARGS, MODULE_ARGS, INCLUDES,
+                EXCLUDES);
+        List<IModuleDef> modules = mRepo.getModules(SERIAL3);
+        Set<String> idSet = new HashSet<>();
+        for (IModuleDef module : modules) {
+            idSet.add(module.getId());
+        }
+        assertEquals("Incorrect number of IDs", 6, idSet.size());
+        assertTrue("Missing ID_A_32", idSet.contains(ID_A_32));
+        assertTrue("Missing ID_A_64", idSet.contains(ID_A_64));
+        assertTrue("Missing ID_B_32", idSet.contains(ID_B_32));
+        assertTrue("Missing ID_B_64", idSet.contains(ID_B_64));
+        assertTrue("Missing ID_C_32", idSet.contains(ID_C_32));
+        assertTrue("Missing ID_C_64", idSet.contains(ID_C_64));
+        for (IModuleDef module : modules) {
+            checkArgs(module);
+        }
     }
 
-    private void createConfig(File testsDir, String name, String token) throws IOException {
-        File config = new File(testsDir, String.format("%s.config", name));
-        String preparer = "";
-        if (token != null) {
-            preparer = String.format(TOKEN, token);
-        }
-        PrintWriter writer = null;
-        try {
-            writer = new PrintWriter(config);
-            writer.format(CONFIG, preparer);
-            writer.flush();
-        } finally {
-            if (writer != null) {
-                writer.close();
-            }
-        }
+    private void checkArgs(IModuleDef module) {
+        IRemoteTest test = module.getTest();
+        assertTrue("Incorrect test type", test instanceof TestStub);
+        TestStub stub = (TestStub) test;
+        assertEquals("Incorrect test arg", "bar", stub.mFoo);
+        assertEquals("Incorrect module arg", "foobar", stub.mBlah);
     }
 }
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/TestStub.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/TestStub.java
new file mode 100644
index 0000000..8a6ab69
--- /dev/null
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/TestStub.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 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.tradefed.config.Option;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.testtype.IRemoteTest;
+
+public class TestStub implements IRemoteTest {
+
+    @Option(name = "module")
+    String mModule;
+    @Option(name = "foo")
+    String mFoo;
+    @Option(name = "blah")
+    String mBlah;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
+        // Do nothing
+    }
+
+}
diff --git a/tools/cts-tradefed/res/config/cts-filtered-sample.xml b/tools/cts-tradefed/res/config/cts-filtered-sample.xml
new file mode 100644
index 0000000..73b98c5
--- /dev/null
+++ b/tools/cts-tradefed/res/config/cts-filtered-sample.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+<configuration description="Runs CTS from a pre-existing CTS installation">
+
+    <include name="common-compatibility-config" />
+
+    <option name="compatibility:plan" value="cts-filtered-sample" />
+
+    <!-- Tell all AndroidJUnitTests to only run the medium sized tests -->
+    <option name="compatibility:test-arg" value="com.android.tradefed.testtype.AndroidJUnitTest:size:medium" />
+
+    <!-- Tell 64bit CtsContentTestCases which timeout to use -->
+    <option name="compatibility:module-arg" value="arm64-v8a CtsContentTestCases:test-timeout:600" />
+
+    <!-- Include CtsGestureTestCases but only run the tests on arm32 -->
+    <option name="compatibility:include-filter" value="armeabi-v7a CtsGestureTestCases" />
+
+    <!-- Exclude CtsMediaStressTestCases -->
+    <option name="compatibility:exclude-filter" value="CtsMediaStressTestCases" />
+
+    <!-- Include CtsUtilTestCases but only run the small tests -->
+    <option name="compatibility:module-arg" value="CtsUtilTestCases:size:small" />
+
+</configuration>
diff --git a/tools/cts-tradefed/res/config/cts.xml b/tools/cts-tradefed/res/config/cts.xml
index 724c52e..2130d4a 100644
--- a/tools/cts-tradefed/res/config/cts.xml
+++ b/tools/cts-tradefed/res/config/cts.xml
@@ -17,13 +17,9 @@
 
     <include name="everything" />
 
-    <option name="enable-root" value="false" />
+    <option name="compatibility:plan" value="cts" />
 
-    <!-- Example Filters
-    <option name="compatibility-test:include-filter" value="arm64-v8a CtsSampleDeviceTestCases" />
-    <option name="compatibility-test:exclude-filter" value="CtsSampleHostTestCases" />
-    -->
-    <option name="compatibility-test:plan" value="cts" />
+    <option name="enable-root" value="false" />
 
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.SettingsPreparer">
         <option name="device-setting" value="install_non_market_apps"/>