| 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 |
| 9 | /// Provides core implementation for Unit Test Framework. |
| 10 | /// Extensions can be provided in separate files |
| 11 | // *************************************************************************** |
| 12 | |
| 13 | #ifndef BOOST_TEST_UNIT_TEST_SUITE_IPP_012205GER |
| 14 | #define BOOST_TEST_UNIT_TEST_SUITE_IPP_012205GER |
| 15 | |
| 16 | // Boost.Test |
| 17 | #include <boost/detail/workaround.hpp> |
| 18 | |
| 19 | #include <boost/test/framework.hpp> |
| 20 | #include <boost/test/results_collector.hpp> |
| 21 | |
| 22 | #include <boost/test/tree/test_unit.hpp> |
| 23 | #include <boost/test/tree/visitor.hpp> |
| 24 | #include <boost/test/tree/traverse.hpp> |
| 25 | #include <boost/test/tree/auto_registration.hpp> |
| 26 | #include <boost/test/tree/global_fixture.hpp> |
| 27 | |
| 28 | #include <boost/test/utils/foreach.hpp> |
| 29 | #include <boost/test/utils/basic_cstring/io.hpp> |
| 30 | |
| 31 | #include <boost/test/unit_test_parameters.hpp> |
| 32 | |
| 33 | // STL |
| 34 | #include <algorithm> |
| 35 | #include <vector> |
| 36 | #include <set> |
| 37 | |
| 38 | #include <boost/test/detail/suppress_warnings.hpp> |
| 39 | |
| 40 | //____________________________________________________________________________// |
| 41 | |
| 42 | namespace boost { |
| 43 | namespace unit_test { |
| 44 | |
| 45 | // ************************************************************************** // |
| 46 | // ************** test_unit ************** // |
| 47 | // ************************************************************************** // |
| 48 | |
| 49 | test_unit::test_unit( const_string name, const_string file_name, std::size_t line_num, test_unit_type t ) |
| 50 | : p_type( t ) |
| 51 | , p_type_name( t == TUT_CASE ? "case" : "suite" ) |
| 52 | , p_file_name( file_name ) |
| 53 | , p_line_num( line_num ) |
| 54 | , p_id( INV_TEST_UNIT_ID ) |
| 55 | , p_parent_id( INV_TEST_UNIT_ID ) |
| 56 | , p_name( std::string( name.begin(), name.size() ) ) |
| 57 | , p_timeout( 0 ) |
| 58 | , p_expected_failures( 0 ) |
| 59 | , p_default_status( RS_INHERIT ) |
| 60 | , p_run_status( RS_INVALID ) |
| 61 | , p_sibling_rank(0) |
| 62 | { |
| 63 | } |
| 64 | |
| 65 | //____________________________________________________________________________// |
| 66 | |
| 67 | test_unit::test_unit( const_string module_name ) |
| 68 | : p_type( TUT_SUITE ) |
| 69 | , p_type_name( "module" ) |
| 70 | , p_line_num( 0 ) |
| 71 | , p_id( INV_TEST_UNIT_ID ) |
| 72 | , p_parent_id( INV_TEST_UNIT_ID ) |
| 73 | , p_name( std::string( module_name.begin(), module_name.size() ) ) |
| 74 | , p_timeout( 0 ) |
| 75 | , p_expected_failures( 0 ) |
| 76 | , p_default_status( RS_INHERIT ) |
| 77 | , p_run_status( RS_INVALID ) |
| 78 | , p_sibling_rank(0) |
| 79 | { |
| 80 | } |
| 81 | |
| 82 | //____________________________________________________________________________// |
| 83 | |
| 84 | test_unit::~test_unit() |
| 85 | { |
| 86 | framework::deregister_test_unit( tu: this ); |
| 87 | } |
| 88 | |
| 89 | //____________________________________________________________________________// |
| 90 | |
| 91 | void |
| 92 | test_unit::depends_on( test_unit* tu ) |
| 93 | { |
| 94 | BOOST_TEST_SETUP_ASSERT( p_id != framework::master_test_suite().p_id, |
| 95 | "Can't add dependency to the master test suite" ); |
| 96 | |
| 97 | p_dependencies.value.push_back( x: tu->p_id ); |
| 98 | } |
| 99 | |
| 100 | //____________________________________________________________________________// |
| 101 | |
| 102 | void |
| 103 | test_unit::add_precondition( precondition_t const& pc ) |
| 104 | { |
| 105 | p_preconditions.value.push_back( x: pc ); |
| 106 | } |
| 107 | |
| 108 | //____________________________________________________________________________// |
| 109 | |
| 110 | test_tools::assertion_result |
| 111 | test_unit::check_preconditions() const |
| 112 | { |
| 113 | BOOST_TEST_FOREACH( test_unit_id, dep_id, p_dependencies.get() ) { |
| 114 | test_unit const& dep = framework::get( id: dep_id, t: TUT_ANY ); |
| 115 | |
| 116 | if( !dep.is_enabled() ) { |
| 117 | test_tools::assertion_result res(false); |
| 118 | res.message() << "dependency test " << dep.p_type_name << " \"" << dep.full_name() << "\" is disabled" ; |
| 119 | return res; |
| 120 | } |
| 121 | |
| 122 | test_results const& test_rslt = unit_test::results_collector.results( id: dep_id ); |
| 123 | if( !test_rslt.passed() ) { |
| 124 | test_tools::assertion_result res(false); |
| 125 | res.message() << "dependency test " << dep.p_type_name << " \"" << dep.full_name() << (test_rslt.skipped() ? "\" was skipped" :"\" has failed" ); |
| 126 | return res; |
| 127 | } |
| 128 | |
| 129 | if( test_rslt.p_test_cases_skipped > 0 ) { |
| 130 | test_tools::assertion_result res(false); |
| 131 | res.message() << "dependency test " << dep.p_type_name << " \"" << dep.full_name() << "\" has skipped test cases" ; |
| 132 | return res; |
| 133 | } |
| 134 | } |
| 135 | |
| 136 | BOOST_TEST_FOREACH( precondition_t, precondition, p_preconditions.get() ) { |
| 137 | test_tools::assertion_result res = precondition( p_id ); |
| 138 | if( !res ) { |
| 139 | test_tools::assertion_result res_out(false); |
| 140 | res_out.message() << "precondition failed" ; |
| 141 | if( !res.has_empty_message() ) |
| 142 | res_out.message() << ": " << res.message(); |
| 143 | return res_out; |
| 144 | } |
| 145 | } |
| 146 | |
| 147 | return true; |
| 148 | } |
| 149 | |
| 150 | //____________________________________________________________________________// |
| 151 | |
| 152 | void |
| 153 | test_unit::increase_exp_fail( counter_t num ) |
| 154 | { |
| 155 | p_expected_failures.value += num; |
| 156 | |
| 157 | if( p_parent_id != INV_TEST_UNIT_ID ) |
| 158 | framework::get<test_suite>( id: p_parent_id ).increase_exp_fail( num ); |
| 159 | } |
| 160 | |
| 161 | //____________________________________________________________________________// |
| 162 | |
| 163 | std::string |
| 164 | test_unit::full_name() const |
| 165 | { |
| 166 | if( p_parent_id == INV_TEST_UNIT_ID || p_parent_id == framework::master_test_suite().p_id ) |
| 167 | return p_name; |
| 168 | |
| 169 | std::string res = framework::get<test_suite>( id: p_parent_id ).full_name(); |
| 170 | res.append(s: "/" ); |
| 171 | |
| 172 | res.append( str: p_name ); |
| 173 | |
| 174 | return res; |
| 175 | } |
| 176 | |
| 177 | //____________________________________________________________________________// |
| 178 | |
| 179 | void |
| 180 | test_unit::add_label( const_string l ) |
| 181 | { |
| 182 | p_labels.value.push_back( x: std::string() + l ); |
| 183 | } |
| 184 | |
| 185 | //____________________________________________________________________________// |
| 186 | |
| 187 | bool |
| 188 | test_unit::has_label( const_string l ) const |
| 189 | { |
| 190 | return std::find( first: p_labels->begin(), last: p_labels->end(), val: l ) != p_labels->end(); |
| 191 | } |
| 192 | |
| 193 | //____________________________________________________________________________// |
| 194 | |
| 195 | // ************************************************************************** // |
| 196 | // ************** test_case ************** // |
| 197 | // ************************************************************************** // |
| 198 | |
| 199 | test_case::test_case( const_string name, boost::function<void ()> const& test_func ) |
| 200 | : test_unit( name, "" , 0, static_cast<test_unit_type>(type) ) |
| 201 | , p_test_func( test_func ) |
| 202 | { |
| 203 | framework::register_test_unit( tc: this ); |
| 204 | } |
| 205 | |
| 206 | //____________________________________________________________________________// |
| 207 | |
| 208 | test_case::test_case( const_string name, const_string file_name, std::size_t line_num, boost::function<void ()> const& test_func ) |
| 209 | : test_unit( name, file_name, line_num, static_cast<test_unit_type>(type) ) |
| 210 | , p_test_func( test_func ) |
| 211 | { |
| 212 | framework::register_test_unit( tc: this ); |
| 213 | } |
| 214 | |
| 215 | //____________________________________________________________________________// |
| 216 | |
| 217 | // ************************************************************************** // |
| 218 | // ************** test_suite ************** // |
| 219 | // ************************************************************************** // |
| 220 | |
| 221 | //____________________________________________________________________________// |
| 222 | |
| 223 | test_suite::test_suite( const_string name, const_string file_name, std::size_t line_num ) |
| 224 | : test_unit( ut_detail::normalize_test_case_name( tu_name: name ), file_name, line_num, static_cast<test_unit_type>(type) ) |
| 225 | { |
| 226 | framework::register_test_unit( ts: this ); |
| 227 | } |
| 228 | |
| 229 | //____________________________________________________________________________// |
| 230 | |
| 231 | test_suite::test_suite( const_string module_name ) |
| 232 | : test_unit( module_name ) |
| 233 | { |
| 234 | framework::register_test_unit( ts: this ); |
| 235 | } |
| 236 | |
| 237 | //____________________________________________________________________________// |
| 238 | |
| 239 | void |
| 240 | test_suite::add( test_unit* tu, counter_t expected_failures, unsigned timeout ) |
| 241 | { |
| 242 | tu->p_timeout.value = timeout; |
| 243 | |
| 244 | m_children.push_back( x: tu->p_id ); |
| 245 | tu->p_parent_id.value = p_id; |
| 246 | |
| 247 | if( tu->p_expected_failures != 0 ) |
| 248 | increase_exp_fail( num: tu->p_expected_failures ); |
| 249 | |
| 250 | if( expected_failures ) |
| 251 | tu->increase_exp_fail( num: expected_failures ); |
| 252 | } |
| 253 | |
| 254 | //____________________________________________________________________________// |
| 255 | |
| 256 | void |
| 257 | test_suite::add( test_unit_generator const& gen, unsigned timeout ) |
| 258 | { |
| 259 | test_unit* tu; |
| 260 | while((tu = gen.next()) != 0) |
| 261 | add( tu, expected_failures: 0, timeout ); |
| 262 | } |
| 263 | |
| 264 | //____________________________________________________________________________// |
| 265 | |
| 266 | void |
| 267 | test_suite::add( test_unit_generator const& gen, decorator::collector_t& decorators ) |
| 268 | { |
| 269 | test_unit* tu; |
| 270 | while((tu = gen.next()) != 0) { |
| 271 | decorators.store_in( tu&: *tu ); |
| 272 | add( tu, expected_failures: 0 ); |
| 273 | } |
| 274 | decorators.reset(); |
| 275 | } |
| 276 | |
| 277 | //____________________________________________________________________________// |
| 278 | |
| 279 | void |
| 280 | test_suite::add( boost::shared_ptr<test_unit_generator> gen_ptr, decorator::collector_t& decorators ) |
| 281 | { |
| 282 | std::pair<boost::shared_ptr<test_unit_generator>, std::vector<decorator::base_ptr> > tmp_p(gen_ptr, decorators.get_lazy_decorators() ); |
| 283 | m_generators.push_back(x: tmp_p); |
| 284 | decorators.reset(); |
| 285 | } |
| 286 | |
| 287 | //____________________________________________________________________________// |
| 288 | |
| 289 | void |
| 290 | test_suite::generate( ) |
| 291 | { |
| 292 | typedef std::pair<boost::shared_ptr<test_unit_generator>, std::vector<decorator::base_ptr> > element_t; |
| 293 | |
| 294 | for(std::vector<element_t>::iterator it(m_generators.begin()), ite(m_generators.end()); |
| 295 | it < ite; |
| 296 | ++it) |
| 297 | { |
| 298 | test_unit* tu; |
| 299 | while((tu = it->first->next()) != 0) { |
| 300 | tu->p_decorators.value.insert( position: tu->p_decorators.value.end(), first: it->second.begin(), last: it->second.end() ); |
| 301 | //it->second.store_in( *tu ); |
| 302 | add( tu, expected_failures: 0 ); |
| 303 | } |
| 304 | |
| 305 | } |
| 306 | m_generators.clear(); |
| 307 | |
| 308 | #if 0 |
| 309 | test_unit* tu; |
| 310 | while((tu = gen.next()) != 0) { |
| 311 | decorators.store_in( *tu ); |
| 312 | add( tu, 0 ); |
| 313 | } |
| 314 | #endif |
| 315 | } |
| 316 | |
| 317 | //____________________________________________________________________________// |
| 318 | |
| 319 | void |
| 320 | test_suite::check_for_duplicate_test_cases() { |
| 321 | // check for clashing names #12597 |
| 322 | std::set<std::string> names; |
| 323 | for( test_unit_id_list::const_iterator it(m_children.begin()), ite(m_children.end()); |
| 324 | it < ite; |
| 325 | ++it) { |
| 326 | std::string name = framework::get(id: *it, t: TUT_ANY).p_name; |
| 327 | std::pair<std::set<std::string>::iterator, bool> ret = names.insert(x: name); |
| 328 | BOOST_TEST_SETUP_ASSERT(ret.second, |
| 329 | "test unit with name '" |
| 330 | + name |
| 331 | + std::string("' registered multiple times in the test suite '" ) |
| 332 | + this->p_name.value |
| 333 | + "'" ); |
| 334 | } |
| 335 | |
| 336 | return; |
| 337 | } |
| 338 | |
| 339 | //____________________________________________________________________________// |
| 340 | |
| 341 | void |
| 342 | test_suite::remove( test_unit_id id ) |
| 343 | { |
| 344 | test_unit_id_list::iterator it = std::find( first: m_children.begin(), last: m_children.end(), val: id ); |
| 345 | |
| 346 | if( it != m_children.end() ) |
| 347 | m_children.erase( position: it ); |
| 348 | } |
| 349 | |
| 350 | //____________________________________________________________________________// |
| 351 | |
| 352 | test_unit_id |
| 353 | test_suite::get( const_string tu_name ) const |
| 354 | { |
| 355 | BOOST_TEST_FOREACH( test_unit_id, id, m_children ) { |
| 356 | if( tu_name == framework::get( id, t: ut_detail::test_id_2_unit_type( id ) ).p_name.get() ) |
| 357 | return id; |
| 358 | } |
| 359 | |
| 360 | return INV_TEST_UNIT_ID; |
| 361 | } |
| 362 | |
| 363 | //____________________________________________________________________________// |
| 364 | |
| 365 | // ************************************************************************** // |
| 366 | // ************** master_test_suite ************** // |
| 367 | // ************************************************************************** // |
| 368 | |
| 369 | master_test_suite_t::master_test_suite_t() |
| 370 | : test_suite( "Master Test Suite" ) |
| 371 | , argc( 0 ) |
| 372 | , argv( 0 ) |
| 373 | { |
| 374 | p_default_status.value = RS_ENABLED; |
| 375 | } |
| 376 | |
| 377 | // ************************************************************************** // |
| 378 | // ************** traverse_test_tree ************** // |
| 379 | // ************************************************************************** // |
| 380 | |
| 381 | void |
| 382 | traverse_test_tree( test_case const& tc, test_tree_visitor& V, bool ignore_status ) |
| 383 | { |
| 384 | if( tc.is_enabled() || ignore_status ) |
| 385 | V.visit( tc ); |
| 386 | } |
| 387 | |
| 388 | //____________________________________________________________________________// |
| 389 | |
| 390 | void |
| 391 | traverse_test_tree( test_suite const& suite, test_tree_visitor& V, bool ignore_status ) |
| 392 | { |
| 393 | // skip disabled test suite unless we asked to ignore this condition |
| 394 | if( !ignore_status && !suite.is_enabled() ) |
| 395 | return; |
| 396 | |
| 397 | // Invoke test_suite_start callback |
| 398 | if( !V.test_suite_start( ts: suite ) ) |
| 399 | return; |
| 400 | |
| 401 | // Recurse into children |
| 402 | std::size_t total_children = suite.m_children.size(); |
| 403 | for( std::size_t i=0; i < total_children; ) { |
| 404 | // this statement can remove the test unit from this list |
| 405 | traverse_test_tree( suite.m_children[i], V, ignore_status ); |
| 406 | if( total_children > suite.m_children.size() ) |
| 407 | total_children = suite.m_children.size(); |
| 408 | else |
| 409 | ++i; |
| 410 | } |
| 411 | |
| 412 | // Invoke test_suite_finish callback |
| 413 | V.test_suite_finish( suite ); |
| 414 | } |
| 415 | |
| 416 | //____________________________________________________________________________// |
| 417 | |
| 418 | void |
| 419 | traverse_test_tree( test_unit_id id, test_tree_visitor& V, bool ignore_status ) |
| 420 | { |
| 421 | if( ut_detail::test_id_2_unit_type( id ) == TUT_CASE ) |
| 422 | traverse_test_tree( tc: framework::get<test_case>( id ), V, ignore_status ); |
| 423 | else |
| 424 | traverse_test_tree( suite: framework::get<test_suite>( id ), V, ignore_status ); |
| 425 | } |
| 426 | |
| 427 | //____________________________________________________________________________// |
| 428 | |
| 429 | // ************************************************************************** // |
| 430 | // ************** object generators ************** // |
| 431 | // ************************************************************************** // |
| 432 | |
| 433 | namespace ut_detail { |
| 434 | |
| 435 | std::string |
| 436 | normalize_test_case_name( const_string name ) |
| 437 | { |
| 438 | std::string norm_name( name.begin(), name.size() ); |
| 439 | |
| 440 | if( name[0] == '&' ) |
| 441 | norm_name = norm_name.substr( pos: 1 ); |
| 442 | |
| 443 | // trim spaces |
| 444 | std::size_t first_not_space = norm_name.find_first_not_of(c: ' '); |
| 445 | if( first_not_space ) { |
| 446 | norm_name.erase(pos: 0, n: first_not_space); |
| 447 | } |
| 448 | |
| 449 | std::size_t last_not_space = norm_name.find_last_not_of(c: ' '); |
| 450 | if( last_not_space !=std::string::npos ) { |
| 451 | norm_name.erase(pos: last_not_space + 1); |
| 452 | } |
| 453 | |
| 454 | // sanitize all chars that might be used in runtime filters |
| 455 | static const char to_replace[] = { ':', '*', '@', '+', '!', '/', ',' }; |
| 456 | for(std::size_t index = 0; |
| 457 | index < sizeof(to_replace)/sizeof(to_replace[0]); |
| 458 | index++) { |
| 459 | std::replace(first: norm_name.begin(), last: norm_name.end(), old_value: to_replace[index], new_value: '_'); |
| 460 | } |
| 461 | |
| 462 | return norm_name; |
| 463 | } |
| 464 | |
| 465 | //____________________________________________________________________________// |
| 466 | |
| 467 | // ************************************************************************** // |
| 468 | // ************** auto_test_unit_registrar ************** // |
| 469 | // ************************************************************************** // |
| 470 | |
| 471 | auto_test_unit_registrar::auto_test_unit_registrar( test_case* tc, decorator::collector_t& decorators, counter_t exp_fail ) |
| 472 | { |
| 473 | framework::current_auto_test_suite().add( tu: tc, expected_failures: exp_fail ); |
| 474 | |
| 475 | decorators.store_in( tu&: *tc ); |
| 476 | decorators.reset(); |
| 477 | } |
| 478 | |
| 479 | //____________________________________________________________________________// |
| 480 | |
| 481 | auto_test_unit_registrar::auto_test_unit_registrar( const_string ts_name, const_string ts_file, std::size_t ts_line, decorator::collector_t& decorators ) |
| 482 | { |
| 483 | test_unit_id id = framework::current_auto_test_suite().get( tu_name: ts_name ); |
| 484 | |
| 485 | test_suite* ts; |
| 486 | |
| 487 | if( id != INV_TEST_UNIT_ID ) { |
| 488 | ts = &framework::get<test_suite>( id ); |
| 489 | BOOST_ASSERT( ts->p_parent_id == framework::current_auto_test_suite().p_id ); |
| 490 | } |
| 491 | else { |
| 492 | ts = new test_suite( ts_name, ts_file, ts_line ); |
| 493 | framework::current_auto_test_suite().add( tu: ts ); |
| 494 | } |
| 495 | |
| 496 | decorators.store_in( tu&: *ts ); |
| 497 | decorators.reset(); |
| 498 | |
| 499 | framework::current_auto_test_suite( ts ); |
| 500 | } |
| 501 | |
| 502 | //____________________________________________________________________________// |
| 503 | |
| 504 | auto_test_unit_registrar::auto_test_unit_registrar( test_unit_generator const& tc_gen, decorator::collector_t& decorators ) |
| 505 | { |
| 506 | framework::current_auto_test_suite().add( gen: tc_gen, decorators ); |
| 507 | } |
| 508 | |
| 509 | //____________________________________________________________________________// |
| 510 | |
| 511 | auto_test_unit_registrar::auto_test_unit_registrar( boost::shared_ptr<test_unit_generator> tc_gen, decorator::collector_t& decorators ) |
| 512 | { |
| 513 | framework::current_auto_test_suite().add( gen_ptr: tc_gen, decorators ); |
| 514 | } |
| 515 | |
| 516 | |
| 517 | //____________________________________________________________________________// |
| 518 | |
| 519 | auto_test_unit_registrar::auto_test_unit_registrar( int ) |
| 520 | { |
| 521 | framework::current_auto_test_suite( ts: 0, push_or_pop: false ); |
| 522 | } |
| 523 | |
| 524 | //____________________________________________________________________________// |
| 525 | |
| 526 | } // namespace ut_detail |
| 527 | |
| 528 | // ************************************************************************** // |
| 529 | // ************** global_fixture ************** // |
| 530 | // ************************************************************************** // |
| 531 | |
| 532 | global_fixture::global_fixture(): registered(false) |
| 533 | { |
| 534 | framework::register_global_fixture( tuf&: *this ); |
| 535 | registered = true; |
| 536 | } |
| 537 | |
| 538 | void global_fixture::unregister_from_framework() { |
| 539 | // not accessing the framework singleton after deregistering -> release |
| 540 | // of the observer from the framework |
| 541 | if(registered) { |
| 542 | framework::deregister_global_fixture( tuf&: *this ); |
| 543 | } |
| 544 | registered = false; |
| 545 | } |
| 546 | |
| 547 | global_fixture::~global_fixture() |
| 548 | { |
| 549 | this->unregister_from_framework(); |
| 550 | } |
| 551 | |
| 552 | // ************************************************************************** // |
| 553 | // ************** global_configuration ************** // |
| 554 | // ************************************************************************** // |
| 555 | |
| 556 | global_configuration::global_configuration(): registered(false) |
| 557 | { |
| 558 | framework::register_observer( to&: *this ); |
| 559 | registered = true; |
| 560 | } |
| 561 | |
| 562 | void global_configuration::unregister_from_framework() |
| 563 | { |
| 564 | // not accessing the framework singleton after deregistering -> release |
| 565 | // of the observer from the framework |
| 566 | if(registered) { |
| 567 | framework::deregister_observer( to&: *this ); |
| 568 | } |
| 569 | registered = false; |
| 570 | } |
| 571 | |
| 572 | global_configuration::~global_configuration() |
| 573 | { |
| 574 | this->unregister_from_framework(); |
| 575 | } |
| 576 | |
| 577 | //____________________________________________________________________________// |
| 578 | |
| 579 | } // namespace unit_test |
| 580 | } // namespace boost |
| 581 | |
| 582 | #include <boost/test/detail/enable_warnings.hpp> |
| 583 | |
| 584 | #endif // BOOST_TEST_UNIT_TEST_SUITE_IPP_012205GER |
| 585 | |