am 54323595: Merge "Support sharding cts runs across multiple devices." into honeycomb

* commit '54323595b1253ebbf3506d5ee67bf373608e8193':
  Support sharding cts runs across multiple devices.
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildHelper.java b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildHelper.java
index 30c2433..4a2d56c 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildHelper.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildHelper.java
@@ -16,6 +16,8 @@
 
 package com.android.cts.tradefed.build;
 
+import com.android.tradefed.build.IFolderBuildInfo;
+
 import java.io.File;
 import java.io.FileNotFoundException;
 
@@ -37,15 +39,21 @@
      *
      * @param rootDir the parent folder that contains the "android-cts" directory and all its
      *            contents.
-     * @throws FileNotFoundException if file does not exist
      */
-    public CtsBuildHelper(File rootDir) throws FileNotFoundException {
+    public CtsBuildHelper(File rootDir) {
         mRootDir = rootDir;
         mCtsDir = new File(mRootDir, CTS_DIR_NAME);
-        if (!mCtsDir.exists()) {
-            throw new FileNotFoundException(String.format(
-                    "CTS install folder %s does not exist", mCtsDir.getAbsolutePath()));
-        }
+    }
+
+    /**
+     * Alternate {@link CtsBuildHelper} constructor that takes the {@link IFolderBuildInfo}
+     * representation of a CTS build.
+     *
+     * @param build the {@link IFolderBuildInfo}
+     * @throws FileNotFoundException
+     */
+    public CtsBuildHelper(IFolderBuildInfo build) throws FileNotFoundException {
+        this(build.getRootDir());
     }
 
     /**
@@ -88,27 +96,36 @@
 
     /**
      * @return a {@link File} representing the test cases directory
-     * @throws FileNotFoundException if dir does not exist
      */
-    public File getTestCasesDir() throws FileNotFoundException {
-        File dir = new File(getRepositoryDir(), "testcases");
-        if (!dir.exists()) {
-            throw new FileNotFoundException(String.format(
-                    "CTS test cases directory %s does not exist", dir.getAbsolutePath()));
-        }
-        return dir;
+    public File getTestCasesDir() {
+        return new File(getRepositoryDir(), "testcases");
     }
 
     /**
      * @return a {@link File} representing the test plan directory
-     * @throws FileNotFoundException if dir does not exist
      */
     public File getTestPlansDir() throws FileNotFoundException {
-        File dir = new File(getRepositoryDir(), "plans");
-        if (!dir.exists()) {
+        return new File(getRepositoryDir(), "plans");
+    }
+
+    /**
+     * Check the validity of the CTS build file system structure.
+     * @throws FileNotFoundException if any major directories are missing
+     */
+    public void validateStructure() throws FileNotFoundException {
+        if (!getCtsDir().exists()) {
             throw new FileNotFoundException(String.format(
-                    "CTS test plans directory %s does not exist", dir.getAbsolutePath()));
+                    "CTS install folder %s does not exist", getCtsDir().getAbsolutePath()));
         }
-        return dir;
+        if (!getTestCasesDir().exists()) {
+            throw new FileNotFoundException(String.format(
+                    "CTS test cases folder %s does not exist",
+                    getTestCasesDir().getAbsolutePath()));
+        }
+        if (!getTestPlansDir().exists()) {
+            throw new FileNotFoundException(String.format(
+                    "CTS test plans folder %s does not exist",
+                    getTestPlansDir().getAbsolutePath()));
+        }
     }
 }
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java
index 9dc92f5..8d72cb5 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java
@@ -30,17 +30,17 @@
 public class CtsBuildProvider implements IBuildProvider {
 
     @Option(name="cts-install-path", description="the path to the cts installation to use")
-    private File mCtsRootDir;
+    private String mCtsRootDirPath = System.getProperty("CTS_ROOT");
 
     /**
      * {@inheritDoc}
      */
     public IBuildInfo getBuild() throws BuildRetrievalError {
-        if (mCtsRootDir == null) {
+        if (mCtsRootDirPath == null) {
             throw new IllegalArgumentException("Missing --cts-install-path");
         }
         IFolderBuildInfo ctsBuild = new FolderBuildInfo(0, "cts", "cts");
-        ctsBuild.setRootDir(mCtsRootDir);
+        ctsBuild.setRootDir(new File(mCtsRootDirPath));
         return ctsBuild;
     }
 
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java
index 7c236c3..6bcab73 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java
@@ -93,13 +93,14 @@
             if (!(buildInfo instanceof IFolderBuildInfo)) {
                 throw new IllegalArgumentException("build info is not a IFolderBuildInfo");
             }
+
             IFolderBuildInfo ctsBuild = (IFolderBuildInfo)buildInfo;
             try {
                 CtsBuildHelper buildHelper = new CtsBuildHelper(ctsBuild.getRootDir());
+                buildHelper.validateStructure();
                 mReportDir = buildHelper.getResultsDir();
-
             } catch (FileNotFoundException e) {
-                throw new IllegalArgumentException("unrecognized cts structure", e);
+                throw new IllegalArgumentException("Invalid CTS build", e);
             }
         }
         // create a unique directory for saving results, using old cts host convention
@@ -107,6 +108,8 @@
         mReportDir = new File(mReportDir, getResultTimestamp());
         mReportDir.mkdirs();
         mStartTime = getTimestamp();
+        Log.logAndDisplay(LogLevel.INFO, LOG_TAG, String.format("Using ctsbuild %s",
+                mReportDir.getAbsolutePath()));
     }
 
     /**
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/targetprep/CtsSetup.java b/tools/tradefed-host/src/com/android/cts/tradefed/targetprep/CtsSetup.java
index 8d3ac5c..7d952a3 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/targetprep/CtsSetup.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/targetprep/CtsSetup.java
@@ -16,12 +16,8 @@
 package com.android.cts.tradefed.targetprep;
 
 import com.android.cts.tradefed.build.CtsBuildHelper;
-import com.android.cts.tradefed.testtype.CtsTest;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.build.IFolderBuildInfo;
-import com.android.tradefed.config.ConfigurationException;
-import com.android.tradefed.config.IConfiguration;
-import com.android.tradefed.config.IConfigurationReceiver;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.targetprep.BuildError;
@@ -36,14 +32,12 @@
  * <p/>
  * All the actions performed in this class must work on a production device.
  */
