| 1 | // (C) Copyright Gennadiy Rozental 2001. |
| 2 | // Distributed under the Boost Software License, Version 1.0. |
| 3 | // (See accompanying file LICENSE_1_0.txt or copy at |
| 4 | // http://www.boost.org/LICENSE_1_0.txt) |
| 5 | |
| 6 | // See http://www.boost.org/libs/test for the library home page. |
| 7 | // |
| 8 | // File : $RCSfile$ |
| 9 | // |
| 10 | // Version : $Revision$ |
| 11 | // |
| 12 | // Description : implements framework API - main driver for the test |
| 13 | // *************************************************************************** |
| 14 | |
| 15 | #ifndef BOOST_TEST_FRAMEWORK_IPP_021005GER |
| 16 | #define BOOST_TEST_FRAMEWORK_IPP_021005GER |
| 17 | |
| 18 | // Boost.Test |
| 19 | #include <boost/test/framework.hpp> |
| 20 | #include <boost/test/execution_monitor.hpp> |
| 21 | #include <boost/test/debug.hpp> |
| 22 | #include <boost/test/unit_test_parameters.hpp> |
| 23 | |
| 24 | #include <boost/test/unit_test_log.hpp> |
| 25 | #include <boost/test/unit_test_log_formatter.hpp> |
| 26 | #include <boost/test/unit_test_monitor.hpp> |
| 27 | #include <boost/test/results_collector.hpp> |
| 28 | #include <boost/test/progress_monitor.hpp> |
| 29 | #include <boost/test/results_reporter.hpp> |
| 30 | #include <boost/test/test_framework_init_observer.hpp> |
| 31 | |
| 32 | #include <boost/test/tree/observer.hpp> |
| 33 | #include <boost/test/tree/test_unit.hpp> |
| 34 | #include <boost/test/tree/visitor.hpp> |
| 35 | #include <boost/test/tree/traverse.hpp> |
| 36 | #include <boost/test/tree/test_case_counter.hpp> |
| 37 | #include <boost/test/tree/global_fixture.hpp> |
| 38 | |
| 39 | #if BOOST_TEST_SUPPORT_TOKEN_ITERATOR |
| 40 | #include <boost/test/utils/iterator/token_iterator.hpp> |
| 41 | #endif |
| 42 | |
| 43 | #include <boost/test/utils/foreach.hpp> |
| 44 | #include <boost/test/utils/basic_cstring/io.hpp> |
| 45 | #include <boost/test/utils/basic_cstring/compare.hpp> |
| 46 | |
| 47 | #include <boost/test/detail/global_typedef.hpp> |
| 48 | #include <boost/test/detail/throw_exception.hpp> |
| 49 | |
| 50 | // Boost |
| 51 | #include <boost/test/utils/timer.hpp> |
| 52 | #include <boost/bind/bind.hpp> |
| 53 | |
| 54 | // STL |
| 55 | #include <limits> |
| 56 | #include <map> |
| 57 | #include <set> |
| 58 | #include <cstdlib> |
| 59 | #include <ctime> |
| 60 | #include <numeric> |
| 61 | #include <cmath> |
| 62 | #include <iterator> |
| 63 | |
| 64 | #ifdef BOOST_NO_STDC_NAMESPACE |
| 65 | namespace std { using ::time; using ::srand; } |
| 66 | #endif |
| 67 | |
| 68 | #include <boost/test/detail/suppress_warnings.hpp> |
| 69 | |
| 70 | //____________________________________________________________________________// |
| 71 | |
| 72 | namespace boost { |
| 73 | namespace unit_test { |
| 74 | namespace framework { |
| 75 | |
| 76 | namespace impl { |
| 77 | |
| 78 | // ************************************************************************** // |
| 79 | // ************** order detection helpers ************** // |
| 80 | // ************************************************************************** // |
| 81 | |
| 82 | struct order_info { |
| 83 | order_info() : depth(-1) {} |
| 84 | |
| 85 | int depth; |
| 86 | std::vector<test_unit_id> dependant_siblings; |
| 87 | }; |
| 88 | |
| 89 | typedef std::set<test_unit_id> tu_id_set; |
| 90 | typedef std::map<test_unit_id,order_info> order_info_per_tu; // !! ?? unordered map |
| 91 | |
| 92 | //____________________________________________________________________________// |
| 93 | |
| 94 | static test_unit_id |
| 95 | get_tu_parent( test_unit_id tu_id ) |
| 96 | { |
| 97 | return framework::get( tu_id, tu_type: TUT_ANY ).p_parent_id; |
| 98 | } |
| 99 | |
| 100 | //____________________________________________________________________________// |
| 101 | |
| 102 | static int |
| 103 | tu_depth( test_unit_id tu_id, test_unit_id master_tu_id, order_info_per_tu& tuoi ) |
| 104 | { |
| 105 | if( tu_id == master_tu_id ) |
| 106 | return 0; |
| 107 | |
| 108 | order_info& info = tuoi[tu_id]; |
| 109 | |
| 110 | if( info.depth == -1 ) |
| 111 | info.depth = tu_depth( tu_id: get_tu_parent( tu_id ), master_tu_id, tuoi ) + 1; |
| 112 | |
| 113 | return info.depth; |
| 114 | } |
| 115 | |
| 116 | //____________________________________________________________________________// |
| 117 | |
| 118 | static void |
| 119 | collect_dependant_siblings( test_unit_id from, test_unit_id to, test_unit_id master_tu_id, order_info_per_tu& tuoi ) |
| 120 | { |
| 121 | int from_depth = tu_depth( tu_id: from, master_tu_id, tuoi ); |
| 122 | int to_depth = tu_depth( tu_id: to, master_tu_id, tuoi ); |
| 123 | |
| 124 | while(from_depth > to_depth) { |
| 125 | from = get_tu_parent( tu_id: from ); |
| 126 | --from_depth; |
| 127 | } |
| 128 | |
| 129 | while(from_depth < to_depth) { |
| 130 | to = get_tu_parent( tu_id: to ); |
| 131 | --to_depth; |
| 132 | } |
| 133 | |
| 134 | while(true) { |
| 135 | test_unit_id from_parent = get_tu_parent( tu_id: from ); |
| 136 | test_unit_id to_parent = get_tu_parent( tu_id: to ); |
| 137 | if( from_parent == to_parent ) |
| 138 | break; |
| 139 | from = from_parent; |
| 140 | to = to_parent; |
| 141 | } |
| 142 | |
| 143 | tuoi[from].dependant_siblings.push_back( x: to ); |
| 144 | } |
| 145 | |
| 146 | //____________________________________________________________________________// |
| 147 | |
| 148 | static counter_t |
| 149 | assign_sibling_rank( test_unit_id tu_id, order_info_per_tu& tuoi ) |
| 150 | { |
| 151 | test_unit& tu = framework::get( tu_id, tu_type: TUT_ANY ); |
| 152 | |
| 153 | BOOST_TEST_SETUP_ASSERT( tu.p_sibling_rank != (std::numeric_limits<counter_t>::max)(), |
| 154 | "Cyclic dependency detected involving test unit \"" + tu.full_name() + "\"" ); |
| 155 | |
| 156 | if( tu.p_sibling_rank != 0 ) |
| 157 | return tu.p_sibling_rank; |
| 158 | |
| 159 | order_info const& info = tuoi[tu_id]; |
| 160 | |
| 161 | // indicate in progress |
| 162 | tu.p_sibling_rank.value = (std::numeric_limits<counter_t>::max)(); |
| 163 | |
| 164 | counter_t new_rank = 1; |
| 165 | BOOST_TEST_FOREACH( test_unit_id, sibling_id, info.dependant_siblings ) |
| 166 | new_rank = (std::max)(a: new_rank, b: assign_sibling_rank( tu_id: sibling_id, tuoi ) + 1); |
| 167 | |
| 168 | return tu.p_sibling_rank.value = new_rank; |
| 169 | } |
| 170 | |
| 171 | //____________________________________________________________________________// |
| 172 | |
| 173 | // ************************************************************************** // |
| 174 | // ************** test_init call wrapper ************** // |
| 175 | // ************************************************************************** // |
| 176 | |
| 177 | static void |
| 178 | invoke_init_func( init_unit_test_func init_func ) |
| 179 | { |
| 180 | #ifdef BOOST_TEST_ALTERNATIVE_INIT_API |
| 181 | BOOST_TEST_I_ASSRT( (*init_func)(), std::runtime_error( "test module initialization failed" ) ); |
| 182 | #else |
| 183 | test_suite* manual_test_units = (*init_func)( framework::master_test_suite().argc, framework::master_test_suite().argv ); |
| 184 | |
| 185 | if( manual_test_units ) |
| 186 | framework::master_test_suite().add( tu: manual_test_units ); |
| 187 | #endif |
| 188 | } |
| 189 | |
| 190 | // ************************************************************************** // |
| 191 | // ************** name_filter ************** // |
| 192 | // ************************************************************************** // |
| 193 | |
| 194 | class name_filter : public test_tree_visitor { |
| 195 | struct component { |
| 196 | component( const_string name ) // has to be implicit |
| 197 | { |
| 198 | if( name == "*" ) |
| 199 | m_kind = SFK_ALL; |
| 200 | else if( first_char( source: name ) == '*' && last_char( source: name ) == '*' ) { |
| 201 | m_kind = SFK_SUBSTR; |
| 202 | m_name = name.substr( beg_index: 1, end_index: name.size()-1 ); |
| 203 | } |
| 204 | else if( first_char( source: name ) == '*' ) { |
| 205 | m_kind = SFK_TRAILING; |
| 206 | m_name = name.substr( beg_index: 1 ); |
| 207 | } |
| 208 | else if( last_char( source: name ) == '*' ) { |
| 209 | m_kind = SFK_LEADING; |
| 210 | m_name = name.substr( beg_index: 0, end_index: name.size()-1 ); |
| 211 | } |
| 212 | else { |
| 213 | m_kind = SFK_MATCH; |
| 214 | m_name = name; |
| 215 | } |
| 216 | } |
| 217 | |
| 218 | bool pass( test_unit const& tu ) const |
| 219 | { |
| 220 | const_string name( tu.p_name ); |
| 221 | |
| 222 | switch( m_kind ) { |
| 223 | default: |
| 224 | case SFK_ALL: |
| 225 | return true; |
| 226 | case SFK_LEADING: |
| 227 | return name.substr( beg_index: 0, end_index: m_name.size() ) == m_name; |
| 228 | case SFK_TRAILING: |
| 229 | return name.size() >= m_name.size() && name.substr( beg_index: name.size() - m_name.size() ) == m_name; |
| 230 | case SFK_SUBSTR: |
| 231 | return name.find( str: m_name ) != const_string::npos; |
| 232 | case SFK_MATCH: |
| 233 | return m_name == tu.p_name.get(); |
| 234 | } |
| 235 | } |
| 236 | enum kind { SFK_ALL, SFK_LEADING, SFK_TRAILING, SFK_SUBSTR, SFK_MATCH }; |
| 237 | |
| 238 | kind m_kind; |
| 239 | const_string m_name; |
| 240 | }; |
| 241 | |
| 242 | public: |
| 243 | // Constructor |
| 244 | name_filter( test_unit_id_list& targ_list, const_string filter_expr ) : m_targ_list( targ_list ), m_depth( 0 ) |
| 245 | { |
| 246 | #ifdef BOOST_TEST_SUPPORT_TOKEN_ITERATOR |
| 247 | utils::string_token_iterator tit( filter_expr, (utils::dropped_delimeters = "/" , |
| 248 | utils::kept_delimeters = utils::dt_none) ); |
| 249 | |
| 250 | while( tit != utils::string_token_iterator() ) { |
| 251 | m_components.push_back( |
| 252 | x: std::vector<component>( utils::string_token_iterator( *tit, (utils::dropped_delimeters = "," , |
| 253 | utils::kept_delimeters = utils::dt_none) ), |
| 254 | utils::string_token_iterator() ) ); |
| 255 | |
| 256 | ++tit; |
| 257 | } |
| 258 | #endif |
| 259 | } |
| 260 | |
| 261 | private: |
| 262 | bool filter_unit( test_unit const& tu ) |
| 263 | { |
| 264 | // skip master test suite |
| 265 | if( m_depth == 0 ) |
| 266 | return true; |
| 267 | |
| 268 | // corresponding name filters are at level m_depth-1 |
| 269 | std::vector<component> const& filters = m_components[m_depth-1]; |
| 270 | |
| 271 | // look for match |
| 272 | using namespace boost::placeholders; |
| 273 | return std::find_if( first: filters.begin(), last: filters.end(), pred: bind( f: &component::pass, a1: _1, a2: boost::ref(t: tu) ) ) != filters.end(); |
| 274 | } |
| 275 | |
| 276 | // test_tree_visitor interface |
| 277 | void visit( test_case const& tc ) BOOST_OVERRIDE |
| 278 | { |
| 279 | // make sure we only accept test cases if we match last component of the filter |
| 280 | if( m_depth == m_components.size() && filter_unit( tu: tc ) ) |
| 281 | m_targ_list.push_back( x: tc.p_id ); // found a test case |
| 282 | } |
| 283 | bool test_suite_start( test_suite const& ts ) BOOST_OVERRIDE |
| 284 | { |
| 285 | if( !filter_unit( tu: ts ) ) |
| 286 | return false; |
| 287 | |
| 288 | if( m_depth < m_components.size() ) { |
| 289 | ++m_depth; |
| 290 | return true; |
| 291 | } |
| 292 | |
| 293 | m_targ_list.push_back( x: ts.p_id ); // found a test suite |
| 294 | |
| 295 | return false; |
| 296 | } |
| 297 | void test_suite_finish( test_suite const& /*ts*/ ) BOOST_OVERRIDE |
| 298 | { |
| 299 | --m_depth; |
| 300 | } |
| 301 | |
| 302 | // Data members |
| 303 | typedef std::vector<std::vector<component> > components_per_level; |
| 304 | |
| 305 | components_per_level m_components; |
| 306 | test_unit_id_list& m_targ_list; |
| 307 | unsigned m_depth; |
| 308 | }; |
| 309 | |
| 310 | // ************************************************************************** // |
| 311 | // ************** label_filter ************** // |
| 312 | // ************************************************************************** // |
| 313 | |
| 314 | class label_filter : public test_tree_visitor { |
| 315 | public: |
| 316 | label_filter( test_unit_id_list& targ_list, const_string label ) |
| 317 | : m_targ_list( targ_list ) |
| 318 | , m_label( label ) |
| 319 | {} |
| 320 | |
| 321 | private: |
| 322 | // test_tree_visitor interface |
| 323 | bool visit( test_unit const& tu ) BOOST_OVERRIDE |
| 324 | { |
| 325 | if( tu.has_label( l: m_label ) ) { |
| 326 | // found a test unit; add it to list of tu to enable with children and stop recursion in case of suites |
| 327 | m_targ_list.push_back( x: tu.p_id ); |
| 328 | return false; |
| 329 | } |
| 330 | |
| 331 | return true; |
| 332 | } |
| 333 | |
| 334 | // Data members |
| 335 | test_unit_id_list& m_targ_list; |
| 336 | const_string m_label; |
| 337 | }; |
| 338 | |
| 339 | // ************************************************************************** // |
| 340 | // ************** set_run_status ************** // |
| 341 | // ************************************************************************** // |
| 342 | |
| 343 | class set_run_status : public test_tree_visitor { |
| 344 | public: |
| 345 | explicit set_run_status( test_unit::run_status rs, test_unit_id_list* dep_collector = 0 ) |
| 346 | : m_new_status( rs ) |
| 347 | , m_dep_collector( dep_collector ) |
| 348 | {} |
| 349 | |
| 350 | // test_tree_visitor interface |
| 351 | bool visit( test_unit const& tu ) BOOST_OVERRIDE |
| 352 | { |
| 353 | const_cast<test_unit&>(tu).p_run_status.value = m_new_status == test_unit::RS_INVALID ? tu.p_default_status : m_new_status; |
| 354 | if( m_dep_collector ) { |
| 355 | BOOST_TEST_FOREACH( test_unit_id, dep_id, tu.p_dependencies.get() ) { |
| 356 | test_unit const& dep = framework::get( tu_id: dep_id, tu_type: TUT_ANY ); |
| 357 | |
| 358 | if( dep.p_run_status == tu.p_run_status ) |
| 359 | continue; |
| 360 | |
| 361 | BOOST_TEST_FRAMEWORK_MESSAGE( "Including test " << dep.p_type_name << ' ' << dep.full_name() << |
| 362 | " as a dependency of test " << tu.p_type_name << ' ' << tu.full_name() ); |
| 363 | |
| 364 | m_dep_collector->push_back( x: dep_id ); |
| 365 | } |
| 366 | } |
| 367 | return true; |
| 368 | } |
| 369 | |
| 370 | private: |
| 371 | // Data members |
| 372 | test_unit::run_status m_new_status; |
| 373 | test_unit_id_list* m_dep_collector; |
| 374 | }; |
| 375 | |
| 376 | // ************************************************************************** // |
| 377 | // ************** parse_filters ************** // |
| 378 | // ************************************************************************** // |
| 379 | |
| 380 | static void |
| 381 | add_filtered_test_units( test_unit_id master_tu_id, const_string filter, test_unit_id_list& targ ) |
| 382 | { |
| 383 | // Choose between two kinds of filters |
| 384 | if( filter[0] == '@' ) { |
| 385 | filter.trim_left( trim_size: 1 ); |
| 386 | label_filter lf( targ, filter ); |
| 387 | traverse_test_tree( master_tu_id, lf, ignore_status: true ); |
| 388 | } |
| 389 | else { |
| 390 | name_filter nf( targ, filter ); |
| 391 | traverse_test_tree( master_tu_id, nf, ignore_status: true ); |
| 392 | } |
| 393 | } |
| 394 | |
| 395 | //____________________________________________________________________________// |
| 396 | |
| 397 | static bool |
| 398 | parse_filters( test_unit_id master_tu_id, test_unit_id_list& tu_to_enable, test_unit_id_list& tu_to_disable ) |
| 399 | { |
| 400 | // 10. collect tu to enable and disable based on filters |
| 401 | bool had_selector_filter = false; |
| 402 | |
| 403 | std::vector<std::string> const& filters = runtime_config::get<std::vector<std::string> >( parameter_name: runtime_config::btrt_run_filters ); |
| 404 | |
| 405 | BOOST_TEST_FOREACH( const_string, filter, filters ) { |
| 406 | BOOST_TEST_SETUP_ASSERT( !filter.is_empty(), "Invalid filter specification" ); |
| 407 | |
| 408 | // each --run_test command may also be separated by a ':' (environment variable) |
| 409 | utils::string_token_iterator t_filter_it( filter, (utils::dropped_delimeters = ":" , |
| 410 | utils::kept_delimeters = utils::dt_none) ); |
| 411 | |
| 412 | while( t_filter_it != utils::string_token_iterator() ) { |
| 413 | const_string filter_token = *t_filter_it; |
| 414 | |
| 415 | enum { SELECTOR, ENABLER, DISABLER } filter_type = SELECTOR; |
| 416 | |
| 417 | // 11. Deduce filter type |
| 418 | if( filter_token[0] == '!' || filter_token[0] == '+' ) { |
| 419 | filter_type = filter_token[0] == '+' ? ENABLER : DISABLER; |
| 420 | filter_token.trim_left( trim_size: 1 ); |
| 421 | BOOST_TEST_SETUP_ASSERT( !filter_token.is_empty(), "Invalid filter specification" ); |
| 422 | } |
| 423 | |
| 424 | had_selector_filter |= filter_type == SELECTOR; |
| 425 | |
| 426 | // 12. Add test units to corresponding list |
| 427 | switch( filter_type ) { |
| 428 | case SELECTOR: |
| 429 | case ENABLER: add_filtered_test_units( master_tu_id, filter: filter_token, targ&: tu_to_enable ); break; |
| 430 | case DISABLER: add_filtered_test_units( master_tu_id, filter: filter_token, targ&: tu_to_disable ); break; |
| 431 | } |
| 432 | |
| 433 | ++t_filter_it; |
| 434 | } |
| 435 | } |
| 436 | |
| 437 | return had_selector_filter; |
| 438 | } |
| 439 | |
| 440 | //____________________________________________________________________________// |
| 441 | |
| 442 | // a poor man's implementation of random_shuffle, deprecated in C++11 |
| 443 | template< class RandomIt, class RandomFunc > |
| 444 | void random_shuffle( RandomIt first, RandomIt last, RandomFunc &r ) |
| 445 | { |
| 446 | typedef typename std::iterator_traits<RandomIt>::difference_type difference_type; |
| 447 | difference_type n = last - first; |
| 448 | for (difference_type i = n-1; i > 0; --i) { |
| 449 | difference_type j = r(i+1); |
| 450 | if (j != i) { |
| 451 | using std::swap; |
| 452 | swap(first[i], first[j]); |
| 453 | } |
| 454 | } |
| 455 | } |
| 456 | |
| 457 | // A simple handle for registering the global fixtures to the master test suite |
| 458 | // without deleting an existing static object (the global fixture itself) when the program |
| 459 | // terminates (shared_ptr). |
| 460 | class global_fixture_handle : public test_unit_fixture { |
| 461 | public: |
| 462 | global_fixture_handle(test_unit_fixture* fixture) : m_global_fixture(fixture) {} |
| 463 | ~global_fixture_handle() BOOST_OVERRIDE {} |
| 464 | |
| 465 | void setup() BOOST_OVERRIDE { |
| 466 | m_global_fixture->setup(); |
| 467 | } |
| 468 | void teardown() BOOST_OVERRIDE { |
| 469 | m_global_fixture->teardown(); |
| 470 | } |
| 471 | |
| 472 | private: |
| 473 | test_unit_fixture* m_global_fixture; |
| 474 | }; |
| 475 | |
| 476 | |
| 477 | } // namespace impl |
| 478 | |
| 479 | // ************************************************************************** // |
| 480 | // ************** framework::state ************** // |
| 481 | // ************************************************************************** // |
| 482 | |
| 483 | unsigned long int const TIMEOUT_EXCEEDED = static_cast<unsigned long int>( -1 ); |
| 484 | |
| 485 | class state { |
| 486 | public: |
| 487 | state() |
| 488 | : m_master_test_suite( 0 ) |
| 489 | , m_curr_test_unit( INV_TEST_UNIT_ID ) |
| 490 | , m_next_test_case_id( MIN_TEST_CASE_ID ) |
| 491 | , m_next_test_suite_id( MIN_TEST_SUITE_ID ) |
| 492 | , m_test_in_progress( false ) |
| 493 | , m_context_idx( 0 ) |
| 494 | , m_log_sinks( ) |
| 495 | , m_report_sink( std::cerr ) |
| 496 | { |
| 497 | } |
| 498 | |
| 499 | ~state() { clear(); } |
| 500 | |
| 501 | void clear() |
| 502 | { |
| 503 | while( !m_test_units.empty() ) { |
| 504 | test_unit_store::value_type const& tu = *m_test_units.begin(); |
| 505 | test_unit const* tu_ptr = tu.second; |
| 506 | |
| 507 | // the delete will erase this element from map |
| 508 | if( ut_detail::test_id_2_unit_type( id: tu.second->p_id ) == TUT_SUITE ) |
| 509 | delete static_cast<test_suite const*>(tu_ptr); |
| 510 | else |
| 511 | delete static_cast<test_case const*>(tu_ptr); |
| 512 | } |
| 513 | } |
| 514 | |
| 515 | void set_tu_id( test_unit& tu, test_unit_id id ) { tu.p_id.value = id; } |
| 516 | |
| 517 | ////////////////////////////////////////////////////////////////// |
| 518 | |
| 519 | // Validates the dependency graph and deduces the sibling dependency rank for each child |
| 520 | void deduce_siblings_order( test_unit_id tu_id, test_unit_id master_tu_id, impl::order_info_per_tu& tuoi ) |
| 521 | { |
| 522 | test_unit& tu = framework::get( tu_id, tu_type: TUT_ANY ); |
| 523 | |
| 524 | // collect all sibling dependencies from tu own list |
| 525 | BOOST_TEST_FOREACH( test_unit_id, dep_id, tu.p_dependencies.get() ) |
| 526 | collect_dependant_siblings( from: tu_id, to: dep_id, master_tu_id, tuoi ); |
| 527 | |
| 528 | if( tu.p_type != TUT_SUITE ) |
| 529 | return; |
| 530 | |
| 531 | test_suite& ts = static_cast<test_suite&>(tu); |
| 532 | |
| 533 | // recursive call to children first |
| 534 | BOOST_TEST_FOREACH( test_unit_id, chld_id, ts.m_children ) |
| 535 | deduce_siblings_order( tu_id: chld_id, master_tu_id, tuoi ); |
| 536 | |
| 537 | ts.m_ranked_children.clear(); |
| 538 | BOOST_TEST_FOREACH( test_unit_id, chld_id, ts.m_children ) { |
| 539 | counter_t rank = assign_sibling_rank( tu_id: chld_id, tuoi ); |
| 540 | ts.m_ranked_children.insert( x: std::make_pair( x&: rank, y&: chld_id ) ); |
| 541 | } |
| 542 | } |
| 543 | |
| 544 | ////////////////////////////////////////////////////////////////// |
| 545 | |
| 546 | // Finalize default run status: |
| 547 | // 1) inherit run status from parent where applicable |
| 548 | // 2) if any of test units in test suite enabled enable it as well |
| 549 | bool finalize_default_run_status( test_unit_id tu_id, test_unit::run_status parent_status ) |
| 550 | { |
| 551 | test_unit& tu = framework::get( tu_id, tu_type: TUT_ANY ); |
| 552 | |
| 553 | if( tu.p_default_status == test_suite::RS_INHERIT ) |
| 554 | tu.p_default_status.value = parent_status; |
| 555 | |
| 556 | // go through list of children |
| 557 | if( tu.p_type == TUT_SUITE ) { |
| 558 | bool has_enabled_child = false; |
| 559 | BOOST_TEST_FOREACH( test_unit_id, chld_id, static_cast<test_suite const&>(tu).m_children ) |
| 560 | has_enabled_child |= finalize_default_run_status( tu_id: chld_id, parent_status: tu.p_default_status ); |
| 561 | |
| 562 | tu.p_default_status.value = has_enabled_child ? test_suite::RS_ENABLED : test_suite::RS_DISABLED; |
| 563 | } |
| 564 | |
| 565 | return tu.p_default_status == test_suite::RS_ENABLED; |
| 566 | } |
| 567 | |
| 568 | ////////////////////////////////////////////////////////////////// |
| 569 | |
| 570 | bool finalize_run_status( test_unit_id tu_id ) |
| 571 | { |
| 572 | test_unit& tu = framework::get( tu_id, tu_type: TUT_ANY ); |
| 573 | |
| 574 | // go through list of children |
| 575 | if( tu.p_type == TUT_SUITE ) { |
| 576 | bool has_enabled_child = false; |
| 577 | BOOST_TEST_FOREACH( test_unit_id, chld_id, static_cast<test_suite const&>(tu).m_children) |
| 578 | has_enabled_child |= finalize_run_status( tu_id: chld_id ); |
| 579 | |
| 580 | tu.p_run_status.value = has_enabled_child ? test_suite::RS_ENABLED : test_suite::RS_DISABLED; |
| 581 | } |
| 582 | |
| 583 | return tu.is_enabled(); |
| 584 | } |
| 585 | |
| 586 | ////////////////////////////////////////////////////////////////// |
| 587 | |
| 588 | void deduce_run_status( test_unit_id master_tu_id ) |
| 589 | { |
| 590 | using namespace framework::impl; |
| 591 | test_unit_id_list tu_to_enable; |
| 592 | test_unit_id_list tu_to_disable; |
| 593 | |
| 594 | // 10. If there are any filters supplied, figure out lists of test units to enable/disable |
| 595 | bool had_selector_filter = !runtime_config::get<std::vector<std::string> >( parameter_name: runtime_config::btrt_run_filters ).empty() && |
| 596 | parse_filters( master_tu_id, tu_to_enable, tu_to_disable ); |
| 597 | |
| 598 | // 20. Set the stage: either use default run status or disable all test units |
| 599 | set_run_status initial_setter( had_selector_filter ? test_unit::RS_DISABLED : test_unit::RS_INVALID ); |
| 600 | traverse_test_tree( master_tu_id, initial_setter, ignore_status: true ); |
| 601 | |
| 602 | // 30. Apply all selectors and enablers. |
| 603 | while( !tu_to_enable.empty() ) { |
| 604 | test_unit& tu = framework::get( tu_id: tu_to_enable.back(), tu_type: TUT_ANY ); |
| 605 | |
| 606 | tu_to_enable.pop_back(); |
| 607 | |
| 608 | // 35. Ignore test units which are already enabled |
| 609 | if( tu.is_enabled() ) |
| 610 | continue; |
| 611 | |
| 612 | // set new status and add all dependencies into tu_to_enable |
| 613 | set_run_status enabler( test_unit::RS_ENABLED, &tu_to_enable ); |
| 614 | traverse_test_tree( tu.p_id, enabler, ignore_status: true ); |
| 615 | |
| 616 | // Add the dependencies of the parent suites, see trac #13149 |
| 617 | test_unit_id parent_id = tu.p_parent_id; |
| 618 | while( parent_id != INV_TEST_UNIT_ID |
| 619 | && parent_id != master_tu_id ) |
| 620 | { |
| 621 | // we do not use the traverse_test_tree as otherwise it would enable the siblings and subtree |
| 622 | // of the test case we want to enable (we need to enable the parent suites and their dependencies only) |
| 623 | // the parent_id needs to be enabled in order to be properly parsed by finalize_run_status, the visit |
| 624 | // does the job |
| 625 | test_unit& tu_parent = framework::get( tu_id: parent_id, tu_type: TUT_ANY ); |
| 626 | enabler.visit( tu: tu_parent ); |
| 627 | parent_id = tu_parent.p_parent_id; |
| 628 | } |
| 629 | } |
| 630 | |
| 631 | // 40. Apply all disablers |
| 632 | while( !tu_to_disable.empty() ) { |
| 633 | test_unit const& tu = framework::get( tu_id: tu_to_disable.back(), tu_type: TUT_ANY ); |
| 634 | |
| 635 | tu_to_disable.pop_back(); |
| 636 | |
| 637 | // 35. Ignore test units which already disabled |
| 638 | if( !tu.is_enabled() ) |
| 639 | continue; |
| 640 | |
| 641 | set_run_status disabler( test_unit::RS_DISABLED ); |
| 642 | traverse_test_tree( tu.p_id, disabler, ignore_status: true ); |
| 643 | } |
| 644 | |
| 645 | // 50. Make sure parents of enabled test units are also enabled |
| 646 | finalize_run_status( tu_id: master_tu_id ); |
| 647 | } |
| 648 | |
| 649 | ////////////////////////////////////////////////////////////////// |
| 650 | |
| 651 | typedef unit_test_monitor_t::error_level execution_result; |
| 652 | |
| 653 | // Random generator using the std::rand function (seeded prior to the call) |
| 654 | struct random_generator_helper { |
| 655 | size_t operator()(size_t i) const { |
| 656 | return std::rand() % i; |
| 657 | } |
| 658 | }; |
| 659 | |
| 660 | // Executes the test tree with the root at specified test unit |
| 661 | execution_result execute_test_tree( test_unit_id tu_id, |
| 662 | unsigned long int timeout_microseconds = 0, |
| 663 | random_generator_helper const * const p_random_generator = 0) |
| 664 | { |
| 665 | test_unit const& tu = framework::get( tu_id, tu_type: TUT_ANY ); |
| 666 | |
| 667 | execution_result result = unit_test_monitor_t::test_ok; |
| 668 | |
| 669 | if( !tu.is_enabled() ) { |
| 670 | BOOST_TEST_FOREACH( test_observer*, to, m_observers ) |
| 671 | to->test_unit_skipped( tu, "disabled" ); |
| 672 | return result; |
| 673 | } |
| 674 | |
| 675 | // 10. Check preconditions, including zero time left for execution and |
| 676 | // successful execution of all dependencies |
| 677 | if( timeout_microseconds == TIMEOUT_EXCEEDED ) { |
| 678 | // notify all observers about skipped test unit |
| 679 | BOOST_TEST_FOREACH( test_observer*, to, m_observers ) |
| 680 | to->test_unit_skipped( tu, "timeout for the test unit is exceeded" ); |
| 681 | |
| 682 | return unit_test_monitor_t::os_timeout; |
| 683 | } |
| 684 | else if( timeout_microseconds == 0 || (tu.p_timeout > 0 && timeout_microseconds > (tu.p_timeout * 1000000) ) ) // deduce timeout for this test unit |
| 685 | timeout_microseconds = tu.p_timeout * 1000000; |
| 686 | |
| 687 | |
| 688 | test_tools::assertion_result const precondition_res = tu.check_preconditions(); |
| 689 | if( !precondition_res ) { |
| 690 | // notify all observers about skipped test unit |
| 691 | BOOST_TEST_FOREACH( test_observer*, to, m_observers ) |
| 692 | to->test_unit_skipped( tu, precondition_res.message() ); |
| 693 | |
| 694 | // It is not an error to skip the test if any of the parent tests |
| 695 | // have failed. This one should be reported as skipped as if it was |
| 696 | // disabled |
| 697 | return unit_test_monitor_t::test_ok; |
| 698 | } |
| 699 | |
| 700 | // 20. Notify all observers about the start of the test unit |
| 701 | BOOST_TEST_FOREACH( test_observer*, to, m_observers ) |
| 702 | to->test_unit_start( tu ); |
| 703 | |
| 704 | // 30. Execute setup fixtures if any; any failure here leads to test unit abortion |
| 705 | BOOST_TEST_FOREACH( test_unit_fixture_ptr, F, tu.p_fixtures.get() ) { |
| 706 | ut_detail::test_unit_id_restore restore_current_test_unit(m_curr_test_unit, tu.p_id); |
| 707 | result = unit_test_monitor.execute_and_translate( func: boost::bind( f: &test_unit_fixture::setup, a1: F ) ); |
| 708 | if( result != unit_test_monitor_t::test_ok ) |
| 709 | break; |
| 710 | test_results const& test_rslt = unit_test::results_collector.results( tu_id: m_curr_test_unit ); |
| 711 | if( test_rslt.aborted() ) { |
| 712 | result = unit_test_monitor_t::test_setup_failure; |
| 713 | break; |
| 714 | } |
| 715 | } |
| 716 | |
| 717 | // This is the time we are going to spend executing the test unit (in microseconds |
| 718 | // as expected by test_observer::test_unit_finish) |
| 719 | unsigned long elapsed_microseconds = 0; |
| 720 | |
| 721 | if( result == unit_test_monitor_t::test_ok ) { |
| 722 | // 40. We are going to time the execution |
| 723 | boost::unit_test::timer::timer tu_timer; |
| 724 | |
| 725 | // we pass the random generator |
| 726 | const random_generator_helper& rand_gen = p_random_generator ? *p_random_generator : random_generator_helper(); |
| 727 | |
| 728 | if( tu.p_type == TUT_SUITE ) { |
| 729 | test_suite const& ts = static_cast<test_suite const&>( tu ); |
| 730 | |
| 731 | if( runtime_config::get<unsigned>( parameter_name: runtime_config::btrt_random_seed ) == 0 ) { |
| 732 | typedef std::pair<counter_t,test_unit_id> value_type; |
| 733 | |
| 734 | BOOST_TEST_FOREACH( value_type, chld, ts.m_ranked_children ) { |
| 735 | // tu_timer.elapsed() returns nanosec, timeout and child_timeout in microsec |
| 736 | unsigned long int chld_timeout = child_timeout( |
| 737 | tu_timeout_microseconds: timeout_microseconds, |
| 738 | elpsed_microsec: static_cast<unsigned long int>( microsecond_wall_time(elapsed: tu_timer.elapsed()) )); |
| 739 | |
| 740 | result = (std::min)( a: result, b: execute_test_tree( tu_id: chld.second, timeout_microseconds: chld_timeout, p_random_generator: &rand_gen ) ); |
| 741 | |
| 742 | if( unit_test_monitor.is_critical_error( e: result ) ) |
| 743 | break; |
| 744 | |
| 745 | // we check for the time elapsed. If this is too high, we fail the current suite and return from here |
| 746 | elapsed_microseconds = static_cast<unsigned long int>( microsecond_wall_time(elapsed: tu_timer.elapsed()) ); |
| 747 | |
| 748 | if( (timeout_microseconds > 0) && (elapsed_microseconds > timeout_microseconds) && (timeout_microseconds != TIMEOUT_EXCEEDED ) ) { |
| 749 | BOOST_TEST_FOREACH( test_observer*, to, m_observers ) { |
| 750 | to->test_unit_timed_out(tu); |
| 751 | } |
| 752 | result = (std::min)( a: result, b: unit_test_monitor_t::os_timeout ); |
| 753 | timeout_microseconds = TIMEOUT_EXCEEDED; |
| 754 | //break; |
| 755 | // we continue to explore the children, such that we can at least update their |
| 756 | // status to skipped |
| 757 | } |
| 758 | } |
| 759 | } |
| 760 | else { |
| 761 | // Go through ranges of children with the same dependency rank and shuffle them |
| 762 | // independently. Execute each subtree in this order |
| 763 | test_unit_id_list children_with_the_same_rank; |
| 764 | |
| 765 | typedef test_suite::children_per_rank::const_iterator it_type; |
| 766 | it_type it = ts.m_ranked_children.begin(); |
| 767 | while( it != ts.m_ranked_children.end() ) { |
| 768 | children_with_the_same_rank.clear(); |
| 769 | |
| 770 | std::pair<it_type,it_type> range = ts.m_ranked_children.equal_range( x: it->first ); |
| 771 | it = range.first; |
| 772 | while( it != range.second ) { |
| 773 | children_with_the_same_rank.push_back( x: it->second ); |
| 774 | it++; |
| 775 | } |
| 776 | |
| 777 | impl::random_shuffle( first: children_with_the_same_rank.begin(), last: children_with_the_same_rank.end(), r: rand_gen ); |
| 778 | |
| 779 | BOOST_TEST_FOREACH( test_unit_id, chld, children_with_the_same_rank ) { |
| 780 | unsigned long int chld_timeout = child_timeout( |
| 781 | tu_timeout_microseconds: timeout_microseconds, |
| 782 | elpsed_microsec: static_cast<unsigned long int>(microsecond_wall_time(elapsed: tu_timer.elapsed())) ); |
| 783 | |
| 784 | result = (std::min)( a: result, b: execute_test_tree( tu_id: chld, timeout_microseconds: chld_timeout, p_random_generator: &rand_gen ) ); |
| 785 | |
| 786 | if( unit_test_monitor.is_critical_error( e: result ) ) |
| 787 | break; |
| 788 | |
| 789 | // we check for the time elapsed. If this is too high, we fail the current suite and return from here |
| 790 | elapsed_microseconds = static_cast<unsigned long int>( microsecond_wall_time(elapsed: tu_timer.elapsed()) ); |
| 791 | if( (timeout_microseconds > 0) && (elapsed_microseconds > timeout_microseconds) && (timeout_microseconds != TIMEOUT_EXCEEDED ) ) { |
| 792 | BOOST_TEST_FOREACH( test_observer*, to, m_observers ) { |
| 793 | to->test_unit_timed_out(tu); |
| 794 | } |
| 795 | result = (std::min)( a: result, b: unit_test_monitor_t::os_timeout ); |
| 796 | timeout_microseconds = TIMEOUT_EXCEEDED; |
| 797 | //break; |
| 798 | // we continue to explore the children, such that we can at least update their |
| 799 | // status to skipped |
| 800 | } |
| 801 | } |
| 802 | } |
| 803 | } |
| 804 | } |
| 805 | else { // TUT_CASE |
| 806 | test_case const& tc = static_cast<test_case const&>( tu ); |
| 807 | |
| 808 | // setup contexts |
| 809 | m_context_idx = 0; |
| 810 | |
| 811 | // setup current test case |
| 812 | ut_detail::test_unit_id_restore restore_current_test_unit(m_curr_test_unit, tc.p_id); |
| 813 | |
| 814 | // execute the test case body, transforms the time out to seconds |
| 815 | result = unit_test_monitor.execute_and_translate( func: tc.p_test_func, timeout_microseconds ); |
| 816 | elapsed_microseconds = static_cast<unsigned long int>( microsecond_wall_time(elapsed: tu_timer.elapsed()) ); |
| 817 | |
| 818 | // cleanup leftover context |
| 819 | m_context.clear(); |
| 820 | |
| 821 | // restore state (scope exit) and abort if necessary |
| 822 | } |
| 823 | } |
| 824 | |
| 825 | // if run error is critical skip teardown, who knows what the state of the program at this point |
| 826 | if( !unit_test_monitor.is_critical_error( e: result ) ) { |
| 827 | // execute teardown fixtures if any in reverse order |
| 828 | BOOST_TEST_REVERSE_FOREACH( test_unit_fixture_ptr, F, tu.p_fixtures.get() ) { |
| 829 | ut_detail::test_unit_id_restore restore_current_test_unit(m_curr_test_unit, tu.p_id); |
| 830 | result = (std::min)( a: result, b: unit_test_monitor.execute_and_translate( func: boost::bind( f: &test_unit_fixture::teardown, a1: F ), timeout_microseconds: 0 ) ); |
| 831 | |
| 832 | if( unit_test_monitor.is_critical_error( e: result ) ) |
| 833 | break; |
| 834 | } |
| 835 | } |
| 836 | |
| 837 | // notify all observers about abortion |
| 838 | if( unit_test_monitor.is_critical_error( e: result ) ) { |
| 839 | BOOST_TEST_FOREACH( test_observer*, to, m_observers ) |
| 840 | to->test_aborted(); |
| 841 | } |
| 842 | |
| 843 | // notify all observers about completion |
| 844 | BOOST_TEST_REVERSE_FOREACH( test_observer*, to, m_observers ) |
| 845 | to->test_unit_finish( tu, elapsed_microseconds ); |
| 846 | |
| 847 | return result; |
| 848 | } |
| 849 | |
| 850 | ////////////////////////////////////////////////////////////////// |
| 851 | |
| 852 | unsigned long int child_timeout( unsigned long tu_timeout_microseconds, unsigned long elpsed_microsec ) |
| 853 | { |
| 854 | if( tu_timeout_microseconds == 0UL || tu_timeout_microseconds == TIMEOUT_EXCEEDED) |
| 855 | return tu_timeout_microseconds; |
| 856 | |
| 857 | return tu_timeout_microseconds > elpsed_microsec ? |
| 858 | tu_timeout_microseconds - elpsed_microsec |
| 859 | : TIMEOUT_EXCEEDED; |
| 860 | } |
| 861 | |
| 862 | struct priority_order { |
| 863 | bool operator()( test_observer* lhs, test_observer* rhs ) const |
| 864 | { |
| 865 | return (lhs->priority() < rhs->priority()) || ((lhs->priority() == rhs->priority()) && (lhs < rhs)); |
| 866 | } |
| 867 | }; |
| 868 | |
| 869 | // Data members |
| 870 | typedef std::map<test_unit_id,test_unit*> test_unit_store; |
| 871 | typedef std::set<test_observer*,priority_order> observer_store; |
| 872 | struct context_frame { |
| 873 | context_frame( std::string const& d, int id, bool sticky ) |
| 874 | : descr( d ) |
| 875 | , frame_id( id ) |
| 876 | , is_sticky( sticky ) |
| 877 | {} |
| 878 | |
| 879 | std::string descr; |
| 880 | int frame_id; |
| 881 | bool is_sticky; |
| 882 | }; |
| 883 | typedef std::vector<context_frame> context_data; |
| 884 | |
| 885 | master_test_suite_t* m_master_test_suite; |
| 886 | std::vector<test_suite*> m_auto_test_suites; |
| 887 | |
| 888 | test_unit_id m_curr_test_unit; |
| 889 | test_unit_store m_test_units; |
| 890 | |
| 891 | test_unit_id m_next_test_case_id; |
| 892 | test_unit_id m_next_test_suite_id; |
| 893 | |
| 894 | bool m_test_in_progress; |
| 895 | |
| 896 | observer_store m_observers; |
| 897 | context_data m_context; |
| 898 | int m_context_idx; |
| 899 | |
| 900 | std::set<global_fixture*> m_global_fixtures; |
| 901 | |
| 902 | boost::execution_monitor m_aux_em; |
| 903 | |
| 904 | std::map<output_format, runtime_config::stream_holder> m_log_sinks; |
| 905 | runtime_config::stream_holder m_report_sink; |
| 906 | }; |
| 907 | |
| 908 | //____________________________________________________________________________// |
| 909 | |
| 910 | namespace impl { |
| 911 | namespace { |
| 912 | |
| 913 | #if defined(__CYGWIN__) |
| 914 | framework::state& s_frk_state() { static framework::state* the_inst = 0; if(!the_inst) the_inst = new framework::state; return *the_inst; } |
| 915 | #else |
| 916 | framework::state& s_frk_state() { static framework::state the_inst; return the_inst; } |
| 917 | #endif |
| 918 | |
| 919 | } // local namespace |
| 920 | |
| 921 | void |
| 922 | setup_for_execution( test_unit const& tu ) |
| 923 | { |
| 924 | s_frk_state().deduce_run_status( master_tu_id: tu.p_id ); |
| 925 | } |
| 926 | |
| 927 | struct sum_to_first_only { |
| 928 | sum_to_first_only() : is_first(true) {} |
| 929 | template <class T, class U> |
| 930 | T operator()(T const& l_, U const& r_) { |
| 931 | if(is_first) { |
| 932 | is_first = false; |
| 933 | return l_ + r_.first; |
| 934 | } |
| 935 | return l_ + ", " + r_.first; |
| 936 | } |
| 937 | |
| 938 | bool is_first; |
| 939 | }; |
| 940 | |
| 941 | void |
| 942 | shutdown_loggers_and_reports() |
| 943 | { |
| 944 | s_frk_state().m_log_sinks.clear(); |
| 945 | s_frk_state().m_report_sink.setup( stream_name: "stderr" ); |
| 946 | } |
| 947 | |
| 948 | void |
| 949 | unregister_global_fixture_and_configuration() |
| 950 | { |
| 951 | // we make a copy as the set will change in the iteration |
| 952 | std::set<global_fixture*> gfixture_copy(s_frk_state().m_global_fixtures); |
| 953 | BOOST_TEST_FOREACH( global_fixture*, tuf, gfixture_copy ) { |
| 954 | tuf->unregister_from_framework(); |
| 955 | } |
| 956 | s_frk_state().m_global_fixtures.clear(); |
| 957 | |
| 958 | state::observer_store gobserver_copy(s_frk_state().m_observers); |
| 959 | BOOST_TEST_FOREACH( test_observer*, to, gobserver_copy ) { |
| 960 | framework::deregister_observer( to&: *to ); |
| 961 | } |
| 962 | s_frk_state().m_observers.clear(); |
| 963 | } |
| 964 | |
| 965 | void |
| 966 | setup_loggers() |
| 967 | { |
| 968 | |
| 969 | BOOST_TEST_I_TRY { |
| 970 | |
| 971 | #ifdef BOOST_TEST_SUPPORT_TOKEN_ITERATOR |
| 972 | bool has_combined_logger = runtime_config::has( parameter_name: runtime_config::btrt_combined_logger ) |
| 973 | && !runtime_config::get< std::vector<std::string> >( parameter_name: runtime_config::btrt_combined_logger ).empty(); |
| 974 | #else |
| 975 | bool has_combined_logger = false; |
| 976 | #endif |
| 977 | |
| 978 | if( !has_combined_logger ) { |
| 979 | unit_test_log.set_threshold_level( runtime_config::get<log_level>( parameter_name: runtime_config::btrt_log_level ) ); |
| 980 | const output_format format = runtime_config::get<output_format>( parameter_name: runtime_config::btrt_log_format ); |
| 981 | unit_test_log.set_format( format ); |
| 982 | |
| 983 | runtime_config::stream_holder& stream_logger = s_frk_state().m_log_sinks[format]; |
| 984 | if( runtime_config::has( parameter_name: runtime_config::btrt_log_sink ) ) { |
| 985 | // we remove all streams in this case, so we do not specify the format |
| 986 | boost::function< void () > log_cleaner = boost::bind( f: &unit_test_log_t::set_stream, |
| 987 | a1: &unit_test_log, |
| 988 | a2: boost::ref(t&: std::cout) |
| 989 | ); |
| 990 | stream_logger.setup( stream_name: runtime_config::get<std::string>( parameter_name: runtime_config::btrt_log_sink ), |
| 991 | cleaner_callback: log_cleaner ); |
| 992 | } |
| 993 | unit_test_log.set_stream( stream_logger.ref() ); |
| 994 | unit_test_log.configure(); |
| 995 | } |
| 996 | else |
| 997 | { |
| 998 | |
| 999 | const std::vector<std::string>& v_output_format = runtime_config::get< std::vector<std::string> >( parameter_name: runtime_config::btrt_combined_logger ) ; |
| 1000 | |
| 1001 | static const std::pair<const char*, log_level> all_log_levels[] = { |
| 1002 | std::make_pair( x: "all" , y: log_successful_tests ), |
| 1003 | std::make_pair( x: "success" , y: log_successful_tests ), |
| 1004 | std::make_pair( x: "test_suite" , y: log_test_units ), |
| 1005 | std::make_pair( x: "unit_scope" , y: log_test_units ), |
| 1006 | std::make_pair( x: "message" , y: log_messages ), |
| 1007 | std::make_pair( x: "warning" , y: log_warnings ), |
| 1008 | std::make_pair( x: "error" , y: log_all_errors ), |
| 1009 | std::make_pair( x: "cpp_exception" , y: log_cpp_exception_errors ), |
| 1010 | std::make_pair( x: "system_error" , y: log_system_errors ), |
| 1011 | std::make_pair( x: "fatal_error" , y: log_fatal_errors ), |
| 1012 | std::make_pair( x: "nothing" , y: log_nothing ) |
| 1013 | }; |
| 1014 | |
| 1015 | static const std::pair<const char*, output_format> all_formats[] = { |
| 1016 | std::make_pair( x: "HRF" , y: OF_CLF ), |
| 1017 | std::make_pair( x: "CLF" , y: OF_CLF ), |
| 1018 | std::make_pair( x: "XML" , y: OF_XML ), |
| 1019 | std::make_pair( x: "JUNIT" , y: OF_JUNIT ) |
| 1020 | }; |
| 1021 | |
| 1022 | |
| 1023 | bool is_first = true; |
| 1024 | |
| 1025 | BOOST_TEST_FOREACH( const_string, current_multi_config, v_output_format ) { |
| 1026 | |
| 1027 | #ifdef BOOST_TEST_SUPPORT_TOKEN_ITERATOR |
| 1028 | |
| 1029 | // ':' may be used for file names: C:/tmp/mylogsink.xml |
| 1030 | // we merge the tokens that start with / or \ with the previous one. |
| 1031 | std::vector<std::string> v_processed_tokens; |
| 1032 | |
| 1033 | { |
| 1034 | utils::string_token_iterator current_config( current_multi_config, (utils::dropped_delimeters = ":" , |
| 1035 | utils::kept_delimeters = utils::dt_none) ); |
| 1036 | |
| 1037 | for( ; current_config != utils::string_token_iterator() ; ++current_config) { |
| 1038 | std::string str_copy(current_config->begin(), current_config->end()); |
| 1039 | if( ( str_copy[0] == '\\' || str_copy[0] == '/' ) |
| 1040 | && v_processed_tokens.size() > 0) { |
| 1041 | v_processed_tokens.back() += ":" + str_copy; // ':' has been eaten up |
| 1042 | } |
| 1043 | else { |
| 1044 | v_processed_tokens.push_back(x: str_copy); |
| 1045 | } |
| 1046 | } |
| 1047 | } |
| 1048 | |
| 1049 | BOOST_TEST_FOREACH( std::string const&, current_config, v_processed_tokens ) { |
| 1050 | |
| 1051 | utils::string_token_iterator current_format_specs( current_config, (utils::keep_empty_tokens, |
| 1052 | utils::dropped_delimeters = "," , |
| 1053 | utils::kept_delimeters = utils::dt_none) ); |
| 1054 | |
| 1055 | output_format format = OF_INVALID ; // default |
| 1056 | if( current_format_specs != utils::string_token_iterator() && |
| 1057 | current_format_specs->size() ) { |
| 1058 | |
| 1059 | for(size_t elem=0; elem < sizeof(all_formats)/sizeof(all_formats[0]); elem++) { |
| 1060 | if(const_string(all_formats[elem].first) == *current_format_specs) { |
| 1061 | format = all_formats[elem].second; |
| 1062 | break; |
| 1063 | } |
| 1064 | } |
| 1065 | } |
| 1066 | |
| 1067 | BOOST_TEST_I_ASSRT( format != OF_INVALID, |
| 1068 | boost::runtime::access_to_missing_argument() |
| 1069 | << "Unable to determine the logger type from '" |
| 1070 | << current_config |
| 1071 | << "'. Possible choices are: " |
| 1072 | << std::accumulate(all_formats, |
| 1073 | all_formats + sizeof(all_formats)/sizeof(all_formats[0]), |
| 1074 | std::string("" ), |
| 1075 | sum_to_first_only()) |
| 1076 | ); |
| 1077 | |
| 1078 | // activates this format |
| 1079 | if( is_first ) { |
| 1080 | unit_test_log.set_format( format ); |
| 1081 | } |
| 1082 | else { |
| 1083 | unit_test_log.add_format( format ); |
| 1084 | } |
| 1085 | is_first = false; |
| 1086 | |
| 1087 | unit_test_log_formatter * const formatter = unit_test_log.get_formatter(format); |
| 1088 | BOOST_TEST_SETUP_ASSERT( formatter, "Logger setup error" ); |
| 1089 | |
| 1090 | log_level formatter_log_level = invalid_log_level; |
| 1091 | ++current_format_specs ; |
| 1092 | if( !current_format_specs->size() ) { |
| 1093 | formatter_log_level = formatter->get_log_level(); // default log level given by the formatter |
| 1094 | } |
| 1095 | else if( current_format_specs != utils::string_token_iterator() ) { |
| 1096 | |
| 1097 | for(size_t elem=0; elem < sizeof(all_log_levels)/sizeof(all_log_levels[0]); elem++) { |
| 1098 | if(const_string(all_log_levels[elem].first) == *current_format_specs) { |
| 1099 | formatter_log_level = all_log_levels[elem].second; |
| 1100 | break; |
| 1101 | } |
| 1102 | } |
| 1103 | } |
| 1104 | |
| 1105 | BOOST_TEST_I_ASSRT( formatter_log_level != invalid_log_level, |
| 1106 | boost::runtime::access_to_missing_argument() |
| 1107 | << "Unable to determine the log level from '" |
| 1108 | << current_config |
| 1109 | << "'. Possible choices are: " |
| 1110 | << std::accumulate(all_log_levels, |
| 1111 | all_log_levels + sizeof(all_log_levels)/sizeof(all_log_levels[0]), |
| 1112 | std::string("" ), |
| 1113 | sum_to_first_only()) |
| 1114 | ); |
| 1115 | |
| 1116 | unit_test_log.set_threshold_level( format, formatter_log_level ); |
| 1117 | |
| 1118 | runtime_config::stream_holder& stream_logger = s_frk_state().m_log_sinks[format]; |
| 1119 | boost::function< void () > log_cleaner = boost::bind( f: &unit_test_log_t::set_stream, |
| 1120 | a1: &unit_test_log, |
| 1121 | a2: format, |
| 1122 | a3: boost::ref(t&: std::cout) ); |
| 1123 | if( ++current_format_specs != utils::string_token_iterator() && |
| 1124 | current_format_specs->size() ) { |
| 1125 | stream_logger.setup( stream_name: *current_format_specs, |
| 1126 | cleaner_callback: log_cleaner ); |
| 1127 | } |
| 1128 | else { |
| 1129 | stream_logger.setup( stream_name: formatter->get_default_stream_description(), |
| 1130 | cleaner_callback: log_cleaner ); |
| 1131 | } |
| 1132 | unit_test_log.set_stream( format, stream_logger.ref() ); |
| 1133 | } |
| 1134 | #endif |
| 1135 | } // for each logger |
| 1136 | |
| 1137 | } // if/else new logger API |
| 1138 | } // BOOST_TEST_I_TRY |
| 1139 | BOOST_TEST_I_CATCH( boost::runtime::init_error, ex ) { |
| 1140 | BOOST_TEST_SETUP_ASSERT( false, ex.msg ); |
| 1141 | } |
| 1142 | BOOST_TEST_I_CATCH( boost::runtime::input_error, ex ) { |
| 1143 | std::cerr << ex.msg << "\n\n" ; |
| 1144 | |
| 1145 | BOOST_TEST_I_THROW( framework::nothing_to_test( boost::exit_exception_failure ) ); |
| 1146 | } |
| 1147 | |
| 1148 | |
| 1149 | } |
| 1150 | |
| 1151 | //____________________________________________________________________________// |
| 1152 | |
| 1153 | } // namespace impl |
| 1154 | |
| 1155 | //____________________________________________________________________________// |
| 1156 | |
| 1157 | // ************************************************************************** // |
| 1158 | // ************** framework::init ************** // |
| 1159 | // ************************************************************************** // |
| 1160 | |
| 1161 | void |
| 1162 | init( init_unit_test_func init_func, int argc, char* argv[] ) |
| 1163 | { |
| 1164 | using namespace impl; |
| 1165 | |
| 1166 | // 10. Set up runtime parameters |
| 1167 | runtime_config::init( argc, argv ); |
| 1168 | |
| 1169 | // 20. Set the desired log level, format and sink |
| 1170 | impl::setup_loggers(); |
| 1171 | |
| 1172 | // 30. Set the desired report level, format and sink |
| 1173 | results_reporter::set_level( runtime_config::get<report_level>( parameter_name: runtime_config::btrt_report_level ) ); |
| 1174 | results_reporter::set_format( runtime_config::get<output_format>( parameter_name: runtime_config::btrt_report_format ) ); |
| 1175 | |
| 1176 | if( runtime_config::has( parameter_name: runtime_config::btrt_report_sink ) ) { |
| 1177 | boost::function< void () > report_cleaner = boost::bind( f: &results_reporter::set_stream, |
| 1178 | a1: boost::ref(t&: std::cerr) |
| 1179 | ); |
| 1180 | s_frk_state().m_report_sink.setup( stream_name: runtime_config::get<std::string>( parameter_name: runtime_config::btrt_report_sink ), |
| 1181 | cleaner_callback: report_cleaner ); |
| 1182 | } |
| 1183 | |
| 1184 | results_reporter::set_stream( s_frk_state().m_report_sink.ref() ); |
| 1185 | |
| 1186 | // 40. Register default test observers |
| 1187 | register_observer( to&: results_collector ); |
| 1188 | register_observer( to&: unit_test_log ); |
| 1189 | |
| 1190 | if( runtime_config::get<bool>( parameter_name: runtime_config::btrt_show_progress ) ) { |
| 1191 | progress_monitor.set_stream( std::cout ); // defaults to stdout |
| 1192 | register_observer( to&: progress_monitor ); |
| 1193 | } |
| 1194 | |
| 1195 | // 50. Set up memory leak detection |
| 1196 | unsigned long detect_mem_leak = runtime_config::get<unsigned long>( parameter_name: runtime_config::btrt_detect_mem_leaks ); |
| 1197 | if( detect_mem_leak > 0 ) { |
| 1198 | debug::detect_memory_leaks( on_off: true, report_file: runtime_config::get<std::string>( parameter_name: runtime_config::btrt_report_mem_leaks ) ); |
| 1199 | debug::break_memory_alloc( mem_alloc_order_num: (long)detect_mem_leak ); |
| 1200 | } |
| 1201 | |
| 1202 | // 60. Initialize master unit test suite |
| 1203 | master_test_suite().argc = argc; |
| 1204 | master_test_suite().argv = argv; |
| 1205 | |
| 1206 | // 70. Invoke test module initialization routine |
| 1207 | BOOST_TEST_I_TRY { |
| 1208 | s_frk_state().m_aux_em.vexecute( F: boost::bind( f: &impl::invoke_init_func, a1: init_func ) ); |
| 1209 | } |
| 1210 | BOOST_TEST_I_CATCH( execution_exception, ex ) { |
| 1211 | BOOST_TEST_SETUP_ASSERT( false, ex.what() ); |
| 1212 | } |
| 1213 | } |
| 1214 | |
| 1215 | //____________________________________________________________________________// |
| 1216 | |
| 1217 | void |
| 1218 | finalize_setup_phase( test_unit_id master_tu_id ) |
| 1219 | { |
| 1220 | if( master_tu_id == INV_TEST_UNIT_ID ) |
| 1221 | master_tu_id = master_test_suite().p_id; |
| 1222 | |
| 1223 | // 10. Apply all decorators to the auto test units |
| 1224 | // 10. checks for consistency (duplicate names, etc) |
| 1225 | class apply_decorators : public test_tree_visitor { |
| 1226 | private: |
| 1227 | // test_tree_visitor interface |
| 1228 | |
| 1229 | bool test_suite_start( test_suite const& ts) BOOST_OVERRIDE |
| 1230 | { |
| 1231 | const_cast<test_suite&>(ts).generate(); |
| 1232 | const_cast<test_suite&>(ts).check_for_duplicate_test_cases(); |
| 1233 | return test_tree_visitor::test_suite_start(ts); |
| 1234 | } |
| 1235 | |
| 1236 | bool visit( test_unit const& tu ) BOOST_OVERRIDE |
| 1237 | { |
| 1238 | BOOST_TEST_FOREACH( decorator::base_ptr, d, tu.p_decorators.get() ) |
| 1239 | d->apply( tu&: const_cast<test_unit&>(tu) ); |
| 1240 | |
| 1241 | return true; |
| 1242 | } |
| 1243 | } ad; |
| 1244 | traverse_test_tree( master_tu_id, ad, ignore_status: true ); |
| 1245 | |
| 1246 | // 20. Finalize setup phase |
| 1247 | impl::order_info_per_tu tuoi; |
| 1248 | impl::s_frk_state().deduce_siblings_order( tu_id: master_tu_id, master_tu_id, tuoi ); |
| 1249 | impl::s_frk_state().finalize_default_run_status( tu_id: master_tu_id, parent_status: test_unit::RS_INVALID ); |
| 1250 | } |
| 1251 | |
| 1252 | // ************************************************************************** // |
| 1253 | // ************** test_in_progress ************** // |
| 1254 | // ************************************************************************** // |
| 1255 | |
| 1256 | bool |
| 1257 | test_in_progress() |
| 1258 | { |
| 1259 | return impl::s_frk_state().m_test_in_progress; |
| 1260 | } |
| 1261 | |
| 1262 | //____________________________________________________________________________// |
| 1263 | |
| 1264 | // ************************************************************************** // |
| 1265 | // ************** framework::shutdown ************** // |
| 1266 | // ************************************************************************** // |
| 1267 | |
| 1268 | void |
| 1269 | shutdown() |
| 1270 | { |
| 1271 | // shuts down the loggers singleton to avoid any further reference to the |
| 1272 | // framework during the destruction of those |
| 1273 | impl::shutdown_loggers_and_reports(); |
| 1274 | |
| 1275 | // unregisters any global fixture and configuration object |
| 1276 | impl::unregister_global_fixture_and_configuration(); |
| 1277 | |
| 1278 | // eliminating some fake memory leak reports. See for more details: |
| 1279 | // http://connect.microsoft.com/VisualStudio/feedback/details/106937/memory-leaks-reported-by-debug-crt-inside-typeinfo-name |
| 1280 | |
| 1281 | # if BOOST_WORKAROUND(BOOST_MSVC, <= 1600 ) && !defined(_DLL) && defined(_DEBUG) |
| 1282 | # if BOOST_WORKAROUND(BOOST_MSVC, < 1600 ) |
| 1283 | #define _Next next |
| 1284 | #define _MemPtr memPtr |
| 1285 | #endif |
| 1286 | __type_info_node* pNode = __type_info_root_node._Next; |
| 1287 | __type_info_node* tmpNode = &__type_info_root_node; |
| 1288 | |
| 1289 | for( ; pNode!=NULL; pNode = tmpNode ) { |
| 1290 | tmpNode = pNode->_Next; |
| 1291 | delete pNode->_MemPtr; |
| 1292 | delete pNode; |
| 1293 | } |
| 1294 | # if BOOST_WORKAROUND(BOOST_MSVC, < 1600 ) |
| 1295 | #undef _Next |
| 1296 | #undef _MemPtr |
| 1297 | #endif |
| 1298 | # endif |
| 1299 | } |
| 1300 | |
| 1301 | //____________________________________________________________________________// |
| 1302 | |
| 1303 | // ************************************************************************** // |
| 1304 | // ************** register_test_unit ************** // |
| 1305 | // ************************************************************************** // |
| 1306 | |
| 1307 | void |
| 1308 | register_test_unit( test_case* tc ) |
| 1309 | { |
| 1310 | BOOST_TEST_SETUP_ASSERT( tc->p_id == INV_TEST_UNIT_ID, BOOST_TEST_L( "test case already registered" ) ); |
| 1311 | |
| 1312 | test_unit_id new_id = impl::s_frk_state().m_next_test_case_id; |
| 1313 | |
| 1314 | BOOST_TEST_SETUP_ASSERT( new_id != MAX_TEST_CASE_ID, BOOST_TEST_L( "too many test cases" ) ); |
| 1315 | |
| 1316 | typedef state::test_unit_store::value_type map_value_type; |
| 1317 | |
| 1318 | impl::s_frk_state().m_test_units.insert( x: map_value_type( new_id, tc ) ); |
| 1319 | impl::s_frk_state().m_next_test_case_id++; |
| 1320 | |
| 1321 | impl::s_frk_state().set_tu_id( tu&: *tc, id: new_id ); |
| 1322 | } |
| 1323 | |
| 1324 | //____________________________________________________________________________// |
| 1325 | |
| 1326 | // ************************************************************************** // |
| 1327 | // ************** register_test_unit ************** // |
| 1328 | // ************************************************************************** // |
| 1329 | |
| 1330 | void |
| 1331 | register_test_unit( test_suite* ts ) |
| 1332 | { |
| 1333 | BOOST_TEST_SETUP_ASSERT( ts->p_id == INV_TEST_UNIT_ID, BOOST_TEST_L( "test suite already registered" ) ); |
| 1334 | |
| 1335 | test_unit_id new_id = impl::s_frk_state().m_next_test_suite_id; |
| 1336 | |
| 1337 | BOOST_TEST_SETUP_ASSERT( new_id != MAX_TEST_SUITE_ID, BOOST_TEST_L( "too many test suites" ) ); |
| 1338 | |
| 1339 | typedef state::test_unit_store::value_type map_value_type; |
| 1340 | |
| 1341 | impl::s_frk_state().m_test_units.insert( x: map_value_type( new_id, ts ) ); |
| 1342 | impl::s_frk_state().m_next_test_suite_id++; |
| 1343 | |
| 1344 | impl::s_frk_state().set_tu_id( tu&: *ts, id: new_id ); |
| 1345 | } |
| 1346 | |
| 1347 | //____________________________________________________________________________// |
| 1348 | |
| 1349 | // ************************************************************************** // |
| 1350 | // ************** deregister_test_unit ************** // |
| 1351 | // ************************************************************************** // |
| 1352 | |
| 1353 | void |
| 1354 | deregister_test_unit( test_unit* tu ) |
| 1355 | { |
| 1356 | impl::s_frk_state().m_test_units.erase( x: tu->p_id ); |
| 1357 | } |
| 1358 | |
| 1359 | //____________________________________________________________________________// |
| 1360 | |
| 1361 | // ************************************************************************** // |
| 1362 | // ************** clear ************** // |
| 1363 | // ************************************************************************** // |
| 1364 | |
| 1365 | void |
| 1366 | clear() |
| 1367 | { |
| 1368 | impl::s_frk_state().clear(); |
| 1369 | } |
| 1370 | |
| 1371 | //____________________________________________________________________________// |
| 1372 | |
| 1373 | // ************************************************************************** // |
| 1374 | // ************** register_observer ************** // |
| 1375 | // ************************************************************************** // |
| 1376 | |
| 1377 | void |
| 1378 | register_observer( test_observer& to ) |
| 1379 | { |
| 1380 | impl::s_frk_state().m_observers.insert( x: &to ); |
| 1381 | } |
| 1382 | |
| 1383 | //____________________________________________________________________________// |
| 1384 | |
| 1385 | // ************************************************************************** // |
| 1386 | // ************** deregister_observer ************** // |
| 1387 | // ************************************************************************** // |
| 1388 | |
| 1389 | void |
| 1390 | deregister_observer( test_observer& to ) |
| 1391 | { |
| 1392 | impl::s_frk_state().m_observers.erase( x: &to ); |
| 1393 | } |
| 1394 | |
| 1395 | //____________________________________________________________________________// |
| 1396 | |
| 1397 | // ************************************************************************** // |
| 1398 | // ************** register_global_fixture ************** // |
| 1399 | // ************************************************************************** // |
| 1400 | |
| 1401 | void |
| 1402 | register_global_fixture( global_fixture& tuf ) |
| 1403 | { |
| 1404 | impl::s_frk_state().m_global_fixtures.insert( x: &tuf ); |
| 1405 | } |
| 1406 | |
| 1407 | //____________________________________________________________________________// |
| 1408 | |
| 1409 | // ************************************************************************** // |
| 1410 | // ************** deregister_global_fixture ************** // |
| 1411 | // ************************************************************************** // |
| 1412 | |
| 1413 | void |
| 1414 | deregister_global_fixture( global_fixture &tuf ) |
| 1415 | { |
| 1416 | impl::s_frk_state().m_global_fixtures.erase( x: &tuf ); |
| 1417 | } |
| 1418 | |
| 1419 | //____________________________________________________________________________// |
| 1420 | |
| 1421 | // ************************************************************************** // |
| 1422 | // ************** add_context ************** // |
| 1423 | // ************************************************************************** // |
| 1424 | |
| 1425 | int |
| 1426 | add_context( ::boost::unit_test::lazy_ostream const& context_descr, bool sticky ) |
| 1427 | { |
| 1428 | std::stringstream buffer; |
| 1429 | context_descr( buffer ); |
| 1430 | int res_idx = impl::s_frk_state().m_context_idx++; |
| 1431 | |
| 1432 | impl::s_frk_state().m_context.push_back( x: state::context_frame( buffer.str(), res_idx, sticky ) ); |
| 1433 | |
| 1434 | return res_idx; |
| 1435 | } |
| 1436 | |
| 1437 | //____________________________________________________________________________// |
| 1438 | |
| 1439 | // ************************************************************************** // |
| 1440 | // ************** clear_context ************** // |
| 1441 | // ************************************************************************** // |
| 1442 | |
| 1443 | struct frame_with_id { |
| 1444 | explicit frame_with_id( int id ) : m_id( id ) {} |
| 1445 | |
| 1446 | bool operator()( state::context_frame const& f ) |
| 1447 | { |
| 1448 | return f.frame_id == m_id; |
| 1449 | } |
| 1450 | int m_id; |
| 1451 | }; |
| 1452 | |
| 1453 | //____________________________________________________________________________// |
| 1454 | |
| 1455 | void |
| 1456 | clear_context( int frame_id ) |
| 1457 | { |
| 1458 | if( frame_id == -1 ) { // clear all non sticky frames |
| 1459 | for( int i=static_cast<int>(impl::s_frk_state().m_context.size())-1; i>=0; i-- ) |
| 1460 | if( !impl::s_frk_state().m_context[i].is_sticky ) |
| 1461 | impl::s_frk_state().m_context.erase( position: impl::s_frk_state().m_context.begin()+i ); |
| 1462 | } |
| 1463 | |
| 1464 | else { // clear specific frame |
| 1465 | state::context_data::iterator it = |
| 1466 | std::find_if( first: impl::s_frk_state().m_context.begin(), last: impl::s_frk_state().m_context.end(), pred: frame_with_id( frame_id ) ); |
| 1467 | |
| 1468 | if( it != impl::s_frk_state().m_context.end() ) // really an internal error if this is not true |
| 1469 | impl::s_frk_state().m_context.erase( position: it ); |
| 1470 | } |
| 1471 | } |
| 1472 | |
| 1473 | //____________________________________________________________________________// |
| 1474 | |
| 1475 | // ************************************************************************** // |
| 1476 | // ************** get_context ************** // |
| 1477 | // ************************************************************************** // |
| 1478 | |
| 1479 | context_generator |
| 1480 | get_context() |
| 1481 | { |
| 1482 | return context_generator(); |
| 1483 | } |
| 1484 | |
| 1485 | //____________________________________________________________________________// |
| 1486 | |
| 1487 | // ************************************************************************** // |
| 1488 | // ************** context_generator ************** // |
| 1489 | // ************************************************************************** // |
| 1490 | |
| 1491 | bool |
| 1492 | context_generator::is_empty() const |
| 1493 | { |
| 1494 | return impl::s_frk_state().m_context.empty(); |
| 1495 | } |
| 1496 | |
| 1497 | //____________________________________________________________________________// |
| 1498 | |
| 1499 | const_string |
| 1500 | context_generator::next() const |
| 1501 | { |
| 1502 | return m_curr_frame < impl::s_frk_state().m_context.size() ? impl::s_frk_state().m_context[m_curr_frame++].descr : const_string(); |
| 1503 | } |
| 1504 | |
| 1505 | //____________________________________________________________________________// |
| 1506 | |
| 1507 | // ************************************************************************** // |
| 1508 | // ************** master_test_suite ************** // |
| 1509 | // ************************************************************************** // |
| 1510 | |
| 1511 | master_test_suite_t& |
| 1512 | master_test_suite() |
| 1513 | { |
| 1514 | if( !impl::s_frk_state().m_master_test_suite ) |
| 1515 | impl::s_frk_state().m_master_test_suite = new master_test_suite_t; |
| 1516 | |
| 1517 | return *impl::s_frk_state().m_master_test_suite; |
| 1518 | } |
| 1519 | |
| 1520 | namespace impl { |
| 1521 | |
| 1522 | master_test_suite_name_setter::master_test_suite_name_setter(const_string name) { |
| 1523 | assign_op( target&: master_test_suite().p_name.value, src: name.trim( exclusions: "\"" ), 0 ); |
| 1524 | } |
| 1525 | |
| 1526 | } |
| 1527 | |
| 1528 | //____________________________________________________________________________// |
| 1529 | |
| 1530 | // ************************************************************************** // |
| 1531 | // ************** current_auto_test_suite ************** // |
| 1532 | // ************************************************************************** // |
| 1533 | |
| 1534 | test_suite& |
| 1535 | current_auto_test_suite( test_suite* ts, bool push_or_pop ) |
| 1536 | { |
| 1537 | if( impl::s_frk_state().m_auto_test_suites.empty() ) |
| 1538 | impl::s_frk_state().m_auto_test_suites.push_back( x: &framework::master_test_suite() ); |
| 1539 | |
| 1540 | if( !push_or_pop ) |
| 1541 | impl::s_frk_state().m_auto_test_suites.pop_back(); |
| 1542 | else if( ts ) |
| 1543 | impl::s_frk_state().m_auto_test_suites.push_back( x: ts ); |
| 1544 | |
| 1545 | return *impl::s_frk_state().m_auto_test_suites.back(); |
| 1546 | } |
| 1547 | |
| 1548 | //____________________________________________________________________________// |
| 1549 | |
| 1550 | // ************************************************************************** // |
| 1551 | // ************** current_test_case ************** // |
| 1552 | // ************************************************************************** // |
| 1553 | |
| 1554 | test_case const& |
| 1555 | current_test_case() |
| 1556 | { |
| 1557 | return get<test_case>( id: impl::s_frk_state().m_curr_test_unit ); |
| 1558 | } |
| 1559 | |
| 1560 | |
| 1561 | test_unit const& |
| 1562 | current_test_unit() |
| 1563 | { |
| 1564 | return *impl::s_frk_state().m_test_units[impl::s_frk_state().m_curr_test_unit]; |
| 1565 | } |
| 1566 | |
| 1567 | //____________________________________________________________________________// |
| 1568 | |
| 1569 | test_unit_id |
| 1570 | current_test_case_id() |
| 1571 | { |
| 1572 | return impl::s_frk_state().m_curr_test_unit; |
| 1573 | } |
| 1574 | |
| 1575 | //____________________________________________________________________________// |
| 1576 | |
| 1577 | // ************************************************************************** // |
| 1578 | // ************** framework::get ************** // |
| 1579 | // ************************************************************************** // |
| 1580 | |
| 1581 | test_unit& |
| 1582 | get( test_unit_id id, test_unit_type t ) |
| 1583 | { |
| 1584 | test_unit* res = impl::s_frk_state().m_test_units[id]; |
| 1585 | |
| 1586 | BOOST_TEST_I_ASSRT( (res->p_type & t) != 0, internal_error( "Invalid test unit type" ) ); |
| 1587 | |
| 1588 | return *res; |
| 1589 | } |
| 1590 | |
| 1591 | //____________________________________________________________________________// |
| 1592 | |
| 1593 | // ************************************************************************** // |
| 1594 | // ************** framework::run ************** // |
| 1595 | // ************************************************************************** // |
| 1596 | |
| 1597 | template <class Cont> |
| 1598 | struct swap_on_delete { |
| 1599 | swap_on_delete(Cont& c1, Cont& c2) : m_c1(c1), m_c2(c2){} |
| 1600 | ~swap_on_delete() { |
| 1601 | m_c1.swap(m_c2); |
| 1602 | } |
| 1603 | |
| 1604 | Cont& m_c1; |
| 1605 | Cont& m_c2; |
| 1606 | }; |
| 1607 | |
| 1608 | struct register_observer_helper { |
| 1609 | register_observer_helper(test_observer& observer) |
| 1610 | : m_observer(observer) |
| 1611 | { |
| 1612 | register_obs(); |
| 1613 | } |
| 1614 | |
| 1615 | ~register_observer_helper() { |
| 1616 | if(m_registered) |
| 1617 | deregister_observer( to&: m_observer ); |
| 1618 | } |
| 1619 | |
| 1620 | void deregister_obs() { |
| 1621 | m_registered = false; |
| 1622 | deregister_observer( to&: m_observer ); |
| 1623 | } |
| 1624 | |
| 1625 | void register_obs() { |
| 1626 | m_registered = true; |
| 1627 | register_observer( to&: m_observer ); |
| 1628 | } |
| 1629 | |
| 1630 | |
| 1631 | test_observer& m_observer; |
| 1632 | bool m_registered; |
| 1633 | }; |
| 1634 | |
| 1635 | void |
| 1636 | run( test_unit_id id, bool continue_test ) |
| 1637 | { |
| 1638 | if( id == INV_TEST_UNIT_ID ) |
| 1639 | id = master_test_suite().p_id; |
| 1640 | |
| 1641 | // Figure out run status for execution phase |
| 1642 | impl::s_frk_state().deduce_run_status( master_tu_id: id ); |
| 1643 | |
| 1644 | test_case_counter tcc; |
| 1645 | traverse_test_tree( id, tcc ); |
| 1646 | |
| 1647 | BOOST_TEST_SETUP_ASSERT( tcc.p_count != 0 , runtime_config::get<std::vector<std::string> >( runtime_config::btrt_run_filters ).empty() |
| 1648 | ? BOOST_TEST_L( "test tree is empty" ) |
| 1649 | : BOOST_TEST_L( "no test cases matching filter or all test cases were disabled" ) ); |
| 1650 | |
| 1651 | bool was_in_progress = framework::test_in_progress(); |
| 1652 | bool call_start_finish = !continue_test || !was_in_progress; |
| 1653 | bool init_ok = true; |
| 1654 | const_string setup_error; |
| 1655 | |
| 1656 | framework_init_observer_t local_init_observer; |
| 1657 | register_observer_helper init_observer_helper( local_init_observer ); |
| 1658 | |
| 1659 | if( call_start_finish ) { |
| 1660 | // indicates the framework that no test is in progress now if observers need to be notified |
| 1661 | impl::s_frk_state().m_test_in_progress = false; |
| 1662 | // unit_test::framework_init_observer will get cleared first |
| 1663 | BOOST_TEST_FOREACH( test_observer*, to, impl::s_frk_state().m_observers ) { |
| 1664 | BOOST_TEST_I_TRY { |
| 1665 | ut_detail::test_unit_id_restore restore_current_test_unit(impl::s_frk_state().m_curr_test_unit, id); |
| 1666 | unit_test_monitor_t::error_level result = unit_test_monitor.execute_and_translate( func: boost::bind( f: &test_observer::test_start, a1: to, a2: tcc.p_count, a3: id ) ); |
| 1667 | if( init_ok ) { |
| 1668 | if( result != unit_test_monitor_t::test_ok ) { |
| 1669 | init_ok = false; |
| 1670 | } |
| 1671 | else { |
| 1672 | if( local_init_observer.has_failed() ) { |
| 1673 | init_ok = false; |
| 1674 | } |
| 1675 | } |
| 1676 | } |
| 1677 | } |
| 1678 | BOOST_TEST_I_CATCH( execution_exception, ex ) { |
| 1679 | if( init_ok ) { |
| 1680 | // log only the first error |
| 1681 | init_ok = false; |
| 1682 | setup_error = ex.what(); |
| 1683 | } |
| 1684 | // break; // we should continue otherwise loggers may have improper structure (XML start missing for instance) |
| 1685 | } |
| 1686 | } |
| 1687 | } |
| 1688 | |
| 1689 | // removing this observer as it should not be of any use for the tests |
| 1690 | init_observer_helper.deregister_obs(); |
| 1691 | |
| 1692 | if( init_ok ) { |
| 1693 | |
| 1694 | // attaching the global fixtures to the main entry point |
| 1695 | test_unit& entry_test_unit = framework::get( id, t: TUT_ANY ); |
| 1696 | std::vector<test_unit_fixture_ptr> v_saved_fixture(entry_test_unit.p_fixtures.value.begin(), |
| 1697 | entry_test_unit.p_fixtures.value.end()); |
| 1698 | |
| 1699 | BOOST_TEST_FOREACH( test_unit_fixture*, tuf, impl::s_frk_state().m_global_fixtures ) { |
| 1700 | entry_test_unit.p_fixtures.value.insert( position: entry_test_unit.p_fixtures.value.begin(), |
| 1701 | x: test_unit_fixture_ptr(new impl::global_fixture_handle(tuf)) ); |
| 1702 | } |
| 1703 | |
| 1704 | swap_on_delete< std::vector<test_unit_fixture_ptr> > raii_fixture(v_saved_fixture, entry_test_unit.p_fixtures.value); |
| 1705 | |
| 1706 | // now work in progress |
| 1707 | impl::s_frk_state().m_test_in_progress = true; |
| 1708 | unsigned seed = runtime_config::get<unsigned>( parameter_name: runtime_config::btrt_random_seed ); |
| 1709 | switch( seed ) { |
| 1710 | case 0: |
| 1711 | break; |
| 1712 | case 1: |
| 1713 | seed = static_cast<unsigned>( std::rand() ^ std::time( timer: 0 ) ); // better init using std::rand() ^ ... |
| 1714 | BOOST_FALLTHROUGH; |
| 1715 | default: |
| 1716 | BOOST_TEST_FRAMEWORK_MESSAGE( "Test cases order is shuffled using seed: " << seed ); |
| 1717 | std::srand( seed: seed ); |
| 1718 | } |
| 1719 | |
| 1720 | // executing the test tree |
| 1721 | impl::s_frk_state().execute_test_tree( tu_id: id ); |
| 1722 | |
| 1723 | // removing previously added global fixtures: dtor raii_fixture |
| 1724 | } |
| 1725 | |
| 1726 | impl::s_frk_state().m_test_in_progress = false; |
| 1727 | |
| 1728 | results_reporter::make_report( l: INV_REPORT_LEVEL, id ); |
| 1729 | |
| 1730 | // reinstalling this observer |
| 1731 | init_observer_helper.register_obs(); |
| 1732 | |
| 1733 | local_init_observer.clear(); |
| 1734 | if( call_start_finish ) { |
| 1735 | // indicates the framework that no test is in progress anymore if observers need to be notified |
| 1736 | // and this is a teardown, so assertions should not raise any exception otherwise an exception |
| 1737 | // might be raised in a dtor of a global fixture |
| 1738 | impl::s_frk_state().m_test_in_progress = false; |
| 1739 | BOOST_TEST_REVERSE_FOREACH( test_observer*, to, impl::s_frk_state().m_observers ) { |
| 1740 | ut_detail::test_unit_id_restore restore_current_test_unit(impl::s_frk_state().m_curr_test_unit, id); |
| 1741 | to->test_finish(); |
| 1742 | } |
| 1743 | } |
| 1744 | |
| 1745 | impl::s_frk_state().m_test_in_progress = was_in_progress; |
| 1746 | |
| 1747 | // propagates the init/teardown error if any |
| 1748 | BOOST_TEST_SETUP_ASSERT( init_ok && !local_init_observer.has_failed(), setup_error ); |
| 1749 | } |
| 1750 | |
| 1751 | //____________________________________________________________________________// |
| 1752 | |
| 1753 | void |
| 1754 | run( test_unit const* tu, bool continue_test ) |
| 1755 | { |
| 1756 | run( id: tu->p_id, continue_test ); |
| 1757 | } |
| 1758 | |
| 1759 | //____________________________________________________________________________// |
| 1760 | |
| 1761 | // ************************************************************************** // |
| 1762 | // ************** assertion_result ************** // |
| 1763 | // ************************************************************************** // |
| 1764 | |
| 1765 | void |
| 1766 | assertion_result( unit_test::assertion_result ar ) |
| 1767 | { |
| 1768 | BOOST_TEST_FOREACH( test_observer*, to, impl::s_frk_state().m_observers ) |
| 1769 | to->assertion_result( ar ); |
| 1770 | } |
| 1771 | |
| 1772 | //____________________________________________________________________________// |
| 1773 | |
| 1774 | // ************************************************************************** // |
| 1775 | // ************** exception_caught ************** // |
| 1776 | // ************************************************************************** // |
| 1777 | |
| 1778 | void |
| 1779 | exception_caught( execution_exception const& ex ) |
| 1780 | { |
| 1781 | BOOST_TEST_FOREACH( test_observer*, to, impl::s_frk_state().m_observers ) |
| 1782 | to->exception_caught( ex ); |
| 1783 | } |
| 1784 | |
| 1785 | //____________________________________________________________________________// |
| 1786 | |
| 1787 | // ************************************************************************** // |
| 1788 | // ************** test_unit_aborted ************** // |
| 1789 | // ************************************************************************** // |
| 1790 | |
| 1791 | void |
| 1792 | test_unit_aborted( test_unit const& tu ) |
| 1793 | { |
| 1794 | BOOST_TEST_FOREACH( test_observer*, to, impl::s_frk_state().m_observers ) |
| 1795 | to->test_unit_aborted( tu ); |
| 1796 | } |
| 1797 | |
| 1798 | // ************************************************************************** // |
| 1799 | // ************** test_aborted ************** // |
| 1800 | // ************************************************************************** // |
| 1801 | |
| 1802 | void |
| 1803 | test_aborted( ) |
| 1804 | { |
| 1805 | BOOST_TEST_FOREACH( test_observer*, to, impl::s_frk_state().m_observers ) |
| 1806 | to->test_aborted( ); |
| 1807 | } |
| 1808 | |
| 1809 | |
| 1810 | //____________________________________________________________________________// |
| 1811 | |
| 1812 | } // namespace framework |
| 1813 | } // namespace unit_test |
| 1814 | } // namespace boost |
| 1815 | |
| 1816 | #include <boost/test/detail/enable_warnings.hpp> |
| 1817 | |
| 1818 | #endif // BOOST_TEST_FRAMEWORK_IPP_021005GER |
| 1819 | |