| // (C) Copyright Gennadiy Rozental 2005-2008. |
| // 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 : $RCSfile$ |
| // |
| // Version : $Revision: 54633 $ |
| // |
| // Description : supplies offline implementation for the Test Tools |
| // *************************************************************************** |
| |
| #ifndef NDNBOOST_TEST_TEST_TOOLS_IPP_012205GER |
| #define NDNBOOST_TEST_TEST_TOOLS_IPP_012205GER |
| |
| // Boost.Test |
| #include <ndnboost/test/test_tools.hpp> |
| #include <ndnboost/test/unit_test_log.hpp> |
| #include <ndnboost/test/output_test_stream.hpp> |
| #include <ndnboost/test/framework.hpp> |
| #include <ndnboost/test/execution_monitor.hpp> // execution_aborted |
| #include <ndnboost/test/unit_test_suite_impl.hpp> |
| |
| // Boost |
| #include <ndnboost/config.hpp> |
| |
| // STL |
| #include <fstream> |
| #include <string> |
| #include <cstring> |
| #include <cctype> |
| #include <cwchar> |
| #include <stdexcept> |
| #include <ios> |
| |
| // !! should we use #include <cstdarg> |
| #include <stdarg.h> |
| |
| #include <ndnboost/test/detail/suppress_warnings.hpp> |
| |
| //____________________________________________________________________________// |
| |
| # ifdef NDNBOOST_NO_STDC_NAMESPACE |
| namespace std { using ::strcmp; using ::strlen; using ::isprint; } |
| #if !defined( NDNBOOST_NO_CWCHAR ) |
| namespace std { using ::wcscmp; } |
| #endif |
| # endif |
| |
| namespace ndnboost { |
| |
| namespace test_tools { |
| |
| // ************************************************************************** // |
| // ************** print_log_value ************** // |
| // ************************************************************************** // |
| |
| void |
| print_log_value<char>::operator()( std::ostream& ostr, char t ) |
| { |
| if( (std::isprint)( static_cast<unsigned char>(t) ) ) |
| ostr << '\'' << t << '\''; |
| else |
| ostr << std::hex |
| #if NDNBOOST_TEST_USE_STD_LOCALE |
| << std::showbase |
| #else |
| << "0x" |
| #endif |
| << static_cast<int>(t); |
| } |
| |
| //____________________________________________________________________________// |
| |
| void |
| print_log_value<unsigned char>::operator()( std::ostream& ostr, unsigned char t ) |
| { |
| ostr << std::hex |
| // showbase is only available for new style streams: |
| #if NDNBOOST_TEST_USE_STD_LOCALE |
| << std::showbase |
| #else |
| << "0x" |
| #endif |
| << static_cast<int>(t); |
| } |
| |
| //____________________________________________________________________________// |
| |
| void |
| print_log_value<char const*>::operator()( std::ostream& ostr, char const* t ) |
| { |
| ostr << ( t ? t : "null string" ); |
| } |
| |
| //____________________________________________________________________________// |
| |
| void |
| print_log_value<wchar_t const*>::operator()( std::ostream& ostr, wchar_t const* t ) |
| { |
| ostr << ( t ? t : L"null string" ); |
| } |
| |
| //____________________________________________________________________________// |
| |
| namespace tt_detail { |
| |
| // ************************************************************************** // |
| // ************** TOOL BOX Implementation ************** // |
| // ************************************************************************** // |
| |
| using ::ndnboost::unit_test::lazy_ostream; |
| |
| bool |
| check_impl( predicate_result const& pr, lazy_ostream const& check_descr, |
| const_string file_name, std::size_t line_num, |
| tool_level tl, check_type ct, |
| std::size_t num_of_args, ... ) |
| { |
| using namespace unit_test; |
| |
| if( !framework::is_initialized() ) |
| throw std::runtime_error( "can't use testing tools before framework is initialized" ); |
| |
| if( !!pr ) |
| tl = PASS; |
| |
| log_level ll; |
| char const* prefix; |
| char const* suffix; |
| |
| switch( tl ) { |
| case PASS: |
| ll = log_successful_tests; |
| prefix = "check "; |
| suffix = " passed"; |
| break; |
| case WARN: |
| ll = log_warnings; |
| prefix = "condition "; |
| suffix = " is not satisfied"; |
| break; |
| case CHECK: |
| ll = log_all_errors; |
| prefix = "check "; |
| suffix = " failed"; |
| break; |
| case REQUIRE: |
| ll = log_fatal_errors; |
| prefix = "critical check "; |
| suffix = " failed"; |
| break; |
| default: |
| return true; |
| } |
| |
| switch( ct ) { |
| case CHECK_PRED: |
| unit_test_log << unit_test::log::begin( file_name, line_num ) |
| << ll << prefix << check_descr << suffix; |
| |
| if( !pr.has_empty_message() ) |
| unit_test_log << ". " << pr.message(); |
| |
| unit_test_log << unit_test::log::end(); |
| break; |
| |
| case CHECK_MSG: |
| unit_test_log << unit_test::log::begin( file_name, line_num ) << ll; |
| |
| if( tl == PASS ) |
| unit_test_log << prefix << "'" << check_descr << "'" << suffix; |
| else |
| unit_test_log << check_descr; |
| |
| if( !pr.has_empty_message() ) |
| unit_test_log << ". " << pr.message(); |
| |
| unit_test_log << unit_test::log::end(); |
| break; |
| |
| case CHECK_EQUAL: |
| case CHECK_NE: |
| case CHECK_LT: |
| case CHECK_LE: |
| case CHECK_GT: |
| case CHECK_GE: { |
| static char const* check_str [] = { " == ", " != ", " < " , " <= ", " > " , " >= " }; |
| static char const* rever_str [] = { " != ", " == ", " >= ", " > " , " <= ", " < " }; |
| |
| va_list args; |
| |
| va_start( args, num_of_args ); |
| char const* arg1_descr = va_arg( args, char const* ); |
| lazy_ostream const* arg1_val = va_arg( args, lazy_ostream const* ); |
| char const* arg2_descr = va_arg( args, char const* ); |
| lazy_ostream const* arg2_val = va_arg( args, lazy_ostream const* ); |
| |
| unit_test_log << unit_test::log::begin( file_name, line_num ) |
| << ll << prefix << arg1_descr << check_str[ct-CHECK_EQUAL] << arg2_descr << suffix; |
| |
| if( tl != PASS ) |
| unit_test_log << " [" << *arg1_val << rever_str[ct-CHECK_EQUAL] << *arg2_val << "]" ; |
| |
| va_end( args ); |
| |
| if( !pr.has_empty_message() ) |
| unit_test_log << ". " << pr.message(); |
| |
| unit_test_log << unit_test::log::end(); |
| break; |
| } |
| |
| case CHECK_CLOSE: |
| case CHECK_CLOSE_FRACTION: { |
| va_list args; |
| |
| va_start( args, num_of_args ); |
| char const* arg1_descr = va_arg( args, char const* ); |
| lazy_ostream const* arg1_val = va_arg( args, lazy_ostream const* ); |
| char const* arg2_descr = va_arg( args, char const* ); |
| lazy_ostream const* arg2_val = va_arg( args, lazy_ostream const* ); |
| /* toler_descr = */ va_arg( args, char const* ); |
| lazy_ostream const* toler_val = va_arg( args, lazy_ostream const* ); |
| |
| unit_test_log << unit_test::log::begin( file_name, line_num ) << ll; |
| |
| unit_test_log << "difference{" << pr.message() << (ct == CHECK_CLOSE ? "%" : "") |
| << "} between " << arg1_descr << "{" << *arg1_val |
| << "} and " << arg2_descr << "{" << *arg2_val |
| << ( tl == PASS ? "} doesn't exceed " : "} exceeds " ) |
| << *toler_val; |
| if( ct == CHECK_CLOSE ) |
| unit_test_log << "%"; |
| |
| va_end( args ); |
| |
| unit_test_log << unit_test::log::end(); |
| break; |
| } |
| case CHECK_SMALL: { |
| va_list args; |
| |
| va_start( args, num_of_args ); |
| char const* arg1_descr = va_arg( args, char const* ); |
| lazy_ostream const* arg1_val = va_arg( args, lazy_ostream const* ); |
| /* toler_descr = */ va_arg( args, char const* ); |
| lazy_ostream const* toler_val = va_arg( args, lazy_ostream const* ); |
| |
| unit_test_log << unit_test::log::begin( file_name, line_num ) << ll; |
| |
| unit_test_log << "absolute value of " << arg1_descr << "{" << *arg1_val << "}" |
| << ( tl == PASS ? " doesn't exceed " : " exceeds " ) |
| << *toler_val; |
| |
| va_end( args ); |
| |
| if( !pr.has_empty_message() ) |
| unit_test_log << ". " << pr.message(); |
| |
| unit_test_log << unit_test::log::end(); |
| break; |
| } |
| |
| case CHECK_PRED_WITH_ARGS: { |
| unit_test_log << unit_test::log::begin( file_name, line_num ) |
| << ll << prefix << check_descr; |
| |
| // print predicate call description |
| { |
| va_list args; |
| va_start( args, num_of_args ); |
| |
| unit_test_log << "( "; |
| for( std::size_t i = 0; i < num_of_args; ++i ) { |
| unit_test_log << va_arg( args, char const* ); |
| va_arg( args, lazy_ostream const* ); // skip argument value; |
| |
| if( i != num_of_args-1 ) |
| unit_test_log << ", "; |
| } |
| unit_test_log << " )" << suffix; |
| va_end( args ); |
| } |
| |
| if( tl != PASS ) { |
| va_list args; |
| va_start( args, num_of_args ); |
| |
| unit_test_log << " for ( "; |
| for( std::size_t i = 0; i < num_of_args; ++i ) { |
| va_arg( args, char const* ); // skip argument description; |
| unit_test_log << *va_arg( args, lazy_ostream const* ); |
| |
| if( i != num_of_args-1 ) |
| unit_test_log << ", "; |
| } |
| unit_test_log << " )"; |
| va_end( args ); |
| } |
| |
| if( !pr.has_empty_message() ) |
| unit_test_log << ". " << pr.message(); |
| |
| unit_test_log << unit_test::log::end(); |
| break; |
| } |
| |
| case CHECK_EQUAL_COLL: { |
| va_list args; |
| |
| va_start( args, num_of_args ); |
| char const* left_begin_descr = va_arg( args, char const* ); |
| char const* left_end_descr = va_arg( args, char const* ); |
| char const* right_begin_descr = va_arg( args, char const* ); |
| char const* right_end_descr = va_arg( args, char const* ); |
| |
| unit_test_log << unit_test::log::begin( file_name, line_num ) |
| << ll << prefix |
| << "{ " << left_begin_descr << ", " << left_end_descr << " } == { " |
| << right_begin_descr << ", " << right_end_descr << " }" |
| << suffix; |
| |
| va_end( args ); |
| |
| if( !pr.has_empty_message() ) |
| unit_test_log << ". " << pr.message(); |
| |
| unit_test_log << unit_test::log::end(); |
| break; |
| } |
| |
| case CHECK_BITWISE_EQUAL: { |
| va_list args; |
| |
| va_start( args, num_of_args ); |
| char const* left_descr = va_arg( args, char const* ); |
| char const* right_descr = va_arg( args, char const* ); |
| |
| unit_test_log << unit_test::log::begin( file_name, line_num ) |
| << ll << prefix << left_descr << " =.= " << right_descr << suffix; |
| |
| va_end( args ); |
| |
| if( !pr.has_empty_message() ) |
| unit_test_log << ". " << pr.message(); |
| |
| unit_test_log << unit_test::log::end(); |
| break; |
| } |
| } |
| |
| switch( tl ) { |
| case PASS: |
| framework::assertion_result( true ); |
| return true; |
| |
| case WARN: |
| return false; |
| |
| case CHECK: |
| framework::assertion_result( false ); |
| return false; |
| |
| case REQUIRE: |
| framework::assertion_result( false ); |
| |
| framework::test_unit_aborted( framework::current_test_case() ); |
| |
| throw execution_aborted(); |
| } |
| |
| return true; |
| } |
| |
| //____________________________________________________________________________// |
| |
| predicate_result |
| equal_impl( char const* left, char const* right ) |
| { |
| return (left && right) ? std::strcmp( left, right ) == 0 : (left == right); |
| } |
| |
| //____________________________________________________________________________// |
| |
| #if !defined( NDNBOOST_NO_CWCHAR ) |
| |
| predicate_result |
| equal_impl( wchar_t const* left, wchar_t const* right ) |
| { |
| return (left && right) ? std::wcscmp( left, right ) == 0 : (left == right); |
| } |
| |
| #endif // !defined( NDNBOOST_NO_CWCHAR ) |
| |
| //____________________________________________________________________________// |
| |
| bool |
| is_defined_impl( const_string symbol_name, const_string symbol_value ) |
| { |
| symbol_value.trim_left( 2 ); |
| return symbol_name != symbol_value; |
| } |
| |
| //____________________________________________________________________________// |
| |
| } // namespace tt_detail |
| |
| // ************************************************************************** // |
| // ************** output_test_stream ************** // |
| // ************************************************************************** // |
| |
| struct output_test_stream::Impl |
| { |
| std::fstream m_pattern; |
| bool m_match_or_save; |
| bool m_text_or_binary; |
| std::string m_synced_string; |
| |
| char get_char() |
| { |
| char res; |
| do { |
| m_pattern.get( res ); |
| } while( m_text_or_binary && res == '\r' && !m_pattern.fail() && !m_pattern.eof() ); |
| |
| return res; |
| } |
| |
| void check_and_fill( predicate_result& res ) |
| { |
| if( !res.p_predicate_value ) |
| res.message() << "Output content: \"" << m_synced_string << '\"'; |
| } |
| }; |
| |
| //____________________________________________________________________________// |
| |
| output_test_stream::output_test_stream( const_string pattern_file_name, bool match_or_save, bool text_or_binary ) |
| : m_pimpl( new Impl ) |
| { |
| if( !pattern_file_name.is_empty() ) { |
| std::ios::openmode m = match_or_save ? std::ios::in : std::ios::out; |
| if( !text_or_binary ) |
| m |= std::ios::binary; |
| |
| m_pimpl->m_pattern.open( pattern_file_name.begin(), m ); |
| |
| NDNBOOST_WARN_MESSAGE( m_pimpl->m_pattern.is_open(), |
| "Can't open pattern file " << pattern_file_name |
| << " for " << (match_or_save ? "reading" : "writing") ); |
| } |
| |
| m_pimpl->m_match_or_save = match_or_save; |
| m_pimpl->m_text_or_binary = text_or_binary; |
| } |
| |
| //____________________________________________________________________________// |
| |
| output_test_stream::~output_test_stream() |
| { |
| delete m_pimpl; |
| } |
| |
| //____________________________________________________________________________// |
| |
| predicate_result |
| output_test_stream::is_empty( bool flush_stream ) |
| { |
| sync(); |
| |
| result_type res( m_pimpl->m_synced_string.empty() ); |
| |
| m_pimpl->check_and_fill( res ); |
| |
| if( flush_stream ) |
| flush(); |
| |
| return res; |
| } |
| |
| //____________________________________________________________________________// |
| |
| predicate_result |
| output_test_stream::check_length( std::size_t length_, bool flush_stream ) |
| { |
| sync(); |
| |
| result_type res( m_pimpl->m_synced_string.length() == length_ ); |
| |
| m_pimpl->check_and_fill( res ); |
| |
| if( flush_stream ) |
| flush(); |
| |
| return res; |
| } |
| |
| //____________________________________________________________________________// |
| |
| predicate_result |
| output_test_stream::is_equal( const_string arg, bool flush_stream ) |
| { |
| sync(); |
| |
| result_type res( const_string( m_pimpl->m_synced_string ) == arg ); |
| |
| m_pimpl->check_and_fill( res ); |
| |
| if( flush_stream ) |
| flush(); |
| |
| return res; |
| } |
| |
| //____________________________________________________________________________// |
| |
| predicate_result |
| output_test_stream::match_pattern( bool flush_stream ) |
| { |
| sync(); |
| |
| result_type result( true ); |
| |
| 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 ) { |
| char c = m_pimpl->get_char(); |
| |
| result = !m_pimpl->m_pattern.fail() && |
| !m_pimpl->m_pattern.eof() && |
| (m_pimpl->m_synced_string[i] == 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) ); |
| |
| // 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; |
| |
| std::string::size_type counter = suffix_size; |
| while( --counter ) { |
| char c = m_pimpl->get_char(); |
| |
| if( m_pimpl->m_pattern.fail() || m_pimpl->m_pattern.eof() ) |
| break; |
| |
| result.message() << c; |
| } |
| |
| result.message() << "..."; |
| |
| // 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; |
| } |
| } |
| } |
| 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.flush(); |
| } |
| } |
| |
| if( flush_stream ) |
| flush(); |
| |
| return result; |
| } |
| |
| //____________________________________________________________________________// |
| |
| void |
| output_test_stream::flush() |
| { |
| m_pimpl->m_synced_string.erase(); |
| |
| #ifndef NDNBOOST_NO_STRINGSTREAM |
| str( std::string() ); |
| #else |
| seekp( 0, std::ios::beg ); |
| #endif |
| } |
| |
| //____________________________________________________________________________// |
| |
| std::size_t |
| output_test_stream::length() |
| { |
| sync(); |
| |
| return m_pimpl->m_synced_string.length(); |
| } |
| |
| //____________________________________________________________________________// |
| |
| void |
| output_test_stream::sync() |
| { |
| #ifdef NDNBOOST_NO_STRINGSTREAM |
| m_pimpl->m_synced_string.assign( str(), pcount() ); |
| freeze( false ); |
| #else |
| m_pimpl->m_synced_string = str(); |
| #endif |
| } |
| |
| //____________________________________________________________________________// |
| |
| } // namespace test_tools |
| |
| } // namespace ndnboost |
| |
| //____________________________________________________________________________// |
| |
| #include <ndnboost/test/detail/enable_warnings.hpp> |
| |
| #endif // NDNBOOST_TEST_TEST_TOOLS_IPP_012205GER |