cobertura改造之路

在项目中使用hudson来作持续集成,使用cobertura来分析代码覆盖率的时候发现它会把一些本不该该算作覆盖分析的代码也会加入到最终的统计中,致使由于一些默认的构造函数或者一些没有被覆盖到的get,set方法使得整个统计数据没法达到比较完美的程度,好比咱们的方法覆盖率一直到不了80%,感受很不给力,鉴于咱们会对方法覆盖率作必定的要求,我就下载了cobertura的代码,准备打个patch。css

相似的代码覆盖率工具一般都离不开对原始文件的打点处理,也就是会分析代码并动态生成打点的字节码,cobertura也不例外,会在指定路径下生成instrumented文件夹,里面包含了被打点处理过的class,打点后的类相似: java

  
  
  
  
  1. source by cobertura 1.9.3:  
  2.  
  3. public class XX extends ParentClass implements filter, HasBeenInstrumented  
  4.  
  5. {  
  6.  
  7. public static int getDefaultParams(someDto)  
  8.  
  9. {  
  10.  
  11. int i = 0;int __cobertura__branch__number == -1;  
  12.  
  13. int __cobertura_line__number__;  
  14.  
  15. ProjectData.getGlobalProjectData().getOrClassData(“com.xxx.web.utils.xxxUtil”).touch(40); StringBuffer result = new StringBuffer();  
  16.  
  17. ………  
  18.  
  19. }  
  20.  
  21. }  
  22.  

全部方法内部的行或者分支都被如上代码打点,使用了asm来实现打点逻辑的字节码生成(asm使用了visitor+adapter模式代代码),下面介绍一些cobertura中的核心结构: net.sourceforge.cobertura.coveragedata包里面定义了记录覆盖率数据的javabean,好比ClassData,LineData,JumpData,好比会记录当前代码的hit次数,某个分支是否被hit到等等,这些数据对象的定义都在这个包下面,另外这个包里面有个HasBeenInstrumented接口,这个接口没有实现,只是用来做为是否须要对当前类作打点的判断依据,好比已经打过点的类就不会再次打点,若是你有类不想被打点,也能够implements这个接口(不建议这么作,由于cobertura代码自己就不稳定),copy一段代码吧 web

 

  
  
  
  
  1. ClassInstrumenter.java  
  2.  
  3. // Do not attempt to instrument interfaces or classes that  
  4.  
  5. // have already been instrumented  
  6.  
  7. //若是该类实现了hasBeenInstrumented接口或自己就是接口,则跳过打点  
  8.  
  9. if (((access & Opcodes.ACC_INTERFACE) != 0)  
  10.  
  11. || arrayContains(interfaces, hasBeenInstrumented))  
  12.  
  13. {  
  14.  
  15. super.visit(version, access, name, signature, superName,  
  16.  
  17. interfaces);  
  18.  
  19. }  
  20.  
  21. else  
  22.  
  23. {  
  24.  
  25. instrument = true;  
  26.  
  27. // Flag this class as having been instrumented  
  28.  
  29. String[] newnewInterfaces = new String[interfaces.length + 1];  
  30.  
  31. System.arraycopy(interfaces, 0, newInterfaces, 0,  
  32.  
  33. interfaces.length);  
  34.  
  35. //对已经打点的类加上implements HasBeenInstrumented  
  36.  
  37. newInterfaces[newInterfaces.length - 1] = hasBeenInstrumented;  
  38.  
  39. super.visit(version, access, name, signature, superName,  
  40.  
  41. newInterfaces);  
  42.  
  43. }  
  44.  

