(24)-case的组织
最后更新于:2022-04-01 06:55:14
# UML图
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-09_56911de0380c7.jpg)
# 解释
上图涉及5个类:`ddmlib`提供的`TestIdentifier`类,`cts`自定义的`IRemoteTest`接口、`ITestPackageDef`接口、`TestFilter`类和`TestPackage`类。具体意义如下:
## TestIdentifier
`ddmlib`为我们提供的属于`instrumentation`测试的`case`实体类,里面有2个属性,case的类名和方法名。这样我们就可以找到一个case。
## IRemoteTest
一种测试类型的接口定义,该测试类型可以直接将结果发送到监听器,让其处理。所以测试runner部分只管撒了欢的跑,抓结果的模块是单独存在的。
## ITestPackageDef
case相关信息的容器,信息从哪里来?举个例子,写`uiautomator case`的人都了解,我们写的case都要打成jar包,随着jar一起生成的还有一个与jar包名一样的xml文件,我们的信息就是从该xml文件里来,里面定义了jar包的标识符,测试包名等信息。
## TestFilter
case的过滤器类,定义一些过滤的条件。里面有2个集合保存了要被过滤掉的case的类名或`TestIdentifier`对象,以及1个特殊的类名和方法名,这个属性默认是为`null`,一般情况下需要你去设值,如果不设置,那么这个条件就不作为过滤的条件,来具体看看删选的处理代码:
~~~
public Collection<TestIdentifier> filter(Collection<TestIdentifier > tests) {
List<TestIdentifier> filteredTests = new ArrayList<TestIdentifier>(tests.size());
for (TestIdentifier test : tests) {
if (mIncludedClass != null && !test.getClassName().equals(mIncludedClass)) {
// skip
continue;
}
if (mIncludedMethod != null && !test.getTestName().equals(mIncludedMethod)) {
// skip
continue;
}
if (mExcludedClasses.contains(test.getClassName())) {
// skip
continue;
}
if (mExcludedTests.contains(test)) {
// skip
continue;
}
filteredTests.add(test);
}
Collections.sort(filteredTests, new TestIdComparator());
return filteredTests;
}
~~~
代码块很简单,随着条件分支一步一步过滤,最后剩下来的case添加到新集合中返回。
## TestPackage
包含上面3个实体对象(除了`TestFilter`),一个TestPackage代表了一个case包(jar或者apk等)相关的所有信息,例如一个`uiautomator`写出的jar包,那么一个jar包就需要定义一个`TestPackage`对象:该jar包包含的case集合,该jar包执行的测试类型,以及jar一些相关属性信息。有了这些就足够了,case就可以执行run的动作了。cts执行的时候只需要得到TestPackage对象集合(代表一个个的case包对象),就可以遍历得到所有要执行的case。
# 具体执行过程
cts中是以plan来定义要跑的case包集合,plan则是一个xml文件,里面定义了一个或多个case包的标识信息。这样去case的目录下就可以找到case包以及case包的xml配置文件。当我们传入一个plan进入cts后,发生了什么?(一下方法都是CtsTest中的方法)
## buildTestsToRun方法
当测试执行的时候,cts会先调用该方法获得所有TestPackage对象,上面说过,获得这个就足够了。具体该实现:
~~~
private List<TestPackage> buildTestsToRun() {
List<TestPackage> testPkgList = new LinkedList<TestPackage>();
try {
// 获得testcases目录下所有的xml文件解析出来的case包对象
ITestPackageRepo testRepo = createTestCaseRepo();
// 得到本次plan所需跑的case
Collection<ITestPackageDef> testPkgDefs = getTestPackagesToRun(testRepo);
for (ITestPackageDef testPkgDef : testPkgDefs) {
addTestPackage(testPkgList, testPkgDef);
}
if (testPkgList.isEmpty()) {
Log.logAndDisplay(LogLevel.WARN, LOG_TAG, "No tests to run");
}
} catch (FileNotFoundException e) {
throw new IllegalArgumentException("failed to find CTS plan file", e);
} catch (ParseException e) {
throw new IllegalArgumentException("failed to parse CTS plan file", e);
} catch (ConfigurationException e) {
throw new IllegalArgumentException("failed to process arguments", e);
}
return testPkgList;
}
~~~
首先会去将固定路径下的(cts根目录下的repository\testcases)下所有xml文件解析出来,这些xml文件都是和case包一一对应的,你不能去解析jar包或者apk包吧,所以需要一个case包配置文件的存在,这样我们读取xml的信息就可以得到相关的信息。然后我们要筛选出本次plan需要跑的case包。
## getTestPackagesToRun方法
该方法里有4条分支,每条分支代表不同的执行任务的标识。
`1`以plan名定义的任务
`2`.以case包的uri定义的集合所定义的任务
`3`.以class定义的(一个类中的所有case)任务
`4`.以sessionID(cts为之前跑过的任务都定义了一个session)所定义的任务,这个是重跑之前的任务。
我们来只看第一种,以plan方式启动的任务。
~~~
private Collection<ITestPackageDef> getTestPackagesToRun(ITestPackageRepo testRepo) throws ParseException, FileNotFoundException, ConfigurationException {
// use LinkedHashSet to have predictable iteration order
Set<ITestPackageDef> testPkgDefs = new LinkedHashSet<ITestPackageDef>();
if (mPlanName != null) {
Log.i(LOG_TAG, String.format("Executing CTS test plan %s", mPlanName));
File ctsPlanFile = mCtsBuildHelper.getTestPlanFile(mPlanName);
ITestPlan plan = createPlan(mPlanName);
plan.parse(createXmlStream(ctsPlanFile));
for (String uri : plan.getTestUris()) {
if (!mExcludedPackageNames.contains(uri)) {
ITestPackageDef testPackage = testRepo.getTestPackage(uri);
testPackage.setExcludedTestFilter(plan.getExcludedTestFilter(uri));
testPkgDefs.add(testPackage);
}
}
} else if (mPackageNames.size() > 0) {
......
} else if (mClassName != null) {
......
} else if (mContinueSessionId != null) {
......
} else {
// should never get here - was checkFields() not called?
throw new IllegalStateException("nothing to run?");
}
return testPkgDefs;
}
~~~
该分支中,首先根据plan名找到所有xml文件,然后解析xml文件,得到case。这样我们就得到了ITestPackageDef对象,遍历得到所有这样的对象,然后将该对象集合返回。回到上面的`buildTestsToRun()`中,然后根据返回的集合元素创建`TestPackage`对象集合,这样我们的处理过程就完成了。