Skip to content

Commit

Permalink
Track the last line seen and report it when there's an uncaught excep…
Browse files Browse the repository at this point in the history
…tion

Reporting the line of the last completed check helps narrow down where exactly
the uncaught exception came from when it doesn't come with a backtrace.
Inspired by Catch2's similar functionality.
  • Loading branch information
tgoyne committed Oct 22, 2022
1 parent e2e2a9d commit a417693
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 13 deletions.
17 changes: 13 additions & 4 deletions test/util/unit_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -478,10 +478,15 @@ class TestList::ThreadContextImpl : public ThreadContext {
public:
IntraTestLogger intra_test_logger;
SharedContextImpl& shared_context;

// Each instance of this type is only used on one thread spawned by the
// test runner, but the tests themselves may spawn additional threads that
// perform checks, so it still needs to be somewhat thread-safe.
Mutex mutex;
std::atomic<long long> num_checks;
long long num_failed_checks;
long num_failed_tests;
std::atomic<long> last_line_seen;
bool errors_seen;

ThreadContextImpl(SharedContextImpl& sc, int ti, util::Logger* attached_logger)
Expand All @@ -503,6 +508,7 @@ class TestList::ThreadContextImpl : public ThreadContext {
num_checks = 0;
num_failed_checks = 0;
num_failed_tests = 0;
last_line_seen = 0;
}
};

Expand Down Expand Up @@ -745,18 +751,19 @@ void TestList::ThreadContextImpl::run(SharedContextImpl::Entry entry, UniqueLock
shared_context.reporter.begin(test_context);
lock.unlock();

last_line_seen = test.details.line_number;
errors_seen = false;
Timer timer;
try {
(*test.run_func)(test_context);
}
catch (std::exception& ex) {
std::string message = "Unhandled exception " + get_type_name(ex) + ": " + ex.what();
test_context.test_failed(message);
test_context.test_failed(
util::format("Unhandled exception after line %1 %2: %3", last_line_seen, get_type_name(ex), ex.what()));
}
catch (...) {
std::string message = "Unhandled exception of unknown type";
test_context.test_failed(message);
test_context.test_failed(util::format("Unhandled exception after line %1 of unknown type", last_line_seen));
}
double elapsed_time = timer.get_elapsed_time();
if (errors_seen)
Expand Down Expand Up @@ -795,9 +802,10 @@ TestContext::TestContext(TestList::ThreadContextImpl& tc, const TestDetails& td,
}


void TestContext::check_succeeded()
void TestContext::check_succeeded(long line)
{
++m_thread_context.num_checks;
m_thread_context.last_line_seen.store(line, std::memory_order_relaxed);
}


Expand All @@ -813,6 +821,7 @@ REALM_NORETURN void TestContext::abort()

void TestContext::check_failed(const char* file, long line, const std::string& message)
{
m_thread_context.last_line_seen = line;
{
LockGuard lock(m_thread_context.mutex);
++m_thread_context.num_checks;
Expand Down
18 changes: 9 additions & 9 deletions test/util/unit_test.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
test_context.throw_failed(__FILE__, __LINE__, #expr, #exception_class); \
} \
catch (exception_class&) { \
test_context.check_succeeded(); \
test_context.check_succeeded(__LINE__); \
return true; \
} \
return false; \
Expand All @@ -118,7 +118,7 @@
} \
catch (exception_class & e) { \
if (exception_cond) { \
test_context.check_succeeded(); \
test_context.check_succeeded(__LINE__); \
return true; \
} \
test_context.throw_ex_cond_failed(__FILE__, __LINE__, e.what(), #expr, #exception_class, \
Expand All @@ -134,7 +134,7 @@
test_context.throw_any_failed(__FILE__, __LINE__, #expr); \
} \
catch (...) { \
test_context.check_succeeded(); \
test_context.check_succeeded(__LINE__); \
return true; \
} \
return false; \
Expand All @@ -147,7 +147,7 @@
test_context.throw_any_failed(__FILE__, __LINE__, #expr); \
} \
catch (const std::exception& e) { \
test_context.check_succeeded(); \
test_context.check_succeeded(__LINE__); \
message = e.what(); \
} \
}())
Expand All @@ -167,7 +167,7 @@
([&] { \
try { \
(expr); \
test_context.check_succeeded(); \
test_context.check_succeeded(__LINE__); \
return true; \
} \
catch (std::exception & ex) { \
Expand Down Expand Up @@ -533,7 +533,7 @@ class TestContext {
bool check_definitely_greater(long double a, long double b, long double eps, const char* file, long line,
const char* a_text, const char* b_text, const char* eps_text);

void check_succeeded();
void check_succeeded(long line);

void throw_failed(const char* file, long line, const char* expr_text, const char* exception_name);
void throw_ex_failed(const char* file, long line, const char* expr_text, const char* exception_name,
Expand Down Expand Up @@ -784,7 +784,7 @@ inline bool TestContext::check_cond(bool cond, const char* file, long line, cons
const char* cond_text)
{
if (REALM_LIKELY(cond)) {
check_succeeded();
check_succeeded(line);
}
else {
cond_failed(file, line, macro_name, cond_text);
Expand All @@ -807,7 +807,7 @@ inline bool TestContext::check_compare(bool cond, const A& a, const B& b, const
const char* macro_name, const char* a_text, const char* b_text)
{
if (REALM_LIKELY(cond)) {
check_succeeded();
check_succeeded(line);
}
else {
std::string a_val, b_val;
Expand All @@ -823,7 +823,7 @@ inline bool TestContext::check_inexact_compare(bool cond, long double a, long do
const char* a_text, const char* b_text, const char* eps_text)
{
if (REALM_LIKELY(cond)) {
check_succeeded();
check_succeeded(line);
}
else {
inexact_compare_failed(file, line, macro_name, a_text, b_text, eps_text, a, b, eps);
Expand Down

0 comments on commit a417693

Please sign in to comment.