1 /* 2 * Copyright (C) 2018 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.IShellOutputReceiver; 20 import com.android.tradefed.config.Configuration; 21 import com.android.tradefed.config.IConfiguration; 22 import com.android.tradefed.config.IConfigurationReceiver; 23 import com.android.tradefed.config.Option; 24 import com.android.tradefed.config.OptionCopier; 25 import com.android.tradefed.device.DeviceNotAvailableException; 26 import com.android.tradefed.log.LogUtil.CLog; 27 import com.android.tradefed.result.ITestInvocationListener; 28 import com.android.tradefed.result.ResultForwarder; 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 java.io.File; 35 import java.io.IOException; 36 import java.lang.reflect.InvocationTargetException; 37 import java.time.Duration; 38 import java.util.ArrayList; 39 import java.util.Collection; 40 import java.util.LinkedHashSet; 41 import java.util.List; 42 import java.util.Set; 43 import java.util.concurrent.TimeUnit; 44 45 /** The base class of gTest */ 46 public abstract class GTestBase 47 implements IRemoteTest, 48 IConfigurationReceiver, 49 ITestFilterReceiver, 50 IRuntimeHintProvider, 51 ITestCollector, 52 IShardableTest, 53 IAbiReceiver { 54 55 private static final List<String> DEFAULT_FILE_EXCLUDE_FILTERS = new ArrayList<>(); 56 57 static { 58 // Exclude .so by default as they are not runnable. 59 DEFAULT_FILE_EXCLUDE_FILTERS.add(".*\\.so"); 60 61 // Exclude configs in case permission are wrong 62 DEFAULT_FILE_EXCLUDE_FILTERS.add(".*\\.config"); 63 } 64 65 @Option(name = "run-disable-tests", description = "Determine to run disable tests or not.") 66 private boolean mRunDisabledTests = false; 67 68 @Option( 69 name = "collect-disable-tests", 70 description = "Determine to collect disable tests or not.") 71 private boolean mCollectDisabledTests = false; 72 73 @Option(name = "module-name", description = "The name of the native test module to run.") 74 private String mTestModule = null; 75 76 @Option( 77 name = "file-exclusion-filter-regex", 78 description = "Regex to exclude certain files from executing. Can be repeated") 79 private Set<String> mFileExclusionFilterRegex = 80 new LinkedHashSet<>(DEFAULT_FILE_EXCLUDE_FILTERS); 81 82 @Option( 83 name = "positive-testname-filter", 84 description = "The GTest-based positive filter of the test name to run.") 85 private String mTestNamePositiveFilter = null; 86 87 @Option( 88 name = "negative-testname-filter", 89 description = "The GTest-based negative filter of the test name to run.") 90 private String mTestNameNegativeFilter = null; 91 92 @Option( 93 name = "include-filter", 94 description = "The GTest-based positive filter of the test names to run." 95 ) 96 private Set<String> mIncludeFilters = new LinkedHashSet<>(); 97 98 /** GTest-based positive filters that are added during retry attempts. */ 99 private Set<String> mRetryIncludeFilters = new LinkedHashSet<>(); 100 101 @Option( 102 name = "exclude-filter", 103 description = "The GTest-based negative filter of the test names to run." 104 ) 105 private Set<String> mExcludeFilters = new LinkedHashSet<>(); 106 107 /** GTest-based negative filters that are added during retry attempts. */ 108 private Set<String> mRetryExcludeFilters = new LinkedHashSet<>(); 109 110 @Option( 111 name = "native-test-timeout", 112 description = 113 "The max time for a gtest to run. Test run will be aborted if any test " 114 + "takes longer.", 115 isTimeVal = true) 116 private long mMaxTestTimeMs = 1 * 60 * 1000L; 117 118 /** @deprecated use --coverage in CoverageOptions instead. */ 119 @Deprecated 120 @Option( 121 name = "coverage", 122 description = 123 "Collect code coverage for this test run. Note that the build under test must be a " 124 + "coverage build or else this will fail." 125 ) 126 private boolean mCoverage = false; 127 128 @Option( 129 name = "prepend-filename", 130 description = "Prepend filename as part of the classname for the tests.") 131 private boolean mPrependFileName = false; 132 133 @Option(name = "before-test-cmd", description = "adb shell command(s) to run before GTest.") 134 private List<String> mBeforeTestCmd = new ArrayList<>(); 135 136 @Option(name = "after-test-cmd", description = "adb shell command(s) to run after GTest.") 137 private List<String> mAfterTestCmd = new ArrayList<>(); 138 139 @Option(name = "run-test-as", description = "User to execute test binary as.") 140 private String mRunTestAs = null; 141 142 @Option( 143 name = "ld-library-path", 144 description = "LD_LIBRARY_PATH value to include in the GTest execution command.") 145 private String mLdLibraryPath = null; 146 147 @Option( 148 name = "ld-library-path-32", 149 description = 150 "LD_LIBRARY_PATH value to include in the GTest execution command " 151 + "for 32-bit tests. If both `--ld-library-path` and " 152 + "`--ld-library-path-32` are set, only the latter is honored " 153 + "for 32-bit tests.") 154 private String mLdLibraryPath32 = null; 155 156 @Option( 157 name = "ld-library-path-64", 158 description = 159 "LD_LIBRARY_PATH value to include in the GTest execution command " 160 + "for 64-bit tests. If both `--ld-library-path` and " 161 + "`--ld-library-path-64` are set, only the latter is honored " 162 + "for 64-bit tests.") 163 private String mLdLibraryPath64 = null; 164 165 @Option( 166 name = "gtest-env", 167 description = 168 "Environment variable to set before executing test. " 169 + "Format is VARIABLE=VALUE. Can be repeated") 170 private List<String> mEnvironmentVars = new ArrayList<>(); 171 172 @Option( 173 name = "native-test-flag", 174 description = 175 "Additional flag values to pass to the native test's shell command. Flags" 176 + " should be complete, including any necessary dashes: \"--flag=value\"") 177 private List<String> mGTestFlags = new ArrayList<>(); 178 179 @Option( 180 name = "runtime-hint", 181 description = "The hint about the test's runtime.", 182 isTimeVal = true) 183 private long mRuntimeHint = 60000; // 1 minute 184 185 @Option( 186 name = "xml-output", 187 description = 188 "Use gtest xml output for test results, " 189 + "if test binaries crash, no output will be available.") 190 private boolean mEnableXmlOutput = false; 191 192 @Option( 193 name = "collect-tests-only", 194 description = 195 "Only invoke the test binary to collect list of applicable test cases. All" 196 + " test run callbacks will be triggered, but test execution will not be" 197 + " actually carried out. This option ignores sharding parameters, so each" 198 + " shard will end up collecting all tests.") 199 private boolean mCollectTestsOnly = false; 200 201 @Option( 202 name = "test-filter-key", 203 description = 204 "run the gtest with the --gtest_filter populated with the filter from the json" 205 + " filter file associated with the binary, the filter file will have the" 206 + " same name as the binary with the .json extension.") 207 private String mTestFilterKey = null; 208 209 @Option( 210 name = "disable-duplicate-test-check", 211 description = "If set to true, it will not check that a method is only run once.") 212 private boolean mDisableDuplicateCheck = false; 213 214 @Option( 215 name = TestTimeoutEnforcer.TEST_CASE_TIMEOUT_OPTION, 216 description = TestTimeoutEnforcer.TEST_CASE_TIMEOUT_DESCRIPTION) 217 private Duration mTestCaseTimeout = Duration.ofSeconds(0L); 218 219 @Option( 220 name = "change-to-working-directory", 221 description = 222 "Change to the working directory of the test binary before executing " 223 + "the test to allow relative references to data files to be " 224 + "resolved.") 225 private boolean mChangeToWorkingDirectory = false; 226 227 @Option( 228 name = "use-gunit-namings", 229 description = "Use the internal gunit version name of flags.") 230 private boolean mSwitchToGunitNamings = false; 231 232 // GTest flags... 233 protected static final String GTEST_FLAG_PRINT_TIME = "--gtest_print_time"; 234 protected static final String GTEST_FLAG_FILTER = "--gtest_filter"; 235 protected static final String GTEST_FLAG_RUN_DISABLED_TESTS = "--gtest_also_run_disabled_tests"; 236 protected static final String GTEST_FLAG_LIST_TESTS = "--gtest_list_tests"; 237 protected static final String GTEST_FLAG_FILE = "--gtest_flagfile"; 238 protected static final String GTEST_XML_OUTPUT = "--gtest_output=xml:%s"; 239 // Expected extension for the filter file associated with the binary (json formatted file) 240 @VisibleForTesting protected static final String FILTER_EXTENSION = ".filter"; 241 242 private int mShardCount = 0; 243 private int mShardIndex = 0; 244 private boolean mIsSharded = false; 245 246 private IConfiguration mConfiguration = null; 247 private IAbi mAbi; 248 249 /** Track if test is executed or not. If executed, subsequent runs will be retry attempts. */ 250 private boolean mTestExecutedBefore = false; 251 252 /** Whether the prev test run was complete or not. */ 253 private boolean mPrevTestRunCompleted = false; 254 255 /** Track failed tests from the previous run. */ 256 private Set<String> mPrevFailedTests = new LinkedHashSet<>(); 257 258 @Override setAbi(IAbi abi)259 public void setAbi(IAbi abi) { 260 mAbi = abi; 261 } 262 263 @Override getAbi()264 public IAbi getAbi() { 265 return mAbi; 266 } 267 268 /** {@inheritDoc} */ 269 @Override setConfiguration(IConfiguration configuration)270 public void setConfiguration(IConfiguration configuration) { 271 mConfiguration = configuration; 272 } 273 274 /** 275 * Set the Android native test module to run. 276 * 277 * @param moduleName The name of the native test module to run 278 */ setModuleName(String moduleName)279 public void setModuleName(String moduleName) { 280 mTestModule = moduleName; 281 } 282 283 /** 284 * Get the Android native test module to run. 285 * 286 * @return the name of the native test module to run, or null if not set 287 */ getModuleName()288 public String getModuleName() { 289 return mTestModule; 290 } 291 292 /** Set whether GTest should run disabled tests. */ setRunDisabled(boolean runDisabled)293 protected void setRunDisabled(boolean runDisabled) { 294 mRunDisabledTests = runDisabled; 295 } 296 297 /** 298 * Get whether GTest should run disabled tests. 299 * 300 * @return True if disabled tests should be run, false otherwise 301 */ getRunDisabledTests()302 public boolean getRunDisabledTests() { 303 return mRunDisabledTests; 304 } 305 306 /** Set the max time in ms for a gtest to run. */ 307 @VisibleForTesting setMaxTestTimeMs(int timeout)308 void setMaxTestTimeMs(int timeout) { 309 mMaxTestTimeMs = timeout; 310 } 311 312 /** 313 * Adds an exclusion file filter regex. 314 * 315 * @param regex to exclude file. 316 */ 317 @VisibleForTesting addFileExclusionFilterRegex(String regex)318 void addFileExclusionFilterRegex(String regex) { 319 mFileExclusionFilterRegex.add(regex); 320 } 321 322 /** Sets the shard index of this test. */ setShardIndex(int shardIndex)323 public void setShardIndex(int shardIndex) { 324 mShardIndex = shardIndex; 325 } 326 327 /** Gets the shard index of this test. */ getShardIndex()328 public int getShardIndex() { 329 return mShardIndex; 330 } 331 332 /** Sets the shard count of this test. */ setShardCount(int shardCount)333 public void setShardCount(int shardCount) { 334 mShardCount = shardCount; 335 } 336 337 /** Returns the current shard-count. */ getShardCount()338 public int getShardCount() { 339 return mShardCount; 340 } 341 342 /** {@inheritDoc} */ 343 @Override getRuntimeHint()344 public long getRuntimeHint() { 345 return mRuntimeHint; 346 } 347 348 /** {@inheritDoc} */ 349 @Override addIncludeFilter(String filter)350 public void addIncludeFilter(String filter) { 351 if (mShardCount > 0) { 352 // If we explicitly start giving filters to GTest, reset the shard-count. GTest first 353 // applies filters then GTEST_TOTAL_SHARDS so it will probably end up not running 354 // anything 355 mShardCount = 0; 356 } 357 mIncludeFilters.add(cleanFilter(filter)); 358 if (mTestExecutedBefore) { 359 // if test is already executed, newly added filters are intended for retry attempts. so 360 // track them. 361 mRetryIncludeFilters.add(cleanFilter(filter)); 362 } 363 } 364 365 /** {@inheritDoc} */ 366 @Override addAllIncludeFilters(Set<String> filters)367 public void addAllIncludeFilters(Set<String> filters) { 368 for (String filter : filters) { 369 addIncludeFilter(cleanFilter(filter)); 370 } 371 } 372 373 /** {@inheritDoc} */ 374 @Override addExcludeFilter(String filter)375 public void addExcludeFilter(String filter) { 376 mExcludeFilters.add(cleanFilter(filter)); 377 if (mTestExecutedBefore) { 378 // if test is already executed, newly added filters are intended for retry attempts. so 379 // track them. 380 mRetryExcludeFilters.add(cleanFilter(filter)); 381 } 382 } 383 384 /** {@inheritDoc} */ 385 @Override addAllExcludeFilters(Set<String> filters)386 public void addAllExcludeFilters(Set<String> filters) { 387 for (String filter : filters) { 388 addExcludeFilter(cleanFilter(filter)); 389 } 390 } 391 392 /** {@inheritDoc} */ 393 @Override clearIncludeFilters()394 public void clearIncludeFilters() { 395 mIncludeFilters.clear(); 396 // Clear the filter file key, to not impact the base filters. 397 mTestFilterKey = null; 398 } 399 400 /** {@inheritDoc} */ 401 @Override getIncludeFilters()402 public Set<String> getIncludeFilters() { 403 return mIncludeFilters; 404 } 405 406 /** {@inheritDoc} */ 407 @Override getExcludeFilters()408 public Set<String> getExcludeFilters() { 409 return mExcludeFilters; 410 } 411 412 /** {@inheritDoc} */ 413 @Override clearExcludeFilters()414 public void clearExcludeFilters() { 415 mExcludeFilters.clear(); 416 } 417 418 /** Gets module name. */ getTestModule()419 public String getTestModule() { 420 return mTestModule; 421 } 422 423 /** Gets regex to exclude certain files from executing. */ getFileExclusionFilterRegex()424 public Set<String> getFileExclusionFilterRegex() { 425 return mFileExclusionFilterRegex; 426 } 427 428 /** Gets the max time for a gtest to run. */ getMaxTestTimeMs()429 public long getMaxTestTimeMs() { 430 return mMaxTestTimeMs; 431 } 432 433 /** Gets shell command(s) to run before GTest. */ getBeforeTestCmd()434 public List<String> getBeforeTestCmd() { 435 return mBeforeTestCmd; 436 } 437 438 /** Gets shell command(s) to run after GTest. */ getAfterTestCmd()439 public List<String> getAfterTestCmd() { 440 return mAfterTestCmd; 441 } 442 443 /** Gets Additional flag values to pass to the native test's shell command. */ getGTestFlags()444 public List<String> getGTestFlags() { 445 return mGTestFlags; 446 } 447 448 /** Gets test filter key. */ getTestFilterKey()449 public String getTestFilterKey() { 450 return mTestFilterKey; 451 } 452 453 /** Gets use gtest xml output for test results or not. */ isEnableXmlOutput()454 public boolean isEnableXmlOutput() { 455 return mEnableXmlOutput; 456 } 457 458 /** Gets only invoke the test binary to collect list of applicable test cases or not. */ isCollectTestsOnly()459 public boolean isCollectTestsOnly() { 460 return mCollectTestsOnly; 461 } 462 463 /** Gets isSharded flag. */ isSharded()464 public boolean isSharded() { 465 return mIsSharded; 466 } 467 468 /** 469 * Define get filter method. 470 * 471 * <p>Sub class must implement how to get it's own filter. 472 * 473 * @param path the full path of the filter file. 474 * @return filter string. 475 */ loadFilter(String path)476 protected abstract String loadFilter(String path) throws DeviceNotAvailableException; 477 478 /** 479 * Helper to get the g-test filter of test to run. 480 * 481 * <p>Note that filters filter on the function name only (eg: Google Test "Test"); all Google 482 * Test "Test Cases" will be considered. 483 * 484 * @param path the full path of the binary on the device. 485 * @return the full filter flag to pass to the g-test, or an empty string if none have been 486 * specified 487 */ getGTestFilters(String path)488 protected String getGTestFilters(String path) throws DeviceNotAvailableException { 489 StringBuilder filter = new StringBuilder(); 490 if (isShardRetry()) { 491 CLog.d("Using updated logic for retry attempt when sharding is involved."); 492 // During retry attempts with sharding, apply new partial or full retry logic. 493 if (usePartialRetry()) { 494 CLog.d("Using partial retry logic since previous run was complete."); 495 // if prev test run was complete and we know a list of test failures from prev run, 496 // do partial retry by explicitly including failed tests 497 clearIncludeFilters(); 498 clearExcludeFilters(); 499 mTestNamePositiveFilter = null; 500 mTestNameNegativeFilter = null; 501 // Remove non-retriable failed tests from prev-failed tests list. non-retriable 502 // failed tests are provided as exclusion filters during retry. 503 for (String retryExcludeFilter : mRetryExcludeFilters) { 504 if (mPrevFailedTests.contains(retryExcludeFilter)) { 505 mPrevFailedTests.remove(retryExcludeFilter); 506 } 507 } 508 // Use the includeFilter method to turnoff sharding during partial retry. 509 addAllIncludeFilters(mPrevFailedTests); 510 // clear retryFilters list for next retry attempt 511 mRetryIncludeFilters.clear(); 512 mRetryExcludeFilters.clear(); 513 } else { 514 CLog.d("Using full retry logic since previous run was incomplete"); 515 // if prev test run was incomplete when sharding is involved, do full retry 516 // by ignoring newly added filters 517 // Don't use the include/excludeFilter methods to avoid stopping sharding. 518 for (String excludeFilter : mRetryExcludeFilters) { 519 mExcludeFilters.remove(excludeFilter); 520 } 521 for (String includeFilter : mRetryIncludeFilters) { 522 mIncludeFilters.remove(includeFilter); 523 } 524 525 // clear retryFilters list for next retry attempt 526 mRetryIncludeFilters.clear(); 527 mRetryExcludeFilters.clear(); 528 } 529 } else { 530 if (mTestNamePositiveFilter != null) { 531 mIncludeFilters.add(mTestNamePositiveFilter); 532 } 533 if (mTestNameNegativeFilter != null) { 534 mExcludeFilters.add(mTestNameNegativeFilter); 535 } 536 if (mTestFilterKey != null) { 537 String fileFilters = loadFilter(path); 538 if (fileFilters != null && !fileFilters.isEmpty()) { 539 if (fileFilters.startsWith("-")) { 540 for (String filterString : fileFilters.substring(1).split(":")) { 541 mExcludeFilters.add(filterString); 542 } 543 } else { 544 String[] filterStrings = fileFilters.split("-"); 545 for (String filterString : filterStrings[0].split(":")) { 546 mIncludeFilters.add(filterString); 547 } 548 if (filterStrings.length == 2) { 549 for (String filterString : filterStrings[1].split(":")) { 550 mExcludeFilters.add(filterString); 551 } 552 } 553 } 554 } 555 } 556 } 557 558 if (!mIncludeFilters.isEmpty() || !mExcludeFilters.isEmpty()) { 559 filter.append(GTEST_FLAG_FILTER); 560 filter.append("="); 561 if (!mIncludeFilters.isEmpty()) { 562 filter.append(ArrayUtil.join(":", mIncludeFilters)); 563 } 564 if (!mExcludeFilters.isEmpty()) { 565 filter.append("-"); 566 filter.append(ArrayUtil.join(":", mExcludeFilters)); 567 } 568 } 569 570 String filterFlag = filter.toString(); 571 // Handle long args 572 if (filterFlag.length() > 500) { 573 String tmpFlag = createFlagFile(filterFlag); 574 if (tmpFlag != null) { 575 return String.format("%s=%s", convertName(GTEST_FLAG_FILE), tmpFlag); 576 } 577 } 578 579 return filterFlag; 580 } 581 582 /** 583 * Create a file containing the filters that will be used via --gtest_flagfile to avoid any OS 584 * limitation in args size. 585 * 586 * @param filter The filter string 587 * @return The path to the file containing the filter. 588 * @throws DeviceNotAvailableException 589 */ createFlagFile(String filter)590 protected String createFlagFile(String filter) throws DeviceNotAvailableException { 591 File tmpFlagFile = null; 592 try { 593 tmpFlagFile = FileUtil.createTempFile("flagfile", ".txt"); 594 FileUtil.writeToFile(filter, tmpFlagFile); 595 } catch (IOException e) { 596 FileUtil.deleteFile(tmpFlagFile); 597 CLog.e(e); 598 return null; 599 } 600 return tmpFlagFile.getAbsolutePath(); 601 } 602 603 /** 604 * Helper to get all the GTest flags to pass into the adb shell command. 605 * 606 * @param path the full path of the binary on the device. 607 * @return the {@link String} of all the GTest flags that should be passed to the GTest 608 */ getAllGTestFlags(String path)609 protected String getAllGTestFlags(String path) throws DeviceNotAvailableException { 610 String flags = 611 String.format("%s %s", convertName(GTEST_FLAG_PRINT_TIME), getGTestFilters(path)); 612 613 if (getRunDisabledTests()) { 614 flags = String.format("%s %s", flags, convertName(GTEST_FLAG_RUN_DISABLED_TESTS)); 615 } 616 617 if (isCollectTestsOnly()) { 618 flags = String.format("%s %s", flags, convertName(GTEST_FLAG_LIST_TESTS)); 619 } 620 621 for (String gTestFlag : getGTestFlags()) { 622 flags = String.format("%s %s", flags, gTestFlag); 623 } 624 return flags; 625 } 626 627 /* 628 * Conforms filters using a {@link TestDescription} format to be recognized by the GTest 629 * executable. 630 */ cleanFilter(String filter)631 public String cleanFilter(String filter) { 632 return filter.replace('#', '.'); 633 } 634 635 /** 636 * Exposed for testing 637 * 638 * @param testRunName 639 * @param listener 640 * @return a {@link GTestXmlResultParser} 641 */ 642 @VisibleForTesting createXmlParser(String testRunName, ITestInvocationListener listener)643 GTestXmlResultParser createXmlParser(String testRunName, ITestInvocationListener listener) { 644 return new GTestXmlResultParser(testRunName, listener); 645 } 646 647 /** 648 * Factory method for creating a {@link IShellOutputReceiver} that parses test output and 649 * forwards results to the result listener. 650 * 651 * @param listener 652 * @param runName 653 * @return a {@link IShellOutputReceiver} 654 */ 655 @VisibleForTesting createResultParser(String runName, ITestInvocationListener listener)656 IShellOutputReceiver createResultParser(String runName, ITestInvocationListener listener) { 657 IShellOutputReceiver receiver = null; 658 if (mCollectTestsOnly) { 659 GTestListTestParser resultParser = 660 new GTestListTestParser(runName, listener, mCollectDisabledTests); 661 resultParser.setPrependFileName(mPrependFileName); 662 receiver = resultParser; 663 } else { 664 GTestResultParser resultParser = new GTestResultParser(runName, listener); 665 resultParser.setPrependFileName(mPrependFileName); 666 receiver = resultParser; 667 } 668 // Erase the prepended binary name if needed 669 erasePrependedFileName(mExcludeFilters, runName); 670 erasePrependedFileName(mIncludeFilters, runName); 671 return receiver; 672 } 673 674 /** 675 * Helper which allows derived classes to wrap the gtest command under some other tool (chroot, 676 * strace, gdb, and similar). 677 */ getGTestCmdLineWrapper(String fullPath, String flags)678 protected String getGTestCmdLineWrapper(String fullPath, String flags) { 679 if (mChangeToWorkingDirectory) { 680 File f = new File(fullPath); 681 String dir = f.getParent(); 682 if (dir != null) { 683 String file = f.getName(); 684 return String.format("cd %s; ./%s %s", dir, file, flags); 685 } 686 } 687 return String.format("%s %s", fullPath, flags); 688 } 689 690 /** 691 * Helper method to build the gtest command to run. 692 * 693 * @param fullPath absolute file system path to gtest binary on device 694 * @param flags gtest execution flags 695 * @return the shell command line to run for the gtest 696 */ getGTestCmdLine(String fullPath, String flags)697 protected String getGTestCmdLine(String fullPath, String flags) { 698 StringBuilder gTestCmdLine = new StringBuilder(); 699 if (mLdLibraryPath32 != null && "32".equals(getAbi().getBitness())) { 700 gTestCmdLine.append(String.format("LD_LIBRARY_PATH=%s ", mLdLibraryPath32)); 701 } else if (mLdLibraryPath64 != null && "64".equals(getAbi().getBitness())) { 702 gTestCmdLine.append(String.format("LD_LIBRARY_PATH=%s ", mLdLibraryPath64)); 703 } else if (mLdLibraryPath != null) { 704 gTestCmdLine.append(String.format("LD_LIBRARY_PATH=%s ", mLdLibraryPath)); 705 } 706 707 for (String environmentVar : mEnvironmentVars) { 708 gTestCmdLine.append(environmentVar + " "); 709 } 710 711 // su to requested user 712 if (mRunTestAs != null) { 713 gTestCmdLine.append(String.format("su %s ", mRunTestAs)); 714 } 715 716 gTestCmdLine.append(getGTestCmdLineWrapper(fullPath, flags)); 717 return gTestCmdLine.toString(); 718 } 719 720 /** {@inheritDoc} */ 721 @Override setCollectTestsOnly(boolean shouldCollectTest)722 public void setCollectTestsOnly(boolean shouldCollectTest) { 723 mCollectTestsOnly = shouldCollectTest; 724 } 725 726 /** {@inheritDoc} */ 727 @Override split(int shardCountHint)728 public Collection<IRemoteTest> split(int shardCountHint) { 729 if (shardCountHint <= 1 || mIsSharded) { 730 return null; 731 } 732 if (mCollectTestsOnly) { 733 // GTest cannot shard and use collect tests only, so prevent sharding in this case. 734 return null; 735 } 736 Collection<IRemoteTest> tests = new ArrayList<>(); 737 for (int i = 0; i < shardCountHint; i++) { 738 tests.add(getTestShard(shardCountHint, i)); 739 } 740 return tests; 741 } 742 743 /** 744 * Make a best effort attempt to retrieve a meaningful short descriptive message for given 745 * {@link Exception} 746 * 747 * @param e the {@link Exception} 748 * @return a short message 749 */ getExceptionMessage(Exception e)750 protected String getExceptionMessage(Exception e) { 751 StringBuilder msgBuilder = new StringBuilder(); 752 if (e.getMessage() != null) { 753 msgBuilder.append(e.getMessage()); 754 } 755 if (e.getCause() != null) { 756 msgBuilder.append(" cause:"); 757 msgBuilder.append(e.getCause().getClass().getSimpleName()); 758 if (e.getCause().getMessage() != null) { 759 msgBuilder.append(" ("); 760 msgBuilder.append(e.getCause().getMessage()); 761 msgBuilder.append(")"); 762 } 763 } 764 return msgBuilder.toString(); 765 } 766 erasePrependedFileName(Set<String> filters, String filename)767 protected void erasePrependedFileName(Set<String> filters, String filename) { 768 if (!mPrependFileName) { 769 return; 770 } 771 Set<String> copy = new LinkedHashSet<>(); 772 for (String filter : filters) { 773 if (filter.startsWith(filename + ".")) { 774 copy.add(filter.substring(filename.length() + 1)); 775 } else { 776 copy.add(filter); 777 } 778 } 779 filters.clear(); 780 filters.addAll(copy); 781 } 782 getTestShard(int shardCount, int shardIndex)783 private IRemoteTest getTestShard(int shardCount, int shardIndex) { 784 GTestBase shard = null; 785 try { 786 shard = this.getClass().getDeclaredConstructor().newInstance(); 787 OptionCopier.copyOptionsNoThrow(this, shard); 788 shard.mShardIndex = shardIndex; 789 shard.mShardCount = shardCount; 790 shard.mIsSharded = true; 791 // We approximate the runtime of each shard to be equal since we can't know. 792 shard.mRuntimeHint = mRuntimeHint / shardCount; 793 shard.mAbi = mAbi; 794 } catch (InstantiationException 795 | IllegalAccessException 796 | InvocationTargetException 797 | NoSuchMethodException e) { 798 // This cannot happen because the class was already created once at that point. 799 throw new RuntimeException( 800 String.format( 801 "%s (%s) when attempting to create shard object", 802 e.getClass().getSimpleName(), getExceptionMessage(e))); 803 } 804 return shard; 805 } 806 807 /** 808 * Returns the test configuration. 809 * 810 * @return an IConfiguration 811 */ getConfiguration()812 protected IConfiguration getConfiguration() { 813 if (mConfiguration == null) { 814 return new Configuration("", ""); 815 } 816 return mConfiguration; 817 } 818 819 /** 820 * Returns the {@link GTestListener} that provides extra debugging info, like detects and 821 * reports duplicate tests if mDisabledDuplicateCheck is false. Otherwise, returns the passed-in 822 * listener. 823 */ getGTestListener(ITestInvocationListener... listeners)824 protected ITestInvocationListener getGTestListener(ITestInvocationListener... listeners) { 825 ITestInvocationListener listener = null; 826 if (mTestCaseTimeout.toMillis() > 0L) { 827 listener = 828 new TestTimeoutEnforcer( 829 mTestCaseTimeout.toMillis(), TimeUnit.MILLISECONDS, listeners); 830 } else { 831 listener = new ResultForwarder(listeners); 832 } 833 if (mDisableDuplicateCheck) { 834 return listener; 835 } 836 837 return new GTestListener(listener); 838 } 839 840 /** 841 * Notify parent of test execution, so that inclusion/exclusion filters can be handled properly 842 * for the retry attempts. 843 */ notifyTestExecution(boolean incompleteTestFound, Set<String> failedTests)844 public void notifyTestExecution(boolean incompleteTestFound, Set<String> failedTests) { 845 mTestExecutedBefore = true; 846 mPrevTestRunCompleted = !incompleteTestFound; 847 mPrevFailedTests = failedTests; 848 } 849 850 /** Whether the current run is a retry attempt with sharding involved. */ isShardRetry()851 private boolean isShardRetry() { 852 return mTestExecutedBefore && (mShardCount > 0); 853 } 854 855 /** 856 * Whether the current retry attempt should be a partial retry or full retry. 857 * 858 * @return true, if partial retry. false, if full retry. 859 */ usePartialRetry()860 private boolean usePartialRetry() { 861 return mTestExecutedBefore 862 && mPrevTestRunCompleted 863 && mPrevFailedTests != null 864 && !mPrevFailedTests.isEmpty(); 865 } 866 convertName(String gtestFlagName)867 protected String convertName(String gtestFlagName) { 868 if (mSwitchToGunitNamings) { 869 return gtestFlagName.replaceAll("--gtest", "--gunit"); 870 } 871 return gtestFlagName; 872 } 873 } 874