我们的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恢复?
我意识到单元测试检查断言应该检查完全相同的东西似乎有些重复,但这会有所帮助,因为我们可以自动检查不应该工作的特定用例。
答案 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 ;那是一个经过充分考验的人。)
所以你可以,但你不应该这样做。