cmocka malloc тестирование OOM и gcov

Мне сложно найти ответ на случай nitch с помощью cmocka, проверить malloc на сбой (моделирование) и использовать gcov

Обновление о cmocka + gcov: я заметил, что получаю пустые файлы gcda, как только я имитирую функцию в своих тестах cmocka. Почему? Поиск в Google cmocka и gcov дает результаты, в которых люди говорят об их совместном использовании. Кажется, большинство людей используют CMake, я рассмотрю это позже, но не должно быть никаких причин (которые я могу придумать), которые потребовали бы от меня использования cmake. Почему я не могу просто использовать cmocka с флагами --coverage / -lgcov?

Оригинальный вопрос:

Я пробовал множество комбинаций, в основном основанных на двух основных идеях:

Я пробовал использовать -Wl, - wrap = malloc, поэтому вызовы malloc переносятся. Из моих тестов cmocka я попытался использовать will_return (__ wrap_malloc, (void *) NULL) для имитации сбоя malloc. В моей функции обертывания я использую mock (), чтобы определить, должен ли я возвращать __real_malloc () или NULL. Это дает идеальный эффект, однако я обнаружил, что gcov не может создать файлы gcda, что является одной из причин упаковки malloc, поэтому я могу протестировать сбой malloc И получить результаты покрытия кода. Я чувствую, что играл в грязные игры с символами и испортил вызовы malloc (), вызываемые из других единиц компиляции (gcov? Cmocka?).

Еще один способ, который я попробовал, заключался в использовании gcc -include с использованием #define для malloc для вызова «my malloc» и компиляции моего целевого кода для тестирования с помощью mymalloc.c (определение «my malloc»). Таким образом, #define malloc _mymalloc помогает мне вызывать только «специальный malloc» из целевого тестового кода, оставляя только malloc везде, где он вызывается (т. Е. Оставьте другие объединенные сборки в покое, чтобы они всегда вызывали real malloc). Однако я не знаю, как правильно использовать will_return () и mock () для выявления случаев отказа и случаев успеха. Если я тестирую malloc (), и я получаю то, что хочу, я возвращаю NULL из malloc на основе mock (), возвращающего 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);
}

Я подхожу к некоторым идеям # define / wrap, которые я пробовал, но в конце концов я либо испортил malloc и заставил gcov не выплевывать мои данные о покрытии, либо у меня нет способа заставить cmocka запускать случаи malloc и возвращать реальные память, т. е. не возвращаться из вызовов mock (). С одной стороны, я мог бы вызвать real malloc из моего тестового драйвера, но и передать его в will_return, но мой test_code не знает размер необходимой памяти, это знает только тестируемый код.

учитывая нехватку времени, я не хочу отказываться от cmocka и моей текущей тестовой инфраструктуры. Я бы рассмотрел другие идеи в будущем, если то, что я хочу, невозможно. То, что я ищу, я знаю, не ново, но я пытаюсь использовать решение cmocka / gcov.

Спасибо


person railed mail    schedule 29.12.2015    source источник


Ответы (2)


Все сводится к тому, с какими символами я возился, используя -lW, - wrap или хитроумные #defines. В любом случае я либо затирал символ для других сайтов вызовов и нарушал код, либо сбивал cmocka с толку тем, что не удалял возвраты из очереди.

Также причина того, что мои файлы gcda не были сгенерированы правильно, - это мои попытки использовать -Wl, - wrap = fseek, а mock () cmocka меня испортил.

Умный #define в fseek / malloc / etc в сочетании с mock () для символа, который вызывается в вашей реализации оболочки, может вкратце запросить набор тестов, чтобы увидеть, следует ли вам возвращать что-то фальшивое, чтобы вызвать сбой теста, или вернуть реальный полученные результаты. Немного хакерский, но помогает.

person railed mail    schedule 30.12.2015
comment
Я должен добавить, что, вероятно, есть более эффективные способы сделать это, и я заинтересован в лучших решениях, если кто-то захочет поделиться. - person railed mail; 30.12.2015
comment
Пожалуйста, не бойтесь наградить себя ответом. Это прояснит ситуацию для тех, кто найдет квестон в будущем. - person Mawg says reinstate Monica; 09.06.2016

У меня работает этот обходной путь: оберните _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(), которая использует 3x 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.

Компиляцию можно запустить так (пример из Makefile):

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():

person Tomáš Čejka    schedule 31.08.2016