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