-->

2012年1月1日 星期日

[gcov] Coverage testing tool

在 GNU 有一套 gcov 可以輔助我們計算 code coverage 的工具

我們在這裡以一個 max(a, b, c) 的 C++ 程式為例,從 compile、test、最後透過 gcov 跑出 gcov 的結果。

下面的程式,為了展示效果,所以將 max 以比較直觀的 if 來判斷大小,用意是為了測試 gcov 的效果。

首先,先編輯一個 max3.cpp 的檔案
# vim max3.cpp


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <stdio.h>
#include <stdlib.h>
 
template <typename T>
T max(T a, T b, T c)
{
  if (a > b)
  {
    if (a > c)
    {
      return a;
    }
    else
    {
      return c;
    }
  }
  else
  {
    if (b > c)
    {
      return b;
    }
    else
    {
      return c;
    }
  }
}
 
void show_usage(void)
{
  fprintf(stderr, "Please input arguments with thress numbers!\n");
}
 
int main(int argc, char **argv)
{
  if (argc < 4)
  {
    show_usage();
    return EXIT_FAILURE;
  }
 
 
  int a = atoi(argv[1]);
  int b = atoi(argv[2]);
  int c = atoi(argv[3]);
  printf(
    "Max number (%d, %d, %d) is %d\n",
    a, b, c,
    max(a, b, c)
  );
 
  return EXIT_SUCCESS;
}
 


若不加 gcov,我們可以用
# g++ max3.cpp -o max3
來編譯我們的程式,接著用

# ./max3
就可以簡單的跑我們的程式,結果如下:

Please input arguments with thress numbers!
因為我們沒有輸入任何數字,所以程式會執行到 show_usage,印出錯誤訊息後,結束程式。

 
void show_usage(void)
{
  fprintf(stderr, "Please input arguments with thress numbers!\n");
}
 
