Lv.的博客

QT unit test code coverage

 

   

准备环境:

qt-creator5.2.1 , gcov(gcc 默认安装),lcov(gcov 的图形化显示界面),qt_testlib 

 

 

各环境介绍:

 

1.gcov 

 

gcov

是一个可用于C/C++的代码覆盖工具,是gcc的内建工具。下面介绍一下如何利用gcov来收集代码覆盖信息。

 

想要用gcov收集代码覆盖信息,需要在gcc编译代码的时候加上这2个选项:

 “-fprofile-arcs -ftest-coverage”,把这个简单的程序编译一下

 gcc -fprofile-arcs -ftest-coverage hello.c -o hello 

编译后会得到一个可执行文件hello和hello.gcno文件,当用gcc编译文件的时候,如果带有“-ftest-coverage”参数,就会生成这个.gcno文件,它包含了程序块和行号等信息

 

接下来可以运行这个hello的程序

 ./hello 5 

./hello 12 

运行结束以后会生成一个hello.gcda文件,如果一个可执行文件带有“-fprofile-arcs”参数编译出来,并且运行过至少一次,就会生成。

这个文件包含了程序基本块跳转的信息。接下来可以用gcov生成代码覆盖信息: 

gcov hello.c 运行结束以后会生成2个文件hello.c.gcov和myfunc.c.gcov。打开看里面的信息:

-: 0:Source:myfunc.c 

-: 0:Graph:hello.gcno 

-: 0:Data:hello.gcda 

-: 0:Runs:1 

-: 0:Programs:1 

-: 1:#include 

-: 2: 

-: 3:void test(int count) 

1: 4:{ 

-: 5: int i; 

10: 6: for (i = 1; i < count; i++) 

-: 7: { 

9: 8: if (i % 3 == 0) 

3: 9: printf (“%d is divisible by 3 

\

n”, i);

 

9: 10: if (i % 11 == 0) 

#

####: 11: printf (“%d is divisible by 11 

\

n”, i);

 

9: 12: if (i % 13 == 0) 

#####: 13: printf (“%d is divisible by 13 

\

n”, i);

-: 14: } 

1: 15:} 

被标记为#####的代码行就是没有被执行过的,代码覆盖的信息是正确的. 

1.2 使用gcov的3个阶段 

(1) 编译

# gcc -f=profile-arcs -f test-coverage  -o test test.c 

# ls 

test  test.c  test.gcno

 

-f profile-arcs -f test-coverage告诉编译器生成gcov需要的额外信息,并在目标文件中插入gcov需要的extra profiling information。因此,该命令在生成可执行文件test

的同时生成test.gcno文件(gcov note文件)。

 

  

(2) 收集信息

 

# ./test 

Success 

# ls 

test  test.c   test.gcda test.gcno 

执行该程序,生成test.gcda文件(gcov data文件)。 

(3) 

报告

# gcov test.c 

File 'test.c' 

Lines executed:87.50% of 8 

test.c:creating 'test.c.gcov' 

  

# ls 

test  test.c  

test.c.gcov

  test.gcda  test.gcno 

生成test.c.gcov文件,该文件记录了每行代码被执行的次数。

  

test.c.gcov

文件内容如下,蓝色表示笔者添加的注释。

 

        -:    0:Source:test.c 

        -:    0:Graph:test.gcno 

        -:    0:Data:test.gcda 

        -:    0:Runs:1 

        -:    0:Programs:1 

        -:    1:#include          //前面的数字表明该clause被执行的次数,下同

 

        -:    2: 

        -:    3:int main (void) 

        1:    4:{ 

        -:    5:    int i, total;

        -:    6: 

        1:    7:    total = 0; 

        -:    8: 

       11:    9:    for (i = 0; i < 10; i++)  //前面的数字11表明该clause被执行11次

       10:   10:        total += i; 

        -:   11: 

        1:   12:    if (total != 45) 

    #####:   13:        printf ("Failure/n"); 

        -:   14:    else 

        1:   15:        printf ("Success/n"); 

        1:   16:    return 0; 

        -:   17:} 

        -:   18: 

1.2 gcov的选项

gcov

的选项不多,也好理解,此处选3个典型的选项并结合例子加以说明。 

(1) -a, --all-blocks 

