如何使用cmake来测试预期会因异常而失败的流程?(例如,由于叮当声和#x27;的地址消毒剂而导致的故障)

如何使用cmake来测试预期会因异常而失败的流程?(例如,由于叮当声和#x27;的地址消毒剂而导致的故障),cmake,clang,automated-tests,address-sanitizer,Cmake,Clang,Automated Tests,Address Sanitizer,我做了一些测试,测试clang的地址消毒剂是否捕捉到了特定的错误。(我想确保我对它能捕获的错误类型的理解是正确的,并且未来的版本会继续捕获我所期望的错误类型。)这意味着我有几个测试失败了,因为它抛出了一个OTHER_FAULT,这似乎是clang运行时报告错误的固定方式 我已经为这些测试设置了WILL\u FAIL标志为TRUE,但这似乎只是检查成功的无异常故障的返回值。如果进程因异常而终止,cmake仍将其归类为失败 我还尝试使用PASS\u REGULAR\u EXPRESSION来观察出现

我做了一些测试,测试clang的地址消毒剂是否捕捉到了特定的错误。(我想确保我对它能捕获的错误类型的理解是正确的,并且未来的版本会继续捕获我所期望的错误类型。)这意味着我有几个测试失败了,因为它抛出了一个
OTHER_FAULT
,这似乎是clang运行时报告错误的固定方式

我已经为这些测试设置了
WILL\u FAIL
标志为
TRUE
,但这似乎只是检查成功的无异常故障的返回值。如果进程因异常而终止,cmake仍将其归类为失败

我还尝试使用
PASS\u REGULAR\u EXPRESSION
来观察出现此错误时打印出来的区别消息,但同样,如果测试以异常终止,cmake似乎将测试归类为失败

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


(特定于叮当声的答案也是一种选择!-但我怀疑这将是我最后一次需要测试这样的东西,所以如果可能的话,我更愿意知道如何使用cmake进行测试)

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

测试崩溃的原因。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);
    }
}
# 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>)
#包括
#包括
#包括
int main(int argc,字符**argv)
{
pid_t pid=fork();
如果(pid==-1)
{
//叉子坏了
返回1;
}
否则如果(pid)
{
//父-等待子对象并解释其结果
int status=0;
等待(&状态);
if(WIFSIGNALED(status))返回0;//信号终止表示成功
否则返回1;
}
其他的
{
//子-执行包装命令
execvp(argv[1],argv+1);
出口(1);
}
}
此程序可在CMake中使用,如下所示:

CMakeLists.txt

#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);
    }
}
# 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>)
#编译我们的包装器
添加可执行文件(test_that_crash test_that_crash.c)
#与add_test(name命令)类似,但仅当测试崩溃(有信号显示)时才假定测试成功
宏(添加测试名称命令)
#使用add_test()命令的通用流自动识别可执行目标
添加测试(名称${NAME}命令测试{u崩溃${COMMAND}${ARGN})
endmacro(添加测试)
# ...
#添加一些测试,它会崩溃
添加测试崩溃(clang.crash.1)

还有一个特定于叮当声的解决方案:使用
ASAN_OPTIONS
环境变量配置其退出方式。(请参阅。)要执行此操作,请将
ASAN_OPTIONS
环境变量设置为
abort_on_error=0
。当地址消毒器检测到问题时,进程将执行退出(1),而不是(大概)中止(),并因此看起来已完全终止。然后,您可以使用cmake的
WILL\u FAIL
机制获取此信息。(仍然不清楚为什么OSX和Linux在这方面有所不同——但这就是问题所在。)

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

(在运行cmake时,另一个可以提高周转时间的方便选项是将
ASAN_symbolr_PATH
设置为空值,这将停止地址消毒剂对堆栈跟踪的符号化。符号化需要一些时间,但在运行cmake时没有意义,因为您看不到输出。)

我没有手工操作,而是编写了一个Python脚本,在OSX上适当地设置环境(在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()
宏(添加asan\u测试basename)
添加_可执行文件(${basename}${basename}.c)
添加\u test(NAME test/${basename}命令${CMAKE\u CURRENT\u SOURCE\u DIR}/wrap\u clang\u sanitizer\u test.py-a$)
设置测试属性(测试/${basename}属性将为真)
endmacro()
这将尽可能快地给出简单的通过/失败。我习惯于通过手动从shell运行有问题的测试并检查输出来调查失败,在这种情况下,我得到了正常的堆栈跟踪(通过
abort
退出有点慢,这不是什么问题)


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

谢谢。cmake代码片段也很受欢迎:)当我把它放在一起时发现,在clang-700.1.76(我在OS X上使用)上,一个消毒器故障会用SIGABRT停止这个过程,这使得上述操作成为必要。但是对于GCC4.8.4(正如我在Linux上使用的那样),进程的退出代码为1,这意味着您可以将WILL_设置为TRUE。(到目前为止,LinuxClang还是个未知数。)为了让以后阅读本文的人受益:LinuxClang的消毒剂也以1的退出代码退出。我想知道为什么他们没有为OSX做这件事。