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 setDevice(ITestDevice device)180 public void setDevice(ITestDevice device) { 181 mDevice = device; 182 } 183 184 /** 185 * {@inheritDoc} 186 */ 187 @Override getDevice()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 */ setModuleName(String moduleName)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 */ getModuleName()206 public String getModuleName() { 207 return mTestModule; 208 } 209 210 /** 211 * Set whether GTest should run disabled tests. 212 */ setRunDisabled(boolean runDisabled)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 */ getRunDisabledTests()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 setMaxTestTimeMs(int timeout)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 addFileExclusionFilterRegex(String regex)240 void addFileExclusionFilterRegex(String regex) { 241 mFileExclusionFilterRegex.add(regex); 242 } 243 244 /** 245 * Sets the shard index of this test. 246 */ 247 @VisibleForTesting setShardIndex(int shardIndex)248 void setShardIndex(int shardIndex) { 249 mShardIndex = shardIndex; 250 } 251 252 /** 253 * Gets the shard index of this test. 254 */ 255 @VisibleForTesting getShardIndex()256 int getShardIndex() { 257 return mShardIndex; 258 } 259 260 /** 261 * Sets the shard count of this test. 262 */ 263 @VisibleForTesting setShardCount(int shardCount)264 void setShardCount(int shardCount) { 265 mShardCount = shardCount; 266 } 267 268 /** 269 * Sets the shard count of this test. 270 */ 271 @VisibleForTesting getShardCount()272 int getShardCount() { 273 return mShardCount; 274 } 275 276 /** 277 * {@inheritDoc} 278 */ 279 @Override getRuntimeHint()280 public long getRuntimeHint() { 281 return mRuntimeHint; 282 } 283 284 /** 285 * {@inheritDoc} 286 */ 287 @Override addIncludeFilter(String filter)288 public void addIncludeFilter(String filter) { 289 mIncludeFilters.add(cleanFilter(filter)); 290 } 291 292 /** 293 * {@inheritDoc} 294 */ 295 @Override addAllIncludeFilters(Set<String> filters)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 addExcludeFilter(String filter)306 public void addExcludeFilter(String filter) { 307 mExcludeFilters.add(cleanFilter(filter)); 308 } 309 310 /** 311 * {@inheritDoc} 312 */ 313 @Override addAllExcludeFilters(Set<String> filters)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 */ cleanFilter(String filter)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 */ getGTestFilters(String binaryOnDevice)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 loadFilter(String binaryOnDevice)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 */ getAllGTestFlags(String binaryOnDevice)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 */ getTestPath()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 doRunAllTestsInSubdirectory( String root, ITestDevice testDevice, ITestInvocationListener listener)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 getFileName(String fullPath)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 isDeviceFileExecutable(String fullPath)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 */ shouldSkipFile(String fullPath)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 */ executeCommandByScript(final ITestDevice testDevice, final String cmd, final IShellOutputReceiver resultParser)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 */ runTest(final ITestDevice testDevice, final IShellOutputReceiver resultParser, final String fullPath, final String flags)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 */ runTestXml( final ITestDevice testDevice, final String fullPath, final String flags, ITestInvocationListener listener)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 createXmlParser(String testRunName, ITestInvocationListener listener)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 */ getGTestCmdLine(String fullPath, String flags)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 getTestShard(int shardCount, int shardIndex)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 split(int shardCountHint)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 createResultParser(String runName, ITestInvocationListener listener)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 run(ITestInvocationListener listener)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 setCollectTestsOnly(boolean shouldCollectTest)763 public void setCollectTestsOnly(boolean shouldCollectTest) { 764 mCollectTestsOnly = shouldCollectTest; 765 } 766 767 } 768