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