1// (C) Copyright 2016 Raffi Enficiaud. 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///@brief Contains the implementatoin of the Junit log formatter (OF_JUNIT) 10// *************************************************************************** 11 12#ifndef BOOST_TEST_JUNIT_LOG_FORMATTER_IPP__ 13#define BOOST_TEST_JUNIT_LOG_FORMATTER_IPP__ 14 15// Boost.Test 16#include <boost/test/output/junit_log_formatter.hpp> 17#include <boost/test/execution_monitor.hpp> 18#include <boost/test/framework.hpp> 19#include <boost/test/tree/test_unit.hpp> 20#include <boost/test/utils/basic_cstring/io.hpp> 21#include <boost/test/utils/xml_printer.hpp> 22#include <boost/test/utils/string_cast.hpp> 23#include <boost/test/framework.hpp> 24 25#include <boost/test/tree/visitor.hpp> 26#include <boost/test/tree/test_case_counter.hpp> 27#include <boost/test/tree/traverse.hpp> 28#include <boost/test/results_collector.hpp> 29 30#include <boost/test/utils/algorithm.hpp> 31#include <boost/test/utils/string_cast.hpp> 32 33//#include <boost/test/results_reporter.hpp> 34 35 36// Boost 37#include <boost/version.hpp> 38 39// STL 40#include <iostream> 41#include <fstream> 42#include <set> 43 44#include <boost/test/detail/suppress_warnings.hpp> 45 46 47//____________________________________________________________________________// 48 49namespace boost { 50namespace unit_test { 51namespace output { 52 53 54struct s_replace_chars { 55 template <class T> 56 void operator()(T& to_replace) 57 { 58 if(to_replace == '/') 59 to_replace = '.'; 60 else if(to_replace == ' ') 61 to_replace = '_'; 62 } 63}; 64 65inline std::string tu_name_normalize(std::string full_name) 66{ 67 // maybe directly using normalize_test_case_name instead? 68 std::for_each(full_name.begin(), full_name.end(), s_replace_chars()); 69 return full_name; 70} 71 72inline std::string tu_name_remove_newlines(std::string full_name) 73{ 74 full_name.erase(std::remove(full_name.begin(), full_name.end(), '\n'), full_name.end()); 75 return full_name; 76} 77 78const_string file_basename(const_string filename) { 79 80 const_string path_sep( "\\/" ); 81 const_string::iterator it = unit_test::utils::find_last_of( filename.begin(), filename.end(), 82 path_sep.begin(), path_sep.end() ); 83 if( it != filename.end() ) 84 filename.trim_left( it + 1 ); 85 86 return filename; 87 88} 89 90// ************************************************************************** // 91// ************** junit_log_formatter ************** // 92// ************************************************************************** // 93 94void 95junit_log_formatter::log_start( std::ostream& /*ostr*/, counter_t /*test_cases_amount*/) 96{ 97 map_tests.clear(); 98 list_path_to_root.clear(); 99 runner_log_entry.clear(); 100} 101 102//____________________________________________________________________________// 103 104class junit_result_helper : public test_tree_visitor { 105private: 106 typedef junit_impl::junit_log_helper::assertion_entry assertion_entry; 107 typedef std::vector< assertion_entry >::const_iterator vect_assertion_entry_citerator; 108 typedef std::list<std::string>::const_iterator list_str_citerator; 109 110public: 111 explicit junit_result_helper( 112 std::ostream& stream, 113 test_unit const& ts, 114 junit_log_formatter::map_trace_t const& mt, 115 junit_impl::junit_log_helper const& runner_log_, 116 bool display_build_info ) 117 : m_stream(stream) 118 , m_ts( ts ) 119 , m_map_test( mt ) 120 , runner_log( runner_log_ ) 121 , m_id( 0 ) 122 , m_display_build_info(display_build_info) 123 { } 124 125 void add_log_entry(assertion_entry const& log) const 126 { 127 std::string entry_type; 128 if( log.log_entry == assertion_entry::log_entry_failure ) { 129 entry_type = "failure"; 130 } 131 else if( log.log_entry == assertion_entry::log_entry_error ) { 132 entry_type = "error"; 133 } 134 else { 135 return; 136 } 137 138 m_stream 139 << "<" << entry_type 140 << " message" << utils::attr_value() << log.logentry_message 141 << " type" << utils::attr_value() << log.logentry_type 142 << ">"; 143 144 if(!log.output.empty()) { 145 m_stream << utils::cdata() << "\n" + log.output; 146 } 147 148 m_stream << "</" << entry_type << ">"; 149 } 150 151 struct conditional_cdata_helper { 152 std::ostream &ostr; 153 std::string const field; 154 bool empty; 155 156 conditional_cdata_helper(std::ostream &ostr_, std::string field_) 157 : ostr(ostr_) 158 , field(field_) 159 , empty(true) 160 {} 161 162 ~conditional_cdata_helper() { 163 if(!empty) { 164 ostr << BOOST_TEST_L( "]]>" ) << "</" << field << '>' << std::endl; 165 } 166 } 167 168 void operator()(const std::string& s) { 169 bool current_empty = s.empty(); 170 if(empty) { 171 if(!current_empty) { 172 empty = false; 173 ostr << '<' << field << '>' << BOOST_TEST_L( "<![CDATA[" ); 174 } 175 } 176 if(!current_empty) { 177 ostr << s; 178 } 179 } 180 }; 181 182 std::list<std::string> build_skipping_chain(test_unit const & tu) const 183 { 184 // we enter here because we know that the tu has been skipped. 185 // either junit has not seen this tu, or it is indicated as disabled 186 assert(m_map_test.count(tu.p_id) == 0 || results_collector.results( tu.p_id ).p_skipped); 187 188 std::list<std::string> out; 189 190 test_unit_id id(tu.p_id); 191 while( id != m_ts.p_id && id != INV_TEST_UNIT_ID) { 192 test_unit const& tu_hierarchy = boost::unit_test::framework::get( id, TUT_ANY ); 193 out.push_back("- disabled test unit: '" + tu_name_remove_newlines(tu_hierarchy.full_name()) + "'\n"); 194 if(m_map_test.count(id) > 0) 195 { 196 // junit has seen the reason: this is enough for constructing the chain 197 break; 198 } 199 id = tu_hierarchy.p_parent_id; 200 } 201 junit_log_formatter::map_trace_t::const_iterator it_element_stack(m_map_test.find(id)); 202 if( it_element_stack != m_map_test.end() ) 203 { 204 out.push_back("- reason: '" + it_element_stack->second.skipping_reason + "'"); 205 out.push_front("Test case disabled because of the following chain of decision:\n"); 206 } 207 208 return out; 209 } 210 211 std::string get_class_name(test_unit const & tu_class) const { 212 std::string classname; 213 test_unit_id id(tu_class.p_parent_id); 214 while( id != m_ts.p_id && id != INV_TEST_UNIT_ID ) { 215 test_unit const& tu = boost::unit_test::framework::get( id, TUT_ANY ); 216 classname = tu_name_normalize(tu.p_name) + "." + classname; 217 id = tu.p_parent_id; 218 } 219 220 // removes the trailing dot 221 if(!classname.empty() && *classname.rbegin() == '.') { 222 classname.erase(classname.size()-1); 223 } 224 225 return classname; 226 } 227 228 void write_testcase_header(test_unit const & tu, 229 test_results const *tr, 230 int nb_assertions) const 231 { 232 std::string name; 233 std::string classname; 234 235 if(tu.p_id == m_ts.p_id ) { 236 name = "boost_test"; 237 } 238 else { 239 classname = get_class_name(tu); 240 name = tu_name_normalize(tu.p_name); 241 } 242 243 if( tu.p_type == TUT_SUITE ) { 244 name += "-setup-teardown"; 245 } 246 247 m_stream << "<testcase assertions" << utils::attr_value() << nb_assertions; 248 if(!classname.empty()) 249 m_stream << " classname" << utils::attr_value() << classname; 250 251 // test case name and time taken 252 m_stream 253 << " name" << utils::attr_value() << name 254 << " time" << utils::attr_value() << double(tr->p_duration_microseconds) * 1E-6 255 << ">" << std::endl; 256 } 257 258 void write_testcase_system_out(junit_impl::junit_log_helper const &detailed_log, 259 test_unit const * tu, 260 bool skipped) const 261 { 262 // system-out + all info/messages, the object skips the empty entries 263 conditional_cdata_helper system_out_helper(m_stream, "system-out"); 264 265 // indicate why the test has been skipped first 266 if( skipped ) { 267 std::list<std::string> skipping_decision_chain = build_skipping_chain(*tu); 268 for(list_str_citerator it(skipping_decision_chain.begin()), ite(skipping_decision_chain.end()); 269 it != ite; 270 ++it) 271 { 272 system_out_helper(*it); 273 } 274 } 275 276 // stdout 277 for(list_str_citerator it(detailed_log.system_out.begin()), ite(detailed_log.system_out.end()); 278 it != ite; 279 ++it) 280 { 281 system_out_helper(*it); 282 } 283 284 // warning/info message last 285 for(vect_assertion_entry_citerator it(detailed_log.assertion_entries.begin()); 286 it != detailed_log.assertion_entries.end(); 287 ++it) 288 { 289 if(it->log_entry != assertion_entry::log_entry_info) 290 continue; 291 system_out_helper(it->output); 292 } 293 } 294 295 void write_testcase_system_err(junit_impl::junit_log_helper const &detailed_log, 296 test_unit const * tu, 297 test_results const *tr) const 298 { 299 // system-err output + test case informations 300 bool has_failed = (tr != 0) ? !tr->p_skipped && !tr->passed() : false; 301 if(!detailed_log.system_err.empty() || has_failed) 302 { 303 std::ostringstream o; 304 if(has_failed) { 305 o << "Failures detected in:" << std::endl; 306 } 307 else { 308 o << "ERROR STREAM:" << std::endl; 309 } 310 311 if(tu->p_type == TUT_SUITE) { 312 if( tu->p_id == m_ts.p_id ) { 313 o << " boost.test global setup/teardown" << std::endl; 314 } else { 315 o << "- test suite: " << tu_name_remove_newlines(tu->full_name()) << std::endl; 316 } 317 } 318 else { 319 o << "- test case: " << tu_name_remove_newlines(tu->full_name()); 320 if(!tu->p_description.value.empty()) 321 o << " '" << tu->p_description << "'"; 322 323 o << std::endl 324 << "- file: " << file_basename(tu->p_file_name) << std::endl 325 << "- line: " << tu->p_line_num << std::endl 326 ; 327 } 328 329 if(!detailed_log.system_err.empty()) 330 o << std::endl << "STDERR BEGIN: ------------" << std::endl; 331 332 for(list_str_citerator it(detailed_log.system_err.begin()), ite(detailed_log.system_err.end()); 333 it != ite; 334 ++it) 335 { 336 o << *it; 337 } 338 339 if(!detailed_log.system_err.empty()) 340 o << std::endl << "STDERR END ------------" << std::endl; 341 342 conditional_cdata_helper system_err_helper(m_stream, "system-err"); 343 system_err_helper(o.str()); 344 } 345 } 346 347 int get_nb_assertions(junit_impl::junit_log_helper const &detailed_log, 348 test_unit const & tu, 349 test_results const *tr) const { 350 int nb_assertions(-1); 351 if( tu.p_type == TUT_SUITE ) { 352 nb_assertions = 0; 353 for(vect_assertion_entry_citerator it(detailed_log.assertion_entries.begin()); 354 it != detailed_log.assertion_entries.end(); 355 ++it) 356 { 357 if(it->log_entry != assertion_entry::log_entry_info) 358 nb_assertions++; 359 } 360 } 361 else { 362 nb_assertions = tr->p_assertions_passed + tr->p_assertions_failed; 363 } 364 365 return nb_assertions; 366 } 367 368 void output_detailed_logs(junit_impl::junit_log_helper const &detailed_log, 369 test_unit const & tu, 370 bool skipped, 371 test_results const *tr) const 372 { 373 int nb_assertions = get_nb_assertions(detailed_log, tu, tr); 374 if(!nb_assertions && tu.p_type == TUT_SUITE) 375 return; 376 377 write_testcase_header(tu, tr, nb_assertions); 378 379 if( skipped ) { 380 m_stream << "<skipped/>" << std::endl; 381 } 382 else { 383 384 for(vect_assertion_entry_citerator it(detailed_log.assertion_entries.begin()); 385 it != detailed_log.assertion_entries.end(); 386 ++it) 387 { 388 add_log_entry(*it); 389 } 390 } 391 392 write_testcase_system_out(detailed_log, &tu, skipped); 393 write_testcase_system_err(detailed_log, &tu, tr); 394 m_stream << "</testcase>" << std::endl; 395 } 396 397 void visit( test_case const& tc ) 398 { 399 400 test_results const& tr = results_collector.results( tc.p_id ); 401 junit_log_formatter::map_trace_t::const_iterator it_find = m_map_test.find(tc.p_id); 402 if(it_find == m_map_test.end()) 403 { 404 // test has been skipped and not seen by the logger 405 output_detailed_logs(junit_impl::junit_log_helper(), tc, true, &tr); 406 } 407 else { 408 output_detailed_logs(it_find->second, tc, tr.p_skipped, &tr); 409 } 410 } 411 412 bool test_suite_start( test_suite const& ts ) 413 { 414 test_results const& tr = results_collector.results( ts.p_id ); 415 416 // unique test suite, without s, nesting not supported in CI 417 if( m_ts.p_id == ts.p_id ) { 418 m_stream << "<testsuite"; 419 420 m_stream 421 // << "disabled=\"" << tr.p_test_cases_skipped << "\" " 422 << " tests" << utils::attr_value() << tr.p_test_cases_passed 423 << " skipped" << utils::attr_value() << tr.p_test_cases_skipped 424 << " errors" << utils::attr_value() << tr.p_test_cases_aborted 425 << " failures" << utils::attr_value() << tr.p_test_cases_failed 426 << " id" << utils::attr_value() << m_id++ 427 << " name" << utils::attr_value() << tu_name_normalize(ts.p_name) 428 << " time" << utils::attr_value() << (tr.p_duration_microseconds * 1E-6) 429 << ">" << std::endl; 430 431 if(m_display_build_info) 432 { 433 m_stream << "<properties>" << std::endl; 434 m_stream << "<property name=\"platform\" value" << utils::attr_value() << BOOST_PLATFORM << std::endl; 435 m_stream << "<property name=\"compiler\" value" << utils::attr_value() << BOOST_COMPILER << std::endl; 436 m_stream << "<property name=\"stl\" value" << utils::attr_value() << BOOST_STDLIB << std::endl; 437 438 std::ostringstream o; 439 o << BOOST_VERSION/100000 << "." << BOOST_VERSION/100 % 1000 << "." << BOOST_VERSION % 100; 440 m_stream << "<property name=\"boost\" value" << utils::attr_value() << o.str() << std::endl; 441 m_stream << "</properties>" << std::endl; 442 } 443 } 444 445 if( !tr.p_skipped ) { 446 // if we land here, then this is a chance that we are logging the fixture setup/teardown of a test-suite. 447 // the setup/teardown logging of a test-case is part of the test case. 448 // we do not care about the test-suite that were skipped (really??) 449 junit_log_formatter::map_trace_t::const_iterator it_find = m_map_test.find(ts.p_id); 450 if(it_find != m_map_test.end()) { 451 output_detailed_logs(it_find->second, ts, false, &tr); 452 } 453 } 454 455 return true; // indicates that the children should also be parsed 456 } 457 458 virtual void test_suite_finish( test_suite const& ts ) 459 { 460 if( m_ts.p_id == ts.p_id ) { 461 write_testcase_system_out(runner_log, 0, false); 462 write_testcase_system_err(runner_log, 0, 0); 463 464 m_stream << "</testsuite>"; 465 return; 466 } 467 } 468 469private: 470 // Data members 471 std::ostream& m_stream; 472 test_unit const& m_ts; 473 junit_log_formatter::map_trace_t const& m_map_test; 474 junit_impl::junit_log_helper const& runner_log; 475 size_t m_id; 476 bool m_display_build_info; 477}; 478 479 480 481void 482junit_log_formatter::log_finish( std::ostream& ostr ) 483{ 484 ostr << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl; 485 486 // getting the root test suite 487 if(!map_tests.empty()) { 488 test_unit* root = &boost::unit_test::framework::get( map_tests.begin()->first, TUT_ANY ); 489 490 // looking for the root of the SUBtree (we stay in the subtree) 491 while(root->p_parent_id != INV_TEST_UNIT_ID && map_tests.count(root->p_parent_id) > 0) { 492 root = &boost::unit_test::framework::get( root->p_parent_id, TUT_ANY ); 493 } 494 junit_result_helper ch( ostr, *root, map_tests, this->runner_log_entry, m_display_build_info ); 495 traverse_test_tree( root->p_id, ch, true ); // last is to ignore disabled suite special handling 496 } 497 else { 498 ostr << "<testsuites errors=\"1\">"; 499 ostr << "<testsuite errors=\"1\" name=\"boost-test-framework\">"; 500 ostr << "<testcase assertions=\"1\" name=\"test-setup\">"; 501 ostr << "<system-out>Incorrect setup: no test case executed</system-out>"; 502 ostr << "</testcase></testsuite></testsuites>"; 503 } 504 return; 505} 506 507//____________________________________________________________________________// 508 509void 510junit_log_formatter::log_build_info( std::ostream& /*ostr*/ ) 511{ 512 m_display_build_info = true; 513} 514 515//____________________________________________________________________________// 516 517void 518junit_log_formatter::test_unit_start( std::ostream& /*ostr*/, test_unit const& tu ) 519{ 520 list_path_to_root.push_back( tu.p_id ); 521 map_tests.insert(std::make_pair(tu.p_id, junit_impl::junit_log_helper())); // current_test_case_id not working here 522} 523 524 525 526//____________________________________________________________________________// 527 528void 529junit_log_formatter::test_unit_finish( std::ostream& /*ostr*/, test_unit const& tu, unsigned long /*elapsed*/ ) 530{ 531 // the time is already stored in the result_reporter 532 assert( tu.p_id == list_path_to_root.back() ); 533 list_path_to_root.pop_back(); 534} 535 536void 537junit_log_formatter::test_unit_aborted( std::ostream& /*ostr*/, test_unit const& tu ) 538{ 539 assert( tu.p_id == list_path_to_root.back() ); 540 //list_path_to_root.pop_back(); 541} 542 543//____________________________________________________________________________// 544 545void 546junit_log_formatter::test_unit_skipped( std::ostream& /*ostr*/, test_unit const& tu, const_string reason ) 547{ 548 // if a test unit is skipped, then the start of this TU has not been called yet. 549 // we cannot use get_current_log_entry here, but the TU id should appear in the map. 550 // The "skip" boolean is given by the boost.test framework 551 junit_impl::junit_log_helper& v = map_tests[tu.p_id]; // not sure if we can use get_current_log_entry() 552 v.skipping_reason.assign(reason.begin(), reason.end()); 553} 554 555//____________________________________________________________________________// 556 557void 558junit_log_formatter::log_exception_start( std::ostream& /*ostr*/, log_checkpoint_data const& checkpoint_data, execution_exception const& ex ) 559{ 560 std::ostringstream o; 561 execution_exception::location const& loc = ex.where(); 562 563 m_is_last_assertion_or_error = false; 564 565 junit_impl::junit_log_helper& last_entry = get_current_log_entry(); 566 567 junit_impl::junit_log_helper::assertion_entry entry; 568 569 entry.logentry_message = "unexpected exception"; 570 entry.log_entry = junit_impl::junit_log_helper::assertion_entry::log_entry_error; 571 572 switch(ex.code()) 573 { 574 case execution_exception::cpp_exception_error: 575 entry.logentry_type = "uncaught exception"; 576 break; 577 case execution_exception::timeout_error: 578 entry.logentry_type = "execution timeout"; 579 break; 580 case execution_exception::user_error: 581 entry.logentry_type = "user, assert() or CRT error"; 582 break; 583 case execution_exception::user_fatal_error: 584 // Looks like never used 585 entry.logentry_type = "user fatal error"; 586 break; 587 case execution_exception::system_error: 588 entry.logentry_type = "system error"; 589 break; 590 case execution_exception::system_fatal_error: 591 entry.logentry_type = "system fatal error"; 592 break; 593 default: 594 entry.logentry_type = "no error"; // not sure how to handle this one 595 break; 596 } 597 598 o << "UNCAUGHT EXCEPTION:" << std::endl; 599 if( !loc.m_function.is_empty() ) 600 o << "- function: \"" << loc.m_function << "\"" << std::endl; 601 602 o << "- file: " << file_basename(loc.m_file_name) << std::endl 603 << "- line: " << loc.m_line_num << std::endl 604 << std::endl; 605 606 o << "\nEXCEPTION STACK TRACE: --------------\n" << ex.what() 607 << "\n-------------------------------------"; 608 609 if( !checkpoint_data.m_file_name.is_empty() ) { 610 o << std::endl << std::endl 611 << "Last checkpoint:" << std::endl 612 << "- message: \"" << checkpoint_data.m_message << "\"" << std::endl 613 << "- file: " << file_basename(checkpoint_data.m_file_name) << std::endl 614 << "- line: " << checkpoint_data.m_line_num << std::endl 615 ; 616 } 617 618 entry.output = o.str(); 619 620 last_entry.assertion_entries.push_back(entry); 621} 622 623//____________________________________________________________________________// 624 625void 626junit_log_formatter::log_exception_finish( std::ostream& /*ostr*/ ) 627{ 628 // sealing the last entry 629 assert(!get_current_log_entry().assertion_entries.back().sealed); 630 get_current_log_entry().assertion_entries.back().sealed = true; 631} 632 633//____________________________________________________________________________// 634 635void 636junit_log_formatter::log_entry_start( std::ostream& /*ostr*/, log_entry_data const& entry_data, log_entry_types let ) 637{ 638 junit_impl::junit_log_helper& last_entry = get_current_log_entry(); 639 last_entry.skipping = false; 640 m_is_last_assertion_or_error = true; 641 switch(let) 642 { 643 case unit_test_log_formatter::BOOST_UTL_ET_INFO: 644 { 645 if(m_log_level_internal > log_successful_tests) { 646 last_entry.skipping = true; 647 break; 648 } 649 // no break on purpose 650 } 651 case unit_test_log_formatter::BOOST_UTL_ET_MESSAGE: 652 { 653 if(m_log_level_internal > log_messages) { 654 last_entry.skipping = true; 655 break; 656 } 657 // no break on purpose 658 } 659 case unit_test_log_formatter::BOOST_UTL_ET_WARNING: 660 { 661 if(m_log_level_internal > log_warnings) { 662 last_entry.skipping = true; 663 break; 664 } 665 std::ostringstream o; 666 junit_impl::junit_log_helper::assertion_entry entry; 667 668 entry.log_entry = junit_impl::junit_log_helper::assertion_entry::log_entry_info; 669 entry.logentry_message = "info"; 670 entry.logentry_type = "message"; 671 672 o << (let == unit_test_log_formatter::BOOST_UTL_ET_WARNING ? 673 "WARNING:" : (let == unit_test_log_formatter::BOOST_UTL_ET_MESSAGE ? 674 "MESSAGE:" : "INFO:")) 675 << std::endl 676 << "- file : " << file_basename(entry_data.m_file_name) << std::endl 677 << "- line : " << entry_data.m_line_num << std::endl 678 << "- message: "; // no CR 679 680 entry.output += o.str(); 681 last_entry.assertion_entries.push_back(entry); 682 break; 683 } 684 default: 685 case unit_test_log_formatter::BOOST_UTL_ET_ERROR: 686 case unit_test_log_formatter::BOOST_UTL_ET_FATAL_ERROR: 687 { 688 std::ostringstream o; 689 junit_impl::junit_log_helper::assertion_entry entry; 690 entry.log_entry = junit_impl::junit_log_helper::assertion_entry::log_entry_failure; 691 entry.logentry_message = "failure"; 692 entry.logentry_type = (let == unit_test_log_formatter::BOOST_UTL_ET_ERROR ? "assertion error" : "fatal error"); 693 694 o << "ASSERTION FAILURE:" << std::endl 695 << "- file : " << file_basename(entry_data.m_file_name) << std::endl 696 << "- line : " << entry_data.m_line_num << std::endl 697 << "- message: " ; // no CR 698 699 entry.output += o.str(); 700 last_entry.assertion_entries.push_back(entry); 701 break; 702 } 703 } 704} 705 706//____________________________________________________________________________// 707 708void 709junit_log_formatter::log_entry_value( std::ostream& /*ostr*/, const_string value ) 710{ 711 junit_impl::junit_log_helper& last_entry = get_current_log_entry(); 712 if(last_entry.skipping) 713 return; 714 715 assert(last_entry.assertion_entries.empty() || !last_entry.assertion_entries.back().sealed); 716 717 if(!last_entry.assertion_entries.empty()) 718 { 719 junit_impl::junit_log_helper::assertion_entry& log_entry = last_entry.assertion_entries.back(); 720 log_entry.output += value; 721 } 722 else 723 { 724 // this may be a message coming from another observer 725 // the prefix is set in the log_entry_start 726 last_entry.system_out.push_back(std::string(value.begin(), value.end())); 727 } 728} 729 730//____________________________________________________________________________// 731 732void 733junit_log_formatter::log_entry_finish( std::ostream& /*ostr*/ ) 734{ 735 junit_impl::junit_log_helper& last_entry = get_current_log_entry(); 736 if(!last_entry.skipping) 737 { 738 assert(last_entry.assertion_entries.empty() || !last_entry.assertion_entries.back().sealed); 739 740 if(!last_entry.assertion_entries.empty()) { 741 junit_impl::junit_log_helper::assertion_entry& log_entry = last_entry.assertion_entries.back(); 742 log_entry.output += "\n\n"; // quote end, CR 743 log_entry.sealed = true; 744 } 745 else { 746 last_entry.system_out.push_back("\n\n"); // quote end, CR 747 } 748 } 749 750 last_entry.skipping = false; 751} 752 753//____________________________________________________________________________// 754 755void 756junit_log_formatter::entry_context_start( std::ostream& /*ostr*/, log_level ) 757{ 758 junit_impl::junit_log_helper& last_entry = get_current_log_entry(); 759 if(last_entry.skipping) 760 return; 761 762 std::vector< junit_impl::junit_log_helper::assertion_entry > &v_failure_or_error = last_entry.assertion_entries; 763 assert(!v_failure_or_error.back().sealed); 764 765 junit_impl::junit_log_helper::assertion_entry& last_log_entry = v_failure_or_error.back(); 766 if(m_is_last_assertion_or_error) 767 { 768 last_log_entry.output += "\n- context:\n"; 769 } 770 else 771 { 772 last_log_entry.output += "\n\nCONTEXT:\n"; 773 } 774} 775 776//____________________________________________________________________________// 777 778void 779junit_log_formatter::entry_context_finish( std::ostream& /*ostr*/, log_level ) 780{ 781 // no op, may be removed 782 junit_impl::junit_log_helper& last_entry = get_current_log_entry(); 783 if(last_entry.skipping) 784 return; 785 assert(!get_current_log_entry().assertion_entries.back().sealed); 786} 787 788//____________________________________________________________________________// 789 790void 791junit_log_formatter::log_entry_context( std::ostream& /*ostr*/, log_level , const_string context_descr ) 792{ 793 junit_impl::junit_log_helper& last_entry = get_current_log_entry(); 794 if(last_entry.skipping) 795 return; 796 797 assert(!last_entry.assertion_entries.back().sealed); 798 junit_impl::junit_log_helper::assertion_entry& last_log_entry = get_current_log_entry().assertion_entries.back(); 799 800 last_log_entry.output += 801 (m_is_last_assertion_or_error ? " - '": "- '") + std::string(context_descr.begin(), context_descr.end()) + "'\n"; // quote end 802} 803 804//____________________________________________________________________________// 805 806 807std::string 808junit_log_formatter::get_default_stream_description() const { 809 std::string name = framework::master_test_suite().p_name.value; 810 811 static const std::string to_replace[] = { " ", "\"", "/", "\\", ":"}; 812 static const std::string replacement[] = { "_", "_" , "_", "_" , "_"}; 813 814 name = unit_test::utils::replace_all_occurrences_of( 815 name, 816 to_replace, to_replace + sizeof(to_replace)/sizeof(to_replace[0]), 817 replacement, replacement + sizeof(replacement)/sizeof(replacement[0])); 818 819 std::ifstream check_init((name + ".xml").c_str()); 820 if(!check_init) 821 return name + ".xml"; 822 823 int index = 0; 824 for(; index < 100; index++) { 825 std::string candidate = name + "_" + utils::string_cast(index) + ".xml"; 826 std::ifstream file(candidate.c_str()); 827 if(!file) 828 return candidate; 829 } 830 831 return name + ".xml"; 832} 833 834} // namespace output 835} // namespace unit_test 836} // namespace boost 837 838#include <boost/test/detail/enable_warnings.hpp> 839 840#endif // BOOST_TEST_junit_log_formatter_IPP_020105GER 841