四、扩展话题

  通过上面三部分的介绍,相信绝大多数覆盖率问题都可以解决,下面2个问题是我们在实际运行过程中遇到的,也分享一下。

  覆盖率的结果只有被测试到的文件会被显示,并非所有被编译的代码都被作为覆盖率的分母

  实际上,可以看到整个覆盖率的产生的过程是4个步骤的流程,一般都通过外围脚本,或者makefile/shell/python来把整个过程自动化。2个思路去解决这个问题,都是通过外围的伪装。第一个,是修改lcov的 app.info ,中间文件,找到其他的文件与覆盖率信息的地方,结合makefile,把所有被编译过的源程序检查是否存于 app.info 中,如果没有,增加进去。第二个伪装,是伪装 *.gcda,没有一些源码覆盖率信息的原因是该文件没有被调用到,没有响应的gcda文件产生。toast(http://toast.taobao.org/)是通过第一种伪装来实现的,更多了解需要去看下开源代码。

  2. 后台进程的覆盖率数据收集;

  其实上述覆盖率信息的产生,不仅可以针对单元测试,对于功能测试同样适用。但功能测试,一般linux下c/c++都是实现了某个Daemon进程,而覆盖率产生的条件是程序需要正常退出,即用户代码调用 exit 正常结束时,gcov_exit 函数才得到调用,其继续调用 __gcov_flush 函数输出统计数据到 *.gcda 文件中。同样2个思路可以解决这个问题,

  第一,给被测程序增加一个 signal handler,拦截 SIGHUP、SIGINT、SIGQUIT、SIGTERM 等常见强制退出信号,并在 signal handler 中主动调用 exit 或 __gcov_flush 函数输出统计结果。但这个需要修改被测程序。这个也是我们之前的通用做法。但参加过清无同学的一个讲座后,发现了下面第二种更好的方法。

  第二,借用动态库预加载技术和 gcc 扩展的 constructor 属性,我们可以将 signalhandler 和其注册过程都封装到一个独立的动态库中,并在预加载动态库时实现信号拦截注册。这样,可以简单地通过如下命令行来实现异常退出时的统计结果输出了。

  五、其他编程语言

  在我们的工程实践中,还有其他的编程语言,都涉及到覆盖率的产生,我们的工程实践推荐下面的方法,

  c/c++, 本文介绍的方法;

  Java, Maven cobertura 插件;

  Python, PyUnit + coverage.py;

  Php, phpunit + –coverage-html ;

  Perl, Test::Class 和 Devel::Cover;

  Shell, shUnit2 + shcov;