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