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.SuiteInfo;
20 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
21 import com.android.compatibility.common.tradefed.targetprep.NetworkConnectivityChecker;
22 import com.android.compatibility.common.tradefed.targetprep.SystemStatusChecker;
23 import com.android.compatibility.common.tradefed.util.OptionHelper;
24 import com.android.compatibility.common.util.AbiUtils;
25 import com.android.compatibility.common.util.ICaseResult;
26 import com.android.compatibility.common.util.IInvocationResult;
27 import com.android.compatibility.common.util.IModuleResult;
28 import com.android.compatibility.common.util.ITestResult;
29 import com.android.compatibility.common.util.ResultHandler;
30 import com.android.compatibility.common.util.TestFilter;
31 import com.android.compatibility.common.util.TestStatus;
32 import com.android.ddmlib.Log.LogLevel;
33 import com.android.tradefed.build.IBuildInfo;
34 import com.android.tradefed.config.ArgsOptionParser;
35 import com.android.tradefed.config.ConfigurationException;
36 import com.android.tradefed.config.ConfigurationFactory;
37 import com.android.tradefed.config.IConfiguration;
38 import com.android.tradefed.config.IConfigurationFactory;
39 import com.android.tradefed.config.Option;
40 import com.android.tradefed.config.Option.Importance;
41 import com.android.tradefed.config.OptionClass;
42 import com.android.tradefed.config.OptionCopier;
43 import com.android.tradefed.device.DeviceNotAvailableException;
44 import com.android.tradefed.device.DeviceUnresponsiveException;
45 import com.android.tradefed.device.ITestDevice;
46 import com.android.tradefed.log.ITestLogger;
47 import com.android.tradefed.log.LogUtil.CLog;
48 import com.android.tradefed.result.ITestInvocationListener;
49 import com.android.tradefed.result.InputStreamSource;
50 import com.android.tradefed.result.LogDataType;
51 import com.android.tradefed.targetprep.ITargetPreparer;
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.IRemoteTest;
56 import com.android.tradefed.testtype.IShardableTest;
57 import com.android.tradefed.util.AbiFormatter;
58 import com.android.tradefed.util.ArrayUtil;
59 import com.android.tradefed.util.TimeUtil;
60 
61 import java.io.ByteArrayOutputStream;
62 import java.io.FileNotFoundException;
63 import java.io.IOException;
64 import java.io.PrintWriter;
65 import java.util.ArrayList;
66 import java.util.Collection;
67 import java.util.HashSet;
68 import java.util.LinkedList;
69 import java.util.List;
70 import java.util.Set;
71 
72 /**
73  * A Test for running Compatibility Suites
74  */
75 @OptionClass(alias = "compatibility")
76 public class CompatibilityTest implements IDeviceTest, IShardableTest, IBuildReceiver {
77 
78     public static final String INCLUDE_FILTER_OPTION = "include-filter";
79     public static final String EXCLUDE_FILTER_OPTION = "exclude-filter";
80     private static final String PLAN_OPTION = "plan";
81     private static final String MODULE_OPTION = "module";
82     private static final String TEST_OPTION = "test";
83     private static final String MODULE_ARG_OPTION = "module-arg";
84     private static final String TEST_ARG_OPTION = "test-arg";
85     public static final String RETRY_OPTION = "retry";
86     private static final String ABI_OPTION = "abi";
87     private static final String SHARD_OPTION = "shards";
88     public static final String SKIP_DEVICE_INFO_OPTION = "skip-device-info";
89     public static final String SKIP_PRECONDITIONS_OPTION = "skip-preconditions";
90     public static final String DEVICE_TOKEN_OPTION = "device-token";
91     private static final String URL = "dynamic-config-url";
92 
93     /* API Key for compatibility test project, used for dynamic configuration */
94     private static final String API_KEY = "AIzaSyAbwX5JRlmsLeygY2WWihpIJPXFLueOQ3U";
95 
96 
97     @Option(name = PLAN_OPTION,
98             description = "the test suite plan to run, such as \"everything\" or \"cts\"",
99             importance = Importance.ALWAYS)
100     private String mSuitePlan;
101 
102     @Option(name = INCLUDE_FILTER_OPTION,
103             description = "the include module filters to apply.",
104             importance = Importance.ALWAYS)
105     private List<String> mIncludeFilters = new ArrayList<>();
106 
107     @Option(name = EXCLUDE_FILTER_OPTION,
108             description = "the exclude module filters to apply.",
109             importance = Importance.ALWAYS)
110     private List<String> mExcludeFilters = new ArrayList<>();
111 
112     @Option(name = MODULE_OPTION,
113             shortName = 'm',
114             description = "the test module to run.",
115             importance = Importance.IF_UNSET)
116     private String mModuleName = null;
117 
118     @Option(name = TEST_OPTION,
119             shortName = 't',
120             description = "the test run.",
121             importance = Importance.IF_UNSET)
122     private String mTestName = null;
123 
124     @Option(name = MODULE_ARG_OPTION,
125             description = "the arguments to pass to a module. The expected format is"
126                     + "\"<module-name>:<arg-name>:<arg-value>\"",
127             importance = Importance.ALWAYS)
128     private List<String> mModuleArgs = new ArrayList<>();
129 
130     @Option(name = TEST_ARG_OPTION,
131             description = "the arguments to pass to a test. The expected format is"
132                     + "\"<test-class>:<arg-name>:<arg-value>\"",
133             importance = Importance.ALWAYS)
134     private List<String> mTestArgs = new ArrayList<>();
135 
136     @Option(name = RETRY_OPTION,
137             shortName = 'r',
138             description = "retry a previous session.",
139             importance = Importance.IF_UNSET)
140     private Integer mRetrySessionId = null;
141 
142     @Option(name = ABI_OPTION,
143             shortName = 'a',
144             description = "the abi to test.",
145             importance = Importance.IF_UNSET)
146     private String mAbiName = null;
147 
148     @Option(name = SHARD_OPTION,
149             description = "split the modules up to run on multiple devices concurrently.")
150     private int mShards = 1;
151 
152     @Option(name = URL,
153             description = "Specify the url for override config")
154     private String mURL = "https://androidpartner.googleapis.com/v1/dynamicconfig/"
155             + "suites/{suite-name}/modules/{module}/version/{version}?key=" + API_KEY;
156 
157     @Option(name = SKIP_DEVICE_INFO_OPTION,
158             shortName = 'd',
159             description = "Whether device info collection should be skipped")
160     private boolean mSkipDeviceInfo = false;
161 
162     @Option(name = SKIP_PRECONDITIONS_OPTION,
163             shortName = 'o',
164             description = "Whether preconditions should be skipped")
165     private boolean mSkipPreconditions = false;
166 
167     @Option(name = DEVICE_TOKEN_OPTION,
168             description = "Holds the devices' tokens, used when scheduling tests that have"
169                     + "prerequisits such as requiring a SIM card. Format is <serial>:<token>",
170             importance = Importance.ALWAYS)
171     private List<String> mDeviceTokens = new ArrayList<>();
172 
173     @Option(name = "bugreport-on-failure",
174             description = "Take a bugreport on every test failure. " +
175                     "Warning: can potentially use a lot of disk space.")
176     private boolean mBugReportOnFailure = false;
177 
178     @Option(name = "logcat-on-failure",
179             description = "Take a logcat snapshot on every test failure.")
180     private boolean mLogcatOnFailure = false;
181 
182     @Option(name = "screenshot-on-failure",
183             description = "Take a screenshot on every test failure.")
184     private boolean mScreenshotOnFailure = false;
185 
186     @Option(name = "reboot-before-test",
187             description = "Reboot the device before the test suite starts.")
188     private boolean mRebootBeforeTest = false;
189 
190     @Option(name = "reboot-on-failure",
191             description = "Reboot the device after every test failure.")
192     private boolean mRebootOnFailure = false;
193 
194     @Option(name = "reboot-per-module",
195             description = "Reboot the device before every module run.")
196     private boolean mRebootPerModule = false;
197 
198     @Option(name = "skip-connectivity-check",
199             description = "Don't verify device connectivity between module execution.")
200     private boolean mSkipConnectivityCheck = false;
201 
202     @Option(name = "preparer-whitelist",
203             description = "Only run specific preparers."
204             + "Specify zero or more ITargetPreparers as canonical class names. "
205             + "e.g. \"com.android.compatibility.common.tradefed.targetprep.ApkInstaller\" "
206             + "If not specified, all configured preparers are run.")
207     private Set<String> mPreparerWhitelist = new HashSet<>();
208 
209     @Option(name = "skip-all-system-status-check",
210             description = "Whether all system status check between modules should be skipped")
211     private boolean mSkipAllSystemStatusCheck = false;
212 
213     @Option(name = "skip-system-status-check",
214             description = "Disable specific system status checkers."
215             + "Specify zero or more SystemStatusChecker as canonical class names. e.g. "
216             + "\"com.android.compatibility.common.tradefed.targetprep.NetworkConnectivityChecker\" "
217             + "If not specified, all configured or whitelisted system status checkers are run.")
218     private Set<String> mSystemStatusCheckBlacklist = new HashSet<>();
219 
220     @Option(name = "system-status-check-whitelist",
221             description = "Only run specific system status checkers."
222             + "Specify zero or more SystemStatusChecker as canonical class names. e.g. "
223             + "\"com.android.compatibility.common.tradefed.targetprep.NetworkConnectivityChecker\" "
224             + "If not specified, all configured system status checkers are run.")
225     private Set<String> mSystemStatusCheckWhitelist = new HashSet<>();
226 
227     @Option(name = "system-status-checker-config", description = "Configuration file for system "
228             + "status checkers invoked between module execution.")
229     private String mSystemStatusCheckerConfig = "system-status-checkers";
230 
231     private int mTotalShards;
232     private IModuleRepo mModuleRepo;
233     private ITestDevice mDevice;
234     private CompatibilityBuildHelper mBuildHelper;
235 
236     /**
237      * Create a new {@link CompatibilityTest} that will run the default list of
238      * modules.
239      */
CompatibilityTest()240     public CompatibilityTest() {
241         this(1 /* totalShards */, new ModuleRepo());
242     }
243 
244     /**
245      * Create a new {@link CompatibilityTest} that will run a sublist of
246      * modules.
247      */
CompatibilityTest(int totalShards, IModuleRepo moduleRepo)248     public CompatibilityTest(int totalShards, IModuleRepo moduleRepo) {
249         if (totalShards < 1) {
250             throw new IllegalArgumentException(
251                     "Must be at least 1 shard. Given:" + totalShards);
252         }
253         mTotalShards = totalShards;
254         mModuleRepo = moduleRepo;
255     }
256 
257     /**
258      * {@inheritDoc}
259      */
260     @Override
getDevice()261     public ITestDevice getDevice() {
262         return mDevice;
263     }
264 
265     /**
266      * {@inheritDoc}
267      */
268     @Override
setDevice(ITestDevice device)269     public void setDevice(ITestDevice device) {
270         mDevice = device;
271     }
272 
273     /**
274      * {@inheritDoc}
275      */
276     @Override
setBuild(IBuildInfo buildInfo)277     public void setBuild(IBuildInfo buildInfo) {
278         mBuildHelper = new CompatibilityBuildHelper(buildInfo);
279         // Initializing the mBuildHelper also updates properties in buildInfo.
280         // TODO(nicksauer): Keeping invocation properties around via buildInfo
281         // is confusing and would be better done in an "InvocationInfo".
282         // Note, the current time is used to generated the result directory.
283         mBuildHelper.init(mSuitePlan, mURL, System.currentTimeMillis());
284     }
285 
286     /**
287      * {@inheritDoc}
288      */
289     @Override
run(ITestInvocationListener listener)290     public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
291         try {
292             // Synchronized so only one shard enters and sets up the moduleRepo. When the other
293             // shards enter after this, moduleRepo is already initialized so they dont do anything
294             synchronized (mModuleRepo) {
295                 if (!mModuleRepo.isInitialized()) {
296                     setupFilters();
297                     // Initialize the repository, {@link CompatibilityBuildHelper#getTestsDir} can
298                     // throw a {@link FileNotFoundException}
299                     mModuleRepo.initialize(mTotalShards, mBuildHelper.getTestsDir(), getAbis(),
300                             mDeviceTokens, mTestArgs, mModuleArgs, mIncludeFilters,
301                             mExcludeFilters, mBuildHelper.getBuildInfo());
302 
303                     // Add the entire list of modules to the CompatibilityBuildHelper for reporting
304                     mBuildHelper.setModuleIds(mModuleRepo.getModuleIds());
305                 }
306 
307             }
308             // Get the tests to run in this shard
309             List<IModuleDef> modules = mModuleRepo.getModules(getDevice().getSerialNumber());
310 
311             listener = new FailureListener(listener, getDevice(), mBugReportOnFailure,
312                     mLogcatOnFailure, mScreenshotOnFailure, mRebootOnFailure);
313             int moduleCount = modules.size();
314             CLog.logAndDisplay(LogLevel.INFO, "Starting %d module%s on %s", moduleCount,
315                     (moduleCount > 1) ? "s" : "", mDevice.getSerialNumber());
316             if (mRebootBeforeTest) {
317                 CLog.d("Rebooting device before test starts as requested.");
318                 mDevice.reboot();
319             }
320 
321             if (mSkipConnectivityCheck) {
322                 String clazz = NetworkConnectivityChecker.class.getCanonicalName();
323                 CLog.logAndDisplay(LogLevel.INFO, "\"--skip-connectivity-check\" is deprecated, "
324                         + "please use \"--skip-system-status-check %s\" instead", clazz);
325                 mSystemStatusCheckBlacklist.add(clazz);
326             }
327 
328             // Get system status checkers
329             List<SystemStatusChecker> checkers = null;
330             if (!mSkipAllSystemStatusCheck) {
331                 try {
332                     checkers = initSystemStatusCheckers();
333                 } catch (ConfigurationException ce) {
334                     throw new RuntimeException("failed to load system status checker config", ce);
335                 }
336             }
337 
338             // Set values and run preconditions
339             for (int i = 0; i < moduleCount; i++) {
340                 IModuleDef module = modules.get(i);
341                 module.setBuild(mBuildHelper.getBuildInfo());
342                 module.setDevice(mDevice);
343                 module.setPreparerWhitelist(mPreparerWhitelist);
344                 module.prepare(mSkipPreconditions);
345             }
346             // Run the tests
347             for (int i = 0; i < moduleCount; i++) {
348                 IModuleDef module = modules.get(i);
349                 long start = System.currentTimeMillis();
350 
351                 if (mRebootPerModule) {
352                     if ("user".equals(mDevice.getProperty("ro.build.type"))) {
353                         CLog.e("reboot-per-module should only be used during development, "
354                             + "this is a\" user\" build device");
355                     } else {
356                         CLog.logAndDisplay(LogLevel.INFO, "Rebooting device before starting next "
357                             + "module");
358                         mDevice.reboot();
359                     }
360                 }
361 
362                 // execute pre module execution checker
363                 runPreModuleCheck(module.getName(), checkers, mDevice, listener);
364                 try {
365                     module.run(listener);
366                 } catch (DeviceUnresponsiveException due) {
367                     // being able to catch a DeviceUnresponsiveException here implies that recovery
368                     // was successful, and test execution should proceed to next module
369                     ByteArrayOutputStream stack = new ByteArrayOutputStream();
370                     due.printStackTrace(new PrintWriter(stack, true));
371                     try {
372                         stack.close();
373                     } catch (IOException ioe) {
374                         // won't happen on BAOS
375                     }
376                     CLog.w("Ignored DeviceUnresponsiveException because recovery was successful, "
377                             + "proceeding with next module. Stack trace: %s",
378                             stack.toString());
379                     CLog.w("This may be due to incorrect timeout setting on module %s",
380                             module.getName());
381                 }
382                 long duration = System.currentTimeMillis() - start;
383                 long expected = module.getRuntimeHint();
384                 long delta = Math.abs(duration - expected);
385                 // Show warning if delta is more than 10% of expected
386                 if (expected > 0 && ((float)delta / (float)expected) > 0.1f) {
387                     CLog.logAndDisplay(LogLevel.WARN,
388                             "Inaccurate runtime hint for %s, expected %s was %s",
389                             module.getId(),
390                             TimeUtil.formatElapsedTime(expected),
391                             TimeUtil.formatElapsedTime(duration));
392                 }
393                 runPostModuleCheck(module.getName(), checkers, mDevice, listener);
394             }
395         } catch (FileNotFoundException fnfe) {
396             throw new RuntimeException("Failed to initialize modules", fnfe);
397         }
398     }
399 
400     /**
401      * Gets the set of ABIs supported by both Compatibility and the device under test
402      *
403      * @return The set of ABIs to run the tests on
404      * @throws DeviceNotAvailableException
405      */
getAbis()406     Set<IAbi> getAbis() throws DeviceNotAvailableException {
407         Set<IAbi> abis = new HashSet<>();
408         Set<String> archAbis = AbiUtils.getAbisForArch(SuiteInfo.TARGET_ARCH);
409         for (String abi : AbiFormatter.getSupportedAbis(mDevice, "")) {
410             // Only test against ABIs supported by Compatibility, and if the
411             // --abi option was given, it must match.
412             if (AbiUtils.isAbiSupportedByCompatibility(abi) && archAbis.contains(abi)
413                     && (mAbiName == null || mAbiName.equals(abi))) {
414                 abis.add(new Abi(abi, AbiUtils.getBitness(abi)));
415             }
416         }
417         if (abis == null || abis.isEmpty()) {
418             if (mAbiName == null) {
419                 throw new IllegalArgumentException("Could not get device's ABIs");
420             } else {
421                 throw new IllegalArgumentException(String.format(
422                         "Device %s doesn't support %s", mDevice.getSerialNumber(), mAbiName));
423             }
424         }
425         return abis;
426     }
427 
initSystemStatusCheckers()428     private List<SystemStatusChecker> initSystemStatusCheckers() throws ConfigurationException {
429         IConfigurationFactory cf = ConfigurationFactory.getInstance();
430         IConfiguration config = cf.createConfigurationFromArgs(
431                 new String[]{mSystemStatusCheckerConfig});
432         // only checks the target preparers from the config
433         List<ITargetPreparer> preparers = config.getTargetPreparers();
434         List<SystemStatusChecker> checkers = new ArrayList<>();
435         for (ITargetPreparer p : preparers) {
436             if (p instanceof SystemStatusChecker) {
437                 SystemStatusChecker s = (SystemStatusChecker)p;
438                 if (shouldIncludeSystemStatusChecker(s)) {
439                     checkers.add(s);
440                 } else {
441                     CLog.i("%s skipped because it's not whitelisted.",
442                             s.getClass().getCanonicalName());
443                 }
444             } else {
445                 CLog.w("Preparer %s does not have type %s, ignored ",
446                         p.getClass().getCanonicalName(),
447                         SystemStatusChecker.class.getCanonicalName());
448             }
449         }
450         return checkers;
451     }
452 
453     /**
454      * Resolve the inclusion and exclusion logic of system status checkers
455      *
456      * @param s the {@link SystemStatusChecker} to perform filtering logic on
457      * @return
458      */
shouldIncludeSystemStatusChecker(SystemStatusChecker s)459     private boolean shouldIncludeSystemStatusChecker(SystemStatusChecker s) {
460         String clazz = s.getClass().getCanonicalName();
461         boolean shouldInclude = mSystemStatusCheckWhitelist.isEmpty()
462                 || mSystemStatusCheckWhitelist.contains(clazz);
463         boolean shouldExclude = !mSystemStatusCheckBlacklist.isEmpty()
464                 && mSystemStatusCheckBlacklist.contains(clazz);
465         return shouldInclude && !shouldExclude;
466     }
467 
runPreModuleCheck(String moduleName, List<SystemStatusChecker> checkers, ITestDevice device, ITestLogger logger)468     private void runPreModuleCheck(String moduleName, List<SystemStatusChecker> checkers,
469             ITestDevice device, ITestLogger logger) throws DeviceNotAvailableException {
470         CLog.i("Running system status checker before module execution: %s", moduleName);
471         List<String> failures = new ArrayList<>();
472         for (SystemStatusChecker checker : checkers) {
473             boolean result = checker.preExecutionCheck(device);
474             if (!result) {
475                 failures.add(checker.getClass().getCanonicalName());
476                 CLog.w("System status checker [%s] failed with message: %s",
477                         checker.getClass().getCanonicalName(), checker.getFailureMessage());
478             }
479         }
480         if (!failures.isEmpty()) {
481             CLog.w("There are failed system status checkers: %s capturing a bugreport",
482                     failures.toString());
483             InputStreamSource bugSource = device.getBugreport();
484             logger.testLog(String.format("bugreport-checker-pre-module-%s", moduleName),
485                     LogDataType.TEXT, bugSource);
486             bugSource.cancel();
487         }
488     }
489 
runPostModuleCheck(String moduleName, List<SystemStatusChecker> checkers, ITestDevice device, ITestLogger logger)490     private void runPostModuleCheck(String moduleName, List<SystemStatusChecker> checkers,
491             ITestDevice device, ITestLogger logger) throws DeviceNotAvailableException {
492         CLog.i("Running system status checker after module execution: %s", moduleName);
493         List<String> failures = new ArrayList<>();
494         for (SystemStatusChecker checker : checkers) {
495             boolean result = checker.postExecutionCheck(device);
496             if (!result) {
497                 failures.add(checker.getClass().getCanonicalName());
498                 CLog.w("System status checker [%s] failed with message: %s",
499                         checker.getClass().getCanonicalName(), checker.getFailureMessage());
500             }
501         }
502         if (!failures.isEmpty()) {
503             CLog.w("There are failed system status checkers: %s capturing a bugreport",
504                     failures.toString());
505             InputStreamSource bugSource = device.getBugreport();
506             logger.testLog(String.format("bugreport-checker-post-module-%s", moduleName),
507                     LogDataType.TEXT, bugSource);
508             bugSource.cancel();
509         }
510     }
511 
512     /**
513      * Sets the include/exclude filters up based on if a module name was given or whether this is a
514      * retry run.
515      */
setupFilters()516     void setupFilters() throws DeviceNotAvailableException {
517         if (mRetrySessionId != null) {
518             // We're retrying so clear -m and -t options
519             // eventually reset these options with values given in the previous session
520             mModuleName = null;
521             mTestName = null;
522             // Load the invocation result
523             IInvocationResult result = null;
524             try {
525                 result = ResultHandler.findResult(mBuildHelper.getResultsDir(), mRetrySessionId);
526             } catch (FileNotFoundException e) {
527                 throw new RuntimeException(e);
528             }
529             if (result == null) {
530                 throw new IllegalArgumentException(String.format(
531                         "Could not find session with id %d", mRetrySessionId));
532             }
533 
534             String oldBuildFingerprint = result.getBuildFingerprint();
535             String currentBuildFingerprint = mDevice.getProperty("ro.build.fingerprint");
536             if (oldBuildFingerprint.equals(currentBuildFingerprint)) {
537                 CLog.logAndDisplay(LogLevel.INFO, "Retrying session from: %s",
538                         CompatibilityBuildHelper.getDirSuffix(result.getStartTime()));
539             } else {
540                 throw new IllegalArgumentException(String.format(
541                         "Device build fingerprint must match %s to retry session %d",
542                         oldBuildFingerprint, mRetrySessionId));
543             }
544 
545             String retryCommandLineArgs = result.getCommandLineArgs();
546             if (retryCommandLineArgs != null) {
547                 // Copy the original command into the build helper so it can be serialized later
548                 mBuildHelper.setRetryCommandLineArgs(retryCommandLineArgs);
549                 try {
550                     // parse the command-line string from the result file and set options
551                     ArgsOptionParser parser = new ArgsOptionParser(this);
552                     parser.parse(OptionHelper.getValidCliArgs(retryCommandLineArgs, this));
553                 } catch (ConfigurationException e) {
554                     throw new RuntimeException(e);
555                 }
556             }
557             // Append each test that failed or was not executed to the filters
558             for (IModuleResult module : result.getModules()) {
559                 for (ICaseResult testResultList : module.getResults()) {
560                     for (ITestResult testResult : testResultList.getResults(TestStatus.PASS)) {
561                         // Create the filter for the test to be run.
562                         TestFilter filter = new TestFilter(
563                                 module.getAbi(), module.getName(), testResult.getFullName());
564                         mExcludeFilters.add(filter.toString());
565                     }
566                 }
567             }
568         }
569         if (mModuleName != null) {
570             mIncludeFilters.clear();
571             try {
572                 List<String> modules = ModuleRepo.getModuleNamesMatching(
573                         mBuildHelper.getTestsDir(), mModuleName);
574                 if (modules.size() == 0) {
575                     throw new IllegalArgumentException(
576                             String.format("No modules found matching %s", mModuleName));
577                 } else if (modules.size() > 1) {
578                     throw new IllegalArgumentException(String.format(
579                             "Multiple modules found matching %s:\n%s\nWhich one did you mean?\n",
580                             mModuleName, ArrayUtil.join("\n", modules)));
581                 } else {
582                     String module = modules.get(0);
583                     mIncludeFilters.add(new TestFilter(mAbiName, module, mTestName).toString());
584                     // We will run this module with previous exclusions,
585                     // unless they exclude the whole module.
586                     List<String> excludeFilters = new ArrayList<>();
587                     for (String excludeFilter : mExcludeFilters) {
588                         TestFilter filter = TestFilter.createFrom(excludeFilter);
589                         String name = filter.getName();
590                         // Add the filter if it applies to this module
591                         if (module.equals(name)) {
592                             excludeFilters.add(excludeFilter);
593                         }
594                     }
595                     mExcludeFilters = excludeFilters;
596                 }
597             } catch (FileNotFoundException e) {
598                 e.printStackTrace();
599             }
600         } else if (mTestName != null) {
601             throw new IllegalArgumentException(
602                     "Test name given without module name. Add --module <module-name>");
603         } else {
604             // If a module has an arg, assume it's included
605             for (String arg : mModuleArgs) {
606                 mIncludeFilters.add(arg.split(":")[0]);
607             }
608         }
609     }
610 
611     /**
612      * {@inheritDoc}
613      */
614     @Override
split()615     public Collection<IRemoteTest> split() {
616         if (mShards <= 1) {
617             return null;
618         }
619 
620         List<IRemoteTest> shardQueue = new LinkedList<>();
621         for (int i = 0; i < mShards; i++) {
622             CompatibilityTest test = new CompatibilityTest(mShards, mModuleRepo);
623             OptionCopier.copyOptionsNoThrow(this, test);
624             // Set the shard count because the copy option on the previous line
625             // copies over the mShard value
626             test.mShards = 0;
627             shardQueue.add(test);
628         }
629 
630         return shardQueue;
631     }
632 
633 }
634