要在CMake下注册测试,我们需要
enable_testing()
或
include(CTest)
然后对于每个单独的测试(名称fooTest
,可执行foo
)
add_executable(foo <foo_sources>)
add_test(fooTest foo)
然后可以使用命令ctest
运行测试。
此外,我们可以使用命令运行测试make check
,前提是我们添加一次
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND})
对于每个测试,我们通过关键字EXCLUDE_FROM_ALL
和命令add_dependencies
扩展上述内容:
add_executable(foo EXCLUDE_FROM_ALL <foo_sources>)
add_test(fooTest foo)
add_dependencies(check foo)
理想情况下,这将使make check
成为ctest
的别名。它不是这样,至少有两个原因:
(1)make check
是有缺陷的,因为它没有将选项传递给ctest
[2]。特别是,ctest -j4
将并行运行 4 个测试,而make -j4 check
将在目标check
的一个线程中工作,其他三个线程将保持空闲状态。
(2)ctest
是有缺陷的[3,4],因为所有测试都是在all
目标下构建的,即与主应用程序一起构建的。在某些情况下,这可能是所需的行为,但在其他情况下,应该可以推迟生成,直到运行测试。
这是否正确地总结了当前的事态?
有什么办法(吃蛋糕并吃它)吗?
[1] https://gitlab.kitware.com/cmake/community/-/wikis/doc/tutorials/EmulateMakeCheck [2] http://comments.gmane.org/gmane.comp.programming.tools.cmake.user/47300 [3] CMake & CTest:make test 不构建测试 [4] http://public.kitware.com/Bug/view.php?id=8774
首先,我要指出,ctest
和make test
只是简单的命令行工具,用于简单的测试任务。如果你想要一个用于严肃测试的工具,请使用CDash,Buildbot,Jenkins或其他任何东西。
关于CTest的缺陷:对CTest的调用没有构建测试是故意的。在几种情况下,这是一个坏主意:
- 编译测试可能会比运行测试本身花费更多资源。对于内存消耗、硬盘读/写或编译时间,这可能是正确的。因此,并行编译和链接可能很糟糕,但并行执行测试可能是有益的。
- 如何处理编译或链接失败?将其报告为失败?报告没有编译?继续编译其他测试还是立即中止?
Autotools按照你想要的方式做到了,人们已经习惯了。但为什么它应该是一个单位?为什么没有两个命令?混合两个任务并使有特殊需求的项目更加困难有什么好处?
我得出的结论是,创建一个目标build-tests
或类似,并遵循CMake开发人员做出的决定,将构建测试和执行测试分离。然后我可以决定是否要并行构建,如何处理编译失败(例如,传递-k
进行)等等。
唯一的缺点是,此目标仅存在于顶级目录中,不能在子目录中使用。
获得CMake内置的这样一个目标将是一个很好的功能请求。对SO咆哮没有好处。
CTest完全没有缺陷,但您使用CMake和CTest的方式似乎"有缺陷"。命令行界面 (CLI) 工具ctest
的调用通常与 CMake 生成目标的调用无关(目标test
除外)。
在我看来,不应使用 CMake Wiki 中描述的自定义check
目标解决方案,因为它会更改 CMake 的默认行为并且不可配置。
相反,应使用以下使用内置选项BUILD_TESTING
的方法:
include(CTest)
if(BUILD_TESTING)
find_package(GTest MODULE REQUIRED)
add_executable(example_test example_test.cpp)
target_link_libraries(
example_test
PRIVATE
GTest::GTest
GTest::Main
)
add_test(NAME example_test COMMAND example_test)
endif()
include(CTest)
在选项BUILD_TESTING
中定义,它允许控制是否构建项目的所有测试。
引用官方文档:
仅当调用了
enable_testing()
命令时,CMake 才会生成测试。CTest
模块在ON
BUILD_TESTING
选项时自动调用该命令。
以上可以在 CLI 上使用,如下所示:
创建测试(默认):
cmake -Hexample-testing -B_builds/example-testing/release -G"Unix Makefiles" -DCMAKE_BUILD_TYPE=Release cmake --build _builds/example-testing/release --config Release
在这种情况下,命令
cd _builds/example-testing/release
和ctest
/cmake --build . --target test
构建并运行测试。不创建测试,设置
-DBUILD_TESTING=OFF
:cmake -Hexample-testing -B_builds/example-testing/release-no-tests -G"Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=OFF cmake --build _builds/example-testing/release-no-tests --config Release
在这种情况下,命令
cd _builds/example-testing/release-no-tests
和ctest
运行任何测试,因为尚未构建任何测试。 该命令cmake --build . --target test
失败,因为它尚未在 CMake 的配置阶段创建。
我们在这里只是触及了表面。请参考ctest --help
,例如,有很多--build-<...>
选项可以对测试/构建进行更精细的控制,尽管我对此没有任何经验。
我强烈建议阅读以下内容:
- CTest - CMake 文档
- CTest 命令 - CMake 文档
- 测试(1) - CMake 文档
- add_test - CMake 文档
如果您确实想启用测试构建,但通过默认情况下不调用的单独目标,并且不通过 CTest 而是直接运行测试,您可以执行以下操作:
include(CTest)
if(BUILD_TESTING)
find_package(GTest MODULE REQUIRED)
option(
BUILD_TESTING_EXCLUDE_FROM_ALL
"Do not build the testing tree together with the default build target."
OFF
)
if(BUILD_TESTING_EXCLUDE_FROM_ALL)
set(add_executable_args_for_test EXCLUDE_FROM_ALL)
endif()
# The "build_test" target is used to build all test executables.
add_custom_target(
build_test
# Workaround for printing the COMMENT, it does not work without a NOOP
# COMMAND.
COMMAND ${CMAKE_COMMAND} -E echo
COMMENT "Building tests..."
VERBATIM
)
add_executable(example_test ${add_executable_args_for_test} example_test.cpp)
target_link_libraries(
example_test
PRIVATE
GTest::GTest
GTest::Main
)
add_test(NAME example_test COMMAND example_test)
add_dependencies(build_test example_test)
# The "check" target is used to build AND run all test executables.
add_custom_target(
check
# Either invoke the test(s) indirectly via "CTest" (commented) or directly.
# COMMAND ${CMAKE_CTEST_COMMAND}
COMMAND example_test
COMMENT "Building and running test..."
VERBATIM
)
# Alternative to the COMMAND in the add_custom_target. Leads to the same
# behavior as calling "CTest" directly.
# add_custom_command(
# TARGET check
# COMMAND ${CMAKE_COMMAND} ARGS --build ${CMAKE_BINARY_DIR} --target test
# VERBATIM
# )
add_dependencies(check build_test)
endif()
- 请注意,上面的代码不会调用 CTest 或目标
test
来运行测试,而是直接调用测试。 - 请阅读注释和注释代码,了解使用 CTest 的替代方法,这些方法与问题中描述的方法类似。
- 很容易增强上面的代码以支持多个测试可执行文件。
恕我直言,Kitware 应该删除整个 CMake Wiki,因为 Wiki 几乎只包含CMake版本 <3.0 的过时信息。其中的大多数信息不能被视为现代CMake。