1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tradefed.config; 18 19 import com.android.tradefed.build.BuildRetrievalError; 20 import com.android.tradefed.build.IBuildProvider; 21 import com.android.tradefed.command.CommandOptions; 22 import com.android.tradefed.command.ICommandOptions; 23 import com.android.tradefed.config.OptionSetter.FieldDef; 24 import com.android.tradefed.device.IDeviceRecovery; 25 import com.android.tradefed.device.IDeviceSelection; 26 import com.android.tradefed.device.TestDeviceOptions; 27 import com.android.tradefed.device.metric.IMetricCollector; 28 import com.android.tradefed.log.ILeveledLogOutput; 29 import com.android.tradefed.log.LogUtil.CLog; 30 import com.android.tradefed.log.StdoutLogger; 31 import com.android.tradefed.postprocessor.BasePostProcessor; 32 import com.android.tradefed.postprocessor.IPostProcessor; 33 import com.android.tradefed.result.FileSystemLogSaver; 34 import com.android.tradefed.result.ILogSaver; 35 import com.android.tradefed.result.ITestInvocationListener; 36 import com.android.tradefed.result.TextResultReporter; 37 import com.android.tradefed.retry.BaseRetryDecision; 38 import com.android.tradefed.retry.IRetryDecision; 39 import com.android.tradefed.sandbox.SandboxOptions; 40 import com.android.tradefed.suite.checker.ISystemStatusChecker; 41 import com.android.tradefed.targetprep.ITargetPreparer; 42 import com.android.tradefed.targetprep.multi.IMultiTargetPreparer; 43 import com.android.tradefed.testtype.IRemoteTest; 44 import com.android.tradefed.testtype.StubTest; 45 import com.android.tradefed.testtype.coverage.CoverageOptions; 46 import com.android.tradefed.util.FileUtil; 47 import com.android.tradefed.util.IDisableable; 48 import com.android.tradefed.util.QuotationAwareTokenizer; 49 import com.android.tradefed.util.SystemUtil; 50 import com.android.tradefed.util.keystore.IKeyStoreClient; 51 52 import com.google.common.annotations.VisibleForTesting; 53 54 import org.kxml2.io.KXmlSerializer; 55 56 import java.io.File; 57 import java.io.IOException; 58 import java.io.PrintStream; 59 import java.io.PrintWriter; 60 import java.lang.reflect.InvocationTargetException; 61 import java.util.ArrayList; 62 import java.util.Collection; 63 import java.util.HashMap; 64 import java.util.HashSet; 65 import java.util.LinkedHashMap; 66 import java.util.List; 67 import java.util.Map; 68 import java.util.Map.Entry; 69 import java.util.Set; 70 import java.util.regex.Matcher; 71 import java.util.regex.Pattern; 72 73 /** 74 * A concrete {@link IConfiguration} implementation that stores the loaded config objects in a map. 75 */ 76 public class Configuration implements IConfiguration { 77 78 // type names for built in configuration objects 79 public static final String BUILD_PROVIDER_TYPE_NAME = "build_provider"; 80 public static final String TARGET_PREPARER_TYPE_NAME = "target_preparer"; 81 // Variation of Multi_target_preparer that runs BEFORE each device target_preparer. 82 public static final String MULTI_PRE_TARGET_PREPARER_TYPE_NAME = "multi_pre_target_preparer"; 83 public static final String MULTI_PREPARER_TYPE_NAME = "multi_target_preparer"; 84 public static final String TEST_TYPE_NAME = "test"; 85 public static final String DEVICE_RECOVERY_TYPE_NAME = "device_recovery"; 86 public static final String LOGGER_TYPE_NAME = "logger"; 87 public static final String LOG_SAVER_TYPE_NAME = "log_saver"; 88 public static final String RESULT_REPORTER_TYPE_NAME = "result_reporter"; 89 public static final String CMD_OPTIONS_TYPE_NAME = "cmd_options"; 90 public static final String DEVICE_REQUIREMENTS_TYPE_NAME = "device_requirements"; 91 public static final String DEVICE_OPTIONS_TYPE_NAME = "device_options"; 92 public static final String SYSTEM_STATUS_CHECKER_TYPE_NAME = "system_checker"; 93 public static final String CONFIGURATION_DESCRIPTION_TYPE_NAME = "config_desc"; 94 public static final String DEVICE_NAME = "device"; 95 public static final String DEVICE_METRICS_COLLECTOR_TYPE_NAME = "metrics_collector"; 96 public static final String METRIC_POST_PROCESSOR_TYPE_NAME = "metric_post_processor"; 97 public static final String SANDBOX_TYPE_NAME = "sandbox"; 98 public static final String SANBOX_OPTIONS_TYPE_NAME = "sandbox_options"; 99 public static final String RETRY_DECISION_TYPE_NAME = "retry_decision"; 100 public static final String COVERAGE_OPTIONS_TYPE_NAME = "coverage"; 101 102 private static Map<String, ObjTypeInfo> sObjTypeMap = null; 103 private static Set<String> sMultiDeviceSupportedTag = null; 104 105 // regexp pattern used to parse map option values 106 private static final Pattern OPTION_KEY_VALUE_PATTERN = Pattern.compile("(?<!\\\\)="); 107 108 private static final Pattern CONFIG_EXCEPTION_PATTERN = 109 Pattern.compile("Could not find option with name '(.*)'"); 110 111 /** Mapping of config object type name to config objects. */ 112 private Map<String, List<Object>> mConfigMap; 113 private final String mName; 114 private final String mDescription; 115 // original command line used to create this given configuration. 116 private String[] mCommandLine; 117 118 // used to track the files that where dynamically downloaded 119 private Set<File> mRemoteFiles = new HashSet<>(); 120 121 /** 122 * Container struct for built-in config object type 123 */ 124 private static class ObjTypeInfo { 125 final Class<?> mExpectedType; 126 /** 127 * true if a list (ie many objects in a single config) are supported for this type 128 */ 129 final boolean mIsListSupported; 130 ObjTypeInfo(Class<?> expectedType, boolean isList)131 ObjTypeInfo(Class<?> expectedType, boolean isList) { 132 mExpectedType = expectedType; 133 mIsListSupported = isList; 134 } 135 } 136 137 /** 138 * Determine if given config object type name is a built in object 139 * 140 * @param typeName the config object type name 141 * @return <code>true</code> if name is a built in object type 142 */ isBuiltInObjType(String typeName)143 static boolean isBuiltInObjType(String typeName) { 144 return getObjTypeMap().containsKey(typeName); 145 } 146 getObjTypeMap()147 private static synchronized Map<String, ObjTypeInfo> getObjTypeMap() { 148 if (sObjTypeMap == null) { 149 sObjTypeMap = new HashMap<String, ObjTypeInfo>(); 150 sObjTypeMap.put(BUILD_PROVIDER_TYPE_NAME, new ObjTypeInfo(IBuildProvider.class, false)); 151 sObjTypeMap.put(TARGET_PREPARER_TYPE_NAME, 152 new ObjTypeInfo(ITargetPreparer.class, true)); 153 sObjTypeMap.put( 154 MULTI_PRE_TARGET_PREPARER_TYPE_NAME, 155 new ObjTypeInfo(IMultiTargetPreparer.class, true)); 156 sObjTypeMap.put(MULTI_PREPARER_TYPE_NAME, 157 new ObjTypeInfo(IMultiTargetPreparer.class, true)); 158 sObjTypeMap.put(TEST_TYPE_NAME, new ObjTypeInfo(IRemoteTest.class, true)); 159 sObjTypeMap.put(DEVICE_RECOVERY_TYPE_NAME, 160 new ObjTypeInfo(IDeviceRecovery.class, false)); 161 sObjTypeMap.put(LOGGER_TYPE_NAME, new ObjTypeInfo(ILeveledLogOutput.class, false)); 162 sObjTypeMap.put(LOG_SAVER_TYPE_NAME, new ObjTypeInfo(ILogSaver.class, false)); 163 sObjTypeMap.put(RESULT_REPORTER_TYPE_NAME, 164 new ObjTypeInfo(ITestInvocationListener.class, true)); 165 sObjTypeMap.put(CMD_OPTIONS_TYPE_NAME, new ObjTypeInfo(ICommandOptions.class, 166 false)); 167 sObjTypeMap.put(DEVICE_REQUIREMENTS_TYPE_NAME, new ObjTypeInfo(IDeviceSelection.class, 168 false)); 169 sObjTypeMap.put(DEVICE_OPTIONS_TYPE_NAME, new ObjTypeInfo(TestDeviceOptions.class, 170 false)); 171 sObjTypeMap.put(DEVICE_NAME, new ObjTypeInfo(IDeviceConfiguration.class, true)); 172 sObjTypeMap.put(SYSTEM_STATUS_CHECKER_TYPE_NAME, 173 new ObjTypeInfo(ISystemStatusChecker.class, true)); 174 sObjTypeMap.put( 175 CONFIGURATION_DESCRIPTION_TYPE_NAME, 176 new ObjTypeInfo(ConfigurationDescriptor.class, false)); 177 sObjTypeMap.put( 178 DEVICE_METRICS_COLLECTOR_TYPE_NAME, 179 new ObjTypeInfo(IMetricCollector.class, true)); 180 sObjTypeMap.put( 181 METRIC_POST_PROCESSOR_TYPE_NAME, 182 new ObjTypeInfo(BasePostProcessor.class, true)); 183 sObjTypeMap.put(SANBOX_OPTIONS_TYPE_NAME, new ObjTypeInfo(SandboxOptions.class, false)); 184 sObjTypeMap.put(RETRY_DECISION_TYPE_NAME, new ObjTypeInfo(IRetryDecision.class, false)); 185 sObjTypeMap.put( 186 COVERAGE_OPTIONS_TYPE_NAME, new ObjTypeInfo(CoverageOptions.class, false)); 187 } 188 return sObjTypeMap; 189 } 190 191 /** 192 * Determine if a given config object type is allowed to exists inside a device tag 193 * configuration. 194 * Authorized type are: build_provider, target_preparer, device_recovery, device_requirements, 195 * device_options 196 * 197 * @param typeName the config object type name 198 * @return True if name is allowed to exists inside the device tag 199 */ doesBuiltInObjSupportMultiDevice(String typeName)200 static boolean doesBuiltInObjSupportMultiDevice(String typeName) { 201 return getMultiDeviceSupportedTag().contains(typeName); 202 } 203 204 /** 205 * Return the {@link Set} of tags that are supported in a device tag for multi device 206 * configuration. 207 */ getMultiDeviceSupportedTag()208 private static synchronized Set<String> getMultiDeviceSupportedTag() { 209 if (sMultiDeviceSupportedTag == null) { 210 sMultiDeviceSupportedTag = new HashSet<String>(); 211 sMultiDeviceSupportedTag.add(BUILD_PROVIDER_TYPE_NAME); 212 sMultiDeviceSupportedTag.add(TARGET_PREPARER_TYPE_NAME); 213 sMultiDeviceSupportedTag.add(DEVICE_RECOVERY_TYPE_NAME); 214 sMultiDeviceSupportedTag.add(DEVICE_REQUIREMENTS_TYPE_NAME); 215 sMultiDeviceSupportedTag.add(DEVICE_OPTIONS_TYPE_NAME); 216 } 217 return sMultiDeviceSupportedTag; 218 } 219 220 /** 221 * Creates an {@link Configuration} with default config objects. 222 */ Configuration(String name, String description)223 public Configuration(String name, String description) { 224 mName = name; 225 mDescription = description; 226 mConfigMap = new LinkedHashMap<String, List<Object>>(); 227 setDeviceConfig(new DeviceConfigurationHolder(ConfigurationDef.DEFAULT_DEVICE_NAME)); 228 setCommandOptions(new CommandOptions()); 229 setTest(new StubTest()); 230 setLogOutput(new StdoutLogger()); 231 setLogSaver(new FileSystemLogSaver()); // FileSystemLogSaver saves to tmp by default. 232 setTestInvocationListener(new TextResultReporter()); 233 // Init an empty list of target_preparers 234 setConfigurationObjectListNoThrow(TARGET_PREPARER_TYPE_NAME, new ArrayList<>()); 235 setMultiPreTargetPreparers(new ArrayList<>()); 236 setMultiTargetPreparers(new ArrayList<>()); 237 setSystemStatusCheckers(new ArrayList<ISystemStatusChecker>()); 238 setConfigurationDescriptor(new ConfigurationDescriptor()); 239 setDeviceMetricCollectors(new ArrayList<>()); 240 setPostProcessors(new ArrayList<>()); 241 setCoverageOptions(new CoverageOptions()); 242 setConfigurationObjectNoThrow(SANBOX_OPTIONS_TYPE_NAME, new SandboxOptions()); 243 setConfigurationObjectNoThrow(RETRY_DECISION_TYPE_NAME, new BaseRetryDecision()); 244 } 245 246 /** 247 * If we are in multi device mode, we cannot allow fetching the regular references because 248 * they are most likely wrong. 249 */ notAllowedInMultiMode(String function)250 private void notAllowedInMultiMode(String function) { 251 if (getConfigurationObjectList(DEVICE_NAME).size() > 1) { 252 throw new UnsupportedOperationException(String.format("Calling %s is not allowed " 253 + "in multi device mode", function)); 254 } 255 if (getConfigurationObjectList(DEVICE_NAME).isEmpty()) { 256 throw new UnsupportedOperationException( 257 "We should always have at least 1 Device config"); 258 } 259 } 260 261 /** {@inheritDoc} */ 262 @Override getName()263 public String getName() { 264 return mName; 265 } 266 267 /** 268 * @return a short user readable description this {@link Configuration} 269 */ getDescription()270 public String getDescription() { 271 return mDescription; 272 } 273 274 /** 275 * {@inheritDoc} 276 */ 277 @Override setCommandLine(String[] arrayArgs)278 public void setCommandLine(String[] arrayArgs) { 279 mCommandLine = arrayArgs; 280 } 281 282 /** 283 * {@inheritDoc} 284 */ 285 @Override getCommandLine()286 public String getCommandLine() { 287 // FIXME: obfuscated passwords from command line. 288 if (mCommandLine != null && mCommandLine.length != 0) { 289 return QuotationAwareTokenizer.combineTokens(mCommandLine); 290 } 291 // If no args were available return null. 292 return null; 293 } 294 295 /** 296 * {@inheritDoc} 297 */ 298 @SuppressWarnings("unchecked") 299 @Override getBuildProvider()300 public IBuildProvider getBuildProvider() { 301 notAllowedInMultiMode("getBuildProvider"); 302 return ((List<IDeviceConfiguration>)getConfigurationObjectList(DEVICE_NAME)) 303 .get(0).getBuildProvider(); 304 } 305 306 /** 307 * {@inheritDoc} 308 */ 309 @SuppressWarnings("unchecked") 310 @Override getTargetPreparers()311 public List<ITargetPreparer> getTargetPreparers() { 312 notAllowedInMultiMode("getTargetPreparers"); 313 return ((List<IDeviceConfiguration>)getConfigurationObjectList(DEVICE_NAME)) 314 .get(0).getTargetPreparers(); 315 } 316 317 /** 318 * {@inheritDoc} 319 */ 320 @SuppressWarnings("unchecked") 321 @Override getTests()322 public List<IRemoteTest> getTests() { 323 return (List<IRemoteTest>) getConfigurationObjectList(TEST_TYPE_NAME); 324 } 325 326 /** 327 * {@inheritDoc} 328 */ 329 @SuppressWarnings("unchecked") 330 @Override getDeviceRecovery()331 public IDeviceRecovery getDeviceRecovery() { 332 notAllowedInMultiMode("getDeviceRecovery"); 333 return ((List<IDeviceConfiguration>)getConfigurationObjectList(DEVICE_NAME)) 334 .get(0).getDeviceRecovery(); 335 } 336 337 /** 338 * {@inheritDoc} 339 */ 340 @Override getLogOutput()341 public ILeveledLogOutput getLogOutput() { 342 return (ILeveledLogOutput) getConfigurationObject(LOGGER_TYPE_NAME); 343 } 344 345 /** 346 * {@inheritDoc} 347 */ 348 @Override getLogSaver()349 public ILogSaver getLogSaver() { 350 return (ILogSaver) getConfigurationObject(LOG_SAVER_TYPE_NAME); 351 } 352 353 /** {@inheritDoc} */ 354 @Override getRetryDecision()355 public IRetryDecision getRetryDecision() { 356 return (IRetryDecision) getConfigurationObject(RETRY_DECISION_TYPE_NAME); 357 } 358 359 /** 360 * {@inheritDoc} 361 */ 362 @SuppressWarnings("unchecked") 363 @Override getMultiTargetPreparers()364 public List<IMultiTargetPreparer> getMultiTargetPreparers() { 365 return (List<IMultiTargetPreparer>) getConfigurationObjectList(MULTI_PREPARER_TYPE_NAME); 366 } 367 368 /** {@inheritDoc} */ 369 @SuppressWarnings("unchecked") 370 @Override getMultiPreTargetPreparers()371 public List<IMultiTargetPreparer> getMultiPreTargetPreparers() { 372 return (List<IMultiTargetPreparer>) 373 getConfigurationObjectList(MULTI_PRE_TARGET_PREPARER_TYPE_NAME); 374 } 375 376 /** 377 * {@inheritDoc} 378 */ 379 @SuppressWarnings("unchecked") 380 @Override getSystemStatusCheckers()381 public List<ISystemStatusChecker> getSystemStatusCheckers() { 382 return (List<ISystemStatusChecker>) 383 getConfigurationObjectList(SYSTEM_STATUS_CHECKER_TYPE_NAME); 384 } 385 386 /** 387 * {@inheritDoc} 388 */ 389 @SuppressWarnings("unchecked") 390 @Override getTestInvocationListeners()391 public List<ITestInvocationListener> getTestInvocationListeners() { 392 return (List<ITestInvocationListener>) getConfigurationObjectList( 393 RESULT_REPORTER_TYPE_NAME); 394 } 395 396 /** {@inheritDoc} */ 397 @SuppressWarnings("unchecked") 398 @Override getMetricCollectors()399 public List<IMetricCollector> getMetricCollectors() { 400 return (List<IMetricCollector>) 401 getConfigurationObjectList(DEVICE_METRICS_COLLECTOR_TYPE_NAME); 402 } 403 404 @SuppressWarnings("unchecked") 405 @Override getPostProcessors()406 public List<IPostProcessor> getPostProcessors() { 407 return (List<IPostProcessor>) getConfigurationObjectList(METRIC_POST_PROCESSOR_TYPE_NAME); 408 } 409 410 /** {@inheritDoc} */ 411 @Override getCommandOptions()412 public ICommandOptions getCommandOptions() { 413 return (ICommandOptions) getConfigurationObject(CMD_OPTIONS_TYPE_NAME); 414 } 415 416 /** {@inheritDoc} */ 417 @Override getConfigurationDescription()418 public ConfigurationDescriptor getConfigurationDescription() { 419 return (ConfigurationDescriptor) 420 getConfigurationObject(CONFIGURATION_DESCRIPTION_TYPE_NAME); 421 } 422 423 /** {@inheritDoc} */ 424 @SuppressWarnings("unchecked") 425 @Override getDeviceRequirements()426 public IDeviceSelection getDeviceRequirements() { 427 notAllowedInMultiMode("getDeviceRequirements"); 428 return ((List<IDeviceConfiguration>)getConfigurationObjectList(DEVICE_NAME)) 429 .get(0).getDeviceRequirements(); 430 } 431 432 /** 433 * {@inheritDoc} 434 */ 435 @SuppressWarnings("unchecked") 436 @Override getDeviceOptions()437 public TestDeviceOptions getDeviceOptions() { 438 notAllowedInMultiMode("getDeviceOptions"); 439 return ((List<IDeviceConfiguration>)getConfigurationObjectList(DEVICE_NAME)) 440 .get(0).getDeviceOptions(); 441 } 442 443 /** 444 * {@inheritDoc} 445 */ 446 @Override getConfigurationObjectList(String typeName)447 public List<?> getConfigurationObjectList(String typeName) { 448 return mConfigMap.get(typeName); 449 } 450 451 /** 452 * {@inheritDoc} 453 */ 454 @SuppressWarnings("unchecked") 455 @Override getDeviceConfigByName(String nameDevice)456 public IDeviceConfiguration getDeviceConfigByName(String nameDevice) { 457 for (IDeviceConfiguration deviceHolder : 458 (List<IDeviceConfiguration>)getConfigurationObjectList(DEVICE_NAME)) { 459 if (deviceHolder.getDeviceName().equals(nameDevice)) { 460 return deviceHolder; 461 } 462 } 463 return null; 464 } 465 466 /** 467 * {@inheritDoc} 468 */ 469 @SuppressWarnings("unchecked") 470 @Override getDeviceConfig()471 public List<IDeviceConfiguration> getDeviceConfig() { 472 return (List<IDeviceConfiguration>)getConfigurationObjectList(DEVICE_NAME); 473 } 474 475 /** {@inheritDoc} */ 476 @SuppressWarnings("unchecked") 477 @Override getCoverageOptions()478 public CoverageOptions getCoverageOptions() { 479 return (CoverageOptions) getConfigurationObject(COVERAGE_OPTIONS_TYPE_NAME); 480 } 481 482 /** 483 * {@inheritDoc} 484 */ 485 @Override getConfigurationObject(String typeName)486 public Object getConfigurationObject(String typeName) { 487 List<?> configObjects = getConfigurationObjectList(typeName); 488 if (configObjects == null) { 489 return null; 490 } 491 ObjTypeInfo typeInfo = getObjTypeMap().get(typeName); 492 if (typeInfo != null && typeInfo.mIsListSupported) { 493 throw new IllegalStateException( 494 String.format( 495 "Wrong method call for type %s. Used getConfigurationObject() for a " 496 + "config object that is stored as a list", 497 typeName)); 498 } 499 if (configObjects.size() != 1) { 500 throw new IllegalStateException(String.format( 501 "Attempted to retrieve single object for %s, but %d are present", 502 typeName, configObjects.size())); 503 } 504 return configObjects.get(0); 505 } 506 507 /** {@inheritDoc} */ 508 @Override getAllConfigurationObjectsOfType(String configType)509 public Collection<Object> getAllConfigurationObjectsOfType(String configType) { 510 Collection<Object> objectsCopy = new ArrayList<Object>(); 511 if (doesBuiltInObjSupportMultiDevice(configType)) { 512 for (IDeviceConfiguration deviceConfig : getDeviceConfig()) { 513 objectsCopy.addAll(deviceConfig.getAllObjectOfType(configType)); 514 } 515 } else { 516 List<?> configObjects = getConfigurationObjectList(configType); 517 if (configObjects != null) { 518 objectsCopy.addAll(configObjects); 519 } 520 } 521 return objectsCopy; 522 } 523 524 /** 525 * Return a copy of all config objects 526 */ getAllConfigurationObjects()527 private Collection<Object> getAllConfigurationObjects() { 528 return getAllConfigurationObjects(null, true); 529 } 530 531 /** Return a copy of all config objects that are not disabled via {@link IDisableable}. */ getAllNonDisabledConfigurationObjects()532 private Collection<Object> getAllNonDisabledConfigurationObjects() { 533 return getAllConfigurationObjects(null, false); 534 } 535 536 /** 537 * Return a copy of all config objects, minus the object configuration of the type specified. 538 * Returns all the config objects if param is null. 539 */ getAllConfigurationObjects( String excludedConfigName, boolean includeDisabled)540 private Collection<Object> getAllConfigurationObjects( 541 String excludedConfigName, boolean includeDisabled) { 542 Collection<Object> objectsCopy = new ArrayList<Object>(); 543 for (Entry<String, List<Object>> entryList : mConfigMap.entrySet()) { 544 if (excludedConfigName != null && excludedConfigName.equals(entryList.getKey())) { 545 continue; 546 } 547 if (includeDisabled) { 548 objectsCopy.addAll(entryList.getValue()); 549 } else { 550 for (Object o : entryList.getValue()) { 551 if (o instanceof IDisableable && ((IDisableable) o).isDisabled()) { 552 continue; 553 } 554 objectsCopy.add(o); 555 } 556 } 557 } 558 return objectsCopy; 559 } 560 561 /** 562 * Creates an OptionSetter which is appropriate for setting options on all objects which 563 * will be returned by {@link #getAllConfigurationObjects}. 564 */ createOptionSetter()565 private OptionSetter createOptionSetter() throws ConfigurationException { 566 return new OptionSetter(getAllConfigurationObjects()); 567 } 568 569 /** 570 * Injects an option value into the set of configuration objects. 571 * 572 * Uses provided arguments as is and fails if arguments have invalid format or 573 * provided ambiguously, e.g. {@code optionKey} argument is provided for non-map option, 574 * or the value for an option of integer type cannot be parsed as an integer number. 575 * 576 * @param optionSetter setter to use for the injection 577 * @param optionName name of the option 578 * @param optionKey map key, if the option is of map type 579 * @param optionValue value of the option or map value, if the option is of map type 580 * @param source source of the option 581 * @throws ConfigurationException if option value cannot be injected 582 */ internalInjectOptionValue(OptionSetter optionSetter, String optionName, String optionKey, String optionValue, String source)583 private void internalInjectOptionValue(OptionSetter optionSetter, String optionName, 584 String optionKey, String optionValue, String source) throws ConfigurationException { 585 if (optionSetter == null) { 586 throw new IllegalArgumentException("optionSetter cannot be null"); 587 } 588 589 // Set all fields that match this option name / key 590 List<FieldDef> affectedFields = optionSetter.setOptionValue( 591 optionName, optionKey, optionValue); 592 593 boolean requiredForRerun = false; 594 // Update the source for each affected field 595 for (FieldDef field : affectedFields) { 596 requiredForRerun |= field.field.getAnnotation(Option.class).requiredForRerun(); 597 if (requiredForRerun) { 598 // Only need to check if the option is required for rerun once if it's set to true. 599 break; 600 } 601 } 602 603 if (requiredForRerun) { 604 OptionDef optionDef = new OptionDef(optionName, optionKey, optionValue, source, null); 605 getConfigurationDescription().addRerunOption(optionDef); 606 } 607 } 608 609 /** 610 * Injects an option value into the set of configuration objects. 611 * 612 * If the option to be set is of map type, an attempt to parse {@code optionValue} argument 613 * into key-value pair is made. In this case {@code optionValue} must have an equal sign 614 * separating a key and a value (e.g. my_key=my_value). 615 * In case a key or a value themselves contain an equal sign, this equal sign in them 616 * must be escaped using a backslash (e.g. a\=b=y\=z). 617 * 618 * @param optionSetter setter to use for the injection 619 * @param optionName name of the option 620 * @param optionValue value of the option 621 * @throws ConfigurationException if option value cannot be injected 622 */ internalInjectOptionValue(OptionSetter optionSetter, String optionName, String optionValue)623 private void internalInjectOptionValue(OptionSetter optionSetter, String optionName, 624 String optionValue) throws ConfigurationException { 625 // Cannot continue without optionSetter 626 if (optionSetter == null) { 627 throw new IllegalArgumentException("optionSetter cannot be null"); 628 } 629 630 // If the option is not a map, then the key is null... 631 if (!optionSetter.isMapOption(optionName)) { 632 internalInjectOptionValue(optionSetter, optionName, null, optionValue, null); 633 return; 634 } 635 636 // ..., otherwise try to parse the value to retrieve the key 637 String[] parts = OPTION_KEY_VALUE_PATTERN.split(optionValue); 638 if (parts.length != 2) { 639 throw new ConfigurationException(String.format( 640 "option '%s' has an invalid format for value %s:w", 641 optionName, optionValue)); 642 } 643 internalInjectOptionValue(optionSetter, optionName, 644 parts[0].replace("\\\\=", "="), parts[1].replace("\\\\=", "="), null); 645 } 646 647 /** 648 * {@inheritDoc} 649 */ 650 @Override injectOptionValue(String optionName, String optionValue)651 public void injectOptionValue(String optionName, String optionValue) 652 throws ConfigurationException { 653 internalInjectOptionValue(createOptionSetter(), optionName, optionValue); 654 } 655 656 /** 657 * {@inheritDoc} 658 */ 659 @Override injectOptionValue(String optionName, String optionKey, String optionValue)660 public void injectOptionValue(String optionName, String optionKey, String optionValue) 661 throws ConfigurationException { 662 internalInjectOptionValue(createOptionSetter(), optionName, optionKey, optionValue, null); 663 } 664 665 /** 666 * {@inheritDoc} 667 */ 668 @Override injectOptionValueWithSource(String optionName, String optionKey, String optionValue, String source)669 public void injectOptionValueWithSource(String optionName, String optionKey, String optionValue, 670 String source) throws ConfigurationException { 671 internalInjectOptionValue(createOptionSetter(), optionName, optionKey, optionValue, source); 672 } 673 674 /** 675 * {@inheritDoc} 676 */ 677 @Override injectOptionValues(List<OptionDef> optionDefs)678 public void injectOptionValues(List<OptionDef> optionDefs) throws ConfigurationException { 679 OptionSetter optionSetter = createOptionSetter(); 680 for (OptionDef optionDef : optionDefs) { 681 internalInjectOptionValue(optionSetter, optionDef.name, optionDef.key, optionDef.value, 682 optionDef.source); 683 } 684 } 685 686 /** {@inheritDoc} */ 687 @Override safeInjectOptionValues(List<OptionDef> optionDefs)688 public void safeInjectOptionValues(List<OptionDef> optionDefs) throws ConfigurationException { 689 OptionSetter optionSetter = createOptionSetter(); 690 for (OptionDef optionDef : optionDefs) { 691 try { 692 internalInjectOptionValue( 693 optionSetter, 694 optionDef.name, 695 optionDef.key, 696 optionDef.value, 697 optionDef.source); 698 } catch (ConfigurationException e) { 699 // Ignoring 700 } 701 } 702 } 703 704 /** 705 * Creates a shallow copy of this object. 706 */ 707 @Override clone()708 public Configuration clone() { 709 Configuration clone = new Configuration(getName(), getDescription()); 710 for (Map.Entry<String, List<Object>> entry : mConfigMap.entrySet()) { 711 if (DEVICE_NAME.equals(entry.getKey())) { 712 List<Object> newDeviceConfigList = new ArrayList<Object>(); 713 for (Object deviceConfig : entry.getValue()) { 714 IDeviceConfiguration config = ((IDeviceConfiguration) deviceConfig); 715 IDeviceConfiguration newDeviceConfig = config.clone(); 716 newDeviceConfigList.add(newDeviceConfig); 717 } 718 clone.setConfigurationObjectListNoThrow(entry.getKey(), newDeviceConfigList); 719 } else { 720 clone.setConfigurationObjectListNoThrow(entry.getKey(), entry.getValue()); 721 } 722 } 723 clone.setCommandLine(this.mCommandLine); 724 return clone; 725 } 726 727 /** {@inheritDoc} */ 728 @Override partialDeepClone(List<String> objectToDeepClone, IKeyStoreClient client)729 public IConfiguration partialDeepClone(List<String> objectToDeepClone, IKeyStoreClient client) 730 throws ConfigurationException { 731 Configuration clonedConfig = this.clone(); 732 List<String> objToDeepClone = new ArrayList<>(objectToDeepClone); 733 if (objectToDeepClone.contains(Configuration.DEVICE_NAME)) { 734 objToDeepClone.remove(Configuration.DEVICE_NAME); 735 objToDeepClone.addAll(getMultiDeviceSupportedTag()); 736 } 737 for (String objType : objToDeepClone) { 738 if (doesBuiltInObjSupportMultiDevice(objType)) { 739 for (int i = 0; i < clonedConfig.getDeviceConfig().size(); i++) { 740 IDeviceConfiguration deepCopyConfig = clonedConfig.getDeviceConfig().get(i); 741 List<?> listOfType = 742 cloneListTFObject(deepCopyConfig.getAllObjectOfType(objType)); 743 clonedConfig.getDeviceConfig().get(i).removeObjectType(objType); 744 for (Object o : listOfType) { 745 clonedConfig.getDeviceConfig().get(i).addSpecificConfig(o); 746 } 747 } 748 } else { 749 clonedConfig.setConfigurationObjectList( 750 objType, 751 cloneListTFObject(clonedConfig.getConfigurationObjectList(objType))); 752 } 753 } 754 return clonedConfig; 755 } 756 cloneListTFObject(List<?> objects)757 private List<?> cloneListTFObject(List<?> objects) throws ConfigurationException { 758 List<Object> copiedList = new ArrayList<>(); 759 for (Object o : objects) { 760 copiedList.add(cloneTFobject(o)); 761 } 762 return copiedList; 763 } 764 cloneTFobject(Object o)765 private Object cloneTFobject(Object o) throws ConfigurationException { 766 try { 767 Object clone = o.getClass().getConstructor().newInstance(); 768 OptionCopier.copyOptions(o, clone); 769 return clone; 770 } catch (InstantiationException 771 | IllegalAccessException 772 | IllegalArgumentException 773 | InvocationTargetException 774 | NoSuchMethodException 775 | SecurityException e) { 776 // Shouldn't happen, except in unit tests 777 throw new ConfigurationException(String.format("Failed to copy %s", o), e); 778 } 779 } 780 addToDefaultDeviceConfig(Object obj)781 private void addToDefaultDeviceConfig(Object obj) { 782 try { 783 getDeviceConfigByName(ConfigurationDef.DEFAULT_DEVICE_NAME).addSpecificConfig(obj); 784 } catch (ConfigurationException e) { 785 // should never happen 786 throw new IllegalArgumentException(e); 787 } 788 } 789 790 /** 791 * {@inheritDoc} 792 */ 793 @Override setBuildProvider(IBuildProvider provider)794 public void setBuildProvider(IBuildProvider provider) { 795 notAllowedInMultiMode("setBuildProvider"); 796 addToDefaultDeviceConfig(provider); 797 } 798 799 /** 800 * {@inheritDoc} 801 */ 802 @Override setTestInvocationListeners(List<ITestInvocationListener> listeners)803 public void setTestInvocationListeners(List<ITestInvocationListener> listeners) { 804 setConfigurationObjectListNoThrow(RESULT_REPORTER_TYPE_NAME, listeners); 805 } 806 807 /** {@inheritDoc} */ 808 @Override setDeviceMetricCollectors(List<IMetricCollector> collectors)809 public void setDeviceMetricCollectors(List<IMetricCollector> collectors) { 810 setConfigurationObjectListNoThrow(DEVICE_METRICS_COLLECTOR_TYPE_NAME, collectors); 811 } 812 813 /** {@inheritDoc} */ 814 @Override setPostProcessors(List<IPostProcessor> processors)815 public void setPostProcessors(List<IPostProcessor> processors) { 816 setConfigurationObjectListNoThrow(METRIC_POST_PROCESSOR_TYPE_NAME, processors); 817 } 818 819 /** 820 * {@inheritDoc} 821 */ 822 @Override setTestInvocationListener(ITestInvocationListener listener)823 public void setTestInvocationListener(ITestInvocationListener listener) { 824 setConfigurationObjectNoThrow(RESULT_REPORTER_TYPE_NAME, listener); 825 } 826 827 /** 828 * {@inheritDoc} 829 */ 830 @Override setDeviceConfig(IDeviceConfiguration deviceConfig)831 public void setDeviceConfig(IDeviceConfiguration deviceConfig) { 832 setConfigurationObjectNoThrow(DEVICE_NAME, deviceConfig); 833 } 834 835 /** 836 * {@inheritDoc} 837 */ 838 @Override setDeviceConfigList(List<IDeviceConfiguration> deviceConfigs)839 public void setDeviceConfigList(List<IDeviceConfiguration> deviceConfigs) { 840 setConfigurationObjectListNoThrow(DEVICE_NAME, deviceConfigs); 841 } 842 843 /** {@inheritDoc} */ 844 @Override setCoverageOptions(CoverageOptions coverageOptions)845 public void setCoverageOptions(CoverageOptions coverageOptions) { 846 setConfigurationObjectNoThrow(COVERAGE_OPTIONS_TYPE_NAME, coverageOptions); 847 } 848 849 /** 850 * {@inheritDoc} 851 */ 852 @Override setTest(IRemoteTest test)853 public void setTest(IRemoteTest test) { 854 setConfigurationObjectNoThrow(TEST_TYPE_NAME, test); 855 } 856 857 /** 858 * {@inheritDoc} 859 */ 860 @Override setTests(List<IRemoteTest> tests)861 public void setTests(List<IRemoteTest> tests) { 862 setConfigurationObjectListNoThrow(TEST_TYPE_NAME, tests); 863 } 864 865 /** 866 * {@inheritDoc} 867 */ 868 @Override setMultiTargetPreparers(List<IMultiTargetPreparer> multiTargPreps)869 public void setMultiTargetPreparers(List<IMultiTargetPreparer> multiTargPreps) { 870 setConfigurationObjectListNoThrow(MULTI_PREPARER_TYPE_NAME, multiTargPreps); 871 } 872 873 /** 874 * {@inheritDoc} 875 */ 876 @Override setMultiTargetPreparer(IMultiTargetPreparer multiTargPrep)877 public void setMultiTargetPreparer(IMultiTargetPreparer multiTargPrep) { 878 setConfigurationObjectNoThrow(MULTI_PREPARER_TYPE_NAME, multiTargPrep); 879 } 880 881 /** {@inheritDoc} */ 882 @Override setMultiPreTargetPreparers(List<IMultiTargetPreparer> multiPreTargPreps)883 public void setMultiPreTargetPreparers(List<IMultiTargetPreparer> multiPreTargPreps) { 884 setConfigurationObjectListNoThrow(MULTI_PRE_TARGET_PREPARER_TYPE_NAME, multiPreTargPreps); 885 } 886 887 /** {@inheritDoc} */ 888 @Override setMultiPreTargetPreparer(IMultiTargetPreparer multiPreTargPrep)889 public void setMultiPreTargetPreparer(IMultiTargetPreparer multiPreTargPrep) { 890 setConfigurationObjectNoThrow(MULTI_PRE_TARGET_PREPARER_TYPE_NAME, multiPreTargPrep); 891 } 892 893 /** 894 * {@inheritDoc} 895 */ 896 @Override setSystemStatusCheckers(List<ISystemStatusChecker> systemCheckers)897 public void setSystemStatusCheckers(List<ISystemStatusChecker> systemCheckers) { 898 setConfigurationObjectListNoThrow(SYSTEM_STATUS_CHECKER_TYPE_NAME, systemCheckers); 899 } 900 901 /** 902 * {@inheritDoc} 903 */ 904 @Override setSystemStatusChecker(ISystemStatusChecker systemChecker)905 public void setSystemStatusChecker(ISystemStatusChecker systemChecker) { 906 setConfigurationObjectNoThrow(SYSTEM_STATUS_CHECKER_TYPE_NAME, systemChecker); 907 } 908 909 /** {@inheritDoc} */ 910 @Override setLogOutput(ILeveledLogOutput logger)911 public void setLogOutput(ILeveledLogOutput logger) { 912 setConfigurationObjectNoThrow(LOGGER_TYPE_NAME, logger); 913 } 914 915 /** {@inheritDoc} */ 916 @Override setLogSaver(ILogSaver logSaver)917 public void setLogSaver(ILogSaver logSaver) { 918 setConfigurationObjectNoThrow(LOG_SAVER_TYPE_NAME, logSaver); 919 } 920 921 /** {@inheritDoc} */ 922 @Override setRetryDecision(IRetryDecision decisionRetry)923 public void setRetryDecision(IRetryDecision decisionRetry) { 924 setConfigurationObjectNoThrow(RETRY_DECISION_TYPE_NAME, decisionRetry); 925 } 926 927 /** Sets the {@link ConfigurationDescriptor} to be used in the configuration. */ setConfigurationDescriptor(ConfigurationDescriptor configDescriptor)928 private void setConfigurationDescriptor(ConfigurationDescriptor configDescriptor) { 929 setConfigurationObjectNoThrow(CONFIGURATION_DESCRIPTION_TYPE_NAME, configDescriptor); 930 } 931 932 /** {@inheritDoc} */ 933 @Override setDeviceRecovery(IDeviceRecovery recovery)934 public void setDeviceRecovery(IDeviceRecovery recovery) { 935 notAllowedInMultiMode("setDeviceRecovery"); 936 addToDefaultDeviceConfig(recovery); 937 } 938 939 /** 940 * {@inheritDoc} 941 */ 942 @Override setTargetPreparer(ITargetPreparer preparer)943 public void setTargetPreparer(ITargetPreparer preparer) { 944 notAllowedInMultiMode("setTargetPreparer"); 945 addToDefaultDeviceConfig(preparer); 946 } 947 948 /** {@inheritDoc} */ 949 @Override setTargetPreparers(List<ITargetPreparer> preparers)950 public void setTargetPreparers(List<ITargetPreparer> preparers) { 951 notAllowedInMultiMode("setTargetPreparers"); 952 getDeviceConfigByName(ConfigurationDef.DEFAULT_DEVICE_NAME).getTargetPreparers().clear(); 953 for (ITargetPreparer prep : preparers) { 954 addToDefaultDeviceConfig(prep); 955 } 956 } 957 958 /** 959 * {@inheritDoc} 960 */ 961 @Override setCommandOptions(ICommandOptions cmdOptions)962 public void setCommandOptions(ICommandOptions cmdOptions) { 963 setConfigurationObjectNoThrow(CMD_OPTIONS_TYPE_NAME, cmdOptions); 964 } 965 966 /** 967 * {@inheritDoc} 968 */ 969 @Override setDeviceRequirements(IDeviceSelection devRequirements)970 public void setDeviceRequirements(IDeviceSelection devRequirements) { 971 notAllowedInMultiMode("setDeviceRequirements"); 972 addToDefaultDeviceConfig(devRequirements); 973 } 974 975 /** 976 * {@inheritDoc} 977 */ 978 @Override setDeviceOptions(TestDeviceOptions devOptions)979 public void setDeviceOptions(TestDeviceOptions devOptions) { 980 notAllowedInMultiMode("setDeviceOptions"); 981 addToDefaultDeviceConfig(devOptions); 982 } 983 984 /** 985 * {@inheritDoc} 986 */ 987 @Override setConfigurationObject(String typeName, Object configObject)988 public synchronized void setConfigurationObject(String typeName, Object configObject) 989 throws ConfigurationException { 990 if (configObject == null) { 991 throw new IllegalArgumentException("configObject cannot be null"); 992 } 993 mConfigMap.remove(typeName); 994 addObject(typeName, configObject); 995 } 996 997 /** 998 * {@inheritDoc} 999 */ 1000 @Override setConfigurationObjectList(String typeName, List<?> configList)1001 public synchronized void setConfigurationObjectList(String typeName, List<?> configList) 1002 throws ConfigurationException { 1003 if (configList == null) { 1004 throw new IllegalArgumentException("configList cannot be null"); 1005 } 1006 mConfigMap.remove(typeName); 1007 mConfigMap.put(typeName, new ArrayList<Object>(1)); 1008 for (Object configObject : configList) { 1009 addObject(typeName, configObject); 1010 } 1011 } 1012 1013 /** {@inheritDoc} */ 1014 @Override isDeviceConfiguredFake(String deviceName)1015 public boolean isDeviceConfiguredFake(String deviceName) { 1016 IDeviceConfiguration deviceConfig = getDeviceConfigByName(deviceName); 1017 if (deviceConfig == null) { 1018 return false; 1019 } 1020 return deviceConfig.isFake(); 1021 } 1022 1023 /** 1024 * Adds a loaded object to this configuration. 1025 * 1026 * @param typeName the unique object type name of the configuration object 1027 * @param configObject the configuration object 1028 * @throws ConfigurationException if object was not the correct type 1029 */ addObject(String typeName, Object configObject)1030 private synchronized void addObject(String typeName, Object configObject) 1031 throws ConfigurationException { 1032 List<Object> objList = mConfigMap.get(typeName); 1033 if (objList == null) { 1034 objList = new ArrayList<Object>(1); 1035 mConfigMap.put(typeName, objList); 1036 } 1037 ObjTypeInfo typeInfo = getObjTypeMap().get(typeName); 1038 if (typeInfo != null && !typeInfo.mExpectedType.isInstance(configObject)) { 1039 throw new ConfigurationException(String.format( 1040 "The config object %s is not the correct type. Expected %s, received %s", 1041 typeName, typeInfo.mExpectedType.getCanonicalName(), 1042 configObject.getClass().getCanonicalName())); 1043 } 1044 if (typeInfo != null && !typeInfo.mIsListSupported && objList.size() > 0) { 1045 throw new ConfigurationException(String.format( 1046 "Only one config object allowed for %s, but multiple were specified.", 1047 typeName)); 1048 } 1049 objList.add(configObject); 1050 if (configObject instanceof IConfigurationReceiver) { 1051 ((IConfigurationReceiver) configObject).setConfiguration(this); 1052 } 1053 // Inject to object inside device holder too. 1054 if (configObject instanceof IDeviceConfiguration) { 1055 for (Object obj : ((IDeviceConfiguration) configObject).getAllObjects()) { 1056 if (obj instanceof IConfigurationReceiver) { 1057 ((IConfigurationReceiver) obj).setConfiguration(this); 1058 } 1059 } 1060 } 1061 } 1062 1063 /** 1064 * A wrapper around {@link #setConfigurationObject(String, Object)} that 1065 * will not throw {@link ConfigurationException}. 1066 * <p/> 1067 * Intended to be used in cases where its guaranteed that 1068 * <var>configObject</var> is the correct type. 1069 * 1070 * @param typeName 1071 * @param configObject 1072 */ setConfigurationObjectNoThrow(String typeName, Object configObject)1073 private void setConfigurationObjectNoThrow(String typeName, Object configObject) { 1074 try { 1075 setConfigurationObject(typeName, configObject); 1076 } catch (ConfigurationException e) { 1077 // should never happen 1078 throw new IllegalArgumentException(e); 1079 } 1080 } 1081 1082 /** 1083 * A wrapper around {@link #setConfigurationObjectList(String, List)} that 1084 * will not throw {@link ConfigurationException}. 1085 * <p/> 1086 * Intended to be used in cases where its guaranteed that 1087 * <var>configObject</var> is the correct type 1088 * 1089 * @param typeName 1090 * @param configList 1091 */ setConfigurationObjectListNoThrow(String typeName, List<?> configList)1092 private void setConfigurationObjectListNoThrow(String typeName, List<?> configList) { 1093 try { 1094 setConfigurationObjectList(typeName, configList); 1095 } catch (ConfigurationException e) { 1096 // should never happen 1097 throw new IllegalArgumentException(e); 1098 } 1099 } 1100 1101 /** 1102 * {@inheritDoc} 1103 */ 1104 @Override setOptionsFromCommandLineArgs(List<String> listArgs)1105 public List<String> setOptionsFromCommandLineArgs(List<String> listArgs) 1106 throws ConfigurationException { 1107 return setOptionsFromCommandLineArgs(listArgs, null); 1108 } 1109 1110 /** 1111 * {@inheritDoc} 1112 */ 1113 @Override setOptionsFromCommandLineArgs(List<String> listArgs, IKeyStoreClient keyStoreClient)1114 public List<String> setOptionsFromCommandLineArgs(List<String> listArgs, 1115 IKeyStoreClient keyStoreClient) 1116 throws ConfigurationException { 1117 // We get all the objects except the one describing the Configuration itself which does not 1118 // allow passing its option via command line. 1119 ArgsOptionParser parser = 1120 new ArgsOptionParser( 1121 getAllConfigurationObjects(CONFIGURATION_DESCRIPTION_TYPE_NAME, true)); 1122 if (keyStoreClient != null) { 1123 parser.setKeyStore(keyStoreClient); 1124 } 1125 try { 1126 return parser.parse(listArgs); 1127 } catch (ConfigurationException e) { 1128 Matcher m = CONFIG_EXCEPTION_PATTERN.matcher(e.getMessage()); 1129 if (!m.matches()) { 1130 throw e; 1131 } 1132 String optionName = m.group(1); 1133 try { 1134 // In case the option exists in the config descriptor, we change the error message 1135 // to be more specific about why the option is rejected. 1136 OptionSetter setter = new OptionSetter(getConfigurationDescription()); 1137 setter.getTypeForOption(optionName); 1138 } catch (ConfigurationException stillThrowing) { 1139 // Throw the original exception since it cannot be found at all. 1140 throw e; 1141 } 1142 throw new OptionNotAllowedException( 1143 String.format( 1144 "Option '%s' cannot be specified via " 1145 + "command line. Only in the configuration xml.", 1146 optionName)); 1147 } 1148 } 1149 1150 /** {@inheritDoc} */ 1151 @Override setBestEffortOptionsFromCommandLineArgs( List<String> listArgs, IKeyStoreClient keyStoreClient)1152 public List<String> setBestEffortOptionsFromCommandLineArgs( 1153 List<String> listArgs, IKeyStoreClient keyStoreClient) throws ConfigurationException { 1154 // We get all the objects except the one describing the Configuration itself which does not 1155 // allow passing its option via command line. 1156 ArgsOptionParser parser = 1157 new ArgsOptionParser( 1158 getAllConfigurationObjects(CONFIGURATION_DESCRIPTION_TYPE_NAME, true)); 1159 if (keyStoreClient != null) { 1160 parser.setKeyStore(keyStoreClient); 1161 } 1162 return parser.parseBestEffort(listArgs, /* Force continue */ true); 1163 } 1164 1165 /** 1166 * Outputs a command line usage help text for this configuration to given 1167 * printStream. 1168 * 1169 * @param out the {@link PrintStream} to use. 1170 * @throws ConfigurationException 1171 */ 1172 @Override printCommandUsage(boolean importantOnly, PrintStream out)1173 public void printCommandUsage(boolean importantOnly, PrintStream out) 1174 throws ConfigurationException { 1175 out.println(String.format("'%s' configuration: %s", getName(), getDescription())); 1176 out.println(); 1177 if (importantOnly) { 1178 out.println("Printing help for only the important options. " + 1179 "To see help for all options, use the --help-all flag"); 1180 out.println(); 1181 } 1182 for (Map.Entry<String, List<Object>> configObjectsEntry : mConfigMap.entrySet()) { 1183 for (Object configObject : configObjectsEntry.getValue()) { 1184 if (configObject instanceof IDeviceConfiguration) { 1185 // We expand the Device Config Object. 1186 for (Object subconfigObject : ((IDeviceConfiguration)configObject) 1187 .getAllObjects()) { 1188 printCommandUsageForObject(importantOnly, out, configObjectsEntry.getKey(), 1189 subconfigObject); 1190 } 1191 } else { 1192 printCommandUsageForObject(importantOnly, out, configObjectsEntry.getKey(), 1193 configObject); 1194 } 1195 } 1196 } 1197 } 1198 printCommandUsageForObject(boolean importantOnly, PrintStream out, String key, Object obj)1199 private void printCommandUsageForObject(boolean importantOnly, PrintStream out, String key, 1200 Object obj) throws ConfigurationException { 1201 String optionHelp = printOptionsForObject(importantOnly, key, obj); 1202 // only print help for object if optionHelp is non zero length 1203 if (optionHelp.length() > 0) { 1204 String classAlias = ""; 1205 if (obj.getClass().isAnnotationPresent(OptionClass.class)) { 1206 final OptionClass classAnnotation = obj.getClass().getAnnotation( 1207 OptionClass.class); 1208 classAlias = String.format("'%s' ", classAnnotation.alias()); 1209 } 1210 out.printf(" %s%s options:", classAlias, key); 1211 out.println(); 1212 out.print(optionHelp); 1213 out.println(); 1214 } 1215 } 1216 1217 /** 1218 * Prints out the available config options for given configuration object. 1219 * 1220 * @param importantOnly print only the important options 1221 * @param objectTypeName the config object type name. Used to generate more 1222 * descriptive error messages 1223 * @param configObject the config object 1224 * @return a {@link String} of option help text 1225 * @throws ConfigurationException 1226 */ printOptionsForObject(boolean importantOnly, String objectTypeName, Object configObject)1227 private String printOptionsForObject(boolean importantOnly, String objectTypeName, 1228 Object configObject) throws ConfigurationException { 1229 return ArgsOptionParser.getOptionHelp(importantOnly, configObject); 1230 } 1231 1232 /** 1233 * {@inheritDoc} 1234 */ 1235 @Override validateOptions()1236 public void validateOptions() throws ConfigurationException { 1237 ArgsOptionParser argsParser = new ArgsOptionParser(getAllNonDisabledConfigurationObjects()); 1238 argsParser.validateMandatoryOptions(); 1239 ICommandOptions options = getCommandOptions(); 1240 if (options.getShardCount() != null && options.getShardCount() < 1) { 1241 throw new ConfigurationException("a shard count must be a positive number"); 1242 } 1243 if (options.getShardIndex() != null 1244 && (options.getShardCount() == null || options.getShardIndex() < 0 1245 || options.getShardIndex() >= options.getShardCount())) { 1246 throw new ConfigurationException("a shard index must be in range [0, shard count)"); 1247 } 1248 } 1249 1250 /** {@inheritDoc} */ 1251 @Override resolveDynamicOptions(DynamicRemoteFileResolver resolver)1252 public void resolveDynamicOptions(DynamicRemoteFileResolver resolver) 1253 throws ConfigurationException, BuildRetrievalError { 1254 // Resolve regardless of sharding if we are in remote environment because we know that's 1255 // where the execution will occur. 1256 if (!isRemoteEnvironment()) { 1257 ICommandOptions options = getCommandOptions(); 1258 if (options.getShardCount() != null && options.getShardIndex() == null) { 1259 CLog.w("Skipping download due to local sharding detected."); 1260 return; 1261 } 1262 } 1263 1264 ArgsOptionParser argsParser = new ArgsOptionParser(getAllConfigurationObjects()); 1265 CLog.d("Resolve and download remote files from @Option"); 1266 // Setup and validate the GCS File paths 1267 mRemoteFiles.addAll(argsParser.validateRemoteFilePath(resolver)); 1268 } 1269 1270 /** Returns whether or not the environment of TF is a remote invocation. */ 1271 @VisibleForTesting isRemoteEnvironment()1272 protected boolean isRemoteEnvironment() { 1273 return SystemUtil.isRemoteEnvironment(); 1274 } 1275 1276 /** {@inheritDoc} */ 1277 @Override cleanConfigurationData()1278 public void cleanConfigurationData() { 1279 for (File file : mRemoteFiles) { 1280 FileUtil.recursiveDelete(file); 1281 } 1282 mRemoteFiles.clear(); 1283 } 1284 1285 /** {@inheritDoc} */ 1286 @Override addFilesToClean(Set<File> toBeCleaned)1287 public void addFilesToClean(Set<File> toBeCleaned) { 1288 mRemoteFiles.addAll(toBeCleaned); 1289 } 1290 1291 /** {@inheritDoc} */ 1292 @Override getFilesToClean()1293 public Set<File> getFilesToClean() { 1294 return mRemoteFiles; 1295 } 1296 1297 /** 1298 * {@inheritDoc} 1299 */ 1300 @Override dumpXml(PrintWriter output)1301 public void dumpXml(PrintWriter output) throws IOException { 1302 dumpXml(output, new ArrayList<String>()); 1303 } 1304 1305 /** {@inheritDoc} */ 1306 @Override dumpXml(PrintWriter output, List<String> excludeFilters)1307 public void dumpXml(PrintWriter output, List<String> excludeFilters) throws IOException { 1308 dumpXml(output, excludeFilters, true, true); 1309 } 1310 1311 /** {@inheritDoc} */ 1312 @Override dumpXml( PrintWriter output, List<String> excludeFilters, boolean printDeprecatedOptions, boolean printUnchangedOptions)1313 public void dumpXml( 1314 PrintWriter output, 1315 List<String> excludeFilters, 1316 boolean printDeprecatedOptions, 1317 boolean printUnchangedOptions) 1318 throws IOException { 1319 KXmlSerializer serializer = new KXmlSerializer(); 1320 serializer.setOutput(output); 1321 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); 1322 serializer.startDocument("UTF-8", null); 1323 serializer.startTag(null, ConfigurationUtil.CONFIGURATION_NAME); 1324 1325 for (IMultiTargetPreparer multiPreTargerPrep : getMultiPreTargetPreparers()) { 1326 ConfigurationUtil.dumpClassToXml( 1327 serializer, 1328 MULTI_PRE_TARGET_PREPARER_TYPE_NAME, 1329 multiPreTargerPrep, 1330 excludeFilters, 1331 printDeprecatedOptions, 1332 printUnchangedOptions); 1333 } 1334 1335 for (IMultiTargetPreparer multipreparer : getMultiTargetPreparers()) { 1336 ConfigurationUtil.dumpClassToXml( 1337 serializer, 1338 MULTI_PREPARER_TYPE_NAME, 1339 multipreparer, 1340 excludeFilters, 1341 printDeprecatedOptions, 1342 printUnchangedOptions); 1343 } 1344 1345 if (getDeviceConfig().size() > 1) { 1346 // Handle multi device. 1347 for (IDeviceConfiguration deviceConfig : getDeviceConfig()) { 1348 serializer.startTag(null, Configuration.DEVICE_NAME); 1349 serializer.attribute(null, "name", deviceConfig.getDeviceName()); 1350 if (deviceConfig.isFake()) { 1351 serializer.attribute(null, "isFake", "true"); 1352 } 1353 ConfigurationUtil.dumpClassToXml( 1354 serializer, 1355 BUILD_PROVIDER_TYPE_NAME, 1356 deviceConfig.getBuildProvider(), 1357 excludeFilters, 1358 printDeprecatedOptions, 1359 printUnchangedOptions); 1360 for (ITargetPreparer preparer : deviceConfig.getTargetPreparers()) { 1361 ConfigurationUtil.dumpClassToXml( 1362 serializer, 1363 TARGET_PREPARER_TYPE_NAME, 1364 preparer, 1365 excludeFilters, 1366 printDeprecatedOptions, 1367 printUnchangedOptions); 1368 } 1369 ConfigurationUtil.dumpClassToXml( 1370 serializer, 1371 DEVICE_RECOVERY_TYPE_NAME, 1372 deviceConfig.getDeviceRecovery(), 1373 excludeFilters, 1374 printDeprecatedOptions, 1375 printUnchangedOptions); 1376 ConfigurationUtil.dumpClassToXml( 1377 serializer, 1378 DEVICE_REQUIREMENTS_TYPE_NAME, 1379 deviceConfig.getDeviceRequirements(), 1380 excludeFilters, 1381 printDeprecatedOptions, 1382 printUnchangedOptions); 1383 ConfigurationUtil.dumpClassToXml( 1384 serializer, 1385 DEVICE_OPTIONS_TYPE_NAME, 1386 deviceConfig.getDeviceOptions(), 1387 excludeFilters, 1388 printDeprecatedOptions, 1389 printUnchangedOptions); 1390 serializer.endTag(null, Configuration.DEVICE_NAME); 1391 } 1392 } else { 1393 // Put single device tags 1394 ConfigurationUtil.dumpClassToXml( 1395 serializer, 1396 BUILD_PROVIDER_TYPE_NAME, 1397 getBuildProvider(), 1398 excludeFilters, 1399 printDeprecatedOptions, 1400 printUnchangedOptions); 1401 for (ITargetPreparer preparer : getTargetPreparers()) { 1402 ConfigurationUtil.dumpClassToXml( 1403 serializer, 1404 TARGET_PREPARER_TYPE_NAME, 1405 preparer, 1406 excludeFilters, 1407 printDeprecatedOptions, 1408 printUnchangedOptions); 1409 } 1410 ConfigurationUtil.dumpClassToXml( 1411 serializer, 1412 DEVICE_RECOVERY_TYPE_NAME, 1413 getDeviceRecovery(), 1414 excludeFilters, 1415 printDeprecatedOptions, 1416 printUnchangedOptions); 1417 ConfigurationUtil.dumpClassToXml( 1418 serializer, 1419 DEVICE_REQUIREMENTS_TYPE_NAME, 1420 getDeviceRequirements(), 1421 excludeFilters, 1422 printDeprecatedOptions, 1423 printUnchangedOptions); 1424 ConfigurationUtil.dumpClassToXml( 1425 serializer, 1426 DEVICE_OPTIONS_TYPE_NAME, 1427 getDeviceOptions(), 1428 excludeFilters, 1429 printDeprecatedOptions, 1430 printUnchangedOptions); 1431 } 1432 for (IRemoteTest test : getTests()) { 1433 ConfigurationUtil.dumpClassToXml( 1434 serializer, 1435 TEST_TYPE_NAME, 1436 test, 1437 excludeFilters, 1438 printDeprecatedOptions, 1439 printUnchangedOptions); 1440 } 1441 ConfigurationUtil.dumpClassToXml( 1442 serializer, 1443 CONFIGURATION_DESCRIPTION_TYPE_NAME, 1444 getConfigurationDescription(), 1445 excludeFilters, 1446 printDeprecatedOptions, 1447 printUnchangedOptions); 1448 ConfigurationUtil.dumpClassToXml( 1449 serializer, 1450 LOGGER_TYPE_NAME, 1451 getLogOutput(), 1452 excludeFilters, 1453 printDeprecatedOptions, 1454 printUnchangedOptions); 1455 ConfigurationUtil.dumpClassToXml( 1456 serializer, 1457 LOG_SAVER_TYPE_NAME, 1458 getLogSaver(), 1459 excludeFilters, 1460 printDeprecatedOptions, 1461 printUnchangedOptions); 1462 for (ITestInvocationListener listener : getTestInvocationListeners()) { 1463 ConfigurationUtil.dumpClassToXml( 1464 serializer, 1465 RESULT_REPORTER_TYPE_NAME, 1466 listener, 1467 excludeFilters, 1468 printDeprecatedOptions, 1469 printUnchangedOptions); 1470 } 1471 ConfigurationUtil.dumpClassToXml( 1472 serializer, 1473 CMD_OPTIONS_TYPE_NAME, 1474 getCommandOptions(), 1475 excludeFilters, 1476 printDeprecatedOptions, 1477 printUnchangedOptions); 1478 1479 for (IMetricCollector collector : getMetricCollectors()) { 1480 ConfigurationUtil.dumpClassToXml( 1481 serializer, 1482 DEVICE_METRICS_COLLECTOR_TYPE_NAME, 1483 collector, 1484 excludeFilters, 1485 printDeprecatedOptions, 1486 printUnchangedOptions); 1487 } 1488 1489 for (IPostProcessor processor : getPostProcessors()) { 1490 ConfigurationUtil.dumpClassToXml( 1491 serializer, 1492 METRIC_POST_PROCESSOR_TYPE_NAME, 1493 processor, 1494 excludeFilters, 1495 printDeprecatedOptions, 1496 printUnchangedOptions); 1497 } 1498 1499 for (ISystemStatusChecker checker : getSystemStatusCheckers()) { 1500 ConfigurationUtil.dumpClassToXml( 1501 serializer, 1502 SYSTEM_STATUS_CHECKER_TYPE_NAME, 1503 checker, 1504 excludeFilters, 1505 printDeprecatedOptions, 1506 printUnchangedOptions); 1507 } 1508 1509 ConfigurationUtil.dumpClassToXml( 1510 serializer, 1511 SANBOX_OPTIONS_TYPE_NAME, 1512 getConfigurationObject(SANBOX_OPTIONS_TYPE_NAME), 1513 excludeFilters, 1514 printDeprecatedOptions, 1515 printUnchangedOptions); 1516 ConfigurationUtil.dumpClassToXml( 1517 serializer, 1518 RETRY_DECISION_TYPE_NAME, 1519 getRetryDecision(), 1520 excludeFilters, 1521 printDeprecatedOptions, 1522 printUnchangedOptions); 1523 ConfigurationUtil.dumpClassToXml( 1524 serializer, 1525 COVERAGE_OPTIONS_TYPE_NAME, 1526 getCoverageOptions(), 1527 excludeFilters, 1528 printDeprecatedOptions, 1529 printUnchangedOptions); 1530 1531 serializer.endTag(null, ConfigurationUtil.CONFIGURATION_NAME); 1532 serializer.endDocument(); 1533 } 1534 } 1535