在.gcov文件中输出每个基本快(basic block)的执行次数。如果没有-a选项,则输出'main'函数这个block的执行次数,如上所示。使用该选项可以

 

    Write individual execution counts for every basic block.  Normally gcov outputs execution counts only for the main blocks of a line.  With 

this option you can determine if blocks within a single line are not being executed. 

  

# gcov -a test.c File 'test.c' 

Lines executed:87.50% of 8     //

代码覆盖率

 

test.c:creating 'test.c.gcov' 

  

Test.c.gcov

文件内容。

 

        -:    0:Source:test.c 

        -:    0:Graph:test.gcno 

        -:    0:Data:test.gcda 

        -:    0:Runs:1 

        -:    0:Programs:1 

        -:    1:#include 

        -:    2: 

        -:    3:int main (void) 

        1:    4:{ 

        -:    5:    int i, total; 

        -:    6: 

    1:    7:    total = 0; 

        -:    8: 

       11:    9:    for (i = 0; i < 10; i++) 

        1:    9-block  0 

       10:    9-block  1 

       11:    9-block  2 

       10:   10:        total += i; 

        -:   11: 

        1:   12:    if (total != 45) 

        1:   12-block  0 

    #####:   13:        printf ("Failure/n"); 

    $$$$$:   13-block  0 

        -:   14:    else 

        1:   15:        printf ("Success/n"); 

        1:   15-block  0 

        1:   16:    return 0; 

        1:   16-block  0 

        -:   17:} 

        -:   18: 

(2) -b, --branch-probabilities 

在.gcov文件中输出每个分支的执行频率,并有分支统计信息。

 

  

# gcov -b test.c 

File 'test.c' 

Lines executed:87.50% of 8 

Branches executed:100.00% of 4 

Taken at least once:75.00% of 4 

Calls executed:50.00% of 2 

test.c:creating 'test.c.gcov' 

  

        -:    0:Source:test.c 

        -:    0:Graph:test.gcno 

        -:    0:Data:test.gcda 

        -:    0:Runs:1 

        -:    0:Programs:1 

        -:    1:#include 

        -:    2: 

        -:    3:int main (void) 

function main called 1 returned 100% blocks executed 86% 

        1:    4:{ 

        -:    5:    int i, total; 

        -:    6: 

        1:    7:    total = 0;

        -:    8: 

       11:    9:    for (i = 0; i < 10; i++) 

branch  0 taken 91% 

branch  1 taken 9% (fallthrough) 

       10:   10:        total += i; 

        -:   11: 

        1:   12:    if (total != 45) 

branch  0 taken 0% (fallthrough) 

branch  1 taken 100% 

    #####:   13:        printf ("Failure/n"); 

call    0 never executed 

        -:   14:    else 

        1:   15:        printf ("Success/n"); 

call    0 returned 100% 

        1:   16:    return 0; 

        -:   17:} 

        -:   18: 

(3) -c, --branch-counts 

  

.gcov

文件中输出每个分支的执行次数。

 

 

# gcov -c test.c 

File 'test.c' 

Lines executed:87.50% of 8 

test.c:creating 'test.c.gcov' 

  

-c是默认选项,其结果与"gcov test.c"执行结果相同。

 

2.lcov 

lcov -d . -t ‘Hello test’ -o ‘hello_test.info’ -b . -c 指定lcov

在当前目录“.”去找代码覆盖的信息,输出为’hello_test.info’ ,这个hello_test.info是一个中间结果,需要把它用genhtml来处理一下,genhtml是lcov里面的一个工具。

 genhtml -o result hello_test.info 指定输出目录是result。

一个完整的html报告就生成了,做一个连接,把这个目录连到随便一个web server的目录下,就可以看报告了。

 

3.总结: 
gcov是配合gcc产生覆盖信息报告的工具; 
lcov是将gcov产生的报告信息,以更直观的方式显示出来工具 基本的使用方法分为4个阶段: 
(一)、gcc编译:产生插装后的目标文件test、gcov结点文件 test.gcno    #gcc -fprofile-arcs -ftest-coverage -o test test.c    # ls 
   test   test.c   test.gcno 
   说明:参数 fprofile-arcs 和 ftest-coverage 告诉gcc编译器:(1)在目标文件test 插装跟踪代码;(2)生成供gcov使用 test.gcno [gcov node 文件]。 
         因此,这里的生成的目标文件比正常编译的文件大。   
