Fenriswolf 程式筆記

奮利斯狼的地盤,小綿羊勿入

從 instrumentation 看 Emma, Cobertura and Clover

目前免費的 converage tools 裡就屬 EMMA 和 Cobertura 最有名,兩者皆是 bytecode instrumentation。以下是針對這兩套工具的比較,另外在 bytecode decompile 的部分我加入了要錢的 Clover(source code instrumentation) 來一起做個評比。讓大家可以看一下 bytecode 和 source code instrumentation 的差異性

  • EMMA

1. 最後更新 : 2005-06-13(已經停止開發這點很可惜)
2. 提供 block, class 和 method coverages
3. Partial line coverages(在多重條件判斷下,只有部分測到會以黃色來表示)
4. coverage 判定標準非常嚴格,以下的範例會再重申這一點
5. 速度最快且最省資源,這可由下面的範例比較得知
6. 比較完整的文件介紹
7. 不需額外的 3rd-party libraries
8. 有 Eclipse 的 plugin – EclEmma
9. 報表範例

  • Cobertura

1. 最後更新 : 2010-03-03
2. 基於 JCoverage 開發而成
3. 從 Cobertura 所產生的程式碼來看是有支援 Partial line coverages,但是在目前的 report 中並看不到這樣的資訊
4. coverage report 比較漂亮(這是老闆愛看的)
5. 只有 block 和 line coverages
6. 可顯示每一行執行的時間
7. 有額外的 ant tags 可用
8. 依頼 asm, jakarta-oro 和 log4j libraries
9. 有 Eclipse 的 plugin – eCobertura
10. 報表範例

以下由兩個比較特別的範例來看不同 coverage tools 的表現
1. Exception
在 call substring method 會丟出一個 IndexOutOfBoundsException
原始程式

String str = "abc";
System.out.println(str.substring(5));

反組譯後的程式

String str = "abc";
System.out.println(str.substring(5));

EMMA 產生的程式碼

boolean aflag[] = ($VRc != null ? $VRc : $VRi())[3];
String s = "abc";
System.out.println(s.substring(5));
aflag[0] = true;

Cobertura 產生的程式碼

boolean flag = false;
int __cobertura__branch__number__ = -1;
TouchCollector.touch("tcloud.dummy.service.CoverageTestMain", 25);
String str = "abc";
TouchCollector.touch("tcloud.dummy.service.CoverageTestMain", 26);
System.out.println(str.substring(5));
TouchCollector.touch("tcloud.dummy.service.CoverageTestMain", 27);

Clover 產生的程式碼

__CLR3_0_217a17agbtkjkdv.R.inc(1571);
__CLR3_0_217a17agbtkjkdv.R.inc(1572);
String str = "abc";
__CLR3_0_217a17agbtkjkdv.R.inc(1573);
System.out.println(str.substring(5));

從這個範例可以看出來 EMMA 嚴格的地方,只要有 exception 丟出,連 String 的宣告也不列入 coverage 內。當被測試的程式碼容易丟出 exception 時,用 EMMA 會大大的降低測試涵蓋率。Cobertura 所插入的程式碼是三者最多的,這就是為什麼他會慢的原因,以經驗來說至少會比 EMMA 慢 15% 以上。另一個問題是,如果被測試的程式碼太多,很容易會有 out of memory 的問題發生。Clover 則是介於兩者之間。

2. For Loop
java 1.5 所支援的 enhanced loop 其實是由 compile sugar 來達成,因此 bytecode 和 source code 差異很大,也導至 instrumentation 所判斷及產生的測試碼大大的不同
原始程式

List<String> list = new ArrayList<String>();
for(String element : list) {
    System.out.println(element);
}

反組譯後的程式

List list = new ArrayList();
String element;
for(Iterator iterator = list.iterator(); iterator.hasNext(); System.out.println(element))
    element = (String)iterator.next();

EMMA 產生的程式碼

