2015-12-29 73 views
3

我有一个很难用cmocka找到一个答案,一个nitch情况下,失败(模拟)测试的malloc,并使用gcov的cmocka的malloc测试OOM和gcov的

更新有关cmocka +的gcov:我注意到我只要我在我的cmocka测试中嘲笑一个函数,就可以获得空的gcda文件。为什么?谷歌搜索cmocka和gcov给出了结果,人们在一起讨论使用这两者。似乎大多数人都在使用CMake,稍后我会看看,但应该没有理由(我能想到)需要我使用cmake。为什么我不能只使用cmocka和--coverage/-lgcov标志?

一部开拓创新的问题:

我已经尝试主要是基于关闭的两个主要思路无数的组合:

我试着用轮候册, - 包=的malloc所以malloc的调用包装。从我的cmocka测试中,我尝试使用will_return(__ wrap_malloc,(void *)NULL)来模拟malloc失败。在我的包装函数中,我使用mock()来确定是否应该返回__real_malloc()或NULL。这有理想的效果,但是我发现gcov无法创建gcda文件,这是包装malloc原因的一部分,所以我可以测试malloc失败并获取代码覆盖率结果。我觉得我玩过带有符号的肮脏游戏,并搞砸了malloc()调用,称为表单其他编译单元(gcov?cmocka?)。

我试过的另一种方法是向我们gcc -include使用#define for malloc来调用“my malloc”并编译我的目标代码以使用mymalloc.c(定义“my malloc”)进行测试。因此,#define malloc _mymalloc可以帮助我仅从目标测试代码中调用“特殊malloc”,而在其他任何被调用的地方独自离开malloc(即,让其他编译单元独立,以便它们始终调用真实的malloc)。然而,我不知道如何正确使用will_return()和mock()来检测失败案例与成功案例。如果我正在测试malloc()失败,我得到我想要的,我从基于mock()返回NULL的“malloc”返回NULL - 这全部在malloc的包装函数中完成,该函数仅在目标代码中调用。但是,如果我想返回真实malloc的结果,那么cmocka将失败,因为我没有从mock()返回结果。我希望我可以只让cmocka从mock()宏中取出结果,然后不关心我没有返回结果,因为我需要来自malloc()的实际结果,所以测试下的代码可以正常工作。

我觉得应该可以将malloc测试与cmocka结合起来,并获得gcov结果。

无论答案是我想拉下面或类似的东西。

int business_code() 
{ 
    void* d = malloc(somethingCalculated); 
    void* e = malloc(somethingElse); 
    if(!d) return someRecovery(); 
    if(!e) return someOtherRecovery(); 
    return 0; 
} 

然后有cmocka测试,如

cmocka_d_fail() 
{ 
    will_return(malloc, NULL); 
    int ret = business_code(); 
    assert_int_equal(ret, ERROR_CODE_D); 
} 

cmocka_e_fail() 
{ 
    will_return(malloc, __LINE__); // someway to tell wrapped malloc to give me real memory because the code under test needs it 
    will_return(malloc, NULL); // I want "d" malloc to succeed but "e" malloc to fail 
    int ret = business_code(); 
    assert_int_equal(ret, ERROR_CODE_E); 
} 

我亲近与一些#的定义/包想法,我试过,但最终我要么陷入困境的malloc和造成的gcov到吐不出来我的覆盖率数据或我没有办法让cmocka运行malloc个案并返回真实内存,即不是从mock()调用返回。一方面,我可以从我的测试驱动程序中调用真正的malloc,然后将其传递给will_return,但是我的test_code不知道所需内存的大小,只有被测代码知道这一点。

给定的时间限制我不想离开cmocka和我目前的测试基础结构。我会考虑未来的其他想法,尽管如果我想要的是不可能的。我在找的东西我知道并不新鲜,但我正在尝试使用cmocka/gcov解决方案。

感谢

回答

2

这一切都归结到什么符号,我搞乱,或者使用-LW, - 包装或聪明的#define。无论是哪种情况,我都是在打破其他呼叫站点的符号,破坏代码或混淆cmocka而没有排队排队返回。

此外,我的gcda文件没有正确生成的原因是我尝试使用-Wl,wrap = fseek和cmocka的mock()让我感到困惑。

fseek/malloc/etc上的一个聪明的#define与mock()相结合,用于在您的包装实现中调用的符号,可以简单地查询测试套件以查看是否应该返回一些伪造的结果,导致测试失败或返回真实结果。有点哈克,但伎俩。

+0

我应该补充说有可能有更好的方法来做到这一点,如果有人想分享,我对更好的解决方案感兴趣。 –

+0

请不要害怕自己给出答案。对于未来找到解决方案的其他人来说,这会让事情变得更加清楚。 – Mawg

0

此替代方法适用于我:换行_test_malloc()而不是malloc()

工作示例可在https://github.com/CESNET/Nemea-Framework/blob/2ef806a0297eddc920dc7ae71731dfb2c0e49a5b/libtrap找到。 tests/test_trap_buffer.c包含一个包裹函数__wrap__test_malloc()(在名称注意4X“_”)的实现

void *__real__test_malloc(const size_t size, const char* file, const int line); 

void *__wrap__test_malloc(size_t size) 
{ 
    int fail = (int) mock(); 
    if (fail) { 
     return NULL; 
    } else { 
     return __real__test_malloc(size, __FILE__, __LINE__); 
    } 
} 

和如test_create_destroy()测试tb_init()函数,它使用3×malloc()

static void test_create_destroy(void **state) 
{ 
    trap_buffer_t *b = NULL; 
    (void) state; /* unused */ 

    b = tb_init(0, 0); 
    assert_null(b); 
    b = tb_init(0, 1); 
    assert_null(b); 
    b = tb_init(1, 0); 
    assert_null(b); 

    will_return(__wrap__test_malloc, 0); 
    will_return(__wrap__test_malloc, 0); 
    will_return(__wrap__test_malloc, 0); 
    b = tb_init(10, 100000); 
    assert_non_null(b); 
    tb_destroy(&b); 
    tb_destroy(&b); 
    tb_destroy(NULL); 
} 

对于完整性,tb_init()是在src/trap_buffer.c线146

编译可以像这样(样品从生成文件)中运行:

buffer: 
    gcc --coverage -g -O0 -DUNIT_TESTING -c tests/test_trap_buffer.c 
    gcc --coverage -g -O0 -DUNIT_TESTING -c src/trap_buffer.c 
    gcc -g -O0 -Wl,--wrap=_test_malloc -lcmocka --coverage -DUNIT_TESTING -o test_buffer test_trap_buffer.o trap_buffer.o 

请参阅UNIT_TESTING为cmocka定义的预处理器宏,这很重要,因为它可以在我们的代码中测试分配函数。

最后,运行测试会为我们生成*.gcda文件,因此我们可以直观地看到代码覆盖率。测试的输出tb_init()https://codecov.io/gh/CESNET/Nemea-Framework/src/775cfd34c9e74574741bc6a0a2b509ae6474dbdb/libtrap/src/trap_buffer.c#L146