+
+// ----------------------------------------------------------------------------
+// helper classes
+// ----------------------------------------------------------------------------
+
+// exception class for MSVC debug CRT assertion failures
+#ifdef wxUSE_VC_CRTDBG
+
+struct CrtAssertFailure
+{
+ CrtAssertFailure(const char *message) : m_msg(message) { }
+
+ const wxString m_msg;
+
+ wxDECLARE_NO_ASSIGN_CLASS(CrtAssertFailure);
+};
+
+#endif // wxUSE_VC_CRTDBG
+
+#if wxDEBUG_LEVEL
+
+// Information about the last not yet handled assertion.
+static wxString s_lastAssertMessage;
+
+static wxString FormatAssertMessage(const wxString& file,
+ int line,
+ const wxString& func,
+ const wxString& cond,
+ const wxString& msg)
+{
+ wxString str;
+ str << "wxWidgets assert: " << cond << " failed "
+ "at " << file << ":" << line << " in " << func
+ << " with message '" << msg << "'";
+ return str;
+}
+
+static void TestAssertHandler(const wxString& file,
+ int line,
+ const wxString& func,
+ const wxString& cond,
+ const wxString& msg)
+{
+ // Determine whether we can safely throw an exception to just make the test
+ // fail or whether we need to abort (in this case "msg" will contain the
+ // explanation why did we decide to do it).
+ wxString abortReason;
+
+ const wxString
+ assertMessage = FormatAssertMessage(file, line, func, cond, msg);
+
+ if ( !wxIsMainThread() )
+ {
+ // Exceptions thrown from worker threads are not caught currently and
+ // so we'd just die without any useful information -- abort instead.
+ abortReason << assertMessage << "in a worker thread.";
+ }
+ else if ( uncaught_exception() )
+ {
+ // Throwing while already handling an exception would result in
+ // terminate() being called and we wouldn't get any useful information
+ // about why the test failed then.
+ if ( s_lastAssertMessage.empty() )
+ {
+ abortReason << assertMessage << "while handling an exception";
+ }
+ else // In this case the exception is due to a previous assert.
+ {
+ abortReason << s_lastAssertMessage << "\n and another "
+ << assertMessage << " while handling it.";
+ }
+ }
+ else // Can "safely" throw from here.
+ {
+ // Remember this in case another assert happens while handling this
+ // exception: we want to show the original assert as it's usually more
+ // useful to determine the real root of the problem.
+ s_lastAssertMessage = assertMessage;
+
+ throw TestAssertFailure(file, line, func, cond, msg);
+ }
+
+ wxFputs(abortReason, stderr);
+ fflush(stderr);
+ _exit(-1);
+}
+
+#endif // wxDEBUG_LEVEL
+
+// this function should only be called from a catch clause
+static string GetExceptionMessage()
+{
+ wxString msg;
+
+ try
+ {
+ throw;
+ }
+#if wxDEBUG_LEVEL
+ catch ( TestAssertFailure& )
+ {
+ msg = s_lastAssertMessage;
+ s_lastAssertMessage.clear();
+ }
+#endif // wxDEBUG_LEVEL
+#ifdef wxUSE_VC_CRTDBG
+ catch ( CrtAssertFailure& e )
+ {
+ msg << "CRT assert failure: " << e.m_msg;
+ }
+#endif // wxUSE_VC_CRTDBG
+ catch ( std::exception& e )
+ {
+ msg << "std::exception: " << e.what();
+ }
+ catch ( ... )
+ {
+ msg = "Unknown exception caught.";
+ }
+
+ return string(msg.mb_str());
+}
+
+// Protector adding handling of wx-specific (this includes MSVC debug CRT in
+// this context) exceptions
+class wxUnitTestProtector : public CppUnit::Protector
+{
+public:
+ virtual bool protect(const CppUnit::Functor &functor,
+ const CppUnit::ProtectorContext& context)
+ {
+ try
+ {
+ return functor();
+ }
+ catch ( std::exception& )
+ {
+ // cppunit deals with the standard exceptions itself, let it do as
+ // it output more details (especially for std::exception-derived
+ // CppUnit::Exception) than we do
+ throw;
+ }
+ catch ( ... )
+ {
+ reportError(context, CppUnit::Message("Uncaught exception",
+ GetExceptionMessage()));
+ }
+
+ return false;
+ }
+};
+
+// Displays the test name before starting to execute it: this helps with
+// diagnosing where exactly does a test crash or hang when/if it does.
+class DetailListener : public CppUnit::TestListener
+{
+public:
+ DetailListener(bool doTiming = false):
+ CppUnit::TestListener(),
+ m_timing(doTiming)
+ {
+ }
+
+ virtual void startTest(CppUnit::Test *test)
+ {
+ printf(" %-60s ", test->getName().c_str());
+ m_result = RESULT_OK;
+ m_watch.Start();
+ }
+
+ virtual void addFailure(const CppUnit::TestFailure& failure)
+ {
+ m_result = failure.isError() ? RESULT_ERROR : RESULT_FAIL;
+ }
+
+ virtual void endTest(CppUnit::Test * WXUNUSED(test))
+ {
+ m_watch.Pause();
+ printf("%s", GetResultStr(m_result));
+ if (m_timing)
+ printf(" %6ld ms", m_watch.Time());
+ printf("\n");
+ }
+
+protected :
+ enum ResultType
+ {
+ RESULT_OK = 0,
+ RESULT_FAIL,
+ RESULT_ERROR,
+ RESULT_MAX
+ };
+
+ const char* GetResultStr(ResultType type) const
+ {
+ static const char *resultTypeNames[] =
+ {
+ " OK",
+ "FAIL",
+ " ERR"
+ };
+
+ wxCOMPILE_TIME_ASSERT( WXSIZEOF(resultTypeNames) == RESULT_MAX,
+ ResultTypeNamesMismatch );
+
+ return resultTypeNames[type];
+ }
+
+ bool m_timing;
+ wxStopWatch m_watch;
+ ResultType m_result;
+};