0

我想为 C 代码测试创建模拟函数并了解在 target_link_libraries() 中可以选择包装类似于模拟的函数,但不明白它是如何工作的?

target_link_libraries(IntegrationTests crypto-testutils mbedcrypto mbedtls sodium cmocka-static
"-Wl,\
--wrap=ExportCTRKeys,\
--wrap=malloc,\
--wrap=GenRandom)

我如何编写这个包装功能以及它是如何工作的?

4

2 回答 2

2

target_link_libraries获取以 开头的参数时-,它将它们视为链接器命令行选项并将它们原封不动地传递给链接器。因此,这与 CMake 无关,而与 ld 无关。您可以在 ld 参考文档中研究这一点,例如这个

--wrap=symbol
对符号 使用包装函数。任何未定义的符号引用都将被解析为. 任何未定义的引用都将被解析为符号。"__wrap_symbol""__real_symbol"

这可用于为系统函数提供包装器。应该调用包装函数。如果它想调用系统函数,它应该调用."__wrap_symbol""__real_symbol"

这是一个简单的例子:

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

如果您使用--wrap malloc将其他代码与此文件链接,则所有调用都"malloc"将改为调用该函数"__wrap_malloc"。对"__real_malloc"in的调用"__wrap_malloc"将调用真正的"malloc"函数。

您可能还希望提供一个"__real_malloc"函数,以便没有--wrap选项的链接会成功。如果这样做,则不应将 的定义与;"__real_malloc"放在同一文件中"__wrap_malloc"。如果你这样做了,汇编器可能会在链接器有机会将其包装到"malloc".

于 2019-09-10T12:32:13.103 回答
1

由于问题是关于 CMake,让我从 CMake 的角度提出一个替代方案。使用 CMake,您可以构建测试来编译要测试的源文件 (SUT) 以及您的测试,而不是与正在测试的目标链接。

这会增加一些额外的编译时间,因为源文件需要在测试中重新编译。如果这是一个问题,请停止阅读。

假设这对您来说不是问题,那么您可以简单地拉入 PUBLIC 标头依赖项,而无需拉入它们的实现。然后,您可以为测试文件中使用的依赖项实现模拟/伪造。

这是一个示例源文件 include/foo.h、foo.c 和 CMakeLists.txt。

#include <ext_decl.h> //Declares struct ext_lib_struct_t in a CMake target ext_decl_lib
int foo(struct ext_lib_struct_t a);
#include <foo.h> //Declares void* foo();
#include <ext_impl.h> //This is the header declaring extlibcall in a CMake target ext_impl_lib
int foo(struct ext_lib_struct_t a) {
   return extlibcall(a); //A mock will be used for this call. 
}
add_library(foo_lib)
target_sources(foo_lib
  PRIVATE
    foo.c
)
target_link_libraries(foo_lib
PUBLIC
    ext_decl_lib
PRIVATE
    ext_impl_lib
)
target_include_directories(foo_lib
  PUBLIC
    include # Where foo.h is.
)

这是您的测试文件 testfoo.c。使用您最喜欢的单元测试框架对其进行自定义。

#include <foo.h>
#include <assert.h>
int test_data;
int extlibcall() {
 return test_data;
}
void testA() { //Replace this with test cases from your favorite unit testing platform.   
   test_data = 2;
   int tr = foo();
   ASSERT(test_data == tr); //Use your favorite assert utility. 
}
int main(void)
{
    testA();
    return 0; 
}

最后是你的测试的 CMake 指令,它可以进入你的测试目录中的 CMakeLists.txt 文件。

add_executable(testlibfoo)
target_sources(testlibfoo
  PRIVATE
    testfoo.c
    $<TARGET_PROPERTY:foo_lib,SOURCE_DIR>/foo.c
)
target_include_directories(test_unity_sall_mgm_join
  PRIVATE
    include
    $<TARGET_PROPERTY:foo_lib,INCLUDE_DIRECTORIES>
)
target_link_libraries(test_unity_sall_mgm_join
  PRIVATE
    assert_lib
    #Add your favorite unit test framework and/or other libraries used in the test
)

测试代码只编译一次 foo.c,因为 foo_lib 没有链接。但是,当其他一些库链接到 foo_lib 时,它可能会在稍后为您的生产代码重新编译。

于 2022-02-02T21:08:09.417 回答