我如何使用 cmake 来测试预期会因异常而失败的进程? (例如,由于 clang 的地址消毒器导致的失败)

How can I use cmake to test processes that are expected to fail with an exception? (e.g., failures due to clang's address sanitizer)

我有一些测试可以测试 clang 的地址清理程序是否捕获特定错误。 (我想确保我对它可以捕获的错误类型的理解是正确的,并且未来的版本会继续捕获我期望它们捕获的错误类型。)这意味着我有几个测试失败了OTHER_FAULT,貌似是clang运行时报错的固定方式

对于这些测试,我已将 WILL_FAIL 标志设置为 TRUE,但这似乎只检查了 return 成功的无异常失败的值。如果进程因异常而终止,cmake 仍然 class 将其视为失败。

我也尝试过使用 PASS_REGULAR_EXPRESSION 来观察发生此错误时打印出的区别消息,但同样,如果它终止,cmake 似乎 class 测试失败有一个例外。

我能做些什么来解决这个问题?

(特定于 clang 的答案也是一个选项! - 但我怀疑这将是我最后一次需要测试这样的东西,所以我更愿意知道如何使用 cmake 通常,如果它是可能)

CTest 只为测试程序的结果提供基本的、常用的解释器。要实现其他解释器,您可以编写简单的 program/script,它包装测试并根据需要解释其结果。例如。 C程序(对于Linux):

test_that_crash.c:

#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>

int main(int argc, char** argv)
{
    pid_t pid = fork();
    if(pid == -1)
    {
        // fork fails
        return 1;
    }
    else if(pid)
    {
        // Parent - wait child and interpret its result
        int status = 0;
        wait(&status);
        if(WIFSIGNALED(status)) return 0; // Signal-terminated means success
        else return 1;
    }
    else
    {
        // Child - execute wrapped command
        execvp(argv[1], argv + 1);
        exit(1);
    }
}

这个程序可以在CMake中使用如下:

CMakeLists.txt:

# Compile our wrapper
add_executable(test_that_crash test_that_crash.c)
# Similar to add_test(name command), but test is assumed successfull only if it is crashed(signalled)
macro(add_test_crashed name command)
    # Use generic flow of add_test() command for automatically recognize our executable target
    add_test(NAME ${name} COMMAND test_that_crash ${command} ${ARGN})
endmacro(add_test_crashed)
# ...

# Add some test, which should crash
add_test_crashed(clang.crash.1 <clang-executable> <clang-args>)

还有一个特定于 clang 的解决方案:使用 ASAN_OPTIONS 环境变量配置其退出方式。 (参见 https://github.com/google/sanitizers/wiki/AddressSanitizerFlags。)为此,将 ASAN_OPTIONS 环境变量设置为 abort_on_error=0。当地址清理器检测到问题时,该进程将执行 _exit(1) 而不是(大概)abort(),因此看起来已经干净地终止了。然后,您可以使用 cmake 的 WILL_FAIL 机制来选择它。 (目前还不清楚为什么 OS X 和 Linux 在这方面有所不同 - 但就是这样。)

作为奖励,测试失败的速度要快得多。

(另一个可以在 运行 通过 cmake 时缩短周转时间的方便选项是将 ASAN_SYMBOLIZER_PATH 设置为空值,这会停止符号化堆栈跟踪的地址清理程序。符号化需要一点时间,但是当 运行 通过 cmake 时这样做是没有意义的,因为你看不到输出。)

我没有手动执行此操作,而是制作了一个 Python 脚本,该脚本在 OS X 上适当地设置环境(在 Linux 上不执行任何操作),并调用测试。然后,我按照 Tsyvarev 的回答使用宏添加每个 asan 测试。

macro(add_asan_test basename)
  add_executable(${basename} ${basename}.c)
  add_test(NAME test/${basename} COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/wrap_clang_sanitizer_test.py -a $<TARGET_FILE:${basename}>)
  set_tests_properties(test/${basename} PROPERTIES WILL_FAIL TRUE)
endmacro()

这会尽快给出一个简单的 pass/fail。我习惯于通过 运行 手动检查来自 shell 的相关测试并检查输出,在这种情况下,我得到的堆栈跟踪是正常的(事实是 [=退出的事实 abort 有点慢不是问题)。

(其他消毒剂也有类似的选择,但我没有研究过。)