Home | History | Annotate | Download | only in testtype
      1 /*
      2  * Copyright (C) 2015 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 
     17 package com.android.compatibility.common.tradefed.testtype;
     18 
     19 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
     20 import com.android.compatibility.common.tradefed.result.InvocationFailureHandler;
     21 import com.android.compatibility.common.tradefed.result.SubPlanHelper;
     22 import com.android.compatibility.common.tradefed.targetprep.NetworkConnectivityChecker;
     23 import com.android.compatibility.common.tradefed.testtype.suite.CompatibilityTestSuite;
     24 import com.android.compatibility.common.tradefed.util.RetryFilterHelper;
     25 import com.android.compatibility.common.tradefed.util.RetryType;
     26 import com.android.compatibility.common.tradefed.util.UniqueModuleCountUtil;
     27 import com.android.compatibility.common.util.IInvocationResult;
     28 import com.android.compatibility.common.util.ResultHandler;
     29 import com.android.compatibility.common.util.TestFilter;
     30 import com.android.ddmlib.Log.LogLevel;
     31 import com.android.tradefed.build.IBuildInfo;
     32 import com.android.tradefed.config.ConfigurationException;
     33 import com.android.tradefed.config.Option;
     34 import com.android.tradefed.config.Option.Importance;
     35 import com.android.tradefed.config.OptionClass;
     36 import com.android.tradefed.config.OptionCopier;
     37 import com.android.tradefed.device.DeviceNotAvailableException;
     38 import com.android.tradefed.device.DeviceUnresponsiveException;
     39 import com.android.tradefed.device.ITestDevice;
     40 import com.android.tradefed.invoker.IInvocationContext;
     41 import com.android.tradefed.invoker.InvocationContext;
     42 import com.android.tradefed.log.ITestLogger;
     43 import com.android.tradefed.log.LogUtil.CLog;
     44 import com.android.tradefed.result.ITestInvocationListener;
     45 import com.android.tradefed.result.InputStreamSource;
     46 import com.android.tradefed.result.LogDataType;
     47 import com.android.tradefed.suite.checker.ISystemStatusChecker;
     48 import com.android.tradefed.suite.checker.ISystemStatusCheckerReceiver;
     49 import com.android.tradefed.suite.checker.StatusCheckerResult;
     50 import com.android.tradefed.suite.checker.StatusCheckerResult.CheckStatus;
     51 import com.android.tradefed.testtype.Abi;
     52 import com.android.tradefed.testtype.IAbi;
     53 import com.android.tradefed.testtype.IBuildReceiver;
     54 import com.android.tradefed.testtype.IDeviceTest;
     55 import com.android.tradefed.testtype.IInvocationContextReceiver;
     56 import com.android.tradefed.testtype.IRemoteTest;
     57 import com.android.tradefed.testtype.IShardableTest;
     58 import com.android.tradefed.testtype.IStrictShardableTest;
     59 import com.android.tradefed.testtype.ITestCollector;
     60 import com.android.tradefed.testtype.suite.TestSuiteInfo;
     61 import com.android.tradefed.util.AbiFormatter;
     62 import com.android.tradefed.util.AbiUtils;
     63 import com.android.tradefed.util.ArrayUtil;
     64 import com.android.tradefed.util.MultiMap;
     65 import com.android.tradefed.util.StreamUtil;
     66 import com.android.tradefed.util.TimeUtil;
     67 
     68 import com.google.common.annotations.VisibleForTesting;
     69 
     70 import java.io.ByteArrayOutputStream;
     71 import java.io.FileNotFoundException;
     72 import java.io.PrintWriter;
     73 import java.util.ArrayList;
     74 import java.util.Arrays;
     75 import java.util.Collection;
     76 import java.util.HashSet;
     77 import java.util.LinkedHashSet;
     78 import java.util.LinkedList;
     79 import java.util.List;
     80 import java.util.Set;
     81 import java.util.concurrent.CountDownLatch;
     82 import java.util.concurrent.TimeUnit;
     83 
     84 /**
     85  * A Test for running Compatibility Suites.
     86  * @deprecated use {@link CompatibilityTestSuite} instead.
     87  */
     88 @Deprecated
     89 @OptionClass(alias = "compatibility")
     90 public class CompatibilityTest implements IDeviceTest, IShardableTest, IBuildReceiver,
     91         IStrictShardableTest, ISystemStatusCheckerReceiver, ITestCollector,
     92         IInvocationContextReceiver {
     93 
     94     public static final String INCLUDE_FILTER_OPTION = "include-filter";
     95     public static final String EXCLUDE_FILTER_OPTION = "exclude-filter";
     96     public static final String SUBPLAN_OPTION = "subplan";
     97     public static final String MODULE_OPTION = "module";
     98     public static final String TEST_OPTION = "test";
     99     public static final String PRECONDITION_ARG_OPTION = "precondition-arg";
    100     public static final String MODULE_ARG_OPTION = "module-arg";
    101     public static final String TEST_ARG_OPTION = "test-arg";
    102     public static final char TEST_OPTION_SHORT_NAME = 't';
    103     public static final String RETRY_OPTION = "retry";
    104     public static final String RETRY_TYPE_OPTION = "retry-type";
    105     public static final String ABI_OPTION = "abi";
    106     public static final String SHARD_OPTION = "shards";
    107     public static final String SKIP_DEVICE_INFO_OPTION = "skip-device-info";
    108     public static final String SKIP_PRECONDITIONS_OPTION = "skip-preconditions";
    109     public static final String SKIP_HOST_ARCH_CHECK = "skip-host-arch-check";
    110     public static final String PRIMARY_ABI_RUN = "primary-abi-only";
    111     public static final String DEVICE_TOKEN_OPTION = "device-token";
    112     public static final String LOGCAT_ON_FAILURE_SIZE_OPTION = "logcat-on-failure-size";
    113 
    114     // Constants for checking invocation or preconditions preparation failure
    115     private static final int NUM_PREP_ATTEMPTS = 10;
    116     private static final int MINUTES_PER_PREP_ATTEMPT = 2;
    117 
    118     @Option(name = SUBPLAN_OPTION,
    119             description = "the subplan to run",
    120             importance = Importance.IF_UNSET)
    121     private String mSubPlan;
    122 
    123     @Option(name = INCLUDE_FILTER_OPTION,
    124             description = "the include module filters to apply.",
    125             importance = Importance.ALWAYS)
    126     private Set<String> mIncludeFilters = new HashSet<>();
    127 
    128     @Option(name = EXCLUDE_FILTER_OPTION,
    129             description = "the exclude module filters to apply.",
    130             importance = Importance.ALWAYS)
    131     private Set<String> mExcludeFilters = new HashSet<>();
    132 
    133     @Option(name = MODULE_OPTION,
    134             shortName = 'm',
    135             description = "the test module to run.",
    136             importance = Importance.IF_UNSET)
    137     private String mModuleName = null;
    138 
    139     @Option(name = TEST_OPTION,
    140             shortName = TEST_OPTION_SHORT_NAME,
    141             description = "the test run.",
    142             importance = Importance.IF_UNSET)
    143     private String mTestName = null;
    144 
    145     @Option(name = PRECONDITION_ARG_OPTION,
    146             description = "the arguments to pass to a precondition. The expected format is"
    147                     + "\"<arg-name>:<arg-value>\"",
    148             importance = Importance.ALWAYS)
    149     private List<String> mPreconditionArgs = new ArrayList<>();
    150 
    151     @Option(name = MODULE_ARG_OPTION,
    152             description = "the arguments to pass to a module. The expected format is"
    153                     + "\"<module-name>:<arg-name>:[<arg-key>:=]<arg-value>\"",
    154             importance = Importance.ALWAYS)
    155     private List<String> mModuleArgs = new ArrayList<>();
    156 
    157     @Option(name = TEST_ARG_OPTION,
    158             description = "the arguments to pass to a test. The expected format is"
    159                     + "\"<test-class>:<arg-name>:[<arg-key>:=]<arg-value>\"",
    160             importance = Importance.ALWAYS)
    161     private List<String> mTestArgs = new ArrayList<>();
    162 
    163     @Option(name = RETRY_OPTION,
    164             shortName = 'r',
    165             description = "retry a previous session's failed and not executed tests.",
    166             importance = Importance.IF_UNSET)
    167     private Integer mRetrySessionId = null;
    168 
    169     @Option(name = RETRY_TYPE_OPTION,
    170             description = "used with " + RETRY_OPTION + ", retry tests of a certain status. "
    171             + "Possible values include \"failed\", \"not_executed\", and \"custom\".",
    172             importance = Importance.IF_UNSET)
    173     private RetryType mRetryType = null;
    174 
    175     @Option(name = ABI_OPTION,
    176             shortName = 'a',
    177             description = "the abi to test.",
    178             importance = Importance.IF_UNSET)
    179     private String mAbiName = null;
    180 
    181     @Option(name = SHARD_OPTION,
    182             description = "split the modules up to run on multiple devices concurrently. "
    183                     + "Deprecated, use --shard-count instead.")
    184     @Deprecated
    185     private int mShards = 1;
    186 
    187     @Option(name = SKIP_DEVICE_INFO_OPTION,
    188             shortName = 'd',
    189             description = "Whether device info collection should be skipped")
    190     private boolean mSkipDeviceInfo = false;
    191 
    192     @Option(name = SKIP_HOST_ARCH_CHECK,
    193             description = "Whether host architecture check should be skipped")
    194     private boolean mSkipHostArchCheck = false;
    195 
    196     @Option(name = SKIP_PRECONDITIONS_OPTION,
    197             shortName = 'o',
    198             description = "Whether preconditions should be skipped")
    199     private boolean mSkipPreconditions = false;
    200 
    201     @Option(name = PRIMARY_ABI_RUN,
    202             description = "Whether to run tests with only the device primary abi. "
    203                     + "This override the --abi option.")
    204     private boolean mPrimaryAbiRun = false;
    205 
    206     @Option(name = DEVICE_TOKEN_OPTION,
    207             description = "Holds the devices' tokens, used when scheduling tests that have"
    208                     + "prerequisites such as requiring a SIM card. Format is <serial>:<token>",
    209             importance = Importance.ALWAYS)
    210     private List<String> mDeviceTokens = new ArrayList<>();
    211 
    212     @Option(name = "bugreport-on-failure",
    213             description = "Take a bugreport on every test failure. " +
    214                     "Warning: can potentially use a lot of disk space.")
    215     private boolean mBugReportOnFailure = false;
    216 
    217     @Option(name = "logcat-on-failure",
    218             description = "Take a logcat snapshot on every test failure.")
    219     private boolean mLogcatOnFailure = false;
    220 
    221     @Option(name = LOGCAT_ON_FAILURE_SIZE_OPTION,
    222             description = "The max number of logcat data in bytes to capture when "
    223             + "--logcat-on-failure is on. Should be an amount that can comfortably fit in memory.")
    224     private int mMaxLogcatBytes = 500 * 1024; // 500K
    225 
    226     @Option(name = "screenshot-on-failure",
    227             description = "Take a screenshot on every test failure.")
    228     private boolean mScreenshotOnFailure = false;
    229 
    230     @Option(name = "reboot-before-test",
    231             description = "Reboot the device before the test suite starts.")
    232     private boolean mRebootBeforeTest = false;
    233 
    234     @Option(name = "reboot-on-failure",
    235             description = "Reboot the device after every test failure.")
    236     private boolean mRebootOnFailure = false;
    237 
    238     @Option(name = "reboot-per-module",
    239             description = "Reboot the device before every module run.")
    240     private boolean mRebootPerModule = false;
    241 
    242     @Option(name = "skip-connectivity-check",
    243             description = "Don't verify device connectivity between module execution.")
    244     private boolean mSkipConnectivityCheck = false;
    245 
    246     @Option(name = "preparer-whitelist",
    247             description = "Only run specific preparers."
    248             + "Specify zero or more ITargetPreparers as canonical class names. "
    249             + "e.g. \"com.android.compatibility.common.tradefed.targetprep.ApkInstaller\" "
    250             + "If not specified, all configured preparers are run.")
    251     private Set<String> mPreparerWhitelist = new HashSet<>();
    252 
    253     @Option(name = "skip-all-system-status-check",
    254             description = "Whether all system status check between modules should be skipped")
    255     private boolean mSkipAllSystemStatusCheck = false;
    256 
    257     @Option(name = "skip-system-status-check",
    258             description = "Disable specific system status checkers."
    259             + "Specify zero or more SystemStatusChecker as canonical class names. e.g. "
    260             + "\"com.android.compatibility.common.tradefed.targetprep.NetworkConnectivityChecker\" "
    261             + "If not specified, all configured or whitelisted system status checkers are run.")
    262     private Set<String> mSystemStatusCheckBlacklist = new HashSet<>();
    263 
    264     @Option(name = "system-status-check-whitelist",
    265             description = "Only run specific system status checkers."
    266             + "Specify zero or more SystemStatusChecker as canonical class names. e.g. "
    267             + "\"com.android.compatibility.common.tradefed.targetprep.NetworkConnectivityChecker\" "
    268             + "If not specified, all configured system status checkers are run.")
    269     private Set<String> mSystemStatusCheckWhitelist = new HashSet<>();
    270 
    271     private List<ISystemStatusChecker> mListCheckers = new ArrayList<>();
    272 
    273     @Option(name = "collect-tests-only",
    274             description = "Only invoke the suite to collect list of applicable test cases. All "
    275                     + "test run callbacks will be triggered, but test execution will not be "
    276                     + "actually carried out.")
    277     private Boolean mCollectTestsOnly = null;
    278 
    279     @Option(name = "module-metadata-include-filter",
    280             description = "Include modules for execution based on matching of metadata fields: "
    281                     + "for any of the specified filter name and value, if a module has a metadata "
    282                     + "field with the same name and value, it will be included. When both module "
    283                     + "inclusion and exclusion rules are applied, inclusion rules will be "
    284                     + "evaluated first. Using this together with test filter inclusion rules may "
    285                     + "result in no tests to execute if the rules don't overlap.")
    286     private MultiMap<String, String> mModuleMetadataIncludeFilter = new MultiMap<>();
    287 
    288     @Option(name = "module-metadata-exclude-filter",
    289             description = "Exclude modules for execution based on matching of metadata fields: "
    290                     + "for any of the specified filter name and value, if a module has a metadata "
    291                     + "field with the same name and value, it will be excluded. When both module "
    292                     + "inclusion and exclusion rules are applied, inclusion rules will be "
    293                     + "evaluated first.")
    294     private MultiMap<String, String> mModuleMetadataExcludeFilter = new MultiMap<>();
    295 
    296     private int mTotalShards;
    297     private Integer mShardIndex = null;
    298     private IModuleRepo mModuleRepo;
    299     private ITestDevice mDevice;
    300     private CompatibilityBuildHelper mBuildHelper;
    301 
    302     // variables used for local sharding scenario
    303     private static CountDownLatch sPreparedLatch;
    304     private boolean mIsLocalSharding = false;
    305     private boolean mIsSharded = false;
    306 
    307     private IInvocationContext mInvocationContext;
    308 
    309     /**
    310      * Create a new {@link CompatibilityTest} that will run the default list of
    311      * modules.
    312      */
    313     public CompatibilityTest() {
    314         this(1 /* totalShards */, new ModuleRepo(), 0);
    315     }
    316 
    317     /**
    318      * Create a new {@link CompatibilityTest} that will run a sublist of
    319      * modules.
    320      */
    321     public CompatibilityTest(int totalShards, IModuleRepo moduleRepo, Integer shardIndex) {
    322         if (totalShards < 1) {
    323             throw new IllegalArgumentException(
    324                     "Must be at least 1 shard. Given:" + totalShards);
    325         }
    326         mTotalShards = totalShards;
    327         mModuleRepo = moduleRepo;
    328         mShardIndex = shardIndex;
    329     }
    330 
    331     /**
    332      * {@inheritDoc}
    333      */
    334     @Override
    335     public ITestDevice getDevice() {
    336         return mDevice;
    337     }
    338 
    339     /**
    340      * {@inheritDoc}
    341      */
    342     @Override
    343     public void setDevice(ITestDevice device) {
    344         mDevice = device;
    345     }
    346 
    347     /**
    348      * {@inheritDoc}
    349      */
    350     @Override
    351     public void setBuild(IBuildInfo buildInfo) {
    352         mBuildHelper = new CompatibilityBuildHelper(buildInfo);
    353     }
    354 
    355     /**
    356      * {@inheritDoc}
    357      */
    358     @Override
    359     public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
    360         try {
    361             List<ISystemStatusChecker> checkers = new ArrayList<>();
    362             // Get system status checkers
    363             if (mSkipAllSystemStatusCheck) {
    364                 CLog.d("Skipping system status checkers");
    365             } else {
    366                 checkSystemStatusBlackAndWhiteList();
    367                 for (ISystemStatusChecker checker : mListCheckers) {
    368                     if(shouldIncludeSystemStatusChecker(checker)) {
    369                         checkers.add(checker);
    370                     }
    371                 }
    372             }
    373 
    374             LinkedList<IModuleDef> modules = initializeModuleRepo();
    375 
    376             mExcludeFilters.clear();
    377             mIncludeFilters.clear();
    378             // Update BuildInfo in each shard to store the original command-line arguments from
    379             // the session to be retried. These arguments will be serialized in the report later.
    380             if (mRetrySessionId != null) {
    381                 loadRetryCommandLineArgs(mRetrySessionId);
    382             }
    383 
    384             listener = new FailureListener(listener, getDevice(), mBugReportOnFailure,
    385                     mLogcatOnFailure, mScreenshotOnFailure, mRebootOnFailure, mMaxLogcatBytes);
    386             int moduleCount = modules.size();
    387             if (moduleCount == 0) {
    388                 CLog.logAndDisplay(LogLevel.INFO, "No module to run on %s.",
    389                         mDevice.getSerialNumber());
    390                 // Make sure we unlock other shards.
    391                 if (sPreparedLatch != null) {
    392                     sPreparedLatch.countDown();
    393                 }
    394                 return;
    395             } else {
    396                 int uniqueModuleCount = UniqueModuleCountUtil.countUniqueModules(modules);
    397                 CLog.logAndDisplay(LogLevel.INFO, "Starting %d test sub-module%s on %s",
    398                         uniqueModuleCount, (uniqueModuleCount > 1) ? "s" : "",
    399                                 mDevice.getSerialNumber());
    400             }
    401 
    402             if (mRebootBeforeTest) {
    403                 CLog.d("Rebooting device before test starts as requested.");
    404                 mDevice.reboot();
    405             }
    406 
    407             if (mSkipConnectivityCheck) {
    408                 String clazz = NetworkConnectivityChecker.class.getCanonicalName();
    409                 CLog.logAndDisplay(LogLevel.INFO, "\"--skip-connectivity-check\" is deprecated, "
    410                         + "please use \"--skip-system-status-check %s\" instead", clazz);
    411                 mSystemStatusCheckBlacklist.add(clazz);
    412             }
    413 
    414             // Set values and run preconditions
    415             boolean isPrepared = true; // whether the device has been successfully prepared
    416             for (int i = 0; i < moduleCount; i++) {
    417                 IModuleDef module = modules.get(i);
    418                 module.setBuild(mBuildHelper.getBuildInfo());
    419                 module.setDevice(mDevice);
    420                 module.setPreparerWhitelist(mPreparerWhitelist);
    421                 // don't set a value if unspecified
    422                 if (mCollectTestsOnly != null) {
    423                     module.setCollectTestsOnly(mCollectTestsOnly);
    424                 }
    425                 isPrepared &= (module.prepare(mSkipPreconditions, mPreconditionArgs));
    426             }
    427             if (!isPrepared) {
    428                 throw new RuntimeException(String.format("Failed preconditions on %s",
    429                         mDevice.getSerialNumber()));
    430             }
    431             if (mIsLocalSharding) {
    432                 try {
    433                     sPreparedLatch.countDown();
    434                     int attempt = 1;
    435                     while(!sPreparedLatch.await(MINUTES_PER_PREP_ATTEMPT, TimeUnit.MINUTES)) {
    436                         if (attempt > NUM_PREP_ATTEMPTS ||
    437                                 InvocationFailureHandler.hasFailed(mBuildHelper)) {
    438                             CLog.logAndDisplay(LogLevel.ERROR,
    439                                     "Incorrect preparation detected, exiting test run from %s",
    440                                     mDevice.getSerialNumber());
    441                             return;
    442                         }
    443                         CLog.logAndDisplay(LogLevel.WARN, "waiting on preconditions");
    444                         attempt++;
    445                     }
    446                 } catch (InterruptedException e) {
    447                     throw new RuntimeException(e);
    448                 }
    449             }
    450             // Module Repo is not useful anymore
    451             mModuleRepo.tearDown();
    452             mModuleRepo = null;
    453             // Run the tests
    454             while (!modules.isEmpty()) {
    455                 // Make sure we remove the modules from the reference list when we are done with
    456                 // them.
    457                 IModuleDef module = modules.poll();
    458                 long start = System.currentTimeMillis();
    459 
    460                 if (mRebootPerModule) {
    461                     if ("user".equals(mDevice.getProperty("ro.build.type"))) {
    462                         CLog.e("reboot-per-module should only be used during development, "
    463                             + "this is a\" user\" build device");
    464                     } else {
    465                         CLog.logAndDisplay(LogLevel.INFO, "Rebooting device before starting next "
    466                             + "module");
    467                         mDevice.reboot();
    468                     }
    469                 }
    470 
    471                 // execute pre module execution checker
    472                 if (checkers != null && !checkers.isEmpty()) {
    473                     runPreModuleCheck(module.getName(), checkers, mDevice, listener);
    474                 }
    475                 IInvocationContext moduleContext = new InvocationContext();
    476                 moduleContext.setConfigurationDescriptor(module.getConfigurationDescriptor());
    477                 moduleContext.addInvocationAttribute(IModuleDef.MODULE_NAME, module.getName());
    478                 moduleContext.addInvocationAttribute(IModuleDef.MODULE_ABI,
    479                         module.getAbi().getName());
    480                 mInvocationContext.setModuleInvocationContext(moduleContext);
    481                 // Populate the module context with devices and builds
    482                 for (String deviceName : mInvocationContext.getDeviceConfigNames()) {
    483                     moduleContext.addAllocatedDevice(
    484                             deviceName, mInvocationContext.getDevice(deviceName));
    485                     moduleContext.addDeviceBuildInfo(
    486                             deviceName, mInvocationContext.getBuildInfo(deviceName));
    487                 }
    488                 module.setInvocationContext(moduleContext);
    489                 try {
    490                     listener.testModuleStarted(moduleContext);
    491                     module.run(listener);
    492                 } catch (DeviceUnresponsiveException due) {
    493                     // being able to catch a DeviceUnresponsiveException here implies that recovery
    494                     // was successful, and test execution should proceed to next module
    495                     ByteArrayOutputStream stack = new ByteArrayOutputStream();
    496                     due.printStackTrace(new PrintWriter(stack, true));
    497                     StreamUtil.close(stack);
    498                     CLog.w("Ignored DeviceUnresponsiveException because recovery was successful, "
    499                             + "proceeding with next module. Stack trace: %s",
    500                             stack.toString());
    501                     CLog.w("This may be due to incorrect timeout setting on module %s",
    502                             module.getName());
    503                 } finally {
    504                     // clear out module invocation context since we are now done with module
    505                     // execution
    506                     mInvocationContext.setModuleInvocationContext(null);
    507                     listener.testModuleEnded();
    508                 }
    509                 long duration = System.currentTimeMillis() - start;
    510                 long expected = module.getRuntimeHint();
    511                 long delta = Math.abs(duration - expected);
    512                 // Show warning if delta is more than 10% of expected
    513                 if (expected > 0 && ((float)delta / (float)expected) > 0.1f) {
    514                     CLog.logAndDisplay(LogLevel.WARN,
    515                             "Inaccurate runtime hint for %s, expected %s was %s",
    516                             module.getId(),
    517                             TimeUtil.formatElapsedTime(expected),
    518                             TimeUtil.formatElapsedTime(duration));
    519                 }
    520                 if (checkers != null && !checkers.isEmpty()) {
    521                     runPostModuleCheck(module.getName(), checkers, mDevice, listener);
    522                 }
    523                 module = null;
    524             }
    525         } catch (FileNotFoundException fnfe) {
    526             throw new RuntimeException("Failed to initialize modules", fnfe);
    527         }
    528     }
    529 
    530     /**
    531      * Initialize module repo.
    532      *
    533      * @return A list of module definition
    534      * @throws DeviceNotAvailableException
    535      * @throws FileNotFoundException
    536      */
    537     protected LinkedList<IModuleDef> initializeModuleRepo()
    538             throws DeviceNotAvailableException, FileNotFoundException {
    539         // FIXME: Each shard will do a full initialization which is not optimal. Need a way
    540         // to be more specific on what to initialize.
    541         synchronized (mModuleRepo) {
    542             if (!mModuleRepo.isInitialized()) {
    543                 setupFilters();
    544                 // Initialize the repository, {@link CompatibilityBuildHelper#getTestsDir} can
    545                 // throw a {@link FileNotFoundException}
    546                 mModuleRepo.initialize(mTotalShards, mShardIndex, mBuildHelper.getTestsDir(),
    547                         getAbis(), mDeviceTokens, mTestArgs, mModuleArgs, mIncludeFilters,
    548                         mExcludeFilters, mModuleMetadataIncludeFilter, mModuleMetadataExcludeFilter,
    549                         mBuildHelper.getBuildInfo());
    550 
    551                 // Add the entire list of modules to the CompatibilityBuildHelper for reporting
    552                 mBuildHelper.setModuleIds(mModuleRepo.getModuleIds());
    553 
    554                 int count = UniqueModuleCountUtil.countUniqueModules(mModuleRepo.getTokenModules())
    555                         + UniqueModuleCountUtil.countUniqueModules(
    556                                   mModuleRepo.getNonTokenModules());
    557                 CLog.logAndDisplay(LogLevel.INFO, "========================================");
    558                 CLog.logAndDisplay(LogLevel.INFO, "Starting a run with %s unique modules.", count);
    559                 CLog.logAndDisplay(LogLevel.INFO, "========================================");
    560             } else {
    561                 CLog.d("ModuleRepo already initialized.");
    562             }
    563             // Get the tests to run in this shard
    564             return mModuleRepo.getModules(getDevice().getSerialNumber(), mShardIndex);
    565         }
    566     }
    567 
    568     /**
    569      * Gets the set of ABIs supported by both Compatibility and the device under test
    570      *
    571      * @return The set of ABIs to run the tests on
    572      * @throws DeviceNotAvailableException
    573      */
    574     Set<IAbi> getAbis() throws DeviceNotAvailableException {
    575         Set<IAbi> abis = new LinkedHashSet<>();
    576         Set<String> archAbis = getAbisForBuildTargetArch();
    577         if (mPrimaryAbiRun) {
    578             if (mAbiName == null) {
    579                 // Get the primary from the device and make it the --abi to run.
    580                 mAbiName = mDevice.getProperty("ro.product.cpu.abi").trim();
    581             } else {
    582                 CLog.d("Option --%s supersedes the option --%s, using abi: %s", ABI_OPTION,
    583                         PRIMARY_ABI_RUN, mAbiName);
    584             }
    585         }
    586         if (mAbiName != null) {
    587             // A particular abi was requested, it still need to be supported by the build.
    588             if ((!mSkipHostArchCheck && !archAbis.contains(mAbiName)) ||
    589                     !AbiUtils.isAbiSupportedByCompatibility(mAbiName)) {
    590                 throw new IllegalArgumentException(String.format("Your CTS hasn't been built with "
    591                         + "abi '%s' support, this CTS currently supports '%s'.",
    592                         mAbiName, archAbis));
    593             } else {
    594                 abis.add(new Abi(mAbiName, AbiUtils.getBitness(mAbiName)));
    595                 return abis;
    596             }
    597         } else {
    598             // Run on all abi in common between the device and CTS.
    599             List<String> deviceAbis = Arrays.asList(AbiFormatter.getSupportedAbis(mDevice, ""));
    600             for (String abi : deviceAbis) {
    601                 if ((mSkipHostArchCheck || archAbis.contains(abi)) &&
    602                         AbiUtils.isAbiSupportedByCompatibility(abi)) {
    603                     abis.add(new Abi(abi, AbiUtils.getBitness(abi)));
    604                 } else {
    605                     CLog.d("abi '%s' is supported by device but not by this CTS build (%s), tests "
    606                             + "will not run against it.", abi, archAbis);
    607                 }
    608             }
    609             if (abis.isEmpty()) {
    610                 throw new IllegalArgumentException(String.format("None of the abi supported by this"
    611                        + " CTS build ('%s') are supported by the device ('%s').",
    612                        archAbis, deviceAbis));
    613             }
    614             return abis;
    615         }
    616     }
    617 
    618     /**
    619      * Return the abis supported by the Host build target architecture.
    620      * Exposed for testing.
    621      */
    622     protected Set<String> getAbisForBuildTargetArch() {
    623         return AbiUtils.getAbisForArch(TestSuiteInfo.getInstance().getTargetArch());
    624     }
    625 
    626     /**
    627      * Check that the system status checker specified by option are valid.
    628      */
    629     protected void checkSystemStatusBlackAndWhiteList() {
    630         for (String checker : mSystemStatusCheckWhitelist) {
    631             try {
    632                 Class.forName(checker);
    633             } catch (ClassNotFoundException e) {
    634                 ConfigurationException ex = new ConfigurationException(
    635                         String.format("--system-status-check-whitelist must contains valid class, "
    636                                 + "%s was not found", checker), e);
    637                 throw new RuntimeException(ex);
    638             }
    639         }
    640         for (String checker : mSystemStatusCheckBlacklist) {
    641             try {
    642                 Class.forName(checker);
    643             } catch (ClassNotFoundException e) {
    644                 ConfigurationException ex = new ConfigurationException(
    645                         String.format("--skip-system-status-check must contains valid class, "
    646                                 + "%s was not found", checker), e);
    647                 throw new RuntimeException(ex);
    648             }
    649         }
    650     }
    651 
    652     /**
    653      * Resolve the inclusion and exclusion logic of system status checkers
    654      *
    655      * @param s the {@link ISystemStatusChecker} to perform filtering logic on
    656      * @return True if the {@link ISystemStatusChecker} should be included, false otherwise.
    657      */
    658     private boolean shouldIncludeSystemStatusChecker(ISystemStatusChecker s) {
    659         String clazz = s.getClass().getCanonicalName();
    660         boolean shouldInclude = mSystemStatusCheckWhitelist.isEmpty()
    661                 || mSystemStatusCheckWhitelist.contains(clazz);
    662         boolean shouldExclude = !mSystemStatusCheckBlacklist.isEmpty()
    663                 && mSystemStatusCheckBlacklist.contains(clazz);
    664         return shouldInclude && !shouldExclude;
    665     }
    666 
    667     @VisibleForTesting
    668     void runPreModuleCheck(String moduleName, List<ISystemStatusChecker> checkers,
    669             ITestDevice device, ITestLogger logger) throws DeviceNotAvailableException {
    670         CLog.i("Running system status checker before module execution: %s", moduleName);
    671         List<String> failures = new ArrayList<>();
    672         for (ISystemStatusChecker checker : checkers) {
    673             StatusCheckerResult result = checker.preExecutionCheck(device);
    674             if (!CheckStatus.SUCCESS.equals(result.getStatus())) {
    675                 failures.add(checker.getClass().getCanonicalName());
    676                 CLog.w("System status checker [%s] failed", checker.getClass().getCanonicalName());
    677             }
    678         }
    679         if (!failures.isEmpty()) {
    680             CLog.w("There are failed system status checkers: %s capturing a bugreport",
    681                     failures.toString());
    682             try (InputStreamSource bugSource = device.getBugreport()) {
    683                 logger.testLog(String.format("bugreport-checker-pre-module-%s", moduleName),
    684                         LogDataType.BUGREPORT, bugSource);
    685             }
    686         }
    687     }
    688 
    689     @VisibleForTesting
    690     void runPostModuleCheck(String moduleName, List<ISystemStatusChecker> checkers,
    691             ITestDevice device, ITestLogger logger) throws DeviceNotAvailableException {
    692         CLog.i("Running system status checker after module execution: %s", moduleName);
    693         List<String> failures = new ArrayList<>();
    694         for (ISystemStatusChecker checker : checkers) {
    695             StatusCheckerResult result = checker.postExecutionCheck(device);
    696             if (!CheckStatus.SUCCESS.equals(result.getStatus())) {
    697                 failures.add(checker.getClass().getCanonicalName());
    698                 CLog.w("System status checker [%s] failed", checker.getClass().getCanonicalName());
    699             }
    700         }
    701         if (!failures.isEmpty()) {
    702             CLog.w("There are failed system status checkers: %s capturing a bugreport",
    703                     failures.toString());
    704             try (InputStreamSource bugSource = device.getBugreport()) {
    705                 logger.testLog(String.format("bugreport-checker-post-module-%s", moduleName),
    706                         LogDataType.BUGREPORT, bugSource);
    707             }
    708         }
    709     }
    710 
    711     /**
    712      * Sets the retry command-line args to be stored in the BuildInfo and serialized into the
    713      * report upon completion of the invocation.
    714      */
    715     void loadRetryCommandLineArgs(Integer sessionId) {
    716         IInvocationResult result = null;
    717         try {
    718             result = ResultHandler.findResult(mBuildHelper.getResultsDir(), sessionId);
    719         } catch (FileNotFoundException e) {
    720             // We should never reach this point, because this method should only be called
    721             // after setupFilters(), so result exists if we've gotten this far
    722             throw new RuntimeException(e);
    723         }
    724         if (result == null) {
    725             // Again, this should never happen
    726             throw new IllegalArgumentException(String.format(
    727                     "Could not find session with id %d", sessionId));
    728         }
    729         String retryCommandLineArgs = result.getCommandLineArgs();
    730         if (retryCommandLineArgs != null) {
    731             mBuildHelper.setRetryCommandLineArgs(retryCommandLineArgs);
    732         }
    733     }
    734 
    735     /**
    736      * Sets the include/exclude filters up based on if a module name was given or whether this is a
    737      * retry run.
    738      */
    739     void setupFilters() throws DeviceNotAvailableException {
    740         if (mRetrySessionId != null) {
    741             // Load the invocation result
    742             RetryFilterHelper helper = createRetryFilterHelper(mRetrySessionId);
    743             helper.validateBuildFingerprint(mDevice);
    744             helper.setCommandLineOptionsFor(this);
    745             helper.populateRetryFilters();
    746             mIncludeFilters = helper.getIncludeFilters();
    747             mExcludeFilters = helper.getExcludeFilters();
    748             helper.tearDown();
    749         } else {
    750             if (mSubPlan != null) {
    751                 ISubPlan subPlan = SubPlanHelper.getSubPlanByName(mBuildHelper, mSubPlan);
    752                 mIncludeFilters.addAll(subPlan.getIncludeFilters());
    753                 mExcludeFilters.addAll(subPlan.getExcludeFilters());
    754             }
    755             if (mModuleName != null) {
    756                 try {
    757                     List<String> modules = ModuleRepo.getModuleNamesMatching(
    758                             mBuildHelper.getTestsDir(), mModuleName);
    759                     if (modules.size() == 0) {
    760                         throw new IllegalArgumentException(
    761                                 String.format("No modules found matching %s", mModuleName));
    762                     } else if (modules.size() > 1) {
    763                         throw new IllegalArgumentException(String.format("Multiple modules found"
    764                                 + " matching %s:\n%s\nWhich one did you mean?\n",
    765                                 mModuleName, ArrayUtil.join("\n", modules)));
    766                     } else {
    767                         String module = modules.get(0);
    768                         cleanFilters(mIncludeFilters, module);
    769                         cleanFilters(mExcludeFilters, module);
    770                         mIncludeFilters.add(
    771                                 new TestFilter(mAbiName, module, mTestName).toString());
    772                     }
    773                 } catch (FileNotFoundException e) {
    774                     throw new RuntimeException(e);
    775                 }
    776             } else if (mTestName != null) {
    777                 throw new IllegalArgumentException(
    778                         "Test name given without module name. Add --module <module-name>");
    779             }
    780         }
    781     }
    782 
    783     /* Creates a new {@link RetryFilterHelper} from attributes of this object. */
    784     protected RetryFilterHelper createRetryFilterHelper(Integer retrySessionId) {
    785         return new RetryFilterHelper(mBuildHelper, retrySessionId,
    786                 mSubPlan, mIncludeFilters, mExcludeFilters, mAbiName, mModuleName, mTestName,
    787                 mRetryType);
    788     }
    789 
    790     /* Helper method designed to remove filters in a list not applicable to the given module */
    791     private static void cleanFilters(Set<String> filters, String module) {
    792         Set<String> cleanedFilters = new HashSet<String>();
    793         for (String filter : filters) {
    794             if (module.equals(TestFilter.createFrom(filter).getName())) {
    795                 cleanedFilters.add(filter); // Module name matches, filter passes
    796             }
    797         }
    798         filters.clear();
    799         filters.addAll(cleanedFilters);
    800     }
    801 
    802     /**
    803      * {@inheritDoc}
    804      */
    805     @Override
    806     public Collection<IRemoteTest> split() {
    807         if (mShards <= 1) {
    808             return null;
    809         }
    810         mIsLocalSharding = true;
    811         List<IRemoteTest> shardQueue = new LinkedList<>();
    812         for (int i = 0; i < mShards; i++) {
    813             CompatibilityTest test = (CompatibilityTest) getTestShard(mShards, i);
    814             test.mIsLocalSharding = true;
    815             shardQueue.add(test);
    816         }
    817         sPreparedLatch = new CountDownLatch(shardQueue.size());
    818         return shardQueue;
    819     }
    820 
    821     /**
    822      * {@inheritDoc}
    823      */
    824     @Override
    825     public Collection<IRemoteTest> split(int shardCount) {
    826         if (shardCount <= 1 || mIsSharded) {
    827             return null;
    828         }
    829         mIsSharded = true;
    830         List<IRemoteTest> shardQueue = new LinkedList<>();
    831         for (int i = 0; i < shardCount; i++) {
    832             CompatibilityTest test = (CompatibilityTest) getTestShard(shardCount, i);
    833             shardQueue.add(test);
    834             test.mIsSharded = true;
    835         }
    836         return shardQueue;
    837     }
    838 
    839     /**
    840      * {@inheritDoc}
    841      */
    842     @Override
    843     public IRemoteTest getTestShard(int shardCount, int shardIndex) {
    844         CompatibilityTest test = new CompatibilityTest(shardCount, mModuleRepo, shardIndex);
    845         OptionCopier.copyOptionsNoThrow(this, test);
    846         // Set the shard count because the copy option on the previous line
    847         // copies over the mShard value
    848         test.mShards = 0;
    849         return test;
    850     }
    851 
    852     /**
    853      * {@inheritDoc}
    854      */
    855     @Override
    856     public void setSystemStatusChecker(List<ISystemStatusChecker> systemCheckers) {
    857         mListCheckers = systemCheckers;
    858     }
    859 
    860     @Override
    861     public void setCollectTestsOnly(boolean collectTestsOnly) {
    862         mCollectTestsOnly = collectTestsOnly;
    863     }
    864 
    865     /**
    866      * Sets include-filters for the compatibility test
    867      */
    868     public void setIncludeFilter(Set<String> includeFilters) {
    869         mIncludeFilters.addAll(includeFilters);
    870     }
    871 
    872     /**
    873      * Sets exclude-filters for the compatibility test
    874      */
    875     public void setExcludeFilter(Set<String> excludeFilters) {
    876         mExcludeFilters.addAll(excludeFilters);
    877     }
    878 
    879     @Override
    880     public void setInvocationContext(IInvocationContext invocationContext) {
    881         mInvocationContext = invocationContext;
    882     }
    883 
    884     /**
    885      * @return the mSubPlan
    886      */
    887     protected String getSubPlan() {
    888         return mSubPlan;
    889     }
    890 
    891     /**
    892      * @return the mIncludeFilters
    893      */
    894     protected Set<String> getIncludeFilters() {
    895         return mIncludeFilters;
    896     }
    897 
    898     /**
    899      * @return the mExcludeFilters
    900      */
    901     protected Set<String> getExcludeFilters() {
    902         return mExcludeFilters;
    903     }
    904 
    905     /**
    906      * @return the mModuleName
    907      */
    908     protected String getModuleName() {
    909         return mModuleName;
    910     }
    911 
    912     /**
    913      * @return the mTestName
    914      */
    915     protected String getTestName() {
    916         return mTestName;
    917     }
    918 
    919     /**
    920      * @return the mModuleArgs
    921      */
    922     protected List<String> getModuleArgs() {
    923         return mModuleArgs;
    924     }
    925 
    926     /**
    927      * @return the mTestArgs
    928      */
    929     protected List<String> getTestArgs() {
    930         return mTestArgs;
    931     }
    932 
    933     /**
    934      * @return the mRetryType
    935      */
    936     protected RetryType getRetryType() {
    937         return mRetryType;
    938     }
    939 
    940     /**
    941      * @return the mAbiName
    942      */
    943     protected String getAbiName() {
    944         return mAbiName;
    945     }
    946 
    947     /**
    948      * @return the mDeviceTokens
    949      */
    950     protected List<String> getDeviceTokens() {
    951         return mDeviceTokens;
    952     }
    953 
    954     /**
    955      * @return the mModuleMetadataIncludeFilter
    956      */
    957     protected MultiMap<String, String> getModuleMetadataIncludeFilter() {
    958         return mModuleMetadataIncludeFilter;
    959     }
    960 
    961     /**
    962      * @return the mModuleMetadataExcludeFilter
    963      */
    964     protected MultiMap<String, String> getModuleMetadataExcludeFilter() {
    965         return mModuleMetadataExcludeFilter;
    966     }
    967 
    968     /**
    969      * @return the mTotalShards
    970      */
    971     protected int getTotalShards() {
    972         return mTotalShards;
    973     }
    974 
    975     /**
    976      * @return the mShardIndex
    977      */
    978     protected Integer getShardIndex() {
    979         return mShardIndex;
    980     }
    981 
    982     /**
    983      * @return the mBuildHelper
    984      */
    985     protected CompatibilityBuildHelper getBuildHelper() {
    986         return mBuildHelper;
    987     }
    988 
    989     /**
    990      * @return the mInvocationContext
    991      */
    992     protected IInvocationContext getInvocationContext() {
    993         return mInvocationContext;
    994     }
    995 
    996     /**
    997      * @return the mModuleRepo
    998      */
    999     protected IModuleRepo getModuleRepo() {
   1000         return mModuleRepo;
   1001     }
   1002 }
   1003