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 : supplies offline implementation for the Test Tools |
13 | // *************************************************************************** |
14 | |
15 | #ifndef BOOST_TEST_TEST_TOOLS_IPP_012205GER |
16 | #define BOOST_TEST_TEST_TOOLS_IPP_012205GER |
17 | |
18 | // Boost.Test |
19 | #include <boost/test/unit_test_log.hpp> |
20 | #include <boost/test/tools/context.hpp> |
21 | #include <boost/test/tools/output_test_stream.hpp> |
22 | |
23 | #include <boost/test/tools/detail/fwd.hpp> |
24 | #include <boost/test/tools/detail/print_helper.hpp> |
25 | |
26 | #include <boost/test/framework.hpp> |
27 | #include <boost/test/tree/test_unit.hpp> |
28 | #include <boost/test/execution_monitor.hpp> // execution_aborted |
29 | |
30 | #include <boost/test/detail/throw_exception.hpp> |
31 | |
32 | // Boost |
33 | #include <boost/config.hpp> |
34 | |
35 | // STL |
36 | #include <fstream> |
37 | #include <string> |
38 | #include <cstring> |
39 | #include <cctype> |
40 | #include <cwchar> |
41 | #include <stdexcept> |
42 | #include <vector> |
43 | #include <utility> |
44 | #include <ios> |
45 | |
46 | // !! should we use #include <cstdarg> |
47 | #include <stdarg.h> |
48 | |
49 | #include <boost/test/detail/suppress_warnings.hpp> |
50 | |
51 | //____________________________________________________________________________// |
52 | |
53 | # ifdef BOOST_NO_STDC_NAMESPACE |
54 | namespace std { using ::strcmp; using ::strlen; using ::isprint; } |
55 | #if !defined( BOOST_NO_CWCHAR ) |
56 | namespace std { using ::wcscmp; } |
57 | #endif |
58 | # endif |
59 | |
60 | namespace boost { |
61 | namespace test_tools { |
62 | namespace tt_detail { |
63 | |
64 | // ************************************************************************** // |
65 | // ************** print_log_value ************** // |
66 | // ************************************************************************** // |
67 | |
68 | void |
69 | print_log_value<char>::operator()( std::ostream& ostr, char t ) |
70 | { |
71 | if( (std::isprint)( static_cast<unsigned char>(t) ) ) |
72 | ostr << '\'' << t << '\''; |
73 | else |
74 | ostr << std::hex |
75 | #if BOOST_TEST_USE_STD_LOCALE |
76 | << std::showbase |
77 | #else |
78 | << "0x" |
79 | #endif |
80 | << static_cast<int>(t); |
81 | } |
82 | |
83 | //____________________________________________________________________________// |
84 | |
85 | void |
86 | print_log_value<unsigned char>::operator()( std::ostream& ostr, unsigned char t ) |
87 | { |
88 | ostr << std::hex |
89 | // showbase is only available for new style streams: |
90 | #if BOOST_TEST_USE_STD_LOCALE |
91 | << std::showbase |
92 | #else |
93 | << "0x" |
94 | #endif |
95 | << static_cast<int>(t); |
96 | } |
97 | |
98 | //____________________________________________________________________________// |
99 | |
100 | void |
101 | print_log_value<char const*>::operator()( std::ostream& ostr, char const* t ) |
102 | { |
103 | ostr << ( t ? t : "null string" ); |
104 | } |
105 | |
106 | //____________________________________________________________________________// |
107 | |
108 | void |
109 | print_log_value<wchar_t const*>::operator()( std::ostream& ostr, wchar_t const* t ) |
110 | { |
111 | ostr << ( t ? t : L"null string" ); |
112 | } |
113 | |
114 | //____________________________________________________________________________// |
115 | |
116 | // ************************************************************************** // |
117 | // ************** TOOL BOX Implementation ************** // |
118 | // ************************************************************************** // |
119 | |
120 | using ::boost::unit_test::lazy_ostream; |
121 | |
122 | static char const* check_str [] = { " == " , " != " , " < " , " <= " , " > " , " >= " }; |
123 | static char const* rever_str [] = { " != " , " == " , " >= " , " > " , " <= " , " < " }; |
124 | |
125 | template<typename OutStream> |
126 | void |
127 | format_report( OutStream& os, assertion_result const& pr, unit_test::lazy_ostream const& assertion_descr, |
128 | tool_level tl, check_type ct, |
129 | std::size_t num_args, va_list args, |
130 | char const* prefix, char const* suffix ) |
131 | { |
132 | using namespace unit_test; |
133 | |
134 | switch( ct ) { |
135 | case CHECK_PRED: |
136 | os << prefix << assertion_descr << suffix; |
137 | |
138 | if( !pr.has_empty_message() ) |
139 | os << ". " << pr.message(); |
140 | break; |
141 | |
142 | case CHECK_BUILT_ASSERTION: { |
143 | os << prefix << assertion_descr << suffix; |
144 | |
145 | if( tl != PASS ) { |
146 | const_string details_message = pr.message(); |
147 | |
148 | if( !details_message.is_empty() ) { |
149 | os << details_message; |
150 | } |
151 | } |
152 | break; |
153 | } |
154 | |
155 | case CHECK_MSG: |
156 | if( tl == PASS ) |
157 | os << prefix << "'" << assertion_descr << "'" << suffix; |
158 | else |
159 | os << assertion_descr; |
160 | |
161 | if( !pr.has_empty_message() ) |
162 | os << ". " << pr.message(); |
163 | break; |
164 | |
165 | case CHECK_EQUAL: |
166 | case CHECK_NE: |
167 | case CHECK_LT: |
168 | case CHECK_LE: |
169 | case CHECK_GT: |
170 | case CHECK_GE: { |
171 | char const* arg1_descr = va_arg( args, char const* ); |
172 | lazy_ostream const* arg1_val = va_arg( args, lazy_ostream const* ); |
173 | char const* arg2_descr = va_arg( args, char const* ); |
174 | lazy_ostream const* arg2_val = va_arg( args, lazy_ostream const* ); |
175 | |
176 | os << prefix << arg1_descr << check_str[ct-CHECK_EQUAL] << arg2_descr << suffix; |
177 | |
178 | if( tl != PASS ) |
179 | os << " [" << *arg1_val << rever_str[ct-CHECK_EQUAL] << *arg2_val << "]" ; |
180 | |
181 | if( !pr.has_empty_message() ) |
182 | os << ". " << pr.message(); |
183 | break; |
184 | } |
185 | |
186 | case CHECK_CLOSE: |
187 | case CHECK_CLOSE_FRACTION: { |
188 | char const* arg1_descr = va_arg( args, char const* ); |
189 | lazy_ostream const* arg1_val = va_arg( args, lazy_ostream const* ); |
190 | char const* arg2_descr = va_arg( args, char const* ); |
191 | lazy_ostream const* arg2_val = va_arg( args, lazy_ostream const* ); |
192 | /* toler_descr = */ va_arg( args, char const* ); |
193 | lazy_ostream const* toler_val = va_arg( args, lazy_ostream const* ); |
194 | |
195 | os << "difference{" << pr.message() |
196 | << "} between " << arg1_descr << "{" << *arg1_val |
197 | << "} and " << arg2_descr << "{" << *arg2_val |
198 | << ( tl == PASS ? "} doesn't exceed " : "} exceeds " ) |
199 | << *toler_val; |
200 | if( ct == CHECK_CLOSE ) |
201 | os << "%" ; |
202 | break; |
203 | } |
204 | case CHECK_SMALL: { |
205 | char const* arg1_descr = va_arg( args, char const* ); |
206 | lazy_ostream const* arg1_val = va_arg( args, lazy_ostream const* ); |
207 | /* toler_descr = */ va_arg( args, char const* ); |
208 | lazy_ostream const* toler_val = va_arg( args, lazy_ostream const* ); |
209 | |
210 | os << "absolute value of " << arg1_descr << "{" << *arg1_val << "}" |
211 | << ( tl == PASS ? " doesn't exceed " : " exceeds " ) |
212 | << *toler_val; |
213 | |
214 | if( !pr.has_empty_message() ) |
215 | os << ". " << pr.message(); |
216 | break; |
217 | } |
218 | |
219 | case CHECK_PRED_WITH_ARGS: { |
220 | std::vector< std::pair<char const*, lazy_ostream const*> > args_copy; |
221 | args_copy.reserve( n: num_args ); |
222 | for( std::size_t i = 0; i < num_args; ++i ) { |
223 | char const* desc = va_arg( args, char const* ); |
224 | lazy_ostream const* value = va_arg( args, lazy_ostream const* ); |
225 | args_copy.push_back( x: std::make_pair( x&: desc, y&: value ) ); |
226 | } |
227 | |
228 | os << prefix << assertion_descr; |
229 | |
230 | // print predicate call description |
231 | os << "( " ; |
232 | for( std::size_t i = 0; i < num_args; ++i ) { |
233 | os << args_copy[i].first; |
234 | |
235 | if( i != num_args-1 ) |
236 | os << ", " ; |
237 | } |
238 | os << " )" << suffix; |
239 | |
240 | if( tl != PASS ) { |
241 | os << " for ( " ; |
242 | for( std::size_t i = 0; i < num_args; ++i ) { |
243 | os << *args_copy[i].second; |
244 | |
245 | if( i != num_args-1 ) |
246 | os << ", " ; |
247 | } |
248 | os << " )" ; |
249 | } |
250 | |
251 | if( !pr.has_empty_message() ) |
252 | os << ". " << pr.message(); |
253 | break; |
254 | } |
255 | |
256 | case CHECK_EQUAL_COLL: { |
257 | char const* left_begin_descr = va_arg( args, char const* ); |
258 | char const* left_end_descr = va_arg( args, char const* ); |
259 | char const* right_begin_descr = va_arg( args, char const* ); |
260 | char const* right_end_descr = va_arg( args, char const* ); |
261 | |
262 | os << prefix << "{ " << left_begin_descr << ", " << left_end_descr << " } == { " |
263 | << right_begin_descr << ", " << right_end_descr << " }" |
264 | << suffix; |
265 | |
266 | if( !pr.has_empty_message() ) |
267 | os << ". " << pr.message(); |
268 | break; |
269 | } |
270 | |
271 | case CHECK_BITWISE_EQUAL: { |
272 | char const* left_descr = va_arg( args, char const* ); |
273 | char const* right_descr = va_arg( args, char const* ); |
274 | |
275 | os << prefix << left_descr << " =.= " << right_descr << suffix; |
276 | |
277 | if( !pr.has_empty_message() ) |
278 | os << ". " << pr.message(); |
279 | break; |
280 | } |
281 | } |
282 | } |
283 | |
284 | //____________________________________________________________________________// |
285 | |
286 | bool |
287 | report_assertion( assertion_result const& ar, |
288 | lazy_ostream const& assertion_descr, |
289 | const_string file_name, |
290 | std::size_t line_num, |
291 | tool_level tl, |
292 | check_type ct, |
293 | std::size_t num_args, ... ) |
294 | { |
295 | using namespace unit_test; |
296 | |
297 | BOOST_TEST_I_ASSRT( framework::current_test_case_id() != INV_TEST_UNIT_ID, |
298 | std::runtime_error( "Can't use testing tools outside of test case implementation." ) ); |
299 | |
300 | if( !!ar ) |
301 | tl = PASS; |
302 | |
303 | log_level ll; |
304 | char const* prefix; |
305 | char const* suffix; |
306 | |
307 | switch( tl ) { |
308 | case PASS: |
309 | ll = log_successful_tests; |
310 | prefix = "check " ; |
311 | suffix = " has passed" ; |
312 | break; |
313 | case WARN: |
314 | ll = log_warnings; |
315 | prefix = "condition " ; |
316 | suffix = " is not satisfied" ; |
317 | break; |
318 | case CHECK: |
319 | ll = log_all_errors; |
320 | prefix = "check " ; |
321 | suffix = " has failed" ; |
322 | break; |
323 | case REQUIRE: |
324 | ll = log_fatal_errors; |
325 | prefix = "critical check " ; |
326 | suffix = " has failed" ; |
327 | break; |
328 | default: |
329 | return true; |
330 | } |
331 | |
332 | unit_test_log << unit_test::log::begin( file_name, line_num ) << ll; |
333 | va_list args; |
334 | va_start( args, num_args ); |
335 | |
336 | format_report( os&: unit_test_log, pr: ar, assertion_descr, tl, ct, num_args, args, prefix, suffix ); |
337 | |
338 | va_end( args ); |
339 | unit_test_log << unit_test::log::end(); |
340 | |
341 | switch( tl ) { |
342 | case PASS: |
343 | framework::assertion_result( ar: AR_PASSED ); |
344 | return true; |
345 | |
346 | case WARN: |
347 | framework::assertion_result( ar: AR_TRIGGERED ); |
348 | return false; |
349 | |
350 | case CHECK: |
351 | framework::assertion_result( ar: AR_FAILED ); |
352 | return false; |
353 | |
354 | case REQUIRE: |
355 | framework::assertion_result( ar: AR_FAILED ); |
356 | |
357 | framework::test_unit_aborted( tu: framework::current_test_case() ); |
358 | |
359 | BOOST_TEST_I_THROW( execution_aborted() ); |
360 | } |
361 | |
362 | return true; |
363 | } |
364 | |
365 | //____________________________________________________________________________// |
366 | |
367 | assertion_result |
368 | format_assertion_result( const_string expr_val, const_string details ) |
369 | { |
370 | assertion_result res(false); |
371 | |
372 | bool starts_new_line = first_char( source: expr_val ) == '\n'; |
373 | |
374 | if( !starts_new_line && !expr_val.is_empty() ) |
375 | res.message().stream() << " [" << expr_val << "]" ; |
376 | |
377 | if( !details.is_empty() ) { |
378 | if( first_char(source: details) != '[' ) |
379 | res.message().stream() << ". " ; |
380 | else |
381 | res.message().stream() << " " ; |
382 | |
383 | res.message().stream() << details; |
384 | } |
385 | |
386 | if( starts_new_line ) |
387 | res.message().stream() << "." << expr_val; |
388 | |
389 | return res; |
390 | } |
391 | |
392 | //____________________________________________________________________________// |
393 | |
394 | BOOST_TEST_DECL std::string |
395 | prod_report_format( assertion_result const& ar, unit_test::lazy_ostream const& assertion_descr, check_type ct, std::size_t num_args, ... ) |
396 | { |
397 | std::ostringstream msg_buff; |
398 | |
399 | va_list args; |
400 | va_start( args, num_args ); |
401 | |
402 | format_report( os&: msg_buff, pr: ar, assertion_descr, tl: CHECK, ct, num_args, args, prefix: "assertion " , suffix: " failed" ); |
403 | |
404 | va_end( args ); |
405 | |
406 | return msg_buff.str(); |
407 | } |
408 | |
409 | //____________________________________________________________________________// |
410 | |
411 | assertion_result |
412 | equal_impl( char const* left, char const* right ) |
413 | { |
414 | return (left && right) ? std::strcmp( s1: left, s2: right ) == 0 : (left == right); |
415 | } |
416 | |
417 | //____________________________________________________________________________// |
418 | |
419 | #if !defined( BOOST_NO_CWCHAR ) |
420 | |
421 | assertion_result |
422 | equal_impl( wchar_t const* left, wchar_t const* right ) |
423 | { |
424 | return (left && right) ? std::wcscmp( s1: left, s2: right ) == 0 : (left == right); |
425 | } |
426 | |
427 | #endif // !defined( BOOST_NO_CWCHAR ) |
428 | |
429 | //____________________________________________________________________________// |
430 | |
431 | bool |
432 | is_defined_impl( const_string symbol_name, const_string symbol_value ) |
433 | { |
434 | symbol_value.trim_left( trim_size: 2 ); |
435 | return symbol_name != symbol_value; |
436 | } |
437 | |
438 | //____________________________________________________________________________// |
439 | |
440 | // ************************************************************************** // |
441 | // ************** context_frame ************** // |
442 | // ************************************************************************** // |
443 | |
444 | context_frame::context_frame( ::boost::unit_test::lazy_ostream const& context_descr ) |
445 | : m_frame_id( unit_test::framework::add_context( context_descr, sticky: true ) ) |
446 | { |
447 | } |
448 | |
449 | //____________________________________________________________________________// |
450 | |
451 | context_frame::~context_frame() |
452 | { |
453 | unit_test::framework::clear_context( frame_id: m_frame_id ); |
454 | } |
455 | |
456 | //____________________________________________________________________________// |
457 | |
458 | context_frame::operator bool() |
459 | { |
460 | return true; |
461 | } |
462 | |
463 | //____________________________________________________________________________// |
464 | |
465 | } // namespace tt_detail |
466 | |
467 | // ************************************************************************** // |
468 | // ************** output_test_stream ************** // |
469 | // ************************************************************************** // |
470 | |
471 | struct output_test_stream::Impl |
472 | { |
473 | std::fstream m_pattern; |
474 | bool m_match_or_save; |
475 | bool m_text_or_binary; |
476 | std::string m_synced_string; |
477 | |
478 | char get_char() |
479 | { |
480 | char res; |
481 | do { |
482 | m_pattern.get( c&: res ); |
483 | } while( m_text_or_binary && res == '\r' && !m_pattern.fail() && !m_pattern.eof() ); |
484 | |
485 | return res; |
486 | } |
487 | |
488 | void check_and_fill( assertion_result& res ) |
489 | { |
490 | if( !res.p_predicate_value ) |
491 | res.message() << "Output content: \"" << m_synced_string << '\"'; |
492 | } |
493 | }; |
494 | |
495 | //____________________________________________________________________________// |
496 | |
497 | output_test_stream::output_test_stream( const_string pattern_file_name, bool match_or_save, bool text_or_binary ) |
498 | : m_pimpl( new Impl ) |
499 | { |
500 | if( !pattern_file_name.is_empty() ) { |
501 | std::ios::openmode m = match_or_save ? std::ios::in : std::ios::out; |
502 | if( !text_or_binary ) |
503 | m |= std::ios::binary; |
504 | |
505 | m_pimpl->m_pattern.open( s: pattern_file_name.begin(), mode: m ); |
506 | |
507 | if( !m_pimpl->m_pattern.is_open() ) |
508 | BOOST_TEST_MESSAGE( "Can't open pattern file " << pattern_file_name << " for " << (match_or_save ? "reading" : "writing" ) ); |
509 | } |
510 | |
511 | m_pimpl->m_match_or_save = match_or_save; |
512 | m_pimpl->m_text_or_binary = text_or_binary; |
513 | } |
514 | |
515 | //____________________________________________________________________________// |
516 | |
517 | output_test_stream::~output_test_stream() |
518 | { |
519 | delete m_pimpl; |
520 | } |
521 | |
522 | //____________________________________________________________________________// |
523 | |
524 | assertion_result |
525 | output_test_stream::is_empty( bool flush_stream ) |
526 | { |
527 | sync(); |
528 | |
529 | assertion_result res( m_pimpl->m_synced_string.empty() ); |
530 | |
531 | m_pimpl->check_and_fill( res ); |
532 | |
533 | if( flush_stream ) |
534 | flush(); |
535 | |
536 | return res; |
537 | } |
538 | |
539 | //____________________________________________________________________________// |
540 | |
541 | assertion_result |
542 | output_test_stream::check_length( std::size_t length_, bool flush_stream ) |
543 | { |
544 | sync(); |
545 | |
546 | assertion_result res( m_pimpl->m_synced_string.length() == length_ ); |
547 | |
548 | m_pimpl->check_and_fill( res ); |
549 | |
550 | if( flush_stream ) |
551 | flush(); |
552 | |
553 | return res; |
554 | } |
555 | |
556 | //____________________________________________________________________________// |
557 | |
558 | assertion_result |
559 | output_test_stream::is_equal( const_string arg, bool flush_stream ) |
560 | { |
561 | sync(); |
562 | |
563 | assertion_result res( const_string( m_pimpl->m_synced_string ) == arg ); |
564 | |
565 | m_pimpl->check_and_fill( res ); |
566 | |
567 | if( flush_stream ) |
568 | flush(); |
569 | |
570 | return res; |
571 | } |
572 | |
573 | //____________________________________________________________________________// |
574 | |
575 | assertion_result |
576 | output_test_stream::match_pattern( bool flush_stream ) |
577 | { |
578 | sync(); |
579 | |
580 | assertion_result result( true ); |
581 | |
582 | if( !m_pimpl->m_pattern.is_open() ) { |
583 | result = false; |
584 | result.message() << "Pattern file can't be opened!" ; |
585 | } |
586 | else { |
587 | if( m_pimpl->m_match_or_save ) { |
588 | for ( std::string::size_type i = 0; i < m_pimpl->m_synced_string.length(); ++i ) { |
589 | char c = m_pimpl->get_char(); |
590 | |
591 | result = !m_pimpl->m_pattern.fail() && |
592 | !m_pimpl->m_pattern.eof() && |
593 | (m_pimpl->m_synced_string[i] == c); |
594 | |
595 | if( !result ) { |
596 | std::string::size_type suffix_size = (std::min)( a: m_pimpl->m_synced_string.length() - i, |
597 | b: static_cast<std::string::size_type>(5) ); |
598 | |
599 | // try to log area around the mismatch |
600 | result.message() << "Mismatch at position " << i << '\n' |
601 | << "..." << m_pimpl->m_synced_string.substr( pos: i, n: suffix_size ) << "..." << '\n' |
602 | << "..." << c; |
603 | |
604 | std::string::size_type counter = suffix_size; |
605 | while( --counter ) { |
606 | char c2 = m_pimpl->get_char(); |
607 | |
608 | if( m_pimpl->m_pattern.fail() || m_pimpl->m_pattern.eof() ) |
609 | break; |
610 | |
611 | result.message() << c2; |
612 | } |
613 | |
614 | result.message() << "..." ; |
615 | |
616 | // skip rest of the bytes. May help for further matching |
617 | m_pimpl->m_pattern.ignore( |
618 | n: static_cast<std::streamsize>( m_pimpl->m_synced_string.length() - i - suffix_size) ); |
619 | break; |
620 | } |
621 | } |
622 | } |
623 | else { |
624 | m_pimpl->m_pattern.write( s: m_pimpl->m_synced_string.c_str(), |
625 | n: static_cast<std::streamsize>( m_pimpl->m_synced_string.length() ) ); |
626 | m_pimpl->m_pattern.flush(); |
627 | } |
628 | } |
629 | |
630 | if( flush_stream ) |
631 | flush(); |
632 | |
633 | return result; |
634 | } |
635 | |
636 | //____________________________________________________________________________// |
637 | |
638 | void |
639 | output_test_stream::flush() |
640 | { |
641 | m_pimpl->m_synced_string.erase(); |
642 | |
643 | #ifndef BOOST_NO_STRINGSTREAM |
644 | str( s: std::string() ); |
645 | #else |
646 | seekp( 0, std::ios::beg ); |
647 | #endif |
648 | } |
649 | |
650 | //____________________________________________________________________________// |
651 | |
652 | std::size_t |
653 | output_test_stream::length() |
654 | { |
655 | sync(); |
656 | |
657 | return m_pimpl->m_synced_string.length(); |
658 | } |
659 | |
660 | //____________________________________________________________________________// |
661 | |
662 | void |
663 | output_test_stream::sync() |
664 | { |
665 | #ifdef BOOST_NO_STRINGSTREAM |
666 | m_pimpl->m_synced_string.assign( str(), pcount() ); |
667 | freeze( false ); |
668 | #else |
669 | m_pimpl->m_synced_string = str(); |
670 | #endif |
671 | } |
672 | |
673 | //____________________________________________________________________________// |
674 | |
675 | } // namespace test_tools |
676 | } // namespace boost |
677 | |
678 | #include <boost/test/detail/enable_warnings.hpp> |
679 | |
680 | #endif // BOOST_TEST_TEST_TOOLS_IPP_012205GER |
681 | |