-public class CtsSetup implements ITargetPreparer, IConfigurationReceiver {
+public class CtsSetup implements ITargetPreparer {
 
     private static final String RUNNER_APK_NAME = "android.core.tests.runner.apk";
     // TODO: read this from configuration file rather than hardcoding
     private static final String TEST_STUBS_APK = "CtsTestStubs.apk";
 
-    private IConfiguration mConfiguration = null;
-
     /**
      * Factory method to create a {@link CtsBuildHelper}.
      * <p/>
@@ -56,34 +50,17 @@
     /**
      * {@inheritDoc}
      */
-    public void setConfiguration(IConfiguration configuration) {
-        mConfiguration = configuration;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
     public void setUp(ITestDevice device, IBuildInfo buildInfo) throws TargetSetupError,
             BuildError, DeviceNotAvailableException {
         if (!(buildInfo instanceof IFolderBuildInfo)) {
             throw new IllegalArgumentException("Provided buildInfo is not a IFolderBuildInfo");
         }
-        if (mConfiguration == null) {
-            throw new IllegalStateException("setConfiguration() was not called before setUp");
-        }
         IFolderBuildInfo ctsBuildInfo = (IFolderBuildInfo)buildInfo;
         try {
             CtsBuildHelper buildHelper = createBuildHelper(ctsBuildInfo.getRootDir());
-            // pass necessary build information to the other config objects
-            mConfiguration.injectOptionValue(CtsTest.TEST_CASES_DIR_OPTION,
-                    buildHelper.getTestCasesDir().getAbsolutePath());
-            mConfiguration.injectOptionValue(CtsTest.TEST_PLANS_DIR_OPTION,
-                    buildHelper.getTestPlansDir().getAbsolutePath());
             installCtsPrereqs(device, buildHelper);
         } catch (FileNotFoundException e) {
             throw new TargetSetupError("Invalid CTS installation", e);
-        } catch (ConfigurationException e) {
-            throw new TargetSetupError("Failed to set repository directory options", e);
         }
     }
 
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java
index a1b29b9..6ed55d3 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java
@@ -16,17 +16,22 @@
 
 package com.android.cts.tradefed.testtype;
 
+import com.android.cts.tradefed.build.CtsBuildHelper;
 import com.android.cts.tradefed.device.DeviceInfoCollector;
 import com.android.ddmlib.Log;
 import com.android.ddmlib.Log.LogLevel;
 import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.build.IFolderBuildInfo;
 import com.android.tradefed.config.Option;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.testtype.IBuildReceiver;
 import com.android.tradefed.testtype.IDeviceTest;
 import com.android.tradefed.testtype.IRemoteTest;
 import com.android.tradefed.testtype.IResumableTest;
+import com.android.tradefed.testtype.IShardableTest;
 import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
 
 import java.io.BufferedInputStream;
@@ -39,6 +44,7 @@
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Queue;
 import java.util.Set;
 
 import junit.framework.Test;
@@ -48,12 +54,10 @@
  * <p/>
  * Supports running all the tests contained in a CTS plan, or individual test packages.
  */
-public class CtsTest implements IDeviceTest, IResumableTest {
+public class CtsTest implements IDeviceTest, IResumableTest, IShardableTest, IBuildReceiver {
 
-    private static final String LOG_TAG = "PlanTest";
+    private static final String LOG_TAG = "CtsTest";
 
-    public static final String TEST_CASES_DIR_OPTION = "test-cases-path";
-    public static final String TEST_PLANS_DIR_OPTION = "test-plans-path";
     private static final String PLAN_OPTION = "plan";
     private static final String PACKAGE_OPTION = "package";
     private static final String CLASS_OPTION = "class";
@@ -77,14 +81,6 @@
             description = "run a specific test method, from given --class")
     private String mMethodName = null;
 
-    @Option(name = TEST_CASES_DIR_OPTION, description =
-        "file path to directory containing CTS test cases")
-    private File mTestCaseDir = null;
-
-    @Option(name = TEST_PLANS_DIR_OPTION, description =
-        "file path to directory containing CTS test plans")
-    private File mTestPlanDir = null;
-
     @Option(name = "collect-device-info", description =
         "flag to control whether to collect info from device. Default true")
     private boolean mCollectDeviceInfo = true;
@@ -94,6 +90,11 @@
         "Default false.")
     private boolean mResume = false;
 
+    @Option(name = "shards", description =
+        "shard the tests to run into separately runnable chunks to execute on multiple devices " +
+        "concurrently")
+    private int mShards = 1;
+
     /** data structure for a {@link IRemoteTest} and its known tests */
     private class KnownTests {
         private final IRemoteTest mTestForPackage;
@@ -116,6 +117,8 @@
     /** list of remaining tests to execute */
     private List<KnownTests> mRemainingTests = null;
 
+    private CtsBuildHelper mCtsBuild = null;
+
     /**
      * {@inheritDoc}
      */
@@ -131,24 +134,6 @@
     }
 
     /**
-     * Set the test plan directory.
-     * <p/>
-     * Exposed for unit testing
-     */
-    void setTestPlanDir(File planDir) {
-        mTestPlanDir = planDir;
-    }
-
-    /**
-     * Set the test case directory.
-     * <p/>
-     * Exposed for unit testing
-     */
-    void setTestCaseDir(File testCaseDir) {
-        mTestCaseDir = testCaseDir;
-    }
-
-    /**
      * Set the plan name to run.
      * <p/>
      * Exposed for unit testing
@@ -214,18 +199,51 @@
      * {@inheritDoc}
      */
     @Override
+    public void setBuild(IBuildInfo build) {
+        if (!(build instanceof IFolderBuildInfo)) {
+            throw new IllegalArgumentException(String.format(
+                    "Wrong build type. Expected %s, received %s", IFolderBuildInfo.class.getName(),
+                    build.getClass().getName()));
+        }
+        try {
+            mCtsBuild = new CtsBuildHelper((IFolderBuildInfo)build);
+            mCtsBuild.validateStructure();
+        } catch (FileNotFoundException e) {
+            throw new IllegalArgumentException("Invalid CTS build provided.", e);
+        }
+    }
+
+    /**
+     * Set the CTS build container.
+     * <p/>
+     * Exposed so unit tests can mock the provided build.
+     *
+     * @param buildHelper
+     */
+    void setBuildHelper(CtsBuildHelper buildHelper) {
+        mCtsBuild = buildHelper;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
     public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
-        checkFields();
+        if (getDevice() == null) {
+            throw new IllegalArgumentException("missing device");
+        }
 
         if (mRemainingTests == null) {
+            checkFields();
             mRemainingTests = buildTestsToRun();
         }
         // always collect the device info, even for resumed runs, since test will likely be running
         // on a different device
-        collectDeviceInfo(getDevice(), mTestCaseDir, listener);
+        collectDeviceInfo(getDevice(), mCtsBuild, listener);
 
         while (!mRemainingTests.isEmpty()) {
             KnownTests testPair = mRemainingTests.get(0);
+
             IRemoteTest test = testPair.getTestForPackage();
             if (test instanceof IDeviceTest) {
                 ((IDeviceTest)test).setDevice(getDevice());
@@ -272,8 +290,8 @@
     private void addTestPackage(List<KnownTests> testList, String testUri,
             ITestPackageDef testPackage) {
         if (testPackage != null) {
-            IRemoteTest testForPackage = testPackage.createTest(mTestCaseDir, mClassName,
-                    mMethodName);
+            IRemoteTest testForPackage = testPackage.createTest(mCtsBuild.getTestCasesDir(),
+                    mClassName, mMethodName);
             if (testForPackage != null) {
                 Collection<TestIdentifier> knownTests = testPackage.getTests();
                 testList.add(new KnownTests(testForPackage, knownTests));
@@ -297,7 +315,7 @@
         if (mPlanName != null) {
             Log.i(LOG_TAG, String.format("Executing CTS test plan %s", mPlanName));
             String ctsPlanRelativePath = String.format("%s.xml", mPlanName);
-            File ctsPlanFile = new File(mTestPlanDir, ctsPlanRelativePath);
+            File ctsPlanFile = new File(mCtsBuild.getTestPlansDir(), ctsPlanRelativePath);
             IPlanXmlParser parser = createXmlParser();
             parser.parse(createXmlStream(ctsPlanFile));
             testUris.addAll(parser.getTestUris());
@@ -323,6 +341,39 @@
     }
 
     /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Collection<IRemoteTest> split() {
+        if (mShards <= 1) {
+            return null;
+        }
+        checkFields();
+        List<KnownTests> allTests = buildTestsToRun();
+
+        if (allTests.size() <= 1) {
+            Log.w(LOG_TAG, "no tests to shard!");
+            return null;
+        }
+
+        // treat shardQueue as a circular queue, to sequentially distribute tests among shards
+        Queue<IRemoteTest> shardQueue = new LinkedList<IRemoteTest>();
+        // don't create more shards than the number of tests we have!
+        for (int i = 0; i < mShards && i < allTests.size(); i++) {
+            CtsTest shard = new CtsTest();
+            shard.mRemainingTests = new LinkedList<KnownTests>();
+            shardQueue.add(shard);
+        }
+        while (!allTests.isEmpty()) {
+            KnownTests testPair = allTests.remove(0);
+            CtsTest shard = (CtsTest)shardQueue.poll();
+            shard.mRemainingTests.add(testPair);
+            shardQueue.add(shard);
+        }
+        return shardQueue;
+    }
+
+    /**
      * Runs the device info collector instrumentation on device, and forwards it to test listeners
      * as run metrics.
      * <p/>
@@ -330,11 +381,12 @@
      *
      * @param listeners
      * @throws DeviceNotAvailableException
+     * @throws FileNotFoundException
      */
-    void collectDeviceInfo(ITestDevice device, File testApkDir, ITestInvocationListener listener)
-            throws DeviceNotAvailableException {
+    void collectDeviceInfo(ITestDevice device, CtsBuildHelper ctsBuild,
+            ITestInvocationListener listener) throws DeviceNotAvailableException {
         if (mCollectDeviceInfo) {
-            DeviceInfoCollector.collectDeviceInfo(device, testApkDir, listener);
+            DeviceInfoCollector.collectDeviceInfo(device, ctsBuild.getTestCasesDir(), listener);
         }
     }
 
@@ -344,7 +396,7 @@
      * Exposed for unit testing
      */
     ITestCaseRepo createTestCaseRepo() {
-        return new TestCaseRepo(mTestCaseDir);
+        return new TestCaseRepo(mCtsBuild.getTestCasesDir());
     }
 
     /**
@@ -381,15 +433,8 @@
             throw new IllegalArgumentException(String.format(
                     "Must specify --%s when --%s is used", CLASS_OPTION, METHOD_OPTION));
         }
-        if (getDevice() == null) {
-            throw new IllegalArgumentException("missing device");
-        }
-        if (mTestCaseDir == null) {
-            throw new IllegalArgumentException(String.format("missing %s option",
-                    TEST_CASES_DIR_OPTION));
-        }
-        if (mTestPlanDir == null) {
-            throw new IllegalArgumentException(String.format("missing %s", TEST_PLANS_DIR_OPTION));
+        if (mCtsBuild == null) {
+            throw new IllegalArgumentException("missing CTS build");
         }
     }
 
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/build/StubCtsBuildHelper.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/build/StubCtsBuildHelper.java
index 7ac30df..0fe2f7a 100644
--- a/tools/tradefed-host/tests/src/com/android/cts/tradefed/build/StubCtsBuildHelper.java
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/build/StubCtsBuildHelper.java
@@ -15,40 +15,25 @@
  */
 package com.android.cts.tradefed.build;
 
-import com.android.tradefed.util.FileUtil;
-
 import java.io.File;
 import java.io.FileNotFoundException;
-import java.io.IOException;
 
 /**
  * Stub implementation of CtsBuildHelper that returns empty files for all methods
  */
 public class StubCtsBuildHelper extends CtsBuildHelper {
 
-    public static StubCtsBuildHelper createStubHelper() throws IOException {
-        File tmpFolder= FileUtil.createTempDir("ctstmp");
-        File ctsinstall = new File(tmpFolder, CtsBuildHelper.CTS_DIR_NAME);
-        ctsinstall.mkdirs();
-        return new StubCtsBuildHelper(tmpFolder);
+    public StubCtsBuildHelper()  {
+        super(new File("tmp"));
     }
 
-    private StubCtsBuildHelper(File rootDir) throws FileNotFoundException {
-        super(rootDir);
+    @Override
+    public void validateStructure() {
+        // ignore
     }
 
     @Override
     public File getTestApp(String appFileName) throws FileNotFoundException {
         return new File("tmp");
     }
-
-    @Override
-    public File getTestPlansDir() throws FileNotFoundException {
-        return new File("tmp");
-    }
-
-    @Override
-    public File getTestCasesDir() {
-        return new File("tmp");
-    }
 }
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/targetprep/CtsSetupTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/targetprep/CtsSetupTest.java
index 51cfa29..4385215 100644
--- a/tools/tradefed-host/tests/src/com/android/cts/tradefed/targetprep/CtsSetupTest.java
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/targetprep/CtsSetupTest.java
@@ -17,10 +17,8 @@
 
 import com.android.cts.tradefed.build.CtsBuildHelper;
 import com.android.cts.tradefed.build.StubCtsBuildHelper;
-import com.android.ddmlib.Log;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.build.IFolderBuildInfo;
-import com.android.tradefed.config.IConfiguration;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.targetprep.BuildError;
@@ -29,7 +27,6 @@
 import org.easymock.EasyMock;
 
 import java.io.File;
-import java.io.IOException;
 
 import junit.framework.TestCase;
 
@@ -38,8 +35,6 @@
  */
 public class CtsSetupTest extends TestCase {
 
-    private static final String LOG_TAG = "CtsSetupTest";
-
     private CtsSetup mSetup;
     private ITestDevice mMockDevice;
 
@@ -52,13 +47,7 @@
         mSetup = new CtsSetup() {
             @Override
             CtsBuildHelper createBuildHelper(File rootDir) {
-                try {
-                    return StubCtsBuildHelper.createStubHelper();
-                } catch (IOException e) {
-                    Log.e(LOG_TAG, e);
-                    fail("failed to create stub helper");
-                    return null;
-                }
+                return new StubCtsBuildHelper();
             }
         };
         mMockDevice = EasyMock.createMock(ITestDevice.class);
@@ -79,20 +68,6 @@
     }
 
     /**
-     * Test {@link CtsSetup#setUp(ITestDevice, IBuildInfo)} when a {@link IConfiguration} has not
-     * been provided.
-     */
-    public void testSetUp_missingConfig() throws TargetSetupError, BuildError,
-            DeviceNotAvailableException {
-        try {
-            mSetup.setUp(mMockDevice,  EasyMock.createMock(IFolderBuildInfo.class));
-            fail("IllegalStateException not thrown");
-        } catch (IllegalStateException e) {
-            // expected
-        }
-    }
-
-    /**
      * Test normal case for {@link CtsSetup#setUp(ITestDevice, IBuildInfo)}
      */
     public void testSetUp() throws TargetSetupError, BuildError, DeviceNotAvailableException {
@@ -104,7 +79,6 @@
                 mMockDevice.installPackage((File)EasyMock.anyObject(), EasyMock.anyBoolean()))
                 .andReturn(null)
                 .anyTimes();
-        mSetup.setConfiguration(EasyMock.createMock(IConfiguration.class));
         EasyMock.replay(ctsBuild, mMockDevice);
         mSetup.setUp(mMockDevice, ctsBuild);
     }
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/CtsTestTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/CtsTestTest.java
index 29a2ca8..157e85e 100644
--- a/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/CtsTestTest.java
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/CtsTestTest.java
@@ -15,6 +15,7 @@
  */
 package com.android.cts.tradefed.testtype;
 
+import com.android.cts.tradefed.build.StubCtsBuildHelper;
 import com.android.ddmlib.testrunner.TestIdentifier;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
@@ -45,6 +46,7 @@
     private IPlanXmlParser mMockPlanParser;
     private ITestDevice mMockDevice;
     private ITestInvocationListener mMockListener;
+    private StubCtsBuildHelper mStubBuildHelper;
 
     private static final String PLAN_NAME = "CTS";
 
@@ -58,6 +60,7 @@
         mMockPlanParser = EasyMock.createMock(IPlanXmlParser.class);
         mMockDevice = EasyMock.createMock(ITestDevice.class);
         mMockListener = EasyMock.createNiceMock(ITestInvocationListener.class);
+        mStubBuildHelper = new StubCtsBuildHelper();
 
         mCtsTest = new CtsTest() {
             @Override
@@ -77,9 +80,7 @@
             }
         };
         mCtsTest.setDevice(mMockDevice);
-        // not used, but needs to be non-null
-        mCtsTest.setTestCaseDir(new File("tmp"));
-        mCtsTest.setTestPlanDir(new File("tmp"));
+        mCtsTest.setBuildHelper(mStubBuildHelper);
         // turn off device collection for simplicity
         mCtsTest.setCollectDeviceInfo(false);
     }