多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
# UML图 ![](https://box.kancloud.cn/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`对象集合,这样我们的处理过程就完成了。