release-request-447544c9-42cd-4c45-a7c6-4f09361c94db-for-git_oc-dr1-release-4277755 snap-temp-L90500000093605838

Change-Id: Ib5bdaedf375ff9b9992d99d486ebeb34146d1887
diff --git a/src/com/android/tradefed/testtype/suite/ITestSuite.java b/src/com/android/tradefed/testtype/suite/ITestSuite.java
index 37a0e60..abd20e3 100644
--- a/src/com/android/tradefed/testtype/suite/ITestSuite.java
+++ b/src/com/android/tradefed/testtype/suite/ITestSuite.java
@@ -169,6 +169,7 @@
                             config.getKey(),
                             config.getValue().getTests(),
                             config.getValue().getTargetPreparers(),
+                            config.getValue().getMultiTargetPreparers(),
                             config.getValue().getConfigurationDescription());
             module.setDevice(mDevice);
             module.setBuild(mBuildInfo);
diff --git a/src/com/android/tradefed/testtype/suite/ModuleDefinition.java b/src/com/android/tradefed/testtype/suite/ModuleDefinition.java
index 8a0c5b1..8b5e089 100644
--- a/src/com/android/tradefed/testtype/suite/ModuleDefinition.java
+++ b/src/com/android/tradefed/testtype/suite/ModuleDefinition.java
@@ -38,8 +38,10 @@
 import com.android.tradefed.targetprep.ITargetCleaner;
 import com.android.tradefed.targetprep.ITargetPreparer;
 import com.android.tradefed.targetprep.TargetSetupError;
+import com.android.tradefed.targetprep.multi.IMultiTargetPreparer;
 import com.android.tradefed.testtype.IBuildReceiver;
 import com.android.tradefed.testtype.IDeviceTest;
+import com.android.tradefed.testtype.IMultiDeviceTest;
 import com.android.tradefed.testtype.IRemoteTest;
 import com.android.tradefed.testtype.IRuntimeHintProvider;
 import com.android.tradefed.testtype.ITestCollector;
@@ -71,8 +73,10 @@
     private Collection<IRemoteTest> mTests = null;
     private List<ITargetPreparer> mPreparers = new ArrayList<>();
     private List<ITargetCleaner> mCleaners = new ArrayList<>();
+    private List<IMultiTargetPreparer> mMultiPreparers = new ArrayList<>();
     private IBuildInfo mBuild;
     private ITestDevice mDevice;
+    private Map<ITestDevice, IBuildInfo> mDeviceInfos;
     private boolean mCollectTestsOnly = false;
 
     private List<TestRunResult> mTestsResults = new ArrayList<>();
@@ -101,6 +105,7 @@
             String name,
             Collection<IRemoteTest> tests,
             List<ITargetPreparer> preparers,
+            List<IMultiTargetPreparer> multiPreparers,
             ConfigurationDescriptor configDescriptor) {
         mId = name;
         mTests = tests;
@@ -114,6 +119,8 @@
                     MODULE_ABI, configDescriptor.getAbi().getName());
         }
 
