1 /* 2 * Copyright (C) 2016 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.suite; 17 18 import com.android.ddmlib.Log.LogLevel; 19 import com.android.ddmlib.testrunner.TestIdentifier; 20 import com.android.ddmlib.testrunner.TestResult; 21 import com.android.ddmlib.testrunner.TestRunResult; 22 import com.android.tradefed.build.IBuildInfo; 23 import com.android.tradefed.config.ConfigurationDescriptor; 24 import com.android.tradefed.device.DeviceNotAvailableException; 25 import com.android.tradefed.device.DeviceUnresponsiveException; 26 import com.android.tradefed.device.ITestDevice; 27 import com.android.tradefed.invoker.IInvocationContext; 28 import com.android.tradefed.invoker.InvocationContext; 29 import com.android.tradefed.log.ILogRegistry.EventType; 30 import com.android.tradefed.log.ITestLogger; 31 import com.android.tradefed.log.LogRegistry; 32 import com.android.tradefed.log.LogUtil.CLog; 33 import com.android.tradefed.result.ITestInvocationListener; 34 import com.android.tradefed.result.ITestLoggerReceiver; 35 import com.android.tradefed.result.ResultForwarder; 36 import com.android.tradefed.suite.checker.ISystemStatusCheckerReceiver; 37 import com.android.tradefed.targetprep.BuildError; 38 import com.android.tradefed.targetprep.ITargetCleaner; 39 import com.android.tradefed.targetprep.ITargetPreparer; 40 import com.android.tradefed.targetprep.TargetSetupError; 41 import com.android.tradefed.targetprep.multi.IMultiTargetPreparer; 42 import com.android.tradefed.testtype.IBuildReceiver; 43 import com.android.tradefed.testtype.IDeviceTest; 44 import com.android.tradefed.testtype.IInvocationContextReceiver; 45 import com.android.tradefed.testtype.IMultiDeviceTest; 46 import com.android.tradefed.testtype.IRemoteTest; 47 import com.android.tradefed.testtype.IRuntimeHintProvider; 48 import com.android.tradefed.testtype.ITestCollector; 49 import com.android.tradefed.util.StreamUtil; 50 51 import com.google.common.annotations.VisibleForTesting; 52 53 import java.io.PrintWriter; 54 import java.io.StringWriter; 55 import java.util.ArrayList; 56 import java.util.Collection; 57 import java.util.Collections; 58 import java.util.HashMap; 59 import java.util.List; 60 import java.util.Map; 61 62 /** 63 * Container for the test run configuration. This class is an helper to prepare and run the tests. 64 */ 65 public class ModuleDefinition implements Comparable<ModuleDefinition>, ITestCollector { 66 67 /** key names used for saving module info into {@link IInvocationContext} */ 68 public static final String MODULE_NAME = "module-name"; 69 public static final String MODULE_ABI = "module-abi"; 70 71 private final IInvocationContext mModuleInvocationContext; 72 73 private final String mId; 74 private Collection<IRemoteTest> mTests = null; 75 private List<ITargetPreparer> mPreparers = new ArrayList<>(); 76 private List<ITargetCleaner> mCleaners = new ArrayList<>(); 77 private List<IMultiTargetPreparer> mMultiPreparers = new ArrayList<>(); 78 private IBuildInfo mBuild; 79 private ITestDevice mDevice; 80 private Map<ITestDevice, IBuildInfo> mDeviceInfos; 81 private boolean mCollectTestsOnly = false; 82 83 private List<TestRunResult> mTestsResults = new ArrayList<>(); 84 private int mExpectedTests = 0; 85 private boolean mIsFailedModule = false; 86 87 // Tracking of preparers performance 88 private long mElapsedPreparation = 0l; 89 private long mElapsedTearDown = 0l; 90 91 private long mElapsedTest = 0l; 92 93 public static final String PREPARATION_TIME = "PREP_TIME"; 94 public static final String TEAR_DOWN_TIME = "TEARDOWN_TIME"; 95 public static final String TEST_TIME = "TEST_TIME"; 96 97 /** 98 * Constructor 99 * 100 * @param name unique name of the test configuration. 101 * @param tests list of {@link IRemoteTest} that needs to run. 102 * @param preparers list of {@link ITargetPreparer} to be used to setup the device. 103 * @param configDescriptor the {@link ConfigurationDescriptor} of the underlying module config. 104 */ 105 public ModuleDefinition( 106 String name, 107 Collection<IRemoteTest> tests, 108 List<ITargetPreparer> preparers, 109 List<IMultiTargetPreparer> multiPreparers, 110 ConfigurationDescriptor configDescriptor) { 111 mId = name; 112 mTests = tests; 113 114 mModuleInvocationContext = new InvocationContext(); 115 mModuleInvocationContext.setConfigurationDescriptor(configDescriptor); 116 mModuleInvocationContext.addInvocationAttribute(MODULE_NAME, mId); 117 // If available in the suite, add the abi name 118 if (configDescriptor.getAbi() != null) { 119 mModuleInvocationContext.addInvocationAttribute( 120 MODULE_ABI, configDescriptor.getAbi().getName()); 121 } 122 123 mMultiPreparers.addAll(multiPreparers); 124 125 for (ITargetPreparer preparer : preparers) { 126 mPreparers.add(preparer); 127 if (preparer instanceof ITargetCleaner) { 128 mCleaners.add((ITargetCleaner) preparer); 129 } 130 } 131 // Reverse cleaner order, so that last target_preparer to setup is first to clean up. 132 Collections.reverse(mCleaners); 133 } 134 135 /** 136 * Returns the next {@link IRemoteTest} from the list of tests. The list of tests of a module 137 * may be shared with another one in case of sharding. 138 */ 139 IRemoteTest poll() { 140 synchronized (mTests) { 141 if (mTests.isEmpty()) { 142 return null; 143 } 144 IRemoteTest test = mTests.iterator().next(); 145 mTests.remove(test); 146 return test; 147 } 148 } 149 150 /** 151 * Add some {@link IRemoteTest} to be executed as part of the module. Used when merging two 152 * modules. 153 */ 154 void addTests(List<IRemoteTest> test) { 155 synchronized (mTests) { 156 mTests.addAll(test); 157 } 158 } 159 160 /** Returns the current number of {@link IRemoteTest} waiting to be executed. */ 161 public int numTests() { 162 synchronized (mTests) { 163 return mTests.size(); 164 } 165 } 166 167 /** 168 * Return True if the Module still has {@link IRemoteTest} to run in its pool. False otherwise. 169 */ 170 protected boolean hasTests() { 171 synchronized (mTests) { 172 return mTests.isEmpty(); 173 } 174 } 175 176 /** Return the unique module name. */ 177 public String getId() { 178 return mId; 179 } 180 181 /** 182 * {@inheritDoc} 183 */ 184 @Override 185 public int compareTo(ModuleDefinition moduleDef) { 186 return getId().compareTo(moduleDef.getId()); 187 } 188 189 /** 190 * Inject the {@link IBuildInfo} to be used during the tests. 191 */ 192 public void setBuild(IBuildInfo build) { 193 mBuild = build; 194 } 195 196 /** 197 * Inject the {@link ITestDevice} to be used during the tests. 198 */ 199 public void setDevice(ITestDevice device) { 200 mDevice = device; 201 } 202 203 /** 204 * Inject the {@link Map} of {@link ITestDevice} and {@link IBuildInfo} for the configuration. 205 */ 206 public void setDeviceInfos(Map<ITestDevice, IBuildInfo> deviceInfos) { 207 mDeviceInfos = deviceInfos; 208 } 209 210 /** 211 * Run all the {@link IRemoteTest} contained in the module and use all the preparers before and 212 * after to setup and clean the device. 213 * 214 * @param listener the {@link ITestInvocationListener} where to report results. 215 * @throws DeviceNotAvailableException in case of device going offline. 216 */ 217 public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { 218 run(listener, null); 219 } 220 221 /** 222 * Run all the {@link IRemoteTest} contained in the module and use all the preparers before and 223 * after to setup and clean the device. 224 * 225 * @param listener the {@link ITestInvocationListener} where to report results. 226 * @param failureListener a particular listener to collect logs on testFail. Can be null. 227 * @throws DeviceNotAvailableException in case of device going offline. 228 */ 229 public void run(ITestInvocationListener listener, TestFailureListener failureListener) 230 throws DeviceNotAvailableException { 231 CLog.d("Running module %s", getId()); 232 Exception preparationException = null; 233 // Setup 234 long prepStartTime = getCurrentTime(); 235 for (ITargetPreparer preparer : mPreparers) { 236 preparationException = runPreparerSetup(preparer, listener); 237 if (preparationException != null) { 238 mIsFailedModule = true; 239 CLog.e("Some preparation step failed. failing the module %s", getId()); 240 break; 241 } 242 } 243 // Skip multi-preparation if preparation already failed. 244 if (preparationException == null) { 245 for (IMultiTargetPreparer multiPreparer : mMultiPreparers) { 246 preparationException = runMultiPreparerSetup(multiPreparer, listener); 247 if (preparationException != null) { 248 mIsFailedModule = true; 249 CLog.e("Some preparation step failed. failing the module %s", getId()); 250 break; 251 } 252 } 253 } 254 mElapsedPreparation = getCurrentTime() - prepStartTime; 255 // Run the tests 256 try { 257 if (preparationException != null) { 258 // For reporting purpose we create a failure placeholder with the error stack 259 // similar to InitializationError of JUnit. 260 TestIdentifier testid = 261 new TestIdentifier( 262 preparationException.getClass().getCanonicalName(), 263 "preparationError"); 264 listener.testRunStarted(getId(), 1); 265 listener.testStarted(testid); 266 StringWriter sw = new StringWriter(); 267 preparationException.printStackTrace(new PrintWriter(sw)); 268 listener.testFailed(testid, sw.toString()); 269 listener.testEnded(testid, Collections.emptyMap()); 270 listener.testRunFailed(sw.toString()); 271 Map<String, String> metrics = new HashMap<>(); 272 metrics.put(TEST_TIME, "0"); 273 listener.testRunEnded(0, metrics); 274 return; 275 } 276 mElapsedTest = getCurrentTime(); 277 while (true) { 278 IRemoteTest test = poll(); 279 if (test == null) { 280 return; 281 } 282 283 if (test instanceof IBuildReceiver) { 284 ((IBuildReceiver) test).setBuild(mBuild); 285 } 286 if (test instanceof IDeviceTest) { 287 ((IDeviceTest) test).setDevice(mDevice); 288 } 289 if (test instanceof IMultiDeviceTest) { 290 ((IMultiDeviceTest) test).setDeviceInfos(mDeviceInfos); 291 } 292 if (test instanceof IInvocationContextReceiver) { 293 ((IInvocationContextReceiver) test) 294 .setInvocationContext(mModuleInvocationContext); 295 } 296 if (test instanceof ISystemStatusCheckerReceiver) { 297 // We do not pass down Status checker because they are already running at the 298 // top level suite. 299 ((ISystemStatusCheckerReceiver) test).setSystemStatusChecker(new ArrayList<>()); 300 } 301 if (test instanceof ITestCollector) { 302 ((ITestCollector) test).setCollectTestsOnly(mCollectTestsOnly); 303 } 304 305 // Run the test, only in case of DeviceNotAvailable we exit the module 306 // execution in order to execute as much as possible. 307 ModuleListener moduleListener = new ModuleListener(listener); 308 List<ITestInvocationListener> currentTestListener = new ArrayList<>(); 309 if (failureListener != null) { 310 currentTestListener.add(failureListener); 311 } 312 currentTestListener.add(moduleListener); 313 try { 314 test.run(new ResultForwarder(currentTestListener)); 315 } catch (RuntimeException re) { 316 CLog.e("Module '%s' - test '%s' threw exception:", getId(), test.getClass()); 317 CLog.e(re); 318 CLog.e("Proceeding to the next test."); 319 reportFailure(new ResultForwarder(currentTestListener), re.getMessage()); 320 } catch (DeviceUnresponsiveException due) { 321 // being able to catch a DeviceUnresponsiveException here implies that 322 // recovery was successful, and test execution should proceed to next 323 // module. 324 CLog.w( 325 "Ignored DeviceUnresponsiveException because recovery was " 326 + "successful, proceeding with next module. Stack trace:"); 327 CLog.w(due); 328 CLog.w("Proceeding to the next test."); 329 reportFailure(new ResultForwarder(currentTestListener), due.getMessage()); 330 } catch (DeviceNotAvailableException dnae) { 331 // We do special logging of some information in Context of the module for easier 332 // debugging. 333 CLog.e( 334 "Module %s threw a DeviceNotAvailableException on device %s during test %s", 335 getId(), mDevice.getSerialNumber(), test.getClass()); 336 CLog.e(dnae); 337 // log an events 338 logDeviceEvent( 339 EventType.MODULE_DEVICE_NOT_AVAILABLE, 340 mDevice.getSerialNumber(), 341 dnae, 342 getId()); 343 throw dnae; 344 } finally { 345 mTestsResults.addAll(moduleListener.getRunResults()); 346 mExpectedTests += moduleListener.getNumTotalTests(); 347 } 348 } 349 } finally { 350 long cleanStartTime = getCurrentTime(); 351 try { 352 // Tear down 353 List<IMultiTargetPreparer> cleanerList = new ArrayList<>(mMultiPreparers); 354 Collections.reverse(cleanerList); 355 for (IMultiTargetPreparer multiCleaner : cleanerList) { 356 CLog.d("Multi cleaner: %s", multiCleaner.getClass().getSimpleName()); 357 multiCleaner.tearDown(mModuleInvocationContext, null); 358 } 359 for (ITargetCleaner cleaner : mCleaners) { 360 CLog.d("Cleaner: %s", cleaner.getClass().getSimpleName()); 361 cleaner.tearDown(mDevice, mBuild, null); 362 } 363 } catch (DeviceNotAvailableException tearDownException) { 364 CLog.e( 365 "Module %s failed during tearDown with: %s", 366 getId(), StreamUtil.getStackTrace(tearDownException)); 367 throw tearDownException; 368 } finally { 369 if (failureListener != null) { 370 failureListener.join(); 371 } 372 mElapsedTearDown = getCurrentTime() - cleanStartTime; 373 // finalize results 374 if (preparationException == null) { 375 reportFinalResults(listener, mExpectedTests, mTestsResults); 376 } 377 } 378 } 379 } 380 381 private void reportFailure(ITestInvocationListener listener, String errorMessage) { 382 listener.testRunFailed(errorMessage); 383 } 384 385 /** Helper to log the device events. */ 386 private void logDeviceEvent(EventType event, String serial, Throwable t, String moduleId) { 387 Map<String, String> args = new HashMap<>(); 388 args.put("serial", serial); 389 args.put("trace", StreamUtil.getStackTrace(t)); 390 args.put("module-id", moduleId); 391 LogRegistry.getLogRegistry().logEvent(LogLevel.DEBUG, event, args); 392 } 393 394 /** Finalize results to report them all and count if there are missing tests. */ 395 private void reportFinalResults( 396 ITestInvocationListener listener, 397 int totalExpectedTests, 398 List<TestRunResult> listResults) { 399 long elapsedTime = 0l; 400 Map<String, String> metrics = new HashMap<>(); 401 listener.testRunStarted(getId(), totalExpectedTests); 402 403 int numResults = 0; 404 for (TestRunResult runResult : listResults) { 405 numResults += runResult.getTestResults().size(); 406 forwardTestResults(runResult.getTestResults(), listener); 407 if (runResult.isRunFailure()) { 408 listener.testRunFailed(runResult.getRunFailureMessage()); 409 mIsFailedModule = true; 410 } 411 elapsedTime += runResult.getElapsedTime(); 412 // put metrics from the tests 413 metrics.putAll(runResult.getRunMetrics()); 414 } 415 // put metrics from the preparation 416 metrics.put(PREPARATION_TIME, Long.toString(mElapsedPreparation)); 417 metrics.put(TEAR_DOWN_TIME, Long.toString(mElapsedTearDown)); 418 metrics.put(TEST_TIME, Long.toString(elapsedTime)); 419 if (totalExpectedTests != numResults) { 420 String error = 421 String.format( 422 "Module %s only ran %d out of %d expected tests.", 423 getId(), numResults, totalExpectedTests); 424 listener.testRunFailed(error); 425 CLog.e(error); 426 mIsFailedModule = true; 427 } 428 listener.testRunEnded(getCurrentTime() - mElapsedTest, metrics); 429 } 430 431 private void forwardTestResults( 432 Map<TestIdentifier, TestResult> testResults, ITestInvocationListener listener) { 433 for (Map.Entry<TestIdentifier, TestResult> testEntry : testResults.entrySet()) { 434 listener.testStarted(testEntry.getKey(), testEntry.getValue().getStartTime()); 435 switch (testEntry.getValue().getStatus()) { 436 case FAILURE: 437 listener.testFailed(testEntry.getKey(), testEntry.getValue().getStackTrace()); 438 break; 439 case ASSUMPTION_FAILURE: 440 listener.testAssumptionFailure( 441 testEntry.getKey(), testEntry.getValue().getStackTrace()); 442 break; 443 case IGNORED: 444 listener.testIgnored(testEntry.getKey()); 445 break; 446 case INCOMPLETE: 447 listener.testFailed( 448 testEntry.getKey(), "Test did not complete due to exception."); 449 break; 450 default: 451 break; 452 } 453 listener.testEnded( 454 testEntry.getKey(), 455 testEntry.getValue().getEndTime(), 456 testEntry.getValue().getMetrics()); 457 } 458 } 459 460 /** Run all the prepare steps. */ 461 private Exception runPreparerSetup(ITargetPreparer preparer, ITestLogger logger) 462 throws DeviceNotAvailableException { 463 CLog.d("Preparer: %s", preparer.getClass().getSimpleName()); 464 try { 465 // set the logger in case they need it. 466 if (preparer instanceof ITestLoggerReceiver) { 467 ((ITestLoggerReceiver) preparer).setTestLogger(logger); 468 } 469 preparer.setUp(mDevice, mBuild); 470 return null; 471 } catch (BuildError | TargetSetupError e) { 472 CLog.e("Unexpected Exception from preparer: %s", preparer.getClass().getName()); 473 CLog.e(e); 474 return e; 475 } 476 } 477 478 /** Run all multi target preparer step. */ 479 private Exception runMultiPreparerSetup(IMultiTargetPreparer preparer, ITestLogger logger) { 480 CLog.d("Multi preparer: %s", preparer.getClass().getSimpleName()); 481 try { 482 // set the logger in case they need it. 483 if (preparer instanceof ITestLoggerReceiver) { 484 ((ITestLoggerReceiver) preparer).setTestLogger(logger); 485 } 486 preparer.setUp(mModuleInvocationContext); 487 return null; 488 } catch (BuildError | TargetSetupError | DeviceNotAvailableException e) { 489 CLog.e("Unexpected Exception from preparer: %s", preparer.getClass().getName()); 490 CLog.e(e); 491 return e; 492 } 493 } 494 495 /** Returns the current time. */ 496 private long getCurrentTime() { 497 return System.currentTimeMillis(); 498 } 499 500 @Override 501 public void setCollectTestsOnly(boolean collectTestsOnly) { 502 mCollectTestsOnly = collectTestsOnly; 503 } 504 505 /** Returns a list of tests that ran in this module. */ 506 List<TestRunResult> getTestsResults() { 507 return mTestsResults; 508 } 509 510 /** Returns the number of tests that was expected to be run */ 511 int getNumExpectedTests() { 512 return mExpectedTests; 513 } 514 515 /** Returns True if a testRunFailure has been called on the module * */ 516 public boolean hasModuleFailed() { 517 return mIsFailedModule; 518 } 519 520 /** {@inheritDoc} */ 521 @Override 522 public String toString() { 523 return getId(); 524 } 525 526 /** Returns the approximate time to run all the tests in the module. */ 527 public long getRuntimeHint() { 528 long hint = 0l; 529 for (IRemoteTest test : mTests) { 530 if (test instanceof IRuntimeHintProvider) { 531 hint += ((IRuntimeHintProvider) test).getRuntimeHint(); 532 } else { 533 hint += 60000; 534 } 535 } 536 return hint; 537 } 538 539 /** Returns the list of {@link ITargetPreparer} defined for this module. */ 540 @VisibleForTesting 541 List<ITargetPreparer> getTargetPreparers() { 542 return mPreparers; 543 } 544 545 /** Returns the list of {@link IRemoteTest} defined for this module. */ 546 @VisibleForTesting 547 List<IRemoteTest> getTests() { 548 return new ArrayList<>(mTests); 549 } 550 551 /** Returns the {@link IInvocationContext} associated with the module. */ 552 public IInvocationContext getModuleInvocationContext() { 553 return mModuleInvocationContext; 554 } 555 } 556