net.sourceforge.cobertura.instrument 这个包顾名思义,包含的全部打点相关的逻辑,最核心的类有三个ide

  • ClassInstrumenter 这个类前面有提到,它实现了asm中的ClassAdapter接口,asm的visitor加adapter模式,tree模式等等你们能够在asm官方的guide中看到介绍,ClassInstrumenter的做用就是做为对单个class文件字节码注入的入口,在visitMethod方法中调用了FirstPassMethodInstrumenter来处理对method的visit
  • FirstPassMethodInstrumenter,SecondPassMethodInstrumenter。这两个东东完成了对方法内部的打点任务,第一个类作了采样的动做,分析了代码里有多少行,有多少个分支等等,在visitEnd方法里调用了SecondPassMethodInstrumenter,使用了asm中的Tree模式再次visit了一次方法的代码并添加了打点的逻辑代码:
  
  
  
  
  1. SecondPassMethodInstrumenter.java  
  2.  
  3. public void visitLineNumber(int line, Label start)  
  4.  
  5. {  
  6.  
  7. // Record initial information about this line of code  
  8.  
  9. currentLine = line;  
  10.  
  11. currentJump = 0;  
  12.  
  13. instrumentOwnerClass();  
  14.  
  15. // Mark the current line number as covered:  
  16.  
  17. // classData.touch(line)  
  18.  
  19. mv.visitIntInsn(SIPUSH, line);  
  20.  
  21. mv.visitMethodInsn(INVOKESTATIC,  
  22.  
  23. TOUCH_COLLECTOR_CLASS, “touch”,  
  24.  
  25. “(Ljava/lang/String;I)V”);  
  26.  
  27. super.visitLineNumber(line, start);  
  28.  
  29. }  
  30.  

剩下的一些包好比reporting,merge提供数据合并和报表展现,ant提供了对ant的集成,还有些main方法提供了命令行的功能,javancss包使用javacc生成代码来分析java语法,提供了对代码质量的计算和分析,话说。。。我重来没在hudson里找到相关的视图,另外这件事明显该交给分析静态代码的工具来干,好比pmd,checkstyle之类,我在pmd中看到了相似的代码,好比分析方法中存在的if,for等再根据方法和这些关键字出现的次数来计算代码的复杂度,Javancss.java 是整个代码分析的入口,用编写者的话说,这个是大脑,这部分代码我估计早晚得干掉。 函数

说了半天了,都是废话,个人目标是对cobertura的ignore功能作改进,最开始个人假设是cobertura经过实现自定义的classadapter和methodadapter来实现对代码的打点工做,而visitor模式的最大缺点是你没法灵活的根据上下文来决定是否须要对一行代码判断是否须要ignore,而asm的tree模式能够做为一个合适的方式来补充实现很是灵活的ignore的规则。在看完cobertura和asm的部分代码后,我发现要在现有的代码结构中添加相关的逻辑仍是有点复杂,不是一个patch就能够解决的问题。 若是实现一些通用的ignorePattern模式在现有的条件下也是可行的,好比定义ClassIgnorePattern,methodIgnorePattern,LineIgnorePattern,这种方式能够提供一些经常使用的实现,好比对默认构造函数的忽略,对代码中get,set方法忽略,对记录日志代码的忽略等等,固然在这种设计下,用户也能够自定义一些IgnorePattern,固然。。这些都是我最初的想法,后来我查询了sourceforge上cobertura的patch list中已经有人对相似的需求打了patch,虽然在实现上只是单纯的硬编码实现(能够忽略get,set方法,默认的构造函数),不过也算是实现了这个需求,也就是说在1.9.4.2或者1.9.5这个版本,若是你们配置相关参数为true的话,项目的覆盖率会有较大的提高。工具

不过话说回来,cobertura里面代码注释是至关的少,规范性也作的很差,代码有点乱,这个ignore的patch已经有人提出了一些异议,但仍是被加到了trunk中,我我的以为只要可配置应该也没太大的问题,只是这个功能的实现实在是太硬编码了,研究了asm代码也算小有收获吧,可是我的感受若是采用asm的tree模式加上官方文档guide提到的transformer来实现对于定制化覆盖率这件事会比较轻松,缺点是效率会下降,可是覆盖率一般都是持续集成来作,时间应该不是咱们关注的重点,最多也就是30%-40%的损失。 总在说要参与开源项目,想到什么都但愿能有个结论,此次总算作的我本身满意了,留篇文章作记念吧,有空我会试试把个人想法落实,练练手。(若是您看到这里,我感受咱们很对路了,若是你有什么好的想法或者建议请联系我:element8325@gmail.com,另外若是您想换个环境也能够经过邮件联系我,谢谢
ui

相关文章
相关标签/搜索