+        mMultiPreparers.addAll(multiPreparers);
+
         for (ITargetPreparer preparer : preparers) {
             mPreparers.add(preparer);
             if (preparer instanceof ITargetCleaner) {
@@ -193,6 +200,13 @@
     }
 
     /**
+     * Inject the {@link Map} of {@link ITestDevice} and {@link IBuildInfo} for the configuration.
+     */
+    public void setDeviceInfos(Map<ITestDevice, IBuildInfo> deviceInfos) {
+        mDeviceInfos = deviceInfos;
+    }
+
+    /**
      * Run all the {@link IRemoteTest} contained in the module and use all the preparers before and
      * after to setup and clean the device.
      *
@@ -225,6 +239,17 @@
                 break;
             }
         }
+        // Skip multi-preparation if preparation already failed.
+        if (preparationException == null) {
+            for (IMultiTargetPreparer multiPreparer : mMultiPreparers) {
+                preparationException = runMultiPreparerSetup(multiPreparer, listener);
+                if (preparationException != null) {
+                    mIsFailedModule = true;
+                    CLog.e("Some preparation step failed. failing the module %s", getId());
+                    break;
+                }
+            }
+        }
         mElapsedPreparation = getCurrentTime() - prepStartTime;
         // Run the tests
         try {
@@ -257,6 +282,9 @@
                 if (test instanceof IDeviceTest) {
                     ((IDeviceTest) test).setDevice(mDevice);
                 }
+                if (test instanceof IMultiDeviceTest) {
+                    ((IMultiDeviceTest) test).setDeviceInfos(mDeviceInfos);
+                }
                 if (test instanceof ISystemStatusCheckerReceiver) {
                     // We do not pass down Status checker because they are already running at the
                     // top level suite.
@@ -314,6 +342,12 @@
             long cleanStartTime = getCurrentTime();
             try {
                 // Tear down
+                List<IMultiTargetPreparer> cleanerList = new ArrayList<>(mMultiPreparers);
+                Collections.reverse(cleanerList);
+                for (IMultiTargetPreparer multiCleaner : cleanerList) {
+                    CLog.d("Multi cleaner: %s", multiCleaner.getClass().getSimpleName());
+                    multiCleaner.tearDown(mModuleInvocationContext, null);
+                }
                 for (ITargetCleaner cleaner : mCleaners) {
                     CLog.d("Cleaner: %s", cleaner.getClass().getSimpleName());
                     cleaner.tearDown(mDevice, mBuild, null);
@@ -430,6 +464,23 @@
         }
     }
 
+    /** Run all multi target preparer step. */
+    private Exception runMultiPreparerSetup(IMultiTargetPreparer preparer, ITestLogger logger) {
+        CLog.d("Multi preparer: %s", preparer.getClass().getSimpleName());
+        try {
+            // set the logger in case they need it.
+            if (preparer instanceof ITestLoggerReceiver) {
+                ((ITestLoggerReceiver) preparer).setTestLogger(logger);
+            }
+            preparer.setUp(mModuleInvocationContext);
+            return null;
+        } catch (BuildError | TargetSetupError | DeviceNotAvailableException e) {
+            CLog.e("Unexpected Exception from preparer: %s", preparer.getClass().getName());
+            CLog.e(e);
+            return e;
+        }
+    }
+
     /** Returns the current time. */
     private long getCurrentTime() {
         return System.currentTimeMillis();
diff --git a/src/com/android/tradefed/testtype/suite/ModuleSplitter.java b/src/com/android/tradefed/testtype/suite/ModuleSplitter.java
index 83d6c19..8f3a17a 100644
--- a/src/com/android/tradefed/testtype/suite/ModuleSplitter.java
+++ b/src/com/android/tradefed/testtype/suite/ModuleSplitter.java
@@ -19,6 +19,7 @@
 import com.android.tradefed.config.IConfiguration;
 import com.android.tradefed.config.OptionCopier;
 import com.android.tradefed.targetprep.ITargetPreparer;
+import com.android.tradefed.targetprep.multi.IMultiTargetPreparer;
 import com.android.tradefed.testtype.IAbiReceiver;
 import com.android.tradefed.testtype.IRemoteTest;
 import com.android.tradefed.testtype.IShardableTest;
@@ -97,7 +98,8 @@
                             new ModuleDefinition(
                                     moduleName,
                                     config.getTests(),
-                                    clonePreparers(config),
+                                    clonePreparers(config.getTargetPreparers()),
+                                    clonePreparers(config.getMultiTargetPreparers()),
                                     config.getConfigurationDescription());
                     currentList.add(module);
                 } else {
@@ -121,7 +123,8 @@
                                     new ModuleDefinition(
                                             moduleName,
                                             shardedTests,
-                                            clonePreparers(config),
+                                            clonePreparers(config.getTargetPreparers()),
+                                            clonePreparers(config.getMultiTargetPreparers()),
                                             config.getConfigurationDescription());
                             currentList.add(module);
                         }
@@ -155,7 +158,8 @@
                 new ModuleDefinition(
                         moduleName,
                         testList,
-                        clonePreparers(config),
+                        clonePreparers(config.getTargetPreparers()),
+                        clonePreparers(config.getMultiTargetPreparers()),
                         config.getConfigurationDescription());
         currentList.add(module);
     }
@@ -171,16 +175,17 @@
     }
 
     /**
-     * Deep clone a list of {@link ITargetPreparer}. We are ensured to find a default constructor
-     * with no arguments since that's the expectation from Tradefed when loading configuration.
-     * Cloning preparers is required since they may be stateful and we cannot share instance across
-     * devices.
+     * Deep clone a list of {@link ITargetPreparer} or {@link IMultiTargetPreparer}. We are ensured
+     * to find a default constructor with no arguments since that's the expectation from Tradefed
+     * when loading configuration. Cloning preparers is required since they may be stateful and we
+     * cannot share instance across devices.
      */
