testng源码阅读之二

来源:互联网 发布:linux关闭selinux命令 编辑:程序博客网 时间:2024/05/12 22:33

最近一直没有时间写博客。在做一个相当于外包的项目。因为项目终于卖出去了,忽悠到人用了。而且最近释放了一个资源。
最近一直忙于测试。

testng执行核心的几个类:
这里写图片描述

xxxWorker、xxxRunner是配套的。runner的类,定义执行的上下文。Worker就是实际的执行器。

testng定义了两个层次的执行。一层是suite这个层面的。一层是每个@Test层面的。从图中可以看到。其中,testRunner,实际是跟testMethodWorker配套使用的。

我们来看一下最底层的testRunner的privateRun()

private void privateRun(XmlTest xmlTest) {    String parallelMode = xmlTest.getParallel();    boolean parallel = XmlSuite.PARALLEL_METHODS.equals(parallelMode)        || "true".equalsIgnoreCase(parallelMode)        || XmlSuite.PARALLEL_CLASSES.equals(parallelMode)        || XmlSuite.PARALLEL_INSTANCES.equals(parallelMode);    {      // parallel      int threadCount = parallel ? xmlTest.getThreadCount() : 1;      // Make sure we create a graph based on the intercepted methods, otherwise an interceptor      // removing methods would cause the graph never to terminate (because it would expect      // termination from methods that never get invoked).      DynamicGraph<ITestNGMethod> graph = createDynamicGraph(intercept(m_allTestMethods));      if (parallel) {        if (graph.getNodeCount() > 0) {          GraphThreadPoolExecutor<ITestNGMethod> executor =              new GraphThreadPoolExecutor<ITestNGMethod>(graph, this,                  threadCount, threadCount, 0, TimeUnit.MILLISECONDS,                  new LinkedBlockingQueue<Runnable>());          executor.run();          try {            long timeOut = m_xmlTest.getTimeOut(XmlTest.DEFAULT_TIMEOUT_MS);            Utils.log("TestRunner", 2, "Starting executor for test " + m_xmlTest.getName()                + " with time out:" + timeOut + " milliseconds.");            executor.awaitTermination(timeOut, TimeUnit.MILLISECONDS);            executor.shutdownNow();          } catch (InterruptedException handled) {            handled.printStackTrace();            Thread.currentThread().interrupt();          }        }      } else {        boolean debug = false;        List<ITestNGMethod> freeNodes = graph.getFreeNodes();        if (debug) {          System.out.println("Free nodes:" + freeNodes);        }        if (graph.getNodeCount() > 0 && freeNodes.isEmpty()) {          throw new TestNGException("No free nodes found in:" + graph);        }        while (! freeNodes.isEmpty()) {          List<IWorker<ITestNGMethod>> runnables = createWorkers(freeNodes);          for (IWorker<ITestNGMethod> r : runnables) {            r.run();          }          graph.setStatus(freeNodes, Status.FINISHED);          freeNodes = graph.getFreeNodes();          if (debug) {            System.out.println("Free nodes:" + freeNodes);          }        }      }    }  }

可以看到,对于多线程跟普通单线程是有不同的处理的。先来简单说说单线程的处理。重点在于 freenodes的获取。获取到所有的freenodes,并根据freenodes,得到对应的testmethods, 然后执行 .反复递归执行,直到所有xmltest的nodes执行完。 所以先来看看freenodes是如何得来的:

      DynamicGraph<ITestNGMethod> graph = createDynamicGraph(intercept(m_allTestMethods));

第一步,先得到动态的节点图:

  private DynamicGraph<ITestNGMethod> createDynamicGraph(ITestNGMethod[] methods) {    DynamicGraph<ITestNGMethod> result = new DynamicGraph<ITestNGMethod>();    result.setComparator(new Comparator<ITestNGMethod>() {      @Override      public int compare(ITestNGMethod o1, ITestNGMethod o2) {        return o1.getPriority() - o2.getPriority();      }    });    DependencyMap dependencyMap = new DependencyMap(methods);    // Keep track of whether we have group dependencies. If we do, preserve-order needs    // to be ignored since group dependencies create inter-class dependencies which can    // end up creating cycles when combined with preserve-order.    boolean hasDependencies = false;    for (ITestNGMethod m : methods) {      result.addNode(m);    @retinder-->这里先把所有的测试方法加载进来。然后按照指定的规则计算      // Dependent methods      //@retinder-->保存测试方法,对应的依赖方法      {        String[] dependentMethods = m.getMethodsDependedUpon();        if (dependentMethods != null) {          for (String d : dependentMethods) {            ITestNGMethod dm = dependencyMap.getMethodDependingOn(d, m);            if (m != dm){                result.addEdge(m, dm);            }          }        }      }      // Dependent groups      //如果是所属的测试集(class)存在依赖的测试集,那么把依赖的测试集的多有的测试方法,加入到当前方法的依赖方法。      {        String[] dependentGroups = m.getGroupsDependedUpon();        for (String d : dependentGroups) {          hasDependencies = true;          List<ITestNGMethod> dg = dependencyMap.getMethodsThatBelongTo(d, m);          if (dg == null) {            throw new TestNGException("Method \"" + m                + "\" depends on nonexistent group \"" + d + "\"");          }          for (ITestNGMethod ddm : dg) {            result.addEdge(m, ddm);          }        }      }    }    // Preserve order    // Don't preserve the ordering if we're running in parallel, otherwise the suite will    // create multiple threads but these threads will be created one after the other,    // giving the impression of parallelism (multiple thread id's) while still running    // sequentially.    @retinder-->这里的注释提到,如果在多线程模式下,设置Preserve order是会使人感觉是跟单线程一样的。因为    必须先启动线程执行依赖的方法,然后启动另外的线程执行后边的测试方法。    @retinder-->这里实际上就是这样。如果是单线程模式下,指定了Preserve order,按照A-B这种顺序执行,实际上会    转化为depency的方式,即B中所有的测试方法,都会依赖A中的测试方法。这样在执行的时候,就会确保A执行完了,才执行B。    同理如果在注释里边进行了分组    if (! hasDependencies        && ! XmlSuite.isParallel(getCurrentXmlTest().getParallel())        && "true".equalsIgnoreCase(getCurrentXmlTest().getPreserveOrder())) {      // If preserve-order was specified and the class order is A, B      // create a new set of dependencies where each method of B depends      // on all the methods of A      ListMultiMap<ITestNGMethod, ITestNGMethod> classDependencies          = createClassDependencies(methods, getCurrentXmlTest());      for (Map.Entry<ITestNGMethod, List<ITestNGMethod>> es : classDependencies.getEntrySet()) {        for (ITestNGMethod dm : es.getValue()) {          result.addEdge(dm, es.getKey());        }      }    }    // Group by instances    @retinder-->如果设置了group-by-instances.    if (getCurrentXmlTest().getGroupByInstances()) {      ListMultiMap<ITestNGMethod, ITestNGMethod> instanceDependencies          = createInstanceDependencies(methods, getCurrentXmlTest());      for (Map.Entry<ITestNGMethod, List<ITestNGMethod>> es : instanceDependencies.getEntrySet()) {        for (ITestNGMethod dm : es.getValue()) {          result.addEdge(dm, es.getKey());        }      }    }    return result;  }

PS:这里其实有个问题,我看代码,所谓的group-by-instance,跟普通的depency并没有太大的区别。感觉的得到的是一样的执行顺序。这里实在看不下去。先挖个坑,以后慢慢填。

这里写图片描述

最后得到一个像上边这个一样的图。

然后不被依赖的,都会先执行,递归,直到所有的方法被执行完。

关于privateMain的具体流程,我打算在之三写,虽然这个博客也并没有人在看,哈哈。不过既然开了头,那就把它完成了。