blob: 166e90a53d8105dfab4f6af38d7a124ed0463fc2 [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tradefed.config;
import com.android.tradefed.build.IBuildProvider;
import com.android.tradefed.build.StubBuildProvider;
import com.android.tradefed.command.CommandOptions;
import com.android.tradefed.command.ICommandOptions;
import com.android.tradefed.device.DeviceSelectionOptions;
import com.android.tradefed.device.IDeviceRecovery;
import com.android.tradefed.device.IDeviceSelection;
import com.android.tradefed.device.TestDeviceOptions;
import com.android.tradefed.device.WaitDeviceRecovery;
import com.android.tradefed.log.ILeveledLogOutput;
import com.android.tradefed.log.StdoutLogger;
import com.android.tradefed.result.FileSystemLogSaver;
import com.android.tradefed.result.ILogSaver;
import com.android.tradefed.result.ITestInvocationListener;
import com.android.tradefed.result.TextResultReporter;
import com.android.tradefed.targetprep.ITargetPreparer;
import com.android.tradefed.targetprep.StubTargetPreparer;
import com.android.tradefed.testtype.IRemoteTest;
import com.android.tradefed.testtype.StubTest;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* A concrete {@link IConfiguration} implementation that stores the loaded config objects in a map
*/
public class Configuration implements IConfiguration {
// type names for built in configuration objects
public static final String BUILD_PROVIDER_TYPE_NAME = "build_provider";
public static final String TARGET_PREPARER_TYPE_NAME = "target_preparer";
public static final String TEST_TYPE_NAME = "test";
public static final String DEVICE_RECOVERY_TYPE_NAME = "device_recovery";
public static final String LOGGER_TYPE_NAME = "logger";
public static final String LOG_SAVER_TYPE_NAME = "log_saver";
public static final String RESULT_REPORTER_TYPE_NAME = "result_reporter";
public static final String CMD_OPTIONS_TYPE_NAME = "cmd_options";
public static final String DEVICE_REQUIREMENTS_TYPE_NAME = "device_requirements";
public static final String DEVICE_OPTIONS_TYPE_NAME = "device_options";
private static Map<String, ObjTypeInfo> sObjTypeMap = null;
/** Mapping of config object type name to config objects. */
private Map<String, List<Object>> mConfigMap;
private final String mName;
private final String mDescription;
/**
* Container struct for built-in config object type
*/
private static class ObjTypeInfo {
final Class<?> mExpectedType;
/** true if a list (ie many objects in a single config) are supported for this type */
final boolean mIsListSupported;
ObjTypeInfo(Class<?> expectedType, boolean isList) {
mExpectedType = expectedType;
mIsListSupported = isList;
}
}
/**
* Determine if given config object type name is a built in object
*
* @param typeName the config object type name
* @return <code>true</code> if name is a built in object type
*/
static boolean isBuiltInObjType(String typeName) {
return getObjTypeMap().containsKey(typeName);
}
private static synchronized Map<String, ObjTypeInfo> getObjTypeMap() {
if (sObjTypeMap == null) {
sObjTypeMap = new HashMap<String, ObjTypeInfo>();
sObjTypeMap.put(BUILD_PROVIDER_TYPE_NAME, new ObjTypeInfo(IBuildProvider.class, false));
sObjTypeMap.put(TARGET_PREPARER_TYPE_NAME, new ObjTypeInfo(ITargetPreparer.class, true));
sObjTypeMap.put(TEST_TYPE_NAME, new ObjTypeInfo(IRemoteTest.class, true));
sObjTypeMap.put(DEVICE_RECOVERY_TYPE_NAME, new ObjTypeInfo(IDeviceRecovery.class, false));
sObjTypeMap.put(LOGGER_TYPE_NAME, new ObjTypeInfo(ILeveledLogOutput.class, false));
sObjTypeMap.put(LOG_SAVER_TYPE_NAME, new ObjTypeInfo(ILogSaver.class, false));
sObjTypeMap.put(RESULT_REPORTER_TYPE_NAME, new ObjTypeInfo(ITestInvocationListener.class,
true));
sObjTypeMap.put(CMD_OPTIONS_TYPE_NAME, new ObjTypeInfo(ICommandOptions.class,
false));
sObjTypeMap.put(DEVICE_REQUIREMENTS_TYPE_NAME, new ObjTypeInfo(IDeviceSelection.class,
false));
sObjTypeMap.put(DEVICE_OPTIONS_TYPE_NAME, new ObjTypeInfo(TestDeviceOptions.class,
false));
}
return sObjTypeMap;
}
/**
* Creates an {@link Configuration} with default config objects.
*/
public Configuration(String name, String description) {
mName = name;
mDescription = description;
mConfigMap = new LinkedHashMap<String, List<Object>>();
setCommandOptions(new CommandOptions());
setDeviceRequirements(new DeviceSelectionOptions());
setDeviceOptions(new TestDeviceOptions());
setBuildProvider(new StubBuildProvider());
setTargetPreparer(new StubTargetPreparer());
setTest(new StubTest());
setDeviceRecovery(new WaitDeviceRecovery());
setLogOutput(new StdoutLogger());
setLogSaver(new FileSystemLogSaver()); // FileSystemLogSaver saves to tmp by default.
setTestInvocationListener(new TextResultReporter());
}
/**
* @return the name of this {@link Configuration}
*/
public String getName() {
return mName;
}
/**
* @return a short user readable description this {@link Configuration}
*/
public String getDescription() {
return mDescription;
}
/**
* {@inheritDoc}
*/
@Override
public IBuildProvider getBuildProvider() {
return (IBuildProvider)getConfigurationObject(BUILD_PROVIDER_TYPE_NAME);
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
@Override
public List<ITargetPreparer> getTargetPreparers() {
return (List<ITargetPreparer>)getConfigurationObjectList(TARGET_PREPARER_TYPE_NAME);
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
@Override
public List<IRemoteTest> getTests() {
return (List<IRemoteTest>)getConfigurationObjectList(TEST_TYPE_NAME);
}
/**
* {@inheritDoc}
*/
@Override
public IDeviceRecovery getDeviceRecovery() {
return (IDeviceRecovery)getConfigurationObject(DEVICE_RECOVERY_TYPE_NAME);
}
/**
* {@inheritDoc}
*/
@Override
public ILeveledLogOutput getLogOutput() {
return (ILeveledLogOutput)getConfigurationObject(LOGGER_TYPE_NAME);
}
/**
* {@inheritDoc}
*/
@Override
public ILogSaver getLogSaver() {
return (ILogSaver)getConfigurationObject(LOG_SAVER_TYPE_NAME);
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
@Override
public List<ITestInvocationListener> getTestInvocationListeners() {
return (List<ITestInvocationListener>)getConfigurationObjectList(RESULT_REPORTER_TYPE_NAME);
}
/**
* {@inheritDoc}
*/
@Override
public ICommandOptions getCommandOptions() {
return (ICommandOptions)getConfigurationObject(CMD_OPTIONS_TYPE_NAME);
}
/**
* {@inheritDoc}
*/
@Override
public IDeviceSelection getDeviceRequirements() {
return (IDeviceSelection)getConfigurationObject(DEVICE_REQUIREMENTS_TYPE_NAME);
}
/**
* {@inheritDoc}
*/
@Override
public TestDeviceOptions getDeviceOptions() {
return (TestDeviceOptions)getConfigurationObject(DEVICE_OPTIONS_TYPE_NAME);
}
/**
* {@inheritDoc}
*/
@Override
public List<?> getConfigurationObjectList(String typeName) {
return mConfigMap.get(typeName);
}
/**
* {@inheritDoc}
*/
@Override
public Object getConfigurationObject(String typeName) {
List<?> configObjects = getConfigurationObjectList(typeName);
if (configObjects == null) {
return null;
}
ObjTypeInfo typeInfo = getObjTypeMap().get(typeName);
if (typeInfo != null && typeInfo.mIsListSupported) {
throw new IllegalStateException(String.format("Wrong method call. " +
"Used getConfigurationObject() for a config object that is stored as a list",
typeName));
}
if (configObjects.size() != 1) {
throw new IllegalStateException(String.format(
"Attempted to retrieve single object for %s, but %d are present",
typeName, configObjects.size()));
}
return configObjects.get(0);
}
/**
* Return a copy of all config objects
*/
private Collection<Object> getAllConfigurationObjects() {
Collection<Object> objectsCopy = new ArrayList<Object>();
for (List<Object> objectList : mConfigMap.values()) {
objectsCopy.addAll(objectList);
}
return objectsCopy;
}
/**
* {@inheritDoc}
*/
@Override
public void injectOptionValue(String optionName, String optionValue)
throws ConfigurationException {
OptionSetter optionSetter = new OptionSetter(getAllConfigurationObjects());
optionSetter.setOptionValue(optionName, optionValue);
}
/**
* {@inheritDoc}
*/
@Override
public void injectOptionValue(String optionName, String optionKey, String optionValue)
throws ConfigurationException {
OptionSetter optionSetter = new OptionSetter(getAllConfigurationObjects());
optionSetter.setOptionMapValue(optionName, optionKey, optionValue);
}
/**
* Creates a shallow copy of this object.
*/
@Override
public Configuration clone() {
Configuration clone = new Configuration(getName(), getDescription());
for (Map.Entry<String, List<Object>> entry : mConfigMap.entrySet()) {
clone.setConfigurationObjectListNoThrow(entry.getKey(), entry.getValue());
}
return clone;
}
/**
* {@inheritDoc}
*/
@Override
public void setBuildProvider(IBuildProvider provider) {
setConfigurationObjectNoThrow(BUILD_PROVIDER_TYPE_NAME, provider);
}
/**
* {@inheritDoc}
*/
@Override
public void setTestInvocationListeners(List<ITestInvocationListener> listeners) {
setConfigurationObjectListNoThrow(RESULT_REPORTER_TYPE_NAME, listeners);
}
/**
* {@inheritDoc}
*/
@Override
public void setTestInvocationListener(ITestInvocationListener listener) {
setConfigurationObjectNoThrow(RESULT_REPORTER_TYPE_NAME, listener);
}
/**
* {@inheritDoc}
*/
@Override
public void setTest(IRemoteTest test) {
setConfigurationObjectNoThrow(TEST_TYPE_NAME, test);
}
/**
* {@inheritDoc}
*/
@Override
public void setTests(List<IRemoteTest> tests) {
setConfigurationObjectListNoThrow(TEST_TYPE_NAME, tests);
}
/**
* {@inheritDoc}
*/
@Override
public void setLogOutput(ILeveledLogOutput logger) {
setConfigurationObjectNoThrow(LOGGER_TYPE_NAME, logger);
}
/**
* {@inheritDoc}
*/
@Override
public void setLogSaver(ILogSaver logSaver) {
setConfigurationObjectNoThrow(LOG_SAVER_TYPE_NAME, logSaver);
}
/**
* {@inheritDoc}
*/
@Override
public void setDeviceRecovery(IDeviceRecovery recovery) {
setConfigurationObjectNoThrow(DEVICE_RECOVERY_TYPE_NAME, recovery);
}
/**
* {@inheritDoc}
*/
@Override
public void setTargetPreparer(ITargetPreparer preparer) {
setConfigurationObjectNoThrow(TARGET_PREPARER_TYPE_NAME, preparer);
}
/**
* {@inheritDoc}
*/
@Override
public void setCommandOptions(ICommandOptions cmdOptions) {
setConfigurationObjectNoThrow(CMD_OPTIONS_TYPE_NAME, cmdOptions);
}
/**
* {@inheritDoc}
*/
@Override
public void setDeviceRequirements(IDeviceSelection devRequirements) {
setConfigurationObjectNoThrow(DEVICE_REQUIREMENTS_TYPE_NAME, devRequirements);
}
/**
* {@inheritDoc}
*/
@Override
public void setDeviceOptions(TestDeviceOptions devOptions) {
setConfigurationObjectNoThrow(DEVICE_OPTIONS_TYPE_NAME, devOptions);
}
/**
* {@inheritDoc}
*/
@Override
public void setConfigurationObject(String typeName, Object configObject)
throws ConfigurationException {
if (configObject == null) {
throw new IllegalArgumentException("configObject cannot be null");
}
mConfigMap.remove(typeName);
addObject(typeName, configObject);
}
/**
* {@inheritDoc}
*/
@Override
public void setConfigurationObjectList(String typeName, List<?> configList)
throws ConfigurationException {
if (configList == null) {
throw new IllegalArgumentException("configList cannot be null");
}
mConfigMap.remove(typeName);
for (Object configObject : configList) {
addObject(typeName, configObject);
}
}
/**
* Adds a loaded object to this configuration.
*
* @param typeName the unique object type name of the configuration object
* @param configObject the configuration object
* @throws ConfigurationException if object was not the correct type
*/
private void addObject(String typeName, Object configObject) throws ConfigurationException {
List<Object> objList = mConfigMap.get(typeName);
if (objList == null) {
objList = new ArrayList<Object>(1);
mConfigMap.put(typeName, objList);
}
ObjTypeInfo typeInfo = getObjTypeMap().get(typeName);
if (typeInfo != null && !typeInfo.mExpectedType.isInstance(configObject)) {
throw new ConfigurationException(String.format(
"The config object %s is not the correct type. Expected %s, received %s",
typeName, typeInfo.mExpectedType.getCanonicalName(),
configObject.getClass().getCanonicalName()));
}
if (typeInfo != null && !typeInfo.mIsListSupported && objList.size() > 0) {
throw new ConfigurationException(String.format(
"Only one config object allowed for %s, but multiple were specified.",
typeName));
}
objList.add(configObject);
if (configObject instanceof IConfigurationReceiver) {
((IConfigurationReceiver)configObject).setConfiguration(this);
}
}
/**
* A wrapper around {@link #setConfigurationObject(String, Object)} that will not throw
* {@link ConfigurationException}.
* <p/>
* Intended to be used in cases where its guaranteed that <var>configObject</var> is the
* correct type.
*
* @param typeName
* @param configObject
*/
private void setConfigurationObjectNoThrow(String typeName, Object configObject) {
try {
setConfigurationObject(typeName, configObject);
} catch (ConfigurationException e) {
// should never happen
throw new IllegalArgumentException(e);
}
}
/**
* A wrapper around {@link #setConfigurationObjectList(String, List)} that will not throw
* {@link ConfigurationException}.
* <p/>
* Intended to be used in cases where its guaranteed that <var>configObject</var> is the
* correct type
*
* @param typeName
* @param configObject
*/
private void setConfigurationObjectListNoThrow(String typeName, List<?> configList) {
try {
setConfigurationObjectList(typeName, configList);
} catch (ConfigurationException e) {
// should never happen
throw new IllegalArgumentException(e);
}
}
/**
* {@inheritDoc}
*/
@Override
public void setOptionsFromCommandLineArgs(List<String> listArgs) throws ConfigurationException {
ArgsOptionParser parser = new ArgsOptionParser(getAllConfigurationObjects());
List<String> unprocessedArgs = parser.parse(listArgs);
if (unprocessedArgs.size() > 0) {
throw new ConfigurationException(String.format(
"Invalid arguments provided. Unprocessed arguments: %s", unprocessedArgs));
}
}
/**
* Outputs a command line usage help text for this configuration to given printStream.
*
* @param out the {@link PrintStream} to use.
* @throws {@link ConfigurationException}
*/
@Override
public void printCommandUsage(boolean importantOnly, PrintStream out)
throws ConfigurationException {
out.println(String.format("'%s' configuration: %s", getName(), getDescription()));
out.println();
if (importantOnly) {
out.println("Printing help for only the important options. " +
"To see help for all options, use the --help-all flag");
out.println();
}
for (Map.Entry<String, List<Object>> configObjectsEntry : mConfigMap.entrySet()) {
for (Object configObject : configObjectsEntry.getValue()) {
String optionHelp = printOptionsForObject(importantOnly,
configObjectsEntry.getKey(), configObject);
// only print help for object if optionHelp is non zero length
if (optionHelp.length() > 0) {
String classAlias = "";
if (configObject.getClass().isAnnotationPresent(OptionClass.class)) {
final OptionClass classAnnotation = configObject.getClass().getAnnotation(
OptionClass.class);
classAlias = String.format("'%s' ", classAnnotation.alias());
}
out.printf(" %s%s options:", classAlias, configObjectsEntry.getKey());
out.println();
out.print(optionHelp);
out.println();
}
}
}
}
/**
* Prints out the available config options for given configuration object.
*
* @param importantOnly print only the important options
* @param objectTypeName the config object type name. Used to generate more descriptive error
* messages
* @param configObject the config object
* @return a {@link String} of option help text
* @throws ConfigurationException
*/
private String printOptionsForObject(boolean importantOnly, String objectTypeName,
Object configObject) throws ConfigurationException {
return ArgsOptionParser.getOptionHelp(importantOnly, configObject);
}
/**
* {@inheritDoc}
*/
@Override
public void validateOptions() throws ConfigurationException {
new ArgsOptionParser(getAllConfigurationObjects()).validateMandatoryOptions();
}
}