【问题标题】:C Testing - undefined reference with Unity and CMakeC 测试 - Unity 和 CMake 的未定义参考
【发布时间】:2016-10-06 04:30:17
【问题描述】:

我目前正在尝试使用 C 语言进行代码覆盖和测试的项目设置。我当前的堆栈是 CLion 用于 IDE,Clang 用于编译器,gcov 和 lcov 用于覆盖,Unity 用于测试框架,以及 CMock用于在测试期间进行模拟/存根。

我目前的包结构如下:

app/root
  | build
      | *.*
  |- cmake
     |- modules
        |- CodeCoverage.cmake
  |- coverage
      |- coverage.info
  |- external
      |- Unity
      |- CMock
      |- CMakeLists.txt
  |- src
      |- *.c
      |- *.h
      |- CMakeLists.txt
  |- tests
      |- *.c
      |- *.h
      |- CMakeLists.txt
  |- CMakeLists.txt

我更高级别的 CMakeLists.txt 看起来像:

cmake_minimum_required(VERSION 3.6)

project(my_c_app)

set(CMAKE_C_COMPILER "/usr/bin/clang")

set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/cmake/modules)


set(CMAKE_VERBOSE_MAKEFILE ON)

add_subdirectory(external)
add_subdirectory(src)
add_subdirectory(tests)

我的应用级 CMakeLists.txt 如下所示:

SET(CMAKE_CXX_FLAGS "-O0")
SET(CMAKE_C_FLAGS "-DLINUX -O0 -Wall -std=c99")

set(SOURCE_FILES
        util.c
        util.h)

add_executable(my_c_app ${SOURCE_FILES})

set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
target_link_libraries(my_c_app Threads::Threads)

target_include_directories(my_c_app PUBLIC ${PROJECT_SOURCE_DIR}/include)

我的测试级别 CMakeLists.txt 看起来像:

enable_testing()

include(CodeCoverage)
include(CTest)

SET(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
SET(CMAKE_C_FLAGS "-DLINUX -O0 -Wall -std=c99 -g -fprofile-arcs -ftest-coverage")

SETUP_TARGET_FOR_COVERAGE(coverage tests ${PROJECT_SOURCE_DIR}/coverage/coverage "'/usr/*';'tests/*';'external/*'")

add_executable(tests util_test.c)

target_link_libraries(tests Unity CMock)

add_test(tests util_test.c)

目前我的问题是我没有正确地做某事。我在尝试测试 util.c 中的函数时得到一个未定义的引用:

CMakeFiles/tests.dir/util_test.c.o: In function `test_my_method':
/home/patches/my_c_app/tests/util_test.c:6: undefined reference to `my_method'

我的 util_test.c 目前是:

#include <unity.h>
#include "../src/util.h"

void test_my_method(void) {
    uchar result = my_method();
    // assertion and other logic would go here
}

int main(void) {
    UNITY_BEGIN();
    RUN_TEST(test_my_method);
    return UNITY_END();
}

我是测试驱动的 c 开发和 CMake 的菜鸟,那么我应该如何设置测试以使它们依赖于 src 中的 c 文件?

如果我只是执行 TEST_ASSERT_EQUAL(1,1) 而不是调用 util.c 函数,我会看到:

1 Tests 0 Failures 0 Ignores
OK

Process finished with exit code 0

所以我觉得我 100% 陷入了某种链接器问题。

【问题讨论】:

    标签: c unit-testing cmake ctest unity-test-framework


    【解决方案1】:

    util.c 不是tests 源的一部分,也不是在链接到tests 的库中编译的。所以你没有为my_method 提供任何定义给tests,因此是未定义的引用。

    您的可执行文件my_c_app 的源中没有main.c 文件。我猜你的主要功能是在util.c 中定义的。如果我是对的,请将其放入 main.c 文件中,并将您的应用级别 CMakeLists.txt 更改为:

    SET(CMAKE_CXX_FLAGS "-O0")
    SET(CMAKE_C_FLAGS "-DLINUX -O0 -Wall -std=c99")
    
    set(SOURCE_FILES
            util.c
            util.h)
    
    add_library(my_c_lib STATIC ${SOURCE_FILES})
    
    set(THREADS_PREFER_PTHREAD_FLAG ON)
    find_package(Threads REQUIRED)
    target_link_libraries(my_c_app Threads::Threads)
    
    target_include_directories(my_c_app PUBLIC ${PROJECT_SOURCE_DIR}/include)
    
    add_executable(my_c_app main.c)
    target_link_libraries(my_c_app my_c_lib)
    

    现在您的源代码已编译到静态库my_c_lib 中,您可以链接到该库。您的应用已链接到它,您也可以在测试级别 CMakeLists.txt 中链接您的测试:

    enable_testing()
    
    include(CodeCoverage)
    include(CTest)
    
    SET(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
    SET(CMAKE_C_FLAGS "-DLINUX -O0 -Wall -std=c99 -g -fprofile-arcs -ftest-coverage")
    
    SETUP_TARGET_FOR_COVERAGE(coverage tests ${PROJECT_SOURCE_DIR}/coverage/coverage "'/usr/*';'tests/*';'external/*'")
    
    add_executable(tests util_test.c)
    
    target_link_libraries(tests Unity CMock my_c_lib)
                                            ^^^^^^^^
    
    add_test(tests util_test.c)
    

    关于这一行:

    add_test(tests util_test.c)
    

    我在documentation 中看不到获取目标及其来源的签名。你想用这个来达到什么目的?

    请注意,您可以使用C_STANDARD 而不是CMAKE_C_FLAGS 指定C 版本:

    SET(CMAKE_C_STANDARD 99)
    

    编译定义也可以用target_compile_definitions 声明以避免全局污染。

    【讨论】:

    • 非常感谢您提供的详细信息。你说的很清楚。为了增加复杂性,将 src 目录包装到 *.so 中,但是对于我的情况来说,独特且未提及的是,我将 *.so 放入 rails 应用程序并使用 FFI 将调用放入其中,因此 main 是不必要。我觉得添加这会使问题变得过于复杂。我可以通过添加 add_executable(tests ${TEST_SOURCES}) 来解决这个问题,其中 TEST_SOURCES 是使用 src 中的必要文​​件定义的。但是,是的,你所说的很受欢迎。
    猜你喜欢
    • 2016-07-01
    • 2017-10-13
    • 2015-09-14
    • 1970-01-01
    • 2014-09-20
    • 1970-01-01
    • 2010-10-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多