629 lines
18 KiB
C++
629 lines
18 KiB
C++
// (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 BOOST_TEST_TEST_TOOLS_IPP_012205GER
|
|
#define BOOST_TEST_TEST_TOOLS_IPP_012205GER
|
|
|
|
// Boost.Test
|
|
#include <boost/test/test_tools.hpp>
|
|
#include <boost/test/unit_test_log.hpp>
|
|
#include <boost/test/output_test_stream.hpp>
|
|
#include <boost/test/framework.hpp>
|
|
#include <boost/test/execution_monitor.hpp> // execution_aborted
|
|
#include <boost/test/unit_test_suite_impl.hpp>
|
|
|
|
// Boost
|
|
#include <boost/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 <boost/test/detail/suppress_warnings.hpp>
|
|
|
|
//____________________________________________________________________________//
|
|
|
|
# ifdef BOOST_NO_STDC_NAMESPACE
|
|
namespace std { using ::strcmp; using ::strlen; using ::isprint; }
|
|
#if !defined( BOOST_NO_CWCHAR )
|
|
namespace std { using ::wcscmp; }
|
|
#endif
|
|
# endif
|
|
|
|
namespace boost {
|
|
|
|
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 BOOST_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 BOOST_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 ::boost::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( BOOST_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( BOOST_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 );
|
|
|
|
BOOST_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 BOOST_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 BOOST_NO_STRINGSTREAM
|
|
m_pimpl->m_synced_string.assign( str(), pcount() );
|
|
freeze( false );
|
|
#else
|
|
m_pimpl->m_synced_string = str();
|
|
#endif
|
|
}
|
|
|
|
//____________________________________________________________________________//
|
|
|
|
} // namespace test_tools
|
|
|
|
} // namespace boost
|
|
|
|
//____________________________________________________________________________//
|
|
|
|
#include <boost/test/detail/enable_warnings.hpp>
|
|
|
|
#endif // BOOST_TEST_TEST_TOOLS_IPP_012205GER
|