我如何使用 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
有点慢不是问题)。
(其他消毒剂也有类似的选择,但我没有研究过。)
我有一些测试可以测试 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
有点慢不是问题)。
(其他消毒剂也有类似的选择,但我没有研究过。)