-    private static List<ITargetPreparer> clonePreparers(IConfiguration config) {
-        List<ITargetPreparer> clones = new ArrayList<>();
-        for (ITargetPreparer prep : config.getTargetPreparers()) {
+    private static <T> List<T> clonePreparers(List<T> preparerList) {
+        List<T> clones = new ArrayList<>();
+        for (T prep : preparerList) {
             try {
-                ITargetPreparer clone = prep.getClass().newInstance();
+                @SuppressWarnings("unchecked")
+                T clone = (T) prep.getClass().newInstance();
                 OptionCopier.copyOptions(prep, clone);
                 // Ensure we copy the Abi too.
                 if (clone instanceof IAbiReceiver) {
diff --git a/src/com/android/tradefed/testtype/suite/ValidateSuiteConfigHelper.java b/src/com/android/tradefed/testtype/suite/ValidateSuiteConfigHelper.java
index 85a8b50..914037f 100644
--- a/src/com/android/tradefed/testtype/suite/ValidateSuiteConfigHelper.java
+++ b/src/com/android/tradefed/testtype/suite/ValidateSuiteConfigHelper.java
@@ -17,6 +17,7 @@
 
 import com.android.tradefed.build.StubBuildProvider;
 import com.android.tradefed.config.IConfiguration;
+import com.android.tradefed.config.IDeviceConfiguration;
 import com.android.tradefed.result.TextResultReporter;
 
 /**
@@ -35,6 +36,15 @@
         if (!config.getBuildProvider().getClass().isAssignableFrom(StubBuildProvider.class)) {
             return false;
         }
+        // if a multi device config is presented, ensure none of the devices define a build_provider
+        for (IDeviceConfiguration deviceConfig : config.getDeviceConfig()) {
+            if (!deviceConfig
+                    .getBuildProvider()
+                    .getClass()
+                    .isAssignableFrom(StubBuildProvider.class)) {
+                return false;
+            }
+        }
         if (config.getTestInvocationListeners().size() != 1) {
             return false;
         }
diff --git a/tests/src/com/android/tradefed/testtype/suite/ModuleDefinitionTest.java b/tests/src/com/android/tradefed/testtype/suite/ModuleDefinitionTest.java
index 4a8cf31..5b98c4a 100644
--- a/tests/src/com/android/tradefed/testtype/suite/ModuleDefinitionTest.java
+++ b/tests/src/com/android/tradefed/testtype/suite/ModuleDefinitionTest.java
@@ -30,6 +30,7 @@
 import com.android.tradefed.targetprep.ITargetCleaner;
 import com.android.tradefed.targetprep.ITargetPreparer;
 import com.android.tradefed.targetprep.TargetSetupError;
+import com.android.tradefed.targetprep.multi.IMultiTargetPreparer;
 import com.android.tradefed.testtype.Abi;
 import com.android.tradefed.testtype.IBuildReceiver;
 import com.android.tradefed.testtype.IDeviceTest;
@@ -56,6 +57,7 @@
     private ITargetPreparer mMockPrep;
     private ITargetCleaner mMockCleaner;
     private List<ITargetPreparer> mTargetPrepList;
+    private List<IMultiTargetPreparer> mMultiTargetPrepList;
     private ITestInvocationListener mMockListener;
     private IBuildInfo mMockBuildInfo;
     private ITestDevice mMockDevice;
@@ -117,11 +119,16 @@
         mMockCleaner = EasyMock.createMock(ITargetCleaner.class);
         mTargetPrepList.add(mMockPrep);
         mTargetPrepList.add(mMockCleaner);
+        mMultiTargetPrepList = new ArrayList<>();
         mMockBuildInfo = EasyMock.createMock(IBuildInfo.class);
         mMockDevice = EasyMock.createMock(ITestDevice.class);
         mModule =
                 new ModuleDefinition(
-                        MODULE_NAME, mTestList, mTargetPrepList, new ConfigurationDescriptor());
+                        MODULE_NAME,
+                        mTestList,
+                        mTargetPrepList,
+                        mMultiTargetPrepList,
+                        new ConfigurationDescriptor());
     }
 
     /**
@@ -197,7 +204,11 @@
         });
         mModule =
                 new ModuleDefinition(
-                        MODULE_NAME, mTestList, mTargetPrepList, new ConfigurationDescriptor());
+                        MODULE_NAME,
+                        mTestList,
+                        mTargetPrepList,
+                        mMultiTargetPrepList,
+                        new ConfigurationDescriptor());
         mMockCleaner.tearDown(EasyMock.eq(mMockDevice), EasyMock.eq(mMockBuildInfo),
                 EasyMock.isNull());
         mMockListener.testRunStarted(EasyMock.eq(MODULE_NAME), EasyMock.eq(1));
@@ -222,7 +233,11 @@
         testList.add(new TestObject("run1", testCount, false));
         mModule =
                 new ModuleDefinition(
-                        MODULE_NAME, testList, mTargetPrepList, new ConfigurationDescriptor());
+                        MODULE_NAME,
+                        testList,
+                        mTargetPrepList,
+                        mMultiTargetPrepList,
+                        new ConfigurationDescriptor());
         mModule.setBuild(mMockBuildInfo);
         mModule.setDevice(mMockDevice);
         mMockPrep.setUp(EasyMock.eq(mMockDevice), EasyMock.eq(mMockBuildInfo));
@@ -254,7 +269,11 @@
         testList.add(new TestObject("run1", testCount, true));
         mModule =
                 new ModuleDefinition(
-                        MODULE_NAME, testList, mTargetPrepList, new ConfigurationDescriptor());
+                        MODULE_NAME,
+                        testList,
+                        mTargetPrepList,
+                        mMultiTargetPrepList,
+                        new ConfigurationDescriptor());
         mModule.setBuild(mMockBuildInfo);
         mModule.setDevice(mMockDevice);
         mMockPrep.setUp(EasyMock.eq(mMockDevice), EasyMock.eq(mMockBuildInfo));
@@ -297,7 +316,9 @@
         descriptor.setAbi(new Abi("arm", "32"));
         List<IRemoteTest> testList = new ArrayList<>();
         testList.add(new TestObject("run1", testCount, false));
-        mModule = new ModuleDefinition(MODULE_NAME, testList, mTargetPrepList, descriptor);
+        mModule =
+                new ModuleDefinition(
+                        MODULE_NAME, testList, mTargetPrepList, mMultiTargetPrepList, descriptor);
         // Check that the invocation module created has expected informations
         IInvocationContext moduleContext = mModule.getModuleInvocationContext();
         assertEquals(
diff --git a/tests/src/com/android/tradefed/testtype/suite/ValidateSuiteConfigHelperTest.java b/tests/src/com/android/tradefed/testtype/suite/ValidateSuiteConfigHelperTest.java
index f5e5a6b..3caa45a 100644
--- a/tests/src/com/android/tradefed/testtype/suite/ValidateSuiteConfigHelperTest.java
+++ b/tests/src/com/android/tradefed/testtype/suite/ValidateSuiteConfigHelperTest.java
@@ -20,7 +20,9 @@
 
 import com.android.tradefed.build.LocalFolderBuildProvider;
 import com.android.tradefed.config.Configuration;
+import com.android.tradefed.config.DeviceConfigurationHolder;
 import com.android.tradefed.config.IConfiguration;
+import com.android.tradefed.config.IDeviceConfiguration;
 import com.android.tradefed.result.CollectingTestListener;
 import com.android.tradefed.result.ITestInvocationListener;
 import com.android.tradefed.result.TextResultReporter;
@@ -50,6 +52,21 @@
         assertFalse(ValidateSuiteConfigHelper.validateConfig(config));
     }
 
+    /**
+     * Test that a config using the device holder config (multi device) is correctly rejected since
+     * it is not using the default build_provider.
+     */
+    @Test
+    public void testNotRunningAsSuite_MultiDevice_buildProvider() throws Exception {
+        IConfiguration config = new Configuration("test", "test description");
+        // LocalFolderBuildProvider extends the default StubBuildProvider but is still correctly
+        // rejected.
+        IDeviceConfiguration deviceConfig = new DeviceConfigurationHolder("default");
+        deviceConfig.addSpecificConfig(new LocalFolderBuildProvider());
+        config.setDeviceConfig(deviceConfig);
+        assertFalse(ValidateSuiteConfigHelper.validateConfig(config));
+    }
+
     /** Test that a config with a result reporter cannot run as suite. */
     @Test
     public void testNotRunningAsSuite_resultReporter() {