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/traverse.hpp> 27#include <boost/test/results_collector.hpp> 28 29#include <boost/test/utils/algorithm.hpp> 30#include <boost/test/utils/string_cast.hpp> 31 32//#include <boost/test/results_reporter.hpp> 33 34 35// Boost 36#include <boost/version.hpp> 37#include <boost/core/ignore_unused.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 if(tr->p_timed_out) 245 name += "-timed-execution"; 246 else 247 name += "-setup-teardown"; 248 } 249 250 m_stream << "<testcase assertions" << utils::attr_value() << nb_assertions; 251 if(!classname.empty()) 252 m_stream << " classname" << utils::attr_value() << classname; 253 254 // test case name and time taken 255 m_stream 256 << " name" << utils::attr_value() << name 257 << " time" << utils::attr_value() << double(tr->p_duration_microseconds) * 1E-6 258 << ">" << std::endl; 259 } 260 261 void write_testcase_system_out(junit_impl::junit_log_helper const &detailed_log, 262 test_unit const * tu, 263 bool skipped) const 264 { 265 // system-out + all info/messages, the object skips the empty entries 266 conditional_cdata_helper system_out_helper(m_stream, "system-out"); 267 268 // indicate why the test has been skipped first 269 if( skipped ) { 270 std::list<std::string> skipping_decision_chain = build_skipping_chain(*tu); 271 for(list_str_citerator it(skipping_decision_chain.begin()), ite(skipping_decision_chain.end()); 272 it != ite; 273 ++it) 274 { 275 system_out_helper(*it); 276 } 277 } 278 279 // stdout 280 for(list_str_citerator it(detailed_log.system_out.begin()), ite(detailed_log.system_out.end()); 281 it != ite; 282 ++it) 283 { 284 system_out_helper(*it); 285 } 286 287 // warning/info message last 288 for(vect_assertion_entry_citerator it(detailed_log.assertion_entries.begin()); 289 it != detailed_log.assertion_entries.end(); 290 ++it) 291 { 292 if(it->log_entry != assertion_entry::log_entry_info) 293 continue; 294 system_out_helper(it->output); 295 } 296 } 297 298 void write_testcase_system_err(junit_impl::junit_log_helper const &detailed_log, 299 test_unit const * tu, 300 test_results const *tr) const 301 { 302 // system-err output + test case informations 303 bool has_failed = (tr != 0) ? !tr->p_skipped && !tr->passed() : false; 304 if(!detailed_log.system_err.empty() || has_failed) 305 { 306 std::ostringstream o; 307 if(has_failed) { 308 o << "Failures detected in:" << std::endl; 309 } 310 else { 311 o << "ERROR STREAM:" << std::endl; 312 } 313 314 if(tu->p_type == TUT_SUITE) { 315 if( tu->p_id == m_ts.p_id ) { 316 o << " boost.test global setup/teardown" << std::endl; 317 } else { 318 o << "- test suite: " << tu_name_remove_newlines(tu->full_name()) << std::endl; 319 } 320 } 321 else { 322 o << "- test case: " << tu_name_remove_newlines(tu->full_name()); 323 if(!tu->p_description.value.empty()) 324 o << " '" << tu->p_description << "'"; 325 326 o << std::endl 327 << "- file: " << file_basename(tu->p_file_name) << std::endl 328 << "- line: " << tu->p_line_num << std::endl 329 ; 330 } 331 332 if(!detailed_log.system_err.empty()) 333 o << std::endl << "STDERR BEGIN: ------------" << std::endl; 334 335 for(list_str_citerator it(detailed_log.system_err.begin()), ite(detailed_log.system_err.end()); 336 it != ite; 337 ++it) 338 { 339 o << *it; 340 } 341 342 if(!detailed_log.system_err.empty()) 343 o << std::endl << "STDERR END ------------" << std::endl; 344 345 conditional_cdata_helper system_err_helper(m_stream, "system-err"); 346 system_err_helper(o.str()); 347 } 348 } 349 350 int get_nb_assertions(junit_impl::junit_log_helper const &detailed_log, 351 test_unit const & tu, 352 test_results const *tr) const { 353 int nb_assertions(-1); 354 if( tu.p_type == TUT_SUITE ) { 355 nb_assertions = 0; 356 for(vect_assertion_entry_citerator it(detailed_log.assertion_entries.begin()); 357 it != detailed_log.assertion_entries.end(); 358 ++it) 359 { 360 if(it->log_entry != assertion_entry::log_entry_info) 361 nb_assertions++; 362 } 363 } 364 else { 365 nb_assertions = static_cast<int>(tr->p_assertions_passed + tr->p_assertions_failed); 366 } 367 368 return nb_assertions; 369 } 370 371 void output_detailed_logs(junit_impl::junit_log_helper const &detailed_log, 372 test_unit const & tu, 373 bool skipped, 374 test_results const *tr) const 375 { 376 int nb_assertions = get_nb_assertions(detailed_log, tu, tr); 377 if(!nb_assertions && tu.p_type == TUT_SUITE) 378 return; 379 380 write_testcase_header(tu, tr, nb_assertions); 381 382 if( skipped ) { 383 m_stream << "<skipped/>" << std::endl; 384 } 385 else { 386 387 for(vect_assertion_entry_citerator it(detailed_log.assertion_entries.begin()); 388 it != detailed_log.assertion_entries.end(); 389 ++it) 390 { 391 add_log_entry(*it); 392 } 393 } 394 395 write_testcase_system_out(detailed_log, &tu, skipped); 396 write_testcase_system_err(detailed_log, &tu, tr); 397 m_stream << "</testcase>" << std::endl; 398 } 399 400 void visit( test_case const& tc ) BOOST_OVERRIDE 401 { 402 403 test_results const& tr = results_collector.results( tc.p_id ); 404 junit_log_formatter::map_trace_t::const_iterator it_find = m_map_test.find(tc.p_id); 405 if(it_find == m_map_test.end()) 406 { 407 // test has been skipped and not seen by the logger 408 output_detailed_logs(junit_impl::junit_log_helper(), tc, true, &tr); 409 } 410 else { 411 output_detailed_logs(it_find->second, tc, tr.p_skipped, &tr); 412 } 413 } 414 415 bool test_suite_start( test_suite const& ts ) BOOST_OVERRIDE 416 { 417 test_results const& tr = results_collector.results( ts.p_id ); 418 419 // unique test suite, without s, nesting not supported in CI 420 if( m_ts.p_id == ts.p_id ) { 421 m_stream << "<testsuite"; 422 423 // think about: maybe we should add the number of fixtures of a test_suite as 424 // independent tests (field p_fixtures). 425 // same goes for the timed-execution: we can think of that as a separate test-unit 426 // in the suite. 427 // see https://llg.cubic.org/docs/junit/ and 428 // http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLJUnitResultFormatter.java?view=markup 429 m_stream 430 // << "disabled=\"" << tr.p_test_cases_skipped << "\" " 431 << " tests" << utils::attr_value() 432 << tr.p_test_cases_passed 433 + tr.p_test_cases_failed 434 // + tr.p_test_cases_aborted // aborted is also failed, we avoid counting it twice 435 << " skipped" << utils::attr_value() << tr.p_test_cases_skipped 436 << " errors" << utils::attr_value() << tr.p_test_cases_aborted 437 << " failures" << utils::attr_value() 438 << tr.p_test_cases_failed 439 + tr.p_test_suites_timed_out 440 + tr.p_test_cases_timed_out 441 - tr.p_test_cases_aborted // failed is not aborted in the Junit sense 442 << " id" << utils::attr_value() << m_id++ 443 << " name" << utils::attr_value() << tu_name_normalize(ts.p_name) 444 << " time" << utils::attr_value() << (tr.p_duration_microseconds * 1E-6) 445 << ">" << std::endl; 446 447 if(m_display_build_info) 448 { 449 m_stream << "<properties>" << std::endl; 450 m_stream << "<property name=\"platform\" value" << utils::attr_value() << BOOST_PLATFORM << " />" << std::endl; 451 m_stream << "<property name=\"compiler\" value" << utils::attr_value() << BOOST_COMPILER << " />" << std::endl; 452 m_stream << "<property name=\"stl\" value" << utils::attr_value() << BOOST_STDLIB << " />" << std::endl; 453 454 std::ostringstream o; 455 o << BOOST_VERSION/100000 << "." << BOOST_VERSION/100 % 1000 << "." << BOOST_VERSION % 100; 456 m_stream << "<property name=\"boost\" value" << utils::attr_value() << o.str() << " />" << std::endl; 457 m_stream << "</properties>" << std::endl; 458 } 459 } 460 461 if( !tr.p_skipped ) { 462 // if we land here, then this is a chance that we are logging the fixture setup/teardown of a test-suite. 463 // the setup/teardown logging of a test-case is part of the test case. 464 // we do not care about the test-suite that were skipped (really??) 465 junit_log_formatter::map_trace_t::const_iterator it_find = m_map_test.find(ts.p_id); 466 if(it_find != m_map_test.end()) { 467 output_detailed_logs(it_find->second, ts, false, &tr); 468 } 469 } 470 471 return true; // indicates that the children should also be parsed 472 } 473 474 void test_suite_finish( test_suite const& ts ) BOOST_OVERRIDE 475 { 476 if( m_ts.p_id == ts.p_id ) { 477 write_testcase_system_out(runner_log, 0, false); 478 write_testcase_system_err(runner_log, 0, 0); 479 480 m_stream << "</testsuite>"; 481 return; 482 } 483 } 484 485private: 486 // Data members 487 std::ostream& m_stream; 488 test_unit const& m_ts; 489 junit_log_formatter::map_trace_t const& m_map_test; 490 junit_impl::junit_log_helper const& runner_log; 491 size_t m_id; 492 bool m_display_build_info; 493}; 494 495 496 497void 498junit_log_formatter::log_finish( std::ostream& ostr ) 499{ 500 ostr << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl; 501 502 // getting the root test suite 503 if(!map_tests.empty()) { 504 test_unit* root = &boost::unit_test::framework::get( map_tests.begin()->first, TUT_ANY ); 505 506 // looking for the root of the SUBtree (we stay in the subtree) 507 while(root->p_parent_id != INV_TEST_UNIT_ID && map_tests.count(root->p_parent_id) > 0) { 508 root = &boost::unit_test::framework::get( root->p_parent_id, TUT_ANY ); 509 } 510 junit_result_helper ch( ostr, *root, map_tests, this->runner_log_entry, m_display_build_info ); 511 traverse_test_tree( root->p_id, ch, true ); // last is to ignore disabled suite special handling 512 } 513 else { 514 ostr << "<testsuites errors=\"1\">"; 515 ostr << "<testsuite errors=\"1\" name=\"boost-test-framework\">"; 516 ostr << "<testcase assertions=\"1\" name=\"test-setup\">"; 517 ostr << "<system-out>Incorrect setup: no test case executed</system-out>"; 518 ostr << "</testcase></testsuite></testsuites>"; 519 } 520 return; 521} 522 523//____________________________________________________________________________// 524 525void 526junit_log_formatter::log_build_info( std::ostream& /*ostr*/, bool log_build_info ) 527{ 528 m_display_build_info = log_build_info; 529} 530 531//____________________________________________________________________________// 532 533void 534junit_log_formatter::test_unit_start( std::ostream& /*ostr*/, test_unit const& tu ) 535{ 536 list_path_to_root.push_back( tu.p_id ); 537 map_tests.insert(std::make_pair(tu.p_id, junit_impl::junit_log_helper())); // current_test_case_id not working here 538} 539 540 541 542//____________________________________________________________________________// 543 544 545void 546junit_log_formatter::test_unit_finish( std::ostream& /*ostr*/, test_unit const& tu, unsigned long /*elapsed*/ ) 547{ 548 // the time is already stored in the result_reporter 549 boost::ignore_unused( tu ); 550 assert( tu.p_id == list_path_to_root.back() ); 551 list_path_to_root.pop_back(); 552} 553 554void 555junit_log_formatter::test_unit_aborted( std::ostream& /*ostr*/, test_unit const& tu ) 556{ 557 boost::ignore_unused( tu ); 558 assert( tu.p_id == list_path_to_root.back() ); 559 //list_path_to_root.pop_back(); 560} 561 562//____________________________________________________________________________// 563 564void 565junit_log_formatter::test_unit_timed_out( std::ostream& /*os*/, test_unit const& tu) 566{ 567 if(tu.p_type == TUT_SUITE) 568 { 569 // if we reach this call, it means that the test has already started and 570 // test_unit_start has already been called on the tu. 571 junit_impl::junit_log_helper& last_entry = get_current_log_entry(); 572 junit_impl::junit_log_helper::assertion_entry entry; 573 entry.logentry_message = "test-suite time out"; 574 entry.logentry_type = "execution timeout"; 575 entry.log_entry = junit_impl::junit_log_helper::assertion_entry::log_entry_error; 576 entry.output = "the current suite exceeded the allocated execution time"; 577 last_entry.assertion_entries.push_back(entry); 578 } 579} 580 581//____________________________________________________________________________// 582 583void 584junit_log_formatter::test_unit_skipped( std::ostream& /*ostr*/, test_unit const& tu, const_string reason ) 585{ 586 // if a test unit is skipped, then the start of this TU has not been called yet. 587 // we cannot use get_current_log_entry here, but the TU id should appear in the map. 588 // The "skip" boolean is given by the boost.test framework 589 junit_impl::junit_log_helper& v = map_tests[tu.p_id]; // not sure if we can use get_current_log_entry() 590 v.skipping_reason.assign(reason.begin(), reason.end()); 591} 592 593//____________________________________________________________________________// 594 595void 596junit_log_formatter::log_exception_start( std::ostream& /*ostr*/, log_checkpoint_data const& checkpoint_data, execution_exception const& ex ) 597{ 598 std::ostringstream o; 599 execution_exception::location const& loc = ex.where(); 600 601 m_is_last_assertion_or_error = false; 602 603 junit_impl::junit_log_helper& last_entry = get_current_log_entry(); 604 605 junit_impl::junit_log_helper::assertion_entry entry; 606 607 entry.logentry_message = "unexpected exception"; 608 entry.log_entry = junit_impl::junit_log_helper::assertion_entry::log_entry_error; 609 610 switch(ex.code()) 611 { 612 case execution_exception::cpp_exception_error: 613 entry.logentry_type = "uncaught exception"; 614 break; 615 case execution_exception::timeout_error: 616 entry.logentry_type = "execution timeout"; 617 break; 618 case execution_exception::user_error: 619 entry.logentry_type = "user, assert() or CRT error"; 620 break; 621 case execution_exception::user_fatal_error: 622 // Looks like never used 623 entry.logentry_type = "user fatal error"; 624 break; 625 case execution_exception::system_error: 626 entry.logentry_type = "system error"; 627 break; 628 case execution_exception::system_fatal_error: 629 entry.logentry_type = "system fatal error"; 630 break; 631 default: 632 entry.logentry_type = "no error"; // not sure how to handle this one 633 break; 634 } 635 636 o << "UNCAUGHT EXCEPTION:" << std::endl; 637 if( !loc.m_function.is_empty() ) 638 o << "- function: \"" << loc.m_function << "\"" << std::endl; 639 640 o << "- file: " << file_basename(loc.m_file_name) << std::endl 641 << "- line: " << loc.m_line_num << std::endl 642 << std::endl; 643 644 o << "\nEXCEPTION STACK TRACE: --------------\n" << ex.what() 645 << "\n-------------------------------------"; 646 647 if( !checkpoint_data.m_file_name.is_empty() ) { 648 o << std::endl << std::endl 649 << "Last checkpoint:" << std::endl 650 << "- message: \"" << checkpoint_data.m_message << "\"" << std::endl 651 << "- file: " << file_basename(checkpoint_data.m_file_name) << std::endl 652 << "- line: " << checkpoint_data.m_line_num << std::endl 653 ; 654 } 655 656 entry.output = o.str(); 657 658 last_entry.assertion_entries.push_back(entry); 659} 660 661//____________________________________________________________________________// 662 663void 664junit_log_formatter::log_exception_finish( std::ostream& /*ostr*/ ) 665{ 666 // sealing the last entry 667 assert(!get_current_log_entry().assertion_entries.back().sealed); 668 get_current_log_entry().assertion_entries.back().sealed = true; 669} 670 671//____________________________________________________________________________// 672 673void 674junit_log_formatter::log_entry_start( std::ostream& /*ostr*/, log_entry_data const& entry_data, log_entry_types let ) 675{ 676 junit_impl::junit_log_helper& last_entry = get_current_log_entry(); 677 last_entry.skipping = false; 678 m_is_last_assertion_or_error = true; 679 switch(let) 680 { 681 case unit_test_log_formatter::BOOST_UTL_ET_INFO: 682 { 683 if(m_log_level_internal > log_successful_tests) { 684 last_entry.skipping = true; 685 break; 686 } 687 BOOST_FALLTHROUGH; 688 } 689 case unit_test_log_formatter::BOOST_UTL_ET_MESSAGE: 690 { 691 if(m_log_level_internal > log_messages) { 692 last_entry.skipping = true; 693 break; 694 } 695 BOOST_FALLTHROUGH; 696 } 697 case unit_test_log_formatter::BOOST_UTL_ET_WARNING: 698 { 699 if(m_log_level_internal > log_warnings) { 700 last_entry.skipping = true; 701 break; 702 } 703 std::ostringstream o; 704 junit_impl::junit_log_helper::assertion_entry entry; 705 706 entry.log_entry = junit_impl::junit_log_helper::assertion_entry::log_entry_info; 707 entry.logentry_message = "info"; 708 entry.logentry_type = "message"; 709 710 o << (let == unit_test_log_formatter::BOOST_UTL_ET_WARNING ? 711 "WARNING:" : (let == unit_test_log_formatter::BOOST_UTL_ET_MESSAGE ? 712 "MESSAGE:" : "INFO:")) 713 << std::endl 714 << "- file : " << file_basename(entry_data.m_file_name) << std::endl 715 << "- line : " << entry_data.m_line_num << std::endl 716 << "- message: "; // no CR 717 718 entry.output += o.str(); 719 last_entry.assertion_entries.push_back(entry); 720 break; 721 } 722 default: 723 case unit_test_log_formatter::BOOST_UTL_ET_ERROR: 724 case unit_test_log_formatter::BOOST_UTL_ET_FATAL_ERROR: 725 { 726 std::ostringstream o; 727 junit_impl::junit_log_helper::assertion_entry entry; 728 entry.log_entry = junit_impl::junit_log_helper::assertion_entry::log_entry_failure; 729 entry.logentry_message = "failure"; 730 entry.logentry_type = (let == unit_test_log_formatter::BOOST_UTL_ET_ERROR ? "assertion error" : "fatal error"); 731 732 o << "ASSERTION FAILURE:" << std::endl 733 << "- file : " << file_basename(entry_data.m_file_name) << std::endl 734 << "- line : " << entry_data.m_line_num << std::endl 735 << "- message: " ; // no CR 736 737 entry.output += o.str(); 738 last_entry.assertion_entries.push_back(entry); 739 break; 740 } 741 } 742} 743 744//____________________________________________________________________________// 745 746void 747junit_log_formatter::log_entry_value( std::ostream& /*ostr*/, const_string value ) 748{ 749 junit_impl::junit_log_helper& last_entry = get_current_log_entry(); 750 if(last_entry.skipping) 751 return; 752 753 assert(last_entry.assertion_entries.empty() || !last_entry.assertion_entries.back().sealed); 754 755 if(!last_entry.assertion_entries.empty()) 756 { 757 junit_impl::junit_log_helper::assertion_entry& log_entry = last_entry.assertion_entries.back(); 758 log_entry.output += value; 759 } 760 else 761 { 762 // this may be a message coming from another observer 763 // the prefix is set in the log_entry_start 764 last_entry.system_out.push_back(std::string(value.begin(), value.end())); 765 } 766} 767 768//____________________________________________________________________________// 769 770void 771junit_log_formatter::log_entry_finish( std::ostream& /*ostr*/ ) 772{ 773 junit_impl::junit_log_helper& last_entry = get_current_log_entry(); 774 if(!last_entry.skipping) 775 { 776 assert(last_entry.assertion_entries.empty() || !last_entry.assertion_entries.back().sealed); 777 778 if(!last_entry.assertion_entries.empty()) { 779 junit_impl::junit_log_helper::assertion_entry& log_entry = last_entry.assertion_entries.back(); 780 log_entry.output += "\n\n"; // quote end, CR 781 log_entry.sealed = true; 782 } 783 else { 784 last_entry.system_out.push_back("\n\n"); // quote end, CR 785 } 786 } 787 788 last_entry.skipping = false; 789} 790 791//____________________________________________________________________________// 792 793void 794junit_log_formatter::entry_context_start( std::ostream& /*ostr*/, log_level ) 795{ 796 junit_impl::junit_log_helper& last_entry = get_current_log_entry(); 797 if(last_entry.skipping) 798 return; 799 800 std::vector< junit_impl::junit_log_helper::assertion_entry > &v_failure_or_error = last_entry.assertion_entries; 801 assert(!v_failure_or_error.back().sealed); 802 803 junit_impl::junit_log_helper::assertion_entry& last_log_entry = v_failure_or_error.back(); 804 if(m_is_last_assertion_or_error) 805 { 806 last_log_entry.output += "\n- context:\n"; 807 } 808 else 809 { 810 last_log_entry.output += "\n\nCONTEXT:\n"; 811 } 812} 813 814//____________________________________________________________________________// 815 816void 817junit_log_formatter::entry_context_finish( std::ostream& /*ostr*/, log_level ) 818{ 819 // no op, may be removed 820 junit_impl::junit_log_helper& last_entry = get_current_log_entry(); 821 if(last_entry.skipping) 822 return; 823 assert(!get_current_log_entry().assertion_entries.back().sealed); 824} 825 826//____________________________________________________________________________// 827 828void 829junit_log_formatter::log_entry_context( std::ostream& /*ostr*/, log_level , const_string context_descr ) 830{ 831 junit_impl::junit_log_helper& last_entry = get_current_log_entry(); 832 if(last_entry.skipping) 833 return; 834 835 assert(!last_entry.assertion_entries.back().sealed); 836 junit_impl::junit_log_helper::assertion_entry& last_log_entry = get_current_log_entry().assertion_entries.back(); 837 838 last_log_entry.output += 839 (m_is_last_assertion_or_error ? " - '": "- '") + std::string(context_descr.begin(), context_descr.end()) + "'\n"; // quote end 840} 841 842//____________________________________________________________________________// 843 844 845std::string 846junit_log_formatter::get_default_stream_description() const { 847 std::string name = framework::master_test_suite().p_name.value; 848 849 static const std::string to_replace[] = { " ", "\"", "/", "\\", ":"}; 850 static const std::string replacement[] = { "_", "_" , "_", "_" , "_"}; 851 852 name = unit_test::utils::replace_all_occurrences_of( 853 name, 854 to_replace, to_replace + sizeof(to_replace)/sizeof(to_replace[0]), 855 replacement, replacement + sizeof(replacement)/sizeof(replacement[0])); 856 857 std::ifstream check_init((name + ".xml").c_str()); 858 if(!check_init) 859 return name + ".xml"; 860 861 int index = 0; 862 for(; index < 100; index++) { 863 std::string candidate = name + "_" + utils::string_cast(index) + ".xml"; 864 std::ifstream file(candidate.c_str()); 865 if(!file) 866 return candidate; 867 } 868 869 return name + ".xml"; 870} 871 872} // namespace output 873} // namespace unit_test 874} // namespace boost 875 876#include <boost/test/detail/enable_warnings.hpp> 877 878#endif // BOOST_TEST_junit_log_formatter_IPP_020105GER 879