boolean aflag[] = ($VRc != null ? $VRc : $VRi())[4];
List list = new ArrayList();
Iterator iterator = list.iterator();
aflag[0] = true;
do
{
    aflag[2] = true;
    if(iterator.hasNext())
    {
        String s = (String)iterator.next();
        System.out.println(s);
        aflag[1] = true;
    } else
    {
        break;
    }
} while(true);
aflag[3] = true;

Cobertura 產生的程式碼

boolean flag = false;
int __cobertura__branch__number__ = -1;
TouchCollector.touch("tcloud.dummy.service.CoverageTestMain", 36);
List list = new ArrayList();
TouchCollector.touch("tcloud.dummy.service.CoverageTestMain", 37);
Iterator iterator = list.iterator();
do
{
    TouchCollector.touch("tcloud.dummy.service.CoverageTestMain", 37);
    __cobertura__line__number__ = 37;
    __cobertura__branch__number__ = 0;
    if(iterator.hasNext())
    {
        if(__cobertura__branch__number__ >= 0)
        {
            TouchCollector.touchJump("tcloud.dummy.service.CoverageTestMain", __cobertura__line__number__, __cobertura__branch__number__, false);
            __cobertura__branch__number__ = -1;
        }
        String element = (String)iterator.next();
        TouchCollector.touch("tcloud.dummy.service.CoverageTestMain", 38);
        System.out.println(element);
    } else
    {
        if(__cobertura__line__number__ == 37 && __cobertura__branch__number__ == 0)
        {
            TouchCollector.touchJump("tcloud.dummy.service.CoverageTestMain", __cobertura__line__number__, __cobertura__branch__number__, true);
            __cobertura__branch__number__ = -1;
        }
        TouchCollector.touch("tcloud.dummy.service.CoverageTestMain", 40);
        return;
    }
} while(true);

Clover 產生的程式碼

__CLR3_0_2145145gbu63tpg.R.inc(1462);
__CLR3_0_2145145gbu63tpg.R.inc(1463);
List list = new ArrayList();
__CLR3_0_2145145gbu63tpg.R.inc(1464);
String element;
for(Iterator iterator = list.iterator(); iterator.hasNext(); System.out.println(element))
{
      element = (String)iterator.next();
      __CLR3_0_2145145gbu63tpg.R.inc(1465);
}

這個範例內 EMMA 一如往常的精簡,Cobertura 還是一樣的囉嗦
比較好玩的是 Clover,因為他是 source code instrumentation,在產生程式碼的時候無法知道 compiler 實際上做了什麼事,只能由 source code 來判讀,所以結果變的跟 EMMA 一樣的簡單,也不合我們的預期,除非程式延用舊的寫法才能有如下的結果,這對於用 JDK 1.5 以上的版本所開發的程式來說不太方便

Clover 由反組譯後的程式所產生的程式碼

    Iterator iterator;
    __CLR3_0_2166166gbu71moi.R.inc(1535);
    __CLR3_0_2166166gbu71moi.R.inc(1536);
    List list = new ArrayList();
    __CLR3_0_2166166gbu71moi.R.inc(1537);
    __CLR3_0_2166166gbu71moi.R.inc(1538);
    iterator = list.iterator();
_L4:
    if(iterator.hasNext())
    {
        __CLR3_0_2166166gbu71moi.R.iget(1539);
    } else
    {
        __CLR3_0_2166166gbu71moi.R.iget(1540);
        return;
    }
    if(true) goto _L2; else goto _L1
_L1:
    break; /* Loop/switch isn't completed */
_L2:
    __CLR3_0_2166166gbu71moi.R.inc(1541);
    String element = (String)iterator.next();
    System.out.println(element);
    if(true) goto _L4; else goto _L3
_L3:

 
 
參考資料
EMMA
Cobertura
Clover

廣告

2012/03/21 - Posted by | Java Tool | , ,

仍無迴響。

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com 標誌

您的留言將使用 WordPress.com 帳號。 登出 /  變更 )

Google+ photo

您的留言將使用 Google+ 帳號。 登出 /  變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 /  變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 /  變更 )

連結到 %s

%d 位部落客按了讚: