Skip to content

Commit

Permalink
Merge branch 'topic/GH-202-boost-timer-deprecation' into next-internal
Browse files Browse the repository at this point in the history
* topic/GH-202-boost-timer-deprecation:
  Change log
  Documentation updates
  Adding the support for skipped test units/suites in the reporter and formatters
  <boost/timer.hpp> removal and time-out for suites and major refactoring
  • Loading branch information
raffienficiaud committed Feb 27, 2019
2 parents 1f913b7 + 1ad1a43 commit 76d1bdf
Show file tree
Hide file tree
Showing 32 changed files with 1,199 additions and 282 deletions.
9 changes: 1 addition & 8 deletions build/Jamfile.v2
Expand Up @@ -7,7 +7,7 @@
import os ;

import ../../predef/check/predef
: check
: check
: predef-check ;

project boost/test
Expand All @@ -25,17 +25,10 @@ project boost/test
<warnings>all
# <warnings-as-errors>on

# adding a dependency on boost/timer as the header are needed, and the junction needs
# to be there in order to build the library.
<library>/boost/timer//boost_timer
: usage-requirements
<define>BOOST_TEST_NO_AUTO_LINK=1
# Disable Warning about boost::noncopyable not being exported
<link>shared,<toolset>msvc:<cxxflags>-wd4275

# Adding a dependency on boost/timer as the headers need to be there in case of the
# header-only usage variant
<use>/boost/timer//boost_timer
;

PRG_EXEC_MON_SOURCES =
Expand Down
10 changes: 10 additions & 0 deletions doc/closing_chapters/change_log.qbk
Expand Up @@ -11,6 +11,13 @@

[h5 New features]

* Boost.Test does not depend on Boost.Timer any more (which was pulling also Boost.Chrono
and Boost.System as transitive dependencies).
* Now Boost.Test raises an exception when the test case times-out on Windows. Prior to this release,
times-out on Windows were not failing the test cases. Note that signaling is not available on Windows,
and it is not possible to interrupt a test even in case of time out.
* Time-out now applies to test-suites as well: a test-suite is marked as timed-out if it exceeds the allocated
time. The test units that were not executed at the time-point of the time-out are skipped.
* It is now possible to pass several values for the same context via the tool
__BOOST_TEST_CONTEXT__.
* A new macro __BOOST_TEST_INFO_SCOPE__ let define a context for the current scope in a sticky way.
Expand Down Expand Up @@ -65,6 +72,7 @@
# [github_issue 181] `doc_example22` (and `23`) are expected to fail, but do not on clang with release variant builds
# [github_issue 194] `master_test_suite` declared twice
# [github_issue 199] Runtime `type_mismatch` after upgrade to `1.69`
# [github_issue 202] `boost/timer.hpp` is deprecated
# [github_issue 203] Test cases with datasets and fixtures don't support flexible fixture interface
# [github_issue 204] Feature Request: Allow specifying timeouts for test cases with datasests.

Expand All @@ -76,6 +84,8 @@
# [pull_request 197] Feature Request: `BOOST_TEST_CONTEXT` that doesn't require introducing a new scope with braces
# [pull_request 205] Fix MinGW `vsnprintf` compile errors and warnings

# [ticket 7397] Boost.Test, since boost `1.48` is using the deprecated `Boost.Timer` class (solved via [github_issue 202])
# [ticket 9434] error: `namespace boost::timer {}` redeclared as different kind of symbol (solved via [github_issue 202])
# [ticket 13106] `libs/test/tools/console_test_runner` does not compile


Expand Down
18 changes: 16 additions & 2 deletions doc/testing_tools/testing_tools_reference.qbk
Expand Up @@ -86,12 +86,26 @@ See [link boost_test.testing_tools.expected_failures here] for more details.
[section:decorator_timeout timeout (decorator)]

``
timeout(unsigned seconds);
timeout(unsigned int seconds);
``

Specifies a time-out for a *test-case*, above which the test-case is forced to stop and reported as failing.
Specifies a time-out for a *test-case* or a *test-suite*, in wall-clock time.

If a test-case lasts longer than the timeout, the test is flagged as failed. On some systems (see below),
the test-case is forced to stop.

For test-suites, the mechanism is similar: every test-unit under the test-suite is allocated a maximum
duration time that is the remainder of the timeout after the previous tests have been executed. If a timeout occurs
during the execution of the suite, the suite is flagged as timed-out and the remaining test-units are skipped.

See [link boost_test.testing_tools.timeout here] for more details.

[note The macro
`BOOST_SIGACTION_BASED_SIGNAL_HANDLING` is defined
if Boost.Test is able to force the test-case to stop.]

[note The support for test suites has been added in [link ref_CHANGE_LOG_3_10 Boost 1.70 / __UTF__ v3.10]]

[endsect] [/ section timeout]


Expand Down
28 changes: 22 additions & 6 deletions doc/testing_tools/timeout.qbk
Expand Up @@ -8,15 +8,31 @@

[section:timeout Time-out for test cases]

The __UTF__ provides the decorator __decorator_timeout__ that specifies a time-out for a specific *test case*,
The argument time (in seconds) sets the maximum allowed duration of a test case. If this time is
exceeded the test case is forced to stop and is reported as failure.
The __UTF__ provides the decorator __decorator_timeout__ that specifies a time-out for a specific *test unit*.
The argument time is always expressed in *seconds ans wall-clock* time.

[bt_example decorator_11..decorator timeout..run-fail]
For test-cases, the time-out value sets the maximum allowed duration for the test. If this time is
exceeded, the test case is reported as failed. On some systems, the __UTF__ is able to force the test-case
to stop through a `SIGALRM` signal (see below).

For test-suites, the time-out value sets the maximum allowed duration for the entire suite to complete. This duration
is the accumulated time of all the test-cases contained in the sub-tree rooted on the test-suite, plus some extra
execution time needed by the __UTF__. For each test-units under a test-suite with time-out, the maximum allowed duration
is set as being the test-suite time out minus the accumulated execution time before the execution of the test-unit.
If this test-unit is a test-case, it is equivalent to setting the decorator __decorator_timeout__ to the test-case
with a time-out value expressed as before.

[note Applied at test suite level, this decorator has no effect.]
In case the test-suite times out, the
suite is flagged as `timed-out` and `failed`, and all the test units (suites and cases) that have not been executed
up to the time-out point are all skipped.

[bt_example decorator_11..decorator timeout..run-fail]

[caution Decorator `timeout` has no effect on Windows build. This feature is not implemented on Windows yet.]
[note The macro
`BOOST_SIGACTION_BASED_SIGNAL_HANDLING` is defined
if Boost.Test is able to force the test-case to stop. This feature is for instance not supported on Windows.
The __UTF__ will still be able to report the test-case as failed (once the test-case finishes).]

[note The support of test suite level time-out has been added in [link ref_CHANGE_LOG_3_10 Boost 1.70 / __UTF__ v3.10] ]

[endsect]
9 changes: 5 additions & 4 deletions include/boost/test/execution_monitor.hpp
Expand Up @@ -111,7 +111,8 @@ namespace boost {
///
/// @section DesignRationale Design Rationale
///
/// The Execution Monitor design assumes that it can be used when no (or almost no) memory available. Also the Execution Monitor is intended to be portable to as many platforms as possible.
/// The Execution Monitor design assumes that it can be used when no (or almost no) memory available. Also the Execution Monitor
/// is intended to be portable to as many platforms as possible.
///
/// @section UserGuide User's guide
/// The Execution Monitor is designed to solve the problem of executing potentially dangerous function that may result in any number of error conditions,
Expand Down Expand Up @@ -337,9 +338,9 @@ class BOOST_TEST_DECL execution_monitor {

/// Specifies the seconds that elapse before a timer_error occurs.
///
/// The @em p_timeout property is an integer timeout (in seconds) for monitored function execution. Use this parameter to monitor code with possible deadlocks
/// or indefinite loops. This feature is only available for some operating systems (not yet Microsoft Windows).
unit_test::readwrite_property<unsigned> p_timeout;
/// The @em p_timeout property is an integer timeout (in microseconds) for monitored function execution. Use this parameter to monitor code with possible deadlocks
/// or infinite loops. This feature is only available for some operating systems (not yet Microsoft Windows).
unit_test::readwrite_property<unsigned long int> p_timeout;

/// Should monitor use alternative stack for the signal catching.
///
Expand Down
103 changes: 80 additions & 23 deletions include/boost/test/impl/execution_monitor.ipp
Expand Up @@ -50,11 +50,12 @@
#include <stdio.h>
#include <cstdarg> // for varargs
#include <stdarg.h>
#include <cmath> // for ceil

#include <iostream> // for varargs

#ifdef BOOST_NO_STDC_NAMESPACE
namespace std { using ::strerror; using ::strlen; using ::strncat; }
namespace std { using ::strerror; using ::strlen; using ::strncat; using ::ceil; }
#endif

// to use vsnprintf
Expand Down Expand Up @@ -100,6 +101,10 @@ using std::va_list;
# define BOOST_TEST_CRT_SET_HOOK(H) (void*)(H)
# endif

# if defined(_MSC_VER) && (_WIN32_WINNT >= 0x0501) /* WinXP */
# define BOOST_TEST_WIN32_WAITABLE_TIMERS
# endif

# if (!BOOST_WORKAROUND(_MSC_VER, >= 1400 ) && \
!defined(BOOST_COMO)) || defined(UNDER_CE)

Expand Down Expand Up @@ -691,7 +696,7 @@ signal_action::~signal_action()
class signal_handler {
public:
// Constructor
explicit signal_handler( bool catch_system_errors, bool detect_fpe, unsigned timeout, bool attach_dbg, char* alt_stack );
explicit signal_handler( bool catch_system_errors, bool detect_fpe, unsigned timeout_microseconds, bool attach_dbg, char* alt_stack );

// Destructor
~signal_handler();
Expand All @@ -714,7 +719,7 @@ public:
private:
// Data members
signal_handler* m_prev_handler;
unsigned m_timeout;
unsigned m_timeout_microseconds;

// Note: We intentionality do not catch SIGCHLD. Users have to deal with it themselves
signal_action m_ILL_action;
Expand All @@ -738,24 +743,24 @@ signal_handler* signal_handler::s_active_handler = signal_handler_ptr();

//____________________________________________________________________________//

signal_handler::signal_handler( bool catch_system_errors, bool detect_fpe, unsigned timeout, bool attach_dbg, char* alt_stack )
signal_handler::signal_handler( bool catch_system_errors, bool detect_fpe, unsigned timeout_microseconds, bool attach_dbg, char* alt_stack )
: m_prev_handler( s_active_handler )
, m_timeout( timeout )
, m_ILL_action ( SIGILL , catch_system_errors, attach_dbg, alt_stack )
, m_FPE_action ( SIGFPE , detect_fpe , attach_dbg, alt_stack )
, m_SEGV_action( SIGSEGV, catch_system_errors, attach_dbg, alt_stack )
, m_BUS_action ( SIGBUS , catch_system_errors, attach_dbg, alt_stack )
, m_timeout_microseconds( timeout_microseconds )
, m_ILL_action ( SIGILL , catch_system_errors, attach_dbg, alt_stack )
, m_FPE_action ( SIGFPE , detect_fpe , attach_dbg, alt_stack )
, m_SEGV_action( SIGSEGV, catch_system_errors, attach_dbg, alt_stack )
, m_BUS_action ( SIGBUS , catch_system_errors, attach_dbg, alt_stack )
#ifdef BOOST_TEST_CATCH_SIGPOLL
, m_POLL_action( SIGPOLL, catch_system_errors, attach_dbg, alt_stack )
, m_POLL_action( SIGPOLL, catch_system_errors, attach_dbg, alt_stack )
#endif
, m_ABRT_action( SIGABRT, catch_system_errors, attach_dbg, alt_stack )
, m_ALRM_action( SIGALRM, timeout > 0 , attach_dbg, alt_stack )
, m_ABRT_action( SIGABRT, catch_system_errors, attach_dbg, alt_stack )
, m_ALRM_action( SIGALRM, timeout_microseconds > 0, attach_dbg, alt_stack )
{
s_active_handler = this;

if( m_timeout > 0 ) {
if( m_timeout_microseconds > 0 ) {
::alarm( 0 );
::alarm( timeout );
::alarm( static_cast<unsigned int>(std::ceil(timeout_microseconds / 1E6) )); // alarm has a precision to the seconds
}

#ifdef BOOST_TEST_USE_ALT_STACK
Expand All @@ -781,7 +786,7 @@ signal_handler::~signal_handler()
{
assert( s_active_handler == this );

if( m_timeout > 0 )
if( m_timeout_microseconds > 0 )
::alarm( 0 );

#ifdef BOOST_TEST_USE_ALT_STACK
Expand Down Expand Up @@ -897,8 +902,10 @@ public:
, m_se_id( 0 )
, m_fault_address( 0 )
, m_dir( false )
, m_timeout( false )
{}

void set_timed_out();
void report() const;
int operator()( unsigned id, _EXCEPTION_POINTERS* exps );

Expand All @@ -909,6 +916,7 @@ private:
unsigned m_se_id;
void* m_fault_address;
bool m_dir;
bool m_timeout;
};

//____________________________________________________________________________//
Expand All @@ -923,6 +931,14 @@ seh_catch_preventer( unsigned /* id */, _EXCEPTION_POINTERS* /* exps */ )

//____________________________________________________________________________//

void
system_signal_exception::set_timed_out()
{
m_timeout = true;
}

//____________________________________________________________________________//

int
system_signal_exception::operator()( unsigned id, _EXCEPTION_POINTERS* exps )
{
Expand Down Expand Up @@ -1074,7 +1090,12 @@ system_signal_exception::report() const
break;

default:
detail::report_error( execution_exception::system_error, "unrecognized exception. Id: 0x%08lx", m_se_id );
if( m_timeout ) {
detail::report_error(execution_exception::timeout_error, "timeout while executing function");
}
else {
detail::report_error( execution_exception::system_error, "unrecognized exception. Id: 0x%08lx", m_se_id );
}
break;
}
}
Expand Down Expand Up @@ -1133,6 +1154,31 @@ execution_monitor::catch_signals( boost::function<int ()> const& F )
#endif
}

#if defined(BOOST_TEST_WIN32_WAITABLE_TIMERS)
HANDLE htimer = INVALID_HANDLE_VALUE;
BOOL bTimerSuccess = FALSE;

if( p_timeout ) {
htimer = ::CreateWaitableTimer(
NULL,
TRUE,
TEXT("Boost.Test timer"));

if( htimer != INVALID_HANDLE_VALUE ) {
LARGE_INTEGER liDueTime;
liDueTime.QuadPart = - static_cast<signed int>(p_timeout) * 10; // resolution of 100 ns

bTimerSuccess = ::SetWaitableTimer(
htimer,
&liDueTime,
0,
0,
0,
FALSE); // Do not restore a suspended system
}
}
#endif

detail::system_signal_exception SSE( this );

int ret_val = 0;
Expand All @@ -1146,8 +1192,26 @@ execution_monitor::catch_signals( boost::function<int ()> const& F )
__except( SSE( GetExceptionCode(), GetExceptionInformation() ) ) {
throw SSE;
}

// we check for time outs: we do not have any signaling facility on Win32
// however, we signal a timeout as a hard error as for the other operating systems
// and throw the signal error handler
if( bTimerSuccess && htimer != INVALID_HANDLE_VALUE) {
if (::WaitForSingleObject(htimer, 0) == WAIT_OBJECT_0) {
SSE.set_timed_out();
throw SSE;
}
}

}
__finally {

#if defined(BOOST_TEST_WIN32_WAITABLE_TIMERS)
if( htimer != INVALID_HANDLE_VALUE ) {
::CloseHandle(htimer);
}
#endif

if( l_catch_system_errors ) {
BOOST_TEST_CRT_SET_HOOK( old_crt_hook );

Expand Down Expand Up @@ -1250,15 +1314,8 @@ execution_monitor::execute( boost::function<int ()> const& F )
#endif

CATCH_AND_REPORT_STD_EXCEPTION( std::bad_alloc )

#if BOOST_WORKAROUND(__BORLANDC__, <= 0x0551)
CATCH_AND_REPORT_STD_EXCEPTION( std::bad_cast )
CATCH_AND_REPORT_STD_EXCEPTION( std::bad_typeid )
#else
CATCH_AND_REPORT_STD_EXCEPTION( std::bad_cast )
CATCH_AND_REPORT_STD_EXCEPTION( std::bad_typeid )
#endif

CATCH_AND_REPORT_STD_EXCEPTION( std::bad_exception )
CATCH_AND_REPORT_STD_EXCEPTION( std::domain_error )
CATCH_AND_REPORT_STD_EXCEPTION( std::invalid_argument )
Expand Down

0 comments on commit 76d1bdf

Please sign in to comment.