🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# 解析配置文件 Cts框架分为9大部分: cmd_options:命令行接受的参数选项,command包中。 device_requirements:设备相关要求,device包中 device_options:设备参数要求,device包中 builde_provider:版本提供者,build包中 target_preparer:预置条件准备,targetprep包中 test:测试类型,存在testtype包中 device_recovery:任务执行过程中设备异常后的设备恢复,device包中 logger:日志系统,log包中 result_reporter:结果统计报告,result包中 每一种任务都要配置好这9个组件,如果不配置,框架就采用自己的默认配置,但也是相当于配置了这几项,所以cts框架的核心在Configuration中。 ~~~ 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(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()); setTestInvocationListener(new TextResultReporter()); } ~~~ 在getObjTypeMap()可以看出来cts框架为这9个组件定义的接口,只要你的类实现了这个接口,cts就可以通过反射机制找到你的类。Configuration类的构造方法中设置了这几个接口默认的实现类,如果你没有配置其他的替代类,cts会默认去加载这些实现类。这些类都是实现了上面方法中对应关系的接口,才能被设置成配置项的。 ![](https://box.kancloud.cn/2016-01-09_56911dcb19de0.jpg) 所有关于类的配置,cts只认你在Configuration类接口的实现类,你如果写了一个类没有继承9大类中的一个接口,你添加了,也会报错的。从上图可以看出,cts默认配置了上面几项,没有配置的采用默认的。这个讲完了,我们就从文章开头的代码开始将它是如何一步一步完成这些配置的。 # Debug 以[上一篇文章](http://blog.csdn.net/itfootball/article/details/40210811)中的解析配置文件代码开始: ~~~ IConfiguration config = getConfigFactory().createConfigurationFromArgs(args); ~~~ ![](https://box.kancloud.cn/2016-01-09_56911dcb6d18c.jpg) 调用的是ConfigurationFactory.createConfigurationFromArgs方法: ~~~ /** * {@inheritDoc} */ @Override public IConfiguration createConfigurationFromArgs(String[] arrayArgs) throws ConfigurationException { List<String> listArgs = new ArrayList<String>(arrayArgs.length); IConfiguration config = internalCreateConfigurationFromArgs(arrayArgs, listArgs); config.setOptionsFromCommandLineArgs(listArgs); return config; } ~~~ 该方法又调用了internalCreateConfigurationFromArgs方法,传入的是参数run cts --plan Signature字符串数组和一个空字符串。 ~~~ private IConfiguration internalCreateConfigurationFromArgs(String[] arrayArgs, List<String> optionArgsRef) throws ConfigurationException { if (arrayArgs.length == 0) { throw new ConfigurationException("Configuration to run was not specified"); } optionArgsRef.addAll(Arrays.asList(arrayArgs)); // first arg is config name final String configName = optionArgsRef.remove(0); ConfigurationDef configDef = getConfigurationDef(configName, false); return configDef.createConfiguration(); } ~~~ 该方法中取出第一个参数cts然后传入getConfigurationDef方法中,获取ConfigurationDef对象。首先看看ConfigurationDef对象是什么?很简单,一个ConfigurationDef代表一个配置文件。 ![](https://box.kancloud.cn/2016-01-09_56911dcc2d3ea.jpg) ~~~ public class ConfigurationDef { /** a map of object type names to config object class name(s). */ private final Map<String, List<String>> mObjectClassMap; /** a list of option name/value pairs. */ private final List<OptionDef> mOptionList; /** a cache of the frequency of every classname */ private final Map<String, Integer> mClassFrequency; static class OptionDef { final String name; final String key; final String value; OptionDef(String optionName, String optionValue) { this(optionName, null, optionValue); } OptionDef(String optionName, String optionKey, String optionValue) { this.name = optionName; this.key = optionKey; this.value = optionValue; } } /** the unique name of the configuration definition */ private final String mName; /** a short description of the configuration definition */ private String mDescription = ""; public ConfigurationDef(String name) { mName = name; // use LinkedHashMap to keep objects in same order they were added. mObjectClassMap = new LinkedHashMap<String, List<String>>(); mOptionList = new ArrayList<OptionDef>(); mClassFrequency = new HashMap<String, Integer>(); } ~~~ 主要关注下面2个属性: mObjectClassMap:保存9大组件的,例如上面的build_provider、device_recovery、test、logger、result_reporter这些标签对应的类都保持该map对象中。 mOptionList:保存option标签的值,例如上面的enable-root的值。 现在进入debug模式验证一下是不是用上面两个属性保存的。将断点打在ConfigurationDef configDef = getConfigurationDef(configName, false);上,删除其他断点。重启debug。按F6跳到下一行,来看一下Variables一栏中值: ![](https://box.kancloud.cn/2016-01-09_56911dcc7f3c3.jpg) 可以看到mObjectClassMap的值为: ~~~ {build_provider=[com.android.cts.tradefed.build.CtsBuildProvider], device_recovery=[com.android.tradefed.device.WaitDeviceRecovery], test=[com.android.cts.tradefed.testtype.CtsTest], logger=[com.android.tradefed.log.FileLogger], result_reporter=[com.android.cts.tradefed.result.CtsXmlResultReporter, com.android.cts.tradefed.result.IssueReporter]} ~~~ 和配置文件里的是一样的,mOptionList也保存了xml中option对应的值: ![](https://box.kancloud.cn/2016-01-09_56911dcd00312.jpg) 然后通过configDef.createConfiguration();来创建Configuration对象。所以我们进入该方法它是如何将原生的替换为cts.xml中配置的: ![](https://box.kancloud.cn/2016-01-09_56911dcd2b073.jpg) 将debug点跳转到for循环中,然后在Variables一栏中分别查看this中的mObjectClassMap和config中mConfigMap的值: ![](https://box.kancloud.cn/2016-01-09_56911dcd94dd0.jpg)![](https://box.kancloud.cn/2016-01-09_56911dcdc1200.jpg) 可以看到mConfigMap是原生的,也就是Configuration的构造方法中添加的接口类,mObjectClassMap保存就是cts.xml配置文件里的接口类。然后根据Map的key值不能重复的特性用cts.xml配置的接口类替换掉原生的接口类。所以把断点打在返回语句return上,按F8看结果: ![](https://box.kancloud.cn/2016-01-09_56911dcdeedb8.jpg) 这个时候可以看到mConfigMap的值变了: ~~~ {cmd_options=[com.android.tradefed.command.CommandOptions@7b2390], device_requirements=[com.android.tradefed.device.DeviceSelectionOptions@5bd18b], device_options=[com.android.tradefed.device.TestDeviceOptions@1a29fe], target_preparer=[com.android.tradefed.targetprep.StubTargetPreparer@1beaa92], build_provider=[com.android.cts.tradefed.build.CtsBuildProvider@c4f317], device_recovery=[com.android.tradefed.device.WaitDeviceRecovery@33967b], test=[com.android.cts.tradefed.testtype.CtsTest@7263d0], logger=[com.android.tradefed.log.FileLogger@1a7422e], result_reporter=[com.android.cts.tradefed.result.CtsXmlResultReporter@1750ae1, com.android.cts.tradefed.result.IssueReporter@b279f3]} ~~~ 9大项全部配置完成。