在 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
來編譯我們的程式,接著用
就可以簡單的跑我們的程式,結果如下:
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
|
接著我們就可以跟平常一樣,開始測試我們的程式
只要有執行過 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%,也就是我們的程式碼,都被執行過至少一次了。
沒有留言:
張貼留言