TestNG源代码分析:依赖管理的实现
TestNG源代码分析:依赖管理的实现
2018-03-19
1 背景
当case之间有依赖关系,有依赖关系的case,它们的执行顺序是有限制的。TestNG提供了依赖管理功能
2 基础理论
这个执行顺序可以用拓扑排序算法实现。
只要是有向无环图就能被拓扑排序,拓扑排序维基典型实现算法:
L ← Empty list that will contain the sorted elements
S ← Set of all nodes with no incoming edges
while S is non-empty do
remove a node n from S
insert n into L
foreach node m with an edge e from n to m do
remove edge e from thegraph
if m has no other incoming edges then
insert m into S
if graph has edges then
return error (graph has at least one cycle)
else
return L (a topologically sorted order)
TestNG依赖管理功能
- dependsOnMethods
@Test
public void serverStartedOk(){}
@Test(dependsOnMethods={ "serverStartedOk" })
public void method1() {}
- dependsOnGroups
@Test(groups = { "init" })
public void serverStartedOk(){}
@Test(groups = { "init" })
public void initEnvironment(){}
@Test(dependsOnGroups = { "init.*})
public void method1() {}
3 TestNG依赖管理源代码
如何找到依赖原理源代码?
通过报错,即即故意制造一个循环依赖,然后看stack trace:
错误代码如下:
@Test(dependsOnMethods={"MethodA"})
public void MethodA(){}
Stack Trace
at org.testng.internal.Graph.topologicalSort(Graph.java:143)
at org.testng.internal.MethodHelper.topologicalSort(MethodHelper.java:231)
org.testng.TestNGException:
The following methods have cyclic dependencies:
Sample.MethodA()[pri:0, instance:Demo.Sample@50c87b21] at org.testng.internal.Graph.topologicalSort(Graph.java:143)
at org.testng.internal.MethodHelper.topologicalSort(MethodHelper.java:231)
at org.testng.internal.MethodHelper.sortMethods(MethodHelper.java:287)
at org.testng.internal.MethodHelper.collectAndOrderMethods(MethodHelper.java:60)
at org.testng.TestRunner.initMethods(TestRunner.java:464)
at org.testng.TestRunner.init(TestRunner.java:247)
at org.testng.TestRunner.init(TestRunner.java:217)
at org.testng.TestRunner.<init>(TestRunner.java:169)
at org.testng.remote.support.RemoteTestNG6_9_10$1.newTestRunner(RemoteTestNG6_9_10.java:28)
at org.testng.remote.support.RemoteTestNG6_9_10$DelegatingTestRunnerFactory.newTestRunner(RemoteTestNG6_9_10.java:61)
at org.testng.SuiteRunner$ProxyTestRunnerFactory.newTestRunner(SuiteRunner.java:594)
at org.testng.SuiteRunner.init(SuiteRunner.java:168)
at org.testng.SuiteRunner.<init>(SuiteRunner.java:117)
at org.testng.TestNG.createSuiteRunner(TestNG.java:1339)
at org.testng.TestNG.createSuiteRunners(TestNG.java:1326)
at org.testng.TestNG.runSuitesLocally(TestNG.java:1180)
at org.testng.TestNG.runSuites(TestNG.java:1104)
at org.testng.TestNG.run(TestNG.java:1076)
at org.testng.remote.AbstractRemoteTestNG.run(AbstractRemoteTestNG.java:126)
at org.testng.remote.RemoteTestNG.initAndRun(RemoteTestNG.java:152)
at org.testng.remote.RemoteTestNG.main(RemoteTestNG.java:57)
4 源代码分析
//MethodHelper.topologicalSort
private static Graph<ITestNGMethod> topologicalSort(ITestNGMethod[] methods, List<ITestNGMethod> sequentialList, List<ITestNGMethod> parallelList)
//Graph.topologicalSort
public void topologicalSort()
注意:
- methods变量包括所有的要跑的用例和用例依赖的用例(依赖用例依赖的用例也包括)
- 依赖管理代码在Graph类中实现,MethodHelper.topologicalSort方法会调用Graph类中的方法
Graph类有三个变量
//m_nodes:放的是所有的节点的引用
private Map<T, Node<T>> m_nodes = Maps.newLinkedHashMap();
//m_independentNodes:所有独立节点的引用,这样的节点(用例)可并发运行
private List<T> m_strictlySortedNodes = null;
//m_strictlySortedNodes:经过严格排序的非独立节点
private Map<T, Node<T>> m_independentNodes = null;
Graph.topologicalSort方法实现依赖节点的拓扑排序
//m_nodes:放的是所有的节点(用例)的引用
//m_independentNodes:所有独立节点的引用
//m_strictlySortedNodes:经过严格排序的非独立节点
public void topologicalSort() {
ppp("================ SORTING");
//m_strictlySortedNodes集合: 最后的结果放到这个集合中
m_strictlySortedNodes=Lists.newArrayList();
initializeIndependentNodes(); //nodes2集合: 非独立的节点
List<Node<T>>nodes2 =Lists.newArrayList(); //1 创建非独立节点集合,即存在依赖关系的方法的集合
for (Node<T> n :getNodes()) { //getNodes()返回m_nodes
if (!isIndependent(n.getObject())){// 判断该节点是否独立,如果不独立的话,添加到nodes2中
ppp("ADDING FOR SORT: " +n.getObject());
nodes2.add(n.clone()); //使用的是clone方法来进行对象的复制,一般不推荐使用clone方法,参见Effective Java Item 11
}
else {
ppp("SKIPPING INDEPENDENT NODE" + n);
}
} //2 将非独立的节点集合排序,为了让属于同类中的方法在集合中的位置近一些,从而在调用的顺序上能够相邻一些
Collections.sort(nodes2); //3 进行拓扑排序,如果发现有循环依赖,马上抛出异常
while (!nodes2.isEmpty()) {
Node<T> node =findNodeWithNoPredecessors(nodes2); // 从nodes2集合中找到没有前驱节点的节点
if (null == node) { // 如果没有找到节点,那么创建一个Tarjan对象来得到一个cycle
List<T> cycle =newTarjan<T>(this,nodes2.get(0).getObject()).getCycle(); // 这里实现了Tarjan算法,用来得到环的路径信息
StringBuffer sb = new StringBuffer(); //在非并发环境中应该尽量使用StringBuilder
sb.append("The following methodshave cyclic dependencies:\n");
for (T m : cycle) {
sb.append(m).append("\n");
}
throw newTestNGException(sb.toString());
}
else { //如果找到了,将这个没有任何前驱节点的节点放到结果结合中,然后从nodes2集合中删除该节点
m_strictlySortedNodes.add(node.getObject());
removeFromNodes(nodes2, node);
}
}
ppp("===============DONESORTING");
if (m_verbose) {
dumpSortedNodes();
}
}
调用方法
private void initializeIndependentNodes() {
if (null == m_independentNodes) {
List<Node<T>> list = Lists.newArrayList(m_nodes.values());
Collections.sort(list);
m_independentNodes = Maps.newLinkedHashMap();
for (Node<T> node : list) {
m_independentNodes.put(node.getObject(), node);
}
}
}
private Collection<Node<T>> getNodes() {
return m_nodes.values();
}
MethodHelper.topologicalSort方法,调用Graph方法区分独立节点和依赖节点,并调用toplogicalSort进行依赖节点拓扑排序
private static Graph<ITestNGMethod> topologicalSort(ITestNGMethod[] methods, List<ITestNGMethod> sequentialList, List<ITestNGMethod> parallelList) {
Graph<ITestNGMethod> result = new Graph<>();
if (methods.length == 0) {
return result; //如果传入的methods数组长度为0,直接返回了空的graph
}
// 区分出要顺序执行的用例(依赖用例)和可并行的用例(独立用例)
for (ITestNGMethod m : methods) {
result.addNode(m); //对于每个方法instance,添加到graph中
List<ITestNGMethod> predecessors = Lists.newArrayList(); //获得该方法依赖的方法
String[] methodsDependedUpon = m.getMethodsDependedUpon();
String[] groupsDependedUpon = m.getGroupsDependedUpon();
if (methodsDependedUpon.length > 0) {
ITestNGMethod[] methodsNamed =
MethodHelper.findDependedUponMethods(m, methods);
for (ITestNGMethod pred : methodsNamed) {
predecessors.add(pred);
}
}
if (groupsDependedUpon.length > 0) {
for (String group : groupsDependedUpon) {
ITestNGMethod[] methodsThatBelongToGroup =
MethodGroupsHelper.findMethodsThatBelongToGroup(m, methods, group);
for (ITestNGMethod pred : methodsThatBelongToGroup) {
predecessors.add(pred);
}
}
}
for (ITestNGMethod predecessor : predecessor s) {
result.addPredecessor(m, predecessor); //将依赖方法加入graph中
}
}
result.topologicalSort();
sequentialList.addAll(result.getStrictlySortedNodes());
parallelList.addAll(result.getIndependentNodes());
return result;
}
调用方法
public void addNode(T tm) {
ppp("ADDING NODE " + tm + " " + tm.hashCode());
m_nodes.put(tm, new Node<>(tm));
// Initially, all the nodes are put in the independent list as well
}
public void addPredecessor(T tm, T predecessor) {
Node<T> node = findNode(tm);
if (null == node) {
throw new TestNGException("Non-existing node: " + tm);
}
else {
node.addPredecessor(predecessor);
addNeighbor(tm, predecessor);
// Remove these two nodes from the independent list
initializeIndependentNodes();
m_independentNodes.remove(predecessor);
m_independentNodes.remove(tm);
ppp(" REMOVED " + predecessor + " FROM INDEPENDENT OBJECTS");
}
}
public Set<T> getIndependentNodes() {
return m_independentNodes.keySet();
}
public List<T> getStrictlySortedNodes() {
return m_strictlySortedNodes;
}
参考
[1] TestNG源代码分析 --- 依赖管理的实现(一)
[2] TestNG源代码分析 --- 依赖管理的实现(二)
TestNG源代码分析:依赖管理的实现的更多相关文章
- hostapd源代码分析(三):管理帧的收发和处理
hostapd源代码分析(三):管理帧的收发和处理 原文链接:http://blog.csdn.net/qq_21949217/article/details/46004379 这篇文章我来讲解一下h ...
- redis 源代码分析(一) 内存管理
一,redis内存管理介绍 redis是一个基于内存的key-value的数据库,其内存管理是很重要的,为了屏蔽不同平台之间的差异,以及统计内存占用量等,redis对内存分配函数进行了一层封装,程序中 ...
- Fragment事务管理源代码分析
转载请标明出处:http://blog.csdn.net/shensky711/article/details/53132952 本文出自: [HansChen的博客] 概述 在Fragment使用中 ...
- cocos2d-x 源代码分析 : Ref (CCObject) 源代码分析 cocos2d-x内存管理策略
从源代码版本号3.x.转载请注明 cocos2d-x 总的文件夹的源代码分析: http://blog.csdn.net/u011225840/article/details/31743129 1.R ...
- 读书摘要观后感与总结:《Glibc内存管理:ptmalloc2源代码分析》
更新中 在Linux平台下做漏洞利用的时候,针对于Heap部分总是有些不求甚解,下面开个博文来记录下<Glibc内存管理:ptmalloc2源代码分析>这本书的读后感和收获,一些简单的点将 ...
- android-plugmgr源代码分析
android-plugmgr是一个Android插件加载框架,它最大的特点就是对插件不需要进行任何约束.关于这个类库的介绍见作者博客,市面上也有一些插件加载框架,但是感觉没有这个好.在这篇文章中,我 ...
- Gradle系列教程之依赖管理(转)
转自Lippi-浮生志 :http://ezlippi.com/blog/2015/05/gradle-dependency-management.html 这一章我将介绍Gradle对依赖管理的强大 ...
- Hadoop源代码分析
http://wenku.baidu.com/link?url=R-QoZXhc918qoO0BX6eXI9_uPU75whF62vFFUBIR-7c5XAYUVxDRX5Rs6QZR9hrBnUdM ...
- Gradle实战教程之依赖管理
这是从我个人网站中复制过来的,原文地址:http://coolshell.info/blog/2015/05/gradle-dependency-management.html,转载请注明出处. 简要 ...
随机推荐
- win10搭建tensorflow-gpu环境
昨天辛苦的配了GPU环境,记录一下防止以后还需要用到. 我配GPU的目的是用tensorflow的gpu来加速 不用ubuntu是因为一来不习惯,二来我不会配ubuntu的扩展显示器,就更不习惯了,习 ...
- Alpha冲刺随笔三:第三天
课程名称:软件工程1916|W(福州大学) 作业要求:项目Alpha冲刺(十天冲刺) 团队名称:葫芦娃队 作业目标:在十天冲刺里对每天的任务进行总结. 随笔汇总:https://www.cnblogs ...
- 磁盘修复工具TestDisk
磁盘修复工具TestDisk TestDisk一款免费的数据的恢复工具,可以用于还原丢失的磁盘分区,恢复磁盘驱动引导功能.它还能检测磁盘损坏的原因,如病毒感染.人为损坏.恶意软件等.该工具采用文本菜单 ...
- 添加js,css 版本号?v= hash
node_modules设置 a.打开 node_modules\gulp-rev\index.js 第144行 manifest[originalFile] = revisionedFile; 更新 ...
- Nowcoder 提高 Day1
比赛链接 A 中位数(前缀和 二分) 额,确实没想到逼近... 然后写了n^2log的暴力,还CE了 只需要判断是否能有大于当前mid的中位数就好 这显然是可以二分的 代码 #include<b ...
- [CF776D]The Door Problem
思路: 并查集维护每个开关的状态on[i]和off[i] .假设灯L由开关S1和S2控制.如果开关是亮的,则S1和S2的状态相反:如果开关是灭的,则S1和S2的状态相同.当一个开关状态已知时,可以得知 ...
- jenkins使用总结
这个教程很全面,可以参考 https://www.jianshu.com/p/dceaa1c7bb49
- 二分图带权匹配 KM算法与费用流模型建立
[二分图带权匹配与最佳匹配] 什么是二分图的带权匹配?二分图的带权匹配就是求出一个匹配集合,使得集合中边的权值之和最大或最小.而二分图的最佳匹配则一定为完备匹配,在此基础上,才要求匹配的边权值之和最大 ...
- C语言中常用的字符串操作函数
程序开头要声明 #include <string.h> 函数名: stpcpy 功 能: 拷贝一个字符串到另一个 用 法: char *stpcpy(char *destin, char ...
- 20172302 《Java软件结构与数据结构》第七周学习总结
2018年学习总结博客总目录:第一周 第二周 第三周 第四周 第五周 第六周 第七周 教材学习内容总结 第11章 二叉查找树 1.二叉查找树是一种含有附加属性的二叉树,该属性即其左孩子小于父节点,而父 ...