1 /* 2 * Copyright (C) 2010 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.tradefed.testtype; 18 19 import com.android.ddmlib.FileListingService; 20 import com.android.ddmlib.IShellOutputReceiver; 21 import com.android.tradefed.config.Option; 22 import com.android.tradefed.config.OptionClass; 23 import com.android.tradefed.config.OptionCopier; 24 import com.android.tradefed.device.CollectingOutputReceiver; 25 import com.android.tradefed.device.DeviceNotAvailableException; 26 import com.android.tradefed.device.ITestDevice; 27 import com.android.tradefed.log.LogUtil.CLog; 28 import com.android.tradefed.result.ITestInvocationListener; 29 import com.android.tradefed.util.ArrayUtil; 30 import com.android.tradefed.util.FileUtil; 31 32 import com.google.common.annotations.VisibleForTesting; 33 34 import org.json.JSONException; 35 import org.json.JSONObject; 36 37 import java.io.File; 38 import java.io.IOException; 39 import java.util.ArrayList; 40 import java.util.Collection; 41 import java.util.HashSet; 42 import java.util.List; 43 import java.util.Set; 44 import java.util.concurrent.TimeUnit; 45 import java.util.regex.Pattern; 46 47 /** A Test that runs a native test package on given device. */ 48 @OptionClass(alias = "gtest") 49 public class GTest 50 implements IDeviceTest, 51 IRemoteTest, 52 ITestFilterReceiver, 53 IRuntimeHintProvider, 54 ITestCollector, 55 IShardableTest, 56 IStrictShardableTest { 57 58 static final String DEFAULT_NATIVETEST_PATH = "/data/nativetest"; 59 private static final Pattern EXE_FILE = Pattern.compile("^[-l]r.x.+"); 60 61 private ITestDevice mDevice = null; 62 private boolean mRunDisabledTests = false; 63 64 @Option(name = "native-test-device-path", 65 description="The path on the device where native tests are located.") 66 private String mNativeTestDevicePath = DEFAULT_NATIVETEST_PATH; 67 68 @Option(name = "file-exclusion-filter-regex", 69 description = "Regex to exclude certain files from executing. Can be repeated") 70 private List<String> mFileExclusionFilterRegex = new ArrayList<>(); 71 72 @Option(name = "module-name", 73 description="The name of the native test module to run.") 74 private String mTestModule = null; 75 76 @Option(name = "positive-testname-filter", 77 description="The GTest-based positive filter of the test name to run.") 78 private String mTestNamePositiveFilter = null; 79 @Option(name = "negative-testname-filter", 80 description="The GTest-based negative filter of the test name to run.") 81 private String mTestNameNegativeFilter = null; 82 83 @Option(name = "include-filter", 84 description="The GTest-based positive filter of the test names to run.") 85 private Set<String> mIncludeFilters = new HashSet<>(); 86 @Option(name = "exclude-filter", 87 description="The GTest-based negative filter of the test names to run.") 88 private Set<String> mExcludeFilters = new HashSet<>(); 89 90 @Option( 91 name = "native-test-timeout", 92 description = 93 "The max time for a gtest to run. Test run will be aborted if any test " 94 + "takes longer.", 95 isTimeVal = true 96 ) 97 private long mMaxTestTimeMs = 1 * 60 * 1000L; 98 99 @Option(name = "send-coverage", 100 description = "Send coverage target info to test listeners.") 101 private boolean mSendCoverage = true; 102 103 @Option(name ="prepend-filename", 104 description = "Prepend filename as part of the classname for the tests.") 105 private boolean mPrependFileName = false; 106 107 @Option(name = "before-test-cmd", 108 description = "adb shell command(s) to run before GTest.") 109 private List<String> mBeforeTestCmd = new ArrayList<>(); 110 111 112 @Option( 113 name = "reboot-before-test", 114 description = "Reboot the device before the test suite starts." 115 ) 116 private boolean mRebootBeforeTest = false; 117 118 @Option(name = "after-test-cmd", 119 description = "adb shell command(s) to run after GTest.") 120 private List<String> mAfterTestCmd = new ArrayList<>(); 121 122 @Option(name = "run-test-as", description = "User to execute test binary as.") 123 private String mRunTestAs = null; 124 125 @Option(name = "ld-library-path", 126 description = "LD_LIBRARY_PATH value to include in the GTest execution command.") 127 private String mLdLibraryPath = null; 128 129 @Option(name = "native-test-flag", description = 130 "Additional flag values to pass to the native test's shell command. " + 131 "Flags should be complete, including any necessary dashes: \"--flag=value\"") 132 private List<String> mGTestFlags = new ArrayList<>(); 133 134 @Option(name = "runtime-hint", description="The hint about the test's runtime.", 135 isTimeVal = true) 136 private long mRuntimeHint = 60000;// 1 minute 137 138 @Option(name = "xml-output", description = "Use gtest xml output for test results, " 139 + "if test binaries crash, no output will be available.") 140 private boolean mEnableXmlOutput = false; 141 142 @Option(name = "stop-runtime", 143 description = "Stops the Java application runtime before test execution.") 144 private boolean mStopRuntime = false; 145 146 @Option(name = "collect-tests-only", 147 description = "Only invoke the test binary to collect list of applicable test cases. " 148 + "All test run callbacks will be triggered, but test execution will " 149 + "not be actually carried out. This option ignores sharding parameters, so " 150 + "each shard will end up collecting all tests.") 151 private boolean mCollectTestsOnly = false; 152 153 @Option(name = "test-filter-key", 154 description = "run the gtest with the --gtest_filter populated with the filter from " 155 + "the json filter file associated with the binary, the filter file will have " 156 + "the same name as the binary with the .json extension.") 157 private String mTestFilterKey = null; 158 159 private int mShardCount = 0; 160 private int mShardIndex = 0; 161 private boolean mIsSharded = false; 162 163 /** coverage target value. Just report all gtests as 'native' for now */ 164 private static final String COVERAGE_TARGET = "Native"; 165 166 // GTest flags... 167 private static final String GTEST_FLAG_PRINT_TIME = "--gtest_print_time"; 168 private static final String GTEST_FLAG_FILTER = "--gtest_filter"; 169 private static final String GTEST_FLAG_RUN_DISABLED_TESTS = "--gtest_also_run_disabled_tests"; 170 private static final String GTEST_FLAG_LIST_TESTS = "--gtest_list_tests"; 171 private static final String GTEST_XML_OUTPUT = "--gtest_output=xml:%s"; 172 // Max characters allowed for executing GTest via command line 173 private static final int GTEST_CMD_CHAR_LIMIT = 1000; 174 // Expected extension for the filter file associated with the binary (json formatted file) 175 protected static final String FILTER_EXTENSION = ".filter"; 176 /** 177 * {@inheritDoc} 178 */ 179 @Override 180 public void setDevice(ITestDevice device) { 181 mDevice = device; 182 } 183 184 /** 185 * {@inheritDoc} 186 */ 187 @Override 188 public ITestDevice getDevice() { 189 return mDevice; 190 } 191 192 /** 193 * Set the Android native test module to run. 194 * 195 * @param moduleName The name of the native test module to run 196 */ 197 public void setModuleName(String moduleName) { 198 mTestModule = moduleName; 199 } 200 201 /** 202 * Get the Android native test module to run. 203 * 204 * @return the name of the native test module to run, or null if not set 205 */ 206 public String getModuleName() { 207 return mTestModule; 208 } 209 210 /** 211 * Set whether GTest should run disabled tests. 212 */ 213 public void setRunDisabled(boolean runDisabled) { 214 mRunDisabledTests = runDisabled; 215 } 216 217 /** 218 * Get whether GTest should run disabled tests. 219 * 220 * @return True if disabled tests should be run, false otherwise 221 */ 222 public boolean getRunDisabledTests() { 223 return mRunDisabledTests; 224 } 225 226 /** 227 * Set the max time in ms for a gtest to run. 228 */ 229 @VisibleForTesting 230 void setMaxTestTimeMs(int timeout) { 231 mMaxTestTimeMs = timeout; 232 } 233 234 /** 235 * Adds an exclusion file filter regex. 236 * 237 * @param regex to exclude file. 238 */ 239 @VisibleForTesting 240 void addFileExclusionFilterRegex(String regex) { 241 mFileExclusionFilterRegex.add(regex); 242 } 243 244 /** 245 * Sets the shard index of this test. 246 */ 247 @VisibleForTesting 248 void setShardIndex(int shardIndex) { 249 mShardIndex = shardIndex; 250 } 251 252 /** 253 * Gets the shard index of this test. 254 */ 255 @VisibleForTesting 256 int getShardIndex() { 257 return mShardIndex; 258 } 259 260 /** 261 * Sets the shard count of this test. 262 */ 263 @VisibleForTesting 264 void setShardCount(int shardCount) { 265 mShardCount = shardCount; 266 } 267 268 /** 269 * Sets the shard count of this test. 270 */ 271 @VisibleForTesting 272 int getShardCount() { 273 return mShardCount; 274 } 275 276 /** 277 * {@inheritDoc} 278 */ 279 @Override 280 public long getRuntimeHint() { 281 return mRuntimeHint; 282 } 283 284 /** 285 * {@inheritDoc} 286 */ 287 @Override 288 public void addIncludeFilter(String filter) { 289 mIncludeFilters.add(cleanFilter(filter)); 290 } 291 292 /** 293 * {@inheritDoc} 294 */ 295 @Override 296 public void addAllIncludeFilters(Set<String> filters) { 297 for (String filter : filters) { 298 mIncludeFilters.add(cleanFilter(filter)); 299 } 300 } 301 302 /** 303 * {@inheritDoc} 304 */ 305 @Override 306 public void addExcludeFilter(String filter) { 307 mExcludeFilters.add(cleanFilter(filter)); 308 } 309 310 /** 311 * {@inheritDoc} 312 */ 313 @Override 314 public void addAllExcludeFilters(Set<String> filters) { 315 for (String filter : filters) { 316 mExcludeFilters.add(cleanFilter(filter)); 317 } 318 } 319 320 /* 321 * Conforms filters using a {@link TestDescription} format to be recognized by the GTest 322 * executable. 323 */ 324 private String cleanFilter(String filter) { 325 return filter.replace('#', '.'); 326 } 327 328 /** 329 * Helper to get the adb gtest filter of test to run. 330 * 331 * Note that filters filter on the function name only (eg: Google Test "Test"); all Google Test 332 * "Test Cases" will be considered. 333 * 334 * @param binaryOnDevice the full path of the binary on the device. 335 * @return the full filter flag to pass to the Gtest, or an empty string if none have been 336 * specified 337 */ 338 private String getGTestFilters(String binaryOnDevice) throws DeviceNotAvailableException { 339 StringBuilder filter = new StringBuilder(); 340 if (mTestNamePositiveFilter != null) { 341 mIncludeFilters.add(mTestNamePositiveFilter); 342 } 343 if (mTestNameNegativeFilter != null) { 344 mExcludeFilters.add(mTestNameNegativeFilter); 345 } 346 if (mTestFilterKey != null) { 347 if (!mIncludeFilters.isEmpty() || !mExcludeFilters.isEmpty()) { 348 CLog.w("Using json file filter, --include/exclude-filter will be ignored."); 349 } 350 String fileFilters = loadFilter(binaryOnDevice); 351 if (fileFilters != null && !fileFilters.isEmpty()) { 352 filter.append(GTEST_FLAG_FILTER); 353 filter.append("="); 354 filter.append(fileFilters); 355 } 356 } else { 357 if (!mIncludeFilters.isEmpty() || !mExcludeFilters.isEmpty()) { 358 filter.append(GTEST_FLAG_FILTER); 359 filter.append("="); 360 if (!mIncludeFilters.isEmpty()) { 361 filter.append(ArrayUtil.join(":", mIncludeFilters)); 362 } 363 if (!mExcludeFilters.isEmpty()) { 364 filter.append("-"); 365 filter.append(ArrayUtil.join(":", mExcludeFilters)); 366 } 367 } 368 } 369 return filter.toString(); 370 } 371 372 private String loadFilter(String binaryOnDevice) throws DeviceNotAvailableException { 373 CLog.i("Loading filter from file for key: '%s'", mTestFilterKey); 374 String filterFile = String.format("%s%s", binaryOnDevice, FILTER_EXTENSION); 375 if (getDevice().doesFileExist(filterFile)) { 376 String content = 377 getDevice().executeShellCommand(String.format("cat \"%s\"", filterFile)); 378 if (content != null && !content.isEmpty()) { 379 try { 380 JSONObject filter = new JSONObject(content); 381 String key = mTestFilterKey; 382 JSONObject filterObject = filter.getJSONObject(key); 383 return filterObject.getString("filter"); 384 } catch (JSONException e) { 385 CLog.e(e); 386 } 387 } 388 CLog.e("Error with content of the filter file %s: %s", filterFile, content); 389 } else { 390 CLog.e("Filter file %s not found", filterFile); 391 } 392 return null; 393 } 394 395 /** 396 * Helper to get all the GTest flags to pass into the adb shell command. 397 * 398 * @param binaryOnDevice the full path of the binary on the device. 399 * @return the {@link String} of all the GTest flags that should be passed to the GTest 400 */ 401 private String getAllGTestFlags(String binaryOnDevice) throws DeviceNotAvailableException { 402 String flags = String.format("%s %s", GTEST_FLAG_PRINT_TIME, 403 getGTestFilters(binaryOnDevice)); 404 405 if (mRunDisabledTests) { 406 flags = String.format("%s %s", flags, GTEST_FLAG_RUN_DISABLED_TESTS); 407 } 408 409 if (mCollectTestsOnly) { 410 flags = String.format("%s %s", flags, GTEST_FLAG_LIST_TESTS); 411 } 412 413 for (String gTestFlag : mGTestFlags) { 414 flags = String.format("%s %s", flags, gTestFlag); 415 } 416 return flags; 417 } 418 419 /** 420 * Gets the path where native tests live on the device. 421 * 422 * @return The path on the device where the native tests live. 423 */ 424 private String getTestPath() { 425 StringBuilder testPath = new StringBuilder(mNativeTestDevicePath); 426 if (mTestModule != null) { 427 testPath.append(FileListingService.FILE_SEPARATOR); 428 testPath.append(mTestModule); 429 } 430 return testPath.toString(); 431 } 432 433 /** 434 * Executes all native tests in a folder as well as in all subfolders recursively. 435 * 436 * @param root The root folder to begin searching for native tests 437 * @param testDevice The device to run tests on 438 * @param listener the {@link ITestInvocationListener} 439 * @throws DeviceNotAvailableException 440 */ 441 @VisibleForTesting 442 void doRunAllTestsInSubdirectory( 443 String root, ITestDevice testDevice, ITestInvocationListener listener) 444 throws DeviceNotAvailableException { 445 if (testDevice.isDirectory(root)) { 446 // recursively run tests in all subdirectories 447 for (String child : testDevice.getChildren(root)) { 448 doRunAllTestsInSubdirectory(root + "/" + child, testDevice, listener); 449 } 450 } else { 451 // assume every file is a valid gtest binary. 452 IShellOutputReceiver resultParser = createResultParser(getFileName(root), listener); 453 if (shouldSkipFile(root)) { 454 return; 455 } 456 String flags = getAllGTestFlags(root); 457 CLog.i("Running gtest %s %s on %s", root, flags, testDevice.getSerialNumber()); 458 if (mEnableXmlOutput) { 459 runTestXml(testDevice, root, flags, listener); 460 } else { 461 runTest(testDevice, resultParser, root, flags); 462 } 463 } 464 } 465 466 String getFileName(String fullPath) { 467 int pos = fullPath.lastIndexOf('/'); 468 if (pos == -1) { 469 return fullPath; 470 } 471 String fileName = fullPath.substring(pos + 1); 472 if (fileName.isEmpty()) { 473 throw new IllegalArgumentException("input should not end with \"/\""); 474 } 475 return fileName; 476 } 477 478 protected boolean isDeviceFileExecutable(String fullPath) throws DeviceNotAvailableException { 479 String fileMode = mDevice.executeShellCommand(String.format("ls -l %s", fullPath)); 480 if (fileMode != null) { 481 return EXE_FILE.matcher(fileMode).find(); 482 } 483 return false; 484 } 485 486 /** 487 * Helper method to determine if we should skip the execution of a given file. 488 * 489 * @param fullPath the full path of the file in question 490 * @return true if we should skip the said file. 491 */ 492 protected boolean shouldSkipFile(String fullPath) throws DeviceNotAvailableException { 493 if (fullPath == null || fullPath.isEmpty()) { 494 return true; 495 } 496 // skip any file that's not executable 497 if (!isDeviceFileExecutable(fullPath)) { 498 return true; 499 } 500 if (mFileExclusionFilterRegex == null || mFileExclusionFilterRegex.isEmpty()) { 501 return false; 502 } 503 for (String regex : mFileExclusionFilterRegex) { 504 if (fullPath.matches(regex)) { 505 CLog.i("File %s matches exclusion file regex %s, skipping", fullPath, regex); 506 return true; 507 } 508 } 509 return false; 510 } 511 512 /** 513 * Helper method to run a gtest command from a temporary script, in the case that the command 514 * is too long to be run directly by adb. 515 * @param testDevice the device on which to run the command 516 * @param cmd the command string to run 517 * @param resultParser the output receiver for reading test results 518 */ 519 protected void executeCommandByScript(final ITestDevice testDevice, final String cmd, 520 final IShellOutputReceiver resultParser) throws DeviceNotAvailableException { 521 String tmpFileDevice = "/data/local/tmp/gtest_script.sh"; 522 testDevice.pushString(String.format("#!/bin/bash\n%s", cmd), tmpFileDevice); 523 // force file to be executable 524 testDevice.executeShellCommand(String.format("chmod 755 %s", tmpFileDevice)); 525 testDevice.executeShellCommand(String.format("sh %s", tmpFileDevice), 526 resultParser, mMaxTestTimeMs /* maxTimeToShellOutputResponse */, 527 TimeUnit.MILLISECONDS, 0 /* retry attempts */); 528 testDevice.executeShellCommand(String.format("rm %s", tmpFileDevice)); 529 } 530 531 /** 532 * Run the given gtest binary 533 * 534 * @param testDevice the {@link ITestDevice} 535 * @param resultParser the test run output parser 536 * @param fullPath absolute file system path to gtest binary on device 537 * @param flags gtest execution flags 538 * @throws DeviceNotAvailableException 539 */ 540 private void runTest(final ITestDevice testDevice, final IShellOutputReceiver resultParser, 541 final String fullPath, final String flags) throws DeviceNotAvailableException { 542 // TODO: add individual test timeout support, and rerun support 543 try { 544 for (String cmd : mBeforeTestCmd) { 545 testDevice.executeShellCommand(cmd); 546 } 547 548 if (mRebootBeforeTest) { 549 CLog.d("Rebooting device before test starts as requested."); 550 testDevice.reboot(); 551 } 552 553 String cmd = getGTestCmdLine(fullPath, flags); 554 // ensure that command is not too long for adb 555 if (cmd.length() < GTEST_CMD_CHAR_LIMIT) { 556 testDevice.executeShellCommand(cmd, resultParser, 557 mMaxTestTimeMs /* maxTimeToShellOutputResponse */, 558 TimeUnit.MILLISECONDS, 559 0 /* retryAttempts */); 560 } else { 561 // wrap adb shell command in script if command is too long for direct execution 562 executeCommandByScript(testDevice, cmd, resultParser); 563 } 564 } catch (DeviceNotAvailableException e) { 565 throw e; 566 } catch (RuntimeException e) { 567 throw e; 568 } finally { 569 // TODO: consider moving the flush of parser data on exceptions to TestDevice or 570 // AdbHelper 571 resultParser.flush(); 572 for (String cmd : mAfterTestCmd) { 573 testDevice.executeShellCommand(cmd); 574 } 575 } 576 } 577 578 /** 579 * Run the given gtest binary and parse XML results This methods typically requires the filter 580 * for .tff and .xml files, otherwise it will post some unwanted results. 581 * 582 * @param testDevice the {@link ITestDevice} 583 * @param fullPath absolute file system path to gtest binary on device 584 * @param flags gtest execution flags 585 * @param listener the {@link ITestInvocationListener} 586 * @throws DeviceNotAvailableException 587 */ 588 private void runTestXml( 589 final ITestDevice testDevice, 590 final String fullPath, 591 final String flags, 592 ITestInvocationListener listener) 593 throws DeviceNotAvailableException { 594 CollectingOutputReceiver outputCollector = new CollectingOutputReceiver(); 595 File tmpOutput = null; 596 try { 597 String testRunName = fullPath.substring(fullPath.lastIndexOf("/") + 1); 598 tmpOutput = FileUtil.createTempFile(testRunName, ".xml"); 599 String tmpResName = fullPath + "_res.xml"; 600 String extraFlag = String.format(GTEST_XML_OUTPUT, tmpResName); 601 String fullFlagCmd = String.format("%s %s", flags, extraFlag); 602 603 // Run the tests with modified flags 604 runTest(testDevice, outputCollector, fullPath, fullFlagCmd); 605 // Pull the result file, may not exist if issue with the test. 606 testDevice.pullFile(tmpResName, tmpOutput); 607 // Clean the file on the device 608 testDevice.executeShellCommand("rm " + tmpResName); 609 GTestXmlResultParser parser = createXmlParser(testRunName, listener); 610 // Attempt to parse the file, doesn't matter if the content is invalid. 611 if (tmpOutput.exists()) { 612 parser.parseResult(tmpOutput, outputCollector); 613 } 614 } catch (DeviceNotAvailableException | RuntimeException e) { 615 throw e; 616 } catch (IOException e) { 617 throw new RuntimeException(e); 618 } finally { 619 outputCollector.flush(); 620 for (String cmd : mAfterTestCmd) { 621 testDevice.executeShellCommand(cmd); 622 } 623 FileUtil.deleteFile(tmpOutput); 624 } 625 } 626 627 /** 628 * Exposed for testing 629 * 630 * @param testRunName 631 * @param listener 632 * @return a {@link GTestXmlResultParser} 633 */ 634 @VisibleForTesting 635 GTestXmlResultParser createXmlParser(String testRunName, ITestInvocationListener listener) { 636 return new GTestXmlResultParser(testRunName, listener); 637 } 638 639 /** 640 * Helper method to build the gtest command to run. 641 * 642 * @param fullPath absolute file system path to gtest binary on device 643 * @param flags gtest execution flags 644 * @return the shell command line to run for the gtest 645 */ 646 protected String getGTestCmdLine(String fullPath, String flags) { 647 StringBuilder gTestCmdLine = new StringBuilder(); 648 if (mLdLibraryPath != null) { 649 gTestCmdLine.append(String.format("LD_LIBRARY_PATH=%s ", mLdLibraryPath)); 650 } 651 if (mShardCount > 0) { 652 if (mCollectTestsOnly) { 653 CLog.w("--collect-tests-only option ignores sharding parameters, and will cause " 654 + "each shard to collect all tests."); 655 } 656 gTestCmdLine.append(String.format("GTEST_SHARD_INDEX=%s ", mShardIndex)); 657 gTestCmdLine.append(String.format("GTEST_TOTAL_SHARDS=%s ", mShardCount)); 658 } 659 660 // su to requested user 661 if (mRunTestAs != null) { 662 gTestCmdLine.append(String.format("su %s ", mRunTestAs)); 663 } 664 665 gTestCmdLine.append(String.format("%s %s", fullPath, flags)); 666 return gTestCmdLine.toString(); 667 } 668 669 /** 670 * {@inheritDoc} 671 */ 672 @Override 673 public IRemoteTest getTestShard(int shardCount, int shardIndex) { 674 GTest shard = new GTest(); 675 OptionCopier.copyOptionsNoThrow(this, shard); 676 shard.mShardIndex = shardIndex; 677 shard.mShardCount = shardCount; 678 shard.mIsSharded = true; 679 // We approximate the runtime of each shard to be equal since we can't know. 680 shard.mRuntimeHint = mRuntimeHint / shardCount; 681 return shard; 682 } 683 684 /** {@inheritDoc} */ 685 @Override 686 public Collection<IRemoteTest> split(int shardCountHint) { 687 if (shardCountHint <= 1 || mIsSharded) { 688 return null; 689 } 690 Collection<IRemoteTest> tests = new ArrayList<>(); 691 for (int i = 0; i < shardCountHint; i++) { 692 tests.add(getTestShard(shardCountHint, i)); 693 } 694 return tests; 695 } 696 697 /** 698 * Factory method for creating a {@link IShellOutputReceiver} that parses test output and 699 * forwards results to the result listener. 700 * 701 * @param listener 702 * @param runName 703 * @return a {@link IShellOutputReceiver} 704 */ 705 @VisibleForTesting 706 IShellOutputReceiver createResultParser(String runName, ITestInvocationListener listener) { 707 IShellOutputReceiver receiver = null; 708 if (mCollectTestsOnly) { 709 GTestListTestParser resultParser = new GTestListTestParser(runName, listener); 710 resultParser.setPrependFileName(mPrependFileName); 711 receiver = resultParser; 712 } else { 713 GTestResultParser resultParser = new GTestResultParser(runName, listener); 714 resultParser.setPrependFileName(mPrependFileName); 715 // TODO: find a better solution for sending coverage info 716 if (mSendCoverage) { 717 resultParser.setCoverageTarget(COVERAGE_TARGET); 718 } 719 receiver = resultParser; 720 } 721 return receiver; 722 } 723 724 /** 725 * {@inheritDoc} 726 */ 727 @Override 728 public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { 729 // TODO: add support for rerunning tests 730 if (mDevice == null) { 731 throw new IllegalArgumentException("Device has not been set"); 732 } 733 734 String testPath = getTestPath(); 735 if (!mDevice.doesFileExist(testPath)) { 736 CLog.w("Could not find native test directory %s in %s!", testPath, 737 mDevice.getSerialNumber()); 738 return; 739 } 740 if (mStopRuntime) { 741 mDevice.executeShellCommand("stop"); 742 } 743 Throwable throwable = null; 744 try { 745 doRunAllTestsInSubdirectory(testPath, mDevice, listener); 746 } catch (Throwable t) { 747 throwable = t; 748 throw t; 749 } finally { 750 if (!(throwable instanceof DeviceNotAvailableException)) { 751 if (mStopRuntime) { 752 mDevice.executeShellCommand("start"); 753 mDevice.waitForDeviceAvailable(); 754 } 755 } 756 } 757 } 758 759 /** 760 * {@inheritDoc} 761 */ 762 @Override 763 public void setCollectTestsOnly(boolean shouldCollectTest) { 764 mCollectTestsOnly = shouldCollectTest; 765 } 766 767 } 768