• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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