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 package com.android.tradefed.testtype; 17 18 import com.android.tradefed.build.BuildRetrievalError; 19 import com.android.tradefed.build.IBuildInfo; 20 import com.android.tradefed.config.ConfigurationException; 21 import com.android.tradefed.config.DynamicRemoteFileResolver; 22 import com.android.tradefed.config.IConfiguration; 23 import com.android.tradefed.config.IConfigurationReceiver; 24 import com.android.tradefed.config.Option; 25 import com.android.tradefed.config.Option.Importance; 26 import com.android.tradefed.config.OptionClass; 27 import com.android.tradefed.config.OptionCopier; 28 import com.android.tradefed.config.OptionSetter; 29 import com.android.tradefed.device.DeviceNotAvailableException; 30 import com.android.tradefed.device.ITestDevice; 31 import com.android.tradefed.invoker.TestInformation; 32 import com.android.tradefed.log.LogUtil.CLog; 33 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric; 34 import com.android.tradefed.result.FailureDescription; 35 import com.android.tradefed.result.ITestInvocationListener; 36 import com.android.tradefed.result.ResultForwarder; 37 import com.android.tradefed.result.TestDescription; 38 import com.android.tradefed.result.proto.TestRecordProto.FailureStatus; 39 import com.android.tradefed.testtype.host.PrettyTestEventLogger; 40 import com.android.tradefed.testtype.junit4.CarryDnaeError; 41 import com.android.tradefed.testtype.junit4.JUnit4ResultForwarder; 42 import com.android.tradefed.testtype.suite.ModuleDefinition; 43 import com.android.tradefed.util.FileUtil; 44 import com.android.tradefed.util.JUnit4TestFilter; 45 import com.android.tradefed.util.StreamUtil; 46 import com.android.tradefed.util.TestFilterHelper; 47 48 import com.google.common.annotations.VisibleForTesting; 49 50 import junit.framework.Test; 51 import junit.framework.TestCase; 52 import junit.framework.TestSuite; 53 54 import org.junit.Ignore; 55 import org.junit.internal.runners.ErrorReportingRunner; 56 import org.junit.runner.Description; 57 import org.junit.runner.JUnitCore; 58 import org.junit.runner.Request; 59 import org.junit.runner.RunWith; 60 import org.junit.runner.Runner; 61 import org.junit.runner.notification.RunNotifier; 62 import org.junit.runners.Suite.SuiteClasses; 63 64 import java.io.File; 65 import java.io.FileNotFoundException; 66 import java.io.IOException; 67 import java.lang.annotation.Annotation; 68 import java.lang.reflect.AnnotatedElement; 69 import java.lang.reflect.InvocationTargetException; 70 import java.lang.reflect.Method; 71 import java.lang.reflect.Modifier; 72 import java.net.MalformedURLException; 73 import java.net.URL; 74 import java.net.URLClassLoader; 75 import java.util.ArrayDeque; 76 import java.util.ArrayList; 77 import java.util.Collection; 78 import java.util.Collections; 79 import java.util.Deque; 80 import java.util.Enumeration; 81 import java.util.HashMap; 82 import java.util.HashSet; 83 import java.util.LinkedHashSet; 84 import java.util.List; 85 import java.util.Set; 86 import java.util.jar.JarEntry; 87 import java.util.jar.JarFile; 88 import java.util.regex.Pattern; 89 90 /** 91 * A test runner for JUnit host based tests. If the test to be run implements {@link IDeviceTest} 92 * this runner will pass a reference to the device. 93 */ 94 @OptionClass(alias = "host") 95 public class HostTest 96 implements IDeviceTest, 97 ITestFilterReceiver, 98 ITestAnnotationFilterReceiver, 99 IRemoteTest, 100 ITestCollector, 101 IBuildReceiver, 102 IAbiReceiver, 103 IShardableTest, 104 IRuntimeHintProvider, 105 IConfigurationReceiver { 106 107 @Option(name = "class", description = "The JUnit test classes to run, in the format " 108 + "<package>.<class>. eg. \"com.android.foo.Bar\". This field can be repeated.", 109 importance = Importance.IF_UNSET) 110 private Set<String> mClasses = new LinkedHashSet<>(); 111 112 @Option(name = "method", description = "The name of the method in the JUnit TestCase to run. " 113 + "eg. \"testFooBar\"", 114 importance = Importance.IF_UNSET) 115 private String mMethodName; 116 117 @Option( 118 name = "jar", 119 description = "The jars containing the JUnit test class to run.", 120 importance = Importance.IF_UNSET 121 ) 122 private Set<String> mJars = new HashSet<>(); 123 124 public static final String SET_OPTION_NAME = "set-option"; 125 public static final String SET_OPTION_DESC = 126 "Options to be passed down to the class under test, key and value should be " 127 + "separated by colon \":\"; for example, if class under test supports " 128 + "\"--iteration 1\" from a command line, it should be passed in as" 129 + " \"--set-option iteration:1\" or \"--set-option iteration:key=value\" for " 130 + "passing options to map; escaping of \"=\" is currently not supported." 131 + "A particular class can be targetted by specifying it. " 132 + "\" --set-option <fully qualified class>:<option name>:<option value>\""; 133 134 @Option(name = SET_OPTION_NAME, description = SET_OPTION_DESC) 135 private List<String> mKeyValueOptions = new ArrayList<>(); 136 137 @Option(name = "include-annotation", 138 description = "The set of annotations a test must have to be run.") 139 private Set<String> mIncludeAnnotations = new HashSet<>(); 140 141 @Option(name = "exclude-annotation", 142 description = "The set of annotations to exclude tests from running. A test must have " 143 + "none of the annotations in this list to run.") 144 private Set<String> mExcludeAnnotations = new HashSet<>(); 145 146 @Option(name = "collect-tests-only", 147 description = "Only invoke the instrumentation to collect list of applicable test " 148 + "cases. All test run callbacks will be triggered, but test execution will " 149 + "not be actually carried out.") 150 private boolean mCollectTestsOnly = false; 151 152 @Option( 153 name = "runtime-hint", 154 isTimeVal = true, 155 description = "The hint about the test's runtime." 156 ) 157 private long mRuntimeHint = 60000; // 1 minute 158 159 enum ShardUnit { 160 CLASS, METHOD; 161 } 162 163 @Option(name = "shard-unit", 164 description = "Shard by class or method") 165 private ShardUnit mShardUnit = ShardUnit.CLASS; 166 167 @Option( 168 name = "enable-pretty-logs", 169 description = 170 "whether or not to enable a logging for each test start and end on both host and " 171 + "device side." 172 ) 173 private boolean mEnableHostDeviceLogs = true; 174 175 private IConfiguration mConfig; 176 private ITestDevice mDevice; 177 private IBuildInfo mBuildInfo; 178 private IAbi mAbi; 179 private TestInformation mTestInfo; 180 private TestFilterHelper mFilterHelper; 181 private boolean mSkipTestClassCheck = false; 182 183 private List<Object> mTestMethods; 184 private List<Class<?>> mLoadedClasses = new ArrayList<>(); 185 private List<URLClassLoader> mOpenClassLoaders = new ArrayList<>(); 186 187 // Initialized as -1 to indicate that this value needs to be recalculated 188 // when test count is requested. 189 private int mNumTestCases = -1; 190 191 private List<File> mJUnit4JarFiles = new ArrayList<>(); 192 193 private static final String EXCLUDE_NO_TEST_FAILURE = "org.junit.runner.manipulation.Filter"; 194 private static final String TEST_FULL_NAME_FORMAT = "%s#%s"; 195 196 /** Track the downloaded files. */ 197 private List<File> mDownloadedFiles = new ArrayList<>(); 198 HostTest()199 public HostTest() { 200 mFilterHelper = new TestFilterHelper(new ArrayList<String>(), new ArrayList<String>(), 201 mIncludeAnnotations, mExcludeAnnotations); 202 } 203 setTestInformation(TestInformation testInfo)204 public void setTestInformation(TestInformation testInfo) { 205 mTestInfo = testInfo; 206 } 207 208 @Override setConfiguration(IConfiguration configuration)209 public void setConfiguration(IConfiguration configuration) { 210 mConfig = configuration; 211 } 212 213 /** 214 * {@inheritDoc} 215 */ 216 @Override getDevice()217 public ITestDevice getDevice() { 218 return mDevice; 219 } 220 221 /** 222 * {@inheritDoc} 223 */ 224 @Override setDevice(ITestDevice device)225 public void setDevice(ITestDevice device) { 226 mDevice = device; 227 } 228 229 /** {@inheritDoc} */ 230 @Override getRuntimeHint()231 public long getRuntimeHint() { 232 return mRuntimeHint; 233 } 234 235 /** {@inheritDoc} */ 236 @Override setAbi(IAbi abi)237 public void setAbi(IAbi abi) { 238 mAbi = abi; 239 } 240 241 /** {@inheritDoc} */ 242 @Override getAbi()243 public IAbi getAbi() { 244 return mAbi; 245 } 246 247 /** 248 * {@inheritDoc} 249 */ 250 @Override setBuild(IBuildInfo buildInfo)251 public void setBuild(IBuildInfo buildInfo) { 252 mBuildInfo = buildInfo; 253 } 254 255 /** 256 * Get the build info received by HostTest. 257 * 258 * @return the {@link IBuildInfo} 259 */ getBuild()260 protected IBuildInfo getBuild() { 261 return mBuildInfo; 262 } 263 264 /** 265 * @return true if shard-unit is method; false otherwise 266 */ shardUnitIsMethod()267 private boolean shardUnitIsMethod() { 268 return ShardUnit.METHOD.equals(mShardUnit); 269 } 270 271 /** 272 * {@inheritDoc} 273 */ 274 @Override addIncludeFilter(String filter)275 public void addIncludeFilter(String filter) { 276 // If filters change, reset test count so we recompute it next time it's requested. 277 mNumTestCases = -1; 278 mFilterHelper.addIncludeFilter(filter); 279 } 280 281 /** 282 * {@inheritDoc} 283 */ 284 @Override addAllIncludeFilters(Set<String> filters)285 public void addAllIncludeFilters(Set<String> filters) { 286 mNumTestCases = -1; 287 mFilterHelper.addAllIncludeFilters(filters); 288 } 289 290 /** {@inheritDoc} */ 291 @Override clearIncludeFilters()292 public void clearIncludeFilters() { 293 mNumTestCases = -1; 294 mFilterHelper.clearIncludeFilters(); 295 } 296 297 /** 298 * {@inheritDoc} 299 */ 300 @Override addExcludeFilter(String filter)301 public void addExcludeFilter(String filter) { 302 mNumTestCases = -1; 303 mFilterHelper.addExcludeFilter(filter); 304 } 305 306 /** {@inheritDoc} */ 307 @Override getIncludeFilters()308 public Set<String> getIncludeFilters() { 309 return mFilterHelper.getIncludeFilters(); 310 } 311 312 /** {@inheritDoc} */ 313 @Override getExcludeFilters()314 public Set<String> getExcludeFilters() { 315 return mFilterHelper.getExcludeFilters(); 316 } 317 318 /** 319 * {@inheritDoc} 320 */ 321 @Override addAllExcludeFilters(Set<String> filters)322 public void addAllExcludeFilters(Set<String> filters) { 323 mNumTestCases = -1; 324 mFilterHelper.addAllExcludeFilters(filters); 325 } 326 327 /** {@inheritDoc} */ 328 @Override clearExcludeFilters()329 public void clearExcludeFilters() { 330 mNumTestCases = -1; 331 mFilterHelper.clearExcludeFilters(); 332 } 333 334 /** 335 * Return the number of test cases across all classes part of the tests 336 */ countTestCases()337 public int countTestCases() { 338 if (mTestMethods != null) { 339 return mTestMethods.size(); 340 } else if (mNumTestCases >= 0) { 341 return mNumTestCases; 342 } 343 // Ensure filters are set in the helper 344 mFilterHelper.addAllIncludeAnnotation(mIncludeAnnotations); 345 mFilterHelper.addAllExcludeAnnotation(mExcludeAnnotations); 346 347 int count = 0; 348 for (Class<?> classObj : getClasses()) { 349 if (IRemoteTest.class.isAssignableFrom(classObj) 350 || Test.class.isAssignableFrom(classObj)) { 351 TestSuite suite = collectTests(collectClasses(classObj)); 352 int suiteCount = suite.countTestCases(); 353 if (suiteCount == 0 354 && IRemoteTest.class.isAssignableFrom(classObj) 355 && !Test.class.isAssignableFrom(classObj)) { 356 // If it's a pure IRemoteTest we count the run() as one test. 357 count++; 358 } else { 359 count += suiteCount; 360 } 361 } else if (hasJUnit4Annotation(classObj)) { 362 Request req = Request.aClass(classObj); 363 req = req.filterWith(new JUnit4TestFilter(mFilterHelper, mJUnit4JarFiles)); 364 Runner checkRunner = req.getRunner(); 365 // If no tests are remaining after filtering, checkRunner is ErrorReportingRunner. 366 // testCount() for ErrorReportingRunner returns 1, skip this classObj in this case. 367 if (checkRunner instanceof ErrorReportingRunner) { 368 if (!EXCLUDE_NO_TEST_FAILURE.equals( 369 checkRunner.getDescription().getClassName())) { 370 // If after filtering we have remaining tests that are malformed, we still 371 // count them toward the total number of tests. (each malformed class will 372 // count as 1 in the testCount()). 373 count += checkRunner.testCount(); 374 } 375 } else { 376 count += checkRunner.testCount(); 377 } 378 } else { 379 count++; 380 } 381 } 382 return mNumTestCases = count; 383 } 384 385 /** 386 * Clear then set a class name to be run. 387 */ setClassName(String className)388 protected void setClassName(String className) { 389 mClasses.clear(); 390 mClasses.add(className); 391 } 392 393 @VisibleForTesting getClassNames()394 public Set<String> getClassNames() { 395 return mClasses; 396 } 397 setMethodName(String methodName)398 void setMethodName(String methodName) { 399 mMethodName = methodName; 400 } 401 402 /** 403 * {@inheritDoc} 404 */ 405 @Override addIncludeAnnotation(String annotation)406 public void addIncludeAnnotation(String annotation) { 407 mIncludeAnnotations.add(annotation); 408 mFilterHelper.addIncludeAnnotation(annotation); 409 } 410 411 /** 412 * {@inheritDoc} 413 */ 414 @Override addAllIncludeAnnotation(Set<String> annotations)415 public void addAllIncludeAnnotation(Set<String> annotations) { 416 mIncludeAnnotations.addAll(annotations); 417 mFilterHelper.addAllIncludeAnnotation(annotations); 418 } 419 420 /** 421 * {@inheritDoc} 422 */ 423 @Override addExcludeAnnotation(String notAnnotation)424 public void addExcludeAnnotation(String notAnnotation) { 425 mExcludeAnnotations.add(notAnnotation); 426 mFilterHelper.addExcludeAnnotation(notAnnotation); 427 } 428 429 /** 430 * {@inheritDoc} 431 */ 432 @Override addAllExcludeAnnotation(Set<String> notAnnotations)433 public void addAllExcludeAnnotation(Set<String> notAnnotations) { 434 mExcludeAnnotations.addAll(notAnnotations); 435 mFilterHelper.addAllExcludeAnnotation(notAnnotations); 436 } 437 438 /** {@inheritDoc} */ 439 @Override getIncludeAnnotations()440 public Set<String> getIncludeAnnotations() { 441 return mIncludeAnnotations; 442 } 443 444 /** {@inheritDoc} */ 445 @Override getExcludeAnnotations()446 public Set<String> getExcludeAnnotations() { 447 return mExcludeAnnotations; 448 } 449 450 /** {@inheritDoc} */ 451 @Override clearIncludeAnnotations()452 public void clearIncludeAnnotations() { 453 mIncludeAnnotations.clear(); 454 mFilterHelper.clearIncludeAnnotations(); 455 } 456 457 /** {@inheritDoc} */ 458 @Override clearExcludeAnnotations()459 public void clearExcludeAnnotations() { 460 mExcludeAnnotations.clear(); 461 mFilterHelper.clearExcludeAnnotations(); 462 } 463 464 /** 465 * Helper to set the information of an object based on some of its type. 466 */ setTestObjectInformation(Object testObj)467 private void setTestObjectInformation(Object testObj) { 468 if (testObj instanceof IBuildReceiver) { 469 if (mBuildInfo == null) { 470 throw new IllegalArgumentException("Missing build information"); 471 } 472 ((IBuildReceiver)testObj).setBuild(mBuildInfo); 473 } 474 if (testObj instanceof IDeviceTest) { 475 if (mDevice == null) { 476 throw new IllegalArgumentException("Missing device"); 477 } 478 ((IDeviceTest)testObj).setDevice(mDevice); 479 } 480 // We are more flexible about abi info since not always available. 481 if (testObj instanceof IAbiReceiver) { 482 ((IAbiReceiver)testObj).setAbi(mAbi); 483 } 484 if (testObj instanceof IInvocationContextReceiver) { 485 ((IInvocationContextReceiver) testObj).setInvocationContext(mTestInfo.getContext()); 486 } 487 if (testObj instanceof ITestInformationReceiver) { 488 ((ITestInformationReceiver) testObj).setTestInformation(mTestInfo); 489 } 490 // managed runner should have the same set-option to pass option too. 491 if (testObj instanceof ISetOptionReceiver) { 492 try { 493 OptionSetter setter = new OptionSetter(testObj); 494 for (String item : mKeyValueOptions) { 495 setter.setOptionValue(SET_OPTION_NAME, item); 496 } 497 } catch (ConfigurationException e) { 498 throw new RuntimeException(e); 499 } 500 } 501 } 502 503 /** {@inheritDoc} */ 504 @Override run(TestInformation testInfo, ITestInvocationListener listener)505 public void run(TestInformation testInfo, ITestInvocationListener listener) 506 throws DeviceNotAvailableException { 507 mTestInfo = testInfo; 508 // Ensure filters are set in the helper 509 mFilterHelper.addAllIncludeAnnotation(mIncludeAnnotations); 510 mFilterHelper.addAllExcludeAnnotation(mExcludeAnnotations); 511 512 try { 513 try { 514 List<Class<?>> classes = getClasses(); 515 if (!mSkipTestClassCheck) { 516 if (classes.isEmpty()) { 517 throw new IllegalArgumentException("No '--class' option was specified."); 518 } 519 } 520 if (mMethodName != null && classes.size() > 1) { 521 throw new IllegalArgumentException( 522 String.format( 523 "'--method' only supports one '--class' name. Multiple were " 524 + "given: '%s'", 525 classes)); 526 } 527 } catch (IllegalArgumentException e) { 528 listener.testRunStarted(this.getClass().getCanonicalName(), 0); 529 FailureDescription failureDescription = 530 FailureDescription.create(StreamUtil.getStackTrace(e)); 531 failureDescription.setFailureStatus(FailureStatus.TEST_FAILURE); 532 listener.testRunFailed(failureDescription); 533 listener.testRunEnded(0L, new HashMap<String, Metric>()); 534 throw e; 535 } 536 537 // Add a pretty logger to the events to mark clearly start/end of test cases. 538 if (mEnableHostDeviceLogs) { 539 PrettyTestEventLogger logger = new PrettyTestEventLogger(mTestInfo.getDevices()); 540 listener = new ResultForwarder(logger, listener); 541 } 542 if (mTestMethods != null) { 543 runTestCases(listener); 544 } else { 545 runTestClasses(listener); 546 } 547 } finally { 548 mLoadedClasses.clear(); 549 for (URLClassLoader cl : mOpenClassLoaders) { 550 StreamUtil.close(cl); 551 } 552 mOpenClassLoaders.clear(); 553 } 554 } 555 runTestClasses(ITestInvocationListener listener)556 private void runTestClasses(ITestInvocationListener listener) 557 throws DeviceNotAvailableException { 558 for (Class<?> classObj : getClasses()) { 559 if (IRemoteTest.class.isAssignableFrom(classObj)) { 560 IRemoteTest test = (IRemoteTest) loadObject(classObj); 561 applyFilters(classObj, test); 562 runRemoteTest(listener, test); 563 } else if (Test.class.isAssignableFrom(classObj)) { 564 TestSuite junitTest = collectTests(collectClasses(classObj)); 565 // Resolve dynamic files for the junit3 test objects 566 Enumeration<Test> allTest = junitTest.tests(); 567 while (allTest.hasMoreElements()) { 568 Test testObj = allTest.nextElement(); 569 mDownloadedFiles.addAll(resolveRemoteFileForObject(testObj)); 570 } 571 try { 572 runJUnit3Tests(listener, junitTest, classObj.getName()); 573 } finally { 574 for (File f : mDownloadedFiles) { 575 FileUtil.recursiveDelete(f); 576 } 577 } 578 } else if (hasJUnit4Annotation(classObj)) { 579 // Include the method name filtering 580 Set<String> includes = mFilterHelper.getIncludeFilters(); 581 if (mMethodName != null) { 582 includes.add(String.format(TEST_FULL_NAME_FORMAT, classObj.getName(), 583 mMethodName)); 584 } 585 // Running in a full JUnit4 manner, no downgrade to JUnit3 {@link Test} 586 Request req = Request.aClass(classObj); 587 Runner checkRunner = null; 588 try { 589 req = req.filterWith(new JUnit4TestFilter(mFilterHelper, mJUnit4JarFiles)); 590 checkRunner = req.getRunner(); 591 } catch (IllegalArgumentException e) { 592 listener.testRunStarted(classObj.getName(), 0); 593 FailureDescription failureDescription = 594 FailureDescription.create(StreamUtil.getStackTrace(e)); 595 failureDescription.setFailureStatus(FailureStatus.TEST_FAILURE); 596 listener.testRunFailed(failureDescription); 597 listener.testRunEnded(0L, new HashMap<String, Metric>()); 598 throw e; 599 } 600 runJUnit4Tests(listener, checkRunner, classObj.getName()); 601 } else { 602 throw new IllegalArgumentException( 603 String.format("%s is not a supported test", classObj.getName())); 604 } 605 } 606 } 607 runTestCases(ITestInvocationListener listener)608 private void runTestCases(ITestInvocationListener listener) throws DeviceNotAvailableException { 609 Set<String> skippedTests = new LinkedHashSet<>(); 610 for (Object obj : getTestMethods()) { 611 if (IRemoteTest.class.isInstance(obj)) { 612 IRemoteTest test = (IRemoteTest) obj; 613 runRemoteTest(listener, test); 614 } else if (TestSuite.class.isInstance(obj)) { 615 TestSuite junitTest = (TestSuite) obj; 616 if (!runJUnit3Tests(listener, junitTest, junitTest.getName())) { 617 skippedTests.add(junitTest.getName()); 618 } 619 } else if (Description.class.isInstance(obj)) { 620 // Running in a full JUnit4 manner, no downgrade to JUnit3 {@link Test} 621 Description desc = (Description) obj; 622 Request req = Request.aClass(desc.getTestClass()); 623 Runner checkRunner = req.filterWith(desc).getRunner(); 624 try { 625 runJUnit4Tests(listener, checkRunner, desc.getClassName()); 626 } catch (IllegalArgumentException e) { 627 listener.testRunStarted(desc.getClassName(), 0); 628 FailureDescription failureDescription = 629 FailureDescription.create(StreamUtil.getStackTrace(e)); 630 failureDescription.setFailureStatus(FailureStatus.TEST_FAILURE); 631 listener.testRunFailed(failureDescription); 632 listener.testRunEnded(0L, new HashMap<String, Metric>()); 633 throw e; 634 } 635 } else { 636 throw new IllegalArgumentException( 637 String.format("%s is not a supported test", obj)); 638 } 639 } 640 CLog.v("The following classes were skipped due to no test cases found: %s", skippedTests); 641 } 642 runRemoteTest(ITestInvocationListener listener, IRemoteTest test)643 private void runRemoteTest(ITestInvocationListener listener, IRemoteTest test) 644 throws DeviceNotAvailableException { 645 if (mCollectTestsOnly) { 646 // Collect only mode is propagated to the test. 647 if (test instanceof ITestCollector) { 648 ((ITestCollector) test).setCollectTestsOnly(true); 649 } else { 650 throw new IllegalArgumentException( 651 String.format( 652 "%s does not implement ITestCollector", test.getClass())); 653 } 654 } 655 test.run(mTestInfo, listener); 656 } 657 658 /** Returns True if some tests were executed, false otherwise. */ runJUnit3Tests( ITestInvocationListener listener, TestSuite junitTest, String className)659 private boolean runJUnit3Tests( 660 ITestInvocationListener listener, TestSuite junitTest, String className) 661 throws DeviceNotAvailableException { 662 if (mCollectTestsOnly) { 663 // Collect only mode, fake the junit test execution. 664 int testCount = junitTest.countTestCases(); 665 listener.testRunStarted(className, testCount); 666 HashMap<String, Metric> empty = new HashMap<>(); 667 for (int i = 0; i < testCount; i++) { 668 Test t = junitTest.testAt(i); 669 // Test does not have a getName method. 670 // using the toString format instead: <testName>(className) 671 String testName = t.toString().split("\\(")[0]; 672 TestDescription testId = new TestDescription(t.getClass().getName(), testName); 673 listener.testStarted(testId); 674 listener.testEnded(testId, empty); 675 } 676 HashMap<String, Metric> emptyMap = new HashMap<>(); 677 listener.testRunEnded(0, emptyMap); 678 if (testCount > 0) { 679 return true; 680 } else { 681 return false; 682 } 683 } else { 684 return JUnitRunUtil.runTest(listener, junitTest, className); 685 } 686 } 687 runJUnit4Tests( ITestInvocationListener listener, Runner checkRunner, String className)688 private void runJUnit4Tests( 689 ITestInvocationListener listener, Runner checkRunner, String className) 690 throws DeviceNotAvailableException { 691 JUnitCore runnerCore = new JUnitCore(); 692 JUnit4ResultForwarder list = new JUnit4ResultForwarder(listener); 693 runnerCore.addListener(list); 694 695 if (!(checkRunner instanceof ErrorReportingRunner)) { 696 // If no tests are remaining after filtering, it returns an Error Runner. 697 long startTime = System.currentTimeMillis(); 698 listener.testRunStarted(className, checkRunner.testCount()); 699 try { 700 if (mCollectTestsOnly) { 701 fakeDescriptionExecution(checkRunner.getDescription(), list); 702 } else { 703 setTestObjectInformation(checkRunner); 704 runnerCore.run(checkRunner); 705 } 706 } catch (CarryDnaeError e) { 707 throw e.getDeviceNotAvailableException(); 708 } finally { 709 for (Description d : findIgnoredClass(checkRunner.getDescription())) { 710 TestDescription testDescription = 711 new TestDescription(d.getClassName(), "No Tests"); 712 listener.testStarted(testDescription); 713 listener.testIgnored(testDescription); 714 listener.testEnded(testDescription, new HashMap<String, Metric>()); 715 } 716 listener.testRunEnded( 717 System.currentTimeMillis() - startTime, new HashMap<String, Metric>()); 718 } 719 } else { 720 // Special case where filtering leaves no tests to run, we report no failure 721 // in this case. 722 if (EXCLUDE_NO_TEST_FAILURE.equals( 723 checkRunner.getDescription().getClassName())) { 724 listener.testRunStarted(className, 0); 725 listener.testRunEnded(0, new HashMap<String, Metric>()); 726 } else { 727 // Run the Error runner to get the failures from test classes. 728 listener.testRunStarted(className, checkRunner.testCount()); 729 RunNotifier failureNotifier = new RunNotifier(); 730 failureNotifier.addListener(list); 731 checkRunner.run(failureNotifier); 732 listener.testRunEnded(0, new HashMap<String, Metric>()); 733 } 734 } 735 } 736 737 /** Search and return all the classes that are @Ignored */ findIgnoredClass(Description description)738 private List<Description> findIgnoredClass(Description description) { 739 List<Description> ignoredClass = new ArrayList<>(); 740 if (description.isSuite()) { 741 for (Description childDescription : description.getChildren()) { 742 ignoredClass.addAll(findIgnoredClass(childDescription)); 743 } 744 } else { 745 if (description.getMethodName() == null) { 746 for (Annotation a : description.getAnnotations()) { 747 if (a.annotationType() != null && a.annotationType().equals(Ignore.class)) { 748 ignoredClass.add(description); 749 break; 750 } 751 } 752 } 753 } 754 return ignoredClass; 755 } 756 757 /** 758 * Helper to fake the execution of JUnit4 Tests, using the {@link Description} 759 */ fakeDescriptionExecution(Description desc, JUnit4ResultForwarder listener)760 private void fakeDescriptionExecution(Description desc, JUnit4ResultForwarder listener) { 761 if (desc.getMethodName() == null || !desc.getChildren().isEmpty()) { 762 for (Description child : desc.getChildren()) { 763 fakeDescriptionExecution(child, listener); 764 } 765 } else { 766 try { 767 listener.testStarted(desc); 768 listener.testFinished(desc); 769 } catch (Exception e) { 770 // Should never happen 771 CLog.e(e); 772 } 773 } 774 } 775 collectClasses(Class<?> classObj)776 private Set<Class<?>> collectClasses(Class<?> classObj) { 777 Set<Class<?>> classes = new HashSet<>(); 778 if (TestSuite.class.isAssignableFrom(classObj)) { 779 TestSuite testObj = (TestSuite) loadObject(classObj); 780 classes.addAll(getClassesFromSuite(testObj)); 781 } else { 782 classes.add(classObj); 783 } 784 return classes; 785 } 786 getClassesFromSuite(TestSuite suite)787 private Set<Class<?>> getClassesFromSuite(TestSuite suite) { 788 Set<Class<?>> classes = new HashSet<>(); 789 Enumeration<Test> tests = suite.tests(); 790 while (tests.hasMoreElements()) { 791 Test test = tests.nextElement(); 792 if (test instanceof TestSuite) { 793 classes.addAll(getClassesFromSuite((TestSuite) test)); 794 } else { 795 classes.addAll(collectClasses(test.getClass())); 796 } 797 } 798 return classes; 799 } 800 collectTests(Set<Class<?>> classes)801 private TestSuite collectTests(Set<Class<?>> classes) { 802 TestSuite suite = new TestSuite(); 803 for (Class<?> classObj : classes) { 804 String packageName = classObj.getPackage().getName(); 805 String className = classObj.getName(); 806 Method[] methods = null; 807 if (mMethodName == null) { 808 methods = classObj.getMethods(); 809 } else { 810 try { 811 methods = new Method[] { 812 classObj.getMethod(mMethodName, (Class[]) null) 813 }; 814 } catch (NoSuchMethodException e) { 815 throw new IllegalArgumentException( 816 String.format("Cannot find %s#%s", className, mMethodName), e); 817 } 818 } 819 820 for (Method method : methods) { 821 if (!Modifier.isPublic(method.getModifiers()) 822 || !method.getReturnType().equals(Void.TYPE) 823 || method.getParameterTypes().length > 0 824 || !method.getName().startsWith("test") 825 || !mFilterHelper.shouldRun(packageName, classObj, method)) { 826 continue; 827 } 828 Test testObj = (Test) loadObject(classObj, false); 829 if (testObj instanceof TestCase) { 830 ((TestCase)testObj).setName(method.getName()); 831 } 832 suite.addTest(testObj); 833 } 834 } 835 return suite; 836 } 837 getTestMethods()838 private List<Object> getTestMethods() throws IllegalArgumentException { 839 if (mTestMethods != null) { 840 return mTestMethods; 841 } 842 mTestMethods = new ArrayList<>(); 843 mFilterHelper.addAllIncludeAnnotation(mIncludeAnnotations); 844 mFilterHelper.addAllExcludeAnnotation(mExcludeAnnotations); 845 List<Class<?>> classes = getClasses(); 846 for (Class<?> classObj : classes) { 847 if (Test.class.isAssignableFrom(classObj)) { 848 TestSuite suite = collectTests(collectClasses(classObj)); 849 for (int i = 0; i < suite.testCount(); i++) { 850 TestSuite singletonSuite = new TestSuite(); 851 singletonSuite.setName(classObj.getName()); 852 Test testObj = suite.testAt(i); 853 singletonSuite.addTest(testObj); 854 if (IRemoteTest.class.isInstance(testObj)) { 855 setTestObjectInformation(testObj); 856 } 857 mTestMethods.add(singletonSuite); 858 } 859 } else if (IRemoteTest.class.isAssignableFrom(classObj)) { 860 // a pure IRemoteTest is considered a test method itself 861 IRemoteTest test = (IRemoteTest) loadObject(classObj); 862 applyFilters(classObj, test); 863 mTestMethods.add(test); 864 } else if (hasJUnit4Annotation(classObj)) { 865 // Running in a full JUnit4 manner, no downgrade to JUnit3 {@link Test} 866 Request req = Request.aClass(classObj); 867 // Include the method name filtering 868 Set<String> includes = mFilterHelper.getIncludeFilters(); 869 if (mMethodName != null) { 870 includes.add(String.format(TEST_FULL_NAME_FORMAT, classObj.getName(), 871 mMethodName)); 872 } 873 874 req = req.filterWith(new JUnit4TestFilter(mFilterHelper, mJUnit4JarFiles)); 875 Runner checkRunner = req.getRunner(); 876 Deque<Description> descriptions = new ArrayDeque<>(); 877 descriptions.push(checkRunner.getDescription()); 878 while (!descriptions.isEmpty()) { 879 Description desc = descriptions.pop(); 880 if (desc.isTest()) { 881 mTestMethods.add(desc); 882 } 883 List<Description> children = desc.getChildren(); 884 Collections.reverse(children); 885 for (Description child : children) { 886 descriptions.push(child); 887 } 888 } 889 } else { 890 throw new IllegalArgumentException( 891 String.format("%s is not a supported test", classObj.getName())); 892 } 893 } 894 return mTestMethods; 895 } 896 getClasses()897 protected final List<Class<?>> getClasses() throws IllegalArgumentException { 898 if (!mLoadedClasses.isEmpty()) { 899 return mLoadedClasses; 900 } 901 // Use a set to avoid repeat between filters and jar search 902 Set<String> classNames = new HashSet<>(); 903 List<Class<?>> classes = mLoadedClasses; 904 for (String className : mClasses) { 905 if (classNames.contains(className)) { 906 continue; 907 } 908 IllegalArgumentException initialError = null; 909 try { 910 classes.add(Class.forName(className, true, getClassLoader())); 911 classNames.add(className); 912 } catch (ClassNotFoundException e) { 913 initialError = 914 new IllegalArgumentException( 915 String.format("Could not load Test class %s", className), e); 916 } 917 if (initialError != null) { 918 // Fallback search a jar for the module under tests if any. 919 String moduleName = 920 mTestInfo 921 .getContext() 922 .getAttributes() 923 .getUniqueMap() 924 .get(ModuleDefinition.MODULE_NAME); 925 if (moduleName != null) { 926 URLClassLoader cl = null; 927 try { 928 File f = getJarFile(moduleName + ".jar", mTestInfo); 929 URL[] urls = {f.toURI().toURL()}; 930 cl = URLClassLoader.newInstance(urls); 931 mJUnit4JarFiles.add(f); 932 Class<?> cls = cl.loadClass(className); 933 classes.add(cls); 934 classNames.add(className); 935 initialError = null; 936 mOpenClassLoaders.add(cl); 937 } catch (FileNotFoundException 938 | MalformedURLException 939 | ClassNotFoundException fallbackSearch) { 940 StreamUtil.close(cl); 941 CLog.e( 942 "Fallback search for a jar containing '%s' didn't work." 943 + "Consider using --jar option directly instead of using --class", 944 className); 945 } 946 } 947 } 948 if (initialError != null) { 949 throw initialError; 950 } 951 } 952 URLClassLoader cl = null; 953 // Inspect for the jar files 954 for (String jarName : mJars) { 955 JarFile jarFile = null; 956 try { 957 File file = getJarFile(jarName, mTestInfo); 958 jarFile = new JarFile(file); 959 Enumeration<JarEntry> e = jarFile.entries(); 960 URL[] urls = {file.toURI().toURL()}; 961 cl = URLClassLoader.newInstance(urls); 962 mJUnit4JarFiles.add(file); 963 mOpenClassLoaders.add(cl); 964 965 while (e.hasMoreElements()) { 966 JarEntry je = e.nextElement(); 967 if (je.isDirectory() 968 || !je.getName().endsWith(".class") 969 || je.getName().contains("$")) { 970 continue; 971 } 972 String className = getClassName(je.getName()); 973 if (classNames.contains(className)) { 974 continue; 975 } 976 try { 977 Class<?> cls = cl.loadClass(className); 978 int modifiers = cls.getModifiers(); 979 if ((IRemoteTest.class.isAssignableFrom(cls) 980 || Test.class.isAssignableFrom(cls) 981 || hasJUnit4Annotation(cls)) 982 && !Modifier.isStatic(modifiers) 983 && !Modifier.isPrivate(modifiers) 984 && !Modifier.isProtected(modifiers) 985 && !Modifier.isInterface(modifiers) 986 && !Modifier.isAbstract(modifiers)) { 987 classes.add(cls); 988 classNames.add(className); 989 } 990 } catch (UnsupportedClassVersionError ucve) { 991 throw new IllegalArgumentException( 992 String.format( 993 "Could not load class %s from jar %s. Reason:\n%s", 994 className, jarName, StreamUtil.getStackTrace(ucve))); 995 } catch (ClassNotFoundException cnfe) { 996 throw new IllegalArgumentException( 997 String.format("Cannot find test class %s", className)); 998 } catch (IllegalAccessError | NoClassDefFoundError err) { 999 // IllegalAccessError can happen when the class or one of its super 1000 // class/interfaces are package-private. We can't load such class from 1001 // here (= outside of the package). Since our intention is not to load 1002 // all classes in the jar, but to find our the main test classes, this 1003 // can be safely skipped. 1004 // NoClassDefFoundErrror is also okay because certain CTS test cases 1005 // might statically link to a jar library (e.g. tools.jar from JDK) 1006 // where certain internal classes in the library are referencing 1007 // classes that are not available in the jar. Again, since our goal here 1008 // is to find test classes, this can be safely skipped. 1009 continue; 1010 } 1011 } 1012 } catch (IOException e) { 1013 CLog.e(e); 1014 throw new IllegalArgumentException(e); 1015 } finally { 1016 StreamUtil.close(jarFile); 1017 } 1018 } 1019 return classes; 1020 } 1021 1022 /** Returns the default classloader. */ 1023 @VisibleForTesting getClassLoader()1024 protected ClassLoader getClassLoader() { 1025 return this.getClass().getClassLoader(); 1026 } 1027 1028 /** load the class object and set the test info (device, build). */ loadObject(Class<?> classObj)1029 protected Object loadObject(Class<?> classObj) { 1030 return loadObject(classObj, true); 1031 } 1032 1033 /** 1034 * Load the class object and set the test info if requested. 1035 * 1036 * @param classObj the class object to be loaded. 1037 * @param setInfo True the test infos need to be set. 1038 * @return The loaded object from the class. 1039 */ loadObject(Class<?> classObj, boolean setInfo)1040 private Object loadObject(Class<?> classObj, boolean setInfo) throws IllegalArgumentException { 1041 final String className = classObj.getName(); 1042 try { 1043 Object testObj = classObj.getDeclaredConstructor().newInstance(); 1044 // set options 1045 setOptionToLoadedObject(testObj, mKeyValueOptions); 1046 // Set the test information if needed. 1047 if (setInfo) { 1048 setTestObjectInformation(testObj); 1049 } 1050 return testObj; 1051 } catch (InstantiationException e) { 1052 throw new IllegalArgumentException(String.format("Could not load Test class %s", 1053 className), e); 1054 } catch (IllegalAccessException e) { 1055 throw new IllegalArgumentException(String.format("Could not load Test class %s", 1056 className), e); 1057 } catch (InvocationTargetException | NoSuchMethodException e) { 1058 throw new IllegalArgumentException( 1059 String.format("Could not load Test class %s", className), e); 1060 } 1061 } 1062 1063 /** 1064 * Helper for Device Runners to use to set options the same way as HostTest, from set-option. 1065 * 1066 * @param testObj the object that will receive the options. 1067 * @param keyValueOptions the list of options formatted as HostTest set-option requires. 1068 */ setOptionToLoadedObject(Object testObj, List<String> keyValueOptions)1069 public static void setOptionToLoadedObject(Object testObj, List<String> keyValueOptions) { 1070 if (!keyValueOptions.isEmpty()) { 1071 OptionSetter setter; 1072 try { 1073 setter = new OptionSetter(testObj); 1074 } catch (ConfigurationException ce) { 1075 CLog.e(ce); 1076 throw new RuntimeException("error creating option setter", ce); 1077 } 1078 for (String item : keyValueOptions) { 1079 // Support escaping ':' using lookbehind in the regex. The regex engine will 1080 // step backwards to check for the escape char when it matches the delim char. 1081 // If it doesn't find the escape char, then a match is registered. 1082 String delim = ":"; 1083 String esc = "\\"; 1084 String regex = "(?<!" + Pattern.quote(esc) + ")" + Pattern.quote(delim); 1085 String[] fields = item.split(regex); 1086 String key, value; 1087 if (fields.length == 3) { 1088 String target = fields[0]; 1089 if (testObj.getClass().getName().equals(target)) { 1090 key = fields[1]; 1091 value = 1092 fields[2].replaceAll( 1093 Pattern.quote(esc) + Pattern.quote(delim), delim); 1094 } else { 1095 // TODO: We should track that all targeted option end up assigned 1096 // eventually. 1097 CLog.d( 1098 "Targeted option %s is not applicable to %s", 1099 item, testObj.getClass().getName()); 1100 continue; 1101 } 1102 } else if (fields.length == 2) { 1103 key = fields[0]; 1104 value = fields[1].replaceAll(Pattern.quote(esc) + Pattern.quote(delim), delim); 1105 } else { 1106 throw new RuntimeException(String.format("invalid option spec \"%s\"", item)); 1107 } 1108 try { 1109 injectOption(setter, item, key, value); 1110 } catch (ConfigurationException ce) { 1111 CLog.e(ce); 1112 throw new RuntimeException( 1113 "error passing option '" 1114 + item 1115 + "' down to test class as key=" 1116 + key 1117 + " value=" 1118 + value, 1119 ce); 1120 } 1121 } 1122 } 1123 } 1124 injectOption(OptionSetter setter, String origItem, String key, String value)1125 private static void injectOption(OptionSetter setter, String origItem, String key, String value) 1126 throws ConfigurationException { 1127 if (value.contains("=")) { 1128 String[] values = value.split("="); 1129 if (values.length != 2) { 1130 throw new RuntimeException( 1131 String.format( 1132 "set-option provided '%s' format is invalid. Only one " 1133 + "'=' is allowed", 1134 origItem)); 1135 } 1136 setter.setOptionValue(key, values[0], values[1]); 1137 } else { 1138 setter.setOptionValue(key, value); 1139 } 1140 } 1141 1142 /** 1143 * Check if an elements that has annotation pass the filter. Exposed for unit testing. 1144 * @param annotatedElement 1145 * @return false if the test should not run. 1146 */ shouldTestRun(AnnotatedElement annotatedElement)1147 protected boolean shouldTestRun(AnnotatedElement annotatedElement) { 1148 return mFilterHelper.shouldTestRun(annotatedElement); 1149 } 1150 1151 /** 1152 * {@inheritDoc} 1153 */ 1154 @Override setCollectTestsOnly(boolean shouldCollectTest)1155 public void setCollectTestsOnly(boolean shouldCollectTest) { 1156 mCollectTestsOnly = shouldCollectTest; 1157 } 1158 1159 /** 1160 * Helper to determine if we are dealing with a Test class with Junit4 annotations. 1161 */ hasJUnit4Annotation(Class<?> classObj)1162 protected boolean hasJUnit4Annotation(Class<?> classObj) { 1163 if (classObj.isAnnotationPresent(SuiteClasses.class)) { 1164 return true; 1165 } 1166 if (classObj.isAnnotationPresent(RunWith.class)) { 1167 return true; 1168 } 1169 for (Method m : classObj.getMethods()) { 1170 if (m.isAnnotationPresent(org.junit.Test.class)) { 1171 return true; 1172 } 1173 } 1174 return false; 1175 } 1176 1177 /** 1178 * Helper method to apply all the filters to an IRemoteTest. 1179 */ applyFilters(Class<?> classObj, IRemoteTest test)1180 private void applyFilters(Class<?> classObj, IRemoteTest test) { 1181 Set<String> includes = mFilterHelper.getIncludeFilters(); 1182 if (mMethodName != null) { 1183 includes.add(String.format(TEST_FULL_NAME_FORMAT, classObj.getName(), mMethodName)); 1184 } 1185 Set<String> excludes = mFilterHelper.getExcludeFilters(); 1186 if (test instanceof ITestFilterReceiver) { 1187 ((ITestFilterReceiver) test).addAllIncludeFilters(includes); 1188 ((ITestFilterReceiver) test).addAllExcludeFilters(excludes); 1189 } else if (!includes.isEmpty() || !excludes.isEmpty()) { 1190 throw new IllegalArgumentException(String.format( 1191 "%s does not implement ITestFilterReceiver", classObj.getName())); 1192 } 1193 if (test instanceof ITestAnnotationFilterReceiver) { 1194 ((ITestAnnotationFilterReceiver) test).addAllIncludeAnnotation( 1195 mIncludeAnnotations); 1196 ((ITestAnnotationFilterReceiver) test).addAllExcludeAnnotation( 1197 mExcludeAnnotations); 1198 } 1199 } 1200 1201 /** We split by individual by either test class or method. */ 1202 @Override split(Integer shardCount, TestInformation testInfo)1203 public Collection<IRemoteTest> split(Integer shardCount, TestInformation testInfo) { 1204 if (shardCount == null) { 1205 return null; 1206 } 1207 if (shardCount < 1) { 1208 throw new IllegalArgumentException("Must have at least 1 shard"); 1209 } 1210 mTestInfo = testInfo; 1211 List<IRemoteTest> listTests = new ArrayList<>(); 1212 try { 1213 List<Class<?>> classes = getClasses(); 1214 if (classes.isEmpty()) { 1215 throw new IllegalArgumentException("Missing Test class name"); 1216 } 1217 if (mMethodName != null && classes.size() > 1) { 1218 throw new IllegalArgumentException("Method name given with multiple test classes"); 1219 } 1220 List<? extends Object> testObjects; 1221 if (shardUnitIsMethod()) { 1222 testObjects = getTestMethods(); 1223 } else { 1224 testObjects = classes; 1225 // ignore shardCount when shard unit is class; 1226 // simply shard by the number of classes 1227 shardCount = testObjects.size(); 1228 } 1229 if (testObjects.size() == 1) { 1230 return null; 1231 } 1232 int i = 0; 1233 int numTotalTestCases = countTestCases(); 1234 for (Object testObj : testObjects) { 1235 Class<?> classObj = Class.class.isInstance(testObj) ? (Class<?>) testObj : null; 1236 HostTest test; 1237 if (i >= listTests.size()) { 1238 test = createHostTest(classObj); 1239 test.mRuntimeHint = 0; 1240 // Carry over non-annotation filters to shards. 1241 test.addAllExcludeFilters(mFilterHelper.getExcludeFilters()); 1242 test.addAllIncludeFilters(mFilterHelper.getIncludeFilters()); 1243 listTests.add(test); 1244 } 1245 test = (HostTest) listTests.get(i); 1246 Collection<? extends Object> subTests; 1247 if (classObj != null) { 1248 test.addClassName(classObj.getName()); 1249 subTests = test.mClasses; 1250 } else { 1251 test.addTestMethod(testObj); 1252 subTests = test.mTestMethods; 1253 } 1254 if (numTotalTestCases == 0) { 1255 // In case there is no tests left 1256 test.mRuntimeHint = 0L; 1257 } else { 1258 test.mRuntimeHint = mRuntimeHint * subTests.size() / numTotalTestCases; 1259 } 1260 i = (i + 1) % shardCount; 1261 } 1262 } finally { 1263 mLoadedClasses.clear(); 1264 for (URLClassLoader cl : mOpenClassLoaders) { 1265 StreamUtil.close(cl); 1266 } 1267 mOpenClassLoaders.clear(); 1268 } 1269 return listTests; 1270 } 1271 addTestMethod(Object testObject)1272 private void addTestMethod(Object testObject) { 1273 if (mTestMethods == null) { 1274 mTestMethods = new ArrayList<>(); 1275 mClasses.clear(); 1276 } 1277 mTestMethods.add(testObject); 1278 if (IRemoteTest.class.isInstance(testObject)) { 1279 addClassName(testObject.getClass().getName()); 1280 } else if (TestSuite.class.isInstance(testObject)) { 1281 addClassName(((TestSuite)testObject).getName()); 1282 } else if (Description.class.isInstance(testObject)) { 1283 addClassName(((Description)testObject).getTestClass().getName()); 1284 } 1285 } 1286 1287 /** 1288 * Add a class to be ran by HostTest. 1289 */ addClassName(String className)1290 private void addClassName(String className) { 1291 mClasses.add(className); 1292 } 1293 1294 /** 1295 * Helper to create a HostTest instance when sharding. Override to return any child from 1296 * HostTest. 1297 */ createHostTest(Class<?> classObj)1298 protected HostTest createHostTest(Class<?> classObj) { 1299 HostTest test; 1300 try { 1301 test = this.getClass().getDeclaredConstructor().newInstance(); 1302 } catch (InstantiationException 1303 | IllegalAccessException 1304 | InvocationTargetException 1305 | NoSuchMethodException e) { 1306 throw new RuntimeException(e); 1307 } 1308 OptionCopier.copyOptionsNoThrow(this, test); 1309 if (classObj != null) { 1310 test.setClassName(classObj.getName()); 1311 } 1312 // clean the jar option since we are loading directly from classes after. 1313 test.mJars = new HashSet<>(); 1314 // Copy the abi if available 1315 test.setAbi(mAbi); 1316 return test; 1317 } 1318 getClassName(String name)1319 private String getClassName(String name) { 1320 // -6 because of .class 1321 return name.substring(0, name.length() - 6).replace('/', '.'); 1322 } 1323 1324 /** 1325 * Inspect several location where the artifact are usually located for different use cases to 1326 * find our jar. 1327 */ 1328 @VisibleForTesting getJarFile(String jarName, TestInformation testInfo)1329 protected File getJarFile(String jarName, TestInformation testInfo) 1330 throws FileNotFoundException { 1331 return testInfo.getDependencyFile(jarName, /* target first*/ false); 1332 } 1333 1334 @VisibleForTesting createResolver()1335 DynamicRemoteFileResolver createResolver() { 1336 DynamicRemoteFileResolver resolver = new DynamicRemoteFileResolver(); 1337 resolver.setDevice(mDevice); 1338 resolver.addExtraArgs(mConfig.getCommandOptions().getDynamicDownloadArgs()); 1339 return resolver; 1340 } 1341 resolveRemoteFileForObject(Object obj)1342 private Set<File> resolveRemoteFileForObject(Object obj) { 1343 try { 1344 OptionSetter setter = new OptionSetter(obj); 1345 return setter.validateRemoteFilePath(createResolver()); 1346 } catch (BuildRetrievalError | ConfigurationException e) { 1347 throw new RuntimeException(e); 1348 } 1349 } 1350 } 1351