【问题标题】:cmake: make tests successfully passing part of the build processcmake:使测试成功通过部分构建过程
【发布时间】:2016-04-18 14:41:59
【问题描述】:

我正在尝试将通过测试作为构建过程的一部分。

这里我使用add_custom_command 作为POST_BUILD 步骤运行测试。

function(register_test NAME)

    add_test(${NAME} ${NAME})

    # make the test run as part of the build process
    add_custom_command(TARGET ${NAME} POST_BUILD COMMAND ${NAME})

endfunction()

这种方法的问题在于只有在构建目标时才会运行测试

$ make
[ 50%] Built target lib1
Linking CXX executable ../../Debug/bin/lib1_test
Running 1 test case...
main.cpp(8): fatal error: in "lib1_test": 
    critical check lib1() == "lib1" has failed [error != lib1]

*** 1 failure is detected in the test module "Master Test Suite"

make[2]: *** [lib1/test/lib1_test] Error 201
make[1]: *** [lib1/test/CMakeFiles/lib1_test.dir/all] Error 2
make: *** [all] Error 2

如果不需要构建目标,则不运行测试,构建通过。

这里我不做任何更改,只是重新运行构建过程

$ make
[ 50%] Built target lib1
[100%] Built target lib1_test

但是,如果 lib1_test 实际运行,则测试失败。

$ ./lib1/test/lib1_test 
Running 1 test case...
main.cpp(8): fatal error: in "lib1_test": 
    critical check lib1() == "lib1" has failed [error != lib1]

*** 1 failure is detected in the test module "Master Test Suite"

更好的方法是创建一个lib1_test.passed 目标,该目标依赖于lib1_test,运行测试,并且仅在测试通过时创建。

我尝试过的:

我曾尝试使用add_custom_target 创建一个依赖于lib1_test 的目标lib1_test.passed,如果成功,则创建一个文件lib1_test.passed

add_custom_target(${NAME}.passed
    DEPENDS ${NAME}
    COMMAND ${NAME}
    COMMAND ${CMAKE_COMMAND} -E touch ${NAME}.passed)

我目前取得的成绩有两个不足:

  • 测试的运行不是正常构建过程的一部分。
    也就是说,make 不会“构建” lib1_test.passed;
    我必须明确声明make lib1_test.passed
  • make lib1_test.passed 将始终执行 lib1_test,无论 lib1_test.passed 是否比 lib1_test1 更新

问题:

我怎样才能让测试的运行成为构建的一部分,失败的测试总是会重新运行?

【问题讨论】:

  • 未如愿以偿,即使不改也要运行lib1_test?它得到不同的输入数据或者什么,如果它没有改变为什么需要重新运行?
  • 否 - 与此相反 - 我希望 lib1_test 作为 make 的一部分运行,如果通过,再次输入 make 不会运行考试。但是,如果失败,再次输入make必须再次运行测试。
  • 那么你只需要依赖二进制文件lib1_testadd_custom_target,如果失败则删除这个二进制(可执行)文件,为什么不试试这样的变种?
  • @user1034749 我的 add_custom_target 调用不会在我输入 make 时自动被调用 - 如何让它运行?

标签: c++ cmake


【解决方案1】:

这是我到目前为止所得到的。实现非常快速和肮脏,但它仍然有效。请检查并判断它是否满足您的需求。

CMakeLists.txt:

cmake_minimum_required(VERSION 2.8.12)

project(test)

enable_testing()

set(lib1_SRC lib.c)

add_library(lib1 ${lib1_SRC})

set(test_SRC test.c)

add_executable(libtest ${test_SRC})
target_link_libraries(libtest lib1)

add_test(NAME libtest COMMAND libtest)

add_custom_command(
  OUTPUT _libtest_completed
  COMMAND ctest -C $<CONFIGURATION> --output-on-failure
  COMMAND cmake -E touch _libtest_completed
  DEPENDS libtest
)

add_custom_target(
  libtest_force ALL
  DEPENDS _libtest_completed
)

为了完整起见源文件:

lib.c:

#include "lib.h"

#include <time.h>

int lib_func() {
    return time(NULL) % 2;
}

lib.h:

#pragma once

int lib_func();

test.c:

#include "lib.h"

int main() {
    return lib_func();
}

不幸的是,由于CMake bug,不可能直接依赖test目标,所以我们必须手动执行日落。

【讨论】:

  • 在构建lib1 时,有没有办法让libtest_force 被构建?也就是说,如果我输入make lib1,它将构建lib1libtestlibtest_force(从而运行测试)。
  • 这三个是“顶级目标”,所以你可以使用add_dependencies。但我会避免循环依赖,它带来的问题多于解决的问题。
  • 我想出了一些或多或少有用的东西。如果您愿意,请查看my answermy question,如果您有任何意见,请发表评论。谢谢!
  • 我实施了您的解决方案,非常感谢您的帮助。我想出了一个slight wrinkle I can't figure out though - 即,将测试标准输出捕获到一个文件中,并且仅在测试失败时才显示它。您可能有任何见解将不胜感激!提前致谢。
【解决方案2】:

如@user3159253 回答中所示,您需要一个输出文件(带有时间戳)供构建环境检查它是否必须再次“构建”有问题的目标。因此,如果您的可执行文件成功构建 - 即使后续调用运行它失败 - 也不会再次构建。

我想添加一个只能有一个目标的解决方案。它将重命名测试可执行输出,运行它并 - 如果成功 - 再次将其重命名为其原始名称以将其标记为“通过”:

function(register_test NAME)

    add_test(NAME ${NAME} COMMAND ${NAME})

    set(TMP "$<TARGET_FILE_DIR:${NAME}>/tmp_$<TARGET_FILE_NAME:${NAME}>")
    set(ORG "$<TARGET_FILE:${NAME}>")

    # make the test run as part of the build process
    add_custom_command(
        TARGET ${NAME} 
        POST_BUILD 
        COMMAND ${CMAKE_COMMAND} -E rename "${ORG}" "${TMP}"
        COMMAND ${TMP}
        COMMAND ${CMAKE_COMMAND} -E rename "${TMP}" "${ORG}"
    )

endfunction()

【讨论】:

    猜你喜欢
    • 2011-09-28
    • 2012-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-12
    • 1970-01-01
    • 2022-12-14
    相关资源
    最近更新 更多