#
#
# 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.