summaryrefslogtreecommitdiff
path: root/boost/test/impl/framework.ipp
diff options
context:
space:
mode:
Diffstat (limited to 'boost/test/impl/framework.ipp')
-rw-r--r--boost/test/impl/framework.ipp210
1 files changed, 186 insertions, 24 deletions
diff --git a/boost/test/impl/framework.ipp b/boost/test/impl/framework.ipp
index a35e8a54ac..ca35ce1c2a 100644
--- a/boost/test/impl/framework.ipp
+++ b/boost/test/impl/framework.ipp
@@ -27,6 +27,7 @@
#include <boost/test/results_collector.hpp>
#include <boost/test/progress_monitor.hpp>
#include <boost/test/results_reporter.hpp>
+#include <boost/test/test_framework_init_observer.hpp>
#include <boost/test/tree/observer.hpp>
#include <boost/test/tree/test_unit.hpp>
@@ -56,6 +57,9 @@
#include <cstdlib>
#include <ctime>
#include <numeric>
+#ifdef BOOST_NO_CXX98_RANDOM_SHUFFLE
+#include <iterator>
+#endif
#ifdef BOOST_NO_STDC_NAMESPACE
namespace std { using ::time; using ::srand; }
@@ -68,6 +72,7 @@ namespace std { using ::time; using ::srand; }
namespace boost {
namespace unit_test {
namespace framework {
+
namespace impl {
// ************************************************************************** //
@@ -435,6 +440,46 @@ parse_filters( test_unit_id master_tu_id, test_unit_id_list& tu_to_enable, test_
//____________________________________________________________________________//
+#ifdef BOOST_NO_CXX98_RANDOM_SHUFFLE
+
+// a poor man's implementation of random_shuffle
+template< class RandomIt, class RandomFunc >
+void random_shuffle( RandomIt first, RandomIt last, RandomFunc &r )
+{
+ typedef typename std::iterator_traits<RandomIt>::difference_type difference_type;
+ difference_type n = last - first;
+ for (difference_type i = n-1; i > 0; --i) {
+ difference_type j = r(i+1);
+ if (j != i) {
+ using std::swap;
+ swap(first[i], first[j]);
+ }
+ }
+}
+
+#endif
+
+
+// A simple handle for registering the global fixtures to the master test suite
+// without deleting an existing static object (the global fixture itself) when the program
+// terminates (shared_ptr).
+class global_fixture_handle : public test_unit_fixture {
+public:
+ global_fixture_handle(test_unit_fixture* fixture) : m_global_fixture(fixture) {}
+ ~global_fixture_handle() {}
+
+ virtual void setup() {
+ m_global_fixture->setup();
+ }
+ virtual void teardown() {
+ m_global_fixture->teardown();
+ }
+
+private:
+ test_unit_fixture* m_global_fixture;
+};
+
+
} // namespace impl
// ************************************************************************** //
@@ -446,7 +491,7 @@ unsigned const TIMEOUT_EXCEEDED = static_cast<unsigned>( -1 );
class state {
public:
state()
- : m_curr_test_case( INV_TEST_UNIT_ID )
+ : m_curr_test_unit( INV_TEST_UNIT_ID )
, m_next_test_case_id( MIN_TEST_CASE_ID )
, m_next_test_suite_id( MIN_TEST_SUITE_ID )
, m_test_in_progress( false )
@@ -603,7 +648,7 @@ public:
}
};
- // Executed the test tree with the root at specified test unit
+ // Executes the test tree with the root at specified test unit
execution_result execute_test_tree( test_unit_id tu_id,
unsigned timeout = 0,
random_generator_helper const * const p_random_generator = 0)
@@ -642,9 +687,15 @@ public:
// 30. Execute setup fixtures if any; any failure here leads to test unit abortion
BOOST_TEST_FOREACH( test_unit_fixture_ptr, F, tu.p_fixtures.get() ) {
+ ut_detail::test_unit_id_restore restore_current_test_unit(m_curr_test_unit, tu.p_id);
result = unit_test_monitor.execute_and_translate( boost::bind( &test_unit_fixture::setup, F ) );
if( result != unit_test_monitor_t::test_ok )
break;
+ test_results const& test_rslt = unit_test::results_collector.results( m_curr_test_unit );
+ if( test_rslt.aborted() ) {
+ result = unit_test_monitor_t::precondition_failure;
+ break;
+ }
}
// This is the time we are going to spend executing the test unit
@@ -688,7 +739,11 @@ public:
const random_generator_helper& rand_gen = p_random_generator ? *p_random_generator : random_generator_helper();
+#ifdef BOOST_NO_CXX98_RANDOM_SHUFFLE
+ impl::random_shuffle( children_with_the_same_rank.begin(), children_with_the_same_rank.end(), rand_gen );
+#else
std::random_shuffle( children_with_the_same_rank.begin(), children_with_the_same_rank.end(), rand_gen );
+#endif
BOOST_TEST_FOREACH( test_unit_id, chld, children_with_the_same_rank ) {
unsigned chld_timeout = child_timeout( timeout, tu_timer.elapsed() );
@@ -710,8 +765,7 @@ public:
m_context_idx = 0;
// setup current test case
- test_unit_id bkup = m_curr_test_case;
- m_curr_test_case = tc.p_id;
+ ut_detail::test_unit_id_restore restore_current_test_unit(m_curr_test_unit, tc.p_id);
// execute the test case body
result = unit_test_monitor.execute_and_translate( tc.p_test_func, timeout );
@@ -720,8 +774,7 @@ public:
// cleanup leftover context
m_context.clear();
- // restore state and abort if necessary
- m_curr_test_case = bkup;
+ // restore state (scope exit) and abort if necessary
}
}
@@ -729,6 +782,7 @@ public:
if( !unit_test_monitor.is_critical_error( result ) ) {
// execute teardown fixtures if any in reverse order
BOOST_TEST_REVERSE_FOREACH( test_unit_fixture_ptr, F, tu.p_fixtures.get() ) {
+ ut_detail::test_unit_id_restore restore_current_test_unit(m_curr_test_unit, tu.p_id);
result = (std::min)( result, unit_test_monitor.execute_and_translate( boost::bind( &test_unit_fixture::teardown, F ), 0 ) );
if( unit_test_monitor.is_critical_error( result ) )
@@ -787,7 +841,7 @@ public:
master_test_suite_t* m_master_test_suite;
std::vector<test_suite*> m_auto_test_suites;
- test_unit_id m_curr_test_case;
+ test_unit_id m_curr_test_unit;
test_unit_store m_test_units;
test_unit_id m_next_test_case_id;
@@ -799,6 +853,8 @@ public:
context_data m_context;
int m_context_idx;
+ std::set<test_unit_fixture*> m_global_fixtures;
+
boost::execution_monitor m_aux_em;
std::map<output_format, runtime_config::stream_holder> m_log_sinks;
@@ -1043,6 +1099,7 @@ init( init_unit_test_func init_func, int argc, char* argv[] )
// 40. Register default test observers
register_observer( results_collector );
register_observer( unit_test_log );
+ register_observer( framework_init_observer );
if( runtime_config::get<bool>( runtime_config::btrt_show_progress ) ) {
progress_monitor.set_stream( std::cout ); // defaults to stdout
@@ -1236,6 +1293,30 @@ deregister_observer( test_observer& to )
//____________________________________________________________________________//
// ************************************************************************** //
+// ************** register_global_fixture ************** //
+// ************************************************************************** //
+
+void
+register_global_fixture( test_unit_fixture& tuf )
+{
+ impl::s_frk_state().m_global_fixtures.insert( &tuf );
+}
+
+//____________________________________________________________________________//
+
+// ************************************************************************** //
+// ************** deregister_global_fixture ************** //
+// ************************************************************************** //
+
+void
+deregister_global_fixture( test_unit_fixture &tuf )
+{
+ impl::s_frk_state().m_global_fixtures.erase( &tuf );
+}
+
+//____________________________________________________________________________//
+
+// ************************************************************************** //
// ************** add_context ************** //
// ************************************************************************** //
@@ -1363,7 +1444,14 @@ current_auto_test_suite( test_suite* ts, bool push_or_pop )
test_case const&
current_test_case()
{
- return get<test_case>( impl::s_frk_state().m_curr_test_case );
+ return get<test_case>( impl::s_frk_state().m_curr_test_unit );
+}
+
+
+test_unit const&
+current_test_unit()
+{
+ return *impl::s_frk_state().m_test_units[impl::s_frk_state().m_curr_test_unit];
}
//____________________________________________________________________________//
@@ -1371,7 +1459,7 @@ current_test_case()
test_unit_id
current_test_case_id()
{
- return impl::s_frk_state().m_curr_test_case;
+ return impl::s_frk_state().m_curr_test_unit;
}
//____________________________________________________________________________//
@@ -1396,6 +1484,17 @@ get( test_unit_id id, test_unit_type t )
// ************** framework::run ************** //
// ************************************************************************** //
+template <class Cont>
+struct swap_on_delete {
+ swap_on_delete(Cont& c1, Cont& c2) : m_c1(c1), m_c2(c2){}
+ ~swap_on_delete() {
+ m_c1.swap(m_c2);
+ }
+
+ Cont& m_c1;
+ Cont& m_c2;
+};
+
void
run( test_unit_id id, bool continue_test )
{
@@ -1414,39 +1513,90 @@ run( test_unit_id id, bool continue_test )
bool was_in_progress = framework::test_in_progress();
bool call_start_finish = !continue_test || !was_in_progress;
-
- impl::s_frk_state().m_test_in_progress = true;
+ bool init_ok = true;
+ const_string setup_error;
if( call_start_finish ) {
+ // indicates the framework that no test is in progress now if observers need to be notified
+ impl::s_frk_state().m_test_in_progress = false;
+ // unit_test::framework_init_observer will get cleared first
BOOST_TEST_FOREACH( test_observer*, to, impl::s_frk_state().m_observers ) {
BOOST_TEST_I_TRY {
- impl::s_frk_state().m_aux_em.vexecute( boost::bind( &test_observer::test_start, to, tcc.p_count ) );
+ ut_detail::test_unit_id_restore restore_current_test_unit(impl::s_frk_state().m_curr_test_unit, id);
+ unit_test_monitor_t::error_level result = unit_test_monitor.execute_and_translate( boost::bind( &test_observer::test_start, to, tcc.p_count ) );
+ if( init_ok ) {
+ if( result != unit_test_monitor_t::test_ok ) {
+ init_ok = false;
+ }
+ else {
+ if( unit_test::framework_init_observer.has_failed() ) {
+ init_ok = false;
+ }
+ }
+ }
}
BOOST_TEST_I_CATCH( execution_exception, ex ) {
- BOOST_TEST_SETUP_ASSERT( false, ex.what() );
+ if( init_ok ) {
+ // log only the first error
+ init_ok = false;
+ setup_error = ex.what();
+ }
+ // break; // we should continue otherwise loggers may have improper structure (XML start missing for instance)
}
}
}
- unsigned seed = runtime_config::get<unsigned>( runtime_config::btrt_random_seed );
- switch( seed ) {
- case 0:
- break;
- case 1:
- seed = static_cast<unsigned>( std::rand() ^ std::time( 0 ) ); // better init using std::rand() ^ ...
- default:
- BOOST_TEST_FRAMEWORK_MESSAGE( "Test cases order is shuffled using seed: " << seed );
- std::srand( seed );
+ if( init_ok ) {
+
+ // attaching the global fixtures to the main entry point
+ test_unit& entry_test_unit = framework::get( id, TUT_ANY );
+ std::vector<test_unit_fixture_ptr> v_saved_fixture(entry_test_unit.p_fixtures.value.begin(),
+ entry_test_unit.p_fixtures.value.end());
+
+ BOOST_TEST_FOREACH( test_unit_fixture*, tuf, impl::s_frk_state().m_global_fixtures ) {
+ entry_test_unit.p_fixtures.value.insert( entry_test_unit.p_fixtures.value.begin(),
+ test_unit_fixture_ptr(new impl::global_fixture_handle(tuf)) );
+ }
+
+ swap_on_delete< std::vector<test_unit_fixture_ptr> > raii_fixture(v_saved_fixture, entry_test_unit.p_fixtures.value);
+
+ // now work in progress
+ impl::s_frk_state().m_test_in_progress = true;
+ unsigned seed = runtime_config::get<unsigned>( runtime_config::btrt_random_seed );
+ switch( seed ) {
+ case 0:
+ break;
+ case 1:
+ seed = static_cast<unsigned>( std::rand() ^ std::time( 0 ) ); // better init using std::rand() ^ ...
+ default:
+ BOOST_TEST_FRAMEWORK_MESSAGE( "Test cases order is shuffled using seed: " << seed );
+ std::srand( seed );
+ }
+
+ // executing the test tree
+ impl::s_frk_state().execute_test_tree( id );
+
+ // removing previously added global fixtures: dtor raii_fixture
}
- impl::s_frk_state().execute_test_tree( id );
+ impl::s_frk_state().m_test_in_progress = false;
+ unit_test::framework_init_observer.clear();
if( call_start_finish ) {
- BOOST_TEST_REVERSE_FOREACH( test_observer*, to, impl::s_frk_state().m_observers )
+ // indicates the framework that no test is in progress anymore if observers need to be notified
+ // and this is a teardown, so assertions should not raise any exception otherwise an exception
+ // might be raised in a dtor of a global fixture
+ impl::s_frk_state().m_test_in_progress = false;
+ BOOST_TEST_REVERSE_FOREACH( test_observer*, to, impl::s_frk_state().m_observers ) {
+ ut_detail::test_unit_id_restore restore_current_test_unit(impl::s_frk_state().m_curr_test_unit, id);
to->test_finish();
+ }
}
impl::s_frk_state().m_test_in_progress = was_in_progress;
+
+ // propagates the init/teardown error if any
+ BOOST_TEST_SETUP_ASSERT( init_ok && !unit_test::framework_init_observer.has_failed(), setup_error );
}
//____________________________________________________________________________//
@@ -1496,6 +1646,18 @@ test_unit_aborted( test_unit const& tu )
to->test_unit_aborted( tu );
}
+// ************************************************************************** //
+// ************** test_aborted ************** //
+// ************************************************************************** //
+
+void
+test_aborted( )
+{
+ BOOST_TEST_FOREACH( test_observer*, to, impl::s_frk_state().m_observers )
+ to->test_aborted( );
+}
+
+
//____________________________________________________________________________//
} // namespace framework