1 /* 2 * Copyright (C) 2017 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.invoker; 17 18 import com.android.ddmlib.IDevice; 19 import com.android.ddmlib.Log.LogLevel; 20 import com.android.tradefed.build.BuildInfo; 21 import com.android.tradefed.build.BuildInfoKey; 22 import com.android.tradefed.build.BuildInfoKey.BuildInfoFileKey; 23 import com.android.tradefed.build.BuildRetrievalError; 24 import com.android.tradefed.build.IBuildInfo; 25 import com.android.tradefed.build.IBuildInfo.BuildInfoProperties; 26 import com.android.tradefed.build.IBuildProvider; 27 import com.android.tradefed.build.IDeviceBuildInfo; 28 import com.android.tradefed.build.IDeviceBuildProvider; 29 import com.android.tradefed.config.GlobalConfiguration; 30 import com.android.tradefed.config.IConfiguration; 31 import com.android.tradefed.config.IDeviceConfiguration; 32 import com.android.tradefed.device.DeviceNotAvailableException; 33 import com.android.tradefed.device.ITestDevice; 34 import com.android.tradefed.device.StubDevice; 35 import com.android.tradefed.device.metric.AutoLogCollector; 36 import com.android.tradefed.device.metric.CollectorHelper; 37 import com.android.tradefed.device.metric.IMetricCollector; 38 import com.android.tradefed.device.metric.IMetricCollectorReceiver; 39 import com.android.tradefed.invoker.TestInvocation.Stage; 40 import com.android.tradefed.invoker.shard.IShardHelper; 41 import com.android.tradefed.log.ITestLogger; 42 import com.android.tradefed.log.LogUtil.CLog; 43 import com.android.tradefed.result.ITestInvocationListener; 44 import com.android.tradefed.result.ITestLoggerReceiver; 45 import com.android.tradefed.result.InputStreamSource; 46 import com.android.tradefed.result.LogDataType; 47 import com.android.tradefed.suite.checker.ISystemStatusCheckerReceiver; 48 import com.android.tradefed.targetprep.BuildError; 49 import com.android.tradefed.targetprep.IHostCleaner; 50 import com.android.tradefed.targetprep.ITargetCleaner; 51 import com.android.tradefed.targetprep.ITargetPreparer; 52 import com.android.tradefed.targetprep.TargetSetupError; 53 import com.android.tradefed.targetprep.multi.IMultiTargetPreparer; 54 import com.android.tradefed.testtype.IBuildReceiver; 55 import com.android.tradefed.testtype.IDeviceTest; 56 import com.android.tradefed.testtype.IInvocationContextReceiver; 57 import com.android.tradefed.testtype.IMultiDeviceTest; 58 import com.android.tradefed.testtype.IRemoteTest; 59 import com.android.tradefed.util.FileUtil; 60 import com.android.tradefed.util.SystemUtil; 61 import com.android.tradefed.util.SystemUtil.EnvVariable; 62 import com.android.tradefed.util.TimeUtil; 63 64 import com.google.common.annotations.VisibleForTesting; 65 import com.google.common.base.Strings; 66 67 import java.io.File; 68 import java.io.IOException; 69 import java.util.ArrayList; 70 import java.util.List; 71 import java.util.ListIterator; 72 import java.util.stream.Collectors; 73 74 /** 75 * Class that describes all the invocation steps: build download, target_prep, run tests, clean up. 76 * Can be extended to override the default behavior of some steps. Order of the steps is driven by 77 * {@link TestInvocation}. 78 */ 79 public class InvocationExecution implements IInvocationExecution { 80 81 public static final String ADB_VERSION_KEY = "adb_version"; 82 83 @Override fetchBuild( IInvocationContext context, IConfiguration config, IRescheduler rescheduler, ITestInvocationListener listener)84 public boolean fetchBuild( 85 IInvocationContext context, 86 IConfiguration config, 87 IRescheduler rescheduler, 88 ITestInvocationListener listener) 89 throws DeviceNotAvailableException, BuildRetrievalError { 90 String currentDeviceName = null; 91 try { 92 updateInvocationContext(context, config); 93 // TODO: evaluate fetching build in parallel 94 for (String deviceName : context.getDeviceConfigNames()) { 95 currentDeviceName = deviceName; 96 IBuildInfo info = null; 97 ITestDevice device = context.getDevice(deviceName); 98 IDeviceConfiguration deviceConfig = config.getDeviceConfigByName(deviceName); 99 IBuildProvider provider = deviceConfig.getBuildProvider(); 100 // Inject the context to the provider if it can receive it 101 if (provider instanceof IInvocationContextReceiver) { 102 ((IInvocationContextReceiver) provider).setInvocationContext(context); 103 } 104 // Get the build 105 if (provider instanceof IDeviceBuildProvider) { 106 // Download a device build if the provider can handle it. 107 info = ((IDeviceBuildProvider) provider).getBuild(device); 108 } else { 109 info = provider.getBuild(); 110 } 111 if (info != null) { 112 info.setDeviceSerial(device.getSerialNumber()); 113 context.addDeviceBuildInfo(deviceName, info); 114 device.setRecovery(deviceConfig.getDeviceRecovery()); 115 } else { 116 CLog.logAndDisplay( 117 LogLevel.WARN, 118 "No build found to test for device: %s", 119 device.getSerialNumber()); 120 IBuildInfo notFoundStub = new BuildInfo(); 121 updateBuild(notFoundStub, config); 122 context.addDeviceBuildInfo(currentDeviceName, notFoundStub); 123 return false; 124 } 125 // TODO: remove build update when reporting is done on context 126 updateBuild(info, config); 127 info.setTestResourceBuild(config.isDeviceConfiguredFake(currentDeviceName)); 128 } 129 } catch (BuildRetrievalError e) { 130 CLog.e(e); 131 if (currentDeviceName != null) { 132 IBuildInfo errorBuild = e.getBuildInfo(); 133 updateBuild(errorBuild, config); 134 context.addDeviceBuildInfo(currentDeviceName, errorBuild); 135 updateInvocationContext(context, config); 136 } 137 throw e; 138 } 139 createSharedResources(context); 140 setAdbVersion(context); 141 return true; 142 } 143 144 @Override cleanUpBuilds(IInvocationContext context, IConfiguration config)145 public void cleanUpBuilds(IInvocationContext context, IConfiguration config) { 146 // Ensure build infos are always cleaned up at the end of invocation. 147 for (String cleanUpDevice : context.getDeviceConfigNames()) { 148 if (context.getBuildInfo(cleanUpDevice) != null) { 149 try { 150 config.getDeviceConfigByName(cleanUpDevice) 151 .getBuildProvider() 152 .cleanUp(context.getBuildInfo(cleanUpDevice)); 153 } catch (RuntimeException e) { 154 // We catch an simply log exception in cleanUp to avoid missing any final 155 // step of the invocation. 156 CLog.e(e); 157 } 158 } 159 } 160 } 161 162 @Override shardConfig( IConfiguration config, IInvocationContext context, IRescheduler rescheduler)163 public boolean shardConfig( 164 IConfiguration config, IInvocationContext context, IRescheduler rescheduler) { 165 return createShardHelper().shardConfig(config, context, rescheduler); 166 } 167 168 /** Create an return the {@link IShardHelper} to be used. */ 169 @VisibleForTesting createShardHelper()170 protected IShardHelper createShardHelper() { 171 return GlobalConfiguration.getInstance().getShardingStrategy(); 172 } 173 174 @Override doSetup( IInvocationContext context, IConfiguration config, final ITestInvocationListener listener)175 public void doSetup( 176 IInvocationContext context, 177 IConfiguration config, 178 final ITestInvocationListener listener) 179 throws TargetSetupError, BuildError, DeviceNotAvailableException { 180 long start = System.currentTimeMillis(); 181 try { 182 // Before all the individual setup, make the multi-pre-target-preparer devices setup 183 runMultiTargetPreparers( 184 config.getMultiPreTargetPreparers(), 185 listener, 186 context, 187 "multi pre target preparer setup"); 188 189 // TODO: evaluate doing device setup in parallel 190 for (String deviceName : context.getDeviceConfigNames()) { 191 ITestDevice device = context.getDevice(deviceName); 192 CLog.d("Starting setup for device: '%s'", device.getSerialNumber()); 193 if (device instanceof ITestLoggerReceiver) { 194 ((ITestLoggerReceiver) context.getDevice(deviceName)).setTestLogger(listener); 195 } 196 for (ITargetPreparer preparer : 197 config.getDeviceConfigByName(deviceName).getTargetPreparers()) { 198 // do not call the preparer if it was disabled 199 if (preparer.isDisabled()) { 200 CLog.d("%s has been disabled. skipping.", preparer); 201 continue; 202 } 203 if (preparer instanceof ITestLoggerReceiver) { 204 ((ITestLoggerReceiver) preparer).setTestLogger(listener); 205 } 206 CLog.d( 207 "starting preparer '%s' on device: '%s'", 208 preparer, device.getSerialNumber()); 209 preparer.setUp(device, context.getBuildInfo(deviceName)); 210 CLog.d( 211 "done with preparer '%s' on device: '%s'", 212 preparer, device.getSerialNumber()); 213 } 214 CLog.d("Done with setup of device: '%s'", device.getSerialNumber()); 215 } 216 // After all the individual setup, make the multi-devices setup 217 runMultiTargetPreparers( 218 config.getMultiTargetPreparers(), 219 listener, 220 context, 221 "multi target preparer setup"); 222 } finally { 223 // Note: These metrics are handled in a try in case of a kernel reset or device issue. 224 // Setup timing metric. It does not include flashing time on boot tests. 225 long setupDuration = System.currentTimeMillis() - start; 226 context.addInvocationTimingMetric(IInvocationContext.TimingEvent.SETUP, setupDuration); 227 CLog.d("Setup duration: %s'", TimeUtil.formatElapsedTime(setupDuration)); 228 // Upload the setup logcat after setup is complete. 229 for (String deviceName : context.getDeviceConfigNames()) { 230 reportLogs(context.getDevice(deviceName), listener, Stage.SETUP); 231 } 232 } 233 } 234 235 /** {@inheritDoc} */ 236 @Override runDevicePreInvocationSetup( IInvocationContext context, IConfiguration config, ITestLogger logger)237 public final void runDevicePreInvocationSetup( 238 IInvocationContext context, IConfiguration config, ITestLogger logger) 239 throws DeviceNotAvailableException, TargetSetupError { 240 for (String deviceName : context.getDeviceConfigNames()) { 241 ITestDevice device = context.getDevice(deviceName); 242 243 CLog.d("Starting device pre invocation setup for : '%s'", device.getSerialNumber()); 244 if (device instanceof ITestLoggerReceiver) { 245 ((ITestLoggerReceiver) context.getDevice(deviceName)).setTestLogger(logger); 246 } 247 if (!config.getCommandOptions().shouldSkipPreDeviceSetup()) { 248 device.preInvocationSetup( 249 context.getBuildInfo(deviceName), 250 context.getBuildInfos() 251 .stream() 252 .filter(buildInfo -> buildInfo.isTestResourceBuild()) 253 .collect(Collectors.toList())); 254 } 255 } 256 } 257 258 /** {@inheritDoc} */ 259 @Override runDevicePostInvocationTearDown( IInvocationContext context, IConfiguration config)260 public final void runDevicePostInvocationTearDown( 261 IInvocationContext context, IConfiguration config) { 262 // Extra tear down step for the device 263 for (String deviceName : context.getDeviceConfigNames()) { 264 ITestDevice device = context.getDevice(deviceName); 265 if (!config.getCommandOptions().shouldSkipPreDeviceSetup()) { 266 device.postInvocationTearDown(); 267 } 268 } 269 } 270 271 /** Runs the {@link IMultiTargetPreparer} specified. */ runMultiTargetPreparers( List<IMultiTargetPreparer> multiPreparers, ITestLogger logger, IInvocationContext context, String description)272 private void runMultiTargetPreparers( 273 List<IMultiTargetPreparer> multiPreparers, 274 ITestLogger logger, 275 IInvocationContext context, 276 String description) 277 throws TargetSetupError, BuildError, DeviceNotAvailableException { 278 for (IMultiTargetPreparer multiPreparer : multiPreparers) { 279 // do not call the preparer if it was disabled 280 if (multiPreparer.isDisabled()) { 281 CLog.d("%s has been disabled. skipping.", multiPreparer); 282 continue; 283 } 284 if (multiPreparer instanceof ITestLoggerReceiver) { 285 ((ITestLoggerReceiver) multiPreparer).setTestLogger(logger); 286 } 287 CLog.d("Starting %s '%s'", description, multiPreparer); 288 multiPreparer.setUp(context); 289 CLog.d("done with %s '%s'", description, multiPreparer); 290 } 291 } 292 293 /** Runs the {@link IMultiTargetPreparer} specified tearDown. */ runMultiTargetPreparersTearDown( List<IMultiTargetPreparer> multiPreparers, IInvocationContext context, ITestLogger logger, Throwable throwable, String description)294 private Throwable runMultiTargetPreparersTearDown( 295 List<IMultiTargetPreparer> multiPreparers, 296 IInvocationContext context, 297 ITestLogger logger, 298 Throwable throwable, 299 String description) 300 throws Throwable { 301 ListIterator<IMultiTargetPreparer> iterator = 302 multiPreparers.listIterator(multiPreparers.size()); 303 Throwable deferredThrowable = null; 304 305 while (iterator.hasPrevious()) { 306 IMultiTargetPreparer multipreparer = iterator.previous(); 307 if (multipreparer.isDisabled() || multipreparer.isTearDownDisabled()) { 308 CLog.d("%s has been disabled. skipping.", multipreparer); 309 continue; 310 } 311 if (multipreparer instanceof ITestLoggerReceiver) { 312 ((ITestLoggerReceiver) multipreparer).setTestLogger(logger); 313 } 314 CLog.d("Starting %s '%s'", description, multipreparer); 315 try { 316 multipreparer.tearDown(context, throwable); 317 } catch (Throwable t) { 318 // We catch it and rethrow later to allow each multi_targetprep to be attempted. 319 // Only the first one will be thrown but all should be logged. 320 CLog.e("Deferring throw for:"); 321 CLog.e(t); 322 if (deferredThrowable == null) { 323 deferredThrowable = t; 324 } 325 } 326 CLog.d("Done with %s '%s'", description, multipreparer); 327 } 328 329 return deferredThrowable; 330 } 331 332 @Override doTeardown( IInvocationContext context, IConfiguration config, ITestLogger logger, Throwable exception)333 public void doTeardown( 334 IInvocationContext context, 335 IConfiguration config, 336 ITestLogger logger, 337 Throwable exception) 338 throws Throwable { 339 Throwable deferredThrowable = null; 340 341 List<IMultiTargetPreparer> multiPreparers = config.getMultiTargetPreparers(); 342 deferredThrowable = 343 runMultiTargetPreparersTearDown( 344 multiPreparers, 345 context, 346 logger, 347 exception, 348 "multi target preparer teardown"); 349 350 // Clear wifi settings, to prevent wifi errors from interfering with teardown process. 351 for (String deviceName : context.getDeviceConfigNames()) { 352 ITestDevice device = context.getDevice(deviceName); 353 device.clearLastConnectedWifiNetwork(); 354 List<ITargetPreparer> preparers = 355 config.getDeviceConfigByName(deviceName).getTargetPreparers(); 356 ListIterator<ITargetPreparer> itr = preparers.listIterator(preparers.size()); 357 while (itr.hasPrevious()) { 358 ITargetPreparer preparer = itr.previous(); 359 if (preparer instanceof ITargetCleaner) { 360 ITargetCleaner cleaner = (ITargetCleaner) preparer; 361 // do not call the cleaner if it was disabled 362 if (cleaner.isDisabled() || cleaner.isTearDownDisabled()) { 363 CLog.d("%s has been disabled. skipping.", cleaner); 364 continue; 365 } 366 // If setup hit a targetSetupError, the setUp() and setTestLogger might not have 367 // run, ensure we still have the logger. 368 if (preparer instanceof ITestLoggerReceiver) { 369 ((ITestLoggerReceiver) preparer).setTestLogger(logger); 370 } 371 try { 372 CLog.d( 373 "starting tearDown '%s' on device: '%s'", 374 preparer, device.getSerialNumber()); 375 cleaner.tearDown(device, context.getBuildInfo(deviceName), exception); 376 CLog.d( 377 "done with tearDown '%s' on device: '%s'", 378 preparer, device.getSerialNumber()); 379 } catch (Throwable e) { 380 // We catch it and rethrow later to allow each targetprep to be attempted. 381 // Only the first one will be thrown but all should be logged. 382 CLog.e("Deferring throw for:"); 383 CLog.e(e); 384 if (deferredThrowable == null) { 385 deferredThrowable = e; 386 } 387 } 388 } 389 } 390 } 391 392 // Extra tear down step for the device 393 runDevicePostInvocationTearDown(context, config); 394 395 // After all, run the multi_pre_target_preparer tearDown. 396 List<IMultiTargetPreparer> multiPrePreparers = config.getMultiPreTargetPreparers(); 397 Throwable preTargetTearDownException = 398 runMultiTargetPreparersTearDown( 399 multiPrePreparers, 400 context, 401 logger, 402 exception, 403 "multi pre target preparer teardown"); 404 if (deferredThrowable == null) { 405 deferredThrowable = preTargetTearDownException; 406 } 407 408 if (deferredThrowable != null) { 409 throw deferredThrowable; 410 } 411 } 412 413 @Override doCleanUp(IInvocationContext context, IConfiguration config, Throwable exception)414 public void doCleanUp(IInvocationContext context, IConfiguration config, Throwable exception) { 415 for (String deviceName : context.getDeviceConfigNames()) { 416 List<ITargetPreparer> preparers = 417 config.getDeviceConfigByName(deviceName).getTargetPreparers(); 418 ListIterator<ITargetPreparer> itr = preparers.listIterator(preparers.size()); 419 while (itr.hasPrevious()) { 420 ITargetPreparer preparer = itr.previous(); 421 if (preparer instanceof IHostCleaner) { 422 IHostCleaner cleaner = (IHostCleaner) preparer; 423 if (preparer.isDisabled() || preparer.isTearDownDisabled()) { 424 CLog.d("%s has been disabled. skipping.", cleaner); 425 continue; 426 } 427 cleaner.cleanUp(context.getBuildInfo(deviceName), exception); 428 } 429 } 430 } 431 } 432 433 @Override runTests( IInvocationContext context, IConfiguration config, ITestInvocationListener listener)434 public void runTests( 435 IInvocationContext context, IConfiguration config, ITestInvocationListener listener) 436 throws Throwable { 437 List<IRemoteTest> remainingTests = new ArrayList<>(config.getTests()); 438 UnexecutedTestReporterThread reporterThread = 439 new UnexecutedTestReporterThread(listener, remainingTests); 440 Runtime.getRuntime().addShutdownHook(reporterThread); 441 TestInvocation.printStageDelimiter(Stage.TEST, false); 442 try { 443 for (IRemoteTest test : config.getTests()) { 444 // For compatibility of those receivers, they are assumed to be single device alloc. 445 if (test instanceof IDeviceTest) { 446 ((IDeviceTest) test).setDevice(context.getDevices().get(0)); 447 } 448 if (test instanceof IBuildReceiver) { 449 ((IBuildReceiver) test) 450 .setBuild(context.getBuildInfo(context.getDevices().get(0))); 451 } 452 if (test instanceof ISystemStatusCheckerReceiver) { 453 ((ISystemStatusCheckerReceiver) test) 454 .setSystemStatusChecker(config.getSystemStatusCheckers()); 455 } 456 457 // TODO: consider adding receivers for only the list of ITestDevice and IBuildInfo. 458 if (test instanceof IMultiDeviceTest) { 459 ((IMultiDeviceTest) test).setDeviceInfos(context.getDeviceBuildMap()); 460 } 461 if (test instanceof IInvocationContextReceiver) { 462 ((IInvocationContextReceiver) test).setInvocationContext(context); 463 } 464 465 updateAutoCollectors(config); 466 467 // We clone the collectors for each IRemoteTest to ensure no state conflicts. 468 List<IMetricCollector> clonedCollectors = new ArrayList<>(); 469 // Add automated collectors 470 for (AutoLogCollector auto : config.getCommandOptions().getAutoLogCollectors()) { 471 clonedCollectors.add(auto.getInstanceForValue()); 472 } 473 474 // Add the collector from the configuration 475 clonedCollectors.addAll( 476 CollectorHelper.cloneCollectors(config.getMetricCollectors())); 477 if (test instanceof IMetricCollectorReceiver) { 478 ((IMetricCollectorReceiver) test).setMetricCollectors(clonedCollectors); 479 // If test can receive collectors then let it handle the how to set them up 480 test.run(listener); 481 } else { 482 // Wrap collectors in each other and collection will be sequential, do this in the 483 // loop to ensure they are always initialized against the right context. 484 ITestInvocationListener listenerWithCollectors = listener; 485 for (IMetricCollector collector : clonedCollectors) { 486 if (collector.isDisabled()) { 487 CLog.d("%s has been disabled. Skipping.", collector); 488 } else { 489 listenerWithCollectors = 490 collector.init(context, listenerWithCollectors); 491 } 492 } 493 test.run(listenerWithCollectors); 494 } 495 remainingTests.remove(test); 496 } 497 } finally { 498 TestInvocation.printStageDelimiter(Stage.TEST, true); 499 // TODO: Look if this can be improved to DeviceNotAvailableException too. 500 Runtime.getRuntime().removeShutdownHook(reporterThread); 501 } 502 503 } 504 505 @Override resetBuildAndReschedule( Throwable exception, ITestInvocationListener listener, IConfiguration config, IInvocationContext context)506 public boolean resetBuildAndReschedule( 507 Throwable exception, 508 ITestInvocationListener listener, 509 IConfiguration config, 510 IInvocationContext context) { 511 if (!(exception instanceof BuildError) && !(exception.getCause() instanceof BuildError)) { 512 for (String deviceName : context.getDeviceConfigNames()) { 513 config.getDeviceConfigByName(deviceName) 514 .getBuildProvider() 515 .buildNotTested(context.getBuildInfo(deviceName)); 516 } 517 return true; 518 } 519 return false; 520 } 521 522 @Override reportLogs(ITestDevice device, ITestInvocationListener listener, Stage stage)523 public void reportLogs(ITestDevice device, ITestInvocationListener listener, Stage stage) { 524 if (device == null) { 525 return; 526 } 527 IDevice idevice = device.getIDevice(); 528 // non stub device 529 if (!(idevice instanceof StubDevice)) { 530 try (InputStreamSource logcatSource = device.getLogcat()) { 531 device.clearLogcat(); 532 String name = 533 String.format( 534 "%s_%s", 535 TestInvocation.getDeviceLogName(stage), device.getSerialNumber()); 536 listener.testLog(name, LogDataType.LOGCAT, logcatSource); 537 } 538 } 539 // emulator logs 540 if (idevice != null && idevice.isEmulator()) { 541 try (InputStreamSource emulatorOutput = device.getEmulatorOutput()) { 542 // TODO: Clear the emulator log 543 String name = TestInvocation.getEmulatorLogName(stage); 544 listener.testLog(name, LogDataType.TEXT, emulatorOutput); 545 } 546 } 547 } 548 549 /** 550 * Update the {@link IInvocationContext} with additional info from the {@link IConfiguration}. 551 * 552 * @param context the {@link IInvocationContext} 553 * @param config the {@link IConfiguration} 554 */ updateInvocationContext(IInvocationContext context, IConfiguration config)555 void updateInvocationContext(IInvocationContext context, IConfiguration config) { 556 // TODO: Once reporting on context is done, only set context attributes 557 if (config.getCommandLine() != null) { 558 // TODO: obfuscate the password if any. 559 context.addInvocationAttribute( 560 TestInvocation.COMMAND_ARGS_KEY, config.getCommandLine()); 561 } 562 if (config.getCommandOptions().getShardCount() != null) { 563 context.addInvocationAttribute( 564 "shard_count", config.getCommandOptions().getShardCount().toString()); 565 } 566 if (config.getCommandOptions().getShardIndex() != null) { 567 context.addInvocationAttribute( 568 "shard_index", config.getCommandOptions().getShardIndex().toString()); 569 } 570 context.setTestTag(getTestTag(config)); 571 } 572 573 /** Helper to create the test tag from the configuration. */ getTestTag(IConfiguration config)574 private String getTestTag(IConfiguration config) { 575 String testTag = config.getCommandOptions().getTestTag(); 576 if (config.getCommandOptions().getTestTagSuffix() != null) { 577 testTag = 578 String.format("%s-%s", testTag, config.getCommandOptions().getTestTagSuffix()); 579 } 580 return testTag; 581 } 582 583 /** Handle setting the test tag on the build info. */ setTestTag(IBuildInfo info, IConfiguration config)584 protected void setTestTag(IBuildInfo info, IConfiguration config) { 585 // When CommandOption is set, it overrides any test-tag from build_providers 586 if (!"stub".equals(config.getCommandOptions().getTestTag())) { 587 info.setTestTag(getTestTag(config)); 588 } else if (Strings.isNullOrEmpty(info.getTestTag())) { 589 // We ensure that that a default test-tag is always available. 590 info.setTestTag("stub"); 591 } else { 592 CLog.w( 593 "Using the test-tag from the build_provider. Consider updating your config to" 594 + " have no alias/namespace in front of test-tag."); 595 } 596 } 597 598 /** 599 * Update the {@link IBuildInfo} with additional info from the {@link IConfiguration}. 600 * 601 * @param info the {@link IBuildInfo} 602 * @param config the {@link IConfiguration} 603 */ updateBuild(IBuildInfo info, IConfiguration config)604 void updateBuild(IBuildInfo info, IConfiguration config) { 605 if (config.getCommandLine() != null) { 606 // TODO: obfuscate the password if any. 607 info.addBuildAttribute(TestInvocation.COMMAND_ARGS_KEY, config.getCommandLine()); 608 } 609 if (config.getCommandOptions().getShardCount() != null) { 610 info.addBuildAttribute( 611 "shard_count", config.getCommandOptions().getShardCount().toString()); 612 } 613 if (config.getCommandOptions().getShardIndex() != null) { 614 info.addBuildAttribute( 615 "shard_index", config.getCommandOptions().getShardIndex().toString()); 616 } 617 setTestTag(info, config); 618 619 if (info.getProperties().contains(BuildInfoProperties.DO_NOT_LINK_TESTS_DIR)) { 620 CLog.d("Skip linking external directory as FileProperty was set."); 621 return; 622 } 623 // Load environment tests dir. 624 if (info instanceof IDeviceBuildInfo) { 625 File testsDir = ((IDeviceBuildInfo) info).getTestsDir(); 626 if (testsDir != null && testsDir.exists()) { 627 handleLinkingExternalDirs( 628 (IDeviceBuildInfo) info, 629 testsDir, 630 EnvVariable.ANDROID_TARGET_OUT_TESTCASES, 631 BuildInfoFileKey.TARGET_LINKED_DIR.getFileKey()); 632 handleLinkingExternalDirs( 633 (IDeviceBuildInfo) info, 634 testsDir, 635 EnvVariable.ANDROID_HOST_OUT_TESTCASES, 636 BuildInfoFileKey.HOST_LINKED_DIR.getFileKey()); 637 } 638 } 639 } 640 handleLinkingExternalDirs( IDeviceBuildInfo info, File testsDir, EnvVariable var, String baseName)641 private void handleLinkingExternalDirs( 642 IDeviceBuildInfo info, File testsDir, EnvVariable var, String baseName) { 643 File externalDir = getExternalTestCasesDirs(var); 644 if (externalDir == null) { 645 String path = SystemUtil.ENV_VARIABLE_PATHS_IN_TESTS_DIR.get(var); 646 File varDir = FileUtil.getFileForPath(testsDir, path); 647 if (varDir.exists()) { 648 // If we found a dir already in the tests dir we keep track of it 649 info.setFile( 650 baseName, 651 varDir, 652 /** version */ 653 "v1"); 654 } 655 return; 656 } 657 try { 658 // Avoid conflict by creating a randomized name for the arriving symlink file. 659 File subDir = FileUtil.createTempDir(baseName, testsDir); 660 subDir.delete(); 661 FileUtil.symlinkFile(externalDir, subDir); 662 // Tag the dir in the build info to be possibly cleaned. 663 info.setFile( 664 baseName, 665 subDir, 666 /** version */ 667 "v1"); 668 // Ensure we always delete the linking, no matter how the JVM exits. 669 subDir.deleteOnExit(); 670 } catch (IOException e) { 671 CLog.e("Failed to load external test dir %s. Ignoring it.", externalDir); 672 CLog.e(e); 673 } 674 } 675 676 /** Populate the shared resources directory for all non-resource build */ createSharedResources(IInvocationContext context)677 private void createSharedResources(IInvocationContext context) { 678 List<IBuildInfo> infos = context.getBuildInfos(); 679 if (infos.size() <= 1) { 680 return; 681 } 682 try { 683 File resourcesDir = null; 684 for (IBuildInfo info : infos) { 685 if (info.isTestResourceBuild()) { 686 if (resourcesDir == null) { 687 resourcesDir = FileUtil.createTempDir("invocation-resources-dir"); 688 } 689 // Create a reception sub-folder for each build info resource to avoid mixing 690 String name = 691 String.format( 692 "%s_%s_%s", 693 info.getBuildBranch(), 694 info.getBuildId(), 695 info.getBuildFlavor()); 696 File buildDir = FileUtil.createTempDir(name, resourcesDir); 697 for (BuildInfoFileKey key : BuildInfoKey.SHARED_KEY) { 698 File f = info.getFile(key); 699 if (f == null) { 700 continue; 701 } 702 File subDir = new File(buildDir, f.getName()); 703 FileUtil.symlinkFile(f, subDir); 704 } 705 } 706 } 707 if (resourcesDir == null) { 708 return; 709 } 710 // Only set the shared dir on real build if it exists. 711 CLog.d("Creating shared resources directory."); 712 for (IBuildInfo info : infos) { 713 if (!info.isTestResourceBuild()) { 714 info.setFile(BuildInfoFileKey.SHARED_RESOURCE_DIR, resourcesDir, "v1"); 715 } 716 } 717 } catch (IOException e) { 718 CLog.e("Failed to create the shared resources dir."); 719 CLog.e(e); 720 } 721 } 722 setAdbVersion(IInvocationContext context)723 private void setAdbVersion(IInvocationContext context) { 724 String version = getAdbVersion(); 725 if (version != null) { 726 context.addInvocationAttribute(ADB_VERSION_KEY, version); 727 } 728 } 729 730 /** Convert the legacy *-on-failure options to the new auto-collect. */ updateAutoCollectors(IConfiguration config)731 private void updateAutoCollectors(IConfiguration config) { 732 if (config.getCommandOptions().captureScreenshotOnFailure()) { 733 config.getCommandOptions() 734 .getAutoLogCollectors() 735 .add(AutoLogCollector.SCREENSHOT_ON_FAILURE); 736 } 737 if (config.getCommandOptions().captureLogcatOnFailure()) { 738 config.getCommandOptions() 739 .getAutoLogCollectors() 740 .add(AutoLogCollector.LOGCAT_ON_FAILURE); 741 } 742 } 743 744 /** Returns the external directory coming from the environment. */ 745 @VisibleForTesting getExternalTestCasesDirs(EnvVariable envVar)746 File getExternalTestCasesDirs(EnvVariable envVar) { 747 return SystemUtil.getExternalTestCasesDir(envVar); 748 } 749 750 /** Returns the adb version in use for the invocation. */ getAdbVersion()751 protected String getAdbVersion() { 752 return GlobalConfiguration.getDeviceManagerInstance().getAdbVersion(); 753 } 754 } 755