0

I'm using CppUTest to handle unit testing with my C library. However, I'm having an issue when compiling the test file.

Here's my Makefile (The CPPUTEST_HOME var is an environment variable):

CPPFLAGS += -I$(CPPUTEST_HOME)/include
CXXFLAGS += -include $(CPPUTEST_HOME)/include/CppUTest/MemoryLeakDetectorNewMacros.h
CFLAGS += -include $(CPPUTEST_HOME)/include/CppUTest/MemoryLeakDetectorMallocMacros.h
LD_LIBRARIES = -L$(CPPUTEST_HOME)/lib -lCppUTest -lCppUTestExt

all:
        c++ -g -Wall $(CPPFLAGS) $(CXXFLAGS) $(CFLAGS) commonTest.cpp -o commonTest $(LD_LIBRARIES)

clean:
        rm -rf *.dSYM commonTest

When I run make, the following output is given:

Undefined symbols for architecture x86_64:
  "_test_common_algos_commonCalloc_wrapper_c", referenced from:
      TEST_common_algos_commonCalloc_Test::testBody() in commonTest-992552.o
  "_test_common_algos_commonMalloc_wrapper_c", referenced from:
      TEST_common_algos_commonMalloc_Test::testBody() in commonTest-992552.o
  "_test_common_algos_commonRealloc_wrapper_c", referenced from:
      TEST_common_algos_commonRealloc_Test::testBody() in commonTest-992552.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [all] Error 1

To me, this seems like a linking problem. However, I don't think I've done anything wrong according to the manual:

commonTest.c:

#include "../common.h"
#include "CppUTest/TestHarness_c.h"

void *ptr = NULL;
int list[10] = {1, 2, 32, 33, 2, 5, 7, 3, 9, 34};

TEST_GROUP_C(common_algos)
{

};

TEST_C(common_algos, commonMalloc)
{
  ptr = malloc_c(6);
  CHECK_EQUAL_C_INT(algos_error, ALGOS_OK);
  strncpy(ptr, "Hello", 5);
  CHECK_EQUAL_C_STRING("Hello", ptr);
  free_c(ptr);
}

TEST_C(common_algos, commonCalloc)
{
  ptr = calloc_c(sizeof(int), 10);
  CHECK_EQUAL_C_INT(algos_error, ALGOS_OK);
  memcpy(ptr, list, sizeof(int)*10);
  for (int i = 0; i < 10; i++)
      CHECK_EQUAL_C_INT(list[i], ptr[i]);
  free_c(ptr);
}

TEST_C(common_algos, commonRealloc)
{
  ptr = malloc_c(10);
  CHECK_EQUAL_C_INT(ALGOS_OK, algos_error);
  memcpy(ptr, list, sizeof(int)*10);
  ptr = realloc_c(ptr, 20);
  CHECK_EQUAL_C_INT(ALGOS_OK, algos_error);
  memcpy(ptr + 10, list, sizeof(int)*10);
  for (int i = 9; i < 19; i++)
      CHECK_EQUAL_C_INT(list[i-9], ptr[i]);
  free_c(ptr);
}

commonTest.cpp:

#include "CppUTest/CommandLineTestRunner.h"
#include "CppUTest/TestHarness_c.h"

TEST_GROUP_C_WRAPPER(common_algos)
{

};

TEST_C_WRAPPER(common_algos, commonMalloc);
TEST_C_WRAPPER(common_algos, commonCalloc);
TEST_C_WRAPPER(common_algos, commonRealloc);


int main(int ac, char **av)
{
 return RUN_ALL_TESTS(ac, av);
}

Lastly, my directory structure:

common
  |______ common.c
  |
  |______ common.h
  |
  |______ test
           |
           |____commonTest.c
           |
           |____commonTest.cpp

For some context, I'm not a C++ programmer, and I'm not experienced with using CppUTest, so bear with me.

Thanks for any help and feel free to ask questions.

S. Sharma
  • 203
  • 1
  • 11
  • Why `TEST_GROUP_C_WRAPPER` defined both in `.c` & `.cpp`. Just casting my eyes over the manual I assume it should be defined only in `.cpp`. – aep Mar 20 '19 at 01:51
  • What do you mean by that? – S. Sharma Mar 20 '19 at 01:59
  • Your `TEST_GROUP_C_WRAPPER(common_algos){}` is defined both in `commonTest.cpp` & `commonTest.c`. Isn't just one definition needed in `.cpp` file i.e. don't you need to remove that definition from your `commonTest.c` file ? – aep Mar 20 '19 at 02:32
  • Thanks for telling me about that. I'll update the post. – S. Sharma Mar 20 '19 at 02:44
  • If you're calling C functions from C++, the include files need to either include or be wrapped in [extern "C"](https://stackoverflow.com/questions/1041866/what-is-the-effect-of-extern-c-in-c). This tells the C++ compiler not to perform [name mangling](https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_72/rzarg/name_mangling.htm) on functions declared therein. Not using the `extern "C"` form when required results in linker errors, as the expected (mangled) and actual (unmangled) names don't match up. – Perette Mar 20 '19 at 02:46
  • Could you perhaps provide an answer to this post? I think what you're suggesting is interesting, but I'm not exactly sure what functions I should wrap in `extern "C"`. – S. Sharma Mar 20 '19 at 02:54
  • Plus, I thought the point of the `*C_WRAPPER` macros were to allow an interface for CppUTest and C. – S. Sharma Mar 20 '19 at 02:58
  • Is `commonTest.c` file actually compiled. Do you have the `commonTest` object file to link against. I had a look at your `Makefile` but, I couldn't find as where the `.c` file is compiled. – aep Mar 20 '19 at 03:16
  • As far as I could look from the manual, I don't think commonTest.c is actually touched at all :/ . However, I don't know precisely what I'm supposed to do with it, or how do I link it with commonTest.cpp – S. Sharma Mar 20 '19 at 03:20
  • `.c` file should definitely be compiled and link against the generating executable. You compile `commonTest.c` with `cc` and link against that object to create your test executable. Have a look at here as how to achieve this with Makefiles. https://stackoverflow.com/questions/40164827/how-to-make-a-makefile-for-c-and-c-with-sources-in-subdirectories. – aep Mar 20 '19 at 03:34
  • If it's the `extern "C"` issue, the "right" (opinion?) way to do it is to add it to the .h files *for the code being compiled with a C compiler*. [See this thread](https://stackoverflow.com/questions/2168241/is-it-required-to-add-extern-c-in-source-file-also). If you can't modify them (perhaps they come with a library), you can instead wrap the corresponding `#include` directives *in .cpp files being compiled with C++*. Effectively, the `extern "C"` is telling the C++ compiler, "hey, this stuff here was compiled with a C compiler, and thus has unmangled names." – Perette Mar 20 '19 at 03:39

0 Answers0