Imported Upstream version 1.72.0
[platform/upstream/boost.git] / boost / test / impl / junit_log_formatter.ipp
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
49 namespace boost {
50 namespace unit_test {
51 namespace output {
52
53
54 struct 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
65 inline 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
72 inline 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
78 const_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
94 void
95 junit_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
104 class junit_result_helper : public test_tree_visitor {
105 private:
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
110 public:
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 )
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 )
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             // independant 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     virtual void    test_suite_finish( test_suite const& ts )
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
485 private:
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
497 void
498 junit_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
525 void
526 junit_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
533 void
534 junit_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
545 void
546 junit_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
554 void
555 junit_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
564 void
565 junit_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
583 void
584 junit_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
595 void
596 junit_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
663 void
664 junit_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
673 void
674 junit_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
746 void
747 junit_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
770 void
771 junit_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
793 void
794 junit_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
816 void
817 junit_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
828 void
829 junit_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
845 std::string
846 junit_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