AshishP
AshishP

Reputation: 39

How does target_link_libraries(--wrap) work?

I want to create mock functions for C code testing and get to know that in target_link_libraries() have option to wrap the function which work similar to mock, But not understand how it will work?

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

How do I write this wrap functionality and how it will work?

Upvotes: 1

Views: 2727

Answers (2)

Babazula
Babazula

Reputation: 11

Since the question is about CMake, let me propose an alternative from CMake perspective. With CMake you can construct your tests to compile your source file to be tested (SUT) together with your test instead of linking with the target being tested.

This would add some extra compilation time as the source file needs to be recompiled within the test. If this is a problem please, you might stop reading.

Assuming this is not an issue for you, then you can simply pull in the PUBLIC header dependencies without pulling in their implementations. You can then implement mocks/fakes for dependencies used in your test file.

Here is an example source file include/foo.h, foo.c, and 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.
)

Here is your test file, testfoo.c. Customize this with your favorite unit testing framework.

#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; 
}

And finally CMake instructions for your test, which can go into a CMakeLists.txt file in your test directory.

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
)

Test code compiles foo.c only once since foo_lib is not linked. However, it will probably be recompiled later for your production code when some other library links to foo_lib.

Upvotes: 1

When target_link_libraries gets arguments which start with -, it treats them as linker command-line options and passes them untouched to the linker. This therefore has nothing to do with CMake and everything to do with ld. You can study this in ld reference documentation, such as this one:

--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".

Upvotes: 2

Related Questions