# # # add_file "current_exception.hh" # content [b54a2fcf1a92314df33ccd16da0b147686451102] # # patch "charset.cc" # from [33ae3508cc1b85b5f89d211897a1d069d8de129c] # to [744a0592671f2e19acf1fbfeab2e72897aecbac9] # # patch "ui.cc" # from [6a72078cb0090454028f61c65ff5698a1a877037] # to [ed513609cf04a1dc4cb2df7e93090b8b250eef95] # # patch "unit_tests.cc" # from [17924bb44d8d9f98d123c3a609b4f913657b4ebb] # to [f1f7a69290901922aeef3686283ec6cea1687302] # # patch "unit_tests.hh" # from [0c622d45cff9c8fbf892546113c4e5aefe0b2a6c] # to [ba399f5ac277fddb0766e040a854889bbd4e7e5f] # ============================================================ --- current_exception.hh b54a2fcf1a92314df33ccd16da0b147686451102 +++ current_exception.hh b54a2fcf1a92314df33ccd16da0b147686451102 @@ -0,0 +1,45 @@ +// Copyright (C) 2007 Zack Weinberg +// +// This program is made available under the GNU GPL version 2.0 or +// greater. See the accompanying file COPYING for details. +// +// This program is distributed WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. + +#ifndef CURRENT_EXCEPTION_HH +#define CURRENT_EXCEPTION_HH + +#include "config.h" +#include + +// Add #ifdeffage here as appropriate for other compiler-specific ways to +// get this information. Windows note: as best I can determine from poking +// around on MSDN, MSVC type_info.name() is already demangled, and there is +// no documented equivalent of __cxa_current_exception_type(). +#ifdef HAVE_CXXABI_H + #include + #ifdef HAVE___CXA_DEMANGLE + inline char const * demangle_typename(char const * name) + { + int status = -1; + char * dem = abi::__cxa_demangle(name, 0, 0, &status); + if (status == 0) + return dem; + else + return 0; + } + #else + #define demangle_typename(x) 0 + #endif + #ifdef HAVE___CXA_CURRENT_EXCEPTION_TYPE + #define get_current_exception_type() abi::__cxa_current_exception_type() + #else + #define get_current_exception_type() 0 + #endif +#else + #define demangle_typename(x) 0 + #define get_current_exception_type() 0 +#endif + +#endif ============================================================ --- charset.cc 33ae3508cc1b85b5f89d211897a1d069d8de129c +++ charset.cc 744a0592671f2e19acf1fbfeab2e72897aecbac9 @@ -620,7 +620,7 @@ UNIT_TEST(charset, idna_encoding) for (size_t i = 0; i < sizeof(idna_vec) / sizeof(struct idna); ++i) { - UNIT_TEST_CHECKPOINT("IDNA language: " + string(idna_vec[i].name)); + UNIT_TEST_CHECKPOINT(("IDNA language: " + string(idna_vec[i].name)).c_str()); size_t p, q; char *uc = stringprep_ucs4_to_utf8(idna_vec[i].in, ============================================================ --- ui.cc 6a72078cb0090454028f61c65ff5698a1a877037 +++ ui.cc ed513609cf04a1dc4cb2df7e93090b8b250eef95 @@ -26,37 +26,9 @@ #include #include -#include #include -// Add #ifdeffage here as appropriate for other compiler-specific ways to -// get this information. Windows note: as best I can determine from poking -// around on MSDN, MSVC type_info.name() is already demangled, and there is -// no documented equivalent of __cxa_current_exception_type(). -#ifdef HAVE_CXXABI_H - #include - #ifdef HAVE___CXA_DEMANGLE - inline char const * demangle_typename(char const * name) - { - int status = -1; - char * dem = abi::__cxa_demangle(name, 0, 0, &status); - if (status == 0) - return dem; - else - return 0; - } - #else - #define demangle_typename(x) 0 - #endif - #ifdef HAVE___CXA_CURRENT_EXCEPTION_TYPE - #define get_current_exception_type() abi::__cxa_current_exception_type() - #else - #define get_current_exception_type() 0 - #endif -#else - #define demangle_typename(x) 0 - #define get_current_exception_type() 0 -#endif +#include "current_exception.hh" using std::clog; using std::cout; ============================================================ --- unit_tests.cc 17924bb44d8d9f98d123c3a609b4f913657b4ebb +++ unit_tests.cc f1f7a69290901922aeef3686283ec6cea1687302 @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,7 @@ #include "unit_tests.hh" #include "sanity.hh" #include "ui.hh" +#include "current_exception.hh" using std::map; using std::pair; @@ -33,14 +35,13 @@ using std::exit; using std::cerr; using std::clog; using std::exit; -using std::atexit; typedef unit_test::unit_test_case test_t; typedef map test_list_t; typedef map group_list_t; // This is used by other global constructors, so initialize on demand. -group_list_t & unit_tests() +static group_list_t & unit_tests() { static group_list_t tests; return tests; @@ -58,36 +59,68 @@ unit_test::unit_test_case::unit_test_cas {} // Testsuite state. -bool any_test_failed = false; -int number_of_failed_tests = 0; -int number_of_succeeded_tests = 0; +static bool any_test_failed = false; +static int number_of_failed_tests = 0; +static int number_of_succeeded_tests = 0; +static bool log_to_stderr = false; // Test state. -bool this_test_failed; -string last_checkpoint; +static bool this_test_failed; -struct require_failed {}; +namespace { struct require_failed {}; } -void note_checkpoint(char const * file, int line, - char const * kind, string const & msg) +static void log_state(char const * file, int line, + char const * kind, char const * msg) { - last_checkpoint = (FL("%s:%s> %s: %s") - % file % line % kind % msg).str(); + L(FL("%s:%s: %s: %s") % file % line % kind % msg); } -void log_checkpoint() +// Report what we can about a fatal exception (caught in the outermost catch +// handlers) which is from the std::exception hierarchy. In this case we +// can access the exception object. +static void log_exception(std::exception const & ex) { - if (last_checkpoint == "") - return; - cerr<<"Last checkpoint: "< interfaces, +// we can at least get the type_info object. +static void +log_exception() { - cerr< %s: %s\n") % file % line % kind % msg; - log_checkpoint(); + std::type_info *ex_type = get_current_exception_type(); + if (ex_type) + { + char const * ex_name = ex_type->name(); + char const * ex_dem = demangle_typename(ex_name); + if (ex_dem == 0) + ex_dem = ex_name; + L(FL("UNCAUGHT EXCEPTION: %s") % ex_dem); + } + else + L(FL("UNCAUGHT EXCEPTION: unknown type")); } void unit_test::do_check(bool checkval, char const * file, @@ -96,10 +129,10 @@ void unit_test::do_check(bool checkval, if (!checkval) { this_test_failed = true; - log_fail(file, line, "CHECK FAILED", message); + log_state(file, line, "CHECK FAILED", message); } else - note_checkpoint(file, line, "CHECK OK", message); + log_state(file, line, "CHECK OK", message); } void unit_test::do_require(bool checkval, char const * file, @@ -108,135 +141,73 @@ void unit_test::do_require(bool checkval if (!checkval) { this_test_failed = true; - log_fail(file, line, "REQUIRE FAILED", message); + log_state(file, line, "REQUIRE FAILED", message); throw require_failed(); } else - note_checkpoint(file, line, "REQUIRE OK", message); + log_state(file, line, "REQUIRE OK", message); } void unit_test::do_checkpoint(char const * file, int line, - string const & message) + char const * message) { - note_checkpoint(file, line, "CHECKPOINT", message); + log_state(file, line, "CHECKPOINT", message); } -void run_test(test_t test) +static void run_test(test_t test) { this_test_failed = false; - last_checkpoint = ""; - cerr<<"----------------------------------------\n"; - cerr<<(FL("Beginning test %s:%s\n") % test.group % test.name); + L(FL("----------------------------------------\n" + "Beginning test %s:%s") % test.group % test.name); + if (!log_to_stderr) + { + string groupname = string(test.group) + ':' + test.name; + cerr << " " << std::left << std::setw(46) << groupname; + if (groupname.size() >= 46) + cerr << " "; + // lack of carriage return is intentional + } + try { test.func(); } catch(require_failed &) { + // no action required + } + catch(std::exception & e) + { + log_exception(e); this_test_failed = true; } catch(...) { - cerr< UNCAUGHT EXCEPTION\n"; + log_exception(); this_test_failed = true; } if (this_test_failed) { ++number_of_failed_tests; - cerr<<(FL("Test %s:%s failed.\n") % test.group % test.name); - log_checkpoint(); + L(FL("Test %s:%s failed.\n") % test.group % test.name); + if (!log_to_stderr) + cerr << "FAIL\n"; } else { ++number_of_succeeded_tests; - cerr<<(FL("Test %s:%s succeeded.\n") % test.group % test.name); + L(FL("Test %s:%s succeeded.\n") % test.group % test.name); + if (!log_to_stderr) + cerr << "ok\n"; } if (this_test_failed) any_test_failed = true; } - -// A teebuf implements the basic_streambuf interface and forwards all -// operations to two 'targets'. This is used to get progress messages sent -// to both the log file and the terminal. Note that it cannot be used for -// reading (and does not implement the read-side interfaces) nor is it -// seekable. -namespace { - template > - class basic_teebuf : public std::basic_streambuf - { - public: - // grmbl grmbl typedefs not inherited grmbl. - typedef C char_type; - typedef T traits_type; - typedef typename T::int_type int_type; - typedef typename T::pos_type pos_type; - typedef typename T::off_type off_type; - - virtual ~basic_teebuf() {} - basic_teebuf(std::basic_streambuf * t1, - std::basic_streambuf * t2) - : std::basic_streambuf(), target1(t1), target2(t2) {} - - protected: - std::basic_streambuf * target1; - std::basic_streambuf * target2; - - virtual void imbue(std::locale const & loc) - { - target1->pubimbue(loc); - target2->pubimbue(loc); - } - virtual basic_teebuf * setbuf(C * p, std::streamsize n) - { - target1->pubsetbuf(p,n); - target2->pubsetbuf(p,n); - return this; - } - virtual int sync() - { - int r1 = target1->pubsync(); - int r2 = target2->pubsync(); - return (r1 == 0 && r2 == 0) ? 0 : -1; - } - - // Not overriding the seek, get, or putback functions produces a - // streambuf which always fails those operations, thanks to the defaults - // in basic_streambuf. - - // As we do no buffering in this object, it would be correct to forward - // xsputn to the targets. However, this could cause a headache in the - // case that one target consumed fewer characters than the other. As - // this streambuf is not used for much data (most of the chatter goes to - // clog) it is okay to fall back on the dumb-but-reliable default xsputn - // in basic_streambuf. - - // You might think that overflow in this object should forward to - // overflow in the targets, but that would defeat buffering in those - // objects. - virtual int_type overflow(int_type c = traits_type::eof()) - { - if (!traits_type::eq_int_type(c,traits_type::eof())) - { - int_type r1 = target1->sputc(c); - int_type r2 = target2->sputc(c); - if (r1 == traits_type::eof() || r2 == traits_type::eof()) - return traits_type::eof(); - return traits_type::not_eof(c); - } - else - { - return sync() ? traits_type::eof() : traits_type::not_eof(c); - } - } - }; - typedef basic_teebuf teebuf; -} - int main(int argc, char * argv[]) { bool help(false); @@ -332,12 +303,9 @@ int main(int argc, char * argv[]) exit(1); } clog.rdbuf(logbuf); - - // Redirect both cerr and cout to a teebuf which will send their data - // to both the logfile and wherever cerr currently goes. - teebuf * progress_output = new teebuf(logbuf, cerr.rdbuf()); - cerr.rdbuf(progress_output); - cout.rdbuf(progress_output); + // Nobody should be writing to cout, but just in case, send it to + // the log. + cout.rdbuf(logbuf); } else { @@ -353,13 +321,18 @@ int main(int argc, char * argv[]) // stream each one is written to. clog.rdbuf(cerr.rdbuf()); cout.rdbuf(cerr.rdbuf()); + + // Suppress double progress messages. + log_to_stderr = true; } global_sanity.set_debug(); - if (tests.size() == 0) // run all tests { + if (!log_to_stderr) + cerr << "Running unit tests...\n"; + for (group_list_t::const_iterator i = unit_tests().begin(); i != unit_tests().end(); i++) { @@ -431,6 +404,8 @@ int main(int argc, char * argv[]) } else { + if (!log_to_stderr) + cerr << "Running unit tests...\n"; for (vector::const_iterator i = to_run.begin(); i != to_run.end(); ++i) { @@ -439,8 +414,11 @@ int main(int argc, char * argv[]) } } - cerr< UNIT_TEST(, fail_check) { @@ -485,7 +464,16 @@ UNIT_TEST(, uncaught) throw int(); } +UNIT_TEST(, uncaught_std) +{ + throw std::bad_exception(); +} +UNIT_TEST(, uncaught_std_what) +{ + throw std::runtime_error("There is no spoon."); +} + // Local Variables: // mode: C++ // fill-column: 76 ============================================================ --- unit_tests.hh 0c622d45cff9c8fbf892546113c4e5aefe0b2a6c +++ unit_tests.hh ba399f5ac277fddb0766e040a854889bbd4e7e5f @@ -10,8 +10,6 @@ // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. -#include - // Log a success/failure message, and set the test state to 'fail' if needed #define UNIT_TEST_CHECK(expression) \ unit_test::do_check(expression, __FILE__, __LINE__, #expression) @@ -63,7 +61,7 @@ namespace unit_test { void do_require(bool checkval, char const * file, int line, char const * message); - void do_checkpoint(char const * file, int line, std::string const & message); + void do_checkpoint(char const * file, int line, char const * message); // Declarative mechanism for specifying unit tests, similar to // auto_unit_test in boost, but more suited to our needs.