Home | History | Annotate | Download | only in suite
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.tradefed.testtype.suite;
     17 
     18 import com.android.ddmlib.Log.LogLevel;
     19 import com.android.ddmlib.testrunner.TestIdentifier;
     20 import com.android.ddmlib.testrunner.TestResult;
     21 import com.android.ddmlib.testrunner.TestRunResult;
     22 import com.android.tradefed.build.IBuildInfo;
     23 import com.android.tradefed.config.ConfigurationDescriptor;
     24 import com.android.tradefed.device.DeviceNotAvailableException;
     25 import com.android.tradefed.device.DeviceUnresponsiveException;
     26 import com.android.tradefed.device.ITestDevice;
     27 import com.android.tradefed.invoker.IInvocationContext;
     28 import com.android.tradefed.invoker.InvocationContext;
     29 import com.android.tradefed.log.ILogRegistry.EventType;
     30 import com.android.tradefed.log.ITestLogger;
     31 import com.android.tradefed.log.LogRegistry;
     32 import com.android.tradefed.log.LogUtil.CLog;
     33 import com.android.tradefed.result.ITestInvocationListener;
     34 import com.android.tradefed.result.ITestLoggerReceiver;
     35 import com.android.tradefed.result.ResultForwarder;
     36 import com.android.tradefed.suite.checker.ISystemStatusCheckerReceiver;
     37 import com.android.tradefed.targetprep.BuildError;
     38 import com.android.tradefed.targetprep.ITargetCleaner;
     39 import com.android.tradefed.targetprep.ITargetPreparer;
     40 import com.android.tradefed.targetprep.TargetSetupError;
     41 import com.android.tradefed.targetprep.multi.IMultiTargetPreparer;
     42 import com.android.tradefed.testtype.IBuildReceiver;
     43 import com.android.tradefed.testtype.IDeviceTest;
     44 import com.android.tradefed.testtype.IInvocationContextReceiver;
     45 import com.android.tradefed.testtype.IMultiDeviceTest;
     46 import com.android.tradefed.testtype.IRemoteTest;
     47 import com.android.tradefed.testtype.IRuntimeHintProvider;
     48 import com.android.tradefed.testtype.ITestCollector;
     49 import com.android.tradefed.util.StreamUtil;
     50 
     51 import com.google.common.annotations.VisibleForTesting;
     52 
     53 import java.io.PrintWriter;
     54 import java.io.StringWriter;
     55 import java.util.ArrayList;
     56 import java.util.Collection;
     57 import java.util.Collections;
     58 import java.util.HashMap;
     59 import java.util.List;
     60 import java.util.Map;
     61 
     62 /**
     63  * Container for the test run configuration. This class is an helper to prepare and run the tests.
     64  */
     65 public class ModuleDefinition implements Comparable<ModuleDefinition>, ITestCollector {
     66 
     67     /** key names used for saving module info into {@link IInvocationContext} */
     68     public static final String MODULE_NAME = "module-name";
     69     public static final String MODULE_ABI = "module-abi";
     70 
     71     private final IInvocationContext mModuleInvocationContext;
     72 
     73     private final String mId;
     74     private Collection<IRemoteTest> mTests = null;
     75     private List<ITargetPreparer> mPreparers = new ArrayList<>();
     76     private List<ITargetCleaner> mCleaners = new ArrayList<>();
     77     private List<IMultiTargetPreparer> mMultiPreparers = new ArrayList<>();
     78     private IBuildInfo mBuild;
     79     private ITestDevice mDevice;
     80     private Map<ITestDevice, IBuildInfo> mDeviceInfos;
     81     private boolean mCollectTestsOnly = false;
     82 
     83     private List<TestRunResult> mTestsResults = new ArrayList<>();
     84     private int mExpectedTests = 0;
     85     private boolean mIsFailedModule = false;
     86 
     87     // Tracking of preparers performance
     88     private long mElapsedPreparation = 0l;
     89     private long mElapsedTearDown = 0l;
     90 
     91     private long mElapsedTest = 0l;
     92 
     93     public static final String PREPARATION_TIME = "PREP_TIME";
     94     public static final String TEAR_DOWN_TIME = "TEARDOWN_TIME";
     95     public static final String TEST_TIME = "TEST_TIME";
     96 
     97     /**
     98      * Constructor
     99      *
    100      * @param name unique name of the test configuration.
    101      * @param tests list of {@link IRemoteTest} that needs to run.
    102      * @param preparers list of {@link ITargetPreparer} to be used to setup the device.
    103      * @param configDescriptor the {@link ConfigurationDescriptor} of the underlying module config.
    104      */
    105     public ModuleDefinition(
    106             String name,
    107             Collection<IRemoteTest> tests,
    108             List<ITargetPreparer> preparers,
    109             List<IMultiTargetPreparer> multiPreparers,
    110             ConfigurationDescriptor configDescriptor) {
    111         mId = name;
    112         mTests = tests;
    113 
    114         mModuleInvocationContext = new InvocationContext();
    115         mModuleInvocationContext.setConfigurationDescriptor(configDescriptor);
    116         mModuleInvocationContext.addInvocationAttribute(MODULE_NAME, mId);
    117         // If available in the suite, add the abi name
    118         if (configDescriptor.getAbi() != null) {
    119             mModuleInvocationContext.addInvocationAttribute(
    120                     MODULE_ABI, configDescriptor.getAbi().getName());
    121         }
    122 
    123         mMultiPreparers.addAll(multiPreparers);
    124 
    125         for (ITargetPreparer preparer : preparers) {
    126             mPreparers.add(preparer);
    127             if (preparer instanceof ITargetCleaner) {
    128                 mCleaners.add((ITargetCleaner) preparer);
    129             }
    130         }
    131         // Reverse cleaner order, so that last target_preparer to setup is first to clean up.
    132         Collections.reverse(mCleaners);
    133     }
    134 
    135     /**
    136      * Returns the next {@link IRemoteTest} from the list of tests. The list of tests of a module
    137      * may be shared with another one in case of sharding.
    138      */
    139     IRemoteTest poll() {
    140         synchronized (mTests) {
    141             if (mTests.isEmpty()) {
    142                 return null;
    143             }
    144             IRemoteTest test = mTests.iterator().next();
    145             mTests.remove(test);
    146             return test;
    147         }
    148     }
    149 
    150     /**
    151      * Add some {@link IRemoteTest} to be executed as part of the module. Used when merging two
    152      * modules.
    153      */
    154     void addTests(List<IRemoteTest> test) {
    155         synchronized (mTests) {
    156             mTests.addAll(test);
    157         }
    158     }
    159 
    160     /** Returns the current number of {@link IRemoteTest} waiting to be executed. */
    161     public int numTests() {
    162         synchronized (mTests) {
    163             return mTests.size();
    164         }
    165     }
    166 
    167     /**
    168      * Return True if the Module still has {@link IRemoteTest} to run in its pool. False otherwise.
    169      */
    170     protected boolean hasTests() {
    171         synchronized (mTests) {
    172             return mTests.isEmpty();
    173         }
    174     }
    175 
    176     /** Return the unique module name. */
    177     public String getId() {
    178         return mId;
    179     }
    180 
    181     /**
    182      * {@inheritDoc}
    183      */
    184     @Override
    185     public int compareTo(ModuleDefinition moduleDef) {
    186         return getId().compareTo(moduleDef.getId());
    187     }
    188 
    189     /**
    190      * Inject the {@link IBuildInfo} to be used during the tests.
    191      */
    192     public void setBuild(IBuildInfo build) {
    193         mBuild = build;
    194     }
    195 
    196     /**
    197      * Inject the {@link ITestDevice} to be used during the tests.
    198      */
    199     public void setDevice(ITestDevice device) {
    200         mDevice = device;
    201     }
    202 
    203     /**
    204      * Inject the {@link Map} of {@link ITestDevice} and {@link IBuildInfo} for the configuration.
    205      */
    206     public void setDeviceInfos(Map<ITestDevice, IBuildInfo> deviceInfos) {
    207         mDeviceInfos = deviceInfos;
    208     }
    209 
    210     /**
    211      * Run all the {@link IRemoteTest} contained in the module and use all the preparers before and
    212      * after to setup and clean the device.
    213      *
    214      * @param listener the {@link ITestInvocationListener} where to report results.
    215      * @throws DeviceNotAvailableException in case of device going offline.
    216      */
    217     public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
    218         run(listener, null);
    219     }
    220 
    221     /**
    222      * Run all the {@link IRemoteTest} contained in the module and use all the preparers before and
    223      * after to setup and clean the device.
    224      *
    225      * @param listener the {@link ITestInvocationListener} where to report results.
    226      * @param failureListener a particular listener to collect logs on testFail. Can be null.
    227      * @throws DeviceNotAvailableException in case of device going offline.
    228      */
    229     public void run(ITestInvocationListener listener, TestFailureListener failureListener)
    230             throws DeviceNotAvailableException {
    231         CLog.d("Running module %s", getId());
    232         Exception preparationException = null;
    233         // Setup
    234         long prepStartTime = getCurrentTime();
    235         for (ITargetPreparer preparer : mPreparers) {
    236             preparationException = runPreparerSetup(preparer, listener);
    237             if (preparationException != null) {
    238                 mIsFailedModule = true;
    239                 CLog.e("Some preparation step failed. failing the module %s", getId());
    240                 break;
    241             }
    242         }
    243         // Skip multi-preparation if preparation already failed.
    244         if (preparationException == null) {
    245             for (IMultiTargetPreparer multiPreparer : mMultiPreparers) {
    246                 preparationException = runMultiPreparerSetup(multiPreparer, listener);
    247                 if (preparationException != null) {
    248                     mIsFailedModule = true;
    249                     CLog.e("Some preparation step failed. failing the module %s", getId());
    250                     break;
    251                 }
    252             }
    253         }
    254         mElapsedPreparation = getCurrentTime() - prepStartTime;
    255         // Run the tests
    256         try {
    257             if (preparationException != null) {
    258                 // For reporting purpose we create a failure placeholder with the error stack
    259                 // similar to InitializationError of JUnit.
    260                 TestIdentifier testid =
    261                         new TestIdentifier(
    262                                 preparationException.getClass().getCanonicalName(),
    263                                 "preparationError");
    264                 listener.testRunStarted(getId(), 1);
    265                 listener.testStarted(testid);
    266                 StringWriter sw = new StringWriter();
    267                 preparationException.printStackTrace(new PrintWriter(sw));
    268                 listener.testFailed(testid, sw.toString());
    269                 listener.testEnded(testid, Collections.emptyMap());
    270                 listener.testRunFailed(sw.toString());
    271                 Map<String, String> metrics = new HashMap<>();
    272                 metrics.put(TEST_TIME, "0");
    273                 listener.testRunEnded(0, metrics);
    274                 return;
    275             }
    276             mElapsedTest = getCurrentTime();
    277             while (true) {
    278                 IRemoteTest test = poll();
    279                 if (test == null) {
    280                     return;
    281                 }
    282 
    283                 if (test instanceof IBuildReceiver) {
    284                     ((IBuildReceiver) test).setBuild(mBuild);
    285                 }
    286                 if (test instanceof IDeviceTest) {
    287                     ((IDeviceTest) test).setDevice(mDevice);
    288                 }
    289                 if (test instanceof IMultiDeviceTest) {
    290                     ((IMultiDeviceTest) test).setDeviceInfos(mDeviceInfos);
    291                 }
    292                 if (test instanceof IInvocationContextReceiver) {
    293                     ((IInvocationContextReceiver) test)
    294                             .setInvocationContext(mModuleInvocationContext);
    295                 }
    296                 if (test instanceof ISystemStatusCheckerReceiver) {
    297                     // We do not pass down Status checker because they are already running at the
    298                     // top level suite.
    299                     ((ISystemStatusCheckerReceiver) test).setSystemStatusChecker(new ArrayList<>());
    300                 }
    301                 if (test instanceof ITestCollector) {
    302                     ((ITestCollector) test).setCollectTestsOnly(mCollectTestsOnly);
    303                 }
    304 
    305                 // Run the test, only in case of DeviceNotAvailable we exit the module
    306                 // execution in order to execute as much as possible.
    307                 ModuleListener moduleListener = new ModuleListener(listener);
    308                 List<ITestInvocationListener> currentTestListener = new ArrayList<>();
    309                 if (failureListener != null) {
    310                     currentTestListener.add(failureListener);
    311                 }
    312                 currentTestListener.add(moduleListener);
    313                 try {
    314                     test.run(new ResultForwarder(currentTestListener));
    315                 } catch (RuntimeException re) {
    316                     CLog.e("Module '%s' - test '%s' threw exception:", getId(), test.getClass());
    317                     CLog.e(re);
    318                     CLog.e("Proceeding to the next test.");
    319                     reportFailure(new ResultForwarder(currentTestListener), re.getMessage());
    320                 } catch (DeviceUnresponsiveException due) {
    321                     // being able to catch a DeviceUnresponsiveException here implies that
    322                     // recovery was successful, and test execution should proceed to next
    323                     // module.
    324                     CLog.w(
    325                             "Ignored DeviceUnresponsiveException because recovery was "
    326                                     + "successful, proceeding with next module. Stack trace:");
    327                     CLog.w(due);
    328                     CLog.w("Proceeding to the next test.");
    329                     reportFailure(new ResultForwarder(currentTestListener), due.getMessage());
    330                 } catch (DeviceNotAvailableException dnae) {
    331                     // We do special logging of some information in Context of the module for easier
    332                     // debugging.
    333                     CLog.e(
    334                             "Module %s threw a DeviceNotAvailableException on device %s during test %s",
    335                             getId(), mDevice.getSerialNumber(), test.getClass());
    336                     CLog.e(dnae);
    337                     // log an events
    338                     logDeviceEvent(
    339                             EventType.MODULE_DEVICE_NOT_AVAILABLE,
    340                             mDevice.getSerialNumber(),
    341                             dnae,
    342                             getId());
    343                     throw dnae;
    344                 } finally {
    345                     mTestsResults.addAll(moduleListener.getRunResults());
    346                     mExpectedTests += moduleListener.getNumTotalTests();
    347                 }
    348             }
    349         } finally {
    350             long cleanStartTime = getCurrentTime();
    351             try {
    352                 // Tear down
    353                 List<IMultiTargetPreparer> cleanerList = new ArrayList<>(mMultiPreparers);
    354                 Collections.reverse(cleanerList);
    355                 for (IMultiTargetPreparer multiCleaner : cleanerList) {
    356                     CLog.d("Multi cleaner: %s", multiCleaner.getClass().getSimpleName());
    357                     multiCleaner.tearDown(mModuleInvocationContext, null);
    358                 }
    359                 for (ITargetCleaner cleaner : mCleaners) {
    360                     CLog.d("Cleaner: %s", cleaner.getClass().getSimpleName());
    361                     cleaner.tearDown(mDevice, mBuild, null);
    362                 }
    363             } catch (DeviceNotAvailableException tearDownException) {
    364                 CLog.e(
    365                         "Module %s failed during tearDown with: %s",
    366                         getId(), StreamUtil.getStackTrace(tearDownException));
    367                 throw tearDownException;
    368             } finally {
    369                 if (failureListener != null) {
    370                     failureListener.join();
    371                 }
    372                 mElapsedTearDown = getCurrentTime() - cleanStartTime;
    373                 // finalize results
    374                 if (preparationException == null) {
    375                     reportFinalResults(listener, mExpectedTests, mTestsResults);
    376                 }
    377             }
    378         }
    379     }
    380 
    381     private void reportFailure(ITestInvocationListener listener, String errorMessage) {
    382         listener.testRunFailed(errorMessage);
    383     }
    384 
    385     /** Helper to log the device events. */
    386     private void logDeviceEvent(EventType event, String serial, Throwable t, String moduleId) {
    387         Map<String, String> args = new HashMap<>();
    388         args.put("serial", serial);
    389         args.put("trace", StreamUtil.getStackTrace(t));
    390         args.put("module-id", moduleId);
    391         LogRegistry.getLogRegistry().logEvent(LogLevel.DEBUG, event, args);
    392     }
    393 
    394     /** Finalize results to report them all and count if there are missing tests. */
    395     private void reportFinalResults(
    396             ITestInvocationListener listener,
    397             int totalExpectedTests,
    398             List<TestRunResult> listResults) {
    399         long elapsedTime = 0l;
    400         Map<String, String> metrics = new HashMap<>();
    401         listener.testRunStarted(getId(), totalExpectedTests);
    402 
    403         int numResults = 0;
    404         for (TestRunResult runResult : listResults) {
    405             numResults += runResult.getTestResults().size();
    406             forwardTestResults(runResult.getTestResults(), listener);
    407             if (runResult.isRunFailure()) {
    408                 listener.testRunFailed(runResult.getRunFailureMessage());
    409                 mIsFailedModule = true;
    410             }
    411             elapsedTime += runResult.getElapsedTime();
    412             // put metrics from the tests
    413             metrics.putAll(runResult.getRunMetrics());
    414         }
    415         // put metrics from the preparation
    416         metrics.put(PREPARATION_TIME, Long.toString(mElapsedPreparation));
    417         metrics.put(TEAR_DOWN_TIME, Long.toString(mElapsedTearDown));
    418         metrics.put(TEST_TIME, Long.toString(elapsedTime));
    419         if (totalExpectedTests != numResults) {
    420             String error =
    421                     String.format(
    422                             "Module %s only ran %d out of %d expected tests.",
    423                             getId(), numResults, totalExpectedTests);
    424             listener.testRunFailed(error);
    425             CLog.e(error);
    426             mIsFailedModule = true;
    427         }
    428         listener.testRunEnded(getCurrentTime() - mElapsedTest, metrics);
    429     }
    430 
    431     private void forwardTestResults(
    432             Map<TestIdentifier, TestResult> testResults, ITestInvocationListener listener) {
    433         for (Map.Entry<TestIdentifier, TestResult> testEntry : testResults.entrySet()) {
    434             listener.testStarted(testEntry.getKey(), testEntry.getValue().getStartTime());
    435             switch (testEntry.getValue().getStatus()) {
    436                 case FAILURE:
    437                     listener.testFailed(testEntry.getKey(), testEntry.getValue().getStackTrace());
    438                     break;
    439                 case ASSUMPTION_FAILURE:
    440                     listener.testAssumptionFailure(
    441                             testEntry.getKey(), testEntry.getValue().getStackTrace());
    442                     break;
    443                 case IGNORED:
    444                     listener.testIgnored(testEntry.getKey());
    445                     break;
    446                 case INCOMPLETE:
    447                     listener.testFailed(
    448                             testEntry.getKey(), "Test did not complete due to exception.");
    449                     break;
    450                 default:
    451                     break;
    452             }
    453             listener.testEnded(
    454                     testEntry.getKey(),
    455                     testEntry.getValue().getEndTime(),
    456                     testEntry.getValue().getMetrics());
    457         }
    458     }
    459 
    460     /** Run all the prepare steps. */
    461     private Exception runPreparerSetup(ITargetPreparer preparer, ITestLogger logger)
    462             throws DeviceNotAvailableException {
    463         CLog.d("Preparer: %s", preparer.getClass().getSimpleName());
    464         try {
    465             // set the logger in case they need it.
    466             if (preparer instanceof ITestLoggerReceiver) {
    467                 ((ITestLoggerReceiver) preparer).setTestLogger(logger);
    468             }
    469             preparer.setUp(mDevice, mBuild);
    470             return null;
    471         } catch (BuildError | TargetSetupError e) {
    472             CLog.e("Unexpected Exception from preparer: %s", preparer.getClass().getName());
    473             CLog.e(e);
    474             return e;
    475         }
    476     }
    477 
    478     /** Run all multi target preparer step. */
    479     private Exception runMultiPreparerSetup(IMultiTargetPreparer preparer, ITestLogger logger) {
    480         CLog.d("Multi preparer: %s", preparer.getClass().getSimpleName());
    481         try {
    482             // set the logger in case they need it.
    483             if (preparer instanceof ITestLoggerReceiver) {
    484                 ((ITestLoggerReceiver) preparer).setTestLogger(logger);
    485             }
    486             preparer.setUp(mModuleInvocationContext);
    487             return null;
    488         } catch (BuildError | TargetSetupError | DeviceNotAvailableException e) {
    489             CLog.e("Unexpected Exception from preparer: %s", preparer.getClass().getName());
    490             CLog.e(e);
    491             return e;
    492         }
    493     }
    494 
    495     /** Returns the current time. */
    496     private long getCurrentTime() {
    497         return System.currentTimeMillis();
    498     }
    499 
    500     @Override
    501     public void setCollectTestsOnly(boolean collectTestsOnly) {
    502         mCollectTestsOnly = collectTestsOnly;
    503     }
    504 
    505     /** Returns a list of tests that ran in this module. */
    506     List<TestRunResult> getTestsResults() {
    507         return mTestsResults;
    508     }
    509 
    510     /** Returns the number of tests that was expected to be run */
    511     int getNumExpectedTests() {
    512         return mExpectedTests;
    513     }
    514 
    515     /** Returns True if a testRunFailure has been called on the module * */
    516     public boolean hasModuleFailed() {
    517         return mIsFailedModule;
    518     }
    519 
    520     /** {@inheritDoc} */
    521     @Override
    522     public String toString() {
    523         return getId();
    524     }
    525 
    526     /** Returns the approximate time to run all the tests in the module. */
    527     public long getRuntimeHint() {
    528         long hint = 0l;
    529         for (IRemoteTest test : mTests) {
    530             if (test instanceof IRuntimeHintProvider) {
    531                 hint += ((IRuntimeHintProvider) test).getRuntimeHint();
    532             } else {
    533                 hint += 60000;
    534             }
    535         }
    536         return hint;
    537     }
    538 
    539     /** Returns the list of {@link ITargetPreparer} defined for this module. */
    540     @VisibleForTesting
    541     List<ITargetPreparer> getTargetPreparers() {
    542         return mPreparers;
    543     }
    544 
    545     /** Returns the list of {@link IRemoteTest} defined for this module. */
    546     @VisibleForTesting
    547     List<IRemoteTest> getTests() {
    548         return new ArrayList<>(mTests);
    549     }
    550 
    551     /** Returns the {@link IInvocationContext} associated with the module. */
    552     public IInvocationContext getModuleInvocationContext() {
    553         return mModuleInvocationContext;
    554     }
    555 }
    556