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 package com.android.compatibility.common.tradefed.testtype; 17 18 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; 19 import com.android.compatibility.common.tradefed.result.TestRunHandler; 20 import com.android.compatibility.common.tradefed.util.LinearPartition; 21 import com.android.compatibility.common.tradefed.util.UniqueModuleCountUtil; 22 import com.android.compatibility.common.util.TestFilter; 23 import com.android.ddmlib.Log.LogLevel; 24 import com.android.tradefed.build.IBuildInfo; 25 import com.android.tradefed.config.ConfigurationException; 26 import com.android.tradefed.config.ConfigurationFactory; 27 import com.android.tradefed.config.IConfiguration; 28 import com.android.tradefed.config.IConfigurationFactory; 29 import com.android.tradefed.log.LogUtil.CLog; 30 import com.android.tradefed.testtype.IAbi; 31 import com.android.tradefed.testtype.IRemoteTest; 32 import com.android.tradefed.testtype.ITestFileFilterReceiver; 33 import com.android.tradefed.testtype.ITestFilterReceiver; 34 import com.android.tradefed.util.AbiUtils; 35 import com.android.tradefed.util.FileUtil; 36 import com.android.tradefed.util.MultiMap; 37 import com.android.tradefed.util.TimeUtil; 38 39 import com.google.common.annotations.VisibleForTesting; 40 41 import java.io.File; 42 import java.io.FilenameFilter; 43 import java.io.IOException; 44 import java.io.PrintWriter; 45 import java.util.ArrayList; 46 import java.util.Collection; 47 import java.util.Collections; 48 import java.util.Comparator; 49 import java.util.HashMap; 50 import java.util.HashSet; 51 import java.util.LinkedList; 52 import java.util.List; 53 import java.util.Map; 54 import java.util.Map.Entry; 55 import java.util.Set; 56 57 /** 58 * Retrieves Compatibility test module definitions from the repository. 59 * 60 * @deprecated This class is associated with {@link CompatibilityTest} which is deprecate 61 */ 62 @Deprecated 63 public class ModuleRepo implements IModuleRepo { 64 65 private static final String CONFIG_EXT = ".config"; 66 private static final Map<String, Integer> ENDING_MODULES = new HashMap<>(); 67 static { 68 // b/62732298 put testFullDisk in the end to accommodate CTSMediaStressTest temporally 69 ENDING_MODULES.put("CtsAppSecurityHostTestCases", 1); 70 ENDING_MODULES.put("CtsMonkeyTestCases", 2); 71 } 72 // Synchronization objects for Token Modules. 73 private int mInitCount = 0; 74 private Set<IModuleDef> mTokenModuleScheduled; 75 private static Object lock = new Object(); 76 77 private int mTotalShards; 78 private Integer mShardIndex; 79 80 private Map<String, Set<String>> mDeviceTokens = new HashMap<>(); 81 private Map<String, Map<String, List<String>>> mTestArgs = new HashMap<>(); 82 private Map<String, Map<String, List<String>>> mModuleArgs = new HashMap<>(); 83 private boolean mIncludeAll; 84 private Map<String, List<TestFilter>> mIncludeFilters = new HashMap<>(); 85 private Map<String, List<TestFilter>> mExcludeFilters = new HashMap<>(); 86 private IConfigurationFactory mConfigFactory = ConfigurationFactory.getInstance(); 87 88 private volatile boolean mInitialized = false; 89 90 // Holds all the tests with tokens waiting to be run. Meaning the DUT must have a specific token. 91 private List<IModuleDef> mTokenModules = new ArrayList<>(); 92 private List<IModuleDef> mNonTokenModules = new ArrayList<>(); 93 94 /** 95 * {@inheritDoc} 96 */ 97 @Override getNumberOfShards()98 public int getNumberOfShards() { 99 return mTotalShards; 100 } 101 102 /** 103 * Returns the device tokens of this module repo. Exposed for testing. 104 */ getDeviceTokens()105 protected Map<String, Set<String>> getDeviceTokens() { 106 return mDeviceTokens; 107 } 108 109 /** 110 * A {@link FilenameFilter} to find all modules in a directory who match the given pattern. 111 */ 112 public static class NameFilter implements FilenameFilter { 113 114 private String mPattern; 115 NameFilter(String pattern)116 public NameFilter(String pattern) { 117 mPattern = pattern; 118 } 119 120 /** 121 * {@inheritDoc} 122 */ 123 @Override accept(File dir, String name)124 public boolean accept(File dir, String name) { 125 return name.contains(mPattern) && name.endsWith(CONFIG_EXT); 126 } 127 } 128 129 /** 130 * {@inheritDoc} 131 */ 132 @Override getNonTokenModules()133 public List<IModuleDef> getNonTokenModules() { 134 return mNonTokenModules; 135 } 136 137 /** 138 * {@inheritDoc} 139 */ 140 @Override getTokenModules()141 public List<IModuleDef> getTokenModules() { 142 return mTokenModules; 143 } 144 145 /** 146 * {@inheritDoc} 147 */ 148 @Override getModuleIds()149 public String[] getModuleIds() { 150 Set<String> moduleIdSet = new HashSet<>(); 151 for (IModuleDef moduleDef : mNonTokenModules) { 152 moduleIdSet.add(moduleDef.getId()); 153 } 154 for (IModuleDef moduleDef : mTokenModules) { 155 moduleIdSet.add(moduleDef.getId()); 156 } 157 return moduleIdSet.toArray(new String[moduleIdSet.size()]); 158 } 159 160 /** 161 * {@inheritDoc} 162 */ 163 @Override isInitialized()164 public boolean isInitialized() { 165 return mInitialized; 166 } 167 168 /** 169 * {@inheritDoc} 170 */ 171 @Override initialize(int totalShards, Integer shardIndex, File testsDir, Set<IAbi> abis, List<String> deviceTokens, List<String> testArgs, List<String> moduleArgs, Set<String> includeFilters, Set<String> excludeFilters, MultiMap<String, String> metadataIncludeFilters, MultiMap<String, String> metadataExcludeFilters, IBuildInfo buildInfo)172 public void initialize(int totalShards, Integer shardIndex, File testsDir, Set<IAbi> abis, 173 List<String> deviceTokens, List<String> testArgs, List<String> moduleArgs, 174 Set<String> includeFilters, Set<String> excludeFilters, 175 MultiMap<String, String> metadataIncludeFilters, 176 MultiMap<String, String> metadataExcludeFilters, 177 IBuildInfo buildInfo) { 178 CLog.d("Initializing ModuleRepo\nShards:%d\nTests Dir:%s\nABIs:%s\nDevice Tokens:%s\n" + 179 "Test Args:%s\nModule Args:%s\nIncludes:%s\nExcludes:%s", 180 totalShards, testsDir.getAbsolutePath(), abis, deviceTokens, testArgs, moduleArgs, 181 includeFilters, excludeFilters); 182 mInitialized = true; 183 mTotalShards = totalShards; 184 mShardIndex = shardIndex; 185 synchronized (lock) { 186 if (mTokenModuleScheduled == null) { 187 mTokenModuleScheduled = new HashSet<>(); 188 } 189 } 190 191 for (String line : deviceTokens) { 192 String[] parts = line.split(":"); 193 if (parts.length == 2) { 194 String key = parts[0]; 195 String value = parts[1]; 196 Set<String> list = mDeviceTokens.get(key); 197 if (list == null) { 198 list = new HashSet<>(); 199 mDeviceTokens.put(key, list); 200 } 201 list.add(value); 202 } else { 203 throw new IllegalArgumentException( 204 String.format("Could not parse device token: %s", line)); 205 } 206 } 207 putArgs(testArgs, mTestArgs); 208 putArgs(moduleArgs, mModuleArgs); 209 mIncludeAll = includeFilters.isEmpty(); 210 // Include all the inclusions 211 addFilters(includeFilters, mIncludeFilters, abis); 212 // Exclude all the exclusions 213 addFilters(excludeFilters, mExcludeFilters, abis); 214 215 File[] configFiles = testsDir.listFiles(new ConfigFilter()); 216 if (configFiles.length == 0) { 217 throw new IllegalArgumentException( 218 String.format("No config files found in %s", testsDir.getAbsolutePath())); 219 } 220 Map<String, Integer> shardedTestCounts = new HashMap<>(); 221 for (File configFile : configFiles) { 222 final String name = configFile.getName().replace(CONFIG_EXT, ""); 223 final String[] pathArg = new String[] { configFile.getAbsolutePath() }; 224 try { 225 // Invokes parser to process the test module config file 226 // Need to generate a different config for each ABI as we cannot guarantee the 227 // configs are idempotent. This however means we parse the same file multiple times 228 for (IAbi abi : abis) { 229 String id = AbiUtils.createId(abi.getName(), name); 230 if (!shouldRunModule(id)) { 231 // If the module should not run tests based on the state of filters, 232 // skip this name/abi combination. 233 continue; 234 } 235 236 IConfiguration config = mConfigFactory.createConfigurationFromArgs(pathArg); 237 if (!filterByConfigMetadata(config, 238 metadataIncludeFilters, metadataExcludeFilters)) { 239 // if the module config did not pass the metadata filters, it's excluded 240 // from execution 241 continue; 242 } 243 Map<String, List<String>> args = new HashMap<>(); 244 if (mModuleArgs.containsKey(name)) { 245 args.putAll(mModuleArgs.get(name)); 246 } 247 if (mModuleArgs.containsKey(id)) { 248 args.putAll(mModuleArgs.get(id)); 249 } 250 injectOptionsToConfig(args, config); 251 252 List<IRemoteTest> tests = config.getTests(); 253 for (IRemoteTest test : tests) { 254 prepareTestClass(name, abi, config, test); 255 } 256 List<IRemoteTest> shardedTests = tests; 257 if (shardedTests.size() > 1) { 258 shardedTestCounts.put(id, shardedTests.size()); 259 } 260 for (IRemoteTest test : shardedTests) { 261 addModuleDef(name, abi, test, pathArg); 262 } 263 } 264 } catch (ConfigurationException e) { 265 throw new RuntimeException(String.format("error parsing config file: %s", 266 configFile.getName()), e); 267 } 268 } 269 mExcludeFilters.clear(); 270 TestRunHandler.setTestRuns(new CompatibilityBuildHelper(buildInfo), shardedTestCounts); 271 } 272 273 /** 274 * Prepare to run test classes. 275 * 276 * @param name module name 277 * @param abi IAbi object that contains abi information 278 * @param config IConfiguration object created from config file 279 * @param test test class 280 * @throws ConfigurationException 281 */ prepareTestClass(final String name, IAbi abi, IConfiguration config, IRemoteTest test)282 protected void prepareTestClass(final String name, IAbi abi, IConfiguration config, 283 IRemoteTest test) throws ConfigurationException { 284 String className = test.getClass().getName(); 285 Map<String, List<String>> testArgsMap = new HashMap<>(); 286 if (mTestArgs.containsKey(className)) { 287 testArgsMap.putAll(mTestArgs.get(className)); 288 } 289 injectOptionsToConfig(testArgsMap, config); 290 addFiltersToTest(test, abi, name); 291 } 292 293 /** 294 * Helper to inject options to a config. 295 */ 296 @VisibleForTesting injectOptionsToConfig(Map<String, List<String>> optionMap, IConfiguration config)297 void injectOptionsToConfig(Map<String, List<String>> optionMap, IConfiguration config) 298 throws ConfigurationException{ 299 for (Entry<String, List<String>> entry : optionMap.entrySet()) { 300 for (String entryValue : entry.getValue()) { 301 String entryName = entry.getKey(); 302 if (entryValue.contains(":=")) { 303 // entryValue is key-value pair 304 String key = entryValue.substring(0, entryValue.indexOf(":=")); 305 String value = entryValue.substring(entryValue.indexOf(":=") + 2); 306 config.injectOptionValue(entryName, key, value); 307 } else { 308 // entryValue is just the argument value 309 config.injectOptionValue(entryName, entryValue); 310 } 311 } 312 } 313 } 314 addFilters(Set<String> stringFilters, Map<String, List<TestFilter>> filters, Set<IAbi> abis)315 private void addFilters(Set<String> stringFilters, 316 Map<String, List<TestFilter>> filters, Set<IAbi> abis) { 317 for (String filterString : stringFilters) { 318 TestFilter filter = TestFilter.createFrom(filterString); 319 String abi = filter.getAbi(); 320 if (abi == null) { 321 for (IAbi a : abis) { 322 addFilter(a.getName(), filter, filters); 323 } 324 } else { 325 addFilter(abi, filter, filters); 326 } 327 } 328 } 329 addFilter(String abi, TestFilter filter, Map<String, List<TestFilter>> filters)330 private void addFilter(String abi, TestFilter filter, 331 Map<String, List<TestFilter>> filters) { 332 getFilter(filters, AbiUtils.createId(abi, filter.getName())).add(filter); 333 } 334 getFilter(Map<String, List<TestFilter>> filters, String id)335 private List<TestFilter> getFilter(Map<String, List<TestFilter>> filters, String id) { 336 List<TestFilter> fs = filters.get(id); 337 if (fs == null) { 338 fs = new ArrayList<>(); 339 filters.put(id, fs); 340 } 341 return fs; 342 } 343 addModuleDef(String name, IAbi abi, IRemoteTest test, String[] configPaths)344 protected void addModuleDef(String name, IAbi abi, IRemoteTest test, String[] configPaths) 345 throws ConfigurationException { 346 // Invokes parser to process the test module config file 347 IConfiguration config = mConfigFactory.createConfigurationFromArgs(configPaths); 348 addModuleDef(new ModuleDef(name, abi, test, config.getTargetPreparers(), 349 config.getConfigurationDescription())); 350 } 351 addModuleDef(IModuleDef moduleDef)352 protected void addModuleDef(IModuleDef moduleDef) { 353 Set<String> tokens = moduleDef.getTokens(); 354 if (tokens != null && !tokens.isEmpty()) { 355 mTokenModules.add(moduleDef); 356 } else { 357 mNonTokenModules.add(moduleDef); 358 } 359 } 360 addFiltersToTest(IRemoteTest test, IAbi abi, String name)361 private void addFiltersToTest(IRemoteTest test, IAbi abi, String name) { 362 String moduleId = AbiUtils.createId(abi.getName(), name); 363 if (!(test instanceof ITestFilterReceiver)) { 364 throw new IllegalArgumentException(String.format( 365 "Test in module %s must implement ITestFilterReceiver.", moduleId)); 366 } 367 List<TestFilter> mdIncludes = getFilter(mIncludeFilters, moduleId); 368 List<TestFilter> mdExcludes = getFilter(mExcludeFilters, moduleId); 369 if (!mdIncludes.isEmpty()) { 370 addTestIncludes((ITestFilterReceiver) test, mdIncludes, name); 371 } 372 if (!mdExcludes.isEmpty()) { 373 addTestExcludes((ITestFilterReceiver) test, mdExcludes, name); 374 } 375 } 376 377 @VisibleForTesting filterByConfigMetadata(IConfiguration config, MultiMap<String, String> include, MultiMap<String, String> exclude)378 protected boolean filterByConfigMetadata(IConfiguration config, 379 MultiMap<String, String> include, MultiMap<String, String> exclude) { 380 MultiMap<String, String> metadata = config.getConfigurationDescription().getAllMetaData(); 381 boolean shouldInclude = false; 382 for (String key : include.keySet()) { 383 Set<String> filters = new HashSet<>(include.get(key)); 384 if (metadata.containsKey(key)) { 385 filters.retainAll(metadata.get(key)); 386 if (!filters.isEmpty()) { 387 // inclusion filter is not empty and there's at least one matching inclusion 388 // rule so there's no need to match other inclusion rules 389 shouldInclude = true; 390 break; 391 } 392 } 393 } 394 if (!include.isEmpty() && !shouldInclude) { 395 // if inclusion filter is not empty and we didn't find a match, the module will not be 396 // included 397 return false; 398 } 399 // Now evaluate exclusion rules, this ordering also means that exclusion rules may override 400 // inclusion rules: a config already matched for inclusion may still be excluded if matching 401 // rules exist 402 for (String key : exclude.keySet()) { 403 Set<String> filters = new HashSet<>(exclude.get(key)); 404 if (metadata.containsKey(key)) { 405 filters.retainAll(metadata.get(key)); 406 if (!filters.isEmpty()) { 407 // we found at least one matching exclusion rules, so we are excluding this 408 // this module 409 return false; 410 } 411 } 412 } 413 // we've matched at least one inclusion rule (if there's any) AND we didn't match any of the 414 // exclusion rules (if there's any) 415 return true; 416 } 417 shouldRunModule(String moduleId)418 private boolean shouldRunModule(String moduleId) { 419 List<TestFilter> mdIncludes = getFilter(mIncludeFilters, moduleId); 420 List<TestFilter> mdExcludes = getFilter(mExcludeFilters, moduleId); 421 // if including all modules or includes exist for this module, and there are not excludes 422 // for the entire module, this module should be run. 423 return (mIncludeAll || !mdIncludes.isEmpty()) && !containsModuleExclude(mdExcludes); 424 } 425 addTestIncludes(ITestFilterReceiver test, List<TestFilter> includes, String name)426 private void addTestIncludes(ITestFilterReceiver test, List<TestFilter> includes, 427 String name) { 428 if (test instanceof ITestFileFilterReceiver) { 429 File includeFile = createFilterFile(name, ".include", includes); 430 ((ITestFileFilterReceiver)test).setIncludeTestFile(includeFile); 431 } else { 432 // add test includes one at a time 433 for (TestFilter include : includes) { 434 String filterTestName = include.getTest(); 435 if (filterTestName != null) { 436 test.addIncludeFilter(filterTestName); 437 } 438 } 439 } 440 } 441 addTestExcludes(ITestFilterReceiver test, List<TestFilter> excludes, String name)442 private void addTestExcludes(ITestFilterReceiver test, List<TestFilter> excludes, 443 String name) { 444 if (test instanceof ITestFileFilterReceiver) { 445 File excludeFile = createFilterFile(name, ".exclude", excludes); 446 ((ITestFileFilterReceiver)test).setExcludeTestFile(excludeFile); 447 } else { 448 // add test excludes one at a time 449 for (TestFilter exclude : excludes) { 450 test.addExcludeFilter(exclude.getTest()); 451 } 452 } 453 } 454 createFilterFile(String prefix, String suffix, List<TestFilter> filters)455 private File createFilterFile(String prefix, String suffix, List<TestFilter> filters) { 456 File filterFile = null; 457 PrintWriter out = null; 458 try { 459 filterFile = FileUtil.createTempFile(prefix, suffix); 460 out = new PrintWriter(filterFile); 461 for (TestFilter filter : filters) { 462 String filterTest = filter.getTest(); 463 if (filterTest != null) { 464 out.println(filterTest); 465 } 466 } 467 out.flush(); 468 } catch (IOException e) { 469 throw new RuntimeException("Failed to create filter file"); 470 } finally { 471 if (out != null) { 472 out.close(); 473 } 474 } 475 filterFile.deleteOnExit(); 476 return filterFile; 477 } 478 479 /* 480 * Returns true iff one or more test filters in excludes apply to the entire module. 481 */ containsModuleExclude(Collection<TestFilter> excludes)482 private boolean containsModuleExclude(Collection<TestFilter> excludes) { 483 for (TestFilter exclude : excludes) { 484 if (exclude.getTest() == null) { 485 return true; 486 } 487 } 488 return false; 489 } 490 491 /** 492 * A {@link FilenameFilter} to find all the config files in a directory. 493 */ 494 public static class ConfigFilter implements FilenameFilter { 495 496 /** 497 * {@inheritDoc} 498 */ 499 @Override accept(File dir, String name)500 public boolean accept(File dir, String name) { 501 CLog.d("%s/%s", dir.getAbsolutePath(), name); 502 return name.endsWith(CONFIG_EXT); 503 } 504 } 505 506 /** 507 * {@inheritDoc} 508 */ 509 @Override getModules(String serial, int shardIndex)510 public LinkedList<IModuleDef> getModules(String serial, int shardIndex) { 511 Collections.sort(mNonTokenModules, new ExecutionOrderComparator()); 512 List<IModuleDef> modules = getShard(mNonTokenModules, shardIndex, mTotalShards); 513 if (modules == null) { 514 modules = new LinkedList<IModuleDef>(); 515 } 516 long estimatedTime = 0; 517 for (IModuleDef def : modules) { 518 estimatedTime += def.getRuntimeHint(); 519 } 520 521 // FIXME: Token Modules are the only last part that is not deterministic. 522 synchronized (lock) { 523 // Get tokens from the device 524 Set<String> tokens = mDeviceTokens.get(serial); 525 if (tokens != null && !tokens.isEmpty()) { 526 // if it matches any of the token modules, add them 527 for (IModuleDef def : mTokenModules) { 528 if (!mTokenModuleScheduled.contains(def)) { 529 if (tokens.equals(def.getTokens())) { 530 modules.add(def); 531 CLog.d("Adding %s to scheduled token", def); 532 mTokenModuleScheduled.add(def); 533 } 534 } 535 } 536 } 537 // the last shard going through may add everything remaining. 538 if (mInitCount == (mTotalShards - 1) && 539 mTokenModuleScheduled.size() != mTokenModules.size()) { 540 mTokenModules.removeAll(mTokenModuleScheduled); 541 if (mTotalShards != 1) { 542 // Only print the warnings if we are sharding. 543 CLog.e("Could not find any token for %s. Adding to last shard.", mTokenModules); 544 } 545 modules.addAll(mTokenModules); 546 } 547 mInitCount++; 548 } 549 Collections.sort(modules, new ExecutionOrderComparator()); 550 int uniqueCount = UniqueModuleCountUtil.countUniqueModules(modules); 551 CLog.logAndDisplay(LogLevel.INFO, "%s running %s test sub-modules, expected to complete " 552 + "in %s.", serial, uniqueCount, TimeUtil.formatElapsedTime(estimatedTime)); 553 CLog.d("module list for this shard: %s", modules); 554 LinkedList<IModuleDef> tests = new LinkedList<>(); 555 tests.addAll(modules); 556 return tests; 557 } 558 559 /** 560 * Helper to linearly split the list into shards with balanced runtimeHint. 561 * Exposed for testing. 562 */ getShard(List<IModuleDef> fullList, int shardIndex, int totalShard)563 protected List<IModuleDef> getShard(List<IModuleDef> fullList, int shardIndex, int totalShard) { 564 List<List<IModuleDef>> res = LinearPartition.split(fullList, totalShard); 565 if (res.isEmpty()) { 566 return null; 567 } 568 if (shardIndex >= res.size()) { 569 // If we could not shard up to expectation 570 return null; 571 } 572 return res.get(shardIndex); 573 } 574 575 /** 576 * @return the {@link List} of modules whose name contains the given pattern. 577 */ getModuleNamesMatching(File directory, String pattern)578 public static List<String> getModuleNamesMatching(File directory, String pattern) { 579 String[] names = directory.list(new NameFilter(pattern)); 580 List<String> modules = new ArrayList<String>(names.length); 581 for (String name : names) { 582 int index = name.indexOf(CONFIG_EXT); 583 if (index > 0) { 584 String module = name.substring(0, index); 585 if (module.equals(pattern)) { 586 // Pattern represents a single module, just return a single-item list 587 modules = new ArrayList<>(1); 588 modules.add(module); 589 return modules; 590 } 591 modules.add(module); 592 } 593 } 594 return modules; 595 } 596 putArgs(List<String> args, Map<String, Map<String, List<String>>> argsMap)597 private static void putArgs(List<String> args, 598 Map<String, Map<String, List<String>>> argsMap) { 599 for (String arg : args) { 600 String[] parts = arg.split(":"); 601 String target = parts[0]; 602 String name = parts[1]; 603 String value; 604 if (parts.length == 4) { 605 // key and value given, keep the pair delimited by ':' and stored as value 606 value = String.format("%s:%s", parts[2], parts[3]); 607 } else { 608 value = parts[2]; 609 } 610 Map<String, List<String>> map = argsMap.get(target); 611 if (map == null) { 612 map = new HashMap<>(); 613 argsMap.put(target, map); 614 } 615 List<String> valueList = map.get(name); 616 if (valueList == null) { 617 valueList = new ArrayList<>(); 618 map.put(name, valueList); 619 } 620 valueList.add(value); 621 } 622 } 623 624 /** 625 * Sort by name and use runtimeHint for separation, shortest test first. 626 */ 627 private static class ExecutionOrderComparator implements Comparator<IModuleDef> { 628 @Override compare(IModuleDef def1, IModuleDef def2)629 public int compare(IModuleDef def1, IModuleDef def2) { 630 int value1 = 0; 631 int value2 = 0; 632 if (ENDING_MODULES.containsKey(def1.getName())) { 633 value1 = ENDING_MODULES.get(def1.getName()); 634 } 635 if (ENDING_MODULES.containsKey(def2.getName())) { 636 value2 = ENDING_MODULES.get(def2.getName()); 637 } 638 if (value1 == 0 && value2 == 0) { 639 int time = (int) Math.signum(def1.getRuntimeHint() - def2.getRuntimeHint()); 640 if (time == 0) { 641 return def1.getName().compareTo(def2.getName()); 642 } 643 return time; 644 } 645 return (int) Math.signum(value1 - value2); 646 } 647 } 648 649 /** 650 * {@inheritDoc} 651 */ 652 @Override tearDown()653 public void tearDown() { 654 mNonTokenModules.clear(); 655 mTokenModules.clear(); 656 mIncludeFilters.clear(); 657 mExcludeFilters.clear(); 658 mTestArgs.clear(); 659 mModuleArgs.clear(); 660 } 661 662 /** 663 * @return the mConfigFactory 664 */ getConfigFactory()665 protected IConfigurationFactory getConfigFactory() { 666 return mConfigFactory; 667 } 668 } 669