int main(int argc, char **argv)
{
  if (argc < 4)
  {
    show_usage();
    return EXIT_FAILURE;
  }
 
我們可以在 compile 的時候,加上分析 coverage 的選項

# g++ max3.cpp -o max3 -fprofile-arcs -ftest-coverage
編譯完後,用 ls 查詢多了一個 max3.gcno 的檔案
# ls
max3  max3.cpp  max3.gcno
接著我們就可以跟平常一樣,開始測試我們的程式

./max3
只要有執行過 binary,就會在該目錄下發現多了 .gcda 的檔案

# ls
max3  max3.cpp  max3.gcda  max3.gcno
測試完以後,用 gcov,參數給 source files

# gcov max3.cpp
File 'max3.cpp'
Lines executed:35.00% of 20
max3.cpp:creating 'max3.cpp.gcov'
然後 ls 就會看到多了 .gcov 的檔案

# ls
max3  max3.cpp  max3.cpp.gcov  max3.gcda  max3.gcno
 這個 .gcov 的檔案就是 coverage 的結果

        -:    0:Source:max3.cpp
        -:    0:Graph:max3.gcno
        -:    0:Data:max3.gcda
        -:    0:Runs:1
        -:    0:Programs:1
        -:    1:#include <stdio.h>
        -:    2:#include <stdlib.h>
        -:    3:
        -:    4:template <typename T>
    #####:    5:T max(T a, T b, T c)
        -:    6:{
    #####:    7:  if (a > b)
        -:    8:  {
    #####:    9:    if (a > c)
        -:   10:    {
    #####:   11:      return a;
        -:   12:    }
        -:   13:    else
        -:   14:    {
    #####:   15:      return c;
        -:   16:    }
        -:   17:  }
        -:   18:  else
        -:   19:  {
    #####:   20:    if (b > c)
        -:   21:    {
    #####:   22:      return b;
        -:   23:    }
        -:   24:    else
        -:   25:    {
    #####:   26:      return c;
        -:   27:    }
        -:   28:  }
        -:   29:}
        -:   30:
        1:   31:void show_usage(void)
        -:   32:{
        1:   33:  fprintf(stderr, "Please input arguments with thress numbers!\n");
        1:   34:}
        -:   35:
        1:   36:int main(int argc, char **argv)
        -:   37:{
        1:   38:  if (argc < 4)
 
        -:   39:  {
        1:   40:    show_usage();
        1:   41:    return EXIT_FAILURE;
        -:   42:  }
        -:   43:
    #####:   44:  int a = atoi(argv[1]);
    #####:   45:  int b = atoi(argv[2]);
    #####:   46:  int c = atoi(argv[3]);
        -:   47:  printf(
        -:   48:    "Max number (%d, %d, %d) is %d\n",
        -:   49:    a, b, c,
        -:   50:    max(a, b, c)
    #####:   51:  );
        -:   52:
    #####:   53:  return EXIT_SUCCESS;
        -:   54:}
        -:   55:
左邊欄有看到 1 代表該 line 被執行了 1 次、##### 代表沒有執行

此時我們可以繼續執行我們的測試,coverage 會繼續的累積

# ./max3 1 3 5
Max number (1, 3, 5) is 5

# gcov max3.cpp
File 'max3.cpp'
Lines executed:80.00% of 20
max3.cpp:creating 'max3.cpp.gcov'

# cat max3.cpp.gcov
        -:    0:Source:max3.cpp
        -:    0:Graph:max3.gcno
        -:    0:Data:max3.gcda
        -:    0:Runs:2
        -:    0:Programs:1
        -:    1:#include <stdio.h>
        -:    2:#include <stdlib.h>
        -:    3:
        -:    4:template <typename T>
        1:    5:T max(T a, T b, T c)
        -:    6:{
        1:    7:  if (a > b)
        -:    8:  {
    #####:    9:    if (a > c)
        -:   10:    {
    #####:   11:      return a;
        -:   12:    }
        -:   13:    else
        -:   14:    {
    #####:   15:      return c;
        -:   16:    }
        -:   17:  }
        -:   18:  else
        -:   19:  {
        1:   20:    if (b > c)
        -:   21:    {
    #####:   22:      return b;
        -:   23:    }
        -:   24:    else
        -:   25:    {
        1:   26:      return c;
        -:   27:    }
        -:   28:  }
        -:   29:}
        -:   30:
        1:   31:void show_usage(void)
        -:   32:{
        1:   33:  fprintf(stderr, "Please input arguments with thress numbers!\n");
        1:   34:}
        -:   35:
        2:   36:int main(int argc, char **argv)
        -:   37:{
        2:   38:  if (argc < 4)
        -:   39:  {
        1:   40:    show_usage();
        1:   41:    return EXIT_FAILURE;
        -:   42:  }
        -:   43:
        1:   44:  int a = atoi(argv[1]);
        1:   45:  int b = atoi(argv[2]);
        1:   46:  int c = atoi(argv[3]);
        -:   47:  printf(
        -:   48:    "Max number (%d, %d, %d) is %d\n",
        -:   49:    a, b, c,
        -:   50:    max(a, b, c)
        1:   51:  );
        -:   52:
        1:   53:  return EXIT_SUCCESS;
        -:   54:}
        -:   55:
測試 code coverage 的好處,可以幫我們檢查我們的 test case 只能跑那些部分,還有那些部分是無法被執行的。於是開 test case 的時候,我們就不用一直增加重複跑到的部分,如果測試你開了 100 個 test cases,和開了 10000 個 test cases,結果跑出來的 coverage rate 沒有因此而提昇的話,那這樣是很沒有效率的。

因為我們現在的 coverage rate 只有 80%,代表我們還有一些 code 沒有被測試,我們就可以繼續開 test case。

./max3 6 4 2
./max3 6 2 4
./max3 2 6 4

# gcov max3.cpp
File 'max3.cpp'
Lines executed:95.00% of 20
max3.cpp:creating 'max3.cpp.gcov'

        -:    4:template <typename T>
        4:    5:T max(T a, T b, T c)
        -:    6:{
        4:    7:  if (a > b)
        -:    8:  {
        2:    9:    if (a > c)
        -:   10:    {
        2:   11:      return a;
        -:   12:    }
        -:   13:    else
        -:   14:    {
    #####:   15:      return c;
        -:   16:    }
        -:   17:  }
        -:   18:  else
        -:   19:  {
        2:   20:    if (b > c)
        -:   21:    {
        1:   22:      return b;
        -:   23:    }
        -:   24:    else
        -:   25:    {
        1:   26:      return c;
        -:   27:    }
        -:   28:  }
        -:   29:}
多了三個 test cases 以後,我們 coverage rate 已經提昇到 95%,還剩下 line 15 沒有被執行到,我們就只要開

# ./max3 6 4 8
Max number (6, 4, 8) is 8
# gcov max3.cpp
File 'max3.cpp'
Lines executed:100.00% of 20
max3.cpp:creating 'max3.cpp.gcov'
最後終於到達 100%,也就是我們的程式碼,都被執行過至少一次了。

沒有留言:

張貼留言