1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.compatibility.testtype; 18 19 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; 20 import com.android.ddmlib.IShellOutputReceiver; 21 import com.android.ddmlib.Log; 22 import com.android.ddmlib.Log.LogLevel; 23 import com.android.ddmlib.MultiLineReceiver; 24 import com.android.tradefed.build.IBuildInfo; 25 import com.android.tradefed.config.Option; 26 import com.android.tradefed.config.OptionCopier; 27 import com.android.tradefed.device.DeviceNotAvailableException; 28 import com.android.tradefed.device.ITestDevice; 29 import com.android.tradefed.log.LogUtil.CLog; 30 import com.android.tradefed.result.ITestInvocationListener; 31 import com.android.tradefed.result.TestDescription; 32 import com.android.tradefed.testtype.IAbi; 33 import com.android.tradefed.testtype.IAbiReceiver; 34 import com.android.tradefed.testtype.IBuildReceiver; 35 import com.android.tradefed.testtype.IDeviceTest; 36 import com.android.tradefed.testtype.IRemoteTest; 37 import com.android.tradefed.testtype.IRuntimeHintProvider; 38 import com.android.tradefed.testtype.IShardableTest; 39 import com.android.tradefed.testtype.ITestCollector; 40 import com.android.tradefed.testtype.ITestFileFilterReceiver; 41 import com.android.tradefed.testtype.ITestFilterReceiver; 42 import com.android.tradefed.util.AbiUtils; 43 import com.android.tradefed.util.ArrayUtil; 44 import com.android.tradefed.util.FileUtil; 45 46 import com.google.common.base.Splitter; 47 48 import java.io.BufferedReader; 49 import java.io.File; 50 import java.io.FileReader; 51 import java.io.FilenameFilter; 52 import java.io.IOException; 53 import java.io.PrintWriter; 54 import java.util.ArrayList; 55 import java.util.Arrays; 56 import java.util.Collection; 57 import java.util.Collections; 58 import java.util.HashSet; 59 import java.util.LinkedHashSet; 60 import java.util.List; 61 import java.util.Set; 62 import java.util.concurrent.TimeUnit; 63 64 import vogar.ExpectationStore; 65 import vogar.ModeId; 66 67 /** 68 * A wrapper to run tests against Dalvik. 69 */ 70 public class DalvikTest implements IAbiReceiver, IBuildReceiver, IDeviceTest, IRemoteTest, 71 IRuntimeHintProvider, IShardableTest, ITestCollector, ITestFileFilterReceiver, 72 ITestFilterReceiver { 73 74 private static final String TAG = DalvikTest.class.getSimpleName(); 75 76 /** 77 * TEST_PACKAGES is a Set containing the names of packages on the classpath known to contain 78 * tests to be run under DalvikTest. The TEST_PACKAGES set is used to shard DalvikTest into 79 * multiple DalvikTests, each responsible for running one of these packages' tests. 80 */ 81 private static final Set<String> TEST_PACKAGES = new HashSet<>(); 82 private static final String JDWP_PACKAGE_BASE = "org.apache.harmony.jpda.tests.jdwp.%s"; 83 static { 84 // Though uppercase, these are package names, not class names String.format(JDWP_PACKAGE_BASE, "ArrayReference")85 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "ArrayReference")); String.format(JDWP_PACKAGE_BASE, "ArrayType")86 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "ArrayType")); String.format(JDWP_PACKAGE_BASE, "ClassLoaderReference")87 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "ClassLoaderReference")); String.format(JDWP_PACKAGE_BASE, "ClassObjectReference")88 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "ClassObjectReference")); String.format(JDWP_PACKAGE_BASE, "ClassType")89 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "ClassType")); String.format(JDWP_PACKAGE_BASE, "DebuggerOnDemand")90 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "DebuggerOnDemand")); String.format(JDWP_PACKAGE_BASE, "Deoptimization")91 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "Deoptimization")); String.format(JDWP_PACKAGE_BASE, "EventModifiers")92 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "EventModifiers")); String.format(JDWP_PACKAGE_BASE, "Events")93 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "Events")); String.format(JDWP_PACKAGE_BASE, "InterfaceType")94 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "InterfaceType")); String.format(JDWP_PACKAGE_BASE, "Method")95 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "Method")); String.format(JDWP_PACKAGE_BASE, "MultiSession")96 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "MultiSession")); String.format(JDWP_PACKAGE_BASE, "ObjectReference")97 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "ObjectReference")); String.format(JDWP_PACKAGE_BASE, "ReferenceType")98 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "ReferenceType")); String.format(JDWP_PACKAGE_BASE, "StackFrame")99 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "StackFrame")); String.format(JDWP_PACKAGE_BASE, "StringReference")100 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "StringReference")); String.format(JDWP_PACKAGE_BASE, "ThreadGroupReference")101 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "ThreadGroupReference")); String.format(JDWP_PACKAGE_BASE, "ThreadReference")102 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "ThreadReference")); String.format(JDWP_PACKAGE_BASE, "VirtualMachine")103 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "VirtualMachine")); 104 } 105 106 private static final String EXPECTATIONS_EXT = ".expectations"; 107 // Command to run the VM, args are bitness, classpath, dalvik-args, abi, runner-args, 108 // include and exclude filters, and exclude filters file. 109 private static final String COMMAND = "dalvikvm%s -classpath %s %s " 110 + "com.android.compatibility.dalvik.DalvikTestRunner --abi=%s %s %s %s %s %s %s"; 111 private static final String INCLUDE_FILE = "/data/local/tmp/dalvik/includes"; 112 private static final String EXCLUDE_FILE = "/data/local/tmp/dalvik/excludes"; 113 private static String START_RUN = "start-run"; 114 private static String END_RUN = "end-run"; 115 private static String START_TEST = "start-test"; 116 private static String END_TEST = "end-test"; 117 private static String FAILURE = "failure"; 118 119 // If we are running with adbconnection jdwp provider (hence a libjdwp agent). 120 private boolean mIsAdbConnection = true; 121 122 @Option(name = "run-name", description = "The name to use when reporting results") 123 private String mRunName; 124 125 @Option(name = "classpath", description = "Holds the paths to search when loading tests") 126 private List<String> mClasspath = new ArrayList<>(); 127 128 @Option(name = "dalvik-arg", description = "Holds arguments to pass to Dalvik") 129 private List<String> mDalvikArgs = new ArrayList<>(); 130 131 @Option(name = "dalvik-arg-adbconnection", 132 description = "Holds arguments to pass to Dalvik when " + 133 "dalvik.vm.jdwp-provider == adbconnection or default or is empty") 134 private List<String> mDalvikArgsAdbconnection = new ArrayList<>(); 135 136 @Option(name = "dalvik-arg-internal", 137 description = "Holds arguments to pass to Dalvik only when " + 138 "dalvik.vm.jdwp-provider == internal") 139 private List<String> mDalvikArgsInternal = new ArrayList<>(); 140 141 @Option(name = "runner-arg", 142 description = "Holds arguments to pass to the device-side test runner") 143 private List<String> mRunnerArgs = new ArrayList<>(); 144 145 @Option(name = "include-filter", 146 description = "The include filters of the test name to run.") 147 private Set<String> mIncludeFilters = new LinkedHashSet<>(); 148 149 @Option(name = "exclude-filter", 150 description = "The exclude filters of the test name to run.") 151 private Set<String> mExcludeFilters = new LinkedHashSet<>(); 152 153 @Option(name = "test-file-include-filter", 154 description="A file containing a list of line separated test classes and optionally" 155 + " methods to include") 156 private File mIncludeTestFile = null; 157 158 @Option(name = "test-file-exclude-filter", 159 description="A file containing a list of line separated test classes and optionally" 160 + " methods to exclude") 161 private File mExcludeTestFile = null; 162 163 @Option(name = "runtime-hint", 164 isTimeVal = true, 165 description="The hint about the test's runtime.") 166 private long mRuntimeHint = 60000;// 1 minute 167 168 @Option(name = "known-failures-adbconnection", 169 description = "Comma-separated list of files specifying known-failures to be skipped") 170 private String mKnownFailuresAdbconnection; 171 @Option(name = "known-failures-internal", 172 description = "Comma-separated list of files specifying known-failures to be skipped") 173 private String mKnownFailuresInternal; 174 175 @Option(name = "known-failures", 176 description = "Comma-separated list of files specifying known-failures to be skipped") 177 private String mKnownFailures; 178 179 @Option(name = "collect-tests-only", 180 description = "Only invoke the instrumentation to collect list of applicable test " 181 + "cases. All test run callbacks will be triggered, but test execution will " 182 + "not be actually carried out.") 183 private boolean mCollectTestsOnly = false; 184 185 @Option(name = "per-test-timeout", 186 description = "The maximum amount of time during which the DalvikTestRunner may " 187 + "yield no output. Because the runner outputs results for each test, this " 188 + "is essentially a per-test timeout") 189 private long mPerTestTimeout = 10; // 10 minutes 190 191 private IAbi mAbi; 192 private CompatibilityBuildHelper mBuildHelper; 193 private ITestDevice mDevice; 194 195 /** 196 * {@inheritDoc} 197 */ 198 @Override setAbi(IAbi abi)199 public void setAbi(IAbi abi) { 200 mAbi = abi; 201 } 202 203 /** 204 * {@inheritDoc} 205 */ 206 @Override getAbi()207 public IAbi getAbi() { 208 return mAbi; 209 } 210 211 /** 212 * {@inheritDoc} 213 */ 214 @Override setBuild(IBuildInfo build)215 public void setBuild(IBuildInfo build) { 216 mBuildHelper = new CompatibilityBuildHelper(build); 217 } 218 219 /** 220 * {@inheritDoc} 221 */ 222 @Override setDevice(ITestDevice device)223 public void setDevice(ITestDevice device) { 224 mDevice = device; 225 } 226 227 /** 228 * {@inheritDoc} 229 */ 230 @Override getDevice()231 public ITestDevice getDevice() { 232 return mDevice; 233 } 234 235 /** 236 * {@inheritDoc} 237 */ 238 @Override addIncludeFilter(String filter)239 public void addIncludeFilter(String filter) { 240 mIncludeFilters.add(filter); 241 } 242 243 /** 244 * {@inheritDoc} 245 */ 246 @Override addAllIncludeFilters(Set<String> filters)247 public void addAllIncludeFilters(Set<String> filters) { 248 mIncludeFilters.addAll(filters); 249 } 250 251 /** 252 * {@inheritDoc} 253 */ 254 @Override addExcludeFilter(String filter)255 public void addExcludeFilter(String filter) { 256 mExcludeFilters.add(filter); 257 } 258 259 /** 260 * {@inheritDoc} 261 */ 262 @Override addAllExcludeFilters(Set<String> filters)263 public void addAllExcludeFilters(Set<String> filters) { 264 mExcludeFilters.addAll(filters); 265 } 266 267 /** 268 * {@inheritDoc} 269 */ 270 @Override getIncludeFilters()271 public Set<String> getIncludeFilters() { 272 return mIncludeFilters; 273 } 274 275 /** 276 * {@inheritDoc} 277 */ 278 @Override getExcludeFilters()279 public Set<String> getExcludeFilters() { 280 return mExcludeFilters; 281 } 282 283 /** 284 * {@inheritDoc} 285 */ 286 @Override clearIncludeFilters()287 public void clearIncludeFilters() { 288 mIncludeFilters.clear(); 289 } 290 291 /** 292 * {@inheritDoc} 293 */ 294 @Override clearExcludeFilters()295 public void clearExcludeFilters() { 296 mExcludeFilters.clear(); 297 } 298 299 /** 300 * {@inheritDoc} 301 */ 302 @Override setIncludeTestFile(File testFile)303 public void setIncludeTestFile(File testFile) { 304 mIncludeTestFile = testFile; 305 } 306 307 /** 308 * {@inheritDoc} 309 */ 310 @Override setExcludeTestFile(File testFile)311 public void setExcludeTestFile(File testFile) { 312 mExcludeTestFile = testFile; 313 } 314 315 /** 316 * {@inheritDoc} 317 */ 318 @Override getRuntimeHint()319 public long getRuntimeHint() { 320 return mRuntimeHint; 321 } 322 323 /** 324 * {@inheritDoc} 325 */ 326 @Override setCollectTestsOnly(boolean shouldCollectTest)327 public void setCollectTestsOnly(boolean shouldCollectTest) { 328 mCollectTestsOnly = shouldCollectTest; 329 } 330 isAdbconnection(ITestDevice device)331 private static boolean isAdbconnection(ITestDevice device) throws DeviceNotAvailableException { 332 String provider = device.getProperty("dalvik.vm.jdwp-provider"); 333 if (provider == null || provider.equals("default") || provider.equals("adbconnection")) { 334 return true; 335 } else if (provider.equals("internal")) { 336 return false; 337 } else { 338 throw new RuntimeException("Unknown dalvik.vm.jdwp-provider = " + provider); 339 } 340 } 341 342 /** 343 * {@inheritDoc} 344 */ 345 @Override run(final ITestInvocationListener listener)346 public void run(final ITestInvocationListener listener) throws DeviceNotAvailableException { 347 String abiName = mAbi.getName(); 348 String bitness = AbiUtils.getBitness(abiName); 349 mIsAdbConnection = isAdbconnection(getDevice()); 350 351 File tmpExcludeFile = null; 352 String excludeFile = ""; 353 try { 354 // push one file of exclude filters to the device 355 tmpExcludeFile = getExcludeFile(); 356 if (!mDevice.pushFile(tmpExcludeFile, EXCLUDE_FILE)) { 357 Log.logAndDisplay(LogLevel.ERROR, TAG, "Couldn't push file: " + tmpExcludeFile); 358 } else { 359 CLog.d("exclude-filter-file: %s", safeReadContentFromFile(tmpExcludeFile)); 360 // If sucessfully pushed then add it as a filter. 361 excludeFile = String.format("--exclude-filter-file=%s", EXCLUDE_FILE); 362 } 363 } catch (IOException e) { 364 throw new RuntimeException("Failed to parse expectations", e); 365 } finally { 366 FileUtil.deleteFile(tmpExcludeFile); 367 } 368 369 // push one file of include filters to the device, if file exists 370 String includeFile = ""; 371 if (mIncludeTestFile != null) { 372 String path = mIncludeTestFile.getAbsolutePath(); 373 if (!mIncludeTestFile.isFile() || !mIncludeTestFile.canRead()) { 374 throw new RuntimeException(String.format("Failed to read include file %s", path)); 375 } 376 if (!mDevice.pushFile(mIncludeTestFile, INCLUDE_FILE)) { 377 Log.logAndDisplay(LogLevel.ERROR, TAG, "Couldn't push file: " + path); 378 } else { 379 CLog.d("include-filter-file: %s", safeReadContentFromFile(mIncludeTestFile)); 380 // If sucessfully pushed then add it as a filter. 381 includeFile = String.format("--include-filter-file=%s", INCLUDE_FILE); 382 } 383 } 384 385 if (mIsAdbConnection) { 386 Log.logAndDisplay(LogLevel.INFO, TAG, "Running with ADBConnection/libjdwp agent"); 387 mDalvikArgs.addAll(mDalvikArgsAdbconnection); 388 } else { 389 Log.logAndDisplay(LogLevel.INFO, TAG, "Running with internal jdwp implementation"); 390 mDalvikArgs.addAll(mDalvikArgsInternal); 391 } 392 393 // Create command 394 mDalvikArgs.add("-Duser.name=shell"); 395 mDalvikArgs.add("-Duser.language=en"); 396 mDalvikArgs.add("-Duser.region=US"); 397 mDalvikArgs.add("-Xcheck:jni"); 398 mDalvikArgs.add("-Xjnigreflimit:2000"); 399 400 String dalvikArgs = ArrayUtil.join(" ", mDalvikArgs); 401 dalvikArgs = dalvikArgs.replace("|#ABI#|", bitness); 402 403 String runnerArgs = ArrayUtil.join(" ", mRunnerArgs); 404 // Filters 405 StringBuilder includeFilters = new StringBuilder(); 406 if (!mIncludeFilters.isEmpty()) { 407 includeFilters.append("--include-filter="); 408 includeFilters.append(ArrayUtil.join(",", mIncludeFilters)); 409 } 410 StringBuilder excludeFilters = new StringBuilder(); 411 if (!mExcludeFilters.isEmpty()) { 412 excludeFilters.append("--exclude-filter="); 413 excludeFilters.append(ArrayUtil.join(",", mExcludeFilters)); 414 } 415 416 // Communicate with DalvikTestRunner if tests should only be collected 417 String collectTestsOnlyString = (mCollectTestsOnly) ? "--collect-tests-only" : ""; 418 final String command = String.format(COMMAND, bitness, 419 ArrayUtil.join(File.pathSeparator, mClasspath), 420 dalvikArgs, abiName, runnerArgs, 421 includeFilters, excludeFilters, includeFile, excludeFile, collectTestsOnlyString); 422 IShellOutputReceiver receiver = new MultiLineReceiver() { 423 private TestDescription test; 424 425 @Override 426 public boolean isCancelled() { 427 return false; 428 } 429 430 @Override 431 public void processNewLines(String[] lines) { 432 for (String line : lines) { 433 String[] parts = line.split(":", 2); 434 String tag = parts[0]; 435 if (tag.equals(START_RUN)) { 436 listener.testRunStarted(mRunName, Integer.parseInt(parts[1])); 437 Log.logAndDisplay(LogLevel.INFO, TAG, command); 438 } else if (tag.equals(END_RUN)) { 439 listener.testRunEnded(Integer.parseInt(parts[1]), 440 Collections.<String, String>emptyMap()); 441 } else if (tag.equals(START_TEST)) { 442 test = getTestDescription(parts[1]); 443 listener.testStarted(test); 444 } else if (tag.equals(FAILURE)) { 445 listener.testFailed(test, processSerializedValue(parts[1])); 446 } else if (tag.equals(END_TEST)) { 447 listener.testEnded(getTestDescription(parts[1]), 448 Collections.<String, String>emptyMap()); 449 } 450 // Always log the output for debugging 451 CLog.d(line); 452 } 453 } 454 455 private String processSerializedValue(String input) { 456 // Opposite of stringify. 457 return input.replace("^~^", "\n"); 458 } 459 460 private TestDescription getTestDescription(String name) { 461 String[] parts = name.split("#"); 462 String className = parts[0]; 463 String testName = ""; 464 if (parts.length > 1) { 465 testName = parts[1]; 466 } 467 return new TestDescription(className, testName); 468 } 469 470 }; 471 mDevice.executeShellCommand(command, receiver, mPerTestTimeout, TimeUnit.MINUTES, 1); 472 } 473 474 /* 475 * Due to known failures, there are typically too many excludes to pass via command line. 476 * Collect excludes from .expectation files in the testcases directory, from files in the 477 * module's resources directory, and from mExcludeTestFile, if set. 478 */ getExcludeFile()479 private File getExcludeFile() throws IOException { 480 File excludeFile = null; 481 PrintWriter out = null; 482 483 try { 484 excludeFile = File.createTempFile("excludes", "txt"); 485 out = new PrintWriter(excludeFile); 486 // create expectation store from set of expectation files found in testcases dir 487 Set<File> expectationFiles = new HashSet<>(); 488 for (File f : mBuildHelper.getTestsDir().listFiles( 489 new ExpectationFileFilter(mRunName))) { 490 expectationFiles.add(f); 491 } 492 ExpectationStore testsDirStore = 493 ExpectationStore.parse(expectationFiles, ModeId.DEVICE); 494 // create expectation store from expectation files found in module resources dir 495 ExpectationStore resourceStore = null; 496 Set<String> knownFailuresFiles = new HashSet<String>(); 497 Splitter splitter = Splitter.on(',').trimResults(); 498 if (mKnownFailures != null) { 499 knownFailuresFiles.addAll(splitter.splitToList(mKnownFailures)); 500 } 501 if (mIsAdbConnection && mKnownFailuresAdbconnection != null) { 502 knownFailuresFiles.addAll(splitter.splitToList(mKnownFailuresAdbconnection)); 503 } 504 if (!mIsAdbConnection && mKnownFailuresInternal != null) { 505 knownFailuresFiles.addAll(splitter.splitToList(mKnownFailuresInternal)); 506 } 507 if (knownFailuresFiles.size() != 0) { 508 resourceStore = ExpectationStore.parseResources( 509 getClass(), knownFailuresFiles, ModeId.DEVICE); 510 } 511 // Add expectations from testcases dir 512 for (String exclude : testsDirStore.getAllFailures().keySet()) { 513 out.println(exclude); 514 } 515 for (String exclude : testsDirStore.getAllOutComes().keySet()) { 516 out.println(exclude); 517 } 518 // Add expectations from resources dir 519 if (resourceStore != null) { 520 for (String exclude : resourceStore.getAllFailures().keySet()) { 521 out.println(exclude); 522 } 523 for (String exclude : resourceStore.getAllOutComes().keySet()) { 524 out.println(exclude); 525 } 526 } 527 // Add excludes from test-file-exclude-filter option 528 for (String exclude : getFiltersFromFile(mExcludeTestFile)) { 529 out.println(exclude); 530 } 531 out.flush(); 532 } finally { 533 if (out != null) { 534 out.close(); 535 } 536 } 537 return excludeFile; 538 } 539 540 541 /* 542 * Helper method that reads filters from a file into a set. 543 * Returns an empty set given a null file 544 */ getFiltersFromFile(File f)545 private static Set<String> getFiltersFromFile(File f) throws IOException { 546 Set<String> filters = new HashSet<String>(); 547 if (f != null) { 548 BufferedReader reader = new BufferedReader(new FileReader(f)); 549 String filter = null; 550 while ((filter = reader.readLine()) != null) { 551 filters.add(filter); 552 } 553 reader.close(); 554 } 555 return filters; 556 } 557 558 /** 559 * {@inheritDoc} 560 */ 561 @Override split()562 public Collection<IRemoteTest> split() { 563 List<IRemoteTest> shards = new ArrayList<>(); 564 // A DalvikTest to run any tests not contained in packages from TEST_PACKAGES, may be empty 565 DalvikTest catchAll = new DalvikTest(); 566 OptionCopier.copyOptionsNoThrow(this, catchAll); 567 catchAll.mAbi = mAbi; 568 shards.add(catchAll); 569 // estimate catchAll's runtime to be that of a single package in TEST_PACKAGES 570 long runtimeHint = mRuntimeHint / TEST_PACKAGES.size(); 571 catchAll.mRuntimeHint = runtimeHint; 572 for (String packageName: TEST_PACKAGES) { 573 catchAll.addExcludeFilter(packageName); 574 // create one shard for package 'packageName' 575 DalvikTest test = new DalvikTest(); 576 OptionCopier.copyOptionsNoThrow(this, test); 577 test.addIncludeFilter(packageName); 578 test.mRuntimeHint = runtimeHint / TEST_PACKAGES.size(); 579 test.mAbi = mAbi; 580 shards.add(test); 581 } 582 // return a shard for each package in TEST_PACKAGE, plus a shard for any other tests 583 return shards; 584 } 585 586 /** 587 * A {@link FilenameFilter} to find all the expectation files in a directory. 588 */ 589 public static class ExpectationFileFilter implements FilenameFilter { 590 591 private String mName; 592 ExpectationFileFilter(String name)593 public ExpectationFileFilter(String name) { 594 mName = name; 595 } 596 /** 597 * {@inheritDoc} 598 */ 599 @Override accept(File dir, String name)600 public boolean accept(File dir, String name) { 601 return name.startsWith(mName) && name.endsWith(EXPECTATIONS_EXT); 602 } 603 } 604 safeReadContentFromFile(File file)605 private String safeReadContentFromFile(File file) { 606 try { 607 return FileUtil.readStringFromFile(file); 608 } catch (IOException e) { 609 CLog.e(e); 610 return null; 611 } 612 } 613 } 614