(二)、运行目标文件:收集运行覆盖信息 test.gcda     # ./test 
      Success  -- 这里是运行结果。     # ls 
      test test.c test.gcno test.gcda    这里test.gcda运行结果,  
(三)、gcov产生报告信息: test.c.gcov     #gcov  test.c       File 'test.c' 
      Lines executed: 87.50% of 8       test.c: creating 'test.c.gcov'    #ls 
      test test.c test.c.gcov test.gcda test.gcno 
(四)、lcov:格式化test.c.gcov ,输出到 test.info文件     #lcov -d . -t 'test' -o 'test.info' -b . -c     说明: 
         -d  . :参数 d指路径, "." 指当前路径          -t  "name" :指目标文件,这里 是 test 
         -o  "filename" :输出格式化后的信息文件名 
(五)、genhtml:根据信息文件(.info)产生html 文档,输出到一个文件夹中 
   #genhtml -o result test.info 
    说明: -o  directory :参数o (output)后面跟路径名称,在当前目录下创建指定目录,本例中是result     
至此: 可以在result目录中打开index.html 浏览覆盖信息  

 

 


   
一.创建测试用例 
1.new project->other project ->Qt unit test 2.编写一个单元测试程序 文件列表: 
qtestlib/tutorial1/testqstring.cpp qtestlib/tutorial1/tutorial1.pro 
假设你要测试QString类的行为。首先,你需要一个用于包含测试函数的类。这个类必须从QObject继承: 
class TestQString: public QObject { 
    Q_OBJECT private slots: 
    void toUpper(); }; 
注意你需要包含QTest头文件,并且测试函数必须声明为私有槽,这样测试框架才可以找到并执行他们。 
然后你需要实现测试函数。实现看起来类似这样: 
QVERIFY()宏将计算传入的表达式的值。如果为真,则测试函数继续进行;否则会向测试日志中增加一条描述错误的信息,并且该测试函数会停止执行。 
但是如果需要向测试日志中增加更多的输出信息,你应该使用QCOMPARE()宏: void TestQString::toUpper() { 
    QString str = “Hello”; 
    QVERIFY(str.toUpper() == “HELLO”); } 
如果两个字符串不相等,他们的值都会追加到测试日志中,这样失败的原因就一目了然了。 
最后,为使我们的测试程序能够单独执行,需要加入下列两行: QTEST_MAIN(TestQString) 
#include “testqstring.moc” 
QTEST_MAIN()宏将扩展成一个简单的main()函数,该main()函数会执行所有的测试函数。注意:如果测试类的声明和实现都在同一个cpp文件中,需要包含产生的moc文件,以使Qt的内省机制起作用。 执行测试程序 
运行生成的可执行文件,你会看到下列输出: 
********* Start testing of TestQString ********* Config: Using QTest library 4.5.1, Qt 4.5.1 PASS   : TestQString::initTestCase() PASS   : TestQString::toUpper() 
PASS   : TestQString::cleanupTestCase() Totals: 3 passed, 0 failed, 0 skipped 

 

 


********* Finished testing of TestQString *********     
三.Mac 下具体配置 
1.确保 gcol ,lcol,genhtml 已经安装 2.在工程文件pro中添加 
QMAKE_CXXFLAGS += -g -Wall -fprofile-arcs -ftest-coverage -O0 QMAKE_LFLAGS += -g -Wall -fprofile-arcs -ftest-coverage  -O0   
LIBS += \     -lgcov 
3. 创建processCoverage.sh #!/bin/bash 
############################################################################## 
# Copyright (c) 2013, Robert Wloch 
# All rights reserved. This program and the accompanying materials # are made available under the terms of the Eclipse Public License v1.0 # which accompanies this distribution, and is available at # http://www.eclipse.org/legal/epl-v10.html # 
# Contributors: 
# Robert Wloch - initial API and implementation 
##############################################################################  
if [ ! $# -eq 3 ]; then 
  echo "usage: ${0} <gcov-files-dir> \"<file-pattern>\" <target-dir>"   exit 0 fi 
lcov -d ${1} -c -o ${1}/coverage.info  
lcov --list-full-path -e ${1}/coverage.info ${2} –o ${1}/coverage-stripped.info  
genhtml -o ${3} ${1}/coverage-stripped.info  
lcov -d ${1} –z  
exit 0

posted @ 2014-12-09 13:15  Avatarx  阅读(930)  评论(0编辑  收藏  举报