Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Capture and inspect all logs in Catch2 tests, fix race #234

Merged
merged 1 commit into from
Dec 21, 2023

Conversation

fknorr
Copy link
Contributor

@fknorr fknorr commented Dec 8, 2023

This PR completely redefines how logging is handled inside unit tests.

  • fixes a long-standing data race when capturing and inspecting logs inside a test
  • silences logs by default, printing them post-mortem when a test fails
  • detects if an unexpected warning or error is logged and fails the test

It works by unconditionally capturing logs in the global thread-safe test_utils::log_test_capture. This allows the test code to query captured messages, and will echo the captured logs through a global Catch2 hook if the test fails. By default, this mechanism will fail any test that logs a warning or error, but it provides functions to relax that behavior either explicitly through test_utils::allow_max_log_level or implicitly through runtime_fixture / device_queue_fixture which permit system-dependent warnings to appear.

The new mechanism is fully independent of the CELERITY_LOG_LEVEL environment, both in which messages it captures and also which ones it dumps in case of a test failure.

Some refactorings and fixes happened alogn the way:

  • Simplified log.h: log_context and its printing was unnecessarily complex
  • Moved std::tuple utils from log.h to utils.h
  • Make logging in runtime and from utils::panic independent of LOG_LEVEL when called from tests
  • Fixed some uninit-read bugs in tests that were surfaced by the new log-level restrictions

@fknorr fknorr requested review from psalz and PeterTh December 8, 2023 09:24
@fknorr fknorr self-assigned this Dec 8, 2023
Copy link

github-actions bot commented Dec 8, 2023

Check-perf-impact results: (4c65f1399a47e0eb1340f63004745b17)

❓ No new benchmark data submitted. ❓
Please re-run the microbenchmarks and include the results if your commit could potentially affect performance.

Copy link
Contributor

@PeterTh PeterTh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. It's a bit silly that this took so much infrastructure to do, but it's great to be rid of that race condition.

test/runtime_tests.cc Show resolved Hide resolved
test/runtime_tests.cc Outdated Show resolved Hide resolved
test/test_utils_tests.cc Outdated Show resolved Hide resolved
Copy link
Member

@psalz psalz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing work! I've added one small note. Also you could maybe consider squashing some of the commits before merging.

The one concern I have with this design (which is already infinitely better than what we had before!) is that by setting e.g. allow_max_log_level(detail::log_level::warn), a test might inadvertently swallow some additional, unexpected warnings. There's already a mechanism that could kind of deal with that (allow_higher_level_log_messages), but then we'd have to repeat the message twice (once to allow it, and once to check that it actually was emitted). I'm just spitballing here, and this doesn't have to be in v1, but maybe something like this could work?

TEST_CASE("my test") {
  test_utils::log_inspector logs;
  logs.allow_max_level(detail::log_level::info); // no-op as info would be the default
  // do stuff
  CHECK(logs.contains(detail::log_level::warn, "some warning"));
  CHECK(logs.contains(detail::log_level::error, "some error"));
  // if there's any unconsumed messages > info once log_inspector goes out of scope, fail test in destructor.
}

include/log.h Outdated Show resolved Hide resolved
@fknorr
Copy link
Contributor Author

fknorr commented Dec 20, 2023

I agree, finer control over which warnings to allow would be great in a follow-up PR!

I have created an issue about this.

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

include/log.h Outdated Show resolved Hide resolved
include/log.h Show resolved Hide resolved
include/utils.h Show resolved Hide resolved
include/utils.h Show resolved Hide resolved
test/test_utils.cc Show resolved Hide resolved
test/test_utils.cc Show resolved Hide resolved
test/test_utils.cc Show resolved Hide resolved
@fknorr fknorr added this to the 0.5.0 milestone Dec 21, 2023
This fixes a long-standing race condition that existed with
test_utils::log_capture, which is has now been removed.

Tests now unconditionally capturing logs in the global thread-safe
test_utils::log_test_capture. This allows the test code to query captured
messages, and will echo the captured logs through a global Catch2 hook if the
test fails. By default, this mechanism will fail any test that logs a warning
or error, but it provides functions to relax that behavior either explicitly
through test_utils::allow_max_log_level or implicitly through runtime_fixture /
device_queue_fixture which permit system-dependent warnings to appear.

The "log.h" interface has also been simplified somewhat.
@fknorr fknorr merged commit c1f7cb4 into master Dec 21, 2023
28 checks passed
@psalz psalz deleted the new-log-capture branch December 21, 2023 14:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants