是否有可能从std :: abort恢复?

时间:2015-12-10 20:05:30

标签: c++ unit-testing abort

我们的C代码库使用assert来检查是否满足前置条件/​​后置条件。

#include <cstdlib>
#include <cassert>

void Aborting_Function(int n);

int main(){

    Aborting_Function(1); //good
    Aborting_Function(0); //calls std::abort()

    //Is it possible to recover somehow?
    //And continue on...
}

void Aborting_Function(int n){
    assert(n > 0);
    //impl...
}

在单元测试中,我想验证功能是否正确遵循其合同 (当他们应该堕胎)。

是否可以从std :: abort恢复?

我意识到单元测试检查断言应该检查完全相同的东西似乎有些重复,但这会有所帮助,因为我们可以自动检查不应该工作的特定用例。

5 个答案:

答案 0 :(得分:5)

简短回答,“不”。

您可能需要考虑使用Google测试框架,而不是颠覆abort()。

这有DEATH_TEST(文档:https://github.com/google/googletest/blob/master/googletest/docs/V1_7_AdvancedGuide.md

基本上它的作用是派生一个子进程并检查该语句是否导致它退出(如果它中止就会这样做。)

答案 1 :(得分:2)

对于单元测试,可以从那里替换abort()和longjmp(),或者从那里通过C ++进行测试。

例如(表示为C ++):

#include <cassert>
#include <csetjmp>
#include <stdexcept>
#include <iostream>

#ifndef lest_ABORT_SIGNATURE
# if _MSC_VER
#  define lest_NORETURN  __declspec(noreturn)
#  define lest_ABORT_SIGNATURE()  _ACRTIMP lest_NORETURN void __cdecl abort(void)
# else
#  define lest_NORETURN  [[noreturn]]
#  define lest_ABORT_SIGNATURE()  lest_NORETURN void __cdecl abort()
# endif
#else
# ifndef  lest_NORETURN
#  define lest_NORETURN
# endif
#endif

#if USE_LONGJMP
    jmp_buf env;

    lest_ABORT_SIGNATURE()
    {
        std::longjmp( env, 1 );
    }
#else
    struct Abort{};

    lest_NORETURN void my_abort()
    {
        throw Abort{};
    }

    lest_ABORT_SIGNATURE()
    {
        // throw indirectly and prevent warning in VC14:
        my_abort();
    }
#endif

int main()
{
#if USE_LONGJMP
    if ( ! setjmp( env ) )
    {
        std::cout << "assert(false):\n";
        assert( false );
    }
    else
    {
        std::cout << "Intercepted abort\n";
    }
#else
    try
    {
        std::cout << "assert(false):\n";
        assert( false );
    }
    catch ( Abort const & )
    {
        std::cout << "Caught Abort\n";
    }
    catch ( std::exception const & e )
    {
        std::cout << "Exception: " << e.what() << "\n";
    }
#endif
    std::cout << "End\n";
}

#if 0
cl  -EHsc -DUSE_LONGJMP=1 abort-own.cpp && abort-own.exe
g++ -Wall -DUSE_LONGJMP=1 -std=c++11 -o abort-own.exe abort-own.cpp && abort-own.exe
#endif

使用VC14(VS2015)进行编译并使用以下命令运行:

cl -EHsc -DUSE_LONGJMP=1 abort-own.cpp && abort-own.exe

产生以下输出:

...
assert(false):
Assertion failed: false, file abort-own.cpp, line 45
Intercepted abort
End

使用VC14之前的编译器进行编译会产生链接错误:

LIBCMT.lib(abort.obj) : error LNK2005: _abort already defined in {file}
{exe} : fatal error LNK1169: one or more multiply defined symbols found

这可以通过以下方式治愈:

使用-std=c++03-std=c++11通过g ++进行编译不会产生多重定义的符号。

我正在为lest test framework开发的更详细的代码版本:

答案 2 :(得分:1)

根据POSIX,

  

abort()函数将导致异常进程终止,除非信号SIGABRT被捕获且信号处理程序没有返回。

这意味着如果你捕获信号并且信号处理程序返回,则仍然需要终止程序(例如,通过将信号处理程序重置为默认终止行为,然后再次发出信号)。

因此,“恢复”的唯一方法是捕获信号而不是从信号处理程序返回。因此无法到达Aborting_Function(0)之后的行。此外,你可能不希望你的程序在信号处理程序中度过剩余的生命,因为那时所有外部变量都变得不安全(除了无锁原子)。这不是很好。

在Windows上,但是?我不知道。

答案 3 :(得分:0)

  

是否可以从std::abort恢复?

这取决于中恢复的含义。来自http://en.cppreference.com/w/cpp/utility/program/abort

  

导致程序异常终止,除非SIGABRT被传递给signal的信号处理程序捕获并且处理程序没有返回。

如果您希望能够从调用std::abort的位置继续,则答案为。如果您希望在程序退出之前能够执行某些操作,则答案为

我猜你希望能够从调用std::abort的地方继续。因此,对于您的用例,答案是

答案 4 :(得分:0)

有一种方法可以做到这一点,但它有点不符合。

您可以使用HippoMocks(免责声明:我是作者)来模拟该函数并使其抛出异常,然后您可以使用它来检查您的测试框架。你不能让它返回一个值,因为它标记为noreturn,并且编译器不会生成任何代码来处理它的返回。

EXPECT_CALL(&abort).Throw(42);

请注意,这在C和C ++中违反了至少5个不同的规则,所以更大的问题是,如果你呢?据我所知,你使用断言来防止显示内部不一致的事情。这些是你不应该测试的东西。所有程序在有和没有断言的情况下都应该同等有效。如果你期望你想要测试的代码中出现任何类型的行为,那么它永远不应该是一个断言,因为它不是一个意外的状态(哎呀,你正在测试那个状态,所以它&#39 ;那是一个经过充分考验的人。)

所以你可以,但你不应该这样做。