libs/test/test/errors_handling_test.cpp
// (C) Copyright Gennadiy Rozental 2001-2004.
// (C) Copyright Beman Dawes 2001.
// 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: errors_handling_test.cpp,v $
//
// Version : $Revision: 1.24 $
//
// Description : tests an ability of Unit Test Framework to catch all kinds
// of test errors in a user code and properly report it.
// ***************************************************************************
// Boost.Test
#include <boost/test/unit_test.hpp>
#include <boost/test/unit_test_result.hpp>
#include <boost/test/detail/unit_test_parameters.hpp>
#include <boost/test/detail/supplied_log_formatters.hpp>
using namespace boost::unit_test;
using namespace boost::test_tools;
// STL
#include <iostream>
struct this_test_log_formatter : public boost::unit_test::ut_detail::msvc65_like_log_formatter
{
explicit this_test_log_formatter( unit_test_log const& log )
: boost::unit_test::ut_detail::msvc65_like_log_formatter( log ) {}
void print_prefix( std::ostream& output, boost::unit_test::const_string, std::size_t line )
{
output << line << ": ";
}
};
//____________________________________________________________________________//
namespace {
enum error_type_enum {
et_begin,
et_none = et_begin,
et_user,
et_cpp_exception,
et_system,
et_fatal_user,
et_fatal_system,
et_end
} error_type;
char const* error_type_name[] = { "no error", "user error", "cpp exception", " system error", "fatal user error", "fatal system error" };
int divide_by_zero = 0;
// will cause an error coresponding to the current error_type;
void error_on_demand()
{
switch( error_type ) {
case et_none:
BOOST_MESSAGE( "error_on_demand() BOOST_MESSAGE" );
break;
case et_user:
unit_test_result::instance().increase_expected_failures();
BOOST_ERROR( "error_on_demand() BOOST_ERROR" );
break;
case et_fatal_user:
unit_test_result::instance().increase_expected_failures();
BOOST_CRITICAL_ERROR( "error_on_demand() BOOST_CRITICAL_ERROR" );
BOOST_ERROR( "Should never reach this code!" );
break;
case et_cpp_exception:
BOOST_CHECKPOINT( "error_on_demand() throw runtime_error" );
throw std::runtime_error( "test std::runtime error what() message" );
case et_system:
BOOST_CHECKPOINT( "error_on_demand() divide by zero" );
divide_by_zero = 1 / divide_by_zero;
break;
case et_fatal_system:
BOOST_CHECKPOINT( "error_on_demand() write to an invalid address" );
{
int* p = 0;
*p = 0;
BOOST_ERROR( "Should never reach this code!" );
}
break;
default:
BOOST_ERROR( "Should never reach this code!" );
}
return;
}
enum test_case_type_enum {
tct_begin,
tct_free_function = tct_begin,
tct_user_test_case,
tct_param_free_function,
tct_param_user_test_case,
tct_end
} test_case_type;
char const* test_case_type_name[] = { "free function",
"user test case",
"parameterized free function",
"parameterized user test case"
};
// simulated user classes to be tested --------------------------------//
// user test cases ----------------------------------------------------//
struct bad_test
{
void test()
{
BOOST_MESSAGE( "(user test case)" );
error_on_demand();
}
void test_param( int )
{
BOOST_MESSAGE( "(parameterized user test case)" );
error_on_demand();
}
};
// free function tests ---------------------------------------------------//
void bad_function()
{
BOOST_MESSAGE( "(free function)" );
error_on_demand();
}
void bad_function_param( int )
{
BOOST_MESSAGE( "(parameterized free function)" );
error_on_demand();
}
int params[] = { 0 };
} // unnamed namespace
//____________________________________________________________________________//
int
test_main( int argc, char * argv[] )
{
bool match_or_save = retrieve_framework_parameter( SAVE_TEST_PATTERN, &argc, argv ) != "yes";
#define PATTERN_FILE_NAME "errors_handling_test.pattern"
std::string pattern_file_name( argc == 1
? (match_or_save ? "./test_files/" PATTERN_FILE_NAME : PATTERN_FILE_NAME)
: argv[1] );
#ifdef __GNUC__
pattern_file_name.append( "2" );
#endif
output_test_stream output( pattern_file_name, match_or_save );
unit_test_log::instance().set_log_stream( output );
unit_test_log::instance().set_log_formatter( new this_test_log_formatter( unit_test_log::instance() ) );
boost::shared_ptr<bad_test> bad_test_instance( new bad_test );
// for each log level
for( log_level level = log_successful_tests;
level <= log_nothing;
level = static_cast<log_level>(level+1) )
{
unit_test_log::instance().set_log_threshold_level( level );
// for each error type
for( error_type = et_begin;
error_type != et_end;
error_type = static_cast<error_type_enum>(error_type+1) )
{
#ifdef __GNUC__
if( error_type == et_system || error_type == et_fatal_system )
continue;
#endif
// for each error location
for( test_case_type = tct_begin;
test_case_type != tct_end;
test_case_type = static_cast<test_case_type_enum>(test_case_type+1) )
{
output << "\n===========================\n\n"
<< "log level: " << int(level) << ';'
<< " error type: " << error_type_name[error_type] << ';'
<< " test case type: " << test_case_type_name[test_case_type] << ';'<< std::endl;
// In typical user code, multiple test cases would be added to a single
// test suite. But for testing the unit test code itself, it is easier
// to isolate each case in its own test suite.
test_suite test( "Errors handling test" );
switch( test_case_type ) {
case tct_free_function:
test.add( BOOST_TEST_CASE( &bad_function ) );
break;
case tct_user_test_case:
test.add( BOOST_CLASS_TEST_CASE( &bad_test::test, bad_test_instance ) );
break;
case tct_param_free_function:
// Borland bug workaround
#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x570))
test.add( boost::unit_test::create_test_case<int*,int>( &bad_function_param, std::string( "bad_function_param" ), (int*)params, params+1 ) );
#else
test.add( BOOST_PARAM_TEST_CASE( &bad_function_param, (int*)params, params+1 ) );
#endif
break;
case tct_param_user_test_case:
#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x570))
test.add( boost::unit_test::create_test_case<bad_test,int*,int>( &bad_test::test_param, std::string( "bad_test::test_param" ), bad_test_instance, (int*)params, params+1 ) );
#else
test.add( BOOST_PARAM_CLASS_TEST_CASE( &bad_test::test_param, bad_test_instance, (int*)params, params+1 ) );
#endif
break;
default:
continue;
}
{
unit_test_result_saver saver;
unit_test_log::instance().start();
unit_test_log::instance().header( 1 );
test.run();
unit_test_log::instance().finish( 1 );
}
unit_test_log::instance().set_log_threshold_level( log_all_errors );
BOOST_CHECK( output.match_pattern() );
unit_test_log::instance().set_log_threshold_level( level );
}
}
}
unit_test_result::instance().short_report( output );
output.match_pattern();
return 0;
} // main
//____________________________________________________________________________//
// ***************************************************************************
// Revision History :
//
// $Log: errors_handling_test.cpp,v $
// Revision 1.24 2004/10/05 04:27:09 rogeeff
// 31 length fix
//
// Revision 1.23 2004/10/05 01:32:09 rogeeff
// file/directory renaming for the sake of CD burning
//
// Revision 1.22 2004/10/01 10:55:43 rogeeff
// some test errors workarrounds
//
// Revision 1.21 2004/06/07 07:34:23 rogeeff
// detail namespace renamed
//
// Revision 1.20 2004/05/27 06:30:48 rogeeff
// no message
//
// Revision 1.19 2004/05/21 06:26:10 rogeeff
// licence update
//
// Revision 1.18 2004/05/11 11:05:06 rogeeff
// basic_cstring introduced and used everywhere
// class properties reworked
// namespace names shortened
//
// Revision 1.17 2003/12/01 00:42:37 rogeeff
// prerelease cleaning
//
// ***************************************************************************
// EOF