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

相關文章
相關標籤/搜索