如何使用gcov对动态库进行单元测试?

时间:2016-02-17 22:05:09

标签: c unit-testing gcc gcov

我正在构建一个动态链接库,并且正在建立一个简单的测试套件。我想使用gcov生成静态代码分析覆盖率报告。

我的库是一个包含函数实现的C文件和包含函数原型的头文件。我的测试套件只是一个以各种方式调用函数并确认输出有效性的应用程序。

我正在使用-fprofile-arcs-ftest-coverage标志编译库和测试套件,如on GNU's guide to GCOV所述。我还包括-O0标志来禁用编译器优化和-g标志以启用调试符号。从测试套件生成的可执行文件动态链接到库。

所有文件都干净利落地编译,没有任何警告,但无法将测试套件链接到库 - 引用了隐藏符号__gcov_merge_add"。如果我在没有-fprofile-arcs-ftest-coverage标志的情况下编译,则链接成功,我可以运行测试套件可执行文件。

在阅读GCOV的GNU指南后,我还有一些问题仍未解决。

  1. 为什么链接失败?我该如何解决这个问题?
  2. 在编译两者库和测试套件时,是否需要包含配置文件和coverage标志?
  3. 这是我的inc/mylib.h文件:

    #ifndef __MYLIB_H__
    #define __MYLIB_H__
    
    #ifdef __cplusplus
    extern "C" {
    #endif /* __cplusplus */
    
    int
    foo (int a);
    
    int
    bar (int a);
    
    #ifdef __cplusplus
    }
    #endif /* __cplusplus */
    
    #endif /* __MYLIB_H__ */
    

    这是我的src/mylib.c文件:

    #include <stdio.h>
    #include "mylib.h"
    
    int foo(int a) {
        if (a > 5) {
            return 5;
        }
        return a;
    }
    
    int bar(int a) {
        if (a < 0) {
            return 0;
        }
        return a;
    }
    

    这是我的test/unittests.c文件:

    #include <stdio.h>
    #include "mylib.h"
    
    void run_foo_tests() {
        int inputs[] = {3, 6};
        int expected_results[] = {3, 5};
        int i, actual_result;
    
        for ( i = 0; i < sizeof(inputs) / sizeof(int); i++ ) {
            actual_result = foo(inputs[i]);
            if (actual_result == expected_results[i]) {
                printf("Test %d passed!\n", i + 1);
            } else {
                printf("Test %d failed!\n", i + 1);
                printf("  Expected result: %d\n", expected_results[i]);
                printf("  Actual result: %d\n", actual_result);
            }
        }
    }
    
    void run_bar_tests() {
        int inputs[] = {3, -1};
        int expected_results[] = {3, 0};
        int i, actual_result;
    
        for ( i = 0; i < sizeof(inputs) / sizeof(int); i++ ) {
            actual_result = bar(inputs[i]);
            if (actual_result == expected_results[i]) {
                printf("Test %d passed!\n", i + 1);
            } else {
                printf("Test %d failed!\n", i + 1);
                printf("  Expected result: %d\n", expected_results[i]);
                printf("  Actual result: %d\n", actual_result);
            }
        }
    }
    
    int main(int argc, char *argv[]) {
        run_foo_tests();
        run_bar_tests();
        return 0;
    }
    

    这是我的Makefile

    CC=gcc
    CFLAGS=-Wall -std=c89 -g -O0 -Iinc -fprofile-arcs -ftest-coverage
    
    all: clean build run_tests
    
    build:
        $(CC) $(CFLAGS) -fPIC -c src/*.c -o lib/mylib.o
        $(CC) -shared lib/mylib.o -o lib/libmylib.so
        $(CC) $(CFLAGS) test/*.c -o bin/unittests -Llib -lmylib
    
    run_tests:
        LD_LIBRARY_PATH=lib bin/unittests
        gcov src/*.c
    
    clean:
        rm -f *.gcda *.gcno *.gcov
        rm -rf bin lib ; mkdir bin lib
    

    当我运行make时,我会看到此输出:

    rm -f *.gcda *.gcno *.gcov
    rm -rf bin lib ; mkdir bin lib
    gcc -Wall -std=c89 -g -O0 -Iinc -fprofile-arcs -ftest-coverage -fPIC -c src/*.c -o lib/mylib.o
    gcc -shared lib/mylib.o -o lib/libmylib.so
    gcc -Wall -std=c89 -g -O0 -Iinc -fprofile-arcs -ftest-coverage test/*.c -o bin/unittests -Llib -lmylib
    /usr/bin/ld: bin/unittests: hidden symbol `__gcov_merge_add' in /usr/lib/gcc/x86_64-redhat-linux/4.8.5/libgcov.a(_gcov_merge_add.o) is referenced by DSO
    /usr/bin/ld: final link failed: Bad value
    collect2: error: ld returned 1 exit status
    make: *** [build] Error 1
    

1 个答案:

答案 0 :(得分:1)

您需要为链接器指定命令行参数-lgcov(通常-ftest-coverage隐含-lgcov,但您有一个单独的链接步骤,其中-ftest-coverage未给出命令行参数)。或者,您可以使用--coverage命令行参数,这也是-fprofile-arcs-ftest-coverage的快捷方式,如下所述:http://www.univ-orleans.fr/sciences/info/ressources/webada/doc/gnat/gcc_3.html

  

- 覆盖

     

此选项用于编译和链接已检测的代码   覆盖分析。该选项是`-fprofile-arcs&#39;的同义词。   `-ftest覆盖&#39; (编译时)和`-lgcov&#39; (当链接时)。看到   有关这些选项的文档以获取更多详细信息。

在同一个地方,顺便说一句,还解释说你不必用这些选项编译所有文件,希望能回答你的问题2.