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