summaryrefslogtreecommitdiff
path: root/boost/test
diff options
context:
space:
mode:
authorDongHun Kwak <dh0128.kwak@samsung.com>2016-10-06 10:41:18 +0900
committerDongHun Kwak <dh0128.kwak@samsung.com>2016-10-06 10:43:11 +0900
commitf763a99a501650eff2c60288aa6f10ef916d769e (patch)
tree02af7e13f9a38c888ebf340fe764cbe7dae99da9 /boost/test
parent5cde13f21d36c7224b0e13d11c4b49379ae5210d (diff)
downloadboost-f763a99a501650eff2c60288aa6f10ef916d769e.tar.gz
boost-f763a99a501650eff2c60288aa6f10ef916d769e.tar.bz2
boost-f763a99a501650eff2c60288aa6f10ef916d769e.zip
Imported Upstream version 1.62.0upstream/1.62.0
Change-Id: I9d4c1ddb7b7d8f0069217ecc582700f9fda6dd4c Signed-off-by: DongHun Kwak <dh0128.kwak@samsung.com>
Diffstat (limited to 'boost/test')
-rw-r--r--boost/test/data/for_each_sample.hpp4
-rw-r--r--boost/test/data/test_case.hpp55
-rw-r--r--boost/test/detail/global_typedef.hpp9
-rw-r--r--boost/test/detail/throw_exception.hpp1
-rw-r--r--boost/test/execution_monitor.hpp15
-rw-r--r--boost/test/framework.hpp1
-rw-r--r--boost/test/impl/execution_monitor.ipp30
-rw-r--r--boost/test/impl/framework.ipp242
-rw-r--r--boost/test/impl/junit_log_formatter.ipp627
-rw-r--r--boost/test/impl/results_collector.ipp11
-rw-r--r--boost/test/impl/test_tools.ipp156
-rw-r--r--boost/test/impl/unit_test_log.ipp434
-rw-r--r--boost/test/impl/unit_test_main.ipp4
-rw-r--r--boost/test/impl/unit_test_parameters.ipp56
-rw-r--r--boost/test/included/test_exec_monitor.hpp1
-rw-r--r--boost/test/included/unit_test.hpp2
-rw-r--r--boost/test/output/compiler_log_formatter.hpp8
-rw-r--r--boost/test/output/junit_log_formatter.hpp135
-rw-r--r--boost/test/results_collector.hpp78
-rw-r--r--boost/test/tools/output_test_stream.hpp33
-rw-r--r--boost/test/tree/global_fixture.hpp7
-rw-r--r--boost/test/tree/observer.hpp56
-rw-r--r--boost/test/tree/test_case_counter.hpp8
-rw-r--r--boost/test/unit_test_log.hpp103
-rw-r--r--boost/test/unit_test_log_formatter.hpp122
-rw-r--r--boost/test/unit_test_parameters.hpp32
-rw-r--r--boost/test/utils/algorithm.hpp98
-rw-r--r--boost/test/utils/runtime/argument_factory.hpp2
-rw-r--r--boost/test/utils/runtime/errors.hpp68
-rw-r--r--boost/test/utils/runtime/modifier.hpp13
30 files changed, 2042 insertions, 369 deletions
diff --git a/boost/test/data/for_each_sample.hpp b/boost/test/data/for_each_sample.hpp
index b3bc1ffc78..4785b038cc 100644
--- a/boost/test/data/for_each_sample.hpp
+++ b/boost/test/data/for_each_sample.hpp
@@ -63,7 +63,9 @@ invoke_action( Action const& action, T&& args, std::true_type /* is_tuple */ )
{
invoke_action_impl( action,
std::forward<T>(args),
- typename make_index_sequence< 0, std::tuple_size<T>::value >::type{} );
+ typename make_index_sequence< 0,
+ std::tuple_size<typename std::decay<T>::type>::value
+ >::type{} );
}
diff --git a/boost/test/data/test_case.hpp b/boost/test/data/test_case.hpp
index 9564816ee4..dd9de141c4 100644
--- a/boost/test/data/test_case.hpp
+++ b/boost/test/data/test_case.hpp
@@ -37,6 +37,7 @@
#include <boost/test/detail/suppress_warnings.hpp>
#include <boost/test/tools/detail/print_helper.hpp>
+#include <boost/test/utils/string_cast.hpp>
#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) \
&& !defined(BOOST_TEST_DATASET_MAX_ARITY)
@@ -122,16 +123,25 @@ public:
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
test_case_gen( const_string tc_name, const_string tc_file, std::size_t tc_line, DataSet&& ds )
: m_tc_name( ut_detail::normalize_test_case_name( tc_name ) )
+ , m_tc_file( tc_file )
+ , m_tc_line( tc_line )
+ , m_tc_index( 0 )
{
data::for_each_sample( std::forward<DataSet>( ds ), *this );
}
test_case_gen( test_case_gen&& gen )
: m_tc_name( gen.m_tc_name )
+ , m_tc_file( gen.m_tc_file )
+ , m_tc_line( gen.m_tc_line )
+ , m_tc_index( gen.m_tc_index )
, m_test_cases( std::move(gen.m_test_cases) )
{}
#else
test_case_gen( const_string tc_name, const_string tc_file, std::size_t tc_line, DataSet const& ds )
: m_tc_name( ut_detail::normalize_test_case_name( tc_name ) )
+ , m_tc_file( tc_file )
+ , m_tc_line( tc_line )
+ , m_tc_index( 0 )
{
data::for_each_sample( ds, *this );
}
@@ -149,24 +159,23 @@ public:
}
#if !defined(BOOST_TEST_DATASET_VARIADIC)
- /// make this variadic
-#define TC_MAKE(z,arity,_) \
- template<BOOST_PP_ENUM_PARAMS(arity, typename Arg)> \
- void operator()( BOOST_PP_ENUM_BINARY_PARAMS(arity, Arg, const& arg) ) const \
- { \
- m_test_cases.push_back( new test_case( m_tc_name, m_tc_file, m_tc_line, \
- boost::bind( &TestCase::template test_method<BOOST_PP_ENUM_PARAMS(arity,Arg)>, \
- BOOST_PP_ENUM_PARAMS(arity, arg) ) ) ); \
- } \
+ // see BOOST_TEST_DATASET_MAX_ARITY to increase the default supported arity
+#define TC_MAKE(z,arity,_) \
+ template<BOOST_PP_ENUM_PARAMS(arity, typename Arg)> \
+ void operator()( BOOST_PP_ENUM_BINARY_PARAMS(arity, Arg, const& arg) ) const \
+ { \
+ m_test_cases.push_back( new test_case( genTestCaseName(), m_tc_file, m_tc_line, \
+ boost::bind( &TestCase::template test_method<BOOST_PP_ENUM_PARAMS(arity,Arg)>,\
+ BOOST_PP_ENUM_PARAMS(arity, arg) ) ) ); \
+ } \
BOOST_PP_REPEAT_FROM_TO(1, BOOST_TEST_DATASET_MAX_ARITY, TC_MAKE, _)
#else
-
template<typename ...Arg>
void operator()(Arg&& ... arg) const
{
m_test_cases.push_back(
- new test_case( m_tc_name,
+ new test_case( genTestCaseName(),
m_tc_file,
m_tc_line,
boost::bind( &TestCase::template test_method<Arg...>,
@@ -175,10 +184,16 @@ public:
#endif
private:
+ std::string genTestCaseName() const
+ {
+ return "_" + utils::string_cast(m_tc_index++);
+ }
+
// Data members
std::string m_tc_name;
const_string m_tc_file;
std::size_t m_tc_line;
+ mutable std::size_t m_tc_index;
mutable std::list<test_unit*> m_test_cases;
};
@@ -217,16 +232,16 @@ make_test_case_gen( const_string tc_name, const_string tc_file, std::size_t tc_l
/**/
#define BOOST_DATA_TEST_CASE_IMPL(arity, F, test_name, dataset, params) \
-struct test_name : public F { \
+struct BOOST_PP_CAT(test_name, case) : public F { \
template<BOOST_PP_ENUM_PARAMS(arity, typename Arg)> \
static void test_method( BOOST_DATA_TEST_CASE_PARAMS( params ) ) \
{ \
BOOST_TEST_CHECKPOINT('"' << #test_name << "\" fixture entry.");\
- test_name t; \
+ BOOST_PP_CAT(test_name, case) t; \
BOOST_TEST_CHECKPOINT('"' << #test_name << "\" entry."); \
BOOST_TEST_CONTEXT( "" \
BOOST_PP_SEQ_FOR_EACH(BOOST_DATA_TEST_CONTEXT, _, params)) \
- t._impl(BOOST_PP_SEQ_ENUM(params)); \
+ t._impl(BOOST_PP_SEQ_ENUM(params)); \
BOOST_TEST_CHECKPOINT('"' << #test_name << "\" exit."); \
} \
private: \
@@ -234,15 +249,21 @@ private: \
void _impl(BOOST_DATA_TEST_CASE_PARAMS( params )); \
}; \
\
-BOOST_AUTO_TU_REGISTRAR( test_name )( \
- boost::unit_test::data::ds_detail::make_test_case_gen<test_name>( \
+BOOST_AUTO_TEST_SUITE( test_name ) \
+ \
+BOOST_AUTO_TU_REGISTRAR( BOOST_PP_CAT(test_name, case) )( \
+ boost::unit_test::data::ds_detail::make_test_case_gen< \
+ BOOST_PP_CAT(test_name, case)>( \
BOOST_STRINGIZE( test_name ), \
__FILE__, __LINE__, \
boost::unit_test::data::ds_detail::seed{} ->* dataset ), \
boost::unit_test::decorator::collector::instance() ); \
\
+BOOST_AUTO_TEST_SUITE_END() \
+ \
template<BOOST_PP_ENUM_PARAMS(arity, typename Arg)> \
- void test_name::_impl( BOOST_DATA_TEST_CASE_PARAMS( params ) ) \
+ void BOOST_PP_CAT(test_name, case)::_impl( \
+ BOOST_DATA_TEST_CASE_PARAMS( params ) ) \
/**/
#define BOOST_DATA_TEST_CASE_WITH_PARAMS( F, test_name, dataset, ... ) \
diff --git a/boost/test/detail/global_typedef.hpp b/boost/test/detail/global_typedef.hpp
index 9b7653c078..b9cfeb5206 100644
--- a/boost/test/detail/global_typedef.hpp
+++ b/boost/test/detail/global_typedef.hpp
@@ -34,10 +34,13 @@ enum report_level { INV_REPORT_LEVEL, CONFIRMATION_REPORT, SHORT_REPORT, DETAIL
//____________________________________________________________________________//
+//! Indicates the output format for the loggers or the test tree printing
enum output_format { OF_INVALID,
- OF_CLF, ///< compiler log format
- OF_XML, ///< XML format for report and log,
- OF_DOT ///< dot format for output content
+ OF_CLF, ///< compiler log format
+ OF_XML, ///< XML format for report and log,
+ OF_JUNIT, ///< JUNIT format for report and log,
+ OF_CUSTOM_LOGGER, ///< User specified logger.
+ OF_DOT ///< dot format for output content
};
//____________________________________________________________________________//
diff --git a/boost/test/detail/throw_exception.hpp b/boost/test/detail/throw_exception.hpp
index 2fee10c2cb..1568ec3c29 100644
--- a/boost/test/detail/throw_exception.hpp
+++ b/boost/test/detail/throw_exception.hpp
@@ -57,7 +57,6 @@ throw_exception(E const& e) { throw e; }
//____________________________________________________________________________//
#define BOOST_TEST_I_THROW( E ) unit_test::ut_detail::throw_exception( E )
-#define BOOST_TEST_I_THROW( E ) unit_test::ut_detail::throw_exception( E )
#define BOOST_TEST_I_ASSRT( cond, ex ) if( cond ) {} else BOOST_TEST_I_THROW( ex )
diff --git a/boost/test/execution_monitor.hpp b/boost/test/execution_monitor.hpp
index c9036be5ba..3a203d15f9 100644
--- a/boost/test/execution_monitor.hpp
+++ b/boost/test/execution_monitor.hpp
@@ -66,6 +66,18 @@
#endif
+
+// Additional macro documentations not being generated without this hack
+#ifdef BOOST_TEST_DOXYGEN_DOC__
+
+//! Disables the support of the alternative stack
+//! during the compilation of the Boost.test framework. This is especially useful
+//! in case it is not possible to detect the lack of alternative stack support for
+//! your compiler (for instance, ESXi).
+#define BOOST_TEST_DISABLE_ALT_STACK
+
+#endif
+
//____________________________________________________________________________//
namespace boost {
@@ -276,7 +288,7 @@ private:
}; // execution_exception
// ************************************************************************** //
-/// Function execution monitor
+/// @brief Function execution monitor
/// This class is used to uniformly detect and report an occurrence of several types of signals and exceptions, reducing various
/// errors to a uniform execution_exception that is returned to a caller.
@@ -465,6 +477,7 @@ public:
char const* const p_failed_exp;
};
+//!@internal
#define BOOST_TEST_SYS_ASSERT( cond ) BOOST_TEST_I_ASSRT( cond, ::boost::system_error( BOOST_STRINGIZE( exp ) ) )
// ************************************************************************** //
diff --git a/boost/test/framework.hpp b/boost/test/framework.hpp
index 1f5189d23c..f94bcf125d 100644
--- a/boost/test/framework.hpp
+++ b/boost/test/framework.hpp
@@ -236,6 +236,7 @@ BOOST_TEST_DECL void test_unit_aborted( test_unit const& );
namespace impl {
// exclusively for self test
BOOST_TEST_DECL void setup_for_execution( test_unit const& );
+BOOST_TEST_DECL void setup_loggers( );
} // namespace impl
// ************************************************************************** //
diff --git a/boost/test/impl/execution_monitor.ipp b/boost/test/impl/execution_monitor.ipp
index f7fc8ea4e2..ce6c6c7eb9 100644
--- a/boost/test/impl/execution_monitor.ipp
+++ b/boost/test/impl/execution_monitor.ipp
@@ -68,6 +68,7 @@ using std::va_list;
// to use vsnprintf
#if defined(__QNXNTO__)
# include <stdio.h>
+using std::va_list;
#endif
#ifdef BOOST_SEH_BASED_SIGNAL_HANDLING
@@ -155,8 +156,10 @@ namespace { void _set_se_translator( void* ) {} }
# include <android/api-level.h>
# endif
+// documentation of BOOST_TEST_DISABLE_ALT_STACK in execution_monitor.hpp
# if !defined(__CYGWIN__) && !defined(__QNXNTO__) && !defined(__bgq__) && \
- (!defined(__ANDROID__) || __ANDROID_API__ >= 8)
+ (!defined(__ANDROID__) || __ANDROID_API__ >= 8) && \
+ !defined(BOOST_TEST_DISABLE_ALT_STACK)
# define BOOST_TEST_USE_ALT_STACK
# endif
@@ -182,8 +185,8 @@ namespace { void _set_se_translator( void* ) {} }
#include <errno.h>
#endif
-#if defined(__GNUC__) && !defined(BOOST_NO_TYPEID)
-# include <cxxabi.h>
+#if !defined(BOOST_NO_TYPEID) && !defined(BOOST_NO_RTTI)
+# include <boost/core/demangle.hpp>
#endif
#include <boost/test/detail/suppress_warnings.hpp>
@@ -304,23 +307,17 @@ struct fpe_except_guard {
unsigned m_previosly_enabled;
};
-#ifndef BOOST_NO_TYPEID
// ************************************************************************** //
// ************** typeid_name ************** //
// ************************************************************************** //
+#if !defined(BOOST_NO_TYPEID) && !defined(BOOST_NO_RTTI)
template<typename T>
-char const*
+std::string
typeid_name( T const& t )
{
-#ifdef __GNUC__
- int status;
-
- return abi::__cxa_demangle( typeid(t).name(), 0, 0, &status );
-#else
- return typeid(t).name();
-#endif
+ return boost::core::demangle(typeid(t).name());
}
#endif
@@ -605,7 +602,7 @@ system_signal_exception::report() const
break;
default:
- report_error( execution_exception::system_error,
+ report_error( execution_exception::system_error,
"unrecognized signal %d", m_sig_info->si_signo );
}
}
@@ -1224,7 +1221,7 @@ execution_monitor::execute( boost::function<int ()> const& F )
"std::string: %s", ex.c_str() ); }
// std:: exceptions
-#ifdef BOOST_NO_TYPEID
+#if defined(BOOST_NO_TYPEID) || defined(BOOST_NO_RTTI)
#define CATCH_AND_REPORT_STD_EXCEPTION( ex_name ) \
catch( ex_name const& ex ) \
{ detail::report_error( execution_exception::cpp_exception_error, \
@@ -1236,7 +1233,7 @@ execution_monitor::execute( boost::function<int ()> const& F )
catch( ex_name const& ex ) \
{ detail::report_error( execution_exception::cpp_exception_error, \
current_exception_cast<boost::exception const>(), \
- "%s: %s", detail::typeid_name(ex), ex.what() ); } \
+ "%s: %s", detail::typeid_name(ex).c_str(), ex.what() ); } \
/**/
#endif
@@ -1266,7 +1263,7 @@ execution_monitor::execute( boost::function<int ()> const& F )
catch( boost::exception const& ex )
{ detail::report_error( execution_exception::cpp_exception_error,
&ex,
-#ifdef BOOST_NO_TYPEID
+#if defined(BOOST_NO_TYPEID) || defined(BOOST_NO_RTTI)
"unknown boost::exception" ); }
#else
typeid(ex).name() ); }
@@ -1437,4 +1434,3 @@ disable( unsigned mask )
#include <boost/test/detail/enable_warnings.hpp>
#endif // BOOST_TEST_EXECUTION_MONITOR_IPP_012205GER
-
diff --git a/boost/test/impl/framework.ipp b/boost/test/impl/framework.ipp
index 78459bac27..a513c612e1 100644
--- a/boost/test/impl/framework.ipp
+++ b/boost/test/impl/framework.ipp
@@ -22,6 +22,7 @@
#include <boost/test/unit_test_parameters.hpp>
#include <boost/test/unit_test_log.hpp>
+#include <boost/test/unit_test_log_formatter.hpp>
#include <boost/test/unit_test_monitor.hpp>
#include <boost/test/results_collector.hpp>
#include <boost/test/progress_monitor.hpp>
@@ -39,6 +40,7 @@
#include <boost/test/utils/foreach.hpp>
#include <boost/test/utils/basic_cstring/io.hpp>
+#include <boost/test/utils/basic_cstring/compare.hpp>
#include <boost/test/detail/global_typedef.hpp>
#include <boost/test/detail/throw_exception.hpp>
@@ -53,6 +55,7 @@
#include <set>
#include <cstdlib>
#include <ctime>
+#include <numeric>
#ifdef BOOST_NO_STDC_NAMESPACE
namespace std { using ::time; using ::srand; }
@@ -98,7 +101,7 @@ tu_depth( test_unit_id tu_id, test_unit_id master_tu_id, order_info_per_tu& tuoi
return 0;
order_info& info = tuoi[tu_id];
-
+
if( info.depth == -1 )
info.depth = tu_depth( get_tu_parent( tu_id ), master_tu_id, tuoi ) + 1;
@@ -236,12 +239,12 @@ public:
name_filter( test_unit_id_list& targ_list, const_string filter_expr ) : m_targ_list( targ_list ), m_depth( 0 )
{
#ifdef BOOST_TEST_SUPPORT_TOKEN_ITERATOR
- utils::string_token_iterator tit( filter_expr, (utils::dropped_delimeters = "/",
+ utils::string_token_iterator tit( filter_expr, (utils::dropped_delimeters = "/",
utils::kept_delimeters = utils::dt_none) );
while( tit != utils::string_token_iterator() ) {
- m_components.push_back(
- std::vector<component>( utils::string_token_iterator( *tit, (utils::dropped_delimeters = ",",
+ m_components.push_back(
+ std::vector<component>( utils::string_token_iterator( *tit, (utils::dropped_delimeters = ",",
utils::kept_delimeters = utils::dt_none) ),
utils::string_token_iterator() ) );
@@ -261,6 +264,7 @@ private:
std::vector<component> const& filters = m_components[m_depth-1];
// look for match
+ using namespace boost::placeholders;
return std::find_if( filters.begin(), filters.end(), bind( &component::pass, _1, boost::ref(tu) ) ) != filters.end();
}
@@ -351,8 +355,8 @@ private:
if( dep.p_run_status == tu.p_run_status )
continue;
- BOOST_TEST_MESSAGE( "Including test " << dep.p_type_name << ' ' << dep.full_name() <<
- " as a dependency of test " << tu.p_type_name << ' ' << tu.full_name() );
+ BOOST_TEST_FRAMEWORK_MESSAGE( "Including test " << dep.p_type_name << ' ' << dep.full_name() <<
+ " as a dependency of test " << tu.p_type_name << ' ' << tu.full_name() );
m_dep_collector->push_back( dep_id );
}
@@ -397,22 +401,32 @@ parse_filters( test_unit_id master_tu_id, test_unit_id_list& tu_to_enable, test_
BOOST_TEST_FOREACH( const_string, filter, filters ) {
BOOST_TEST_SETUP_ASSERT( !filter.is_empty(), "Invalid filter specification" );
- enum { SELECTOR, ENABLER, DISABLER } filter_type = SELECTOR;
+ // each --run_test command may also be separated by a ':' (environment variable)
+ utils::string_token_iterator t_filter_it( filter, (utils::dropped_delimeters = ":",
+ utils::kept_delimeters = utils::dt_none) );
- // 11. Deduce filter type
- if( filter[0] == '!' || filter[0] == '+' ) {
- filter_type = filter[0] == '+' ? ENABLER : DISABLER;
- filter.trim_left( 1 );
- BOOST_TEST_SETUP_ASSERT( !filter.is_empty(), "Invalid filter specification" );
- }
+ while( t_filter_it != utils::string_token_iterator() ) {
+ const_string filter_token = *t_filter_it;
+
+ enum { SELECTOR, ENABLER, DISABLER } filter_type = SELECTOR;
+
+ // 11. Deduce filter type
+ if( filter_token[0] == '!' || filter_token[0] == '+' ) {
+ filter_type = filter_token[0] == '+' ? ENABLER : DISABLER;
+ filter_token.trim_left( 1 );
+ BOOST_TEST_SETUP_ASSERT( !filter_token.is_empty(), "Invalid filter specification" );
+ }
- had_selector_filter |= filter_type == SELECTOR;
+ had_selector_filter |= filter_type == SELECTOR;
- // 12. Add test units to corresponding list
- switch( filter_type ) {
- case SELECTOR:
- case ENABLER: add_filtered_test_units( master_tu_id, filter, tu_to_enable ); break;
- case DISABLER: add_filtered_test_units( master_tu_id, filter, tu_to_disable ); break;
+ // 12. Add test units to corresponding list
+ switch( filter_type ) {
+ case SELECTOR:
+ case ENABLER: add_filtered_test_units( master_tu_id, filter_token, tu_to_enable ); break;
+ case DISABLER: add_filtered_test_units( master_tu_id, filter_token, tu_to_disable ); break;
+ }
+
+ ++t_filter_it;
}
}
@@ -437,7 +451,7 @@ public:
, m_next_test_suite_id( MIN_TEST_SUITE_ID )
, m_test_in_progress( false )
, m_context_idx( 0 )
- , m_log_sink( std::cout )
+ , m_log_sinks( )
, m_report_sink( std::cerr )
{
}
@@ -787,7 +801,7 @@ public:
boost::execution_monitor m_aux_em;
- runtime_config::stream_holder m_log_sink;
+ std::map<output_format, runtime_config::stream_holder> m_log_sinks;
runtime_config::stream_holder m_report_sink;
};
@@ -809,7 +823,178 @@ setup_for_execution( test_unit const& tu )
{
s_frk_state().deduce_run_status( tu.p_id );
}
-
+
+struct sum_to_first_only {
+ sum_to_first_only() : is_first(true) {}
+ template <class T, class U>
+ T operator()(T const& l_, U const& r_) {
+ if(is_first) {
+ is_first = false;
+ return l_ + r_.first;
+ }
+ return l_ + ", " + r_.first;
+ }
+
+ bool is_first;
+};
+
+void
+setup_loggers()
+{
+
+ BOOST_TEST_I_TRY {
+
+
+
+#ifdef BOOST_TEST_SUPPORT_TOKEN_ITERATOR
+ bool has_combined_logger = runtime_config::has( runtime_config::COMBINED_LOGGER )
+ && !runtime_config::get< std::vector<std::string> >( runtime_config::COMBINED_LOGGER ).empty();
+#else
+ bool has_combined_logger = false;
+#endif
+
+ if( !has_combined_logger ) {
+ unit_test_log.set_threshold_level( runtime_config::get<log_level>( runtime_config::LOG_LEVEL ) );
+ const output_format format = runtime_config::get<output_format>( runtime_config::LOG_FORMAT );
+ unit_test_log.set_format( format );
+
+ runtime_config::stream_holder& stream_logger = s_frk_state().m_log_sinks[format];
+ if( runtime_config::has( runtime_config::LOG_SINK ) )
+ stream_logger.setup( runtime_config::get<const_string>( runtime_config::LOG_SINK ) );
+ unit_test_log.set_stream( stream_logger.ref() );
+ }
+ else
+ {
+
+ const std::vector<std::string>& v_output_format = runtime_config::get< std::vector<std::string> >( runtime_config::COMBINED_LOGGER ) ;
+
+ static const std::pair<const char*, log_level> all_log_levels[] = {
+ std::make_pair( "all" , log_successful_tests ),
+ std::make_pair( "success" , log_successful_tests ),
+ std::make_pair( "test_suite" , log_test_units ),
+ std::make_pair( "unit_scope" , log_test_units ),
+ std::make_pair( "message" , log_messages ),
+ std::make_pair( "warning" , log_warnings ),
+ std::make_pair( "error" , log_all_errors ),
+ std::make_pair( "cpp_exception" , log_cpp_exception_errors ),
+ std::make_pair( "system_error" , log_system_errors ),
+ std::make_pair( "fatal_error" , log_fatal_errors ),
+ std::make_pair( "nothing" , log_nothing )
+ };
+
+ static const std::pair<const char*, output_format> all_formats[] = {
+ std::make_pair( "HRF" , OF_CLF ),
+ std::make_pair( "CLF" , OF_CLF ),
+ std::make_pair( "XML" , OF_XML ),
+ std::make_pair( "JUNIT", OF_JUNIT )
+ };
+
+
+ bool is_first = true;
+
+ BOOST_TEST_FOREACH( const_string, current_multi_config, v_output_format ) {
+
+#ifdef BOOST_TEST_SUPPORT_TOKEN_ITERATOR
+ utils::string_token_iterator current_config( current_multi_config, (utils::dropped_delimeters = ":",
+ utils::kept_delimeters = utils::dt_none) );
+
+ for( ; current_config != utils::string_token_iterator() ; ++current_config) {
+
+ utils::string_token_iterator current_format_specs( *current_config, (utils::keep_empty_tokens,
+ utils::dropped_delimeters = ",",
+ utils::kept_delimeters = utils::dt_none) );
+
+ output_format format = OF_INVALID ; // default
+ if( current_format_specs != utils::string_token_iterator() &&
+ current_format_specs->size() ) {
+
+ for(size_t elem=0; elem < sizeof(all_formats)/sizeof(all_formats[0]); elem++) {
+ if(const_string(all_formats[elem].first) == *current_format_specs) {
+ format = all_formats[elem].second;
+ break;
+ }
+ }
+ }
+
+ BOOST_TEST_I_ASSRT( format != OF_INVALID,
+ boost::runtime::access_to_missing_argument()
+ << "Unable to determine the logger type from '"
+ << *current_config
+ << "'. Possible choices are: "
+ << std::accumulate(all_formats,
+ all_formats + sizeof(all_formats)/sizeof(all_formats[0]),
+ std::string(""),
+ sum_to_first_only())
+ );
+
+ // activates this format
+ if( is_first ) {
+ unit_test_log.set_format( format );
+ }
+ else {
+ unit_test_log.add_format( format );
+ }
+ is_first = false;
+
+ unit_test_log_formatter * const formatter = unit_test_log.get_formatter(format);
+ BOOST_TEST_SETUP_ASSERT( formatter, "Logger setup error" );
+
+ log_level formatter_log_level = invalid_log_level;
+ if( !current_format_specs->size() ) {
+ formatter_log_level = formatter->get_log_level(); // default log level given by the formatter
+ }
+ else if( ++current_format_specs != utils::string_token_iterator() ) {
+
+ for(size_t elem=0; elem < sizeof(all_log_levels)/sizeof(all_log_levels[0]); elem++) {
+ if(const_string(all_log_levels[elem].first) == *current_format_specs) {
+ formatter_log_level = all_log_levels[elem].second;
+ break;
+ }
+ }
+ }
+
+
+ BOOST_TEST_I_ASSRT( formatter_log_level != invalid_log_level,
+ boost::runtime::access_to_missing_argument()
+ << "Unable to determine the log level from '"
+ << *current_config
+ << "'. Possible choices are: "
+ << std::accumulate(all_log_levels,
+ all_log_levels + sizeof(all_log_levels)/sizeof(all_log_levels[0]),
+ std::string(""),
+ sum_to_first_only())
+ );
+
+ unit_test_log.set_threshold_level( format, formatter_log_level );
+
+ runtime_config::stream_holder& stream_logger = s_frk_state().m_log_sinks[format];
+ if( ++current_format_specs != utils::string_token_iterator() &&
+ current_format_specs->size() ) {
+ stream_logger.setup( *current_format_specs );
+ }
+ else {
+ stream_logger.setup( formatter->get_default_stream_description() );
+ }
+ unit_test_log.set_stream( format, stream_logger.ref() );
+
+ }
+#endif
+ }
+
+ }
+ }
+ BOOST_TEST_I_CATCH( boost::runtime::init_error, ex ) {
+ BOOST_TEST_SETUP_ASSERT( false, ex.msg );
+ }
+ BOOST_TEST_I_CATCH( boost::runtime::input_error, ex ) {
+ std::cerr << ex.msg << "\n\n";
+
+ BOOST_TEST_I_THROW( framework::nothing_to_test( boost::exit_exception_failure ) );
+ }
+
+
+}
+
//____________________________________________________________________________//
} // namespace impl
@@ -829,15 +1014,14 @@ init( init_unit_test_func init_func, int argc, char* argv[] )
runtime_config::init( argc, argv );
// 20. Set the desired log level, format and sink
- unit_test_log.set_threshold_level( runtime_config::get<log_level>( runtime_config::LOG_LEVEL ) );
- unit_test_log.set_format( runtime_config::get<output_format>( runtime_config::LOG_FORMAT ) );
- s_frk_state().m_log_sink.setup( runtime_config::LOG_SINK );
- unit_test_log.set_stream( s_frk_state().m_log_sink.ref() );
+ impl::setup_loggers();
// 30. Set the desired report level, format and sink
results_reporter::set_level( runtime_config::get<report_level>( runtime_config::REPORT_LEVEL ) );
results_reporter::set_format( runtime_config::get<output_format>( runtime_config::REPORT_FORMAT ) );
- s_frk_state().m_report_sink.setup( runtime_config::REPORT_SINK );
+
+ if( runtime_config::has( runtime_config::REPORT_SINK ) )
+ s_frk_state().m_report_sink.setup( runtime_config::get<const_string>( runtime_config::REPORT_SINK ) );
results_reporter::set_stream( s_frk_state().m_report_sink.ref() );
// 40. Register default test observers
@@ -845,7 +1029,7 @@ init( init_unit_test_func init_func, int argc, char* argv[] )
register_observer( unit_test_log );
if( runtime_config::get<bool>( runtime_config::SHOW_PROGRESS ) ) {
- progress_monitor.set_stream( s_frk_state().m_log_sink.ref() );
+ progress_monitor.set_stream( std::cout ); // defaults to stdout
register_observer( progress_monitor );
}
@@ -1235,7 +1419,7 @@ run( test_unit_id id, bool continue_test )
case 1:
seed = static_cast<unsigned>( std::rand() ^ std::time( 0 ) ); // better init using std::rand() ^ ...
default:
- BOOST_TEST_MESSAGE( "Test cases order is shuffled using seed: " << seed );
+ BOOST_TEST_FRAMEWORK_MESSAGE( "Test cases order is shuffled using seed: " << seed );
std::srand( seed );
}
diff --git a/boost/test/impl/junit_log_formatter.ipp b/boost/test/impl/junit_log_formatter.ipp
new file mode 100644
index 0000000000..a07ee5e2b0
--- /dev/null
+++ b/boost/test/impl/junit_log_formatter.ipp
@@ -0,0 +1,627 @@
+// (C) Copyright 2016 Raffi Enficiaud.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+// See http://www.boost.org/libs/test for the library home page.
+//
+///@file
+///@brief Contains the implementatoin of the Junit log formatter (OF_JUNIT)
+// ***************************************************************************
+
+#ifndef BOOST_TEST_JUNIT_LOG_FORMATTER_IPP__
+#define BOOST_TEST_JUNIT_LOG_FORMATTER_IPP__
+
+// Boost.Test
+#include <boost/test/output/junit_log_formatter.hpp>
+#include <boost/test/execution_monitor.hpp>
+#include <boost/test/framework.hpp>
+#include <boost/test/tree/test_unit.hpp>
+#include <boost/test/utils/basic_cstring/io.hpp>
+#include <boost/test/utils/xml_printer.hpp>
+#include <boost/test/utils/string_cast.hpp>
+#include <boost/test/framework.hpp>
+
+#include <boost/test/tree/visitor.hpp>
+#include <boost/test/tree/test_case_counter.hpp>
+#include <boost/test/tree/traverse.hpp>
+#include <boost/test/results_collector.hpp>
+
+#include <boost/test/utils/algorithm.hpp>
+#include <boost/test/utils/string_cast.hpp>
+
+//#include <boost/test/results_reporter.hpp>
+
+
+// Boost
+#include <boost/version.hpp>
+
+// STL
+#include <iostream>
+#include <fstream>
+#include <set>
+
+#include <boost/test/detail/suppress_warnings.hpp>
+
+
+//____________________________________________________________________________//
+
+namespace boost {
+namespace unit_test {
+namespace output {
+
+
+struct s_replace_chars {
+ template <class T>
+ void operator()(T& to_replace)
+ {
+ if(to_replace == '/')
+ to_replace = '.';
+ else if(to_replace == ' ')
+ to_replace = '_';
+ }
+};
+
+inline std::string tu_name_normalize(std::string full_name)
+{
+ // maybe directly using normalize_test_case_name instead?
+ std::for_each(full_name.begin(), full_name.end(), s_replace_chars());
+ return full_name;
+}
+
+const_string file_basename(const_string filename) {
+
+ const_string path_sep( "\\/" );
+ const_string::iterator it = unit_test::utils::find_last_of( filename.begin(), filename.end(),
+ path_sep.begin(), path_sep.end() );
+ if( it != filename.end() )
+ filename.trim_left( it + 1 );
+
+ return filename;
+
+}
+
+// ************************************************************************** //
+// ************** junit_log_formatter ************** //
+// ************************************************************************** //
+
+void
+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;
+}
+
+//____________________________________________________________________________//
+
+class junit_result_helper : public test_tree_visitor {
+public:
+ explicit junit_result_helper(
+ std::ostream& stream,
+ test_unit const& ts,
+ junit_log_formatter::map_trace_t const& mt,
+ bool display_build_info )
+ : m_stream(stream)
+ , m_ts( ts )
+ , m_map_test( mt )
+ , m_id( 0 )
+ , m_display_build_info(display_build_info)
+ { }
+
+ void add_log_entry(std::string const& entry_type,
+ test_case const& tc,
+ junit_impl::junit_log_helper::assertion_entry const& log) const
+ {
+ m_stream
+ << "<" << entry_type
+ << " message" << utils::attr_value() << log.logentry_message
+ << " type" << utils::attr_value() << log.logentry_type
+ << ">";
+
+ if(!log.output.empty()) {
+ m_stream << utils::cdata() << "\n" + log.output;
+ }
+
+ m_stream << "</" << entry_type << ">";
+ }
+
+ void visit( test_case const& tc )
+ {
+ test_results const& tr = results_collector.results( tc.p_id );
+
+ junit_impl::junit_log_helper detailed_log;
+ bool need_skipping_reason = false;
+ bool skipped = false;
+
+ 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;
+ }
+ else
+ {
+ need_skipping_reason = true;
+ }
+
+ std::string classname;
+ test_unit_id id(tc.p_parent_id);
+ while( id != m_ts.p_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;
+ }
+
+ // removes the trailing dot
+ if(!classname.empty() && *classname.rbegin() == '.') {
+ classname.erase(classname.size()-1);
+ }
+
+ //
+ // test case header
+
+ // total number of assertions
+ m_stream << "<testcase assertions" << utils::attr_value() << tr.p_assertions_passed + tr.p_assertions_failed;
+
+ // class name
+ 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
+ << ">" << std::endl;
+
+ if( tr.p_skipped || skipped ) {
+ m_stream << "<skipped/>" << std::endl;
+ }
+ else {
+
+ 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_failure) {
+ add_log_entry("failure", tc, *it);
+ }
+ else if(it->log_entry == junit_impl::junit_log_helper::assertion_entry::log_entry_error) {
+ add_log_entry("error", tc, *it);
+ }
+ }
+ }
+
+ // 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;
+ }
+
+ if(!system_out.empty()) {
+ m_stream
+ << "<system-out>"
+ << utils::cdata() << system_out
+ << "</system-out>"
+ << std::endl;
+ }
+
+ // system-err output + test case informations
+ std::string system_err = detailed_log.system_err;
+ {
+ // 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;
+ }
+ 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 )
+ {
+ // unique test suite, without s, nesting not supported in CI
+ if( m_ts.p_id != ts.p_id )
+ 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
+ // << "disabled=\"" << tr.p_test_cases_skipped << "\" "
+ << " tests" << utils::attr_value() << tr.p_test_cases_passed
+ << " skipped" << utils::attr_value() << tr.p_test_cases_skipped
+ << " errors" << utils::attr_value() << tr.p_test_cases_aborted
+ << " failures" << utils::attr_value() << tr.p_test_cases_failed
+ << " id" << utils::attr_value() << m_id++
+ << " name" << utils::attr_value() << tu_name_normalize(ts.p_name)
+ << " time" << utils::attr_value() << (tr.p_duration_microseconds * 1E-6)
+ << ">" << std::endl;
+
+ if(m_display_build_info)
+ {
+ m_stream << "<properties>" << std::endl;
+ m_stream << "<property name=\"platform\" value" << utils::attr_value() << BOOST_PLATFORM << std::endl;
+ m_stream << "<property name=\"compiler\" value" << utils::attr_value() << BOOST_COMPILER << std::endl;
+ m_stream << "<property name=\"stl\" value" << utils::attr_value() << BOOST_STDLIB << std::endl;
+
+ std::ostringstream o;
+ o << BOOST_VERSION/100000 << "." << BOOST_VERSION/100 % 1000 << "." << BOOST_VERSION % 100;
+ m_stream << "<property name=\"boost\" value" << utils::attr_value() << o.str() << std::endl;
+ m_stream << "</properties>" << std::endl;
+ }
+
+ return true; // indicates that the children should also be parsed
+ }
+
+ virtual void test_suite_finish( test_suite const& ts )
+ {
+ if( m_ts.p_id != ts.p_id )
+ return;
+ m_stream << "</testsuite>";
+ }
+
+private:
+ // Data members
+ std::ostream& m_stream;
+ test_unit const& m_ts;
+ junit_log_formatter::map_trace_t const& m_map_test;
+ size_t m_id;
+ bool m_display_build_info;
+};
+
+
+
+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
+
+ return;
+}
+
+//____________________________________________________________________________//
+
+void
+junit_log_formatter::log_build_info( std::ostream& ostr )
+{
+ m_display_build_info = true;
+}
+
+//____________________________________________________________________________//
+
+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
+}
+
+
+
+//____________________________________________________________________________//
+
+void
+junit_log_formatter::test_unit_finish( std::ostream& ostr, test_unit const& tu, unsigned long elapsed )
+{
+ // the time is already stored in the result_reporter
+ assert( tu.p_id == list_path_to_root.back() );
+ list_path_to_root.pop_back();
+}
+
+void
+junit_log_formatter::test_unit_aborted( std::ostream& os, test_unit const& tu )
+{
+ assert( tu.p_id == list_path_to_root.back() );
+ //list_path_to_root.pop_back();
+}
+
+//____________________________________________________________________________//
+
+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());
+ }
+}
+
+//____________________________________________________________________________//
+
+void
+junit_log_formatter::log_exception_start( std::ostream& ostr, log_checkpoint_data const& checkpoint_data, execution_exception const& ex )
+{
+ std::ostringstream o;
+ execution_exception::location const& loc = ex.where();
+
+ 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;
+
+ entry.logentry_message = "unexpected exception";
+ entry.log_entry = junit_impl::junit_log_helper::assertion_entry::log_entry_error;
+
+ 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 << "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;
+
+ o << "\nEXCEPTION STACK TRACE: --------------\n" << ex.what()
+ << "\n-------------------------------------";
+
+ 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
+ ;
+ }
+
+ entry.output = o.str();
+
+ last_entry.assertion_entries.push_back(entry);
+ }
+
+ // check what to do with this one
+}
+
+//____________________________________________________________________________//
+
+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;
+}
+
+//____________________________________________________________________________//
+
+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()];
+ m_is_last_assertion_or_error = true;
+ switch(let)
+ {
+ case unit_test_log_formatter::BOOST_UTL_ET_INFO:
+ case unit_test_log_formatter::BOOST_UTL_ET_MESSAGE:
+ case unit_test_log_formatter::BOOST_UTL_ET_WARNING:
+ {
+ 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";
+
+ o << (let == unit_test_log_formatter::BOOST_UTL_ET_WARNING ?
+ "WARNING:" : (let == unit_test_log_formatter::BOOST_UTL_ET_MESSAGE ?
+ "MESSAGE:" : "INFO:"))
+ << std::endl
+ << "- file : " << file_basename(entry_data.m_file_name) << std::endl
+ << "- line : " << entry_data.m_line_num << std::endl
+ << "- message: "; // no CR
+
+ entry.output += o.str();
+ last_entry.assertion_entries.push_back(entry);
+ break;
+ }
+ default:
+ case unit_test_log_formatter::BOOST_UTL_ET_ERROR:
+ case unit_test_log_formatter::BOOST_UTL_ET_FATAL_ERROR:
+ {
+ std::ostringstream o;
+ junit_impl::junit_log_helper::assertion_entry entry;
+ entry.log_entry = junit_impl::junit_log_helper::assertion_entry::log_entry_failure;
+ entry.logentry_message = "failure";
+ entry.logentry_type = (let == unit_test_log_formatter::BOOST_UTL_ET_ERROR ? "assertion error" : "fatal error");
+
+ o << "ASSERTION FAILURE:" << std::endl
+ << "- file : " << file_basename(entry_data.m_file_name) << std::endl
+ << "- line : " << entry_data.m_line_num << std::endl
+ << "- message: " ; // no CR
+
+ entry.output += o.str();
+ last_entry.assertion_entries.push_back(entry);
+ 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 );
+
+ if(!last_entry.assertion_entries.empty())
+ {
+ junit_impl::junit_log_helper::assertion_entry& log_entry = last_entry.assertion_entries.back();
+ log_entry.output += value;
+ }
+ else
+ {
+ // this may be a message coming from another observer
+ // the prefix is set in the log_entry_start
+ last_entry.system_out += 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
+ }
+}
+
+//____________________________________________________________________________//
+
+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;
+ assert(!v_failure_or_error.back().sealed);
+
+ if(m_is_last_assertion_or_error)
+ {
+ v_failure_or_error.back().output += "\n- context:\n";
+ }
+ else
+ {
+ v_failure_or_error.back().output += "\n\nCONTEXT:\n";
+ }
+}
+
+//____________________________________________________________________________//
+
+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);
+}
+
+//____________________________________________________________________________//
+
+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
+}
+
+//____________________________________________________________________________//
+
+
+std::string
+junit_log_formatter::get_default_stream_description() const {
+ std::string name = framework::master_test_suite().p_name.value;
+
+ static const std::string to_replace[] = { " ", "\"", "/", "\\", ":"};
+ static const std::string replacement[] = { "_", "_" , "_", "_" , "_"};
+
+ name = unit_test::utils::replace_all_occurrences_of(
+ name,
+ to_replace, to_replace + sizeof(to_replace)/sizeof(to_replace[0]),
+ replacement, replacement + sizeof(replacement)/sizeof(replacement[0]));
+
+ std::ifstream check_init((name + ".xml").c_str());
+ if(!check_init)
+ return name + ".xml";
+
+ int index = 0;
+ for(; index < 100; index++) {
+ std::string candidate = name + "_" + utils::string_cast(index) + ".xml";
+ std::ifstream file(candidate.c_str());
+ if(!file)
+ return candidate;
+ }
+
+ return name + ".xml";
+}
+
+} // namespace output
+} // namespace unit_test
+} // namespace boost
+
+#include <boost/test/detail/enable_warnings.hpp>
+
+#endif // BOOST_TEST_junit_log_formatter_IPP_020105GER
diff --git a/boost/test/impl/results_collector.ipp b/boost/test/impl/results_collector.ipp
index 17a31955ea..daee8bc3bf 100644
--- a/boost/test/impl/results_collector.ipp
+++ b/boost/test/impl/results_collector.ipp
@@ -83,6 +83,7 @@ test_results::operator+=( test_results const& tr )
p_test_cases_failed.value += tr.p_test_cases_failed;
p_test_cases_skipped.value += tr.p_test_cases_skipped;
p_test_cases_aborted.value += tr.p_test_cases_aborted;
+ p_duration_microseconds.value += tr.p_duration_microseconds;
}
//____________________________________________________________________________//
@@ -99,6 +100,7 @@ test_results::clear()
p_test_cases_failed.value = 0;
p_test_cases_skipped.value = 0;
p_test_cases_aborted.value = 0;
+ p_duration_microseconds.value= 0;
p_aborted.value = false;
p_skipped.value = false;
}
@@ -184,7 +186,7 @@ private:
//____________________________________________________________________________//
void
-results_collector_t::test_unit_finish( test_unit const& tu, unsigned long )
+results_collector_t::test_unit_finish( test_unit const& tu, unsigned long elapsed_in_microseconds )
{
if( tu.p_type == TUT_SUITE ) {
results_collect_helper ch( s_rc_impl().m_results_store[tu.p_id], tu );
@@ -192,15 +194,16 @@ results_collector_t::test_unit_finish( test_unit const& tu, unsigned long )
traverse_test_tree( tu, ch );
}
else {
- test_results const& tr = s_rc_impl().m_results_store[tu.p_id];
+ test_results & tr = s_rc_impl().m_results_store[tu.p_id];
+ tr.p_duration_microseconds.value = elapsed_in_microseconds;
bool num_failures_match = tr.p_aborted || tr.p_assertions_failed >= tr.p_expected_failures;
if( !num_failures_match )
- BOOST_TEST_MESSAGE( "Test case " << tu.full_name() << " has fewer failures than expected" );
+ BOOST_TEST_FRAMEWORK_MESSAGE( "Test case " << tu.full_name() << " has fewer failures than expected" );
bool check_any_assertions = tr.p_aborted || (tr.p_assertions_failed != 0) || (tr.p_assertions_passed != 0);
if( !check_any_assertions )
- BOOST_TEST_MESSAGE( "Test case " << tu.full_name() << " did not check any assertions" );
+ BOOST_TEST_FRAMEWORK_MESSAGE( "Test case " << tu.full_name() << " did not check any assertions" );
}
}
diff --git a/boost/test/impl/test_tools.ipp b/boost/test/impl/test_tools.ipp
index ed94da3a5b..a6b20a7729 100644
--- a/boost/test/impl/test_tools.ipp
+++ b/boost/test/impl/test_tools.ipp
@@ -29,6 +29,8 @@
#include <boost/test/detail/throw_exception.hpp>
+#include <boost/test/utils/algorithm.hpp>
+
// Boost
#include <boost/config.hpp>
@@ -505,7 +507,7 @@ output_test_stream::output_test_stream( const_string pattern_file_name, bool mat
m_pimpl->m_pattern.open( pattern_file_name.begin(), m );
if( !m_pimpl->m_pattern.is_open() )
- BOOST_TEST_MESSAGE( "Can't open pattern file " << pattern_file_name << " for " << (match_or_save ? "reading" : "writing") );
+ BOOST_TEST_FRAMEWORK_MESSAGE( "Can't open pattern file " << pattern_file_name << " for " << (match_or_save ? "reading" : "writing") );
}
m_pimpl->m_match_or_save = match_or_save;
@@ -572,57 +574,163 @@ output_test_stream::is_equal( const_string arg, bool flush_stream )
//____________________________________________________________________________//
+std::string pretty_print_log(std::string str) {
+
+ static const std::string to_replace[] = { "\r", "\n" };
+ static const std::string replacement[] = { "\\r", "\\n" };
+
+ return unit_test::utils::replace_all_occurrences_of(
+ str,
+ to_replace, to_replace + sizeof(to_replace)/sizeof(to_replace[0]),
+ replacement, replacement + sizeof(replacement)/sizeof(replacement[0]));
+}
+
assertion_result
output_test_stream::match_pattern( bool flush_stream )
{
+ const std::string::size_type n_chars_presuffix = 10;
sync();
assertion_result result( true );
+ const std::string stream_string_repr = get_stream_string_representation();
+
if( !m_pimpl->m_pattern.is_open() ) {
result = false;
result.message() << "Pattern file can't be opened!";
}
else {
if( m_pimpl->m_match_or_save ) {
- for ( std::string::size_type i = 0; i < m_pimpl->m_synced_string.length(); ++i ) {
+
+ int offset = 0;
+ std::vector<char> last_elements;
+ for ( std::string::size_type i = 0; static_cast<int>(i + offset) < static_cast<int>(stream_string_repr.length()); ++i ) {
char c = m_pimpl->get_char();
- result = !m_pimpl->m_pattern.fail() &&
+ if( last_elements.size() <= n_chars_presuffix ) {
+ last_elements.push_back( c );
+ }
+ else {
+ last_elements[ i % last_elements.size() ] = c;
+ }
+
+ bool is_same = !m_pimpl->m_pattern.fail() &&
!m_pimpl->m_pattern.eof() &&
- (m_pimpl->m_synced_string[i] == c);
+ (stream_string_repr[i+offset] == c);
- if( !result ) {
- std::string::size_type suffix_size = (std::min)( m_pimpl->m_synced_string.length() - i,
- static_cast<std::string::size_type>(5) );
+ if( !is_same ) {
- // try to log area around the mismatch
- result.message() << "Mismatch at position " << i << '\n'
- << "..." << m_pimpl->m_synced_string.substr( i, suffix_size ) << "..." << '\n'
- << "..." << c;
+ result = false;
- std::string::size_type counter = suffix_size;
- while( --counter ) {
+ std::string::size_type prefix_size = (std::min)( i + offset, n_chars_presuffix );
+
+ std::string::size_type suffix_size = (std::min)( stream_string_repr.length() - i - offset,
+ n_chars_presuffix );
+
+ // try to log area around the mismatch
+ std::string substr = stream_string_repr.substr(0, i+offset);
+ std::size_t line = std::count(substr.begin(), substr.end(), '\n');
+ std::size_t column = i + offset - substr.rfind('\n');
+
+ result.message()
+ << "Mismatch at position " << i
+ << " (line " << line
+ << ", column " << column
+ << "): '" << pretty_print_log(std::string(1, stream_string_repr[i+offset])) << "' != '" << pretty_print_log(std::string(1, c)) << "' :\n";
+
+ // we already escape this substring because we need its actual size for the pretty print
+ // of the difference location.
+ std::string sub_str_prefix(pretty_print_log(stream_string_repr.substr( i + offset - prefix_size, prefix_size )));
+
+ // we need this substring as is because we compute the best matching substrings on it.
+ std::string sub_str_suffix(stream_string_repr.substr( i + offset, suffix_size));
+ result.message() << "... " << sub_str_prefix + pretty_print_log(sub_str_suffix) << " ..." << '\n';
+
+ result.message() << "... ";
+ for( std::size_t j = 0; j < last_elements.size() ; j++ )
+ result.message() << pretty_print_log(std::string(1, last_elements[(i + j + 1) % last_elements.size()]));
+
+ std::vector<char> last_elements_ordered;
+ last_elements_ordered.push_back(c);
+ for( std::string::size_type counter = 0; counter < suffix_size - 1 ; counter++ ) {
char c2 = m_pimpl->get_char();
if( m_pimpl->m_pattern.fail() || m_pimpl->m_pattern.eof() )
break;
- result.message() << c2;
+ result.message() << pretty_print_log(std::string(1, c2));
+
+ last_elements_ordered.push_back(c2);
+ }
+
+ // tries to find the best substring matching in the remainder of the
+ // two strings
+ std::size_t max_nb_char_in_common = 0;
+ std::size_t best_pattern_start_index = 0;
+ std::size_t best_stream_start_index = 0;
+ for( std::size_t pattern_start_index = best_pattern_start_index;
+ pattern_start_index < last_elements_ordered.size();
+ pattern_start_index++ ) {
+ for( std::size_t stream_start_index = best_stream_start_index;
+ stream_start_index < sub_str_suffix.size();
+ stream_start_index++ ) {
+
+ std::size_t max_size = (std::min)( last_elements_ordered.size() - pattern_start_index, sub_str_suffix.size() - stream_start_index );
+ if( max_nb_char_in_common > max_size )
+ break; // safely break to go to the outer loop
+
+ std::size_t nb_char_in_common = 0;
+ for( std::size_t k = 0; k < max_size; k++) {
+ if( last_elements_ordered[pattern_start_index + k] == sub_str_suffix[stream_start_index + k] )
+ nb_char_in_common ++;
+ else
+ break; // we take fully macthing substring only
+ }
+
+ if( nb_char_in_common > max_nb_char_in_common ) {
+ max_nb_char_in_common = nb_char_in_common;
+ best_pattern_start_index = pattern_start_index;
+ best_stream_start_index = stream_start_index;
+ }
+ }
+ }
+
+ // indicates with more precision the location of the mismatchs in ascii arts ...
+ result.message() << " ...\n... ";
+ for( std::string::size_type j = 0; j < sub_str_prefix.size(); j++) {
+ result.message() << ' ';
+ }
+
+ for( std::size_t k = 0; k < (std::max)(best_pattern_start_index, best_stream_start_index); k++ ) { // 1 is for the current char c
+ std::string s1(pretty_print_log(std::string(1, last_elements_ordered[(std::min)(k, best_pattern_start_index)])));
+ std::string s2(pretty_print_log(std::string(1, sub_str_suffix[(std::min)(k, best_stream_start_index)])));
+ for( int h = (std::max)(s1.size(), s2.size()); h > 0; h--)
+ result.message() << "~";
}
+ result.message() << "\n";
- result.message() << "...";
+ // first char is a replicat of c, so we do not copy it.
+ for(std::string::size_type counter = 0; counter < last_elements_ordered.size() - 1 ; counter++)
+ last_elements[ (i + 1 + counter) % last_elements.size() ] = last_elements_ordered[counter + 1];
+
+ i += last_elements_ordered.size()-1;
+ offset += best_stream_start_index - best_pattern_start_index;
- // skip rest of the bytes. May help for further matching
- m_pimpl->m_pattern.ignore(
- static_cast<std::streamsize>( m_pimpl->m_synced_string.length() - i - suffix_size) );
- break;
}
+
+ }
+
+ // not needed anymore
+ /*
+ if(offset > 0 && false) {
+ m_pimpl->m_pattern.ignore(
+ static_cast<std::streamsize>( offset ));
}
+ */
}
else {
- m_pimpl->m_pattern.write( m_pimpl->m_synced_string.c_str(),
- static_cast<std::streamsize>( m_pimpl->m_synced_string.length() ) );
+ m_pimpl->m_pattern.write( stream_string_repr.c_str(),
+ static_cast<std::streamsize>( stream_string_repr.length() ) );
m_pimpl->m_pattern.flush();
}
}
@@ -647,6 +755,12 @@ output_test_stream::flush()
#endif
}
+
+std::string
+output_test_stream::get_stream_string_representation() const {
+ return m_pimpl->m_synced_string;
+}
+
//____________________________________________________________________________//
std::size_t
diff --git a/boost/test/impl/unit_test_log.ipp b/boost/test/impl/unit_test_log.ipp
index 4c9ac40691..5f3fa6510d 100644
--- a/boost/test/impl/unit_test_log.ipp
+++ b/boost/test/impl/unit_test_log.ipp
@@ -23,12 +23,14 @@
#include <boost/test/unit_test_parameters.hpp>
#include <boost/test/utils/basic_cstring/compare.hpp>
+#include <boost/test/utils/foreach.hpp>
#include <boost/test/output/compiler_log_formatter.hpp>
#include <boost/test/output/xml_log_formatter.hpp>
+#include <boost/test/output/junit_log_formatter.hpp>
// Boost
-#include <boost/scoped_ptr.hpp>
+#include <boost/shared_ptr.hpp>
#include <boost/io/ios_state.hpp>
typedef ::boost::io::ios_base_all_saver io_saver_type;
@@ -81,38 +83,68 @@ entry_value_collector::~entry_value_collector()
namespace {
+// log data
+struct unit_test_log_data_helper_impl {
+ typedef boost::shared_ptr<unit_test_log_formatter> formatter_ptr;
+ typedef boost::shared_ptr<io_saver_type> saver_ptr;
+
+ bool m_enabled;
+ output_format m_format;
+ std::ostream* m_stream;
+ saver_ptr m_stream_state_saver;
+ formatter_ptr m_log_formatter;
+ bool m_entry_in_progress;
+
+ unit_test_log_data_helper_impl(unit_test_log_formatter* p_log_formatter, output_format format, bool enabled = false)
+ : m_enabled( enabled )
+ , m_format( format )
+ , m_stream( &std::cout )
+ , m_stream_state_saver( new io_saver_type( std::cout ) )
+ , m_log_formatter()
+ , m_entry_in_progress( false )
+ {
+ m_log_formatter.reset(p_log_formatter);
+ m_log_formatter->set_log_level(log_all_errors);
+ }
+
+ // helper functions
+ std::ostream& stream()
+ {
+ return *m_stream;
+ }
+
+ log_level get_log_level() const
+ {
+ return m_log_formatter->get_log_level();
+ }
+};
+
struct unit_test_log_impl {
// Constructor
unit_test_log_impl()
- : m_stream( &std::cout )
- , m_stream_state_saver( new io_saver_type( std::cout ) )
- , m_threshold_level( log_all_errors )
- , m_log_formatter( new output::compiler_log_formatter )
{
+ m_log_formatter_data.push_back( unit_test_log_data_helper_impl(new output::compiler_log_formatter, OF_CLF, true) ); // only this one is active by default,
+ m_log_formatter_data.push_back( unit_test_log_data_helper_impl(new output::xml_log_formatter, OF_XML, false) );
+ m_log_formatter_data.push_back( unit_test_log_data_helper_impl(new output::junit_log_formatter, OF_JUNIT, false) );
}
- // log data
- typedef scoped_ptr<unit_test_log_formatter> formatter_ptr;
- typedef scoped_ptr<io_saver_type> saver_ptr;
-
- std::ostream* m_stream;
- saver_ptr m_stream_state_saver;
- log_level m_threshold_level;
- formatter_ptr m_log_formatter;
+ typedef std::vector<unit_test_log_data_helper_impl> v_formatter_data_t;
+ v_formatter_data_t m_log_formatter_data;
// entry data
- bool m_entry_in_progress;
- bool m_entry_started;
log_entry_data m_entry_data;
+ bool has_entry_in_progress() const {
+ BOOST_TEST_FOREACH( unit_test_log_data_helper_impl const&, current_logger_data, m_log_formatter_data ) {
+ if( current_logger_data.m_entry_in_progress )
+ return true;
+ }
+ return false;
+ }
+
// check point data
log_checkpoint_data m_checkpoint_data;
- // helper functions
- std::ostream& stream()
- {
- return *m_stream;
- }
void set_checkpoint( const_string file, std::size_t line_num, const_string msg )
{
assign_op( m_checkpoint_data.m_message, msg, 0 );
@@ -130,15 +162,17 @@ unit_test_log_impl& s_log_impl() { static unit_test_log_impl the_inst; return th
void
unit_test_log_t::test_start( counter_t test_cases_amount )
{
- if( s_log_impl().m_threshold_level == log_nothing )
- return;
+ BOOST_TEST_FOREACH( unit_test_log_data_helper_impl&, current_logger_data, s_log_impl().m_log_formatter_data ) {
+ if( !current_logger_data.m_enabled || current_logger_data.get_log_level() == log_nothing )
+ continue;
- s_log_impl().m_log_formatter->log_start( s_log_impl().stream(), test_cases_amount );
+ current_logger_data.m_log_formatter->log_start( current_logger_data.stream(), test_cases_amount );
- if( runtime_config::get<bool>( runtime_config::BUILD_INFO ) )
- s_log_impl().m_log_formatter->log_build_info( s_log_impl().stream() );
+ if( runtime_config::get<bool>( runtime_config::BUILD_INFO ) )
+ current_logger_data.m_log_formatter->log_build_info( current_logger_data.stream() );
- s_log_impl().m_entry_in_progress = false;
+ current_logger_data.m_entry_in_progress = false;
+ }
}
//____________________________________________________________________________//
@@ -146,12 +180,14 @@ unit_test_log_t::test_start( counter_t test_cases_amount )
void
unit_test_log_t::test_finish()
{
- if( s_log_impl().m_threshold_level == log_nothing )
- return;
+ BOOST_TEST_FOREACH( unit_test_log_data_helper_impl&, current_logger_data, s_log_impl().m_log_formatter_data ) {
+ if( !current_logger_data.m_enabled || current_logger_data.get_log_level() == log_nothing )
+ continue;
- s_log_impl().m_log_formatter->log_finish( s_log_impl().stream() );
+ current_logger_data.m_log_formatter->log_finish( current_logger_data.stream() );
- s_log_impl().stream().flush();
+ current_logger_data.stream().flush();
+ }
}
//____________________________________________________________________________//
@@ -167,13 +203,13 @@ unit_test_log_t::test_aborted()
void
unit_test_log_t::test_unit_start( test_unit const& tu )
{
- if( s_log_impl().m_threshold_level > log_test_units )
- return;
-
- if( s_log_impl().m_entry_in_progress )
+ if( s_log_impl().has_entry_in_progress() )
*this << log::end();
-
- s_log_impl().m_log_formatter->test_unit_start( s_log_impl().stream(), tu );
+ BOOST_TEST_FOREACH( unit_test_log_data_helper_impl&, current_logger_data, s_log_impl().m_log_formatter_data ) {
+ if( !current_logger_data.m_enabled || current_logger_data.get_log_level() > log_test_units )
+ continue;
+ current_logger_data.m_log_formatter->test_unit_start( current_logger_data.stream(), tu );
+ }
}
//____________________________________________________________________________//
@@ -181,15 +217,18 @@ unit_test_log_t::test_unit_start( test_unit const& tu )
void
unit_test_log_t::test_unit_finish( test_unit const& tu, unsigned long elapsed )
{
- if( s_log_impl().m_threshold_level > log_test_units )
- return;
-
s_log_impl().m_checkpoint_data.clear();
- if( s_log_impl().m_entry_in_progress )
+ if( s_log_impl().has_entry_in_progress() )
*this << log::end();
- s_log_impl().m_log_formatter->test_unit_finish( s_log_impl().stream(), tu, elapsed );
+ BOOST_TEST_FOREACH( unit_test_log_data_helper_impl&, current_logger_data, s_log_impl().m_log_formatter_data ) {
+
+ if( !current_logger_data.m_enabled || current_logger_data.get_log_level() > log_test_units )
+ continue;
+
+ current_logger_data.m_log_formatter->test_unit_finish( current_logger_data.stream(), tu, elapsed );
+ }
}
//____________________________________________________________________________//
@@ -197,13 +236,29 @@ unit_test_log_t::test_unit_finish( test_unit const& tu, unsigned long elapsed )
void
unit_test_log_t::test_unit_skipped( test_unit const& tu, const_string reason )
{
- if( s_log_impl().m_threshold_level > log_test_units )
- return;
+ if( s_log_impl().has_entry_in_progress() )
+ *this << log::end();
+
+ BOOST_TEST_FOREACH( unit_test_log_data_helper_impl&, current_logger_data, s_log_impl().m_log_formatter_data ) {
+ if( !current_logger_data.m_enabled || current_logger_data.get_log_level() > log_test_units )
+ continue;
- if( s_log_impl().m_entry_in_progress )
+ current_logger_data.m_log_formatter->test_unit_skipped( current_logger_data.stream(), tu, reason );
+ }
+}
+
+void
+unit_test_log_t::test_unit_aborted( test_unit const& tu )
+{
+ if( s_log_impl().has_entry_in_progress() )
*this << log::end();
- s_log_impl().m_log_formatter->test_unit_skipped( s_log_impl().stream(), tu, reason );
+ BOOST_TEST_FOREACH( unit_test_log_data_helper_impl&, current_logger_data, s_log_impl().m_log_formatter_data ) {
+ if( !current_logger_data.m_enabled || current_logger_data.get_log_level() > log_test_units )
+ continue;
+
+ current_logger_data.m_log_formatter->test_unit_aborted(current_logger_data.stream(), tu );
+ }
}
//____________________________________________________________________________//
@@ -216,17 +271,20 @@ unit_test_log_t::exception_caught( execution_exception const& ex )
(ex.code() <= execution_exception::timeout_error ? log_system_errors
: log_fatal_errors );
- if( l >= s_log_impl().m_threshold_level ) {
- if( s_log_impl().m_entry_in_progress )
- *this << log::end();
+ if( s_log_impl().has_entry_in_progress() )
+ *this << log::end();
- s_log_impl().m_log_formatter->log_exception_start( s_log_impl().stream(), s_log_impl().m_checkpoint_data, ex );
+ BOOST_TEST_FOREACH( unit_test_log_data_helper_impl&, current_logger_data, s_log_impl().m_log_formatter_data ) {
- log_entry_context( l );
+ if( current_logger_data.m_enabled && l >= current_logger_data.get_log_level() ) {
- s_log_impl().m_log_formatter->log_exception_finish( s_log_impl().stream() );
- }
+ current_logger_data.m_log_formatter->log_exception_start( current_logger_data.stream(), s_log_impl().m_checkpoint_data, ex );
+
+ log_entry_context( l );
+ current_logger_data.m_log_formatter->log_exception_finish( current_logger_data.stream() );
+ }
+ }
clear_entry_context();
}
@@ -249,10 +307,14 @@ set_unix_slash( char in )
unit_test_log_t&
unit_test_log_t::operator<<( log::begin const& b )
{
- if( s_log_impl().m_entry_in_progress )
+ if( s_log_impl().has_entry_in_progress() )
*this << log::end();
- s_log_impl().m_stream_state_saver->restore();
+ BOOST_TEST_FOREACH( unit_test_log_data_helper_impl&, current_logger_data, s_log_impl().m_log_formatter_data ) {
+ if( current_logger_data.m_enabled ) {
+ current_logger_data.m_stream_state_saver->restore();
+ }
+ }
s_log_impl().m_entry_data.clear();
@@ -273,12 +335,15 @@ unit_test_log_t::operator<<( log::begin const& b )
unit_test_log_t&
unit_test_log_t::operator<<( log::end const& )
{
- if( s_log_impl().m_entry_in_progress ) {
+ if( s_log_impl().has_entry_in_progress() ) {
log_entry_context( s_log_impl().m_entry_data.m_level );
- s_log_impl().m_log_formatter->log_entry_finish( s_log_impl().stream() );
-
- s_log_impl().m_entry_in_progress = false;
+ BOOST_TEST_FOREACH( unit_test_log_data_helper_impl&, current_logger_data, s_log_impl().m_log_formatter_data ) {
+ if( current_logger_data.m_enabled && current_logger_data.m_entry_in_progress ) {
+ current_logger_data.m_log_formatter->log_entry_finish( current_logger_data.stream() );
+ }
+ current_logger_data.m_entry_in_progress = false;
+ }
}
clear_entry_context();
@@ -309,43 +374,53 @@ unit_test_log_t::operator()( log_level l )
//____________________________________________________________________________//
bool
-unit_test_log_t::log_entry_start()
+unit_test_log_t::log_entry_start(output_format log_format)
{
- if( s_log_impl().m_entry_in_progress )
+ BOOST_TEST_FOREACH( unit_test_log_data_helper_impl&, current_logger_data, s_log_impl().m_log_formatter_data ) {
+
+ if( current_logger_data.m_format != log_format )
+ continue;
+
+ if( current_logger_data.m_entry_in_progress )
+ return true;
+
+ if( !current_logger_data.m_enabled )
+ return false;
+
+ switch( s_log_impl().m_entry_data.m_level ) {
+ case log_successful_tests:
+ current_logger_data.m_log_formatter->log_entry_start( current_logger_data.stream(), s_log_impl().m_entry_data,
+ unit_test_log_formatter::BOOST_UTL_ET_INFO );
+ break;
+ case log_messages:
+ current_logger_data.m_log_formatter->log_entry_start( current_logger_data.stream(), s_log_impl().m_entry_data,
+ unit_test_log_formatter::BOOST_UTL_ET_MESSAGE );
+ break;
+ case log_warnings:
+ current_logger_data.m_log_formatter->log_entry_start( current_logger_data.stream(), s_log_impl().m_entry_data,
+ unit_test_log_formatter::BOOST_UTL_ET_WARNING );
+ break;
+ case log_all_errors:
+ case log_cpp_exception_errors:
+ case log_system_errors:
+ current_logger_data.m_log_formatter->log_entry_start( current_logger_data.stream(), s_log_impl().m_entry_data,
+ unit_test_log_formatter::BOOST_UTL_ET_ERROR );
+ break;
+ case log_fatal_errors:
+ current_logger_data.m_log_formatter->log_entry_start( current_logger_data.stream(), s_log_impl().m_entry_data,
+ unit_test_log_formatter::BOOST_UTL_ET_FATAL_ERROR );
+ break;
+ case log_nothing:
+ case log_test_units:
+ case invalid_log_level:
+ return false;
+ }
+
+ current_logger_data.m_entry_in_progress = true;
return true;
-
- switch( s_log_impl().m_entry_data.m_level ) {
- case log_successful_tests:
- s_log_impl().m_log_formatter->log_entry_start( s_log_impl().stream(), s_log_impl().m_entry_data,
- unit_test_log_formatter::BOOST_UTL_ET_INFO );
- break;
- case log_messages:
- s_log_impl().m_log_formatter->log_entry_start( s_log_impl().stream(), s_log_impl().m_entry_data,
- unit_test_log_formatter::BOOST_UTL_ET_MESSAGE );
- break;
- case log_warnings:
- s_log_impl().m_log_formatter->log_entry_start( s_log_impl().stream(), s_log_impl().m_entry_data,
- unit_test_log_formatter::BOOST_UTL_ET_WARNING );
- break;
- case log_all_errors:
- case log_cpp_exception_errors:
- case log_system_errors:
- s_log_impl().m_log_formatter->log_entry_start( s_log_impl().stream(), s_log_impl().m_entry_data,
- unit_test_log_formatter::BOOST_UTL_ET_ERROR );
- break;
- case log_fatal_errors:
- s_log_impl().m_log_formatter->log_entry_start( s_log_impl().stream(), s_log_impl().m_entry_data,
- unit_test_log_formatter::BOOST_UTL_ET_FATAL_ERROR );
- break;
- case log_nothing:
- case log_test_units:
- case invalid_log_level:
- return false;
}
- s_log_impl().m_entry_in_progress = true;
-
- return true;
+ return false;
}
//____________________________________________________________________________//
@@ -353,9 +428,11 @@ unit_test_log_t::log_entry_start()
unit_test_log_t&
unit_test_log_t::operator<<( const_string value )
{
- if( s_log_impl().m_entry_data.m_level >= s_log_impl().m_threshold_level && !value.empty() && log_entry_start() )
- s_log_impl().m_log_formatter->log_entry_value( s_log_impl().stream(), value );
+ BOOST_TEST_FOREACH( unit_test_log_data_helper_impl&, current_logger_data, s_log_impl().m_log_formatter_data ) {
+ if( current_logger_data.m_enabled && s_log_impl().m_entry_data.m_level >= current_logger_data.get_log_level() && !value.empty() && log_entry_start(current_logger_data.m_format) )
+ current_logger_data.m_log_formatter->log_entry_value( current_logger_data.stream(), value );
+ }
return *this;
}
@@ -364,9 +441,10 @@ unit_test_log_t::operator<<( const_string value )
unit_test_log_t&
unit_test_log_t::operator<<( lazy_ostream const& value )
{
- if( s_log_impl().m_entry_data.m_level >= s_log_impl().m_threshold_level && !value.empty() && log_entry_start() )
- s_log_impl().m_log_formatter->log_entry_value( s_log_impl().stream(), value );
-
+ BOOST_TEST_FOREACH( unit_test_log_data_helper_impl&, current_logger_data, s_log_impl().m_log_formatter_data ) {
+ if( current_logger_data.m_enabled && s_log_impl().m_entry_data.m_level >= current_logger_data.get_log_level() && !value.empty() && log_entry_start(current_logger_data.m_format) )
+ current_logger_data.m_log_formatter->log_entry_value( current_logger_data.stream(), value );
+ }
return *this;
}
@@ -381,12 +459,26 @@ unit_test_log_t::log_entry_context( log_level l )
const_string frame;
- s_log_impl().m_log_formatter->entry_context_start( s_log_impl().stream(), l );
+ BOOST_TEST_FOREACH( unit_test_log_data_helper_impl&, current_logger_data, s_log_impl().m_log_formatter_data ) {
+ if( current_logger_data.m_enabled ) {
+ current_logger_data.m_log_formatter->entry_context_start( current_logger_data.stream(), l );
+ }
+ }
while( !(frame=context.next()).is_empty() )
- s_log_impl().m_log_formatter->log_entry_context( s_log_impl().stream(), frame );
+ {
+ BOOST_TEST_FOREACH( unit_test_log_data_helper_impl&, current_logger_data, s_log_impl().m_log_formatter_data ) {
+ if( current_logger_data.m_enabled ) {
+ current_logger_data.m_log_formatter->log_entry_context( current_logger_data.stream(), frame );
+ }
+ }
+ }
- s_log_impl().m_log_formatter->entry_context_finish( s_log_impl().stream() );
+ BOOST_TEST_FOREACH( unit_test_log_data_helper_impl&, current_logger_data, s_log_impl().m_log_formatter_data ) {
+ if( current_logger_data.m_enabled ) {
+ current_logger_data.m_log_formatter->entry_context_finish( current_logger_data.stream() );
+ }
+ }
}
//____________________________________________________________________________//
@@ -402,11 +494,30 @@ unit_test_log_t::clear_entry_context()
void
unit_test_log_t::set_stream( std::ostream& str )
{
- if( s_log_impl().m_entry_in_progress )
+ if( s_log_impl().has_entry_in_progress() )
+ return;
+
+ BOOST_TEST_FOREACH( unit_test_log_data_helper_impl&, current_logger_data, s_log_impl().m_log_formatter_data ) {
+ current_logger_data.m_stream = &str;
+ current_logger_data.m_stream_state_saver.reset( new io_saver_type( str ) );
+ }
+}
+
+//____________________________________________________________________________//
+
+void
+unit_test_log_t::set_stream( output_format log_format, std::ostream& str )
+{
+ if( s_log_impl().has_entry_in_progress() )
return;
- s_log_impl().m_stream = &str;
- s_log_impl().m_stream_state_saver.reset( new io_saver_type( str ) );
+ BOOST_TEST_FOREACH( unit_test_log_data_helper_impl&, current_logger_data, s_log_impl().m_log_formatter_data ) {
+ if( current_logger_data.m_format == log_format) {
+ current_logger_data.m_stream = &str;
+ current_logger_data.m_stream_state_saver.reset( new io_saver_type( str ) );
+ break;
+ }
+ }
}
//____________________________________________________________________________//
@@ -414,10 +525,28 @@ unit_test_log_t::set_stream( std::ostream& str )
void
unit_test_log_t::set_threshold_level( log_level lev )
{
- if( s_log_impl().m_entry_in_progress || lev == invalid_log_level )
+ if( s_log_impl().has_entry_in_progress() || lev == invalid_log_level )
+ return;
+
+ BOOST_TEST_FOREACH( unit_test_log_data_helper_impl&, current_logger_data, s_log_impl().m_log_formatter_data ) {
+ current_logger_data.m_log_formatter->set_log_level( lev );
+ }
+}
+
+//____________________________________________________________________________//
+
+void
+unit_test_log_t::set_threshold_level( output_format log_format, log_level lev )
+{
+ if( s_log_impl().has_entry_in_progress() || lev == invalid_log_level )
return;
- s_log_impl().m_threshold_level = lev;
+ BOOST_TEST_FOREACH( unit_test_log_data_helper_impl&, current_logger_data, s_log_impl().m_log_formatter_data ) {
+ if( current_logger_data.m_format == log_format) {
+ current_logger_data.m_log_formatter->set_log_level( lev );
+ break;
+ }
+ }
}
//____________________________________________________________________________//
@@ -425,26 +554,90 @@ unit_test_log_t::set_threshold_level( log_level lev )
void
unit_test_log_t::set_format( output_format log_format )
{
- if( s_log_impl().m_entry_in_progress )
+ if( s_log_impl().has_entry_in_progress() )
return;
- switch( log_format ) {
- default:
- case OF_CLF:
- set_formatter( new output::compiler_log_formatter );
- break;
- case OF_XML:
- set_formatter( new output::xml_log_formatter );
- break;
+ BOOST_TEST_FOREACH( unit_test_log_data_helper_impl&, current_logger_data, s_log_impl().m_log_formatter_data ) {
+ current_logger_data.m_enabled = current_logger_data.m_format == log_format;
}
}
//____________________________________________________________________________//
void
+unit_test_log_t::add_format( output_format log_format )
+{
+ if( s_log_impl().has_entry_in_progress() )
+ return;
+
+ BOOST_TEST_FOREACH( unit_test_log_data_helper_impl&, current_logger_data, s_log_impl().m_log_formatter_data ) {
+ if( current_logger_data.m_format == log_format) {
+ current_logger_data.m_enabled = true;
+ break;
+ }
+ }
+}
+
+//____________________________________________________________________________//
+
+unit_test_log_formatter*
+unit_test_log_t::get_formatter( output_format log_format ) {
+ BOOST_TEST_FOREACH( unit_test_log_data_helper_impl&, current_logger_data, s_log_impl().m_log_formatter_data ) {
+ if( current_logger_data.m_format == log_format) {
+ return current_logger_data.m_log_formatter.get();
+ }
+ }
+ return 0;
+}
+
+
+void
+unit_test_log_t::add_formatter( unit_test_log_formatter* the_formatter )
+{
+ // remove only user defined logger
+ for(unit_test_log_impl::v_formatter_data_t::iterator it(s_log_impl().m_log_formatter_data.begin()),
+ ite(s_log_impl().m_log_formatter_data.end());
+ it != ite;
+ ++it)
+ {
+ if( it->m_format == OF_CUSTOM_LOGGER) {
+ s_log_impl().m_log_formatter_data.erase(it);
+ break;
+ }
+ }
+
+ if( the_formatter ) {
+ s_log_impl().m_log_formatter_data.push_back( unit_test_log_data_helper_impl(the_formatter, OF_CUSTOM_LOGGER, true) );
+ }
+}
+
+void
unit_test_log_t::set_formatter( unit_test_log_formatter* the_formatter )
{
- s_log_impl().m_log_formatter.reset( the_formatter );
+ // remove only user defined logger
+ log_level current_level = invalid_log_level;
+ std::ostream *current_stream = 0;
+ output_format previous_format = OF_INVALID;
+ for(unit_test_log_impl::v_formatter_data_t::iterator it(s_log_impl().m_log_formatter_data.begin()),
+ ite(s_log_impl().m_log_formatter_data.end());
+ it != ite;
+ ++it)
+ {
+ if( it->m_enabled ) {
+ if( current_level == invalid_log_level || it->m_format < previous_format || it->m_format == OF_CUSTOM_LOGGER) {
+ current_level = it->get_log_level();
+ current_stream = &(it->stream());
+ previous_format = it->m_format;
+ }
+ }
+ }
+
+ if( the_formatter ) {
+ add_formatter(the_formatter);
+ set_format(OF_CUSTOM_LOGGER);
+ set_threshold_level(OF_CUSTOM_LOGGER, current_level);
+ set_stream(OF_CUSTOM_LOGGER, *current_stream);
+ }
}
//____________________________________________________________________________//
@@ -459,6 +652,18 @@ unit_test_log_formatter::log_entry_value( std::ostream& ostr, lazy_ostream const
log_entry_value( ostr, (wrap_stringstream().ref() << value).str() );
}
+void
+unit_test_log_formatter::set_log_level(log_level new_log_level)
+{
+ m_log_level = new_log_level;
+}
+
+log_level
+unit_test_log_formatter::get_log_level() const
+{
+ return m_log_level;
+}
+
//____________________________________________________________________________//
} // namespace unit_test
@@ -467,3 +672,4 @@ unit_test_log_formatter::log_entry_value( std::ostream& ostr, lazy_ostream const
#include <boost/test/detail/enable_warnings.hpp>
#endif // BOOST_TEST_UNIT_TEST_LOG_IPP_012205GER
+
diff --git a/boost/test/impl/unit_test_main.ipp b/boost/test/impl/unit_test_main.ipp
index 1f30c0213e..db61930652 100644
--- a/boost/test/impl/unit_test_main.ipp
+++ b/boost/test/impl/unit_test_main.ipp
@@ -191,7 +191,9 @@ unit_test_main( init_unit_test_func init_func, int argc, char* argv[] )
if( runtime_config::get<bool>( runtime_config::WAIT_FOR_DEBUGGER ) ) {
results_reporter::get_stream() << "Press any key to continue..." << std::endl;
- std::getchar();
+ // getchar is defined as a macro in uClibc. Use parenthesis to fix
+ // gcc bug 58952 for gcc <= 4.8.2.
+ (std::getchar)();
results_reporter::get_stream() << "Continuing..." << std::endl;
}
diff --git a/boost/test/impl/unit_test_parameters.ipp b/boost/test/impl/unit_test_parameters.ipp
index 3ca183e400..f49079ea3e 100644
--- a/boost/test/impl/unit_test_parameters.ipp
+++ b/boost/test/impl/unit_test_parameters.ipp
@@ -24,6 +24,7 @@
#include <boost/test/utils/basic_cstring/basic_cstring.hpp>
#include <boost/test/utils/basic_cstring/compare.hpp>
#include <boost/test/utils/basic_cstring/io.hpp>
+#include <boost/test/utils/iterator/token_iterator.hpp>
#include <boost/test/debug.hpp>
#include <boost/test/framework.hpp>
@@ -69,7 +70,7 @@ namespace rt = boost::runtime;
namespace runtime_config {
-// UTF parameters
+// UTF parameters
std::string AUTO_START_DBG = "auto_start_dbg";
std::string BREAK_EXEC_PATH = "break_exec_path";
std::string BUILD_INFO = "build_info";
@@ -82,6 +83,7 @@ std::string LIST_LABELS = "list_labels";
std::string LOG_FORMAT = "log_format";
std::string LOG_LEVEL = "log_level";
std::string LOG_SINK = "log_sink";
+std::string COMBINED_LOGGER = "logger";
std::string OUTPUT_FORMAT = "output_format";
std::string RANDOM_SEED = "random";
std::string REPORT_FORMAT = "report_format";
@@ -134,9 +136,11 @@ register_parameters( rt::parameters_store& store )
#endif
));
- break_exec_path.add_cla_id( "--", BREAK_EXEC_PATH, "=" );
+ break_exec_path.add_cla_id( "--", BREAK_EXEC_PATH, "=" );
store.add( break_exec_path );
+ ///////////////////////////////////////////////
+
rt::option build_info( BUILD_INFO, (
rt::description = "Displays library build information.",
rt::env_var = "BOOST_TEST_BUILD_INFO",
@@ -162,7 +166,7 @@ register_parameters( rt::parameters_store& store )
#endif
rt::help = "If option " + CATCH_SYS_ERRORS + " has value no the frameworks does not attempt to catch "
"asynchronous system failure events (signals on *NIX platforms or structured exceptions on Windows). "
- " Default value is "
+ " Default value is "
#ifdef BOOST_TEST_DEFAULTS_TO_CORE_DUMP
"no."
#else
@@ -226,8 +230,8 @@ register_parameters( rt::parameters_store& store )
rt::env_var = "BOOST_TEST_LIST_CONTENT",
rt::default_value = OF_INVALID,
rt::optional_value = OF_CLF,
- rt::enum_values<unit_test::output_format>::value =
-#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) && !defined(BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX)
+ rt::enum_values<unit_test::output_format>::value =
+#if defined(BOOST_TEST_CLA_NEW_API)
{
{ "HRF", OF_CLF },
{ "DOT", OF_DOT }
@@ -266,17 +270,19 @@ register_parameters( rt::parameters_store& store )
rt::env_var = "BOOST_TEST_LOG_FORMAT",
rt::default_value = OF_CLF,
rt::enum_values<unit_test::output_format>::value =
-#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) && !defined(BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX)
+#if defined(BOOST_TEST_CLA_NEW_API)
{
{ "HRF", OF_CLF },
{ "CLF", OF_CLF },
- { "XML", OF_XML }
+ { "XML", OF_XML },
+ { "JUNIT", OF_JUNIT },
},
#else
rt::enum_values_list<unit_test::output_format>()
( "HRF", OF_CLF )
( "CLF", OF_CLF )
( "XML", OF_XML )
+ ( "JUNIT", OF_JUNIT )
,
#endif
rt::help = "Parameter " + LOG_FORMAT + " allows to set the frameowrk's log format to one "
@@ -284,7 +290,7 @@ register_parameters( rt::parameters_store& store )
"parameter are the names of the output formats supplied by the framework. By "
"default the framework uses human readable format (HRF) for testing log. This "
"format is similar to compiler error format. Alternatively you can specify XML "
- "as log format. This format is easier to process by testing automation tools."
+ "or JUNIT as log format, which are easier to process by testing automation tools."
));
log_format.add_cla_id( "--", LOG_FORMAT, "=" );
@@ -298,7 +304,7 @@ register_parameters( rt::parameters_store& store )
rt::env_var = "BOOST_TEST_LOG_LEVEL",
rt::default_value = log_all_errors,
rt::enum_values<unit_test::log_level>::value =
-#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) && !defined(BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX)
+#if defined(BOOST_TEST_CLA_NEW_API)
{
{ "all" , log_successful_tests },
{ "success" , log_successful_tests },
@@ -361,7 +367,7 @@ register_parameters( rt::parameters_store& store )
rt::description = "Specifies output format (both log and report).",
rt::env_var = "BOOST_TEST_OUTPUT_FORMAT",
rt::enum_values<unit_test::output_format>::value =
-#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) && !defined(BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX)
+#if defined(BOOST_TEST_CLA_NEW_API)
{
{ "HRF", OF_CLF },
{ "CLF", OF_CLF },
@@ -374,7 +380,7 @@ register_parameters( rt::parameters_store& store )
( "XML", OF_XML )
,
#endif
- rt::help = "Parameter " + OUTPUT_FORMAT + " combines an effect of " + REPORT_FORMAT +
+ rt::help = "Parameter " + OUTPUT_FORMAT + " combines an effect of " + REPORT_FORMAT +
" and " + LOG_FORMAT + " parameters. This parameter has higher priority "
"than either one of them. In other words if this parameter is specified "
"it overrides the value of other two parameters. This parameter does not "
@@ -387,6 +393,19 @@ register_parameters( rt::parameters_store& store )
output_format.add_cla_id( "-", "o", " " );
store.add( output_format );
+ /////////////////////////////////////////////// combined logger option
+
+ rt::parameter<std::string,rt::REPEATABLE_PARAM> combined_logger( COMBINED_LOGGER, (
+ rt::description = "Specifies log level and sink for one or several log format",
+ rt::env_var = "BOOST_TEST_LOGGER",
+ rt::value_hint = "log_format:log_level:log_sink",
+ rt::help = "Parameter " + COMBINED_LOGGER + " allows to specify the logger type, level and sink\n"
+ "in one command."
+ ));
+
+ combined_logger.add_cla_id( "--", COMBINED_LOGGER, "=" );
+ store.add( combined_logger );
+
///////////////////////////////////////////////
rt::parameter<unsigned> random_seed( RANDOM_SEED, (
@@ -416,7 +435,7 @@ register_parameters( rt::parameters_store& store )
rt::env_var = "BOOST_TEST_REPORT_FORMAT",
rt::default_value = OF_CLF,
rt::enum_values<unit_test::output_format>::value =
-#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) && !defined(BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX)
+#if defined(BOOST_TEST_CLA_NEW_API)
{
{ "HRF", OF_CLF },
{ "CLF", OF_CLF },
@@ -448,7 +467,7 @@ register_parameters( rt::parameters_store& store )
rt::env_var = "BOOST_TEST_REPORT_LEVEL",
rt::default_value = CONFIRMATION_REPORT,
rt::enum_values<unit_test::report_level>::value =
-#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) && !defined(BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX)
+#if defined(BOOST_TEST_CLA_NEW_API)
{
{ "confirm", CONFIRMATION_REPORT },
{ "short", SHORT_REPORT },
@@ -531,7 +550,7 @@ register_parameters( rt::parameters_store& store )
"filters', which allow to disable some test units. The __UTF__ also supports "
"enabling/disabling test units at compile time. These settings identify the default "
"set of test units to run. Parameter " + RUN_FILTERS + " is used to change this default. "
- "This parameter is repeatable, so you can specify more than one filter if necessary."
+ "This parameter is repeatable, so you can specify more than one filter if necessary."
));
tests_to_run.add_cla_id( "--", RUN_FILTERS, "=" );
@@ -545,8 +564,8 @@ register_parameters( rt::parameters_store& store )
rt::env_var = "BOOST_TEST_SAVE_PATTERN",
rt::help = "Parameter " + SAVE_TEST_PATTERN + " facilitates switching mode of operation for "
"testing output streams.\n\nThis parameter serves no particular purpose within the "
- "framework itself. It can be used by test modules relying on output_test_stream to "
- "implement testing logic. Default mode is 'match' (false)."
+ "framework itself. It can be used by test modules relying on output_test_stream to "
+ "implement testing logic. Default mode is 'match' (false)."
));
save_test_pattern.add_cla_id( "--", SAVE_TEST_PATTERN, "=" );
@@ -664,6 +683,7 @@ init( int& argc, char** argv )
s_arguments_store.set( REPORT_FORMAT, of );
s_arguments_store.set( LOG_FORMAT, of );
}
+
}
BOOST_TEST_I_CATCH( rt::init_error, ex ) {
BOOST_TEST_SETUP_ASSERT( false, ex.msg );
@@ -689,7 +709,7 @@ init( int& argc, char** argv )
std::cerr << "\n";
parser->usage( std::cerr );
}
-
+
BOOST_TEST_I_THROW( framework::nothing_to_test( boost::exit_exception_failure ) );
}
BOOST_TEST_I_CATCH( rt::input_error, ex ) {
@@ -697,7 +717,7 @@ init( int& argc, char** argv )
if( parser )
parser->usage( std::cerr, ex.param_name );
-
+
BOOST_TEST_I_THROW( framework::nothing_to_test( boost::exit_exception_failure ) );
}
}
diff --git a/boost/test/included/test_exec_monitor.hpp b/boost/test/included/test_exec_monitor.hpp
index cedbcb0ad8..34b6ef44dd 100644
--- a/boost/test/included/test_exec_monitor.hpp
+++ b/boost/test/included/test_exec_monitor.hpp
@@ -14,6 +14,7 @@
#define BOOST_INCLUDED_TEST_EXEC_MONITOR_HPP_071894GER
#include <boost/test/impl/compiler_log_formatter.ipp>
+#include <boost/test/impl/junit_log_formatter.ipp>
#include <boost/test/impl/debug.ipp>
#include <boost/test/impl/decorator.ipp>
#include <boost/test/impl/execution_monitor.ipp>
diff --git a/boost/test/included/unit_test.hpp b/boost/test/included/unit_test.hpp
index 03c0277d6d..8835acd455 100644
--- a/boost/test/included/unit_test.hpp
+++ b/boost/test/included/unit_test.hpp
@@ -13,6 +13,7 @@
#define BOOST_INCLUDED_UNIT_TEST_FRAMEWORK_HPP_071894GER
#include <boost/test/impl/compiler_log_formatter.ipp>
+#include <boost/test/impl/junit_log_formatter.ipp>
#include <boost/test/impl/debug.ipp>
#include <boost/test/impl/decorator.ipp>
#include <boost/test/impl/framework.ipp>
@@ -28,6 +29,7 @@
#include <boost/test/impl/unit_test_monitor.ipp>
#include <boost/test/impl/unit_test_parameters.ipp>
#include <boost/test/impl/xml_log_formatter.ipp>
+#include <boost/test/impl/junit_log_formatter.ipp>
#include <boost/test/impl/xml_report_formatter.ipp>
#define BOOST_TEST_INCLUDED
diff --git a/boost/test/output/compiler_log_formatter.hpp b/boost/test/output/compiler_log_formatter.hpp
index e3f98d7567..cb6172aab6 100644
--- a/boost/test/output/compiler_log_formatter.hpp
+++ b/boost/test/output/compiler_log_formatter.hpp
@@ -5,11 +5,8 @@
// See http://www.boost.org/libs/test for the library home page.
//
-// File : $RCSfile$
-//
-// Version : $Revision$
-//
-// Description : contains compiler like Log formatter definition
+/// @file
+/// @brief Contains the formatter for the Human Readable Format (HRF)
// ***************************************************************************
#ifndef BOOST_TEST_COMPILER_LOG_FORMATTER_HPP_020105GER
@@ -31,6 +28,7 @@ namespace output {
// ************** compiler_log_formatter ************** //
// ************************************************************************** //
+//!@brief Log formatter for the Human Readable Format (HRF) log format
class BOOST_TEST_DECL compiler_log_formatter : public unit_test_log_formatter {
public:
compiler_log_formatter() : m_color_output( false ) {}
diff --git a/boost/test/output/junit_log_formatter.hpp b/boost/test/output/junit_log_formatter.hpp
new file mode 100644
index 0000000000..b6e818e37f
--- /dev/null
+++ b/boost/test/output/junit_log_formatter.hpp
@@ -0,0 +1,135 @@
+// (C) Copyright 2016 Raffi Enficiaud.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+// See http://www.boost.org/libs/test for the library home page.
+//
+///@file
+///@brief Contains the definition of the Junit log formatter (OF_JUNIT)
+// ***************************************************************************
+
+#ifndef BOOST_TEST_JUNIT_LOG_FORMATTER__
+#define BOOST_TEST_JUNIT_LOG_FORMATTER__
+
+// Boost.Test
+#include <boost/test/detail/global_typedef.hpp>
+#include <boost/test/unit_test_log_formatter.hpp>
+#include <boost/test/tree/test_unit.hpp>
+
+//#include <boost/test/results_collector.hpp>
+
+// STL
+#include <cstddef> // std::size_t
+#include <map>
+#include <list>
+
+#include <boost/test/detail/suppress_warnings.hpp>
+
+//____________________________________________________________________________//
+
+namespace boost {
+namespace unit_test {
+namespace output {
+
+
+ namespace junit_impl {
+
+ // helper for the JUnit logger
+ struct junit_log_helper
+ {
+ struct assertion_entry {
+
+ enum log_entry_t {
+ log_entry_info,
+ log_entry_error,
+ log_entry_failure
+ };
+
+ assertion_entry() : sealed(false)
+ {}
+
+ std::string logentry_message;
+ std::string logentry_type; // the one that will get expanded in the final junit (failure, error)
+ std::string output; // additional information/message generated by the assertion
+
+ log_entry_t log_entry; // the type associated to the assertion (or error)
+
+ bool sealed; // indicates if the entry can accept additional information
+ };
+
+ std::string system_out; // sysout: additional information
+ std::string system_err; // syserr: additional information
+
+ // list of failure, errors and messages (assertions message and the full log)
+ std::vector< assertion_entry > assertion_entries;
+
+ };
+ }
+
+// ************************************************************************** //
+// ************** junit_log_formatter ************** //
+// ************************************************************************** //
+
+/// JUnit logger class
+class junit_log_formatter : public unit_test_log_formatter {
+public:
+
+ junit_log_formatter() : m_display_build_info(false)
+ {
+ this->m_log_level = log_successful_tests;
+ }
+
+ // Formatter interface
+ void log_start( std::ostream&, counter_t test_cases_amount );
+ void log_finish( std::ostream& );
+ void log_build_info( std::ostream& );
+
+ void test_unit_start( std::ostream&, test_unit const& tu );
+ void test_unit_finish( std::ostream&, test_unit const& tu, unsigned long elapsed );
+ void test_unit_skipped( std::ostream&, test_unit const& tu, const_string reason );
+ void test_unit_aborted( std::ostream& os, test_unit const& tu );
+
+ void log_exception_start( std::ostream&, log_checkpoint_data const&, execution_exception const& ex );
+ void log_exception_finish( std::ostream& );
+
+ void log_entry_start( std::ostream&, log_entry_data const&, log_entry_types let );
+
+ using unit_test_log_formatter::log_entry_value; // bring base class functions into overload set
+ void log_entry_value( std::ostream&, const_string value );
+ void log_entry_finish( std::ostream& );
+
+ void entry_context_start( std::ostream&, log_level );
+ void log_entry_context( std::ostream&, const_string );
+ void entry_context_finish( std::ostream& );
+
+ //! Discards changes in the log level
+ virtual void set_log_level(log_level )
+ {
+ }
+
+ //! Instead of a regular stream, returns a file name corresponding to
+ //! the current master test suite. If the file already exists, adds an index
+ //! to it.
+ virtual std::string get_default_stream_description() const;
+
+
+private:
+ typedef std::map<test_unit_id, junit_impl::junit_log_helper> map_trace_t;
+ map_trace_t map_tests;
+
+ std::list<test_unit_id> list_path_to_root;
+ test_unit_id root_id;
+ bool m_display_build_info;
+ bool m_is_last_assertion_or_error; // true if failure, false if error
+
+ friend class junit_result_helper;
+};
+
+} // namespace output
+} // namespace unit_test
+} // namespace boost
+
+#include <boost/test/detail/enable_warnings.hpp>
+
+#endif // BOOST_TEST_JUNIT_LOG_FORMATTER__
diff --git a/boost/test/results_collector.hpp b/boost/test/results_collector.hpp
index d12fefb39c..7d2c2bee58 100644
--- a/boost/test/results_collector.hpp
+++ b/boost/test/results_collector.hpp
@@ -5,10 +5,11 @@
// See http://www.boost.org/libs/test for the library home page.
//
-/// @file results_collector.hpp @brief defines testing result collector components
+/// @file
+/// @brief Defines testing result collector components
///
-/// Defines class results_collector_t that is responsible for
-/// gathering test results and class test_results for presenting this information to end-user
+/// Defines classes for keeping track (@ref test_results) and collecting
+/// (@ref results_collector_t) the states of the test units.
// ***************************************************************************
#ifndef BOOST_TEST_RESULTS_COLLECTOR_HPP_071894GER
@@ -44,59 +45,71 @@ inline void first_failed_assertion() {}
// ************************************************************************** //
/// @brief Collection of attributes constituting test unit results
///
-/// This class is a collection of attributes describing testing results. The atributes presented as public properties on
+/// This class is a collection of attributes describing a test result.
+///
+/// The attributes presented as public properties on
/// an instance of the class. In addition summary conclusion methods are presented to generate simple answer to pass/fail question
-// ************************************************************************** //
class BOOST_TEST_DECL test_results {
public:
test_results();
/// Type representing counter like public property
- typedef BOOST_READONLY_PROPERTY( counter_t, (results_collector_t)(test_results)(results_collect_helper) ) counter_prop;
+ typedef BOOST_READONLY_PROPERTY( counter_t, (results_collector_t)
+ (test_results)
+ (results_collect_helper) ) counter_prop;
/// Type representing boolean like public property
- typedef BOOST_READONLY_PROPERTY( bool, (results_collector_t)(test_results)(results_collect_helper) ) bool_prop;
+ typedef BOOST_READONLY_PROPERTY( bool, (results_collector_t)
+ (test_results)
+ (results_collect_helper) ) bool_prop;
- /// @name Public properties
- counter_prop p_assertions_passed;
- counter_prop p_assertions_failed;
- counter_prop p_warnings_failed;
+ counter_prop p_assertions_passed; //!< Number of successful assertions
+ counter_prop p_assertions_failed; //!< Number of failing assertions
+ counter_prop p_warnings_failed; //!< Number of warnings
counter_prop p_expected_failures;
- counter_prop p_test_cases_passed;
- counter_prop p_test_cases_warned;
- counter_prop p_test_cases_failed;
- counter_prop p_test_cases_skipped;
- counter_prop p_test_cases_aborted;
- bool_prop p_aborted;
- bool_prop p_skipped;
- /// @}
-
- /// @name Summary conclusion
+ counter_prop p_test_cases_passed; //!< Number of successfull test cases
+ counter_prop p_test_cases_warned; //!< Number of warnings in test cases
+ counter_prop p_test_cases_failed; //!< Number of failing test cases
+ counter_prop p_test_cases_skipped; //!< Number of skipped test cases
+ counter_prop p_test_cases_aborted; //!< Number of aborted test cases
+ counter_prop p_duration_microseconds; //!< Duration of the test in microseconds
+ bool_prop p_aborted; //!< Indicates that the test unit execution has been aborted
+ bool_prop p_skipped; //!< Indicates that the test unit execution has been skipped
/// Returns true if test unit passed
bool passed() const;
- /// Produces result code for the test unit execution
- /// This methhod return one of the result codes defined in boost/cstdlib.hpp
- /// @returns boost::exit_success on success, boost::exit_exception_failure in case test unit was aborted for any reason
- /// (incuding uncausght exception) and boost::exit_test_failure otherwise
+ /// Produces result code for the test unit execution
+ ///
+ /// This methhod return one of the result codes defined in @c boost/cstdlib.hpp
+ /// @returns
+ /// - @c boost::exit_success on success,
+ /// - @c boost::exit_exception_failure in case test unit
+ /// was aborted for any reason (incuding uncaught exception)
+ /// - and @c boost::exit_test_failure otherwise
int result_code() const;
- /// @}
- // collection helper
+ //! Combines the results of the current instance with another
+ //!
+ //! Only the counters are updated and the @c p_aborted and @c p_skipped are left unchanged.
void operator+=( test_results const& );
+ //! Resets the current state of the result
void clear();
};
// ************************************************************************** //
-/// This class implements test observer interface to collect the result of test unit execution
-// ************************************************************************** //
-
+/// @brief Collects and combines the test results
+///
+/// This class collects and combines the results of the test unit during the execution of the
+/// test tree. The results_collector_t::results() function combines the test results on a subtree
+/// of the test tree.
+///
+/// @see boost::unit_test::test_observer
class BOOST_TEST_DECL results_collector_t : public test_observer, public singleton<results_collector_t> {
public:
- virtual void test_start( counter_t test_cases_amount );
+ virtual void test_start( counter_t );
virtual void test_unit_start( test_unit const& );
virtual void test_unit_finish( test_unit const&, unsigned long );
@@ -109,7 +122,7 @@ public:
virtual int priority() { return 2; }
/// Results access per test unit
-
+ ///
/// @param[in] tu_id id of a test unit
test_results const& results( test_unit_id tu_id ) const;
@@ -125,4 +138,3 @@ BOOST_TEST_SINGLETON_INST( results_collector )
#include <boost/test/detail/enable_warnings.hpp>
#endif // BOOST_TEST_RESULTS_COLLECTOR_HPP_071894GER
-
diff --git a/boost/test/tools/output_test_stream.hpp b/boost/test/tools/output_test_stream.hpp
index 02d3715e9d..2abbf7b521 100644
--- a/boost/test/tools/output_test_stream.hpp
+++ b/boost/test/tools/output_test_stream.hpp
@@ -39,10 +39,10 @@ class BOOST_TEST_DECL output_test_stream : public wrap_stringstream::wrapped_str
public:
//! Constructor
//!
- //!@param[in] pattern_file_name indicates the name of the file for matching. If the
+ //!@param[in] pattern_file_name indicates the name of the file for matching. If the
//! string is empty, the standard input or output streams are used instead
//! (depending on match_or_save)
- //!@param[in] match_or_save if true, the pattern file will be read, otherwise it will be
+ //!@param[in] match_or_save if true, the pattern file will be read, otherwise it will be
//! written
//!@param[in] text_or_binary if false, opens the stream in binary mode. Otherwise the stream
//! is opened with default flags and the carriage returns are ignored.
@@ -51,38 +51,49 @@ public:
bool text_or_binary = true );
// Destructor
- ~output_test_stream();
+ virtual ~output_test_stream();
//! Checks if the stream is empty
//!
//!@param[in] flush_stream if true, flushes the stream after the call
- assertion_result is_empty( bool flush_stream = true );
-
+ virtual assertion_result is_empty( bool flush_stream = true );
+
//! Checks the length of the stream
//!
//!@param[in] length target length
//!@param[in] flush_stream if true, flushes the stream after the call. Set to false to call
//! additional checks on the same content.
- assertion_result check_length( std::size_t length, bool flush_stream = true );
-
+ virtual assertion_result check_length( std::size_t length, bool flush_stream = true );
+
//! Checks the content of the stream against a string
//!
//!@param[in] arg_ the target stream
//!@param[in] flush_stream if true, flushes the stream after the call.
- assertion_result is_equal( const_string arg_, bool flush_stream = true );
+ virtual assertion_result is_equal( const_string arg_, bool flush_stream = true );
//! Checks the content of the stream against a pattern file
//!
- //!@param[in] flush_stream if true, flushes the stream after the call.
- assertion_result match_pattern( bool flush_stream = true );
+ //!@param[in] flush_stream if true, flushes/resets the stream after the call.
+ virtual assertion_result match_pattern( bool flush_stream = true );
//! Flushes the stream
void flush();
+protected:
+
+ //! Returns the string representation of the stream
+ //!
+ //! May be overriden in order to mutate the string before the matching operations.
+ virtual std::string get_stream_string_representation() const;
+
private:
// helper functions
+
+ //! Length of the stream
std::size_t length();
- void sync();
+
+ //! Synching the stream into an internal string representation
+ virtual void sync();
struct Impl;
Impl* m_pimpl;
diff --git a/boost/test/tree/global_fixture.hpp b/boost/test/tree/global_fixture.hpp
index 89ee61eb0c..2114595929 100644
--- a/boost/test/tree/global_fixture.hpp
+++ b/boost/test/tree/global_fixture.hpp
@@ -5,11 +5,8 @@
// See http://www.boost.org/libs/test for the library home page.
//
-// File : $RCSfile$
-//
-// Version : $Revision: 74640 $
-//
-// Description : defines global_fixture
+/// @file
+/// Defines global_fixture
// ***************************************************************************
#ifndef BOOST_TEST_TREE_GLOBAL_FIXTURE_HPP_091911GER
diff --git a/boost/test/tree/observer.hpp b/boost/test/tree/observer.hpp
index d878949f67..4db930fb07 100644
--- a/boost/test/tree/observer.hpp
+++ b/boost/test/tree/observer.hpp
@@ -28,17 +28,57 @@ namespace unit_test {
// ************** test_observer ************** //
// ************************************************************************** //
+/// @brief Generic test observer interface
+///
+/// This interface is used by observers in order to receive notifications from the
+/// Boost.Test framework on the current execution state.
+///
+/// Several observers can be running at the same time, and it is not unusual to
+/// have interactions among them. The test_observer#priority member function allows the specification
+/// of a particular order among them (lowest priority executed first, except specified otherwise).
+///
class BOOST_TEST_DECL test_observer {
public:
- // test observer interface
- virtual void test_start( counter_t /* test_cases_amount */ ) {}
+
+ //! Called before the framework starts executing the test cases
+ //!
+ //! @param[in] number_of_test_cases indicates the number of test cases. Only active
+ //! test cases are taken into account.
+ //!
+ virtual void test_start( counter_t /* number_of_test_cases */ ) {}
+
+
+ //! Called after the framework ends executing the test cases
+ //!
+ //! @note The call is made with a reversed priority order.
virtual void test_finish() {}
+
+ //! Called when a critical error is detected
+ //!
+ //! The critical errors are mainly the signals sent by the system and caught by the Boost.Test framework.
+ //! Since the running binary may be in incoherent/instable state, the test execution is aborted and all remaining
+ //! tests are discarded.
+ //!
+ //! @note may be called before test_observer::test_unit_finish()
virtual void test_aborted() {}
- virtual void test_unit_start( test_unit const& ) {}
- virtual void test_unit_finish( test_unit const&, unsigned long /* elapsed */ ) {}
+ //! Called before the framework starts executing a test unit
+ //!
+ //! @param[in] test_unit the test being executed
+ virtual void test_unit_start( test_unit const& /* test */) {}
+
+ //! Called at each end of a test unit.
+ //!
+ //! @param elapsed duration of the test unit in microseconds.
+ virtual void test_unit_finish( test_unit const& /* test */, unsigned long /* elapsed */ ) {}
virtual void test_unit_skipped( test_unit const& tu, const_string ) { test_unit_skipped( tu ); }
virtual void test_unit_skipped( test_unit const& ) {} ///< backward compatibility
+
+ //! Called when a test unit indicates a fatal error.
+ //!
+ //! A fatal error happens when
+ //! - a strong assertion (with @c REQUIRE) fails, which indicates that the test case cannot continue
+ //! - an unexpected exception is caught by the Boost.Test framework
virtual void test_unit_aborted( test_unit const& ) {}
virtual void assertion_result( unit_test::assertion_result ar )
@@ -50,12 +90,18 @@ public:
default: break;
}
}
+
+ //! Called when an exception is intercepted
+ //!
+ //! In case an exception is intercepted, this call happens before the call
+ //! to @ref test_unit_aborted in order to log
+ //! additional data about the exception.
virtual void exception_caught( execution_exception const& ) {}
virtual int priority() { return 0; }
protected:
- // depracated now
+ //! Deprecated
virtual void assertion_result( bool /* passed */ ) {}
BOOST_TEST_PROTECTED_VIRTUAL ~test_observer() {}
diff --git a/boost/test/tree/test_case_counter.hpp b/boost/test/tree/test_case_counter.hpp
index 6feaddcb0e..a74f37f152 100644
--- a/boost/test/tree/test_case_counter.hpp
+++ b/boost/test/tree/test_case_counter.hpp
@@ -5,11 +5,8 @@
// See http://www.boost.org/libs/test for the library home page.
//
-// File : $RCSfile$
-//
-// Version : $Revision: 74640 $
-//
-// Description : defines test_case_counter
+/// @file
+/// Defines @ref test_case_counter
// ***************************************************************************
#ifndef BOOST_TEST_TREE_TEST_CASE_COUNTER_HPP_100211GER
@@ -33,6 +30,7 @@ namespace unit_test {
// ************** test_case_counter ************** //
// ************************************************************************** //
+///! Counts the number of enabled test cases
class test_case_counter : public test_tree_visitor {
public:
// Constructor
diff --git a/boost/test/unit_test_log.hpp b/boost/test/unit_test_log.hpp
index 4126953d02..ba998b0ca4 100644
--- a/boost/test/unit_test_log.hpp
+++ b/boost/test/unit_test_log.hpp
@@ -85,6 +85,30 @@ private:
// ************** unit_test_log ************** //
// ************************************************************************** //
+/// @brief Manages the sets of loggers, their streams and log levels
+///
+/// The Boost.Test framework allows for having several formatters/loggers at the same time, each of which
+/// having their own log level and output stream.
+///
+/// This class serves the purpose of
+/// - exposing an interface to the test framework (as a boost::unit_test::test_observer)
+/// - exposing an interface to the testing tools
+/// - managing several loggers
+///
+/// @note Accesses to the functions exposed by this class are made through the singleton
+/// @c boost::unit_test::unit_test_log.
+///
+/// Users/developers willing to implement their own formatter need to:
+/// - implement a boost::unit_test::unit_test_log_formatter that will output the desired format
+/// - register the formatter during a eg. global fixture using the method @c set_formatter (though the framework singleton).
+///
+/// @warning this observer has a higher priority than the @ref boost::unit_test::results_collector_t. This means
+/// that the various @ref boost::unit_test::test_results associated to each test unit may not be available at the time
+/// the @c test_unit_start, @c test_unit_finish ... are called.
+///
+/// @see
+/// - boost::unit_test::test_observer
+/// - boost::unit_test::unit_test_log_formatter
class BOOST_TEST_DECL unit_test_log_t : public test_observer, public singleton<unit_test_log_t> {
public:
// test_observer interface implementation
@@ -95,17 +119,83 @@ public:
virtual void test_unit_start( test_unit const& );
virtual void test_unit_finish( test_unit const&, unsigned long elapsed );
virtual void test_unit_skipped( test_unit const&, const_string );
+ virtual void test_unit_aborted( test_unit const& );
virtual void exception_caught( execution_exception const& ex );
virtual int priority() { return 1; }
// log configuration methods
+ //! Sets the stream for all loggers
+ //!
+ //! This will override the log sink/stream of all loggers, whether enabled or not.
void set_stream( std::ostream& );
+
+ //! Sets the stream for specific logger
+ //!
+ //! @note Has no effect if the specified format is not found
+ //! @par Since Boost 1.62
+ void set_stream( output_format, std::ostream& );
+
+ //! Sets the threshold level for all loggers/formatters.
+ //!
+ //! This will override the log level of all loggers, whether enabled or not.
void set_threshold_level( log_level );
+
+ //! Sets the threshold/log level of a specific format
+ //!
+ //! @note Has no effect if the specified format is not found
+ //! @par Since Boost 1.62
+ void set_threshold_level( output_format, log_level );
+
+ //! Add a format to the set of loggers
+ //!
+ //! Adding a logger means that the specified logger is enabled. The log level is managed by the formatter itself
+ //! and specifies what events are forwarded to the underlying formatter.
+ //! @par Since Boost 1.62
+ void add_format( output_format );
+
+ //! Sets the format of the logger
+ //!
+ //! This will become the only active format of the logs.
void set_format( output_format );
+
+ //! Returns the logger instance for a specific format.
+ //!
+ //! @returns the logger/formatter instance, or @c (unit_test_log_formatter*)0 if the format is not found.
+ //! @par Since Boost 1.62
+ unit_test_log_formatter* get_formatter( output_format );
+
+ //! Sets the logger instance
+ //!
+ //! The specified logger becomes the unique active one. The custom log formatter has the
+ //! format @c OF_CUSTOM_LOGGER. If such a format exists already, its formatter gets replaced by the one
+ //! given in argument.
+ //!
+ //! The log level and output stream of the new formatter are taken from the currently active logger. In case
+ //! several loggers are active, the order of priority is CUSTOM, HRF, XML, and JUNIT.
+ //! If (unit_test_log_formatter*)0 is given as argument, the custom logger (if any) is removed.
+ //!
+ //! @note The ownership of the pointer is transfered to the Boost.Test framework. This call is equivalent to
+ //! - a call to @c add_formatter
+ //! - a call to @c set_format(OF_CUSTOM_LOGGER)
+ //! - a configuration of the newly added logger with a previously configured stream and log level.
void set_formatter( unit_test_log_formatter* );
+ //! Adds a custom log formatter to the set of formatters
+ //!
+ //! The specified logger is added with the format @c OF_CUSTOM_LOGGER, such that it can
+ //! be futher selected or its stream/log level can be specified.
+ //! If there is already a custom logger (with @c OF_CUSTOM_LOGGER), then
+ //! the existing one gets replaced by the one given in argument.
+ //! The provided logger is added with an enabled state.
+ //! If (unit_test_log_formatter*)0 is given as argument, the custom logger (if any) is removed and
+ //! no other action is performed.
+ //!
+ //! @note The ownership of the pointer is transfered to the Boost.Test framework.
+ //! @par Since Boost 1.62
+ void add_formatter( unit_test_log_formatter* the_formatter );
+
// test progress logging
void set_checkpoint( const_string file, std::size_t line_num, const_string msg = const_string() );
@@ -120,7 +210,7 @@ public:
private:
// Implementation helpers
- bool log_entry_start();
+ bool log_entry_start(output_format log_format);
void log_entry_context( log_level l );
void clear_entry_context();
@@ -142,6 +232,17 @@ BOOST_TEST_SINGLETON_INST( unit_test_log )
// ************** Unit test log interface helpers ************** //
// ************************************************************************** //
+// messages sent by the framework
+#define BOOST_TEST_FRAMEWORK_MESSAGE( M ) \
+ (::boost::unit_test::unit_test_log \
+ << ::boost::unit_test::log::begin( \
+ "boost.test framework", \
+ __LINE__ )) \
+ ( ::boost::unit_test::log_messages ) \
+ << BOOST_TEST_LAZY_MSG( M ) \
+/**/
+
+
#define BOOST_TEST_MESSAGE( M ) \
BOOST_TEST_LOG_ENTRY( ::boost::unit_test::log_messages ) \
<< BOOST_TEST_LAZY_MSG( M ) \
diff --git a/boost/test/unit_test_log_formatter.hpp b/boost/test/unit_test_log_formatter.hpp
index 1e3d64322f..5a9d6bada5 100644
--- a/boost/test/unit_test_log_formatter.hpp
+++ b/boost/test/unit_test_log_formatter.hpp
@@ -23,6 +23,7 @@
// STL
#include <iosfwd>
#include <string> // for std::string
+#include <iostream>
#include <boost/test/detail/suppress_warnings.hpp>
@@ -72,19 +73,37 @@ struct BOOST_TEST_DECL log_checkpoint_data
};
// ************************************************************************** //
-/// Abstract Unit Test Framework log formatter interface
-
-/// During the test module execution Unit Test Framework can report messages about success or failure of assertions,
-/// which test suites are being run and more (specifically which messages are reported depends on log level threshold selected by the user).
-/// All these messages constitute Unit Test Framework log. There are many ways (formats) to present these messages to the user. Boost.Test comes with
-/// two formats: "Compiler-like log format" and "XML based log format". Former is intended for human consumption and later is intended for processing
-/// by automated regression test systems. If you want to produce some other format you need to implement class with specific interface and use
-/// method unit_test_log_t::set_formatter during a test module initialization to set an active formatter. The class unit_test_log_formatter defines this
-/// interface.
+/// @brief Abstract Unit Test Framework log formatter interface
///
-/// This interface requires you to format all possible messages being produced in the log. These includes error messages about failed assertions, messages
-/// about caught exceptions and information messages about test units being started/ended. All the methods in this interface takes a reference to standard
-/// stream as a first argument. This is where final messages needs to be directed to. Also you are given all the information necessary to produce a message.
+/// During the test module execution Unit Test Framework can report messages about success
+/// or failure of assertions, which test suites are being run and more (specifically which
+/// messages are reported depends on log level threshold selected by the user).
+///
+/// All these messages constitute Unit Test Framework log. There are many ways (formats) to present
+/// these messages to the user.
+///
+/// Boost.Test comes with three formats:
+/// - Compiler-like log format: intended for human consumption/diagnostic
+/// - XML based log format: intended for processing by automated regression test systems.
+/// - JUNIT based log format: intended for processing by automated regression test systems.
+///
+/// If you want to produce some other format you need to implement class with specific interface and use
+/// method @c unit_test_log_t::set_formatter during a test module initialization to set an active formatter.
+/// The class unit_test_log_formatter defines this interface.
+///
+/// This interface requires you to format all possible messages being produced in the log.
+/// These includes error messages about failed assertions, messages about caught exceptions and
+/// information messages about test units being started/ended. All the methods in this interface takes
+/// a reference to standard stream as a first argument. This is where final messages needs to be directed
+/// to. Also you are given all the information necessary to produce a message.
+///
+/// @par Since Boost 1.62:
+/// - Each formatter may indicate the default output stream. This is convenient for instance for streams intended
+/// for automated processing that indicate a file. See @c get_default_stream_description for more details.
+/// - Each formatter may manage its own log level through the getter/setter @c get_log_level and @c set_log_level .
+///
+/// @see
+/// - boost::unit_test::test_observer for an indication of the calls of the test observer interface
class BOOST_TEST_DECL unit_test_log_formatter {
public:
/// Types of log entries (messages written into a log)
@@ -95,26 +114,31 @@ public:
BOOST_UTL_ET_FATAL_ERROR ///< Fatal error notification message
};
+ //! Constructor
+ unit_test_log_formatter()
+ : m_log_level(log_all_errors)
+ {}
+
// Destructor
virtual ~unit_test_log_formatter() {}
// @name Test start/finish
/// Invoked at the beginning of test module execution
-
+ ///
/// @param[in] os output stream to write a messages to
/// @param[in] test_cases_amount total test case amount to be run
/// @see log_finish
virtual void log_start( std::ostream& os, counter_t test_cases_amount ) = 0;
/// Invoked at the end of test module execution
-
+ ///
/// @param[in] os output stream to write a messages into
/// @see log_start
virtual void log_finish( std::ostream& os ) = 0;
/// Invoked when Unit Test Framework build information is requested
-
+ ///
/// @param[in] os output stream to write a messages into
virtual void log_build_info( std::ostream& os ) = 0;
// @}
@@ -122,22 +146,22 @@ public:
// @name Test unit start/finish
/// Invoked when test unit starts (either test suite or test case)
-
+ ///
/// @param[in] os output stream to write a messages into
/// @param[in] tu test unit being started
/// @see test_unit_finish
virtual void test_unit_start( std::ostream& os, test_unit const& tu ) = 0;
/// Invoked when test unit finishes
-
+ ///
/// @param[in] os output stream to write a messages into
/// @param[in] tu test unit being finished
- /// @param[in] elapsed time in milliseconds spend executing this test unit
+ /// @param[in] elapsed time in microseconds spend executing this test unit
/// @see test_unit_start
virtual void test_unit_finish( std::ostream& os, test_unit const& tu, unsigned long elapsed ) = 0;
/// Invoked if test unit skipped for any reason
-
+ ///
/// @param[in] os output stream to write a messages into
/// @param[in] tu skipped test unit
/// @param[in] reason explanation why was it skipped
@@ -149,14 +173,23 @@ public:
/// Deprecated version of this interface
virtual void test_unit_skipped( std::ostream& os, test_unit const& tu ) {}
+ /// Invoked when a test unit is aborted
+ virtual void test_unit_aborted( std::ostream& os, test_unit const& tu ) {}
+
// @}
// @name Uncaught exception report
/// Invoked when Unit Test Framework detects uncaught exception
-
- /// Call to this function starts uncaught exception report. It is going to followed by context information. Report is finalized by call to
- /// log_exception_finish.
+ ///
+ /// The framwork calls this function when an uncaught exception it detected.
+ /// This call is followed by context information:
+ /// - one call to @c entry_context_start,
+ /// - as many calls to @c log_entry_context as there are context entries
+ /// - one call to @c entry_context_finish
+ ///
+ /// The logging of the exception information is finilized by a call to @c log_exception_finish.
+ ///
/// @param[in] os output stream to write a messages into
/// @param[in] lcd information about the last checkpoint before the exception was triggered
/// @param[in] ex information about the caught exception
@@ -164,7 +197,7 @@ public:
virtual void log_exception_start( std::ostream& os, log_checkpoint_data const& lcd, execution_exception const& ex ) = 0;
/// Invoked when Unit Test Framework detects uncaught exception
-
+ ///
/// Call to this function finishes uncaught exception report.
/// @param[in] os output stream to write a messages into
/// @see log_exception_start
@@ -182,10 +215,13 @@ public:
/// @param[in] led log entry attributes
/// @param[in] let log entry type log_entry_finish
/// @see log_entry_value, log_entry_finish
+ ///
+ /// @note call to this function may happen before any call to test_unit_start or all calls to test_unit_finish as the
+ /// framework might log errors raised during global initialization/shutdown.
virtual void log_entry_start( std::ostream& os, log_entry_data const& led, log_entry_types let ) = 0;
/// Invoked by Unit Test Framework to report a log entry content
-
+ ///
/// This is one of two overloaded methods to report log entry content. This one is used to report plain string value.
/// @param[in] os output stream to write a messages into.
/// @param[in] value log entry string value
@@ -217,7 +253,7 @@ public:
/// Context consists of multiple "scopes" identified by description messages assigned by the test module using
/// BOOST_TEST_INFO/BOOST_TEST_CONTEXT statements.
/// @param[in] os output stream to write a messages into
- /// @param[in] l entry log_leveg, to be used to fine tune the message
+ /// @param[in] l entry log_level, to be used to fine tune the message
/// @see log_entry_context, entry_context_finish
virtual void entry_context_start( std::ostream& os, log_level l ) = 0;
@@ -235,6 +271,42 @@ public:
/// @see log_entry_start, entry_context_context
virtual void entry_context_finish( std::ostream& os ) = 0;
// @}
+
+ // @name Log level management
+
+ /// Sets the log level of the logger/formatter
+ ///
+ /// Some loggers need to manage the log level by their own. This
+ /// member function let the implementation decide of that.
+ /// @par Since Boost 1.62
+ virtual void set_log_level(log_level new_log_level);
+
+ /// Returns the log level of the logger/formatter
+ /// @par Since Boost 1.62
+ virtual log_level get_log_level() const;
+ // @}
+
+
+ // @name Stream management
+
+ /// Returns a default stream for this logger.
+ ///
+ /// The returned string describes the stream as if it was passed from
+ /// the command line @c "--log_sink" parameter. With that regards, @b stdout and @b stderr
+ /// have special meaning indicating the standard output or error stream respectively.
+ ///
+ /// @par Since Boost 1.62
+ virtual std::string get_default_stream_description() const
+ {
+ return "stdout";
+ }
+
+ // @}
+
+
+protected:
+ log_level m_log_level;
+
};
} // namespace unit_test
diff --git a/boost/test/unit_test_parameters.hpp b/boost/test/unit_test_parameters.hpp
index ad69c1732e..d6fecf0b8a 100644
--- a/boost/test/unit_test_parameters.hpp
+++ b/boost/test/unit_test_parameters.hpp
@@ -17,6 +17,7 @@
// Boost.Test
#include <boost/test/detail/global_typedef.hpp>
#include <boost/test/utils/runtime/argument.hpp>
+#include <boost/make_shared.hpp>
// STL
#include <iostream>
@@ -34,7 +35,7 @@ namespace runtime_config {
// ************** runtime_config ************** //
// ************************************************************************** //
-// UTF parameters
+// UTF parameters
BOOST_TEST_DECL extern std::string AUTO_START_DBG;
BOOST_TEST_DECL extern std::string BREAK_EXEC_PATH;
BOOST_TEST_DECL extern std::string BUILD_INFO;
@@ -44,6 +45,7 @@ BOOST_TEST_DECL extern std::string DETECT_FP_EXCEPT;
BOOST_TEST_DECL extern std::string DETECT_MEM_LEAKS;
BOOST_TEST_DECL extern std::string LIST_CONTENT;
BOOST_TEST_DECL extern std::string LIST_LABELS;
+BOOST_TEST_DECL extern std::string COMBINED_LOGGER;
BOOST_TEST_DECL extern std::string LOG_FORMAT;
BOOST_TEST_DECL extern std::string LOG_LEVEL;
BOOST_TEST_DECL extern std::string LOG_SINK;
@@ -76,6 +78,11 @@ get( runtime::cstring parameter_name )
return argument_store().get<T>( parameter_name );
}
+inline bool has( runtime::cstring parameter_name )
+{
+ return argument_store().has( parameter_name );
+}
+
/// For public access
BOOST_TEST_DECL bool save_pattern();
@@ -86,35 +93,34 @@ BOOST_TEST_DECL bool save_pattern();
class stream_holder {
public:
// Constructor
- explicit stream_holder( std::ostream& default_stream )
+ explicit stream_holder( std::ostream& default_stream = std::cout)
: m_stream( &default_stream )
{
}
- void setup( runtime::cstring param_name )
+ void setup( const const_string& stream_name )
{
- if( !runtime_config::argument_store().has( param_name ) )
+ if(stream_name.empty())
return;
- std::string const& file_name = runtime_config::get<std::string>( param_name );
-
- if( file_name == "stderr" )
+ if( stream_name == "stderr" )
m_stream = &std::cerr;
- else if( file_name == "stdout" )
+ else if( stream_name == "stdout" )
m_stream = &std::cout;
else {
- m_file.open( file_name.c_str() );
- m_stream = &m_file;
+ m_file = boost::make_shared<std::ofstream>();
+ m_file->open( std::string(stream_name.begin(), stream_name.end()).c_str() );
+ m_stream = m_file.get();
}
}
// Access methods
- std::ostream& ref() const { return *m_stream; }
+ std::ostream& ref() const { return *m_stream; }
private:
// Data members
- std::ofstream m_file;
- std::ostream* m_stream;
+ boost::shared_ptr<std::ofstream> m_file;
+ std::ostream* m_stream;
};
} // namespace runtime_config
diff --git a/boost/test/utils/algorithm.hpp b/boost/test/utils/algorithm.hpp
index 76625cbd91..a5491816eb 100644
--- a/boost/test/utils/algorithm.hpp
+++ b/boost/test/utils/algorithm.hpp
@@ -213,6 +213,104 @@ find_last_not_of( BidirectionalIterator1 first1, BidirectionalIterator1 last1,
//____________________________________________________________________________//
+
+/// @brief This algorithm replaces all occurrences of a set of substrings by another substrings
+///
+/// @param str - string of operation
+/// @param first1 - iterator to the beginning of the substrings to replace
+/// @param last1 - iterator to the end of the substrings to replace
+/// @param first2 - iterator to the beginning of the substrings to replace with
+/// @param last2 - iterator to the end of the substrings to replace with
+template<class StringClass, class ForwardIterator>
+inline StringClass
+replace_all_occurrences_of( StringClass str,
+ ForwardIterator first1, ForwardIterator last1,
+ ForwardIterator first2, ForwardIterator last2)
+{
+ for(; first1 != last1 && first2 != last2; ++first1, ++first2) {
+ std::size_t found = str.find( *first1 );
+ while( found != StringClass::npos ) {
+ str.replace(found, first1->size(), *first2 );
+ found = str.find( *first1, found + first2->size() );
+ }
+ }
+
+ return str;
+}
+
+/// @brief This algorithm replaces all occurrences of a string with basic wildcards
+/// with another (optionally containing wildcards as well).
+///
+/// @param str - string to transform
+/// @param it_string_to_find - iterator to the beginning of the substrings to replace
+/// @param it_string_to_find_end - iterator to the end of the substrings to replace
+/// @param it_string_to_replace - iterator to the beginning of the substrings to replace with
+/// @param it_string_to_replace_end - iterator to the end of the substrings to replace with
+///
+/// The wildcard is the symbol '*'. Only a unique wildcard per string is supported. The replacement
+/// string may also contain a wildcard, in which case it is considered as a placeholder to the content
+/// of the wildcard in the source string.
+/// Example:
+/// - In order to replace the occurrences of @c 'time=\"some-variable-value\"' to a constant string,
+/// one may use @c 'time=\"*\"' as the string to search for, and 'time=\"0.0\"' as the replacement string.
+/// - In order to replace the occurrences of 'file.cpp(XX)' per 'file.cpp:XX', where XX is a variable to keep,
+/// on may use @c 'file.cpp(*)' as the string to search for, and 'file.cpp:*' as the replacement string.
+template<class StringClass, class ForwardIterator>
+inline StringClass
+replace_all_occurrences_with_wildcards(
+ StringClass str,
+ ForwardIterator it_string_to_find, ForwardIterator it_string_to_find_end,
+ ForwardIterator it_string_to_replace, ForwardIterator it_string_to_replace_end)
+{
+ for(; it_string_to_find != it_string_to_find_end && it_string_to_replace != it_string_to_replace_end;
+ ++it_string_to_find, ++ it_string_to_replace) {
+
+ std::size_t wildcard_pos = it_string_to_find->find("*");
+ if(wildcard_pos == StringClass::npos) {
+ ForwardIterator it_to_find_current_end(it_string_to_find);
+ ForwardIterator it_to_replace_current_end(it_string_to_replace);
+ str = replace_all_occurrences_of(
+ str,
+ it_string_to_find, ++it_to_find_current_end,
+ it_string_to_replace, ++it_to_replace_current_end);
+ continue;
+ }
+
+ std::size_t wildcard_pos_replace = it_string_to_replace->find("*");
+
+ std::size_t found_begin = str.find( it_string_to_find->substr(0, wildcard_pos) );
+ while( found_begin != StringClass::npos ) {
+ std::size_t found_end = str.find(it_string_to_find->substr(wildcard_pos+1), found_begin + wildcard_pos + 1); // to simplify
+ if( found_end != StringClass::npos ) {
+
+ if( wildcard_pos_replace == StringClass::npos ) {
+ StringClass replace_content = *it_string_to_replace;
+ str.replace(
+ found_begin,
+ found_end + (it_string_to_find->size() - wildcard_pos - 1 ) - found_begin,
+ replace_content);
+ } else {
+ StringClass replace_content =
+ it_string_to_replace->substr(0, wildcard_pos_replace)
+ + str.substr(found_begin + wildcard_pos,
+ found_end - found_begin - wildcard_pos)
+ + it_string_to_replace->substr(wildcard_pos_replace+1) ;
+ str.replace(
+ found_begin,
+ found_end + (it_string_to_find->size() - wildcard_pos - 1 ) - found_begin,
+ replace_content);
+
+ }
+ }
+
+ // may adapt the restart to the replacement and be more efficient
+ found_begin = str.find( it_string_to_find->substr(0, wildcard_pos), found_begin + 1 );
+ }
+ }
+
+ return str;
+}
+
} // namespace utils
} // namespace unit_test
} // namespace boost
diff --git a/boost/test/utils/runtime/argument_factory.hpp b/boost/test/utils/runtime/argument_factory.hpp
index a163deb9a2..f3448f8cc4 100644
--- a/boost/test/utils/runtime/argument_factory.hpp
+++ b/boost/test/utils/runtime/argument_factory.hpp
@@ -128,7 +128,7 @@ template<typename EnumType>
struct value_interpreter<EnumType, true> {
template<typename Modifiers>
explicit value_interpreter( Modifiers const& m )
-#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) && !defined(BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX)
+#if defined(BOOST_TEST_CLA_NEW_API)
: m_name_to_value( m[enum_values<EnumType>::value] )
{
}
diff --git a/boost/test/utils/runtime/errors.hpp b/boost/test/utils/runtime/errors.hpp
index c11686132c..37f8d9371b 100644
--- a/boost/test/utils/runtime/errors.hpp
+++ b/boost/test/utils/runtime/errors.hpp
@@ -74,60 +74,60 @@ class specific_param_error : public Base {
protected:
explicit specific_param_error( cstring param_name ) : Base( param_name ) {}
~specific_param_error() BOOST_NOEXCEPT_OR_NOTHROW {}
-};
+
+public:
//____________________________________________________________________________//
-#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
+#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && \
+ !defined(BOOST_NO_CXX11_REF_QUALIFIERS)
-template<typename Derived, typename Base>
-inline Derived
-operator<<(specific_param_error<Derived, Base>&& ex, char const* val)
-{
- ex.msg.append( val );
+ Derived operator<<(char const* val) &&
+ {
+ this->msg.append( val );
- return reinterpret_cast<Derived&&>(ex);
-}
+ return reinterpret_cast<Derived&&>(*this);
+ }
-//____________________________________________________________________________//
+ //____________________________________________________________________________//
-template<typename Derived, typename Base, typename T>
-inline Derived
-operator<<(specific_param_error<Derived, Base>&& ex, T const& val)
-{
- ex.msg.append( unit_test::utils::string_cast( val ) );
+ template<typename T>
+ Derived operator<<(T const& val) &&
+ {
+ this->msg.append( unit_test::utils::string_cast( val ) );
- return reinterpret_cast<Derived&&>(ex);
-}
+ return reinterpret_cast<Derived&&>(*this);
+ }
-//____________________________________________________________________________//
+ //____________________________________________________________________________//
#else
-template<typename Derived, typename Base>
-inline Derived
-operator<<(specific_param_error<Derived, Base> const& ex, char const* val)
-{
- const_cast<specific_param_error<Derived, Base>&>(ex).msg.append( val );
+ Derived const& operator<<(char const* val) const
+ {
+ const_cast<specific_param_error<Derived, Base>&>(*this).msg.append( val );
- return static_cast<Derived const&>(ex);
-}
+ return static_cast<Derived const&>(*this);
+ }
-//____________________________________________________________________________//
+ //____________________________________________________________________________//
-template<typename Derived, typename Base, typename T>
-inline Derived
-operator<<(specific_param_error<Derived, Base> const& ex, T const& val)
-{
- const_cast<specific_param_error<Derived, Base>&>(ex).msg.append( unit_test::utils::string_cast( val ) );
+ template<typename T>
+ Derived const& operator<<(T const& val) const
+ {
+ const_cast<specific_param_error<Derived, Base>&>(*this).msg.append( unit_test::utils::string_cast( val ) );
- return static_cast<Derived const&>(ex);
-}
+ return static_cast<Derived const&>(*this);
+ }
-//____________________________________________________________________________//
+ //____________________________________________________________________________//
#endif
+};
+
+
+
// ************************************************************************** //
// ************** specific exception types ************** //
// ************************************************************************** //
diff --git a/boost/test/utils/runtime/modifier.hpp b/boost/test/utils/runtime/modifier.hpp
index ed77ca0afe..f4f5a42baa 100644
--- a/boost/test/utils/runtime/modifier.hpp
+++ b/boost/test/utils/runtime/modifier.hpp
@@ -23,6 +23,15 @@
#include <boost/test/detail/suppress_warnings.hpp>
+
+// New CLA API available only for some C++11 compilers
+#if !defined(BOOST_NO_CXX11_AUTO_DECLARATIONS) \
+ && !defined(BOOST_NO_CXX11_TEMPLATE_ALIASES) \
+ && !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) \
+ && !defined(BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX)
+#define BOOST_TEST_CLA_NEW_API
+#endif
+
namespace boost {
namespace runtime {
@@ -32,10 +41,6 @@ namespace runtime {
namespace {
-#if !defined(BOOST_NO_CXX11_AUTO_DECLARATIONS) && !defined(BOOST_NO_CXX11_TEMPLATE_ALIASES)
-#define BOOST_TEST_CLA_NEW_API
-#endif
-
#ifdef BOOST_TEST_CLA_NEW_API
auto const& description = unit_test::static_constant<nfp::typed_keyword<cstring,struct description_t>>::value;
auto const& help = unit_test::static_constant<nfp::typed_keyword<cstring,struct help_t>>::value;