Можно ли восстановить из std :: abort?

Наша кодовая база C использует утверждать проверить, что предварительные условия / почтовые условия выполняются.

#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?

Я понимаю, что кажется несколько повторным, чтобы модульные тесты проверяли точно то же самое, что должны проверять утверждения, но это было бы полезно, так как мы могли бы автоматизировать проверку определенных вариантов использования, которые не должны работать.

3

Решение

Краткий ответ «нет».

Вместо того, чтобы подрывать abort (), вы можете рассмотреть возможность использования тестовой среды Google.

Это DEATH_TEST (документация здесь: https://github.com/google/googletest/blob/master/googletest/docs/V1_7_AdvancedGuide.md)

По сути, это то, что делает ветвь дочернего процесса и проверяет, вызывает ли оператор его завершение (что он сделал бы, если бы он прервался).

5

Другие решения

Согласно POSIX,

Функция abort () должна вызывать аварийное завершение процесса, если только сигнал SIGABRT не перехватывается и обработчик сигнала не возвращается.

Это подразумевает, что если вы перехватываете сигнал и обработчик сигнала возвращается, он все равно должен завершить программу (скажем, сбросив обработчик сигнала к поведению завершения по умолчанию, а затем снова подняв сигнал).

Таким образом, единственный способ «восстановить» — это перехватить сигнал, а не вернуться из обработчика сигнала. Так что строка после Aborting_Function(0) не может быть достигнуто Более того, вы, вероятно, не хотите, чтобы ваша программа все равно проводила остаток своей жизни в обработчике сигналов, потому что тогда все внешние переменные стали бы небезопасными для доступа (за исключением атомарных соединений без блокировки). Это не очень приятно.

На винде, правда? Я понятия не имею.

1

Для модульного тестирования можно заменить 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

Это может быть вылечено:

Компиляция через g ++ с использованием -std=c++03 или же -std=c++11 не приводит к многократному определению символа.

Более сложные версии кода выше я разрабатываю для чтобы тестовые рамки:

1

Можно ли вылечиться от std::abort?

Это зависит от того, что вы подразумеваете под оправиться от. От http://en.cppreference.com/w/cpp/utility/program/abort

Вызывает ненормальное завершение программы, если SIGABRT перехватывается обработчиком сигнала, передаваемым сигналу, и обработчик не возвращается.

Если вы хотите, чтобы иметь возможность продолжить откуда std::abort был вызван, то ответ нет. Если вы хотите быть в состоянии сделать что-то до выхода из программы, то ответ да.

Я предполагаю, что вы хотели бы продолжить std::abort назывался. Следовательно, ответ нет для вашего случая использования.

0

Есть способ сделать это, но он немного не соответствует.

Вы можете использовать HippoMocks (заявление об отказе от ответственности: я автор), чтобы смоделировать функцию и заставить ее вызвать исключение, которое вы затем используете для проверки в своей тестовой среде. Вы не можете заставить его возвращать значение, так как оно помечено как noreturn, и компилятор не сгенерирует никакого кода для обработки возвратов из него.

EXPECT_CALL(&abort).Throw(42);

Обратите внимание, что это нарушает как минимум 5 различных правил в C и C ++, так что большой вопрос, не так ли? Насколько я могу судить, вы используете утверждения для защиты от вещей, которые показывают внутреннюю несогласованность. Это то, что вы не должны проверять. Все ваши программы должны быть одинаково действительны с и без утверждений. Если вы ожидаете какого-либо поведения от своего кода, который хотите протестировать, то это никогда не должно быть утверждением, так как это не неожиданное состояние (черт, вы тестируете это состояние, поэтому оно хорошо проверено). ,

Так что вы можете, но не должны этого хотеть.

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector