diff options
Diffstat (limited to 'boost/test/impl')
21 files changed, 7533 insertions, 0 deletions
diff --git a/boost/test/impl/compiler_log_formatter.ipp b/boost/test/impl/compiler_log_formatter.ipp new file mode 100644 index 0000000000..d49c2f8f4d --- /dev/null +++ b/boost/test/impl/compiler_log_formatter.ipp @@ -0,0 +1,222 @@ +// (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: 57992 $ +// +// Description : implements compiler like Log formatter +// *************************************************************************** + +#ifndef BOOST_TEST_COMPILER_LOG_FORMATTER_IPP_020105GER +#define BOOST_TEST_COMPILER_LOG_FORMATTER_IPP_020105GER + +// Boost.Test +#include <boost/test/output/compiler_log_formatter.hpp> +#include <boost/test/unit_test_suite_impl.hpp> +#include <boost/test/framework.hpp> +#include <boost/test/utils/basic_cstring/io.hpp> +#include <boost/test/utils/lazy_ostream.hpp> + +// Boost +#include <boost/version.hpp> + +// STL +#include <iostream> + +#include <boost/test/detail/suppress_warnings.hpp> + +//____________________________________________________________________________// + +namespace boost { + +namespace unit_test { + +namespace output { + +// ************************************************************************** // +// ************** compiler_log_formatter ************** // +// ************************************************************************** // + +namespace { + +const_string +test_phase_identifier() +{ + return framework::is_initialized() + ? const_string( framework::current_test_case().p_name.get() ) + : BOOST_TEST_L( "Test setup" ); +} + +} // local namespace + +//____________________________________________________________________________// + +void +compiler_log_formatter::log_start( std::ostream& output, counter_t test_cases_amount ) +{ + if( test_cases_amount > 0 ) + output << "Running " << test_cases_amount << " test " + << (test_cases_amount > 1 ? "cases" : "case") << "...\n"; +} + +//____________________________________________________________________________// + +void +compiler_log_formatter::log_finish( std::ostream& ostr ) +{ + ostr.flush(); +} + +//____________________________________________________________________________// + +void +compiler_log_formatter::log_build_info( std::ostream& output ) +{ + output << "Platform: " << BOOST_PLATFORM << '\n' + << "Compiler: " << BOOST_COMPILER << '\n' + << "STL : " << BOOST_STDLIB << '\n' + << "Boost : " << BOOST_VERSION/100000 << "." + << BOOST_VERSION/100 % 1000 << "." + << BOOST_VERSION % 100 << std::endl; +} + +//____________________________________________________________________________// + +void +compiler_log_formatter::test_unit_start( std::ostream& output, test_unit const& tu ) +{ + output << "Entering test " << tu.p_type_name << " \"" << tu.p_name << "\"" << std::endl; +} + +//____________________________________________________________________________// + +void +compiler_log_formatter::test_unit_finish( std::ostream& output, test_unit const& tu, unsigned long elapsed ) +{ + output << "Leaving test " << tu.p_type_name << " \"" << tu.p_name << "\""; + + if( elapsed > 0 ) { + output << "; testing time: "; + if( elapsed % 1000 == 0 ) + output << elapsed/1000 << "ms"; + else + output << elapsed << "mks"; + } + + output << std::endl; +} + +//____________________________________________________________________________// + +void +compiler_log_formatter::test_unit_skipped( std::ostream& output, test_unit const& tu ) +{ + output << "Test " << tu.p_type_name << " \"" << tu.p_name << "\"" << "is skipped" << std::endl; +} + +//____________________________________________________________________________// + +void +compiler_log_formatter::log_exception( std::ostream& output, log_checkpoint_data const& checkpoint_data, execution_exception const& ex ) +{ + execution_exception::location const& loc = ex.where(); + print_prefix( output, loc.m_file_name, loc.m_line_num ); + + output << "fatal error in \"" << (loc.m_function.is_empty() ? test_phase_identifier() : loc.m_function ) << "\": "; + + output << ex.what(); + + if( !checkpoint_data.m_file_name.is_empty() ) { + output << '\n'; + print_prefix( output, checkpoint_data.m_file_name, checkpoint_data.m_line_num ); + output << "last checkpoint"; + if( !checkpoint_data.m_message.empty() ) + output << ": " << checkpoint_data.m_message; + } + + output << std::endl; +} + +//____________________________________________________________________________// + +void +compiler_log_formatter::log_entry_start( std::ostream& output, log_entry_data const& entry_data, log_entry_types let ) +{ + switch( let ) { + case BOOST_UTL_ET_INFO: + print_prefix( output, entry_data.m_file_name, entry_data.m_line_num ); + output << "info: "; + break; + case BOOST_UTL_ET_MESSAGE: + break; + case BOOST_UTL_ET_WARNING: + print_prefix( output, entry_data.m_file_name, entry_data.m_line_num ); + output << "warning in \"" << test_phase_identifier() << "\": "; + break; + case BOOST_UTL_ET_ERROR: + print_prefix( output, entry_data.m_file_name, entry_data.m_line_num ); + output << "error in \"" << test_phase_identifier() << "\": "; + break; + case BOOST_UTL_ET_FATAL_ERROR: + print_prefix( output, entry_data.m_file_name, entry_data.m_line_num ); + output << "fatal error in \"" << test_phase_identifier() << "\": "; + break; + } +} + +//____________________________________________________________________________// + +void +compiler_log_formatter::log_entry_value( std::ostream& output, const_string value ) +{ + output << value; +} + +//____________________________________________________________________________// + +void +compiler_log_formatter::log_entry_value( std::ostream& output, lazy_ostream const& value ) +{ + output << value; +} + +//____________________________________________________________________________// + +void +compiler_log_formatter::log_entry_finish( std::ostream& output ) +{ + output << std::endl; +} + +//____________________________________________________________________________// + +void +compiler_log_formatter::print_prefix( std::ostream& output, const_string file, std::size_t line ) +{ +#ifdef __APPLE_CC__ + // Xcode-compatible logging format, idea by Richard Dingwall at + // <http://richarddingwall.name/2008/06/01/using-the-boost-unit-test-framework-with-xcode-3/>. + output << file << ':' << line << ": "; +#else + output << file << '(' << line << "): "; +#endif +} + +//____________________________________________________________________________// + +} // namespace output + +} // namespace unit_test + +} // namespace boost + +//____________________________________________________________________________// + +#include <boost/test/detail/enable_warnings.hpp> + +#endif // BOOST_TEST_COMPILER_LOG_FORMATTER_IPP_020105GER diff --git a/boost/test/impl/cpp_main.ipp b/boost/test/impl/cpp_main.ipp new file mode 100644 index 0000000000..e5156e49e6 --- /dev/null +++ b/boost/test/impl/cpp_main.ipp @@ -0,0 +1,139 @@ +// (C) Copyright Gennadiy Rozental 2001-2008. +// (C) Copyright Beman Dawes 1995-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$ +// +// Version : $Revision: 49312 $ +// +// Description : main function implementation for Program Executon Monitor +// *************************************************************************** + +#ifndef BOOST_TEST_CPP_MAIN_IPP_012205GER +#define BOOST_TEST_CPP_MAIN_IPP_012205GER + +// Boost.Test +#include <boost/test/execution_monitor.hpp> +#include <boost/test/detail/config.hpp> +#include <boost/test/utils/basic_cstring/io.hpp> + +// Boost +#include <boost/cstdlib.hpp> // for exit codes +#include <boost/config.hpp> // for workarounds + +// STL +#include <iostream> +#include <cstdlib> // std::getenv +#include <cstring> // std::strerror + +#include <boost/test/detail/suppress_warnings.hpp> + +//____________________________________________________________________________// + +#ifdef BOOST_NO_STDC_NAMESPACE +namespace std { using ::getenv; using ::strerror; } +#endif + +namespace { + +struct cpp_main_caller { + cpp_main_caller( int (*cpp_main_func)( int argc, char* argv[] ), int argc, char** argv ) + : m_cpp_main_func( cpp_main_func ) + , m_argc( argc ) + , m_argv( argv ) {} + + int operator()() { return (*m_cpp_main_func)( m_argc, m_argv ); } + +private: + // Data members + int (*m_cpp_main_func)( int argc, char* argv[] ); + int m_argc; + char** m_argv; +}; + +} // local namespace + +// ************************************************************************** // +// ************** prg_exec_monitor_main ************** // +// ************************************************************************** // + +namespace boost { + +int BOOST_TEST_DECL +prg_exec_monitor_main( int (*cpp_main)( int argc, char* argv[] ), int argc, char* argv[] ) +{ + int result = 0; + + try { + boost::unit_test::const_string p( std::getenv( "BOOST_TEST_CATCH_SYSTEM_ERRORS" ) ); + ::boost::execution_monitor ex_mon; + + ex_mon.p_catch_system_errors.value = p != "no"; + + result = ex_mon.execute( + ::boost::unit_test::callback0<int>( cpp_main_caller( cpp_main, argc, argv ) ) ); + + if( result == 0 ) + result = ::boost::exit_success; + else if( result != ::boost::exit_success ) { + std::cout << "\n**** error return code: " << result << std::endl; + result = ::boost::exit_failure; + } + } + catch( ::boost::execution_exception const& exex ) { + std::cout << "\n**** exception(" << exex.code() << "): " << exex.what() << std::endl; + result = ::boost::exit_exception_failure; + } + catch( ::boost::system_error const& ex ) { + std::cout << "\n**** failed to initialize execution monitor." + << "\n**** expression at fault: " << ex.p_failed_exp + << "\n**** error(" << ex.p_errno << "): " << std::strerror( ex.p_errno ) << std::endl; + result = ::boost::exit_exception_failure; + } + + if( result != ::boost::exit_success ) { + std::cerr << "******** errors detected; see standard output for details ********" << std::endl; + } + else { + // Some prefer a confirming message when all is well, while others don't + // like the clutter. Use an environment variable to avoid command + // line argument modifications; for use in production programs + // that's a no-no in some organizations. + ::boost::unit_test::const_string p( std::getenv( "BOOST_PRG_MON_CONFIRM" ) ); + if( p != "no" ) { + std::cerr << std::flush << "no errors detected" << std::endl; + } + } + + return result; +} + +} // namespace boost + +#if !defined(BOOST_TEST_DYN_LINK) && !defined(BOOST_TEST_NO_MAIN) + +// ************************************************************************** // +// ************** main function for tests using lib ************** // +// ************************************************************************** // + +int cpp_main( int argc, char* argv[] ); // prototype for user's cpp_main() + +int BOOST_TEST_CALL_DECL +main( int argc, char* argv[] ) +{ + return ::boost::prg_exec_monitor_main( &cpp_main, argc, argv ); +} + +//____________________________________________________________________________// + +#endif // !BOOST_TEST_DYN_LINK && !BOOST_TEST_NO_MAIN + +//____________________________________________________________________________// + +#include <boost/test/detail/enable_warnings.hpp> + +#endif // BOOST_TEST_CPP_MAIN_IPP_012205GER diff --git a/boost/test/impl/debug.ipp b/boost/test/impl/debug.ipp new file mode 100644 index 0000000000..d351271f92 --- /dev/null +++ b/boost/test/impl/debug.ipp @@ -0,0 +1,970 @@ +// (C) Copyright Gennadiy Rozental 2006-2008. +// Use, modification, and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/libs/test for the library home page. +// +// File : $RCSfile$ +// +// Version : $Revision: 57992 $ +// +// Description : debug interfaces implementation +// *************************************************************************** + +#ifndef BOOST_TEST_DEBUG_API_IPP_112006GER +#define BOOST_TEST_DEBUG_API_IPP_112006GER + +// Boost.Test +#include <boost/test/detail/config.hpp> +#include <boost/test/detail/workaround.hpp> +#include <boost/test/detail/global_typedef.hpp> + +#include <boost/test/debug.hpp> +#include <boost/test/debug_config.hpp> + +// Implementation on Windows +#if defined(_WIN32) && !defined(UNDER_CE) && !defined(BOOST_DISABLE_WIN32) // ******* WIN32 + +# define BOOST_WIN32_BASED_DEBUG + +// SYSTEM API +# include <windows.h> +# include <winreg.h> +# include <cstdio> +# include <cstring> + +# if !defined(NDEBUG) && defined(_MSC_VER) +# define BOOST_MS_CRT_BASED_DEBUG +# include <crtdbg.h> +# endif + + +# if BOOST_WORKAROUND( BOOST_MSVC, <1300) +# define snprintf _snprintf +# endif + +# ifdef BOOST_NO_STDC_NAMESPACE +namespace std { using ::memset; using ::sprintf; } +# endif + +#elif defined(unix) || defined(__unix) // ********************* UNIX + +# define BOOST_UNIX_BASED_DEBUG + +// Boost.Test +#include <boost/test/utils/class_properties.hpp> +#include <boost/test/utils/algorithm.hpp> + +// STL +#include <cstring> // std::memcpy +#include <map> +#include <cstdio> +#include <stdarg.h> // !! ?? cstdarg + +// SYSTEM API +# include <unistd.h> +# include <signal.h> +# include <fcntl.h> + +# include <sys/types.h> +# include <sys/stat.h> +# include <sys/wait.h> +# include <sys/time.h> +# include <stdio.h> +# include <stdlib.h> + +# if defined(sun) || defined(__sun) + +# define BOOST_SUN_BASED_DEBUG + +# ifndef BOOST_TEST_DBG_LIST +# define BOOST_TEST_DBG_LIST dbx;gdb +# endif + +# define BOOST_TEST_CNL_DBG dbx +# define BOOST_TEST_GUI_DBG dbx-ddd + +# include <procfs.h> + +# elif defined(linux) || defined(__linux) + +# define BOOST_LINUX_BASED_DEBUG + +# include <sys/ptrace.h> + +# ifndef BOOST_TEST_STAT_LINE_MAX +# define BOOST_TEST_STAT_LINE_MAX 500 +# endif + +# ifndef BOOST_TEST_DBG_LIST +# define BOOST_TEST_DBG_LIST gdb +# endif + +# define BOOST_TEST_CNL_DBG gdb +# define BOOST_TEST_GUI_DBG gdb-xterm + +# endif + +#endif + +#include <boost/test/detail/suppress_warnings.hpp> + +//____________________________________________________________________________// + +namespace boost { + +namespace debug { + +using unit_test::const_string; + +// ************************************************************************** // +// ************** debug::info_t ************** // +// ************************************************************************** // + +namespace { + +#if defined(BOOST_WIN32_BASED_DEBUG) // *********************** WIN32 + +template<typename T> +inline void +dyn_symbol( T& res, char const* module_name, char const* symbol_name ) +{ + HMODULE m = ::GetModuleHandleA( module_name ); + + if( !m ) + m = ::LoadLibraryA( module_name ); + + res = reinterpret_cast<T>( ::GetProcAddress( m, symbol_name ) ); +} + +//____________________________________________________________________________// + +static struct info_t { + typedef BOOL (WINAPI* IsDebuggerPresentT)(); + typedef LONG (WINAPI* RegQueryValueExT)( HKEY, char const* /*LPTSTR*/, LPDWORD, LPDWORD, LPBYTE, LPDWORD ); + typedef LONG (WINAPI* RegOpenKeyT)( HKEY, char const* /*LPCTSTR*/, PHKEY ); + typedef LONG (WINAPI* RegCloseKeyT)( HKEY ); + + info_t(); + + IsDebuggerPresentT m_is_debugger_present; + RegOpenKeyT m_reg_open_key; + RegQueryValueExT m_reg_query_value; + RegCloseKeyT m_reg_close_key; + +} s_info; + +//____________________________________________________________________________// + +info_t::info_t() +{ + dyn_symbol( m_is_debugger_present, "kernel32", "IsDebuggerPresent" ); + dyn_symbol( m_reg_open_key, "advapi32", "RegOpenKeyA" ); + dyn_symbol( m_reg_query_value, "advapi32", "RegQueryValueExA" ); + dyn_symbol( m_reg_close_key, "advapi32", "RegCloseKey" ); +} + +//____________________________________________________________________________// + +#elif defined(BOOST_UNIX_BASED_DEBUG) + +// ************************************************************************** // +// ************** fd_holder ************** // +// ************************************************************************** // + +struct fd_holder { + explicit fd_holder( int fd ) : m_fd( fd ) {} + ~fd_holder() + { + if( m_fd != -1 ) + ::close( m_fd ); + } + + operator int() { return m_fd; } + +private: + // Data members + int m_fd; +}; + + +// ************************************************************************** // +// ************** process_info ************** // +// ************************************************************************** // + +struct process_info { + // Constructor + explicit process_info( int pid ); + + // access methods + int parent_pid() const { return m_parent_pid; } + const_string binary_name() const { return m_binary_name; } + const_string binary_path() const { return m_binary_path; } + +private: + // Data members + int m_parent_pid; + const_string m_binary_name; + const_string m_binary_path; + +#if defined(BOOST_SUN_BASED_DEBUG) + struct psinfo m_psi; +#elif defined(BOOST_LINUX_BASED_DEBUG) + char m_stat_line[BOOST_TEST_STAT_LINE_MAX+1]; +#endif + char m_binary_path_buff[500+1]; // !! ?? +}; + +//____________________________________________________________________________// + +process_info::process_info( int pid ) +: m_parent_pid( 0 ) +{ +#if defined(BOOST_SUN_BASED_DEBUG) + char fname_buff[30]; + + ::snprintf( fname_buff, sizeof(fname_buff), "/proc/%d/psinfo", pid ); + + fd_holder psinfo_fd( ::open( fname_buff, O_RDONLY ) ); + + if( psinfo_fd == -1 ) + return; + + if( ::read( psinfo_fd, &m_psi, sizeof(m_psi) ) == -1 ) + return; + + m_parent_pid = m_psi.pr_ppid; + + m_binary_name.assign( m_psi.pr_fname ); + + //-------------------------- // + + ::snprintf( fname_buff, sizeof(fname_buff), "/proc/%d/as", pid ); + + fd_holder as_fd( ::open( fname_buff, O_RDONLY ) ); + uintptr_t binary_name_pos; + + // !! ?? could we avoid reading whole m_binary_path_buff? + if( as_fd == -1 || + ::lseek( as_fd, m_psi.pr_argv, SEEK_SET ) == -1 || + ::read ( as_fd, &binary_name_pos, sizeof(binary_name_pos) ) == -1 || + ::lseek( as_fd, binary_name_pos, SEEK_SET ) == -1 || + ::read ( as_fd, m_binary_path_buff, sizeof(m_binary_path_buff) ) == -1 ) + return; + + m_binary_path.assign( m_binary_path_buff ); + +#elif defined(BOOST_LINUX_BASED_DEBUG) + char fname_buff[30]; + + ::snprintf( fname_buff, sizeof(fname_buff), "/proc/%d/stat", pid ); + + fd_holder psinfo_fd( ::open( fname_buff, O_RDONLY ) ); + + if( psinfo_fd == -1 ) + return; + + ssize_t num_read = ::read( psinfo_fd, m_stat_line, sizeof(m_stat_line)-1 ); + if( num_read == -1 ) + return; + + m_stat_line[num_read] = 0; + + char const* name_beg = m_stat_line; + while( *name_beg && *name_beg != '(' ) + ++name_beg; + + char const* name_end = name_beg+1; + while( *name_end && *name_end != ')' ) + ++name_end; + + std::sscanf( name_end+1, "%*s%d", &m_parent_pid ); + + m_binary_name.assign( name_beg+1, name_end ); + + ::snprintf( fname_buff, sizeof(fname_buff), "/proc/%d/exe", pid ); + num_read = ::readlink( fname_buff, m_binary_path_buff, sizeof(m_binary_path_buff)-1 ); + + if( num_read == -1 ) + return; + + m_binary_path_buff[num_read] = 0; + m_binary_path.assign( m_binary_path_buff, num_read ); +#endif +} + +//____________________________________________________________________________// + +// ************************************************************************** // +// ************** prepare_window_title ************** // +// ************************************************************************** // + +static char* +prepare_window_title( dbg_startup_info const& dsi ) +{ + typedef unit_test::const_string str_t; + + static char title_str[50]; + + str_t path_sep( "\\/" ); + + str_t::iterator it = unit_test::find_last_of( dsi.binary_path.begin(), dsi.binary_path.end(), + path_sep.begin(), path_sep.end() ); + + if( it == dsi.binary_path.end() ) + it = dsi.binary_path.begin(); + else + ++it; + + ::snprintf( title_str, sizeof(title_str), "%*s %ld", (int)(dsi.binary_path.end()-it), it, dsi.pid ); + + return title_str; +} + +//____________________________________________________________________________// + +// ************************************************************************** // +// ************** save_execlp ************** // +// ************************************************************************** // + +typedef unit_test::basic_cstring<char> mbuffer; + +inline char* +copy_arg( mbuffer& dest, const_string arg ) +{ + if( dest.size() < arg.size()+1 ) + return 0; + + char* res = dest.begin(); + + std::memcpy( res, arg.begin(), arg.size()+1 ); + + dest.trim_left( arg.size()+1 ); + + return res; +} + +//____________________________________________________________________________// + +bool +safe_execlp( char const* file, ... ) +{ + static char* argv_buff[200]; + + va_list args; + char const* arg; + + // first calculate actual number of arguments + int num_args = 2; // file name and 0 at least + + va_start( args, file ); + while( !!(arg = va_arg( args, char const* )) ) + num_args++; + va_end( args ); + + // reserve space for the argument pointers array + char** argv_it = argv_buff; + mbuffer work_buff( reinterpret_cast<char*>(argv_buff), sizeof(argv_buff) ); + work_buff.trim_left( num_args * sizeof(char*) ); + + // copy all the argument values into local storage + if( !(*argv_it++ = copy_arg( work_buff, file )) ) + return false; + + printf( "!! %s\n", file ); + + va_start( args, file ); + while( !!(arg = va_arg( args, char const* )) ) { + printf( "!! %s\n", arg ); + if( !(*argv_it++ = copy_arg( work_buff, arg )) ) + return false; + } + va_end( args ); + + *argv_it = 0; + + return ::execvp( file, argv_buff ) != -1; +} + +//____________________________________________________________________________// + +// ************************************************************************** // +// ************** start_debugger_in_emacs ************** // +// ************************************************************************** // + +static void +start_debugger_in_emacs( dbg_startup_info const& dsi, char const* emacs_name, char const* dbg_command ) +{ + char const* title = prepare_window_title( dsi ); + + if( !title ) + return; + + dsi.display.is_empty() + ? safe_execlp( emacs_name, "-title", title, "--eval", dbg_command, 0 ) + : safe_execlp( emacs_name, "-title", title, "-display", dsi.display.begin(), "--eval", dbg_command, 0 ); +} + +//____________________________________________________________________________// + +// ************************************************************************** // +// ************** gdb starters ************** // +// ************************************************************************** // + +static char const* +prepare_gdb_cmnd_file( dbg_startup_info const& dsi ) +{ + // prepare pid value + char pid_buff[16]; + ::snprintf( pid_buff, sizeof(pid_buff), "%ld", dsi.pid ); + unit_test::const_string pid_str( pid_buff ); + + static char cmd_file_name[] = "/tmp/btl_gdb_cmd_XXXXXX"; // !! ?? + + // prepare commands + fd_holder cmd_fd( ::mkstemp( cmd_file_name ) ); + + if( cmd_fd == -1 ) + return 0; + +#define WRITE_STR( str ) if( ::write( cmd_fd, str.begin(), str.size() ) == -1 ) return 0; +#define WRITE_CSTR( str ) if( ::write( cmd_fd, str, sizeof( str )-1 ) == -1 ) return 0; + + WRITE_CSTR( "file " ); + WRITE_STR( dsi.binary_path ); + WRITE_CSTR( "\nattach " ); + WRITE_STR( pid_str ); + WRITE_CSTR( "\nshell unlink " ); + WRITE_STR( dsi.init_done_lock ); + WRITE_CSTR( "\ncont" ); + if( dsi.break_or_continue ) + WRITE_CSTR( "\nup 4" ); + + WRITE_CSTR( "\necho \\n" ); // !! ?? + WRITE_CSTR( "\nlist -" ); + WRITE_CSTR( "\nlist" ); + WRITE_CSTR( "\nshell unlink " ); + WRITE_CSTR( cmd_file_name ); + + return cmd_file_name; +} + +//____________________________________________________________________________// + +static void +start_gdb_in_console( dbg_startup_info const& dsi ) +{ + char const* cmnd_file_name = prepare_gdb_cmnd_file( dsi ); + + if( !cmnd_file_name ) + return; + + safe_execlp( "gdb", "-q", "-x", cmnd_file_name, 0 ); +} + +//____________________________________________________________________________// + +static void +start_gdb_in_xterm( dbg_startup_info const& dsi ) +{ + char const* title = prepare_window_title( dsi ); + char const* cmnd_file_name = prepare_gdb_cmnd_file( dsi ); + + if( !title || !cmnd_file_name ) + return; + + safe_execlp( "xterm", "-T", title, "-display", dsi.display.begin(), + "-bg", "black", "-fg", "white", "-geometry", "88x30+10+10", "-fn", "9x15", "-e", + "gdb", "-q", "-x", cmnd_file_name, 0 ); +} + +//____________________________________________________________________________// + +static void +start_gdb_in_emacs( dbg_startup_info const& dsi ) +{ + char const* cmnd_file_name = prepare_gdb_cmnd_file( dsi ); + if( !cmnd_file_name ) + return; + + char dbg_cmd_buff[500]; // !! ?? + ::snprintf( dbg_cmd_buff, sizeof(dbg_cmd_buff), "(progn (gdb \"gdb -q -x %s\"))", cmnd_file_name ); + + start_debugger_in_emacs( dsi, "emacs", dbg_cmd_buff ); +} + +//____________________________________________________________________________// + +static void +start_gdb_in_xemacs( dbg_startup_info const& ) +{ + // !! ?? +} + +//____________________________________________________________________________// + +// ************************************************************************** // +// ************** dbx starters ************** // +// ************************************************************************** // + +static char const* +prepare_dbx_cmd_line( dbg_startup_info const& dsi, bool list_source = true ) +{ + static char cmd_line_buff[500]; // !! ?? + + ::snprintf( cmd_line_buff, sizeof(cmd_line_buff), "unlink %s;cont;%s%s", + dsi.init_done_lock.begin(), + dsi.break_or_continue ? "up 2;": "", + list_source ? "echo \" \";list -w3;" : "" ); + + return cmd_line_buff; +} + +//____________________________________________________________________________// + +static void +start_dbx_in_console( dbg_startup_info const& dsi ) +{ + char pid_buff[16]; + ::snprintf( pid_buff, sizeof(pid_buff), "%ld", dsi.pid ); + + safe_execlp( "dbx", "-q", "-c", prepare_dbx_cmd_line( dsi ), dsi.binary_path.begin(), pid_buff, 0 ); +} + +//____________________________________________________________________________// + +static void +start_dbx_in_xterm( dbg_startup_info const& dsi ) +{ + char const* title = prepare_window_title( dsi ); + if( !title ) + return; + + char pid_buff[16]; // !! ?? + ::snprintf( pid_buff, sizeof(pid_buff), "%ld", dsi.pid ); + + safe_execlp( "xterm", "-T", title, "-display", dsi.display.begin(), + "-bg", "black", "-fg", "white", "-geometry", "88x30+10+10", "-fn", "9x15", "-e", + "dbx", "-q", "-c", prepare_dbx_cmd_line( dsi ), dsi.binary_path.begin(), pid_buff, 0 ); +} + +//____________________________________________________________________________// + +static void +start_dbx_in_emacs( dbg_startup_info const& /*dsi*/ ) +{ +// char dbg_cmd_buff[500]; // !! ?? +// +// ::snprintf( dbg_cmd_buff, sizeof(dbg_cmd_buff), "(progn (dbx \"dbx -q -c cont %s %ld\"))", dsi.binary_path.begin(), dsi.pid ); + +// start_debugger_in_emacs( dsi, "emacs", dbg_cmd_buff ); +} + +//____________________________________________________________________________// + +static void +start_dbx_in_xemacs( dbg_startup_info const& ) +{ + // !! ?? +} + +//____________________________________________________________________________// + +static void +start_dbx_in_ddd( dbg_startup_info const& dsi ) +{ + char const* title = prepare_window_title( dsi ); + if( !title ) + return; + + char pid_buff[16]; // !! ?? + ::snprintf( pid_buff, sizeof(pid_buff), "%ld", dsi.pid ); + + safe_execlp( "ddd", "-display", dsi.display.begin(), + "--dbx", "-q", "-c", prepare_dbx_cmd_line( dsi, false ), dsi.binary_path.begin(), pid_buff, 0 ); +} + +//____________________________________________________________________________// + +// ************************************************************************** // +// ************** debug::info_t ************** // +// ************************************************************************** // + +static struct info_t { + // Constructor + info_t(); + + // Public properties + unit_test::readwrite_property<std::string> p_dbg; + + // Data members + std::map<std::string,dbg_starter> m_dbg_starter_reg; +} s_info; + +//____________________________________________________________________________// + +info_t::info_t() +{ + p_dbg.value = ::getenv( "DISPLAY" ) + ? std::string( BOOST_STRINGIZE( BOOST_TEST_GUI_DBG ) ) + : std::string( BOOST_STRINGIZE( BOOST_TEST_CNL_DBG ) ); + + m_dbg_starter_reg[std::string("gdb")] = &start_gdb_in_console; + m_dbg_starter_reg[std::string("gdb-emacs")] = &start_gdb_in_emacs; + m_dbg_starter_reg[std::string("gdb-xterm")] = &start_gdb_in_xterm; + m_dbg_starter_reg[std::string("gdb-xemacs")] = &start_gdb_in_xemacs; + + m_dbg_starter_reg[std::string("dbx")] = &start_dbx_in_console; + m_dbg_starter_reg[std::string("dbx-emacs")] = &start_dbx_in_emacs; + m_dbg_starter_reg[std::string("dbx-xterm")] = &start_dbx_in_xterm; + m_dbg_starter_reg[std::string("dbx-xemacs")] = &start_dbx_in_xemacs; + m_dbg_starter_reg[std::string("dbx-ddd")] = &start_dbx_in_ddd; +} + +//____________________________________________________________________________// + +#endif + +} // local namespace + +// ************************************************************************** // +// ************** check if program is running under debugger ************** // +// ************************************************************************** // + +bool +under_debugger() +{ +#if defined(BOOST_WIN32_BASED_DEBUG) // *********************** WIN32 + + return !!s_info.m_is_debugger_present && s_info.m_is_debugger_present(); + +#elif defined(BOOST_UNIX_BASED_DEBUG) // ********************** UNIX + + // !! ?? could/should we cache the result somehow? + const_string dbg_list = BOOST_TEST_STRINGIZE( BOOST_TEST_DBG_LIST ); + + pid_t pid = ::getpid(); + + while( pid != 0 ) { + process_info pi( pid ); + + // !! ?? should we use tokenizer here instead? + if( dbg_list.find( pi.binary_name() ) != const_string::npos ) + return true; + + pid = (pi.parent_pid() == pid ? 0 : pi.parent_pid()); + } + + return false; + +#else // ****************************************************** default + + return false; + +#endif +} + +//____________________________________________________________________________// + +// ************************************************************************** // +// ************** cause program to break execution ************** // +// ************** in debugger at call point ************** // +// ************************************************************************** // + +void +debugger_break() +{ + // !! ?? auto-start debugger? + +#if defined(BOOST_WIN32_BASED_DEBUG) // *********************** WIN32 + +#if BOOST_WORKAROUND(BOOST_MSVC, >= 1300) || \ + BOOST_WORKAROUND(__GNUC__, >= 3) && !defined(__MINGW32__) || \ + defined(__INTEL_COMPILER) +# define BOOST_DEBUG_BREAK __debugbreak +#else +# define BOOST_DEBUG_BREAK DebugBreak +#endif + +#ifndef __MINGW32__ + if( !under_debugger() ) { + __try { + __try { + BOOST_DEBUG_BREAK(); + } + __except( UnhandledExceptionFilter(GetExceptionInformation()) ) + { + // User opted to ignore the breakpoint + return; + } + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + // If we got here, the user has pushed Debug. Debugger is already attached to our process and we + // continue to let the another BOOST_DEBUG_BREAK to be called. + } + } +#endif + + BOOST_DEBUG_BREAK(); + +#elif defined(BOOST_UNIX_BASED_DEBUG) // ********************** UNIX + + ::kill( ::getpid(), SIGTRAP ); + +#else // ****************************************************** default + +#endif +} + +//____________________________________________________________________________// + +// ************************************************************************** // +// ************** console debugger setup ************** // +// ************************************************************************** // + +#if defined(BOOST_UNIX_BASED_DEBUG) // ************************ UNIX + +std::string +set_debugger( unit_test::const_string dbg_id, dbg_starter s ) +{ + std::string old = s_info.p_dbg; + + assign_op( s_info.p_dbg.value, dbg_id, 0 ); + + if( !!s ) + s_info.m_dbg_starter_reg[s_info.p_dbg] = s; + + return old; +} + +#else // ***************************************************** default + +std::string +set_debugger( unit_test::const_string, dbg_starter ) +{ + return std::string(); +} + +#endif + +//____________________________________________________________________________// + +// ************************************************************************** // +// ************** attach debugger to the current process ************** // +// ************************************************************************** // + +bool +attach_debugger( bool break_or_continue ) +{ + if( under_debugger() ) + return false; + +#if defined(BOOST_WIN32_BASED_DEBUG) // *********************** WIN32 + + const int MAX_CMD_LINE = 200; + + // *************************************************** // + // Debugger "ready" event + + SECURITY_ATTRIBUTES attr; + attr.nLength = sizeof(attr); + attr.lpSecurityDescriptor = NULL; + attr.bInheritHandle = true; + + // manual resettable, initially non signaled, unnamed event, + // that will signal me that debugger initialization is done + HANDLE dbg_init_done_ev = ::CreateEvent( + &attr, // pointer to security attributes + true, // flag for manual-reset event + false, // flag for initial state + NULL // pointer to event-object name + ); + + if( !dbg_init_done_ev ) + return false; + + // *************************************************** // + // Debugger command line format + + HKEY reg_key; + + if( !s_info.m_reg_open_key || (*s_info.m_reg_open_key)( + HKEY_LOCAL_MACHINE, // handle of open key + "Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug", // name of subkey to open + ®_key ) != ERROR_SUCCESS ) // address of handle of open key + return false; + + char format[MAX_CMD_LINE]; + DWORD format_size = MAX_CMD_LINE; + DWORD type = REG_SZ; + + if( !s_info.m_reg_query_value || (*s_info.m_reg_query_value)( + reg_key, // handle of open key + "Debugger", // name of subkey to query + 0, // reserved + &type, // value type + (LPBYTE)format, // buffer for returned string + &format_size ) != ERROR_SUCCESS ) // in: buffer size; out: actual size of returned string + return false; + + if( !s_info.m_reg_close_key || (*s_info.m_reg_close_key)( reg_key ) != ERROR_SUCCESS ) + return false; + + // *************************************************** // + // Debugger command line + + char cmd_line[MAX_CMD_LINE]; + std::sprintf( cmd_line, format, ::GetCurrentProcessId(), dbg_init_done_ev ); + + // *************************************************** // + // Debugger window parameters + + STARTUPINFOA startup_info; + std::memset( &startup_info, 0, sizeof(startup_info) ); + + startup_info.cb = sizeof(startup_info); + startup_info.dwFlags = STARTF_USESHOWWINDOW; + startup_info.wShowWindow = SW_SHOWNORMAL; + + // debugger process s_info + PROCESS_INFORMATION debugger_info; + + bool created = !!::CreateProcessA( + NULL, // pointer to name of executable module; NULL - use the one in command line + cmd_line, // pointer to command line string + NULL, // pointer to process security attributes; NULL - debugger's handle can't be inherited + NULL, // pointer to thread security attributes; NULL - debugger's handle can't be inherited + true, // debugger inherit opened handles + 0, // priority flags; 0 - normal priority + NULL, // pointer to new environment block; NULL - use this process environment + NULL, // pointer to current directory name; NULL - use this process correct directory + &startup_info, // pointer to STARTUPINFO that specifies main window appearance + &debugger_info // pointer to PROCESS_INFORMATION that will contain the new process identification + ); + + if( created ) + ::WaitForSingleObject( dbg_init_done_ev, INFINITE ); + + ::CloseHandle( dbg_init_done_ev ); + + if( !created ) + return false; + + if( break_or_continue ) + debugger_break(); + + return true; + +#elif defined(BOOST_UNIX_BASED_DEBUG) // ********************** UNIX + + char init_done_lock_fn[] = "/tmp/btl_dbg_init_done_XXXXXX"; + fd_holder init_done_lock_fd( ::mkstemp( init_done_lock_fn ) ); + + if( init_done_lock_fd == -1 ) + return false; + + pid_t child_pid = fork(); + + if( child_pid == -1 ) + return false; + + if( child_pid != 0 ) { // parent process - here we will start the debugger + dbg_startup_info dsi; + + process_info pi( child_pid ); + if( pi.binary_path().is_empty() ) + ::exit( -1 ); + + dsi.pid = child_pid; + dsi.break_or_continue = break_or_continue; + dsi.binary_path = pi.binary_path(); + dsi.display = ::getenv( "DISPLAY" ); + dsi.init_done_lock = init_done_lock_fn; + + dbg_starter starter = s_info.m_dbg_starter_reg[s_info.p_dbg]; + if( !!starter ) + starter( dsi ); + + ::perror( "Boost.Test execution monitor failed to start a debugger:" ); + + ::exit( -1 ); + } + + // child process - here we will continue our test module execution ; // !! ?? should it be vice versa + + while( ::access( init_done_lock_fn, F_OK ) == 0 ) { + struct timeval to = { 0, 100 }; + + ::select( 0, 0, 0, 0, &to ); + } + +// char dummy; +// while( ::read( init_done_lock_fd, &dummy, sizeof(char) ) == 0 ); + + if( break_or_continue ) + debugger_break(); + + return true; + +#else // ****************************************************** default + + return false; + +#endif +} + +//____________________________________________________________________________// + +// ************************************************************************** // +// ************** switch on/off detect memory leaks feature ************** // +// ************************************************************************** // + +void +detect_memory_leaks( bool on_off ) +{ + unit_test::ut_detail::ignore_unused_variable_warning( on_off ); + +#ifdef BOOST_MS_CRT_BASED_DEBUG + int flags = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG ); + + if( !on_off ) + flags &= ~_CRTDBG_LEAK_CHECK_DF; + else { + flags |= _CRTDBG_LEAK_CHECK_DF; + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT); + } + + _CrtSetDbgFlag ( flags ); +#endif // BOOST_MS_CRT_BASED_DEBUG +} + +//____________________________________________________________________________// + +// ************************************************************************** // +// ************** cause program to break execution in ************** // +// ************** debugger at specific allocation point ************** // +// ************************************************************************** // + +void +break_memory_alloc( long mem_alloc_order_num ) +{ + unit_test::ut_detail::ignore_unused_variable_warning( mem_alloc_order_num ); + +#ifdef BOOST_MS_CRT_BASED_DEBUG + _CrtSetBreakAlloc( mem_alloc_order_num ); +#endif // BOOST_MS_CRT_BASED_DEBUG +} + +} // namespace debug + +} // namespace boost + +//____________________________________________________________________________// + +#include <boost/test/detail/enable_warnings.hpp> + +#endif // BOOST_TEST_DEBUG_API_IPP_112006GER + diff --git a/boost/test/impl/exception_safety.ipp b/boost/test/impl/exception_safety.ipp new file mode 100644 index 0000000000..e2d529a810 --- /dev/null +++ b/boost/test/impl/exception_safety.ipp @@ -0,0 +1,537 @@ +// (C) Copyright Gennadiy Rozental 2005-2008. +// Use, modification, and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// 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 : Facilities to perform exception safety tests +// *************************************************************************** + +#ifndef BOOST_TEST_EXECUTION_SAFETY_IPP_112005GER +#define BOOST_TEST_EXECUTION_SAFETY_IPP_112005GER + +// Boost.Test +#include <boost/test/detail/config.hpp> + +#if BOOST_TEST_SUPPORT_INTERACTION_TESTING + +#include <boost/test/detail/global_typedef.hpp> +#include <boost/test/detail/unit_test_parameters.hpp> + +#include <boost/test/utils/callback.hpp> +#include <boost/test/utils/wrap_stringstream.hpp> +#include <boost/test/utils/iterator/token_iterator.hpp> + +#include <boost/test/interaction_based.hpp> +#include <boost/test/test_tools.hpp> +#include <boost/test/unit_test_log.hpp> +#include <boost/test/framework.hpp> +#include <boost/test/test_observer.hpp> +#include <boost/test/debug.hpp> + +#include <boost/test/detail/suppress_warnings.hpp> + +// Boost +#include <boost/lexical_cast.hpp> + +// STL +#include <vector> +#include <cstdlib> +#include <map> +#include <iomanip> +#include <cctype> +#include <boost/limits.hpp> + +//____________________________________________________________________________// + +namespace boost { + +using namespace ::boost::unit_test; + +namespace itest { + +// ************************************************************************** // +// ************** execution_path_point ************** // +// ************************************************************************** // + +enum exec_path_point_type { EPP_SCOPE, EPP_EXCEPT, EPP_DECISION, EPP_ALLOC }; + +struct execution_path_point { + execution_path_point( exec_path_point_type t, const_string file, std::size_t line_num ) + : m_type( t ) + , m_file_name( file ) + , m_line_num( line_num ) + {} + + exec_path_point_type m_type; + const_string m_file_name; + std::size_t m_line_num; + + // Execution path point specific + struct decision_data { + bool value; + unsigned forced_exception_point; + }; + struct scope_data { + unsigned size; + char const* name; + }; + struct except_data { + char const* description; + }; + struct alloc_data { + void* ptr; + std::size_t size; + }; + + union { + struct decision_data m_decision; + struct scope_data m_scope; + struct except_data m_except; + struct alloc_data m_alloc; + }; +}; + +// ************************************************************************** // +// ************** exception safety test implementation ************** // +// ************************************************************************** // + +struct exception_safety_tester : itest::manager, test_observer { + // helpers types + struct unique_exception {}; + + // Constructor + explicit exception_safety_tester( const_string test_name ); + ~exception_safety_tester(); + + // check last run and prepare for next + bool next_execution_path(); + + // memory tracking + + // manager interface implementation + virtual void exception_point( const_string file, std::size_t line_num, const_string description ); + virtual bool decision_point( const_string file, std::size_t line_num ); + virtual unsigned enter_scope( const_string file, std::size_t line_num, const_string scope_name ); + virtual void leave_scope( unsigned enter_scope_point ); + virtual void allocated( const_string file, std::size_t line_num, void* p, std::size_t s ); + virtual void freed( void* p ); + + // test observer interface + virtual void assertion_result( bool passed ); + virtual int priority() { return (std::numeric_limits<int>::max)(); } // we want this observer to run the last + +private: + void failure_point(); + void report_error(); + + typedef std::vector<execution_path_point> exec_path; + typedef std::map<void*,unsigned> registry; + + // Data members + bool m_internal_activity; + + unsigned m_exception_point_counter; + unsigned m_forced_exception_point; + + unsigned m_exec_path_point; + exec_path m_execution_path; + + unsigned m_exec_path_counter; + unsigned m_break_exec_path; + + bool m_invairant_failed; + registry m_memory_in_use; +}; + +//____________________________________________________________________________// + +struct activity_guard { + bool& m_v; + + activity_guard( bool& v ) : m_v( v ) { m_v = true; } + ~activity_guard() { m_v = false; } +}; + +//____________________________________________________________________________// + +exception_safety_tester::exception_safety_tester( const_string test_name ) +: m_internal_activity( true ) +, m_exception_point_counter( 0 ) +, m_forced_exception_point( 1 ) +, m_exec_path_point( 0 ) +, m_exec_path_counter( 1 ) +, m_break_exec_path( static_cast<unsigned>(-1) ) +, m_invairant_failed( false ) +{ + framework::register_observer( *this ); + + if( !runtime_config::break_exec_path().is_empty() ) { + using namespace unit_test; + + string_token_iterator tit( runtime_config::break_exec_path(), + (dropped_delimeters = ":",kept_delimeters = " ") ); + + const_string test_to_break = *tit; + + if( test_to_break == test_name ) { + ++tit; + + m_break_exec_path = lexical_cast<unsigned>( *tit ); + } + } + + m_internal_activity = false; +} + +//____________________________________________________________________________// + +exception_safety_tester::~exception_safety_tester() +{ + m_internal_activity = true; + + framework::deregister_observer( *this ); +} + +//____________________________________________________________________________// + +bool +exception_safety_tester::next_execution_path() +{ + activity_guard ag( m_internal_activity ); + + // check memory usage + if( m_execution_path.size() > 0 ) { + bool errors_detected = m_invairant_failed || (m_memory_in_use.size() != 0); + framework::assertion_result( !errors_detected ); + + if( errors_detected ) + report_error(); + + m_memory_in_use.clear(); + } + + m_exec_path_point = 0; + m_exception_point_counter = 0; + m_invairant_failed = false; + ++m_exec_path_counter; + + while( m_execution_path.size() > 0 ) { + switch( m_execution_path.back().m_type ) { + case EPP_SCOPE: + case EPP_ALLOC: + m_execution_path.pop_back(); + break; + + case EPP_DECISION: + if( !m_execution_path.back().m_decision.value ) { + m_execution_path.pop_back(); + break; + } + + m_execution_path.back().m_decision.value = false; + m_forced_exception_point = m_execution_path.back().m_decision.forced_exception_point; + return true; + + case EPP_EXCEPT: + m_execution_path.pop_back(); + ++m_forced_exception_point; + return true; + } + } + + BOOST_TEST_MESSAGE( "Total tested " << --m_exec_path_counter << " execution path" ); + + return false; +} + +//____________________________________________________________________________// + +void +exception_safety_tester::exception_point( const_string file, std::size_t line_num, const_string description ) +{ + activity_guard ag( m_internal_activity ); + + if( ++m_exception_point_counter == m_forced_exception_point ) { + m_execution_path.push_back( + execution_path_point( EPP_EXCEPT, file, line_num ) ); + + m_execution_path.back().m_except.description = description.begin(); + + ++m_exec_path_point; + + failure_point(); + } +} + +//____________________________________________________________________________// + +bool +exception_safety_tester::decision_point( const_string file, std::size_t line_num ) +{ + activity_guard ag( m_internal_activity ); + + if( m_exec_path_point < m_execution_path.size() ) { + BOOST_REQUIRE_MESSAGE( m_execution_path[m_exec_path_point].m_type == EPP_DECISION && + m_execution_path[m_exec_path_point].m_file_name == file && + m_execution_path[m_exec_path_point].m_line_num == line_num, + "Function under test exibit non-deterministic behavior" ); + } + else { + m_execution_path.push_back( + execution_path_point( EPP_DECISION, file, line_num ) ); + + m_execution_path.back().m_decision.value = true; + m_execution_path.back().m_decision.forced_exception_point = m_forced_exception_point; + } + + return m_execution_path[m_exec_path_point++].m_decision.value; +} + +//____________________________________________________________________________// + +unsigned +exception_safety_tester::enter_scope( const_string file, std::size_t line_num, const_string scope_name ) +{ + activity_guard ag( m_internal_activity ); + + if( m_exec_path_point < m_execution_path.size() ) { + BOOST_REQUIRE_MESSAGE( m_execution_path[m_exec_path_point].m_type == EPP_SCOPE && + m_execution_path[m_exec_path_point].m_file_name == file && + m_execution_path[m_exec_path_point].m_line_num == line_num, + "Function under test exibit non-deterministic behavior" ); + } + else { + m_execution_path.push_back( + execution_path_point( EPP_SCOPE, file, line_num ) ); + } + + m_execution_path[m_exec_path_point].m_scope.size = 0; + m_execution_path[m_exec_path_point].m_scope.name = scope_name.begin(); + + return m_exec_path_point++; +} + +//____________________________________________________________________________// + +void +exception_safety_tester::leave_scope( unsigned enter_scope_point ) +{ + activity_guard ag( m_internal_activity ); + + BOOST_REQUIRE_MESSAGE( m_execution_path[enter_scope_point].m_type == EPP_SCOPE, + "Function under test exibit non-deterministic behavior" ); + + m_execution_path[enter_scope_point].m_scope.size = m_exec_path_point - enter_scope_point; +} + +//____________________________________________________________________________// + +void +exception_safety_tester::allocated( const_string file, std::size_t line_num, void* p, std::size_t s ) +{ + if( m_internal_activity ) + return; + + activity_guard ag( m_internal_activity ); + + if( m_exec_path_point < m_execution_path.size() ) + BOOST_REQUIRE_MESSAGE( m_execution_path[m_exec_path_point].m_type == EPP_ALLOC, + "Function under test exibit non-deterministic behavior" ); + else + m_execution_path.push_back( + execution_path_point( EPP_ALLOC, file, line_num ) ); + + m_execution_path[m_exec_path_point].m_alloc.ptr = p; + m_execution_path[m_exec_path_point].m_alloc.size = s; + + m_memory_in_use.insert( std::make_pair( p, m_exec_path_point++ ) ); +} + +//____________________________________________________________________________// + +void +exception_safety_tester::freed( void* p ) +{ + if( m_internal_activity ) + return; + + activity_guard ag( m_internal_activity ); + + registry::iterator it = m_memory_in_use.find( p ); + if( it != m_memory_in_use.end() ) { + m_execution_path[it->second].m_alloc.ptr = 0; + m_memory_in_use.erase( it ); + } +} + +//____________________________________________________________________________// + +void +exception_safety_tester::assertion_result( bool passed ) +{ + if( !m_internal_activity && !passed ) { + m_invairant_failed = true; + + failure_point(); + } +} + +//____________________________________________________________________________// + +void +exception_safety_tester::failure_point() +{ + if( m_exec_path_counter == m_break_exec_path ) + debug::debugger_break(); + + throw unique_exception(); +} + +//____________________________________________________________________________// + +namespace { + +inline void +format_location( wrap_stringstream& formatter, execution_path_point const& /*p*/, unsigned indent ) +{ + if( indent ) + formatter << std::left << std::setw( indent ) << ""; + +// !! ?? optional if( p.m_file_name ) +// formatter << p.m_file_name << '(' << p.m_line_num << "): "; +} + +//____________________________________________________________________________// + +template<typename ExecPathIt> +inline void +format_execution_path( wrap_stringstream& formatter, ExecPathIt it, ExecPathIt end, unsigned indent = 0 ) +{ + while( it != end ) { + switch( it->m_type ) { + case EPP_SCOPE: + format_location( formatter, *it, indent ); + formatter << "> \"" << it->m_scope.name << "\"\n"; + format_execution_path( formatter, it+1, it + it->m_scope.size, indent + 2 ); + format_location( formatter, *it, indent ); + formatter << "< \"" << it->m_scope.name << "\"\n"; + it += it->m_scope.size; + break; + + case EPP_DECISION: + format_location( formatter, *it, indent ); + formatter << "Decision made as " << std::boolalpha << it->m_decision.value << '\n'; + ++it; + break; + + case EPP_EXCEPT: + format_location( formatter, *it, indent ); + formatter << "Forced failure"; + if( it->m_except.description ) + formatter << ": " << it->m_except.description; + formatter << "\n"; + ++it; + break; + + case EPP_ALLOC: + if( it->m_alloc.ptr ) { + format_location( formatter, *it, indent ); + formatter << "Allocated memory block 0x" << std::uppercase << it->m_alloc.ptr + << ", " << it->m_alloc.size << " bytes long: <"; + + unsigned i; + for( i = 0; i < std::min<std::size_t>( it->m_alloc.size, 8 ); i++ ) { + unsigned char c = static_cast<unsigned char*>(it->m_alloc.ptr)[i]; + if( (std::isprint)( c ) ) + formatter << c; + else + formatter << '.'; + } + + formatter << "> "; + + for( i = 0; i < std::min<std::size_t>( it->m_alloc.size, 8 ); i++ ) { + unsigned c = static_cast<unsigned char*>(it->m_alloc.ptr)[i]; + formatter << std::hex << std::uppercase << c << ' '; + } + + formatter << "\n"; + } + ++it; + break; + } + } +} + +//____________________________________________________________________________// + +} // local namespace + +void +exception_safety_tester::report_error() +{ + activity_guard ag( m_internal_activity ); + + unit_test_log << unit_test::log::begin( m_execution_path.back().m_file_name, + m_execution_path.back().m_line_num ) + << log_all_errors; + + wrap_stringstream formatter; + + if( m_invairant_failed ) + formatter << "Failed invariant"; + + if( m_memory_in_use.size() != 0 ) { + if( m_invairant_failed ) + formatter << " and "; + + formatter << static_cast<unsigned int>(m_memory_in_use.size()) << " memory leak"; + if( m_memory_in_use.size() > 1 ) + formatter << 's'; + } + formatter << " detected in the execution path " << m_exec_path_counter << ":\n"; + + format_execution_path( formatter, m_execution_path.begin(), m_execution_path.end() ); + + unit_test_log << const_string( formatter.str() ) << unit_test::log::end(); +} + +//____________________________________________________________________________// + +// ************************************************************************** // +// ************** exception safety test ************** // +// ************************************************************************** // + +void BOOST_TEST_DECL +exception_safety( callback0<> const& F, const_string test_name ) +{ + exception_safety_tester est( test_name ); + + do { + try { + F(); + } + catch( exception_safety_tester::unique_exception const& ) {} + + } while( est.next_execution_path() ); +} + +//____________________________________________________________________________// + +} // namespace itest + +} // namespace boost + +//____________________________________________________________________________// + +#include <boost/test/detail/enable_warnings.hpp> + +#endif // non-ancient compiler + +#endif // BOOST_TEST_EXECUTION_SAFETY_IPP_112005GER diff --git a/boost/test/impl/execution_monitor.ipp b/boost/test/impl/execution_monitor.ipp new file mode 100644 index 0000000000..d9d5b6a34a --- /dev/null +++ b/boost/test/impl/execution_monitor.ipp @@ -0,0 +1,1367 @@ +// (C) Copyright Gennadiy Rozental 2001-2008. +// (C) Copyright Beman Dawes and Ullrich Koethe 1995-2001. +// Use, modification, and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/libs/test for the library home page. +// +// File : $RCSfile$ +// +// Version : $Revision: 57992 $ +// +// Description : provides execution monitor implementation for all supported +// configurations, including Microsoft structured exception based, unix signals +// based and special workarounds for borland +// +// Note that when testing requirements or user wishes preclude use of this +// file as a separate compilation unit, it may be included as a header file. +// +// Header dependencies are deliberately restricted to reduce coupling to other +// boost libraries. +// *************************************************************************** + +#ifndef BOOST_TEST_EXECUTION_MONITOR_IPP_012205GER +#define BOOST_TEST_EXECUTION_MONITOR_IPP_012205GER + +// Boost.Test +#include <boost/test/detail/config.hpp> +#include <boost/test/detail/workaround.hpp> +#include <boost/test/execution_monitor.hpp> +#include <boost/test/debug.hpp> + +// Boost +#include <boost/cstdlib.hpp> // for exit codes +#include <boost/config.hpp> // for workarounds +#include <boost/exception/get_error_info.hpp> // for get_error_info +#include <boost/exception/current_exception_cast.hpp> // for current_exception_cast + +// STL +#include <string> // for std::string +#include <new> // for std::bad_alloc +#include <typeinfo> // for std::bad_cast, std::bad_typeid +#include <exception> // for std::exception, std::bad_exception +#include <stdexcept> // for std exception hierarchy +#include <cstring> // for C string API +#include <cassert> // for assert +#include <cstddef> // for NULL +#include <cstdio> // for vsnprintf +#include <cstdarg> // for varargs + +#ifdef BOOST_NO_STDC_NAMESPACE +namespace std { using ::strerror; using ::strlen; using ::strncat; } +#endif + +// to use vsnprintf +#if defined(__SUNPRO_CC) || defined(__SunOS) +# include <stdio.h> +# include <stdarg.h> +using std::va_list; +#endif + +// to use vsnprintf +#if defined(__QNXNTO__) +# include <stdio.h> +#endif + +#if defined(_WIN32) && !defined(BOOST_DISABLE_WIN32) && \ + (!defined(__COMO__) && !defined(__MWERKS__) && !defined(__GNUC__) || \ + BOOST_WORKAROUND(__MWERKS__, >= 0x3000)) + +# define BOOST_SEH_BASED_SIGNAL_HANDLING + +# include <windows.h> + +# if defined(__MWERKS__) || (defined(_MSC_VER) && !defined(UNDER_CE)) +# include <eh.h> +# endif + +# if defined(__BORLANDC__) && __BORLANDC__ >= 0x560 || defined(__MWERKS__) +# include <stdint.h> +# endif + +# if defined(__BORLANDC__) && __BORLANDC__ < 0x560 + typedef unsigned uintptr_t; +# endif + +# if BOOST_WORKAROUND(_MSC_VER, < 1300 ) || defined(UNDER_CE) +typedef void* uintptr_t; +# endif + +// for the FP control routines +#include <float.h> + +#ifndef EM_INVALID +#define EM_INVALID _EM_INVALID +#endif + +#ifndef EM_DENORMAL +#define EM_DENORMAL _EM_DENORMAL +#endif + +#ifndef EM_ZERODIVIDE +#define EM_ZERODIVIDE _EM_ZERODIVIDE +#endif + +#ifndef EM_OVERFLOW +#define EM_OVERFLOW _EM_OVERFLOW +#endif + +#ifndef EM_UNDERFLOW +#define EM_UNDERFLOW _EM_UNDERFLOW +#endif + +#ifndef MCW_EM +#define MCW_EM _MCW_EM +#endif + +# if !defined(NDEBUG) && defined(_MSC_VER) && !defined(UNDER_CE) +# include <crtdbg.h> +# define BOOST_TEST_CRT_HOOK_TYPE _CRT_REPORT_HOOK +# define BOOST_TEST_CRT_ASSERT _CRT_ASSERT +# define BOOST_TEST_CRT_ERROR _CRT_ERROR +# define BOOST_TEST_CRT_SET_HOOK(H) _CrtSetReportHook(H) +# else +# define BOOST_TEST_CRT_HOOK_TYPE void* +# define BOOST_TEST_CRT_ASSERT 2 +# define BOOST_TEST_CRT_ERROR 1 +# define BOOST_TEST_CRT_SET_HOOK(H) (void*)(H) +# endif + +# if !BOOST_WORKAROUND(_MSC_VER, >= 1400 ) || defined(UNDER_CE) + +typedef void* _invalid_parameter_handler; + +inline _invalid_parameter_handler +_set_invalid_parameter_handler( _invalid_parameter_handler arg ) +{ + return arg; +} + +# endif + +# if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x0564)) || defined(UNDER_CE) + +namespace { void _set_se_translator( void* ) {} } + +# endif + +#elif defined(BOOST_HAS_SIGACTION) + +# define BOOST_SIGACTION_BASED_SIGNAL_HANDLING + +# include <unistd.h> +# include <signal.h> +# include <setjmp.h> + +# if defined(__FreeBSD__) + +# ifndef SIGPOLL +# define SIGPOLL SIGIO +# endif + +# if (__FreeBSD_version < 70100) + +# define ILL_ILLADR 0 // ILL_RESAD_FAULT +# define ILL_PRVOPC ILL_PRIVIN_FAULT +# define ILL_ILLOPN 2 // ILL_RESOP_FAULT +# define ILL_COPROC ILL_FPOP_FAULT + +# define BOOST_TEST_LIMITED_SIGNAL_DETAILS +# define BOOST_TEST_IGNORE_SIGCHLD + +# endif +# endif + +# if !defined(__CYGWIN__) && !defined(__QNXNTO__) +# define BOOST_TEST_USE_ALT_STACK +# endif + +# if defined(SIGPOLL) && !defined(__CYGWIN__) && \ + !(defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__)) && \ + !defined(__NetBSD__) && \ + !defined(__QNXNTO__) +# define BOOST_TEST_CATCH_SIGPOLL +# endif + +# ifdef BOOST_TEST_USE_ALT_STACK +# define BOOST_TEST_ALT_STACK_SIZE SIGSTKSZ +# endif + +#else + +# define BOOST_NO_SIGNAL_HANDLING + +#endif + +#ifndef UNDER_CE +#include <errno.h> +#endif + +#include <boost/test/detail/suppress_warnings.hpp> + +//____________________________________________________________________________// + +namespace boost { + +// ************************************************************************** // +// ************** report_error ************** // +// ************************************************************************** // + +namespace detail { + +#ifdef __BORLANDC__ +# define BOOST_TEST_VSNPRINTF( a1, a2, a3, a4 ) std::vsnprintf( (a1), (a2), (a3), (a4) ) +#elif BOOST_WORKAROUND(_MSC_VER, <= 1310) || \ + BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3000)) || \ + defined(UNDER_CE) +# define BOOST_TEST_VSNPRINTF( a1, a2, a3, a4 ) _vsnprintf( (a1), (a2), (a3), (a4) ) +#else +# define BOOST_TEST_VSNPRINTF( a1, a2, a3, a4 ) vsnprintf( (a1), (a2), (a3), (a4) ) +#endif + +template <typename ErrorInfo> +typename ErrorInfo::value_type +extract( boost::exception const* ex ) +{ + if( !ex ) + return 0; + + typename ErrorInfo::value_type const * val = boost::get_error_info<ErrorInfo>( *ex ); + + return val ? *val : 0; +} + +//____________________________________________________________________________// + +static void +report_error( execution_exception::error_code ec, boost::exception const* be, char const* format, va_list* args ) +{ + static const int REPORT_ERROR_BUFFER_SIZE = 512; + static char buf[REPORT_ERROR_BUFFER_SIZE]; + + BOOST_TEST_VSNPRINTF( buf, sizeof(buf)-1, format, *args ); + buf[sizeof(buf)-1] = 0; + + va_end( *args ); + + throw execution_exception( ec, buf, execution_exception::location( extract<throw_file>( be ), + extract<throw_line>( be ), + extract<throw_function>( be ) ) ); +} + +//____________________________________________________________________________// + +static void +report_error( execution_exception::error_code ec, char const* format, ... ) +{ + va_list args; + va_start( args, format ); + + report_error( ec, 0, format, &args ); +} + +//____________________________________________________________________________// + +static void +report_error( execution_exception::error_code ec, boost::exception const* be, char const* format, ... ) +{ + va_list args; + va_start( args, format ); + + report_error( ec, be, format, &args ); +} + +//____________________________________________________________________________// + +template<typename Tr,typename Functor> +inline int +do_invoke( Tr const& tr, Functor const& F ) +{ + return tr ? (*tr)( F ) : F(); +} + +//____________________________________________________________________________// + +} // namespace detail + +#if defined(BOOST_SIGACTION_BASED_SIGNAL_HANDLING) + +// ************************************************************************** // +// ************** Sigaction based signal handling ************** // +// ************************************************************************** // + +namespace detail { + +// ************************************************************************** // +// ************** boost::detail::system_signal_exception ************** // +// ************************************************************************** // + +class system_signal_exception { +public: + // Constructor + system_signal_exception() + : m_sig_info( 0 ) + , m_context( 0 ) + {} + + // Access methods + void operator()( siginfo_t* i, void* c ) + { + m_sig_info = i; + m_context = c; + } + void report() const; + +private: + // Data members + siginfo_t* m_sig_info; // system signal detailed info + void* m_context; // signal context +}; + +//____________________________________________________________________________// + +void +system_signal_exception::report() const +{ + if( !m_sig_info ) + return; // no error actually occur? + + switch( m_sig_info->si_code ) { + case SI_USER: + report_error( execution_exception::system_error, + "signal: generated by kill() (or family); uid=%d; pid=%d", + (int)m_sig_info->si_uid, (int)m_sig_info->si_pid ); + break; + case SI_QUEUE: + report_error( execution_exception::system_error, + "signal: sent by sigqueue()" ); + break; + case SI_TIMER: + report_error( execution_exception::system_error, + "signal: the expiration of a timer set by timer_settimer()" ); + break; + case SI_ASYNCIO: + report_error( execution_exception::system_error, + "signal: generated by the completion of an asynchronous I/O request" ); + break; + case SI_MESGQ: + report_error( execution_exception::system_error, + "signal: generated by the the arrival of a message on an empty message queue" ); + break; + default: + break; + } + + switch( m_sig_info->si_signo ) { + case SIGILL: + switch( m_sig_info->si_code ) { +#ifndef BOOST_TEST_LIMITED_SIGNAL_DETAILS + case ILL_ILLOPC: + report_error( execution_exception::system_fatal_error, + "signal: illegal opcode; address of failing instruction: 0x%08lx", + m_sig_info->si_addr ); + break; + case ILL_ILLTRP: + report_error( execution_exception::system_fatal_error, + "signal: illegal trap; address of failing instruction: 0x%08lx", + m_sig_info->si_addr ); + break; + case ILL_PRVREG: + report_error( execution_exception::system_fatal_error, + "signal: privileged register; address of failing instruction: 0x%08lx", + m_sig_info->si_addr ); + break; + case ILL_BADSTK: + report_error( execution_exception::system_fatal_error, + "signal: internal stack error; address of failing instruction: 0x%08lx", + m_sig_info->si_addr ); + break; +#endif + case ILL_ILLOPN: + report_error( execution_exception::system_fatal_error, + "signal: illegal operand; address of failing instruction: 0x%08lx", + m_sig_info->si_addr ); + break; + case ILL_ILLADR: + report_error( execution_exception::system_fatal_error, + "signal: illegal addressing mode; address of failing instruction: 0x%08lx", + m_sig_info->si_addr ); + break; + case ILL_PRVOPC: + report_error( execution_exception::system_fatal_error, + "signal: privileged opcode; address of failing instruction: 0x%08lx", + m_sig_info->si_addr ); + break; + case ILL_COPROC: + report_error( execution_exception::system_fatal_error, + "signal: co-processor error; address of failing instruction: 0x%08lx", + m_sig_info->si_addr ); + break; + default: + report_error( execution_exception::system_fatal_error, + "signal: SIGILL, si_code: %d (illegal instruction; address of failing instruction: 0x%08lx)", + m_sig_info->si_addr, m_sig_info->si_code ); + break; + } + break; + + case SIGFPE: + switch( m_sig_info->si_code ) { + case FPE_INTDIV: + report_error( execution_exception::system_error, + "signal: integer divide by zero; address of failing instruction: 0x%08lx", + m_sig_info->si_addr ); + break; + case FPE_INTOVF: + report_error( execution_exception::system_error, + "signal: integer overflow; address of failing instruction: 0x%08lx", + m_sig_info->si_addr ); + break; + case FPE_FLTDIV: + report_error( execution_exception::system_error, + "signal: floating point divide by zero; address of failing instruction: 0x%08lx", + m_sig_info->si_addr ); + break; + case FPE_FLTOVF: + report_error( execution_exception::system_error, + "signal: floating point overflow; address of failing instruction: 0x%08lx", + m_sig_info->si_addr ); + break; + case FPE_FLTUND: + report_error( execution_exception::system_error, + "signal: floating point underflow; address of failing instruction: 0x%08lx", + m_sig_info->si_addr ); + break; + case FPE_FLTRES: + report_error( execution_exception::system_error, + "signal: floating point inexact result; address of failing instruction: 0x%08lx", + m_sig_info->si_addr ); + break; + case FPE_FLTINV: + report_error( execution_exception::system_error, + "signal: invalid floating point operation; address of failing instruction: 0x%08lx", + m_sig_info->si_addr ); + break; + case FPE_FLTSUB: + report_error( execution_exception::system_error, + "signal: subscript out of range; address of failing instruction: 0x%08lx", + m_sig_info->si_addr ); + break; + default: + report_error( execution_exception::system_error, + "signal: SIGFPE, si_code: %d (errnoneous arithmetic operations; address of failing instruction: 0x%08lx)", + m_sig_info->si_addr, m_sig_info->si_code ); + break; + } + break; + + case SIGSEGV: + switch( m_sig_info->si_code ) { +#ifndef BOOST_TEST_LIMITED_SIGNAL_DETAILS + case SEGV_MAPERR: + report_error( execution_exception::system_fatal_error, + "memory access violation at address: 0x%08lx: no mapping at fault address", + m_sig_info->si_addr ); + break; + case SEGV_ACCERR: + report_error( execution_exception::system_fatal_error, + "memory access violation at address: 0x%08lx: invalid permissions", + m_sig_info->si_addr ); + break; +#endif + default: + report_error( execution_exception::system_fatal_error, + "signal: SIGSEGV, si_code: %d (memory access violation at address: 0x%08lx)", + m_sig_info->si_addr, m_sig_info->si_code ); + break; + } + break; + + case SIGBUS: + switch( m_sig_info->si_code ) { +#ifndef BOOST_TEST_LIMITED_SIGNAL_DETAILS + case BUS_ADRALN: + report_error( execution_exception::system_fatal_error, + "memory access violation at address: 0x%08lx: invalid address alignment", + m_sig_info->si_addr ); + break; + case BUS_ADRERR: + report_error( execution_exception::system_fatal_error, + "memory access violation at address: 0x%08lx: non-existent physical address", + m_sig_info->si_addr ); + break; + case BUS_OBJERR: + report_error( execution_exception::system_fatal_error, + "memory access violation at address: 0x%08lx: object specific hardware error", + m_sig_info->si_addr ); + break; +#endif + default: + report_error( execution_exception::system_fatal_error, + "signal: SIGSEGV, si_code: %d (memory access violation at address: 0x%08lx)", + m_sig_info->si_addr, m_sig_info->si_code ); + break; + } + break; + + case SIGCHLD: + switch( m_sig_info->si_code ) { +#ifndef BOOST_TEST_LIMITED_SIGNAL_DETAILS + case CLD_EXITED: + report_error( execution_exception::system_error, + "child has exited; pid: %d; uid: %d; exit value: %d", + (int)m_sig_info->si_pid, (int)m_sig_info->si_uid, (int)m_sig_info->si_status ); + break; + case CLD_KILLED: + report_error( execution_exception::system_error, + "child was killed; pid: %d; uid: %d; exit value: %d", + (int)m_sig_info->si_pid, (int)m_sig_info->si_uid, (int)m_sig_info->si_status ); + break; + case CLD_DUMPED: + report_error( execution_exception::system_error, + "child terminated abnormally; pid: %d; uid: %d; exit value: %d", + (int)m_sig_info->si_pid, (int)m_sig_info->si_uid, (int)m_sig_info->si_status ); + break; + case CLD_TRAPPED: + report_error( execution_exception::system_error, + "traced child has trapped; pid: %d; uid: %d; exit value: %d", + (int)m_sig_info->si_pid, (int)m_sig_info->si_uid, (int)m_sig_info->si_status ); + break; + case CLD_STOPPED: + report_error( execution_exception::system_error, + "child has stopped; pid: %d; uid: %d; exit value: %d", + (int)m_sig_info->si_pid, (int)m_sig_info->si_uid, (int)m_sig_info->si_status ); + break; + case CLD_CONTINUED: + report_error( execution_exception::system_error, + "stopped child had continued; pid: %d; uid: %d; exit value: %d", + (int)m_sig_info->si_pid, (int)m_sig_info->si_uid, (int)m_sig_info->si_status ); + break; +#endif + default: + report_error( execution_exception::system_error, + "signal: SIGCHLD, si_code: %d (child process has terminated; pid: %d; uid: %d; exit value: %d)", + (int)m_sig_info->si_pid, (int)m_sig_info->si_uid, (int)m_sig_info->si_status, m_sig_info->si_code ); + break; + } + break; + +#if defined(BOOST_TEST_CATCH_SIGPOLL) + + case SIGPOLL: + switch( m_sig_info->si_code ) { +#ifndef BOOST_TEST_LIMITED_SIGNAL_DETAILS + case POLL_IN: + report_error( execution_exception::system_error, + "data input available; band event %d", + (int)m_sig_info->si_band ); + break; + case POLL_OUT: + report_error( execution_exception::system_error, + "output buffers available; band event %d", + (int)m_sig_info->si_band ); + break; + case POLL_MSG: + report_error( execution_exception::system_error, + "input message available; band event %d", + (int)m_sig_info->si_band ); + break; + case POLL_ERR: + report_error( execution_exception::system_error, + "i/o error; band event %d", + (int)m_sig_info->si_band ); + break; + case POLL_PRI: + report_error( execution_exception::system_error, + "high priority input available; band event %d", + (int)m_sig_info->si_band ); + break; +#if defined(POLL_ERR) && defined(POLL_HUP) && (POLL_ERR - POLL_HUP) + case POLL_HUP: + report_error( execution_exception::system_error, + "device disconnected; band event %d", + (int)m_sig_info->si_band ); + break; +#endif +#endif + default: + report_error( execution_exception::system_error, + "signal: SIGPOLL, si_code: %d (asynchronous I/O event occured; band event %d)", + (int)m_sig_info->si_band, m_sig_info->si_code ); + break; + } + break; + +#endif + + case SIGABRT: + report_error( execution_exception::system_error, + "signal: SIGABRT (application abort requested)" ); + break; + + case SIGALRM: + report_error( execution_exception::timeout_error, + "signal: SIGALRM (timeout while executing function)" ); + break; + + default: + report_error( execution_exception::system_error, "unrecognized signal" ); + } +} + +//____________________________________________________________________________// + +// ************************************************************************** // +// ************** boost::detail::signal_action ************** // +// ************************************************************************** // + +// Forward declaration +extern "C" { +static void execution_monitor_jumping_signal_handler( int sig, siginfo_t* info, void* context ); +static void execution_monitor_attaching_signal_handler( int sig, siginfo_t* info, void* context ); +} + +class signal_action { + typedef struct sigaction* sigaction_ptr; +public: + //Constructor + signal_action(); + signal_action( int sig, bool install, bool attach_dbg, char* alt_stack ); + ~signal_action(); + +private: + // Data members + int m_sig; + bool m_installed; + struct sigaction m_new_action; + struct sigaction m_old_action; +}; + +//____________________________________________________________________________// + +signal_action::signal_action() +: m_installed( false ) +{} + +//____________________________________________________________________________// + +signal_action::signal_action( int sig, bool install, bool attach_dbg, char* alt_stack ) +: m_sig( sig ) +, m_installed( install ) +{ + if( !install ) + return; + + std::memset( &m_new_action, 0, sizeof(struct sigaction) ); + + BOOST_TEST_SYS_ASSERT( ::sigaction( m_sig , sigaction_ptr(), &m_new_action ) != -1 ); + + if( m_new_action.sa_sigaction || m_new_action.sa_handler ) { + m_installed = false; + return; + } + + m_new_action.sa_flags |= SA_SIGINFO; + m_new_action.sa_sigaction = attach_dbg ? &execution_monitor_attaching_signal_handler + : &execution_monitor_jumping_signal_handler; + BOOST_TEST_SYS_ASSERT( sigemptyset( &m_new_action.sa_mask ) != -1 ); + +#ifdef BOOST_TEST_USE_ALT_STACK + if( alt_stack ) + m_new_action.sa_flags |= SA_ONSTACK; +#endif + + BOOST_TEST_SYS_ASSERT( ::sigaction( m_sig, &m_new_action, &m_old_action ) != -1 ); +} + +//____________________________________________________________________________// + +signal_action::~signal_action() +{ + if( m_installed ) + ::sigaction( m_sig, &m_old_action , sigaction_ptr() ); +} + +//____________________________________________________________________________// + +// ************************************************************************** // +// ************** boost::detail::signal_handler ************** // +// ************************************************************************** // + +class signal_handler { +public: + // Constructor + explicit signal_handler( bool catch_system_errors, int timeout, bool attach_dbg, char* alt_stack ); + + // Destructor + ~signal_handler(); + + // access methods + static sigjmp_buf& jump_buffer() + { + assert( !!s_active_handler ); + + return s_active_handler->m_sigjmp_buf; + } + + static system_signal_exception& sys_sig() + { + assert( !!s_active_handler ); + + return s_active_handler->m_sys_sig; + } + +private: + // Data members + signal_handler* m_prev_handler; + int m_timeout; + + signal_action m_ILL_action; + signal_action m_FPE_action; + signal_action m_SEGV_action; + signal_action m_BUS_action; + signal_action m_CHLD_action; + signal_action m_POLL_action; + signal_action m_ABRT_action; + signal_action m_ALRM_action; + + sigjmp_buf m_sigjmp_buf; + system_signal_exception m_sys_sig; + + static signal_handler* s_active_handler; +}; + +// !! need to be placed in thread specific storage +typedef signal_handler* signal_handler_ptr; +signal_handler* signal_handler::s_active_handler = signal_handler_ptr(); + +//____________________________________________________________________________// + +signal_handler::signal_handler( bool catch_system_errors, int timeout, bool attach_dbg, char* alt_stack ) +: m_prev_handler( s_active_handler ) +, m_timeout( timeout ) +, m_ILL_action ( SIGILL , catch_system_errors, attach_dbg, alt_stack ) +, m_FPE_action ( SIGFPE , catch_system_errors, attach_dbg, alt_stack ) +, m_SEGV_action( SIGSEGV, catch_system_errors, attach_dbg, alt_stack ) +, m_BUS_action ( SIGBUS , catch_system_errors, attach_dbg, alt_stack ) +#ifndef BOOST_TEST_IGNORE_SIGCHLD +, m_CHLD_action( SIGCHLD, catch_system_errors, attach_dbg, alt_stack ) +#endif +#ifdef BOOST_TEST_CATCH_SIGPOLL +, m_POLL_action( SIGPOLL, catch_system_errors, attach_dbg, alt_stack ) +#endif +, m_ABRT_action( SIGABRT, catch_system_errors, attach_dbg, alt_stack ) +, m_ALRM_action( SIGALRM, timeout > 0 , attach_dbg, alt_stack ) +{ + s_active_handler = this; + + if( m_timeout > 0 ) { + ::alarm( 0 ); + ::alarm( timeout ); + } + +#ifdef BOOST_TEST_USE_ALT_STACK + if( alt_stack ) { + stack_t sigstk; + std::memset( &sigstk, 0, sizeof(stack_t) ); + + BOOST_TEST_SYS_ASSERT( ::sigaltstack( 0, &sigstk ) != -1 ); + + if( sigstk.ss_flags & SS_DISABLE ) { + sigstk.ss_sp = alt_stack; + sigstk.ss_size = BOOST_TEST_ALT_STACK_SIZE; + sigstk.ss_flags = 0; + BOOST_TEST_SYS_ASSERT( ::sigaltstack( &sigstk, 0 ) != -1 ); + } + } +#endif +} + +//____________________________________________________________________________// + +signal_handler::~signal_handler() +{ + assert( s_active_handler == this ); + + if( m_timeout > 0 ) + ::alarm( 0 ); + +#ifdef BOOST_TEST_USE_ALT_STACK +#ifdef __GNUC__ + // We shouldn't need to explicitly initialize all the members here, + // but gcc warns if we don't, so add initializers for each of the + // members specified in the POSIX std: + stack_t sigstk = { 0, 0, 0 }; +#else + stack_t sigstk = { }; +#endif + + sigstk.ss_size = MINSIGSTKSZ; + sigstk.ss_flags = SS_DISABLE; + BOOST_TEST_SYS_ASSERT( ::sigaltstack( &sigstk, 0 ) != -1 ); +#endif + + s_active_handler = m_prev_handler; +} + +//____________________________________________________________________________// + +// ************************************************************************** // +// ************** execution_monitor_signal_handler ************** // +// ************************************************************************** // + +extern "C" { + +static bool ignore_sigchild( siginfo_t* info ) +{ + return info->si_signo == SIGCHLD +#ifndef BOOST_TEST_LIMITED_SIGNAL_DETAILS + && info->si_code == CLD_EXITED +#endif +#ifdef BOOST_TEST_IGNORE_NON_ZERO_CHILD_CODE + ; +#else + && (int)info->si_status == 0; +#endif +} + +//____________________________________________________________________________// + +static void execution_monitor_jumping_signal_handler( int sig, siginfo_t* info, void* context ) +{ + if( ignore_sigchild( info ) ) + return; + + signal_handler::sys_sig()( info, context ); + + siglongjmp( signal_handler::jump_buffer(), sig ); +} + +//____________________________________________________________________________// + +static void execution_monitor_attaching_signal_handler( int sig, siginfo_t* info, void* context ) +{ + if( ignore_sigchild( info ) ) + return; + + if( !debug::attach_debugger( false ) ) + execution_monitor_jumping_signal_handler( sig, info, context ); + + // debugger attached; it will handle the signal + BOOST_TEST_SYS_ASSERT( ::signal( sig, SIG_DFL ) != SIG_ERR ); +} + +//____________________________________________________________________________// + +} + +} // namespace detail + +// ************************************************************************** // +// ************** execution_monitor::catch_signals ************** // +// ************************************************************************** // + +int +execution_monitor::catch_signals( unit_test::callback0<int> const& F ) +{ + using namespace detail; + +#if defined(__CYGWIN__) + p_catch_system_errors.value = false; +#endif + +#ifdef BOOST_TEST_USE_ALT_STACK + if( !!p_use_alt_stack && !m_alt_stack ) + m_alt_stack.reset( new char[BOOST_TEST_ALT_STACK_SIZE] ); +#else + p_use_alt_stack.value = false; +#endif + + signal_handler local_signal_handler( p_catch_system_errors, p_timeout, p_auto_start_dbg, + !p_use_alt_stack ? 0 : m_alt_stack.get() ); + + if( !sigsetjmp( signal_handler::jump_buffer(), 1 ) ) + return detail::do_invoke( m_custom_translators , F ); + else + throw local_signal_handler.sys_sig(); +} + +//____________________________________________________________________________// + +#elif defined(BOOST_SEH_BASED_SIGNAL_HANDLING) + +// ************************************************************************** // +// ************** Microsoft structured exception handling ************** // +// ************************************************************************** // + +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x0564)) +namespace { void _set_se_translator( void* ) {} } +#endif + +namespace detail { + +// ************************************************************************** // +// ************** boost::detail::system_signal_exception ************** // +// ************************************************************************** // + +class system_signal_exception { +public: + // Constructor + explicit system_signal_exception( execution_monitor* em ) + : m_em( em ) + , m_se_id( 0 ) + , m_fault_address( 0 ) + , m_dir( false ) + {} + + void report() const; + int operator()( unsigned int id, _EXCEPTION_POINTERS* exps ); + +private: + // Data members + execution_monitor* m_em; + + unsigned int m_se_id; + void* m_fault_address; + bool m_dir; +}; + +static void +seh_catch_preventer( unsigned int /* id */, _EXCEPTION_POINTERS* /* exps */ ) +{ + throw; +} + +//____________________________________________________________________________// + +int +system_signal_exception::operator()( unsigned int id, _EXCEPTION_POINTERS* exps ) +{ + const unsigned int MSFT_CPP_EXCEPT = 0xE06d7363; // EMSC + + if( !m_em->p_catch_system_errors || (id == MSFT_CPP_EXCEPT) ) + return EXCEPTION_CONTINUE_SEARCH; + + if( !!m_em->p_auto_start_dbg && debug::attach_debugger( false ) ) { + m_em->p_catch_system_errors.value = false; + _set_se_translator( &seh_catch_preventer ); + + return EXCEPTION_CONTINUE_EXECUTION; + } + + m_se_id = id; + if( m_se_id == EXCEPTION_ACCESS_VIOLATION && exps->ExceptionRecord->NumberParameters == 2 ) { + m_fault_address = (void*)exps->ExceptionRecord->ExceptionInformation[1]; + m_dir = exps->ExceptionRecord->ExceptionInformation[0] == 0; + } + + return EXCEPTION_EXECUTE_HANDLER; +} + +//____________________________________________________________________________// + +void +system_signal_exception::report() const +{ + switch( m_se_id ) { + // cases classified as system_fatal_error + case EXCEPTION_ACCESS_VIOLATION: { + if( !m_fault_address ) + detail::report_error( execution_exception::system_fatal_error, "memory access violation" ); + else + detail::report_error( + execution_exception::system_fatal_error, + "memory access violation occurred at address 0x%08lx, while attempting to %s", + m_fault_address, + m_dir ? " read inaccessible data" + : " write to an inaccessible (or protected) address" + ); + break; + } + + case EXCEPTION_ILLEGAL_INSTRUCTION: + detail::report_error( execution_exception::system_fatal_error, "illegal instruction" ); + break; + + case EXCEPTION_PRIV_INSTRUCTION: + detail::report_error( execution_exception::system_fatal_error, "tried to execute an instruction whose operation is not allowed in the current machine mode" ); + break; + + case EXCEPTION_IN_PAGE_ERROR: + detail::report_error( execution_exception::system_fatal_error, "access to a memory page that is not present" ); + break; + + case EXCEPTION_STACK_OVERFLOW: + detail::report_error( execution_exception::system_fatal_error, "stack overflow" ); + break; + + case EXCEPTION_NONCONTINUABLE_EXCEPTION: + detail::report_error( execution_exception::system_fatal_error, "tried to continue execution after a non continuable exception occurred" ); + break; + + // cases classified as (non-fatal) system_trap + case EXCEPTION_DATATYPE_MISALIGNMENT: + detail::report_error( execution_exception::system_error, "data misalignment" ); + break; + + case EXCEPTION_INT_DIVIDE_BY_ZERO: + detail::report_error( execution_exception::system_error, "integer divide by zero" ); + break; + + case EXCEPTION_INT_OVERFLOW: + detail::report_error( execution_exception::system_error, "integer overflow" ); + break; + + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: + detail::report_error( execution_exception::system_error, "array bounds exceeded" ); + break; + + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + detail::report_error( execution_exception::system_error, "floating point divide by zero" ); + break; + + case EXCEPTION_FLT_STACK_CHECK: + detail::report_error( execution_exception::system_error, + "stack overflowed or underflowed as the result of a floating-point operation" ); + break; + + case EXCEPTION_FLT_DENORMAL_OPERAND: + detail::report_error( execution_exception::system_error, + "operand of floating point operation is denormal" ); + break; + +# if 0 // !! ?? + case EXCEPTION_FLT_INEXACT_RESULT: + detail::report_error( execution_exception::system_error, + "result of a floating-point operation cannot be represented exactly" ); + break; +#endif + + case EXCEPTION_FLT_OVERFLOW: + detail::report_error( execution_exception::system_error, + "exponent of a floating-point operation is greater than the magnitude allowed by the corresponding type" ); + break; + + case EXCEPTION_FLT_UNDERFLOW: + detail::report_error( execution_exception::system_error, + "exponent of a floating-point operation is less than the magnitude allowed by the corresponding type" ); + break; + + case EXCEPTION_FLT_INVALID_OPERATION: + detail::report_error( execution_exception::system_error, "floating point error" ); + break; + + case EXCEPTION_BREAKPOINT: + detail::report_error( execution_exception::system_error, "breakpoint encountered" ); + break; + + default: + detail::report_error( execution_exception::system_error, "unrecognized exception. Id: 0x%08lx", m_se_id ); + break; + } +} + +//____________________________________________________________________________// + +// ************************************************************************** // +// ************** assert_reporting_function ************** // +// ************************************************************************** // + +int BOOST_TEST_CALL_DECL +assert_reporting_function( int reportType, char* userMessage, int* ) +{ + switch( reportType ) { + case BOOST_TEST_CRT_ASSERT: + detail::report_error( execution_exception::user_error, userMessage ); + + return 1; // return value and retVal are not important since we never reach this line + case BOOST_TEST_CRT_ERROR: + detail::report_error( execution_exception::system_error, userMessage ); + + return 1; // return value and retVal are not important since we never reach this line + default: + return 0; // use usual reporting method + } +} // assert_reporting_function + +//____________________________________________________________________________// + +void BOOST_TEST_CALL_DECL +invalid_param_handler( wchar_t const* /* expr */, + wchar_t const* /* func */, + wchar_t const* /* file */, + unsigned int /* line */, + uintptr_t /* reserved */) +{ + detail::report_error( execution_exception::user_error, + "Invalid parameter detected by C runtime library" ); +} + +//____________________________________________________________________________// + +void BOOST_TEST_CALL_DECL +switch_fp_exceptions( bool on_off ) +{ + if( !on_off ) + _clearfp(); + + int cw = ::_controlfp( 0, 0 ); + + int exceptions_mask = EM_INVALID|EM_DENORMAL|EM_ZERODIVIDE|EM_OVERFLOW|EM_UNDERFLOW; + + if( on_off ) + cw &= ~exceptions_mask; // Set the exception masks on, turn exceptions off + else + cw |= exceptions_mask; // Set the exception masks off, turn exceptions on + + if( on_off ) + _clearfp(); + + // Set the control word + ::_controlfp( cw, MCW_EM ); +} + +//____________________________________________________________________________// + +} // namespace detail + +// ************************************************************************** // +// ************** execution_monitor::catch_signals ************** // +// ************************************************************************** // + +int +execution_monitor::catch_signals( unit_test::callback0<int> const& F ) +{ + _invalid_parameter_handler old_iph = _invalid_parameter_handler(); + BOOST_TEST_CRT_HOOK_TYPE old_crt_hook = 0; + + if( !p_catch_system_errors ) + _set_se_translator( &detail::seh_catch_preventer ); + else { + if( !!p_detect_fp_exceptions ) + detail::switch_fp_exceptions( true ); + + old_crt_hook = BOOST_TEST_CRT_SET_HOOK( &detail::assert_reporting_function ); + + old_iph = _set_invalid_parameter_handler( + reinterpret_cast<_invalid_parameter_handler>( &detail::invalid_param_handler ) ); + } + + detail::system_signal_exception SSE( this ); + + int ret_val = 0; + + __try { + __try { + ret_val = detail::do_invoke( m_custom_translators, F ); + } + __except( SSE( GetExceptionCode(), GetExceptionInformation() ) ) { + throw SSE; + } + } + __finally { + if( !!p_catch_system_errors ) { + if( !!p_detect_fp_exceptions ) + detail::switch_fp_exceptions( false ); + + BOOST_TEST_CRT_SET_HOOK( old_crt_hook ); + + _set_invalid_parameter_handler( old_iph ); + } + } + + return ret_val; +} + +//____________________________________________________________________________// + +#else // default signal handler + +namespace detail { + +class system_signal_exception { +public: + void report() const {} +}; + +} // namespace detail + +int +execution_monitor::catch_signals( unit_test::callback0<int> const& F ) +{ + return detail::do_invoke( m_custom_translators , F ); +} + +//____________________________________________________________________________// + +#endif // choose signal handler + +// ************************************************************************** // +// ************** execution_monitor::execute ************** // +// ************************************************************************** // + +int +execution_monitor::execute( unit_test::callback0<int> const& F ) +{ + if( debug::under_debugger() ) + p_catch_system_errors.value = false; + + try { + return catch_signals( F ); + } + + // Catch-clause reference arguments are a bit different from function + // arguments (ISO 15.3 paragraphs 18 & 19). Apparently const isn't + // required. Programmers ask for const anyhow, so we supply it. That's + // easier than answering questions about non-const usage. + + catch( char const* ex ) + { detail::report_error( execution_exception::cpp_exception_error, + "C string: %s", ex ); } + catch( std::string const& ex ) + { detail::report_error( execution_exception::cpp_exception_error, + "std::string: %s", ex.c_str() ); } + + // std:: exceptions + + catch( std::bad_alloc const& ex ) + { detail::report_error( execution_exception::cpp_exception_error, + current_exception_cast<boost::exception const>(), + "std::bad_alloc: %s", ex.what() ); } + +#if BOOST_WORKAROUND(__BORLANDC__, <= 0x0551) + catch( std::bad_cast const& ex ) + { detail::report_error( execution_exception::cpp_exception_error, + current_exception_cast<boost::exception const>(), + "std::bad_cast" ); } + catch( std::bad_typeid const& ex ) + { detail::report_error( execution_exception::cpp_exception_error, + current_exception_cast<boost::exception const>(), + "std::bad_typeid" ); } +#else + catch( std::bad_cast const& ex ) + { detail::report_error( execution_exception::cpp_exception_error, + current_exception_cast<boost::exception const>(), + "std::bad_cast: %s", ex.what() ); } + catch( std::bad_typeid const& ex ) + { detail::report_error( execution_exception::cpp_exception_error, + current_exception_cast<boost::exception const>(), + "std::bad_typeid: %s", ex.what() ); } +#endif + + catch( std::bad_exception const& ex ) + { detail::report_error( execution_exception::cpp_exception_error, + current_exception_cast<boost::exception const>(), + "std::bad_exception: %s", ex.what() ); } + catch( std::domain_error const& ex ) + { detail::report_error( execution_exception::cpp_exception_error, + current_exception_cast<boost::exception const>(), + "std::domain_error: %s", ex.what() ); } + catch( std::invalid_argument const& ex ) + { detail::report_error( execution_exception::cpp_exception_error, + current_exception_cast<boost::exception const>(), + "std::invalid_argument: %s", ex.what() ); } + catch( std::length_error const& ex ) + { detail::report_error( execution_exception::cpp_exception_error, + current_exception_cast<boost::exception const>(), + "std::length_error: %s", ex.what() ); } + catch( std::out_of_range const& ex ) + { detail::report_error( execution_exception::cpp_exception_error, + current_exception_cast<boost::exception const>(), + "std::out_of_range: %s", ex.what() ); } + catch( std::range_error const& ex ) + { detail::report_error( execution_exception::cpp_exception_error, + current_exception_cast<boost::exception const>(), + "std::range_error: %s", ex.what() ); } + catch( std::overflow_error const& ex ) + { detail::report_error( execution_exception::cpp_exception_error, + current_exception_cast<boost::exception const>(), + "std::overflow_error: %s", ex.what() ); } + catch( std::underflow_error const& ex ) + { detail::report_error( execution_exception::cpp_exception_error, + current_exception_cast<boost::exception const>(), + "std::underflow_error: %s", ex.what() ); } + catch( std::logic_error const& ex ) + { detail::report_error( execution_exception::cpp_exception_error, + current_exception_cast<boost::exception const>(), + "std::logic_error: %s", ex.what() ); } + catch( std::runtime_error const& ex ) + { detail::report_error( execution_exception::cpp_exception_error, + current_exception_cast<boost::exception const>(), + "std::runtime_error: %s", ex.what() ); } + catch( std::exception const& ex ) + { detail::report_error( execution_exception::cpp_exception_error, + current_exception_cast<boost::exception const>(), + "std::exception: %s", ex.what() ); } + + catch( boost::exception const& ex ) + { detail::report_error( execution_exception::cpp_exception_error, + &ex, + "unknown boost::exception" ); } + + // system errors + catch( system_error const& ex ) + { detail::report_error( execution_exception::cpp_exception_error, + "system_error produced by: %s: %s", ex.p_failed_exp.get(), std::strerror( ex.p_errno ) ); } + catch( detail::system_signal_exception const& ex ) + { ex.report(); } + + // not an error + catch( execution_aborted const& ) + { return 0; } + + // just forward + catch( execution_exception const& ) + { throw; } + + // unknown error + catch( ... ) + { detail::report_error( execution_exception::cpp_exception_error, "unknown type" ); } + + return 0; // never reached; supplied to quiet compiler warnings +} // execute + +//____________________________________________________________________________// + +// ************************************************************************** // +// ************** system_error ************** // +// ************************************************************************** // + +system_error::system_error( char const* exp ) +#ifdef UNDER_CE +: p_errno( GetLastError() ) +#else +: p_errno( errno ) +#endif +, p_failed_exp( exp ) +{} + +//____________________________________________________________________________// + +// ************************************************************************** // +// ************** execution_exception ************** // +// ************************************************************************** // + +execution_exception::execution_exception( error_code ec_, const_string what_msg_, location const& location_ ) +: m_error_code( ec_ ) +, m_what( what_msg_.empty() ? BOOST_TEST_L( "uncaught exception, system error or abort requested" ) : what_msg_ ) +, m_location( location_ ) +{} + +//____________________________________________________________________________// + +execution_exception::location::location( char const* file_name, size_t line_num, char const* func ) +: m_file_name( file_name ? file_name : "unknown location" ) +, m_line_num( line_num ) +, m_function( func ) +{} + +//____________________________________________________________________________// + +} // namespace boost + +#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 new file mode 100644 index 0000000000..1fbcffc039 --- /dev/null +++ b/boost/test/impl/framework.ipp @@ -0,0 +1,503 @@ +// (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: 57991 $ +// +// Description : implements framework API - main driver for the test +// *************************************************************************** + +#ifndef BOOST_TEST_FRAMEWORK_IPP_021005GER +#define BOOST_TEST_FRAMEWORK_IPP_021005GER + +// Boost.Test +#include <boost/test/framework.hpp> +#include <boost/test/execution_monitor.hpp> +#include <boost/test/debug.hpp> +#include <boost/test/unit_test_suite_impl.hpp> +#include <boost/test/unit_test_log.hpp> +#include <boost/test/unit_test_monitor.hpp> +#include <boost/test/test_observer.hpp> +#include <boost/test/results_collector.hpp> +#include <boost/test/progress_monitor.hpp> +#include <boost/test/results_reporter.hpp> +#include <boost/test/test_tools.hpp> + +#include <boost/test/detail/unit_test_parameters.hpp> +#include <boost/test/detail/global_typedef.hpp> + +#include <boost/test/utils/foreach.hpp> + +// Boost +#include <boost/timer.hpp> + +// STL +#include <map> +#include <set> +#include <cstdlib> +#include <ctime> + +#ifdef BOOST_NO_STDC_NAMESPACE +namespace std { using ::time; using ::srand; } +#endif + +#include <boost/test/detail/suppress_warnings.hpp> + +//____________________________________________________________________________// + +namespace boost { + +namespace unit_test { + +// ************************************************************************** // +// ************** test_start calls wrapper ************** // +// ************************************************************************** // + +namespace ut_detail { + +struct test_start_caller { + test_start_caller( test_observer* to, counter_t tc_amount ) + : m_to( to ) + , m_tc_amount( tc_amount ) + {} + + int operator()() + { + m_to->test_start( m_tc_amount ); + return 0; + } + +private: + // Data members + test_observer* m_to; + counter_t m_tc_amount; +}; + +//____________________________________________________________________________// + +struct test_init_caller { + explicit test_init_caller( init_unit_test_func init_func ) + : m_init_func( init_func ) + {} + int operator()() + { +#ifdef BOOST_TEST_ALTERNATIVE_INIT_API + if( !(*m_init_func)() ) + throw std::runtime_error( "test module initialization failed" ); +#else + test_suite* manual_test_units = (*m_init_func)( framework::master_test_suite().argc, framework::master_test_suite().argv ); + + if( manual_test_units ) + framework::master_test_suite().add( manual_test_units ); +#endif + return 0; + } + + // Data members + init_unit_test_func m_init_func; +}; + +} + +// ************************************************************************** // +// ************** framework ************** // +// ************************************************************************** // + +class framework_impl : public test_tree_visitor { +public: + framework_impl() + : m_master_test_suite( 0 ) + , m_curr_test_case( INV_TEST_UNIT_ID ) + , m_next_test_case_id( MIN_TEST_CASE_ID ) + , m_next_test_suite_id( MIN_TEST_SUITE_ID ) + , m_is_initialized( false ) + , m_test_in_progress( false ) + {} + + ~framework_impl() { clear(); } + + void clear() + { + while( !m_test_units.empty() ) { + test_unit_store::value_type const& tu = *m_test_units.begin(); + test_unit* tu_ptr = tu.second; + + // the delete will erase this element from map + if( ut_detail::test_id_2_unit_type( tu.second->p_id ) == tut_suite ) + delete (test_suite const*)tu_ptr; + else + delete (test_case const*)tu_ptr; + } + } + + void set_tu_id( test_unit& tu, test_unit_id id ) { tu.p_id.value = id; } + + // test_tree_visitor interface implementation + void visit( test_case const& tc ) + { + if( !tc.check_dependencies() ) { + BOOST_TEST_FOREACH( test_observer*, to, m_observers ) + to->test_unit_skipped( tc ); + + return; + } + + BOOST_TEST_FOREACH( test_observer*, to, m_observers ) + to->test_unit_start( tc ); + + boost::timer tc_timer; + test_unit_id bkup = m_curr_test_case; + m_curr_test_case = tc.p_id; + unit_test_monitor_t::error_level run_result = unit_test_monitor.execute_and_translate( tc ); + + unsigned long elapsed = static_cast<unsigned long>( tc_timer.elapsed() * 1e6 ); + + if( unit_test_monitor.is_critical_error( run_result ) ) { + BOOST_TEST_FOREACH( test_observer*, to, m_observers ) + to->test_aborted(); + } + + BOOST_TEST_FOREACH( test_observer*, to, m_observers ) + to->test_unit_finish( tc, elapsed ); + + m_curr_test_case = bkup; + + if( unit_test_monitor.is_critical_error( run_result ) ) + throw test_being_aborted(); + } + + bool test_suite_start( test_suite const& ts ) + { + if( !ts.check_dependencies() ) { + BOOST_TEST_FOREACH( test_observer*, to, m_observers ) + to->test_unit_skipped( ts ); + + return false; + } + + BOOST_TEST_FOREACH( test_observer*, to, m_observers ) + to->test_unit_start( ts ); + + return true; + } + + void test_suite_finish( test_suite const& ts ) + { + BOOST_TEST_FOREACH( test_observer*, to, m_observers ) + to->test_unit_finish( ts, 0 ); + } + + ////////////////////////////////////////////////////////////////// + struct priority_order { + bool operator()( test_observer* lhs, test_observer* rhs ) const + { + return (lhs->priority() < rhs->priority()) || ((lhs->priority() == rhs->priority()) && (lhs < rhs)); + } + }; + + typedef std::map<test_unit_id,test_unit*> test_unit_store; + typedef std::set<test_observer*,priority_order> observer_store; + + master_test_suite_t* m_master_test_suite; + test_unit_id m_curr_test_case; + test_unit_store m_test_units; + + test_unit_id m_next_test_case_id; + test_unit_id m_next_test_suite_id; + + bool m_is_initialized; + bool m_test_in_progress; + + observer_store m_observers; +}; + +//____________________________________________________________________________// + +namespace { + +#if defined(__CYGWIN__) +framework_impl& s_frk_impl() { static framework_impl* the_inst = 0; if(!the_inst) the_inst = new framework_impl; return *the_inst; } +#else +framework_impl& s_frk_impl() { static framework_impl the_inst; return the_inst; } +#endif + +} // local namespace + +//____________________________________________________________________________// + +namespace framework { + +void +init( init_unit_test_func init_func, int argc, char* argv[] ) +{ + runtime_config::init( argc, argv ); + + // set the log level and format + unit_test_log.set_threshold_level( runtime_config::log_level() ); + unit_test_log.set_format( runtime_config::log_format() ); + + // set the report level and format + results_reporter::set_level( runtime_config::report_level() ); + results_reporter::set_format( runtime_config::report_format() ); + + register_observer( results_collector ); + register_observer( unit_test_log ); + + if( runtime_config::show_progress() ) + register_observer( progress_monitor ); + + if( runtime_config::detect_memory_leaks() > 0 ) { + debug::detect_memory_leaks( true ); + debug::break_memory_alloc( runtime_config::detect_memory_leaks() ); + } + + // init master unit test suite + master_test_suite().argc = argc; + master_test_suite().argv = argv; + + try { + boost::execution_monitor em; + + ut_detail::test_init_caller tic( init_func ); + + em.execute( tic ); + } + catch( execution_exception const& ex ) { + throw setup_error( ex.what() ); + } + + s_frk_impl().m_is_initialized = true; +} + +//____________________________________________________________________________// + +bool +is_initialized() +{ + return s_frk_impl().m_is_initialized; +} + +//____________________________________________________________________________// + +void +register_test_unit( test_case* tc ) +{ + BOOST_TEST_SETUP_ASSERT( tc->p_id == INV_TEST_UNIT_ID, BOOST_TEST_L( "test case already registered" ) ); + + test_unit_id new_id = s_frk_impl().m_next_test_case_id; + + BOOST_TEST_SETUP_ASSERT( new_id != MAX_TEST_CASE_ID, BOOST_TEST_L( "too many test cases" ) ); + + typedef framework_impl::test_unit_store::value_type map_value_type; + + s_frk_impl().m_test_units.insert( map_value_type( new_id, tc ) ); + s_frk_impl().m_next_test_case_id++; + + s_frk_impl().set_tu_id( *tc, new_id ); +} + +//____________________________________________________________________________// + +void +register_test_unit( test_suite* ts ) +{ + BOOST_TEST_SETUP_ASSERT( ts->p_id == INV_TEST_UNIT_ID, BOOST_TEST_L( "test suite already registered" ) ); + + test_unit_id new_id = s_frk_impl().m_next_test_suite_id; + + BOOST_TEST_SETUP_ASSERT( new_id != MAX_TEST_SUITE_ID, BOOST_TEST_L( "too many test suites" ) ); + + typedef framework_impl::test_unit_store::value_type map_value_type; + s_frk_impl().m_test_units.insert( map_value_type( new_id, ts ) ); + s_frk_impl().m_next_test_suite_id++; + + s_frk_impl().set_tu_id( *ts, new_id ); +} + +//____________________________________________________________________________// + +void +deregister_test_unit( test_unit* tu ) +{ + s_frk_impl().m_test_units.erase( tu->p_id ); +} + +//____________________________________________________________________________// + +void +clear() +{ + s_frk_impl().clear(); +} + +//____________________________________________________________________________// + +void +register_observer( test_observer& to ) +{ + s_frk_impl().m_observers.insert( &to ); +} + +//____________________________________________________________________________// + +void +deregister_observer( test_observer& to ) +{ + s_frk_impl().m_observers.erase( &to ); +} + +//____________________________________________________________________________// + +void +reset_observers() +{ + s_frk_impl().m_observers.clear(); +} + +//____________________________________________________________________________// + +master_test_suite_t& +master_test_suite() +{ + if( !s_frk_impl().m_master_test_suite ) + s_frk_impl().m_master_test_suite = new master_test_suite_t; + + return *s_frk_impl().m_master_test_suite; +} + +//____________________________________________________________________________// + +test_case const& +current_test_case() +{ + return get<test_case>( s_frk_impl().m_curr_test_case ); +} + +//____________________________________________________________________________// + +test_unit& +get( test_unit_id id, test_unit_type t ) +{ + test_unit* res = s_frk_impl().m_test_units[id]; + + if( (res->p_type & t) == 0 ) + throw internal_error( "Invalid test unit type" ); + + return *res; +} + +//____________________________________________________________________________// + +void +run( test_unit_id id, bool continue_test ) +{ + if( id == INV_TEST_UNIT_ID ) + id = master_test_suite().p_id; + + test_case_counter tcc; + traverse_test_tree( id, tcc ); + + BOOST_TEST_SETUP_ASSERT( tcc.p_count != 0 , runtime_config::test_to_run().is_empty() + ? BOOST_TEST_L( "test tree is empty" ) + : BOOST_TEST_L( "no test cases matching filter" ) ); + + bool call_start_finish = !continue_test || !s_frk_impl().m_test_in_progress; + bool was_in_progress = s_frk_impl().m_test_in_progress; + + s_frk_impl().m_test_in_progress = true; + + if( call_start_finish ) { + BOOST_TEST_FOREACH( test_observer*, to, s_frk_impl().m_observers ) { + boost::execution_monitor em; + + try { + em.execute( ut_detail::test_start_caller( to, tcc.p_count ) ); + } + catch( execution_exception const& ex ) { + throw setup_error( ex.what() ); + } + } + } + + switch( runtime_config::random_seed() ) { + case 0: + break; + case 1: { + unsigned int seed = static_cast<unsigned int>( std::time( 0 ) ); + BOOST_TEST_MESSAGE( "Test cases order is shuffled using seed: " << seed ); + std::srand( seed ); + break; + } + default: + BOOST_TEST_MESSAGE( "Test cases order is shuffled using seed: " << runtime_config::random_seed() ); + std::srand( runtime_config::random_seed() ); + } + + try { + traverse_test_tree( id, s_frk_impl() ); + } + catch( test_being_aborted const& ) { + // abort already reported + } + + if( call_start_finish ) { + BOOST_TEST_FOREACH( test_observer*, to, s_frk_impl().m_observers ) + to->test_finish(); + } + + s_frk_impl().m_test_in_progress = was_in_progress; +} + +//____________________________________________________________________________// + +void +run( test_unit const* tu, bool continue_test ) +{ + run( tu->p_id, continue_test ); +} + +//____________________________________________________________________________// + +void +assertion_result( bool passed ) +{ + BOOST_TEST_FOREACH( test_observer*, to, s_frk_impl().m_observers ) + to->assertion_result( passed ); +} + +//____________________________________________________________________________// + +void +exception_caught( execution_exception const& ex ) +{ + BOOST_TEST_FOREACH( test_observer*, to, s_frk_impl().m_observers ) + to->exception_caught( ex ); +} + +//____________________________________________________________________________// + +void +test_unit_aborted( test_unit const& tu ) +{ + BOOST_TEST_FOREACH( test_observer*, to, s_frk_impl().m_observers ) + to->test_unit_aborted( tu ); +} + +//____________________________________________________________________________// + +} // namespace framework + +} // namespace unit_test + +} // namespace boost + +//____________________________________________________________________________// + +#include <boost/test/detail/enable_warnings.hpp> + +#endif // BOOST_TEST_FRAMEWORK_IPP_021005GER diff --git a/boost/test/impl/interaction_based.ipp b/boost/test/impl/interaction_based.ipp new file mode 100644 index 0000000000..56bb8672aa --- /dev/null +++ b/boost/test/impl/interaction_based.ipp @@ -0,0 +1,90 @@ +// (C) Copyright Gennadiy Rozental 2005-2008. +// Use, modification, and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// 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 : Facilities to perform interaction-based testing +// *************************************************************************** + +#ifndef BOOST_TEST_INTERACTION_BASED_IPP_112105GER +#define BOOST_TEST_INTERACTION_BASED_IPP_112105GER + +// Boost.Test +#include <boost/test/detail/config.hpp> + +#if BOOST_TEST_SUPPORT_INTERACTION_TESTING + +// Boost.Test +#include <boost/test/detail/config.hpp> +#include <boost/test/utils/callback.hpp> +#include <boost/test/interaction_based.hpp> +#include <boost/test/mock_object.hpp> +#include <boost/test/framework.hpp> // for setup_error + +#include <boost/test/detail/suppress_warnings.hpp> + +// STL +#include <stdexcept> +#include <string> + +//____________________________________________________________________________// + +namespace boost { + +namespace itest { // interaction-based testing + +// ************************************************************************** // +// ************** manager ************** // +// ************************************************************************** // + +manager::manager() +{ + instance_ptr( true, this ); +} + +//____________________________________________________________________________// + +manager::~manager() +{ + instance_ptr( true ); +} + +//____________________________________________________________________________// + +manager* +manager::instance_ptr( bool reset, manager* new_ptr ) +{ + static manager dummy( 0 ); + + static manager* ptr = &dummy; + + if( reset ) { + if( new_ptr ) { + BOOST_TEST_SETUP_ASSERT( ptr == &dummy, BOOST_TEST_L( "Can't run two interation based test the same time" ) ); + + ptr = new_ptr; + } + else + ptr = &dummy; + } + + return ptr; +} + +} // namespace itest + +} // namespace boost + +//____________________________________________________________________________// + +#include <boost/test/detail/enable_warnings.hpp> + +#endif // not ancient compiler + +#endif // BOOST_TEST_INTERACTION_BASED_IPP_112105GER diff --git a/boost/test/impl/logged_expectations.ipp b/boost/test/impl/logged_expectations.ipp new file mode 100644 index 0000000000..2df790c61b --- /dev/null +++ b/boost/test/impl/logged_expectations.ipp @@ -0,0 +1,246 @@ +// (C) Copyright Gennadiy Rozental 2005-2008. +// Use, modification, and distribution are subject to the +// Boost Software License, ELOG_VER 1.0. (See accompanying file +// 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 : Facilities to perform interaction based testng of logged expectations +// *************************************************************************** + +#ifndef BOOST_TEST_LOGGED_EXPECTATIONS_IPP_120905GER +#define BOOST_TEST_LOGGED_EXPECTATIONS_IPP_120905GER + +// Boost.Test +#include <boost/test/detail/config.hpp> + +#if BOOST_TEST_SUPPORT_INTERACTION_TESTING + +#include <boost/test/detail/global_typedef.hpp> + +#include <boost/test/utils/callback.hpp> +#include <boost/test/utils/iterator/token_iterator.hpp> + +#include <boost/test/interaction_based.hpp> +#include <boost/test/test_tools.hpp> + +#include <boost/test/detail/suppress_warnings.hpp> + +// Boost +#include <boost/lexical_cast.hpp> + +// STL +#include <fstream> + +//____________________________________________________________________________// + +namespace boost { + +using namespace ::boost::unit_test; + +namespace itest { + +// ************************************************************************** // +// ************** logged expectation test implementation ************** // +// ************************************************************************** // + +struct expectations_logger : itest::manager { + // Constructor + expectations_logger( const_string log_file_name, bool test_or_log ); + + virtual bool decision_point( const_string, std::size_t ); + virtual unsigned enter_scope( const_string, std::size_t, const_string scope_name ); + virtual void allocated( const_string, std::size_t, void*, std::size_t s ); + virtual void data_flow( const_string d ); + virtual std::string return_value( const_string default_value ); + +private: + // Data members + bool m_test_or_log; + std::fstream m_log_file; +}; + +literal_string ELOG_VER = "1.0"; +literal_string CLMN_SEP = "|"; +static const char LINE_SEP = '\n'; + +literal_string FILE_SIG = "ELOG"; +literal_string SCOPE_SIG = "SCOPE"; +literal_string ALLOC_SIG = "ALLOC"; +literal_string DP_SIG = "SWITCH"; +literal_string DATA_SIG = "DATA"; +literal_string RETURN_SIG = "RETURN"; + +//____________________________________________________________________________// + +expectations_logger::expectations_logger( const_string log_file_name, bool test_or_log ) +: m_test_or_log( test_or_log ) +{ + BOOST_REQUIRE_MESSAGE( !log_file_name.is_empty(), "Empty expectations log file name" ); + + m_log_file.open( log_file_name.begin(), test_or_log ? std::ios::in : std::ios::out ); + + BOOST_REQUIRE_MESSAGE( m_log_file.is_open(), + "Can't open expectations log file " << log_file_name + << " for " << ( m_test_or_log ? "reading" : "writing") ); + + if( m_test_or_log ) { + std::string line; + + std::getline( m_log_file, line, LINE_SEP ); + + const_string cline( line ); + string_token_iterator tit( cline, (dropped_delimeters = CLMN_SEP, kept_delimeters = dt_none)); + + BOOST_CHECK_EQUAL( *tit, FILE_SIG ); + ++tit; + BOOST_CHECK_EQUAL( *tit, ELOG_VER ); + } + else { + m_log_file << FILE_SIG << CLMN_SEP << ELOG_VER << LINE_SEP; + } +} + +//____________________________________________________________________________// + +bool +expectations_logger::decision_point( const_string, std::size_t ) +{ + if( m_test_or_log ) { + std::string line; + + std::getline( m_log_file, line, LINE_SEP ); + + const_string cline( line ); + string_token_iterator tit( cline, (dropped_delimeters = CLMN_SEP, kept_delimeters = dt_none)); + + BOOST_CHECK_EQUAL( *tit, DP_SIG ); ++tit; + return lexical_cast<bool>( *tit ); + } + else { + m_log_file << DP_SIG << CLMN_SEP << std::boolalpha << true << LINE_SEP; + + return true; + } +} + +//____________________________________________________________________________// + +unsigned +expectations_logger::enter_scope( const_string, std::size_t, const_string scope_name ) +{ + if( m_test_or_log ) { + std::string line; + + std::getline( m_log_file, line, LINE_SEP ); + + const_string cline( line ); + string_token_iterator tit( cline, (dropped_delimeters = CLMN_SEP, kept_delimeters = dt_none)); + + BOOST_CHECK_EQUAL( *tit, SCOPE_SIG ); ++tit; + BOOST_CHECK_EQUAL( *tit, scope_name ); + } + else { + m_log_file << SCOPE_SIG << CLMN_SEP << scope_name << LINE_SEP; + } + + return 0; +} + +//____________________________________________________________________________// + +void +expectations_logger::allocated( const_string, std::size_t, void*, std::size_t s ) +{ + if( m_test_or_log ) { + std::string line; + + std::getline( m_log_file, line, LINE_SEP ); + + const_string cline( line ); + string_token_iterator tit( cline, (dropped_delimeters = CLMN_SEP, kept_delimeters = dt_none)); + + BOOST_CHECK_EQUAL( *tit, ALLOC_SIG ); ++tit; + BOOST_CHECK_EQUAL( lexical_cast<std::size_t>( *tit ), s ); + } + else { + m_log_file << ALLOC_SIG << CLMN_SEP << s << LINE_SEP; + } +} + +//____________________________________________________________________________// + +void +expectations_logger::data_flow( const_string d ) +{ + if( m_test_or_log ) { + std::string line; + + std::getline( m_log_file, line, LINE_SEP ); + + const_string cline( line ); + string_token_iterator tit( cline, (dropped_delimeters = CLMN_SEP, kept_delimeters = dt_none)); + + BOOST_CHECK_EQUAL( *tit, DATA_SIG ); ++tit; + BOOST_CHECK_EQUAL( *tit, d ); + } + else { + m_log_file << DATA_SIG << CLMN_SEP << d << LINE_SEP; + } +} + +//____________________________________________________________________________// + +std::string +expectations_logger::return_value( const_string default_value ) +{ + if( m_test_or_log ) { + std::string line; + + std::getline( m_log_file, line, LINE_SEP ); + + const_string cline( line ); + string_token_iterator tit( cline, (dropped_delimeters = CLMN_SEP, kept_delimeters = dt_none)); + + BOOST_CHECK_EQUAL( *tit, RETURN_SIG ); ++tit; + + return std::string( tit->begin(), tit->size() ); + } + else { + m_log_file << RETURN_SIG << CLMN_SEP << default_value << LINE_SEP; + + return std::string(); + } +} + +//____________________________________________________________________________// + +// ************************************************************************** // +// ************** logged expectations test ************** // +// ************************************************************************** // + +void BOOST_TEST_DECL +logged_expectations( callback0<> const& F, const_string log_file_name, bool test_or_log ) +{ + expectations_logger el( log_file_name, test_or_log ); + + F(); +} + +//____________________________________________________________________________// + +} // namespace itest + +} // namespace boost + +//____________________________________________________________________________// + +#include <boost/test/detail/enable_warnings.hpp> + +#endif // not ancient compiler + +#endif // BOOST_TEST_LOGGED_EXPECTATIONS_IPP_120905GER diff --git a/boost/test/impl/plain_report_formatter.ipp b/boost/test/impl/plain_report_formatter.ipp new file mode 100644 index 0000000000..d901b8ea42 --- /dev/null +++ b/boost/test/impl/plain_report_formatter.ipp @@ -0,0 +1,198 @@ +// (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: 49312 $ +// +// Description : plain report formatter definition +// *************************************************************************** + +#ifndef BOOST_TEST_PLAIN_REPORT_FORMATTER_IPP_020105GER +#define BOOST_TEST_PLAIN_REPORT_FORMATTER_IPP_020105GER + +// Boost.Test +#include <boost/test/output/plain_report_formatter.hpp> +#include <boost/test/utils/custom_manip.hpp> +#include <boost/test/results_collector.hpp> +#include <boost/test/unit_test_suite_impl.hpp> + +#include <boost/test/utils/basic_cstring/io.hpp> + +// STL +#include <iomanip> +#include <boost/config/no_tr1/cmath.hpp> +#include <iostream> + +#include <boost/test/detail/suppress_warnings.hpp> + +# ifdef BOOST_NO_STDC_NAMESPACE +namespace std { using ::log10; } +# endif + +//____________________________________________________________________________// + +namespace boost { + +namespace unit_test { + +namespace output { + +namespace { + +typedef custom_manip<struct quote_t> quote; + +template<typename T> +inline std::ostream& +operator<<( custom_printer<quote> const& p, T const& value ) +{ + *p << '"' << value << '"'; + + return *p; +} + +//____________________________________________________________________________// + +void +print_stat_value( std::ostream& ostr, counter_t v, counter_t indent, counter_t total, + const_string name, const_string res ) +{ + if( v > 0 ) { + ostr << std::setw( indent ) << "" + << v << ' ' << name << ( v != 1 ? "s" : "" ); + if( total > 0 ) + ostr << " out of " << total; + + ostr << ' ' << res << '\n'; + } +} + +//____________________________________________________________________________// + +} // local namespace + +// ************************************************************************** // +// ************** plain_report_formatter ************** // +// ************************************************************************** // + +void +plain_report_formatter::results_report_start( std::ostream& ostr ) +{ + m_indent = 0; + ostr << '\n'; +} + +//____________________________________________________________________________// + +void +plain_report_formatter::results_report_finish( std::ostream& ostr ) +{ + ostr.flush(); +} + +//____________________________________________________________________________// + +void +plain_report_formatter::test_unit_report_start( test_unit const& tu, std::ostream& ostr ) +{ + test_results const& tr = results_collector.results( tu.p_id ); + + const_string descr; + + if( tr.passed() ) + descr = "passed"; + else if( tr.p_skipped ) + descr = "skipped"; + else if( tr.p_aborted ) + descr = "aborted"; + else + descr = "failed"; + + ostr << std::setw( m_indent ) << "" + << "Test " << (tu.p_type == tut_case ? "case " : "suite " ) << quote() << tu.p_name << ' ' << descr; + + if( tr.p_skipped ) { + ostr << " due to " << (tu.check_dependencies() ? "test aborting\n" : "failed dependancy\n" ); + m_indent += 2; + return; + } + + counter_t total_assertions = tr.p_assertions_passed + tr.p_assertions_failed; + counter_t total_tc = tr.p_test_cases_passed + tr.p_test_cases_failed + tr.p_test_cases_skipped; + + if( total_assertions > 0 || total_tc > 0 ) + ostr << " with:"; + + ostr << '\n'; + m_indent += 2; + + print_stat_value( ostr, tr.p_assertions_passed, m_indent, total_assertions, "assertion", "passed" ); + print_stat_value( ostr, tr.p_assertions_failed, m_indent, total_assertions, "assertion", "failed" ); + print_stat_value( ostr, tr.p_expected_failures, m_indent, 0 , "failure" , "expected" ); + print_stat_value( ostr, tr.p_test_cases_passed, m_indent, total_tc , "test case", "passed" ); + print_stat_value( ostr, tr.p_test_cases_failed, m_indent, total_tc , "test case", "failed" ); + print_stat_value( ostr, tr.p_test_cases_skipped, m_indent, total_tc , "test case", "skipped" ); + print_stat_value( ostr, tr.p_test_cases_aborted, m_indent, total_tc , "test case", "aborted" ); + + ostr << '\n'; +} + +//____________________________________________________________________________// + +void +plain_report_formatter::test_unit_report_finish( test_unit const&, std::ostream& ) +{ + m_indent -= 2; +} + +//____________________________________________________________________________// + +void +plain_report_formatter::do_confirmation_report( test_unit const& tu, std::ostream& ostr ) +{ + test_results const& tr = results_collector.results( tu.p_id ); + + if( tr.passed() ) { + ostr << "*** No errors detected\n"; + return; + } + + if( tr.p_skipped ) { + ostr << "*** Test " << tu.p_type_name << " skipped due to " + << (tu.check_dependencies() ? "test aborting\n" : "failed dependancy\n" ); + return; + } + + if( tr.p_assertions_failed == 0 ) { + ostr << "*** errors detected in test " << tu.p_type_name << " " << quote() << tu.p_name + << "; see standard output for details\n"; + return; + } + + counter_t num_failures = tr.p_assertions_failed; + + ostr << "*** " << num_failures << " failure" << ( num_failures != 1 ? "s" : "" ) << " detected"; + + if( tr.p_expected_failures > 0 ) + ostr << " (" << tr.p_expected_failures << " failure" << ( tr.p_expected_failures != 1 ? "s" : "" ) << " expected)"; + + ostr << " in test " << tu.p_type_name << " " << quote() << tu.p_name << "\n"; +} + +//____________________________________________________________________________// + +} // namespace output + +} // namespace unit_test + +} // namespace boost + +//____________________________________________________________________________// + +#include <boost/test/detail/enable_warnings.hpp> + +#endif // BOOST_TEST_PLAIN_REPORT_FORMATTER_IPP_020105GER diff --git a/boost/test/impl/progress_monitor.ipp b/boost/test/impl/progress_monitor.ipp new file mode 100644 index 0000000000..d245aa6185 --- /dev/null +++ b/boost/test/impl/progress_monitor.ipp @@ -0,0 +1,110 @@ +// (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: 57992 $ +// +// Description : implements simple text based progress monitor +// *************************************************************************** + +#ifndef BOOST_TEST_PROGRESS_MONITOR_IPP_020105GER +#define BOOST_TEST_PROGRESS_MONITOR_IPP_020105GER + +// Boost.Test +#include <boost/test/progress_monitor.hpp> +#include <boost/test/unit_test_suite_impl.hpp> + +#include <boost/test/detail/unit_test_parameters.hpp> + +// Boost +#include <boost/progress.hpp> +#include <boost/scoped_ptr.hpp> + +#include <boost/test/detail/suppress_warnings.hpp> + +//____________________________________________________________________________// + +namespace boost { + +namespace unit_test { + +// ************************************************************************** // +// ************** progress_monitor ************** // +// ************************************************************************** // + +namespace { + +struct progress_monitor_impl { + // Constructor + progress_monitor_impl() + : m_stream( runtime_config::log_sink() ) + {} + + std::ostream* m_stream; + scoped_ptr<progress_display> m_progress_display; +}; + +progress_monitor_impl& s_pm_impl() { static progress_monitor_impl the_inst; return the_inst; } + +} // local namespace + +//____________________________________________________________________________// + +void +progress_monitor_t::test_start( counter_t test_cases_amount ) +{ + s_pm_impl().m_progress_display.reset( new progress_display( test_cases_amount, *s_pm_impl().m_stream ) ); +} + +//____________________________________________________________________________// + +void +progress_monitor_t::test_aborted() +{ + (*s_pm_impl().m_progress_display) += s_pm_impl().m_progress_display->count(); +} + +//____________________________________________________________________________// + +void +progress_monitor_t::test_unit_finish( test_unit const& tu, unsigned long ) +{ + if( tu.p_type == tut_case ) + ++(*s_pm_impl().m_progress_display); +} + +//____________________________________________________________________________// + +void +progress_monitor_t::test_unit_skipped( test_unit const& tu ) +{ + test_case_counter tcc; + traverse_test_tree( tu, tcc ); + + (*s_pm_impl().m_progress_display) += tcc.p_count; +} + +//____________________________________________________________________________// + +void +progress_monitor_t::set_stream( std::ostream& ostr ) +{ + s_pm_impl().m_stream = &ostr; +} + +//____________________________________________________________________________// + +} // namespace unit_test + +} // namespace boost + +//____________________________________________________________________________// + +#include <boost/test/detail/enable_warnings.hpp> + +#endif // BOOST_TEST_PROGRESS_MONITOR_IPP_020105GER diff --git a/boost/test/impl/results_collector.ipp b/boost/test/impl/results_collector.ipp new file mode 100644 index 0000000000..e85528ba99 --- /dev/null +++ b/boost/test/impl/results_collector.ipp @@ -0,0 +1,294 @@ +// (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: 57992 $ +// +// Description : implements Unit Test results collecting facility. +// *************************************************************************** + +#ifndef BOOST_TEST_RESULTS_COLLECTOR_IPP_021105GER +#define BOOST_TEST_RESULTS_COLLECTOR_IPP_021105GER + +// Boost.Test +#include <boost/test/unit_test_suite_impl.hpp> +#include <boost/test/unit_test_log.hpp> +#include <boost/test/results_collector.hpp> +#include <boost/test/framework.hpp> + +// Boost +#include <boost/cstdlib.hpp> + +// STL +#include <map> + +#include <boost/test/detail/suppress_warnings.hpp> + +//____________________________________________________________________________// + +namespace boost { + +namespace unit_test { + +// ************************************************************************** // +// ************** test_results ************** // +// ************************************************************************** // + +test_results::test_results() +{ + clear(); +} + +//____________________________________________________________________________// + +bool +test_results::passed() const +{ + return !p_skipped && + p_test_cases_failed == 0 && + p_assertions_failed <= p_expected_failures && + !p_aborted; +} + +//____________________________________________________________________________// + +int +test_results::result_code() const +{ + return passed() ? exit_success + : ( (p_assertions_failed > p_expected_failures || p_skipped ) + ? exit_test_failure + : exit_exception_failure ); +} + +//____________________________________________________________________________// + +void +test_results::operator+=( test_results const& tr ) +{ + p_assertions_passed.value += tr.p_assertions_passed; + p_assertions_failed.value += tr.p_assertions_failed; + p_test_cases_passed.value += tr.p_test_cases_passed; + 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; +} + +//____________________________________________________________________________// + +void +test_results::clear() +{ + p_assertions_passed.value = 0; + p_assertions_failed.value = 0; + p_expected_failures.value = 0; + p_test_cases_passed.value = 0; + p_test_cases_failed.value = 0; + p_test_cases_skipped.value = 0; + p_test_cases_aborted.value = 0; + p_aborted.value = false; + p_skipped.value = true; +} + +//____________________________________________________________________________// + +// ************************************************************************** // +// ************** results_collector ************** // +// ************************************************************************** // + +#if !BOOST_WORKAROUND(BOOST_MSVC, <1300) + +namespace { + +struct results_collector_impl { + std::map<test_unit_id,test_results> m_results_store; +}; + +results_collector_impl& s_rc_impl() { static results_collector_impl the_inst; return the_inst; } + +} // local namespace + +#else + +struct results_collector_impl { + std::map<test_unit_id,test_results> m_results_store; +}; + +static results_collector_impl& s_rc_impl() { static results_collector_impl the_inst; return the_inst; } + +#endif + +//____________________________________________________________________________// + +void +results_collector_t::test_start( counter_t ) +{ + s_rc_impl().m_results_store.clear(); +} + +//____________________________________________________________________________// + +void +results_collector_t::test_finish() +{ + // do nothing +} + +//____________________________________________________________________________// + +void +results_collector_t::test_aborted() +{ + // do nothing +} + +//____________________________________________________________________________// + +void +results_collector_t::test_unit_start( test_unit const& tu ) +{ + // init test_results entry + test_results& tr = s_rc_impl().m_results_store[tu.p_id]; + + tr.clear(); + + tr.p_expected_failures.value = tu.p_expected_failures; + tr.p_skipped.value = false; +} + +//____________________________________________________________________________// + +class results_collect_helper : public test_tree_visitor { +public: + explicit results_collect_helper( test_results& tr, test_unit const& ts ) : m_tr( tr ), m_ts( ts ) {} + + void visit( test_case const& tc ) + { + test_results const& tr = results_collector.results( tc.p_id ); + m_tr += tr; + + if( tr.passed() ) + m_tr.p_test_cases_passed.value++; + else if( tr.p_skipped ) + m_tr.p_test_cases_skipped.value++; + else { + if( tr.p_aborted ) + m_tr.p_test_cases_aborted.value++; + m_tr.p_test_cases_failed.value++; + } + } + bool test_suite_start( test_suite const& ts ) + { + if( m_ts.p_id == ts.p_id ) + return true; + else { + m_tr += results_collector.results( ts.p_id ); + return false; + } + } + +private: + // Data members + test_results& m_tr; + test_unit const& m_ts; +}; + +//____________________________________________________________________________// + +void +results_collector_t::test_unit_finish( test_unit const& tu, unsigned long ) +{ + if( tu.p_type == tut_suite ) { + results_collect_helper ch( s_rc_impl().m_results_store[tu.p_id], tu ); + + traverse_test_tree( tu, ch ); + } + else { + test_results const& tr = s_rc_impl().m_results_store[tu.p_id]; + + 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.p_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.p_name << " did not check any assertions" ); + } +} + +//____________________________________________________________________________// + +void +results_collector_t::test_unit_skipped( test_unit const& tu ) +{ + if( tu.p_type == tut_suite ) { + test_case_counter tcc; + traverse_test_tree( tu, tcc ); + + test_results& tr = s_rc_impl().m_results_store[tu.p_id]; + + tr.clear(); + + tr.p_skipped.value = true; + tr.p_test_cases_skipped.value = tcc.p_count; + } +} + +//____________________________________________________________________________// + +void +results_collector_t::assertion_result( bool passed ) +{ + test_results& tr = s_rc_impl().m_results_store[framework::current_test_case().p_id]; + + if( passed ) + tr.p_assertions_passed.value++; + else + tr.p_assertions_failed.value++; + + if( tr.p_assertions_failed == 1 ) + first_failed_assertion(); +} + +//____________________________________________________________________________// + +void +results_collector_t::exception_caught( execution_exception const& ) +{ + test_results& tr = s_rc_impl().m_results_store[framework::current_test_case().p_id]; + + tr.p_assertions_failed.value++; +} + +//____________________________________________________________________________// + +void +results_collector_t::test_unit_aborted( test_unit const& tu ) +{ + s_rc_impl().m_results_store[tu.p_id].p_aborted.value = true; +} + +//____________________________________________________________________________// + +test_results const& +results_collector_t::results( test_unit_id id ) const +{ + return s_rc_impl().m_results_store[id]; +} + +//____________________________________________________________________________// + +} // namespace unit_test + +} // namespace boost + +//____________________________________________________________________________// + +#include <boost/test/detail/enable_warnings.hpp> + +#endif // BOOST_TEST_RESULTS_COLLECTOR_IPP_021105GER diff --git a/boost/test/impl/results_reporter.ipp b/boost/test/impl/results_reporter.ipp new file mode 100644 index 0000000000..71a0f66f91 --- /dev/null +++ b/boost/test/impl/results_reporter.ipp @@ -0,0 +1,202 @@ +// (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: 57992 $ +// +// Description : result reporting facilties +// *************************************************************************** + +#ifndef BOOST_TEST_RESULTS_REPORTER_IPP_020105GER +#define BOOST_TEST_RESULTS_REPORTER_IPP_020105GER + +// Boost.Test +#include <boost/test/results_reporter.hpp> +#include <boost/test/unit_test_suite_impl.hpp> +#include <boost/test/results_collector.hpp> +#include <boost/test/framework.hpp> +#include <boost/test/output/plain_report_formatter.hpp> +#include <boost/test/output/xml_report_formatter.hpp> + +#include <boost/test/detail/unit_test_parameters.hpp> + +// Boost +#include <boost/scoped_ptr.hpp> +#include <boost/io/ios_state.hpp> +typedef ::boost::io::ios_base_all_saver io_saver_type; + +// STL +#include <iostream> + +#include <boost/test/detail/suppress_warnings.hpp> + +//____________________________________________________________________________// + +namespace boost { + +namespace unit_test { + +namespace results_reporter { + +// ************************************************************************** // +// ************** result reporter implementation ************** // +// ************************************************************************** // + +namespace { + +struct results_reporter_impl : test_tree_visitor { + // Constructor + results_reporter_impl() + : m_output( runtime_config::report_sink() ) + , m_stream_state_saver( new io_saver_type( *m_output ) ) + , m_report_level( CONFIRMATION_REPORT ) + , m_formatter( new output::plain_report_formatter ) + {} + + // test tree visitor interface implementation + void visit( test_case const& tc ) + { + m_formatter->test_unit_report_start( tc, *m_output ); + m_formatter->test_unit_report_finish( tc, *m_output ); + } + bool test_suite_start( test_suite const& ts ) + { + m_formatter->test_unit_report_start( ts, *m_output ); + + if( m_report_level == DETAILED_REPORT && !results_collector.results( ts.p_id ).p_skipped ) + return true; + + m_formatter->test_unit_report_finish( ts, *m_output ); + return false; + } + void test_suite_finish( test_suite const& ts ) + { + m_formatter->test_unit_report_finish( ts, *m_output ); + } + + typedef scoped_ptr<io_saver_type> saver_ptr; + + // Data members + std::ostream* m_output; + saver_ptr m_stream_state_saver; + report_level m_report_level; + scoped_ptr<format> m_formatter; +}; + +results_reporter_impl& s_rr_impl() { static results_reporter_impl the_inst; return the_inst; } + +} // local namespace + +// ************************************************************************** // +// ************** report configuration ************** // +// ************************************************************************** // + +void +set_level( report_level l ) +{ + if( l != INV_REPORT_LEVEL ) + s_rr_impl().m_report_level = l; +} + +//____________________________________________________________________________// + +void +set_stream( std::ostream& ostr ) +{ + s_rr_impl().m_output = &ostr; + s_rr_impl().m_stream_state_saver.reset( new io_saver_type( ostr ) ); +} + +//____________________________________________________________________________// + +std::ostream& +get_stream() +{ + return *s_rr_impl().m_output; +} + +//____________________________________________________________________________// + +void +set_format( output_format rf ) +{ + switch( rf ) { + case CLF: + set_format( new output::plain_report_formatter ); + break; + case XML: + set_format( new output::xml_report_formatter ); + break; + default: + break; + } +} + +//____________________________________________________________________________// + +void +set_format( results_reporter::format* f ) +{ + if( f ) + s_rr_impl().m_formatter.reset( f ); +} + +//____________________________________________________________________________// + +// ************************************************************************** // +// ************** report initiation ************** // +// ************************************************************************** // + +void +make_report( report_level l, test_unit_id id ) +{ + if( l == INV_REPORT_LEVEL ) + l = s_rr_impl().m_report_level; + + if( l == NO_REPORT ) + return; + + if( id == INV_TEST_UNIT_ID ) + id = framework::master_test_suite().p_id; + + s_rr_impl().m_stream_state_saver->restore(); + + report_level bkup = s_rr_impl().m_report_level; + s_rr_impl().m_report_level = l; + + s_rr_impl().m_formatter->results_report_start( *s_rr_impl().m_output ); + + switch( l ) { + case CONFIRMATION_REPORT: + s_rr_impl().m_formatter->do_confirmation_report( framework::get<test_unit>( id ), *s_rr_impl().m_output ); + break; + case SHORT_REPORT: + case DETAILED_REPORT: + traverse_test_tree( id, s_rr_impl() ); + break; + default: + break; + } + + s_rr_impl().m_formatter->results_report_finish( *s_rr_impl().m_output ); + s_rr_impl().m_report_level = bkup; +} + +//____________________________________________________________________________// + +} // namespace results_reporter + +} // namespace unit_test + +} // namespace boost + +//____________________________________________________________________________// + +#include <boost/test/detail/enable_warnings.hpp> + +#endif // BOOST_TEST_RESULTS_REPORTER_IPP_020105GER diff --git a/boost/test/impl/test_main.ipp b/boost/test/impl/test_main.ipp new file mode 100644 index 0000000000..6c41ceea6f --- /dev/null +++ b/boost/test/impl/test_main.ipp @@ -0,0 +1,68 @@ +// (C) Copyright Gennadiy Rozental 2001-2008. +// (C) Copyright Beman Dawes 1995-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$ +// +// Version : $$Revision: 49312 $ +// +// Description : implements main function for Test Execution Monitor. +// *************************************************************************** + +#ifndef BOOST_TEST_TEST_MAIN_IPP_012205GER +#define BOOST_TEST_TEST_MAIN_IPP_012205GER + +// Boost.Test +#include <boost/test/framework.hpp> +#include <boost/test/test_tools.hpp> +#include <boost/test/unit_test_suite.hpp> + +// Boost +#include <boost/cstdlib.hpp> + +#include <boost/test/detail/suppress_warnings.hpp> + +//____________________________________________________________________________// + +extern int test_main( int argc, char* argv[] ); // prototype for user's test_main() + +struct test_main_caller { + test_main_caller( int argc, char** argv ) : m_argc( argc ), m_argv( argv ) {} + + void operator()() { + int test_main_result = test_main( m_argc, m_argv ); + + // translate a test_main non-success return into a test error + BOOST_CHECK( test_main_result == 0 || test_main_result == boost::exit_success ); + } + +private: + // Data members + int m_argc; + char** m_argv; +}; + +// ************************************************************************** // +// ************** test main ************** // +// ************************************************************************** // + +::boost::unit_test::test_suite* +init_unit_test_suite( int argc, char* argv[] ) { + using namespace ::boost::unit_test; + + framework::master_test_suite().p_name.value = "Test Program"; + + framework::master_test_suite().add( BOOST_TEST_CASE( test_main_caller( argc, argv ) ) ); + + return 0; +} + +//____________________________________________________________________________// + +#include <boost/test/detail/enable_warnings.hpp> + +#endif // BOOST_TEST_TEST_MAIN_IPP_012205GER diff --git a/boost/test/impl/test_tools.ipp b/boost/test/impl/test_tools.ipp new file mode 100644 index 0000000000..67fb25dc0e --- /dev/null +++ b/boost/test/impl/test_tools.ipp @@ -0,0 +1,628 @@ +// (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 diff --git a/boost/test/impl/unit_test_log.ipp b/boost/test/impl/unit_test_log.ipp new file mode 100644 index 0000000000..dbe4015514 --- /dev/null +++ b/boost/test/impl/unit_test_log.ipp @@ -0,0 +1,444 @@ +// (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: 57992 $ +// +// Description : implemets Unit Test Log +// *************************************************************************** + +#ifndef BOOST_TEST_UNIT_TEST_LOG_IPP_012205GER +#define BOOST_TEST_UNIT_TEST_LOG_IPP_012205GER + +// Boost.Test +#include <boost/test/unit_test_log.hpp> +#include <boost/test/unit_test_log_formatter.hpp> +#include <boost/test/unit_test_suite_impl.hpp> +#include <boost/test/execution_monitor.hpp> + +#include <boost/test/detail/unit_test_parameters.hpp> + +#include <boost/test/utils/basic_cstring/compare.hpp> + +#include <boost/test/output/compiler_log_formatter.hpp> +#include <boost/test/output/xml_log_formatter.hpp> + +// Boost +#include <boost/scoped_ptr.hpp> +#include <boost/io/ios_state.hpp> +typedef ::boost::io::ios_base_all_saver io_saver_type; + +#include <boost/test/detail/suppress_warnings.hpp> + +//____________________________________________________________________________// + +namespace boost { + +namespace unit_test { + +// ************************************************************************** // +// ************** entry_value_collector ************** // +// ************************************************************************** // + +namespace ut_detail { + +entry_value_collector const& +entry_value_collector::operator<<( lazy_ostream const& v ) const +{ + unit_test_log << v; + + return *this; +} + +//____________________________________________________________________________// + +entry_value_collector const& +entry_value_collector::operator<<( const_string v ) const +{ + unit_test_log << v; + + return *this; +} + +//____________________________________________________________________________// + +entry_value_collector::~entry_value_collector() +{ + if( m_last ) + unit_test_log << log::end(); +} + +//____________________________________________________________________________// + +} // namespace ut_detail + +// ************************************************************************** // +// ************** unit_test_log ************** // +// ************************************************************************** // + +namespace { + +struct unit_test_log_impl { + // Constructor + unit_test_log_impl() + : m_stream( runtime_config::log_sink() ) + , m_stream_state_saver( new io_saver_type( *m_stream ) ) + , m_threshold_level( log_all_errors ) + , m_log_formatter( new output::compiler_log_formatter ) + { + } + + // 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; + + // entry data + bool m_entry_in_progress; + bool m_entry_started; + log_entry_data m_entry_data; + + // 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 ); + m_checkpoint_data.m_file_name = file; + m_checkpoint_data.m_line_num = line_num; + } +}; + +unit_test_log_impl& s_log_impl() { static unit_test_log_impl the_inst; return the_inst; } + +} // local namespace + +//____________________________________________________________________________// + +void +unit_test_log_t::test_start( counter_t test_cases_amount ) +{ + if( s_log_impl().m_threshold_level == log_nothing ) + return; + + s_log_impl().m_log_formatter->log_start( s_log_impl().stream(), test_cases_amount ); + + if( runtime_config::show_build_info() ) + s_log_impl().m_log_formatter->log_build_info( s_log_impl().stream() ); + + s_log_impl().m_entry_in_progress = false; +} + +//____________________________________________________________________________// + +void +unit_test_log_t::test_finish() +{ + if( s_log_impl().m_threshold_level == log_nothing ) + return; + + s_log_impl().m_log_formatter->log_finish( s_log_impl().stream() ); + + s_log_impl().stream().flush(); +} + +//____________________________________________________________________________// + +void +unit_test_log_t::test_aborted() +{ + BOOST_TEST_LOG_ENTRY( log_messages ) << "Test is 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 ) + *this << log::end(); + + s_log_impl().m_log_formatter->test_unit_start( s_log_impl().stream(), 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 ) + *this << log::end(); + + s_log_impl().m_log_formatter->test_unit_finish( s_log_impl().stream(), tu, elapsed ); +} + +//____________________________________________________________________________// + +void +unit_test_log_t::test_unit_skipped( test_unit const& tu ) +{ + if( s_log_impl().m_threshold_level > log_test_units ) + return; + + if( s_log_impl().m_entry_in_progress ) + *this << log::end(); + + s_log_impl().m_log_formatter->test_unit_skipped( s_log_impl().stream(), tu ); +} + +//____________________________________________________________________________// + +void +unit_test_log_t::test_unit_aborted( test_unit const& ) +{ + // do nothing +} + +//____________________________________________________________________________// + +void +unit_test_log_t::assertion_result( bool ) +{ + // do nothing +} + +//____________________________________________________________________________// + +void +unit_test_log_t::exception_caught( execution_exception const& ex ) +{ + log_level l = + ex.code() <= execution_exception::cpp_exception_error ? log_cpp_exception_errors : + (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(); + + s_log_impl().m_log_formatter->log_exception( s_log_impl().stream(), s_log_impl().m_checkpoint_data, ex ); + } +} + +//____________________________________________________________________________// + +void +unit_test_log_t::set_checkpoint( const_string file, std::size_t line_num, const_string msg ) +{ + s_log_impl().set_checkpoint( file, line_num, msg ); +} + +//____________________________________________________________________________// + +char +set_unix_slash( char in ) +{ + return in == '\\' ? '/' : in; +} + +unit_test_log_t& +unit_test_log_t::operator<<( log::begin const& b ) +{ + if( s_log_impl().m_entry_in_progress ) + *this << log::end(); + + s_log_impl().m_stream_state_saver->restore(); + + s_log_impl().m_entry_data.clear(); + + assign_op( s_log_impl().m_entry_data.m_file_name, b.m_file_name, 0 ); + + // normalize file name + std::transform( s_log_impl().m_entry_data.m_file_name.begin(), s_log_impl().m_entry_data.m_file_name.end(), + s_log_impl().m_entry_data.m_file_name.begin(), + &set_unix_slash ); + + s_log_impl().m_entry_data.m_line_num = b.m_line_num; + + return *this; +} + +//____________________________________________________________________________// + +unit_test_log_t& +unit_test_log_t::operator<<( log::end const& ) +{ + if( s_log_impl().m_entry_in_progress ) + s_log_impl().m_log_formatter->log_entry_finish( s_log_impl().stream() ); + + s_log_impl().m_entry_in_progress = false; + + return *this; +} + +//____________________________________________________________________________// + +unit_test_log_t& +unit_test_log_t::operator<<( log_level l ) +{ + s_log_impl().m_entry_data.m_level = l; + + return *this; +} + +//____________________________________________________________________________// + +ut_detail::entry_value_collector +unit_test_log_t::operator()( log_level l ) +{ + *this << l; + + return ut_detail::entry_value_collector(); +} + +//____________________________________________________________________________// + +bool +unit_test_log_t::log_entry_start() +{ + if( s_log_impl().m_entry_in_progress ) + 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; +} + +//____________________________________________________________________________// + +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 ); + + return *this; +} + +//____________________________________________________________________________// + +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 ); + + return *this; +} + +//____________________________________________________________________________// + +void +unit_test_log_t::set_stream( std::ostream& str ) +{ + if( s_log_impl().m_entry_in_progress ) + return; + + s_log_impl().m_stream = &str; + s_log_impl().m_stream_state_saver.reset( new io_saver_type( str ) ); +} + +//____________________________________________________________________________// + +void +unit_test_log_t::set_threshold_level( log_level lev ) +{ + if( s_log_impl().m_entry_in_progress || lev == invalid_log_level ) + return; + + s_log_impl().m_threshold_level = lev; +} + +//____________________________________________________________________________// + +void +unit_test_log_t::set_format( output_format log_format ) +{ + if( s_log_impl().m_entry_in_progress ) + return; + + if( log_format == CLF ) + set_formatter( new output::compiler_log_formatter ); + else + set_formatter( new output::xml_log_formatter ); +} + +//____________________________________________________________________________// + +void +unit_test_log_t::set_formatter( unit_test_log_formatter* the_formatter ) +{ + s_log_impl().m_log_formatter.reset( the_formatter ); +} + +//____________________________________________________________________________// + +// ************************************************************************** // +// ************** unit_test_log_formatter ************** // +// ************************************************************************** // + +void +unit_test_log_formatter::log_entry_value( std::ostream& ostr, lazy_ostream const& value ) +{ + log_entry_value( ostr, (wrap_stringstream().ref() << value).str() ); +} + +//____________________________________________________________________________// + +} // namespace unit_test + +} // namespace boost + +//____________________________________________________________________________// + +#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 new file mode 100644 index 0000000000..9874c69ab2 --- /dev/null +++ b/boost/test/impl/unit_test_main.ipp @@ -0,0 +1,246 @@ +// (C) Copyright Gennadiy Rozental 2001-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 : main function implementation for Unit Test Framework +// *************************************************************************** + +#ifndef BOOST_TEST_UNIT_TEST_MAIN_IPP_012205GER +#define BOOST_TEST_UNIT_TEST_MAIN_IPP_012205GER + +// Boost.Test +#include <boost/test/framework.hpp> +#include <boost/test/results_collector.hpp> +#include <boost/test/unit_test_suite_impl.hpp> +#include <boost/test/results_reporter.hpp> + +#include <boost/test/detail/unit_test_parameters.hpp> + +#if !defined(__BORLANDC__) && !BOOST_WORKAROUND( BOOST_MSVC, < 1300 ) && !BOOST_WORKAROUND( __SUNPRO_CC, < 0x5100 ) +#define BOOST_TEST_SUPPORT_RUN_BY_NAME +#include <boost/test/utils/iterator/token_iterator.hpp> +#endif + +// Boost +#include <boost/cstdlib.hpp> +#include <boost/bind.hpp> + +// STL +#include <stdexcept> +#include <iostream> + +#include <boost/test/detail/suppress_warnings.hpp> + +//____________________________________________________________________________// + +namespace boost { + +namespace unit_test { + +// ************************************************************************** // +// ************** test_case_filter ************** // +// ************************************************************************** // + +class test_case_filter : public test_tree_visitor { +public: + struct single_filter { + single_filter( const_string in ) + { + if( in == "*" ) + m_kind = SFK_ALL; + else if( first_char( in ) == '*' && last_char( in ) == '*' ) { + m_kind = SFK_SUBSTR; + m_value = in.substr( 1, in.size()-1 ); + } + else if( first_char( in ) == '*' ) { + m_kind = SFK_TRAILING; + m_value = in.substr( 1 ); + } + else if( last_char( in ) == '*' ) { + m_kind = SFK_LEADING; + m_value = in.substr( 0, in.size()-1 ); + } + else { + m_kind = SFK_MATCH; + m_value = in; + } + }; + + bool pass( test_unit const& tu ) const + { + const_string name( tu.p_name ); + + switch( m_kind ) { + default: + case SFK_ALL: + return true; + + case SFK_LEADING: + return name.substr( 0, m_value.size() ) == m_value; + + case SFK_TRAILING: + return name.size() >= m_value.size() && name.substr( name.size() - m_value.size() ) == m_value; + + case SFK_SUBSTR: + return name.find( m_value ) != const_string::npos; + + case SFK_MATCH: + return m_value == tu.p_name.get(); + } + } + enum kind { SFK_ALL, SFK_LEADING, SFK_TRAILING, SFK_SUBSTR, SFK_MATCH }; + + kind m_kind; + const_string m_value; + }; + // Constructor +#ifndef BOOST_TEST_SUPPORT_RUN_BY_NAME + explicit test_case_filter( const_string ) : m_depth( 0 ) {} +#else + explicit test_case_filter( const_string tc_to_run ) + : m_depth( 0 ) + { + string_token_iterator tit( tc_to_run, (dropped_delimeters = "/", kept_delimeters = dt_none) ); + + while( tit != string_token_iterator() ) { + m_filters.push_back( + std::vector<single_filter>( string_token_iterator( *tit, (dropped_delimeters = ",", kept_delimeters = dt_none) ), + string_token_iterator() ) ); + + ++tit; + } + } +#endif + + void filter_unit( test_unit const& tu ) + { + if( (++m_depth - 1) > m_filters.size() ) { + tu.p_enabled.value = true; + return; + } + + if( m_depth == 1 ) + return; + + std::vector<single_filter> const& filters = m_filters[m_depth-2]; + + tu.p_enabled.value = + std::find_if( filters.begin(), filters.end(), bind( &single_filter::pass, _1, boost::ref(tu) ) ) != filters.end(); + } + + // test tree visitor interface + virtual void visit( test_case const& tc ) + { + if( m_depth < m_filters.size() ) { + tc.p_enabled.value = false; + return; + } + + filter_unit( tc ); + + --m_depth; + } + + virtual bool test_suite_start( test_suite const& ts ) + { + filter_unit( ts ); + + if( !ts.p_enabled ) + --m_depth; + + return ts.p_enabled; + } + + virtual void test_suite_finish( test_suite const& ) { --m_depth; } + +private: + // Data members + std::vector<std::vector<single_filter> > m_filters; + unsigned m_depth; +}; + +// ************************************************************************** // +// ************** unit_test_main ************** // +// ************************************************************************** // + +int BOOST_TEST_DECL +unit_test_main( init_unit_test_func init_func, int argc, char* argv[] ) +{ + try { + framework::init( init_func, argc, argv ); + + if( !runtime_config::test_to_run().is_empty() ) { + test_case_filter filter( runtime_config::test_to_run() ); + + traverse_test_tree( framework::master_test_suite().p_id, filter ); + } + + framework::run(); + + results_reporter::make_report(); + + return runtime_config::no_result_code() + ? boost::exit_success + : results_collector.results( framework::master_test_suite().p_id ).result_code(); + } + catch( framework::nothing_to_test const& ) { + return boost::exit_success; + } + catch( framework::internal_error const& ex ) { + results_reporter::get_stream() << "Boost.Test framework internal error: " << ex.what() << std::endl; + + return boost::exit_exception_failure; + } + catch( framework::setup_error const& ex ) { + results_reporter::get_stream() << "Test setup error: " << ex.what() << std::endl; + + return boost::exit_exception_failure; + } + catch( ... ) { + results_reporter::get_stream() << "Boost.Test framework internal error: unknown reason" << std::endl; + + return boost::exit_exception_failure; + } +} + +} // namespace unit_test + +} // namespace boost + +#if !defined(BOOST_TEST_DYN_LINK) && !defined(BOOST_TEST_NO_MAIN) + +// ************************************************************************** // +// ************** main function for tests using lib ************** // +// ************************************************************************** // + +int BOOST_TEST_CALL_DECL +main( int argc, char* argv[] ) +{ + // prototype for user's unit test init function +#ifdef BOOST_TEST_ALTERNATIVE_INIT_API + extern bool init_unit_test(); + + boost::unit_test::init_unit_test_func init_func = &init_unit_test; +#else + extern ::boost::unit_test::test_suite* init_unit_test_suite( int argc, char* argv[] ); + + boost::unit_test::init_unit_test_func init_func = &init_unit_test_suite; +#endif + + return ::boost::unit_test::unit_test_main( init_func, argc, argv ); +} + +#endif // !BOOST_TEST_DYN_LINK && !BOOST_TEST_NO_MAIN + +//____________________________________________________________________________// + +#include <boost/test/detail/enable_warnings.hpp> + +#endif // BOOST_TEST_UNIT_TEST_MAIN_IPP_012205GER diff --git a/boost/test/impl/unit_test_monitor.ipp b/boost/test/impl/unit_test_monitor.ipp new file mode 100644 index 0000000000..b162ad922c --- /dev/null +++ b/boost/test/impl/unit_test_monitor.ipp @@ -0,0 +1,101 @@ +// (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: 49312 $ +// +// Description : implements specific subclass of Executon Monitor used by Unit +// Test Framework to monitor test cases run. +// *************************************************************************** + +#ifndef BOOST_TEST_UNIT_TEST_MONITOR_IPP_012205GER +#define BOOST_TEST_UNIT_TEST_MONITOR_IPP_012205GER + +// Boost.Test +#include <boost/test/unit_test_monitor.hpp> +#include <boost/test/unit_test_suite_impl.hpp> +#include <boost/test/test_tools.hpp> +#include <boost/test/framework.hpp> + +#include <boost/test/detail/unit_test_parameters.hpp> + +#include <boost/test/detail/suppress_warnings.hpp> + +//____________________________________________________________________________// + +namespace boost { + +namespace unit_test { + +namespace { + +template<typename F> +struct zero_return_wrapper_t { + explicit zero_return_wrapper_t( F const& f ) : m_f( f ) {} + + int operator()() { m_f(); return 0; } + + F const& m_f; +}; + +template<typename F> +zero_return_wrapper_t<F> +zero_return_wrapper( F const& f ) +{ + return zero_return_wrapper_t<F>( f ); +} + +} + +// ************************************************************************** // +// ************** unit_test_monitor ************** // +// ************************************************************************** // + +unit_test_monitor_t::error_level +unit_test_monitor_t::execute_and_translate( test_case const& tc ) +{ + try { + p_catch_system_errors.value = runtime_config::catch_sys_errors(); + p_timeout.value = tc.p_timeout.get(); + p_auto_start_dbg.value = runtime_config::auto_start_dbg(); + p_use_alt_stack.value = runtime_config::use_alt_stack(); + p_detect_fp_exceptions.value = runtime_config::detect_fp_exceptions(); + + execute( callback0<int>( zero_return_wrapper( tc.test_func() ) ) ); + } + catch( execution_exception const& ex ) { + framework::exception_caught( ex ); + framework::test_unit_aborted( framework::current_test_case() ); + + // translate execution_exception::error_code to error_level + switch( ex.code() ) { + case execution_exception::no_error: return test_ok; + case execution_exception::user_error: return unexpected_exception; + case execution_exception::cpp_exception_error: return unexpected_exception; + case execution_exception::system_error: return os_exception; + case execution_exception::timeout_error: return os_timeout; + case execution_exception::user_fatal_error: + case execution_exception::system_fatal_error: return fatal_error; + default: return unexpected_exception; + } + } + + return test_ok; +} + +//____________________________________________________________________________// + +} // namespace unit_test + +} // namespace boost + +//____________________________________________________________________________// + +#include <boost/test/detail/enable_warnings.hpp> + +#endif // BOOST_TEST_UNIT_TEST_MONITOR_IPP_012205GER diff --git a/boost/test/impl/unit_test_parameters.ipp b/boost/test/impl/unit_test_parameters.ipp new file mode 100644 index 0000000000..fd8e1adebc --- /dev/null +++ b/boost/test/impl/unit_test_parameters.ipp @@ -0,0 +1,527 @@ +// (C) Copyright Gennadiy Rozental 2001-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: 63640 $ +// +// Description : simple implementation for Unit Test Framework parameter +// handling routines. May be rewritten in future to use some kind of +// command-line arguments parsing facility and environment variable handling +// facility +// *************************************************************************** + +#ifndef BOOST_TEST_UNIT_TEST_PARAMETERS_IPP_012205GER +#define BOOST_TEST_UNIT_TEST_PARAMETERS_IPP_012205GER + +// Boost.Test +#include <boost/test/detail/unit_test_parameters.hpp> +#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/fixed_mapping.hpp> +#include <boost/test/debug.hpp> +#include <boost/test/framework.hpp> + +// Boost.Runtime.Param +#include <boost/test/utils/runtime/cla/dual_name_parameter.hpp> +#include <boost/test/utils/runtime/cla/parser.hpp> + +namespace rt = boost::runtime; +namespace cla = rt::cla; + + +#ifndef UNDER_CE +#include <boost/test/utils/runtime/env/variable.hpp> + +namespace env = rt::env; +#endif + + +// Boost +#include <boost/config.hpp> +#include <boost/test/detail/suppress_warnings.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/test/detail/enable_warnings.hpp> + +// STL +#include <map> +#include <cstdlib> +#include <iostream> +#include <fstream> + +#include <boost/test/detail/suppress_warnings.hpp> + +//____________________________________________________________________________// + +# ifdef BOOST_NO_STDC_NAMESPACE +namespace std { using ::getenv; using ::strncmp; using ::strcmp; } +# endif + +namespace boost { + +namespace unit_test { + +// ************************************************************************** // +// ************** input operations for unit_test's enums ************** // +// ************************************************************************** // + +std::istream& +operator>>( std::istream& in, unit_test::log_level& ll ) +{ + static fixed_mapping<const_string,unit_test::log_level,case_ins_less<char const> > log_level_name( + "all" , log_successful_tests, + "success" , log_successful_tests, + "test_suite" , log_test_units, + "unit_scope" , log_test_units, + "message" , log_messages, + "warning" , log_warnings, + "error" , log_all_errors, + "cpp_exception" , log_cpp_exception_errors, + "system_error" , log_system_errors, + "fatal_error" , log_fatal_errors, + "nothing" , log_nothing, + + invalid_log_level + ); + + std::string val; + in >> val; + + ll = log_level_name[val]; + BOOST_TEST_SETUP_ASSERT( ll != unit_test::invalid_log_level, "invalid log level " + val ); + + return in; +} + +//____________________________________________________________________________// + +std::istream& +operator>>( std::istream& in, unit_test::report_level& rl ) +{ + fixed_mapping<const_string,unit_test::report_level,case_ins_less<char const> > report_level_name ( + "confirm", CONFIRMATION_REPORT, + "short", SHORT_REPORT, + "detailed", DETAILED_REPORT, + "no", NO_REPORT, + + INV_REPORT_LEVEL + ); + + std::string val; + in >> val; + + rl = report_level_name[val]; + BOOST_TEST_SETUP_ASSERT( rl != INV_REPORT_LEVEL, "invalid report level " + val ); + + return in; +} + +//____________________________________________________________________________// + +std::istream& +operator>>( std::istream& in, unit_test::output_format& of ) +{ + fixed_mapping<const_string,unit_test::output_format,case_ins_less<char const> > output_format_name ( + "HRF", unit_test::CLF, + "CLF", unit_test::CLF, + "XML", unit_test::XML, + + unit_test::INV_OF + ); + + std::string val; + in >> val; + + of = output_format_name[val]; + BOOST_TEST_SETUP_ASSERT( of != unit_test::INV_OF, "invalid output format " + val ); + + return in; +} + +//____________________________________________________________________________// + +// ************************************************************************** // +// ************** runtime_config ************** // +// ************************************************************************** // + +namespace runtime_config { + +namespace { + +// framework parameters and corresponding command-line arguments +std::string AUTO_START_DBG = "auto_start_dbg"; +std::string BREAK_EXEC_PATH = "break_exec_path"; +std::string BUILD_INFO = "build_info"; +std::string CATCH_SYS_ERRORS = "catch_system_errors"; +std::string DETECT_FP_EXCEPT = "detect_fp_exceptions"; +std::string DETECT_MEM_LEAKS = "detect_memory_leaks"; +std::string LOG_FORMAT = "log_format"; +std::string LOG_LEVEL = "log_level"; +std::string LOG_SINK = "log_sink"; +std::string OUTPUT_FORMAT = "output_format"; +std::string RANDOM_SEED = "random"; +std::string REPORT_FORMAT = "report_format"; +std::string REPORT_LEVEL = "report_level"; +std::string REPORT_SINK = "report_sink"; +std::string RESULT_CODE = "result_code"; +std::string TESTS_TO_RUN = "run_test"; +std::string SAVE_TEST_PATTERN = "save_pattern"; +std::string SHOW_PROGRESS = "show_progress"; +std::string USE_ALT_STACK = "use_alt_stack"; + +fixed_mapping<const_string,const_string> parameter_2_env_var( + AUTO_START_DBG , "BOOST_TEST_AUTO_START_DBG", + BREAK_EXEC_PATH , "BOOST_TEST_BREAK_EXEC_PATH", + BUILD_INFO , "BOOST_TEST_BUILD_INFO", + CATCH_SYS_ERRORS , "BOOST_TEST_CATCH_SYSTEM_ERRORS", + DETECT_FP_EXCEPT , "BOOST_TEST_DETECT_FP_EXCEPTIONS", + DETECT_MEM_LEAKS , "BOOST_TEST_DETECT_MEMORY_LEAK", + LOG_FORMAT , "BOOST_TEST_LOG_FORMAT", + LOG_LEVEL , "BOOST_TEST_LOG_LEVEL", + LOG_SINK , "BOOST_TEST_LOG_SINK", + OUTPUT_FORMAT , "BOOST_TEST_OUTPUT_FORMAT", + RANDOM_SEED , "BOOST_TEST_RANDOM", + REPORT_FORMAT , "BOOST_TEST_REPORT_FORMAT", + REPORT_LEVEL , "BOOST_TEST_REPORT_LEVEL", + REPORT_SINK , "BOOST_TEST_REPORT_SINK", + RESULT_CODE , "BOOST_TEST_RESULT_CODE", + TESTS_TO_RUN , "BOOST_TESTS_TO_RUN", + SAVE_TEST_PATTERN , "BOOST_TEST_SAVE_PATTERN", + SHOW_PROGRESS , "BOOST_TEST_SHOW_PROGRESS", + USE_ALT_STACK , "BOOST_TEST_USE_ALT_STACK", + + "" +); + +//____________________________________________________________________________// + +// storage for the CLAs +cla::parser s_cla_parser; +std::string s_empty; + +output_format s_report_format; +output_format s_log_format; + +//____________________________________________________________________________// + +template<typename T> +T +retrieve_parameter( const_string parameter_name, cla::parser const& s_cla_parser, T const& default_value = T(), T const& optional_value = T() ) +{ + rt::const_argument_ptr arg = s_cla_parser[parameter_name]; + if( arg ) { + if( rtti::type_id<T>() == rtti::type_id<bool>() || + !static_cast<cla::parameter const&>( arg->p_formal_parameter.get() ).p_optional_value ) + return s_cla_parser.get<T>( parameter_name ); + + optional<T> val = s_cla_parser.get<optional<T> >( parameter_name ); + if( val ) + return *val; + else + return optional_value; + } + + boost::optional<T> v; + +#ifndef UNDER_CE + env::get( parameter_2_env_var[parameter_name], v ); +#endif + + if( v ) + return *v; + else + return default_value; +} + +//____________________________________________________________________________// + +} // local namespace + +void +init( int& argc, char** argv ) +{ + using namespace cla; + + try { + s_cla_parser - cla::ignore_mismatch + << cla::dual_name_parameter<bool>( AUTO_START_DBG + "|d" ) + - (cla::prefix = "--|-",cla::separator = "=| ",cla::guess_name,cla::optional, + cla::description = "Automatically starts debugger if system level error (signal) occurs") + << cla::named_parameter<std::string>( BREAK_EXEC_PATH ) + - (cla::prefix = "--",cla::separator = "=",cla::guess_name,cla::optional, + cla::description = "For the exception safety testing allows to break at specific execution path") + << cla::dual_name_parameter<bool>( BUILD_INFO + "|i" ) + - (cla::prefix = "--|-",cla::separator = "=| ",cla::guess_name,cla::optional, + cla::description = "Shows library build information" ) + << cla::dual_name_parameter<bool>( CATCH_SYS_ERRORS + "|s" ) + - (cla::prefix = "--|-",cla::separator = "=| ",cla::guess_name,cla::optional, + cla::description = "Allows to switch between catching and ignoring system errors (signals)") + << cla::named_parameter<bool>( DETECT_FP_EXCEPT ) + - (cla::prefix = "--",cla::separator = "=",cla::guess_name,cla::optional, + cla::description = "Allows to switch between catching and ignoring floating point exceptions") + << cla::named_parameter<long>( DETECT_MEM_LEAKS ) + - (cla::prefix = "--",cla::separator = "=",cla::guess_name,cla::optional, + cla::description = "Allows to switch between catching and ignoring memory leaks") + << cla::dual_name_parameter<unit_test::output_format>( LOG_FORMAT + "|f" ) + - (cla::prefix = "--|-",cla::separator = "=| ",cla::guess_name,cla::optional, + cla::description = "Specifies log format") + << cla::dual_name_parameter<unit_test::log_level>( LOG_LEVEL + "|l" ) + - (cla::prefix = "--|-",cla::separator = "=| ",cla::guess_name,cla::optional, + cla::description = "Specifies log level") + << cla::dual_name_parameter<std::string>( LOG_SINK + "|k" ) + - (cla::prefix = "--|-",cla::separator = "=| ",cla::guess_name,cla::optional, + cla::description = "Specifies log sink:stdout(default),stderr or file name") + << cla::dual_name_parameter<unit_test::output_format>( OUTPUT_FORMAT + "|o" ) + - (cla::prefix = "--|-",cla::separator = "=| ",cla::guess_name,cla::optional, + cla::description = "Specifies output format (both log and report)") + << cla::dual_name_parameter<int>( RANDOM_SEED + "|a" ) + - (cla::prefix = "--|-",cla::separator = "=| ",cla::guess_name,cla::optional,cla::optional_value, + cla::description = "Allows to switch between sequential and random order of test units execution.\n" + "Optionally allows to specify concrete seed for random number generator") + << cla::dual_name_parameter<unit_test::output_format>( REPORT_FORMAT + "|m" ) + - (cla::prefix = "--|-",cla::separator = "=| ",cla::guess_name,cla::optional, + cla::description = "Specifies report format") + << cla::dual_name_parameter<unit_test::report_level>(REPORT_LEVEL + "|r") + - (cla::prefix = "--|-",cla::separator = "=| ",cla::guess_name,cla::optional, + cla::description = "Specifies report level") + << cla::dual_name_parameter<std::string>( REPORT_SINK + "|e" ) + - (cla::prefix = "--|-",cla::separator = "=| ",cla::guess_name,cla::optional, + cla::description = "Specifies report sink:stderr(default),stdout or file name") + << cla::dual_name_parameter<bool>( RESULT_CODE + "|c" ) + - (cla::prefix = "--|-",cla::separator = "=| ",cla::guess_name,cla::optional, + cla::description = "Allows to disable test modules's result code generation") + << cla::dual_name_parameter<std::string>( TESTS_TO_RUN + "|t" ) + - (cla::prefix = "--|-",cla::separator = "=| ",cla::guess_name,cla::optional, + cla::description = "Allows to filter which test units to run") + << cla::named_parameter<bool>( SAVE_TEST_PATTERN ) + - (cla::prefix = "--",cla::separator = "=",cla::guess_name,cla::optional, + cla::description = "Allows to switch between saving and matching against test pattern file") + << cla::dual_name_parameter<bool>( SHOW_PROGRESS + "|p" ) + - (cla::prefix = "--|-",cla::separator = "=| ",cla::guess_name,cla::optional, + cla::description = "Turns on progress display") + << cla::named_parameter<bool>( USE_ALT_STACK ) + - (cla::prefix = "--",cla::separator = "=",cla::guess_name,cla::optional, + cla::description = "Turns on/off usage of an alternative stack for signal handling") + + << cla::dual_name_parameter<bool>( "help|?" ) + - (cla::prefix = "--|-",cla::separator = "=",cla::guess_name,cla::optional, + cla::description = "this help message") + ; + + s_cla_parser.parse( argc, argv ); + + if( s_cla_parser["help"] ) { + s_cla_parser.help( std::cout ); + throw framework::nothing_to_test(); + } + + s_report_format = retrieve_parameter( REPORT_FORMAT, s_cla_parser, unit_test::CLF ); + s_log_format = retrieve_parameter( LOG_FORMAT, s_cla_parser, unit_test::CLF ); + + unit_test::output_format of = retrieve_parameter( OUTPUT_FORMAT, s_cla_parser, unit_test::INV_OF ); + + if( of != unit_test::INV_OF ) + s_report_format = s_log_format = of; + } + catch( rt::logic_error const& ex ) { + std::ostringstream err; + + err << "Fail to process runtime parameters: " << ex.msg() << std::endl; + s_cla_parser.usage( err ); + + throw framework::setup_error( err.str() ); + } +} + +//____________________________________________________________________________// + +unit_test::log_level +log_level() +{ + return retrieve_parameter( LOG_LEVEL, s_cla_parser, unit_test::log_all_errors ); +} + +//____________________________________________________________________________// + +bool +no_result_code() +{ + return !retrieve_parameter( RESULT_CODE, s_cla_parser, true ); +} + +//____________________________________________________________________________// + +unit_test::report_level +report_level() +{ + return retrieve_parameter( REPORT_LEVEL, s_cla_parser, unit_test::CONFIRMATION_REPORT ); +} + +//____________________________________________________________________________// + +const_string +test_to_run() +{ + static std::string s_test_to_run = retrieve_parameter( TESTS_TO_RUN, s_cla_parser, s_empty ); + + return s_test_to_run; +} + +//____________________________________________________________________________// + +const_string +break_exec_path() +{ + static std::string s_break_exec_path = retrieve_parameter( BREAK_EXEC_PATH, s_cla_parser, s_empty ); + + return s_break_exec_path; +} + +//____________________________________________________________________________// + +bool +save_pattern() +{ + return retrieve_parameter( SAVE_TEST_PATTERN, s_cla_parser, false ); +} + +//____________________________________________________________________________// + +bool +show_progress() +{ + return retrieve_parameter( SHOW_PROGRESS, s_cla_parser, false ); +} + +//____________________________________________________________________________// + +bool +show_build_info() +{ + return retrieve_parameter( BUILD_INFO, s_cla_parser, false ); +} + +//____________________________________________________________________________// + +bool +catch_sys_errors() +{ + return retrieve_parameter( CATCH_SYS_ERRORS, s_cla_parser, +#ifdef BOOST_TEST_DEFAULTS_TO_CORE_DUMP + false +#else + true +#endif + ); +} + +//____________________________________________________________________________// + +bool +auto_start_dbg() +{ + // !! set debugger as an option + return retrieve_parameter( AUTO_START_DBG, s_cla_parser, false ); +; +} + +//____________________________________________________________________________// + +bool +use_alt_stack() +{ + return retrieve_parameter( USE_ALT_STACK, s_cla_parser, true ); +} + +//____________________________________________________________________________// + +bool +detect_fp_exceptions() +{ + return retrieve_parameter( DETECT_FP_EXCEPT, s_cla_parser, false ); +} + +//____________________________________________________________________________// + +output_format +report_format() +{ + return s_report_format; +} + +//____________________________________________________________________________// + +output_format +log_format() +{ + return s_log_format; +} + +//____________________________________________________________________________// + +std::ostream* +report_sink() +{ + std::string sink_name = retrieve_parameter( REPORT_SINK, s_cla_parser, s_empty ); + + if( sink_name.empty() || sink_name == "stderr" ) + return &std::cerr; + + if( sink_name == "stdout" ) + return &std::cout; + + static std::ofstream log_file( sink_name.c_str() ); + return &log_file; +} + +//____________________________________________________________________________// + +std::ostream* +log_sink() +{ + std::string sink_name = retrieve_parameter( LOG_SINK, s_cla_parser, s_empty ); + + if( sink_name.empty() || sink_name == "stdout" ) + return &std::cout; + + if( sink_name == "stderr" ) + return &std::cerr; + + static std::ofstream report_file( sink_name.c_str() ); + return &report_file; +} + +//____________________________________________________________________________// + +long +detect_memory_leaks() +{ + return retrieve_parameter( DETECT_MEM_LEAKS, s_cla_parser, static_cast<long>(1) ); +} + +//____________________________________________________________________________// + +int +random_seed() +{ + return retrieve_parameter( RANDOM_SEED, s_cla_parser, 0, 1 ); +} + +//____________________________________________________________________________// + +} // namespace runtime_config + +} // namespace unit_test + +} // namespace boost + +//____________________________________________________________________________// + +#include <boost/test/detail/enable_warnings.hpp> + +#endif // BOOST_TEST_UNIT_TEST_PARAMETERS_IPP_012205GER diff --git a/boost/test/impl/unit_test_suite.ipp b/boost/test/impl/unit_test_suite.ipp new file mode 100644 index 0000000000..859a0b5670 --- /dev/null +++ b/boost/test/impl/unit_test_suite.ipp @@ -0,0 +1,346 @@ +// (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 : privides core implementation for Unit Test Framework. +// Extensions can be provided in separate files +// *************************************************************************** + +#ifndef BOOST_TEST_UNIT_TEST_SUITE_IPP_012205GER +#define BOOST_TEST_UNIT_TEST_SUITE_IPP_012205GER + +// Boost.Test +#include <boost/detail/workaround.hpp> +#include <boost/test/unit_test_suite_impl.hpp> +#include <boost/test/framework.hpp> +#include <boost/test/utils/foreach.hpp> +#include <boost/test/results_collector.hpp> +#include <boost/test/detail/unit_test_parameters.hpp> + +// Boost +#include <boost/timer.hpp> + +// STL +#include <algorithm> +#include <vector> + +#include <boost/test/detail/suppress_warnings.hpp> + +#if BOOST_WORKAROUND(__BORLANDC__, < 0x600) && \ + BOOST_WORKAROUND(_STLPORT_VERSION, <= 0x450) \ + /**/ + using std::rand; // rand is in std and random_shuffle is in _STL +#endif + +//____________________________________________________________________________// + +namespace boost { + +namespace unit_test { + +// ************************************************************************** // +// ************** test_unit ************** // +// ************************************************************************** // + +test_unit::test_unit( const_string name, test_unit_type t ) +: p_type( t ) +, p_type_name( t == tut_case ? "case" : "suite" ) +, p_id( INV_TEST_UNIT_ID ) +, p_name( std::string( name.begin(), name.size() ) ) +, p_enabled( true ) +{ +} + +//____________________________________________________________________________// + +test_unit::~test_unit() +{ + framework::deregister_test_unit( this ); +} + +//____________________________________________________________________________// + +void +test_unit::depends_on( test_unit* tu ) +{ + m_dependencies.push_back( tu->p_id ); +} + +//____________________________________________________________________________// + +bool +test_unit::check_dependencies() const +{ + BOOST_TEST_FOREACH( test_unit_id, tu_id, m_dependencies ) { + if( !unit_test::results_collector.results( tu_id ).passed() ) + return false; + } + + return true; +} + +//____________________________________________________________________________// + +void +test_unit::increase_exp_fail( unsigned num ) +{ + p_expected_failures.value += num; + + if( p_parent_id != 0 ) + framework::get<test_suite>( p_parent_id ).increase_exp_fail( num ); +} + +//____________________________________________________________________________// + +// ************************************************************************** // +// ************** test_case ************** // +// ************************************************************************** // + +test_case::test_case( const_string name, callback0<> const& test_func ) +: test_unit( name, static_cast<test_unit_type>(type) ) +, m_test_func( test_func ) +{ + // !! weirdest MSVC BUG; try to remove this statement; looks like it eats first token of next statement +#if BOOST_WORKAROUND(BOOST_MSVC,<1300) + 0; +#endif + framework::register_test_unit( this ); +} + +//____________________________________________________________________________// + +// ************************************************************************** // +// ************** test_suite ************** // +// ************************************************************************** // + +//____________________________________________________________________________// + +test_suite::test_suite( const_string name ) +: test_unit( name, static_cast<test_unit_type>(type) ) +{ + framework::register_test_unit( this ); +} + +//____________________________________________________________________________// + +void +test_suite::add( test_unit* tu, counter_t expected_failures, unsigned timeout ) +{ + if( timeout != 0 ) + tu->p_timeout.value = timeout; + + m_members.push_back( tu->p_id ); + tu->p_parent_id.value = p_id; + + if( tu->p_expected_failures ) + increase_exp_fail( tu->p_expected_failures ); + + if( expected_failures ) + tu->increase_exp_fail( expected_failures ); +} + +//____________________________________________________________________________// + +void +test_suite::add( test_unit_generator const& gen, unsigned timeout ) +{ + test_unit* tu; + while((tu = gen.next(), tu)) + add( tu, 0, timeout ); +} + +//____________________________________________________________________________// + +void +test_suite::remove( test_unit_id id ) +{ + std::vector<test_unit_id>::iterator it = std::find( m_members.begin(), m_members.end(), id ); + + if( it != m_members.end() ) + m_members.erase( it ); +} + +//____________________________________________________________________________// + +test_unit_id +test_suite::get( const_string tu_name ) const +{ + BOOST_TEST_FOREACH( test_unit_id, id, m_members ) { + if( tu_name == framework::get( id, ut_detail::test_id_2_unit_type( id ) ).p_name.get() ) + return id; + } + + return INV_TEST_UNIT_ID; +} + +//____________________________________________________________________________// + +// ************************************************************************** // +// ************** traverse_test_tree ************** // +// ************************************************************************** // + +void +traverse_test_tree( test_case const& tc, test_tree_visitor& V ) +{ + if( tc.p_enabled ) + V.visit( tc ); +} + +//____________________________________________________________________________// + +void +traverse_test_tree( test_suite const& suite, test_tree_visitor& V ) +{ + if( !suite.p_enabled || !V.test_suite_start( suite ) ) + return; + + try { + if( runtime_config::random_seed() == 0 ) { + BOOST_TEST_FOREACH( test_unit_id, id, suite.m_members ) + traverse_test_tree( id, V ); + } + else { + std::vector<test_unit_id> members( suite.m_members ); + std::random_shuffle( members.begin(), members.end() ); + BOOST_TEST_FOREACH( test_unit_id, id, members ) + traverse_test_tree( id, V ); + } + + } catch( test_being_aborted const& ) { + V.test_suite_finish( suite ); + framework::test_unit_aborted( suite ); + + throw; + } + + V.test_suite_finish( suite ); +} + +//____________________________________________________________________________// + +void +traverse_test_tree( test_unit_id id, test_tree_visitor& V ) +{ + if( ut_detail::test_id_2_unit_type( id ) == tut_case ) + traverse_test_tree( framework::get<test_case>( id ), V ); + else + traverse_test_tree( framework::get<test_suite>( id ), V ); +} + +//____________________________________________________________________________// + +// ************************************************************************** // +// ************** test_case_counter ************** // +// ************************************************************************** // + +void +test_case_counter::visit( test_case const& tc ) +{ + if( tc.p_enabled ) + ++p_count.value; +} + +//____________________________________________________________________________// + +// ************************************************************************** // +// ************** object generators ************** // +// ************************************************************************** // + +namespace ut_detail { + +std::string +normalize_test_case_name( const_string name ) +{ + return ( name[0] == '&' + ? std::string( name.begin()+1, name.size()-1 ) + : std::string( name.begin(), name.size() ) ); +} + +//____________________________________________________________________________// + +// ************************************************************************** // +// ************** auto_test_unit_registrar ************** // +// ************************************************************************** // + +auto_test_unit_registrar::auto_test_unit_registrar( test_case* tc, counter_t exp_fail ) +{ + curr_ts_store().back()->add( tc, exp_fail ); +} + +//____________________________________________________________________________// + +auto_test_unit_registrar::auto_test_unit_registrar( const_string ts_name ) +{ + test_unit_id id = curr_ts_store().back()->get( ts_name ); + + test_suite* ts; + + if( id != INV_TEST_UNIT_ID ) { + ts = &framework::get<test_suite>( id ); // !! test for invalid tu type + BOOST_ASSERT( ts->p_parent_id == curr_ts_store().back()->p_id ); + } + else { + ts = new test_suite( ts_name ); + curr_ts_store().back()->add( ts ); + } + + curr_ts_store().push_back( ts ); +} + +//____________________________________________________________________________// + +auto_test_unit_registrar::auto_test_unit_registrar( test_unit_generator const& tc_gen ) +{ + curr_ts_store().back()->add( tc_gen ); +} + +//____________________________________________________________________________// + +auto_test_unit_registrar::auto_test_unit_registrar( int ) +{ + if( curr_ts_store().size() == 0 ) + return; // report error? + + curr_ts_store().pop_back(); +} + +//____________________________________________________________________________// + +std::list<test_suite*>& +auto_test_unit_registrar::curr_ts_store() +{ + static std::list<test_suite*> inst( 1, &framework::master_test_suite() ); + return inst; +} + +//____________________________________________________________________________// + +} // namespace ut_detail + +// ************************************************************************** // +// ************** global_fixture ************** // +// ************************************************************************** // + +global_fixture::global_fixture() +{ + framework::register_observer( *this ); +} + +//____________________________________________________________________________// + +} // namespace unit_test + +} // namespace boost + +//____________________________________________________________________________// + +#include <boost/test/detail/enable_warnings.hpp> + +#endif // BOOST_TEST_UNIT_TEST_SUITE_IPP_012205GER diff --git a/boost/test/impl/xml_log_formatter.ipp b/boost/test/impl/xml_log_formatter.ipp new file mode 100644 index 0000000000..8322e0b7e2 --- /dev/null +++ b/boost/test/impl/xml_log_formatter.ipp @@ -0,0 +1,180 @@ +// (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: 57992 $ +// +// Description : implements XML Log formatter +// *************************************************************************** + +#ifndef BOOST_TEST_XML_LOG_FORMATTER_IPP_020105GER +#define BOOST_TEST_XML_LOG_FORMATTER_IPP_020105GER + +// Boost.Test +#include <boost/test/output/xml_log_formatter.hpp> +#include <boost/test/unit_test_suite_impl.hpp> +#include <boost/test/framework.hpp> +#include <boost/test/utils/basic_cstring/io.hpp> + +#include <boost/test/utils/xml_printer.hpp> + +// Boost +#include <boost/version.hpp> + +// STL +#include <iostream> + +#include <boost/test/detail/suppress_warnings.hpp> + +//____________________________________________________________________________// + +namespace boost { + +namespace unit_test { + +namespace output { + +static const_string tu_type_name( test_unit const& tu ) +{ + return tu.p_type == tut_case ? "TestCase" : "TestSuite"; +} + +// ************************************************************************** // +// ************** xml_log_formatter ************** // +// ************************************************************************** // + +void +xml_log_formatter::log_start( std::ostream& ostr, counter_t ) +{ + ostr << "<TestLog>"; +} + +//____________________________________________________________________________// + +void +xml_log_formatter::log_finish( std::ostream& ostr ) +{ + ostr << "</TestLog>"; +} + +//____________________________________________________________________________// + +void +xml_log_formatter::log_build_info( std::ostream& ostr ) +{ + ostr << "<BuildInfo" + << " platform" << attr_value() << BOOST_PLATFORM + << " compiler" << attr_value() << BOOST_COMPILER + << " stl" << attr_value() << BOOST_STDLIB + << " boost=\"" << BOOST_VERSION/100000 << "." + << BOOST_VERSION/100 % 1000 << "." + << BOOST_VERSION % 100 << '\"' + << "/>"; +} + +//____________________________________________________________________________// + +void +xml_log_formatter::test_unit_start( std::ostream& ostr, test_unit const& tu ) +{ + ostr << "<" << tu_type_name( tu ) << " name" << attr_value() << tu.p_name.get() << ">"; +} + +//____________________________________________________________________________// + +void +xml_log_formatter::test_unit_finish( std::ostream& ostr, test_unit const& tu, unsigned long elapsed ) +{ + if( tu.p_type == tut_case ) + ostr << "<TestingTime>" << elapsed << "</TestingTime>"; + + ostr << "</" << tu_type_name( tu ) << ">"; +} + +//____________________________________________________________________________// + +void +xml_log_formatter::test_unit_skipped( std::ostream& ostr, test_unit const& tu ) +{ + ostr << "<" << tu_type_name( tu ) + << " name" << attr_value() << tu.p_name.get() + << " skipped" << attr_value() << "yes" + << "/>"; +} + +//____________________________________________________________________________// + +void +xml_log_formatter::log_exception( std::ostream& ostr, log_checkpoint_data const& checkpoint_data, execution_exception const& ex ) +{ + execution_exception::location const& loc = ex.where(); + + ostr << "<Exception file" << attr_value() << loc.m_file_name + << " line" << attr_value() << loc.m_line_num; + + if( !loc.m_function.is_empty() ) + ostr << " function" << attr_value() << loc.m_function; + + ostr << ">" << cdata() << ex.what(); + + if( !checkpoint_data.m_file_name.is_empty() ) { + ostr << "<LastCheckpoint file" << attr_value() << checkpoint_data.m_file_name + << " line" << attr_value() << checkpoint_data.m_line_num + << ">" + << cdata() << checkpoint_data.m_message + << "</LastCheckpoint>"; + } + + ostr << "</Exception>"; +} + +//____________________________________________________________________________// + +void +xml_log_formatter::log_entry_start( std::ostream& ostr, log_entry_data const& entry_data, log_entry_types let ) +{ + static literal_string xml_tags[] = { "Info", "Message", "Warning", "Error", "FatalError" }; + + m_curr_tag = xml_tags[let]; + ostr << '<' << m_curr_tag + << BOOST_TEST_L( " file" ) << attr_value() << entry_data.m_file_name + << BOOST_TEST_L( " line" ) << attr_value() << entry_data.m_line_num + << BOOST_TEST_L( "><![CDATA[" ); +} + +//____________________________________________________________________________// + +void +xml_log_formatter::log_entry_value( std::ostream& ostr, const_string value ) +{ + ostr << value; +} + +//____________________________________________________________________________// + +void +xml_log_formatter::log_entry_finish( std::ostream& ostr ) +{ + ostr << BOOST_TEST_L( "]]></" ) << m_curr_tag << BOOST_TEST_L( ">" ); + + m_curr_tag.clear(); +} + +//____________________________________________________________________________// + +} // namespace output + +} // namespace unit_test + +} // namespace boost + +//____________________________________________________________________________// + +#include <boost/test/detail/enable_warnings.hpp> + +#endif // BOOST_TEST_XML_LOG_FORMATTER_IPP_020105GER diff --git a/boost/test/impl/xml_report_formatter.ipp b/boost/test/impl/xml_report_formatter.ipp new file mode 100644 index 0000000000..0a512857bc --- /dev/null +++ b/boost/test/impl/xml_report_formatter.ipp @@ -0,0 +1,115 @@ +// (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: 49312 $ +// +// Description : XML report formatter +// *************************************************************************** + +#ifndef BOOST_TEST_XML_REPORT_FORMATTER_IPP_020105GER +#define BOOST_TEST_XML_REPORT_FORMATTER_IPP_020105GER + +// Boost.Test +#include <boost/test/results_collector.hpp> +#include <boost/test/unit_test_suite_impl.hpp> +#include <boost/test/output/xml_report_formatter.hpp> + +#include <boost/test/utils/xml_printer.hpp> +#include <boost/test/utils/basic_cstring/io.hpp> + +#include <boost/test/detail/suppress_warnings.hpp> + +//____________________________________________________________________________// + +namespace boost { + +namespace unit_test { + +namespace output { + +void +xml_report_formatter::results_report_start( std::ostream& ostr ) +{ + ostr << "<TestResult>"; +} + +//____________________________________________________________________________// + +void +xml_report_formatter::results_report_finish( std::ostream& ostr ) +{ + ostr << "</TestResult>"; +} + + +//____________________________________________________________________________// + +void +xml_report_formatter::test_unit_report_start( test_unit const& tu, std::ostream& ostr ) +{ + test_results const& tr = results_collector.results( tu.p_id ); + + const_string descr; + + if( tr.passed() ) + descr = "passed"; + else if( tr.p_skipped ) + descr = "skipped"; + else if( tr.p_aborted ) + descr = "aborted"; + else + descr = "failed"; + + ostr << '<' << ( tu.p_type == tut_case ? "TestCase" : "TestSuite" ) + << " name" << attr_value() << tu.p_name.get() + << " result" << attr_value() << descr + << " assertions_passed" << attr_value() << tr.p_assertions_passed + << " assertions_failed" << attr_value() << tr.p_assertions_failed + << " expected_failures" << attr_value() << tr.p_expected_failures; + + if( tu.p_type == tut_suite ) + ostr << " test_cases_passed" << attr_value() << tr.p_test_cases_passed + << " test_cases_failed" << attr_value() << tr.p_test_cases_failed + << " test_cases_skipped" << attr_value() << tr.p_test_cases_skipped + << " test_cases_aborted" << attr_value() << tr.p_test_cases_aborted; + + + ostr << '>'; +} + +//____________________________________________________________________________// + +void +xml_report_formatter::test_unit_report_finish( test_unit const& tu, std::ostream& ostr ) +{ + ostr << "</" << ( tu.p_type == tut_case ? "TestCase" : "TestSuite" ) << '>'; +} + +//____________________________________________________________________________// + +void +xml_report_formatter::do_confirmation_report( test_unit const& tu, std::ostream& ostr ) +{ + test_unit_report_start( tu, ostr ); + test_unit_report_finish( tu, ostr ); +} + +//____________________________________________________________________________// + +} // namespace output + +} // namespace unit_test + +} // namespace boost + +//____________________________________________________________________________// + +#include <boost/test/detail/enable_warnings.hpp> + +#endif // BOOST_TEST_XML_REPORT_FORMATTER_IPP_020105GER |