"ctest"与"make check":糟糕的构建时间与中断的选项传递



要在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

首先,我要指出,ctestmake 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模块在ONBUILD_TESTING选项时自动调用该命令。

以上可以在 CLI 上使用,如下所示:

  1. 创建测试(默认):

    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/releasectest/cmake --build . --target test构建运行测试。

  2. 不创建测试,设置-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-testsctest运行任何测试,因为尚未构建任何测试。 该命令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

相关内容

  • 没有找到相关文章

最新更新