diff options
Diffstat (limited to 'boost/test/impl/junit_log_formatter.ipp')
-rw-r--r-- | boost/test/impl/junit_log_formatter.ipp | 477 |
1 files changed, 311 insertions, 166 deletions
diff --git a/boost/test/impl/junit_log_formatter.ipp b/boost/test/impl/junit_log_formatter.ipp index a07ee5e2b0..dd528bc903 100644 --- a/boost/test/impl/junit_log_formatter.ipp +++ b/boost/test/impl/junit_log_formatter.ipp @@ -90,7 +90,7 @@ junit_log_formatter::log_start( std::ostream& ostr, counter_t test_cases_amount) { map_tests.clear(); list_path_to_root.clear(); - root_id = INV_TEST_UNIT_ID; + runner_log_entry.clear(); } //____________________________________________________________________________// @@ -101,10 +101,12 @@ public: std::ostream& stream, test_unit const& ts, junit_log_formatter::map_trace_t const& mt, + junit_impl::junit_log_helper const& runner_log_, bool display_build_info ) : m_stream(stream) , m_ts( ts ) , m_map_test( mt ) + , runner_log( runner_log_ ) , m_id( 0 ) , m_display_build_info(display_build_info) { } @@ -126,46 +128,71 @@ public: m_stream << "</" << entry_type << ">"; } - void visit( test_case const& tc ) + struct conditional_cdata_helper { + std::ostream &ostr; + std::string const field; + bool empty; + + conditional_cdata_helper(std::ostream &ostr_, std::string field_) + : ostr(ostr_) + , field(field_) + , empty(true) + {} + + ~conditional_cdata_helper() { + if(!empty) { + ostr << BOOST_TEST_L( "]]>" ) << "</" << field << '>' << std::endl; + } + } + + void operator()(const std::string& s) { + bool current_empty = s.empty(); + if(empty) { + if(!current_empty) { + empty = false; + ostr << '<' << field << '>' << BOOST_TEST_L( "<![CDATA[" ); + } + } + if(!current_empty) { + ostr << s; + } + } + }; + + std::list<std::string> build_skipping_chain(test_case const & tc) const { - test_results const& tr = results_collector.results( tc.p_id ); + // we enter here because we know that the tc has been skipped. + // either junit has not seen this tc, or it is indicated as disabled + assert(m_map_test.count(tc.p_id) == 0 || results_collector.results( tc.p_id ).p_skipped); - junit_impl::junit_log_helper detailed_log; - bool need_skipping_reason = false; - bool skipped = false; + std::list<std::string> out; - junit_log_formatter::map_trace_t::const_iterator it_element(m_map_test.find(tc.p_id)); - if( it_element != m_map_test.end() ) - { - detailed_log = it_element->second; + test_unit_id id(tc.p_id); + while( id != m_ts.p_id && id != INV_TEST_UNIT_ID) { + test_unit const& tu = boost::unit_test::framework::get( id, TUT_ANY ); + out.push_back("- disabled test unit: '" + tu.full_name() + "'\n"); + if(m_map_test.count(id) > 0) + { + // junit has seen the reason: this is enough for constructing the chain + break; + } + id = tu.p_parent_id; } - else + junit_log_formatter::map_trace_t::const_iterator it_element_stack(m_map_test.find(id)); + if( it_element_stack != m_map_test.end() ) { - need_skipping_reason = true; + out.push_back("- reason: '" + it_element_stack->second.skipping_reason + "'"); + out.push_front("Test case disabled because of the following chain of decision:\n"); } + return out; + } + + std::string get_class_name(test_case const & tc) const { std::string classname; test_unit_id id(tc.p_parent_id); - while( id != m_ts.p_id ) { + while( id != m_ts.p_id && id != INV_TEST_UNIT_ID ) { test_unit const& tu = boost::unit_test::framework::get( id, TUT_ANY ); - - if(need_skipping_reason) - { - test_results const& tr_parent = results_collector.results( id ); - if( tr_parent.p_skipped ) - { - skipped = true; - detailed_log.system_out+= "- disabled: " + tu.full_name() + "\n"; - } - junit_log_formatter::map_trace_t::const_iterator it_element_stack(m_map_test.find(id)); - if( it_element_stack != m_map_test.end() ) - { - detailed_log.system_out+= "- skipping decision: '" + it_element_stack->second.system_out + "'"; - detailed_log.system_out = "SKIPPING decision stack:\n" + detailed_log.system_out; - need_skipping_reason = false; - } - } - classname = tu_name_normalize(tu.p_name) + "." + classname; id = tu.p_parent_id; } @@ -175,23 +202,118 @@ public: classname.erase(classname.size()-1); } + return classname; + } + + void write_testcase_header(test_case const & tc, + test_results const *tr = 0) const + { // // test case header // total number of assertions - m_stream << "<testcase assertions" << utils::attr_value() << tr.p_assertions_passed + tr.p_assertions_failed; + m_stream << "<testcase assertions" << utils::attr_value() << tr->p_assertions_passed + tr->p_assertions_failed; // class name + const std::string classname = get_class_name(tc); if(!classname.empty()) m_stream << " classname" << utils::attr_value() << classname; // test case name and time taken m_stream << " name" << utils::attr_value() << tu_name_normalize(tc.p_name) - << " time" << utils::attr_value() << double(tr.p_duration_microseconds) * 1E-6 + << " time" << utils::attr_value() << double(tr->p_duration_microseconds) * 1E-6 << ">" << std::endl; + } - if( tr.p_skipped || skipped ) { + void write_testcase_system_out(junit_impl::junit_log_helper const &detailed_log, + test_case const * tc, + bool skipped, + test_results const *tr = 0) const + { + // system-out + all info/messages, the object skips the empty entries + conditional_cdata_helper system_out_helper(m_stream, "system-out"); + + // indicate why the test has been skipped first + if( skipped ) { + std::list<std::string> skipping_decision_chain = build_skipping_chain(*tc); + for(std::list<std::string>::const_iterator it(skipping_decision_chain.begin()), ite(skipping_decision_chain.end()); + it != ite; + ++it) + { + system_out_helper(*it); + } + } + + // stdout + for(std::list<std::string>::const_iterator it(detailed_log.system_out.begin()), ite(detailed_log.system_out.end()); + it != ite; + ++it) + { + system_out_helper(*it); + } + + // warning/info message last + for(std::vector< junit_impl::junit_log_helper::assertion_entry >::const_iterator it(detailed_log.assertion_entries.begin()); + it != detailed_log.assertion_entries.end(); + ++it) + { + if(it->log_entry != junit_impl::junit_log_helper::assertion_entry::log_entry_info) + continue; + system_out_helper(it->output); + } + } + + void write_testcase_system_err(junit_impl::junit_log_helper const &detailed_log, + test_case const * tc, + test_results const *tr = 0) const + { + // system-err output + test case informations + bool has_failed = (tr != 0) ? !tr->passed() : false; + if(!detailed_log.system_err.empty() || has_failed) + { + conditional_cdata_helper system_err_helper(m_stream, "system-err"); + std::ostringstream o; + if(has_failed) { + o << "Failures detected in:" << std::endl; + } + else { + o << "ERROR STREAM:" << std::endl; + } + + o << "- test case: " << tc->full_name() << std::endl; + if(!tc->p_description.value.empty()) + o << " '" << tc->p_description << "'"; + + o << std::endl + << "- file: " << file_basename(tc->p_file_name) << std::endl + << "- line: " << tc->p_line_num << std::endl + ; + + if(!detailed_log.system_err.empty()) + o << std::endl << "STDERR BEGIN: ------------" << std::endl; + + system_err_helper(o.str()); + for(std::list<std::string>::const_iterator it(detailed_log.system_err.begin()), ite(detailed_log.system_err.end()); + it != ite; + ++it) + { + system_err_helper(*it); + } + + if(!detailed_log.system_err.empty()) + o << std::endl << "STDERR END ------------" << std::endl; + } + } + + void output_detailed_logs(junit_impl::junit_log_helper const &detailed_log, + test_case const & tc, + bool skipped, + test_results const *tr = 0) const + { + write_testcase_header(tc, tr); + + if( skipped ) { m_stream << "<skipped/>" << std::endl; } else { @@ -209,45 +331,24 @@ public: } } - // system-out + all info/messages - std::string system_out = detailed_log.system_out; - for(std::vector< junit_impl::junit_log_helper::assertion_entry >::const_iterator it(detailed_log.assertion_entries.begin()); - it != detailed_log.assertion_entries.end(); - ++it) - { - if(it->log_entry != junit_impl::junit_log_helper::assertion_entry::log_entry_info) - continue; - system_out += it->output; - } + write_testcase_system_out(detailed_log, &tc, skipped, tr); + write_testcase_system_err(detailed_log, &tc, tr); + m_stream << "</testcase>" << std::endl; + } - if(!system_out.empty()) { - m_stream - << "<system-out>" - << utils::cdata() << system_out - << "</system-out>" - << std::endl; - } + void visit( test_case const& tc ) + { - // system-err output + test case informations - std::string system_err = detailed_log.system_err; + test_results const& tr = results_collector.results( tc.p_id ); + junit_log_formatter::map_trace_t::const_iterator it_find = m_map_test.find(tc.p_id); + if(it_find == m_map_test.end()) { - // test case information (redundant but useful) - std::ostringstream o; - o << "Test case:" << std::endl - << "- name: " << tc.full_name() << std::endl - << "- description: '" << tc.p_description << "'" << std::endl - << "- file: " << file_basename(tc.p_file_name) << std::endl - << "- line: " << tc.p_line_num << std::endl - ; - system_err = o.str() + system_err; + // test has been skipped and not seen by the logger + output_detailed_logs(junit_impl::junit_log_helper(), tc, true, &tr); + } + else { + output_detailed_logs(it_find->second, tc, tr.p_skipped, &tr); } - m_stream - << "<system-err>" - << utils::cdata() << system_err - << "</system-err>" - << std::endl; - - m_stream << "</testcase>" << std::endl; } bool test_suite_start( test_suite const& ts ) @@ -257,8 +358,6 @@ public: return true; test_results const& tr = results_collector.results( ts.p_id ); - - m_stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl; m_stream << "<testsuite"; m_stream @@ -292,6 +391,10 @@ public: { if( m_ts.p_id != ts.p_id ) return; + + write_testcase_system_out(runner_log, 0, false, 0); + write_testcase_system_err(runner_log, 0, 0); + m_stream << "</testsuite>"; } @@ -300,6 +403,7 @@ private: std::ostream& m_stream; test_unit const& m_ts; junit_log_formatter::map_trace_t const& m_map_test; + junit_impl::junit_log_helper const& runner_log; size_t m_id; bool m_display_build_info; }; @@ -309,9 +413,26 @@ private: void junit_log_formatter::log_finish( std::ostream& ostr ) { - junit_result_helper ch( ostr, boost::unit_test::framework::get( root_id, TUT_SUITE ), map_tests, m_display_build_info ); - traverse_test_tree( root_id, ch, true ); // last is to ignore disabled suite special handling + ostr << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl; + + // getting the root test suite + if(!map_tests.empty()) { + test_unit* root = &boost::unit_test::framework::get( map_tests.begin()->first, TUT_ANY ); + // looking for the root of the SUBtree (we stay in the subtree) + while(root->p_parent_id != INV_TEST_UNIT_ID && map_tests.count(root->p_parent_id) > 0) { + root = &boost::unit_test::framework::get( root->p_parent_id, TUT_ANY ); + } + junit_result_helper ch( ostr, *root, map_tests, this->runner_log_entry, m_display_build_info ); + traverse_test_tree( root->p_id, ch, true ); // last is to ignore disabled suite special handling + } + else { + ostr << "<testsuites errors=\"1\">"; + ostr << "<testsuite errors=\"1\" name=\"boost-test-framework\">"; + ostr << "<testcase assertions=\"1\" name=\"test-setup\">"; + ostr << "<system-out>Incorrect setup: no test case executed</system-out>"; + ostr << "</testcase></testsuite></testsuites>"; + } return; } @@ -328,8 +449,6 @@ junit_log_formatter::log_build_info( std::ostream& ostr ) void junit_log_formatter::test_unit_start( std::ostream& ostr, test_unit const& tu ) { - if(list_path_to_root.empty()) - root_id = tu.p_id; list_path_to_root.push_back( tu.p_id ); map_tests.insert(std::make_pair(tu.p_id, junit_impl::junit_log_helper())); // current_test_case_id not working here } @@ -358,16 +477,11 @@ junit_log_formatter::test_unit_aborted( std::ostream& os, test_unit const& tu ) void junit_log_formatter::test_unit_skipped( std::ostream& ostr, test_unit const& tu, const_string reason ) { - if(tu.p_type == TUT_CASE) - { - junit_impl::junit_log_helper& v = map_tests[tu.p_id]; - v.system_out.assign(reason.begin(), reason.end()); - } - else - { - junit_impl::junit_log_helper& v = map_tests[tu.p_id]; - v.system_out.assign(reason.begin(), reason.end()); - } + // if a test unit is skipped, then the start of this TU has not been called yet. + // we cannot use get_current_log_entry here, but the TU id should appear in the map. + // The "skip" boolean is given by the boost.test framework + junit_impl::junit_log_helper& v = map_tests[tu.p_id]; // not sure if we can use get_current_log_entry() + v.skipping_reason.assign(reason.begin(), reason.end()); } //____________________________________________________________________________// @@ -380,67 +494,62 @@ junit_log_formatter::log_exception_start( std::ostream& ostr, log_checkpoint_dat m_is_last_assertion_or_error = false; - if(!list_path_to_root.empty()) - { - junit_impl::junit_log_helper& last_entry = map_tests[list_path_to_root.back()]; - - junit_impl::junit_log_helper::assertion_entry entry; + junit_impl::junit_log_helper& last_entry = get_current_log_entry(); - entry.logentry_message = "unexpected exception"; - entry.log_entry = junit_impl::junit_log_helper::assertion_entry::log_entry_error; + junit_impl::junit_log_helper::assertion_entry entry; - switch(ex.code()) - { - case execution_exception::cpp_exception_error: - entry.logentry_type = "uncaught exception"; - break; - case execution_exception::timeout_error: - entry.logentry_type = "execution timeout"; - break; - case execution_exception::user_error: - entry.logentry_type = "user, assert() or CRT error"; - break; - case execution_exception::user_fatal_error: - // Looks like never used - entry.logentry_type = "user fatal error"; - break; - case execution_exception::system_error: - entry.logentry_type = "system error"; - break; - case execution_exception::system_fatal_error: - entry.logentry_type = "system fatal error"; - break; - default: - entry.logentry_type = "no error"; // not sure how to handle this one - break; - } + entry.logentry_message = "unexpected exception"; + entry.log_entry = junit_impl::junit_log_helper::assertion_entry::log_entry_error; - o << "UNCAUGHT EXCEPTION:" << std::endl; - if( !loc.m_function.is_empty() ) - o << "- function: \"" << loc.m_function << "\"" << std::endl; - - o << "- file: " << file_basename(loc.m_file_name) << std::endl - << "- line: " << loc.m_line_num << std::endl - << std::endl; + switch(ex.code()) + { + case execution_exception::cpp_exception_error: + entry.logentry_type = "uncaught exception"; + break; + case execution_exception::timeout_error: + entry.logentry_type = "execution timeout"; + break; + case execution_exception::user_error: + entry.logentry_type = "user, assert() or CRT error"; + break; + case execution_exception::user_fatal_error: + // Looks like never used + entry.logentry_type = "user fatal error"; + break; + case execution_exception::system_error: + entry.logentry_type = "system error"; + break; + case execution_exception::system_fatal_error: + entry.logentry_type = "system fatal error"; + break; + default: + entry.logentry_type = "no error"; // not sure how to handle this one + break; + } - o << "\nEXCEPTION STACK TRACE: --------------\n" << ex.what() - << "\n-------------------------------------"; + o << "UNCAUGHT EXCEPTION:" << std::endl; + if( !loc.m_function.is_empty() ) + o << "- function: \"" << loc.m_function << "\"" << std::endl; - if( !checkpoint_data.m_file_name.is_empty() ) { - o << std::endl << std::endl - << "Last checkpoint:" << std::endl - << "- message: \"" << checkpoint_data.m_message << "\"" << std::endl - << "- file: " << file_basename(checkpoint_data.m_file_name) << std::endl - << "- line: " << checkpoint_data.m_line_num << std::endl - ; - } + o << "- file: " << file_basename(loc.m_file_name) << std::endl + << "- line: " << loc.m_line_num << std::endl + << std::endl; - entry.output = o.str(); + o << "\nEXCEPTION STACK TRACE: --------------\n" << ex.what() + << "\n-------------------------------------"; - last_entry.assertion_entries.push_back(entry); + if( !checkpoint_data.m_file_name.is_empty() ) { + o << std::endl << std::endl + << "Last checkpoint:" << std::endl + << "- message: \"" << checkpoint_data.m_message << "\"" << std::endl + << "- file: " << file_basename(checkpoint_data.m_file_name) << std::endl + << "- line: " << checkpoint_data.m_line_num << std::endl + ; } - // check what to do with this one + entry.output = o.str(); + + last_entry.assertion_entries.push_back(entry); } //____________________________________________________________________________// @@ -449,8 +558,8 @@ void junit_log_formatter::log_exception_finish( std::ostream& ostr ) { // sealing the last entry - assert(!map_tests[list_path_to_root.back()].assertion_entries.back().sealed); - map_tests[list_path_to_root.back()].assertion_entries.back().sealed = true; + assert(!get_current_log_entry().assertion_entries.back().sealed); + get_current_log_entry().assertion_entries.back().sealed = true; } //____________________________________________________________________________// @@ -458,17 +567,36 @@ junit_log_formatter::log_exception_finish( std::ostream& ostr ) void junit_log_formatter::log_entry_start( std::ostream& ostr, log_entry_data const& entry_data, log_entry_types let ) { - junit_impl::junit_log_helper& last_entry = map_tests[list_path_to_root.back()]; + junit_impl::junit_log_helper& last_entry = get_current_log_entry(); + last_entry.skipping = false; m_is_last_assertion_or_error = true; switch(let) { case unit_test_log_formatter::BOOST_UTL_ET_INFO: + { + if(m_log_level_internal > log_successful_tests) { + last_entry.skipping = true; + break; + } + // no break on purpose + } case unit_test_log_formatter::BOOST_UTL_ET_MESSAGE: + { + if(m_log_level_internal > log_messages) { + last_entry.skipping = true; + break; + } + // no break on purpose + } case unit_test_log_formatter::BOOST_UTL_ET_WARNING: { + if(m_log_level_internal > log_warnings) { + last_entry.skipping = true; + break; + } std::ostringstream o; - junit_impl::junit_log_helper::assertion_entry entry; + entry.log_entry = junit_impl::junit_log_helper::assertion_entry::log_entry_info; entry.logentry_message = "info"; entry.logentry_type = "message"; @@ -505,22 +633,18 @@ junit_log_formatter::log_entry_start( std::ostream& ostr, log_entry_data const& break; } } - } - //____________________________________________________________________________// - - - //____________________________________________________________________________// void junit_log_formatter::log_entry_value( std::ostream& ostr, const_string value ) { - assert(map_tests[list_path_to_root.back()].assertion_entries.empty() || !map_tests[list_path_to_root.back()].assertion_entries.back().sealed); - junit_impl::junit_log_helper& last_entry = map_tests[list_path_to_root.back()]; - std::ostringstream o; - utils::print_escaped_cdata( o, value ); + junit_impl::junit_log_helper& last_entry = get_current_log_entry(); + if(last_entry.skipping) + return; + + assert(last_entry.assertion_entries.empty() || !last_entry.assertion_entries.back().sealed); if(!last_entry.assertion_entries.empty()) { @@ -531,7 +655,7 @@ junit_log_formatter::log_entry_value( std::ostream& ostr, const_string value ) { // this may be a message coming from another observer // the prefix is set in the log_entry_start - last_entry.system_out += value; + last_entry.system_out.push_back(std::string(value.begin(), value.end())); } } @@ -540,16 +664,22 @@ junit_log_formatter::log_entry_value( std::ostream& ostr, const_string value ) void junit_log_formatter::log_entry_finish( std::ostream& ostr ) { - assert(map_tests[list_path_to_root.back()].assertion_entries.empty() || !map_tests[list_path_to_root.back()].assertion_entries.back().sealed); - junit_impl::junit_log_helper& last_entry = map_tests[list_path_to_root.back()]; - if(!last_entry.assertion_entries.empty()) { - junit_impl::junit_log_helper::assertion_entry& log_entry = last_entry.assertion_entries.back(); - log_entry.output += "\n\n"; // quote end, CR - log_entry.sealed = true; - } - else { - last_entry.system_out += "\n\n"; // quote end, CR + junit_impl::junit_log_helper& last_entry = get_current_log_entry(); + if(!last_entry.skipping) + { + assert(last_entry.assertion_entries.empty() || !last_entry.assertion_entries.back().sealed); + + if(!last_entry.assertion_entries.empty()) { + junit_impl::junit_log_helper::assertion_entry& log_entry = last_entry.assertion_entries.back(); + log_entry.output += "\n\n"; // quote end, CR + log_entry.sealed = true; + } + else { + last_entry.system_out.push_back("\n\n"); // quote end, CR + } } + + last_entry.skipping = false; } //____________________________________________________________________________// @@ -557,16 +687,21 @@ junit_log_formatter::log_entry_finish( std::ostream& ostr ) void junit_log_formatter::entry_context_start( std::ostream& ostr, log_level ) { - std::vector< junit_impl::junit_log_helper::assertion_entry > &v_failure_or_error = map_tests[list_path_to_root.back()].assertion_entries; + junit_impl::junit_log_helper& last_entry = get_current_log_entry(); + if(last_entry.skipping) + return; + + std::vector< junit_impl::junit_log_helper::assertion_entry > &v_failure_or_error = last_entry.assertion_entries; assert(!v_failure_or_error.back().sealed); + junit_impl::junit_log_helper::assertion_entry& last_log_entry = v_failure_or_error.back(); if(m_is_last_assertion_or_error) { - v_failure_or_error.back().output += "\n- context:\n"; + last_log_entry.output += "\n- context:\n"; } else { - v_failure_or_error.back().output += "\n\nCONTEXT:\n"; + last_log_entry.output += "\n\nCONTEXT:\n"; } } @@ -576,7 +711,10 @@ void junit_log_formatter::entry_context_finish( std::ostream& ostr ) { // no op, may be removed - assert(!map_tests[list_path_to_root.back()].assertion_entries.back().sealed); + junit_impl::junit_log_helper& last_entry = get_current_log_entry(); + if(last_entry.skipping) + return; + assert(!get_current_log_entry().assertion_entries.back().sealed); } //____________________________________________________________________________// @@ -584,8 +722,15 @@ junit_log_formatter::entry_context_finish( std::ostream& ostr ) void junit_log_formatter::log_entry_context( std::ostream& ostr, const_string context_descr ) { - assert(!map_tests[list_path_to_root.back()].assertion_entries.back().sealed); - map_tests[list_path_to_root.back()].assertion_entries.back().output += (m_is_last_assertion_or_error ? " - '": "- '") + std::string(context_descr.begin(), context_descr.end()) + "'\n"; // quote end + junit_impl::junit_log_helper& last_entry = get_current_log_entry(); + if(last_entry.skipping) + return; + + assert(!last_entry.assertion_entries.back().sealed); + junit_impl::junit_log_helper::assertion_entry& last_log_entry = get_current_log_entry().assertion_entries.back(); + + last_log_entry.output += + (m_is_last_assertion_or_error ? " - '": "- '") + std::string(context_descr.begin(), context_descr.end()) + "'\n"; // quote end } //____________________________________________________________________________// |