LINUX.ORG.RU

Unit-тестирование и --wrap (ld)

 , ,


0

2

Есть некий файлик mylib/ldap.c, в котором есть функция int search(LDAP *ld, const char *id). Этот файлик входит в разделяемую библиоту libfoobar.

Задача — изолировать и оттестировать функцию foobar. Однако проблема в том, что эта функция использует как другие функции с библиотеки libfoobar так и функции с libldap-2.4.

Библиотека libfoobar собирается отдельно от тестов (отдельным CMake проектом). Затем тест собирается приблизительно так:

add_executable(search_test search_test.c)
set_target_properties(search_test PROPERTIES
    LINK_FLAGS "-Wl,--wrap=internalFunc -Wl,--wrap=ldap_search_s"
)
target_link_libraries(search_test ${FOOBAR_LIBRARIES})
add_test(NAME search_test COMMAND search_test)
И есть приблизительно такой код в search_test.c:
int __wrap_internalFunc(const char *str) {
    fprintf(stderr, "## DEBUG: __wrap_internalFunc: %s\n", str);
    return __real_internalFunc(str);
}

static void test_search(void **state) {
    LDAP *ld = (*(TEST_DATA **) state)->ldap;

    int err = search(ld, "something");
    assert(err == LDAP_SUCCESS);
}

Проблема состоит в том, что --wrap не срабатывает для внутренних функций, т.е. search() не дергает __wrap_internalFunc(), а напрямую лезет к оригинальной internalFunc() (internalFunc не является static и находится в одном исходном файле с search()). Пробовал собирать libfoobar статически — не помогает.

Помогите пожалуйста понять процесс линковки, и где моя ошибка.

P.S. Собственно, заюзана фича --wrap из ld. man ld:

--wrap=symbol
Use a wrapper function for symbol. Any undefined reference to symbol will be resolved to «__wrap_ symbol ». Any undefined reference to «__real_ symbol » will be resolved to symbol.
This can be used to provide a wrapper for a system function. The wrapper function should be called «__wrap_ symbol ». If it wishes to call the system function, it should call «__real_ symbol ».

Here is a trivial example:

void *
__wrap_malloc (size_t c)
{
  printf ("malloc called with %zu\n", c);
  return __real_malloc (c);
}

If you link other code with this file using --wrap malloc, then all calls to «malloc» will call the function «__wrap_malloc» instead. The call to «__real_malloc» in «__wrap_malloc» will call the real «malloc» function.
You may wish to provide a «__real_malloc» function as well, so that links without the --wrap option will succeed. If you do this, you should not put the definition of «__real_malloc» in the same file as «__wrap_malloc»; if you do, the assembler may resolve the call before the linker has a chance to wrap it to «malloc».

Помогите пожалуйста понять процесс линковки, и где моя ошибка.

Линкер берёт скомпилированный бинарник, в котором могут присутствовать ссылки на неизвестные внешние функции, и каким-либо образом затыкает их. Если вызов функции находится в том же файле, что и определение этой функции, то корректный вызов компилятор вполне может вставить сам, и в этом случае до линкера дело просто не дойдёт.

Собственно, да:

Use a wrapper function for symbol. Any undefined reference to symbol will be resolved to «__wrap_ symbol ». Any undefined reference to «__real_ symbol » will be resolved to symbol.

undefined reference

Вызов функции из той же единицы трансляции имеет право оказаться вполне себе defined. На самом деле это всё теория, и я не знаю, как оно там реально происходит, но выглядит похоже.

Перечитал твой пост, понял, что у тебя internalFunc не в единице трансляции с тестом, а в библиотеке. Ну тогда тем более: ты тесту уже готовую библиотеку подсовываешь, внутри которой вообще нет непролинкованных функций, потому и заворачивать во wrap нечего.

devsdc ★★ ()

Тебе нужен hippomocks для моков. И до кучи Catch, если ты до сих пор пользуешься GTest, каким-нибудь.

anonymous ()
Ответ на: комментарий от devsdc

Перечитал твой пост, понял, что у тебя internalFunc не в единице трансляции с тестом, а в библиотеке.

Билдил я библиотеку и статичечки. Ну а статическая либа (насколько я знаю) єто просто архив с обьектными файлами.

В любом случае — internalFunc находится в одном объекте (единице трансляции) вместе с search. Завтра ещё попробую саму библиотеку собрать с -Wl,--wrap=... — авось поможет (хотя судя по твоей заметке — затея так себе)

KennyMinigun ★★★★★ ()
Ответ на: комментарий от KennyMinigun

Почитал про cmocka. Тоже подходящий инструмент.

anonymous ()
Ответ на: комментарий от devsdc

Сделал вот так, вроди помогло:

search_test.c

/* make sure compiler will not spoil all the --wrap fun */
__attribute__((weak))
int internalFunc(const char *str);

/* make static functions accessible */
#include "foobar/ldap.c"

int __wrap_internalFunc(const char *str) { /* ... */ }

Ну и собираю это все как и раньше с разделяемой библиотекой. Надобно только понять, почему оно работает :)

KennyMinigun ★★★★★ ()
Ответ на: комментарий от KennyMinigun

UPD, работает только на одной платформе (mingw-i386). Провал.

KennyMinigun ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.