Skip to content

CXX-3094 Use Catch2 SKIP() and compact reporter #1206

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

Merged
merged 18 commits into from
Sep 11, 2024

Conversation

eramongodb
Copy link
Contributor

Catch2

Summary

Followup to #1200. Verified by this patch.

Refactors the test suite to use the new SKIP() macro. Updates EVG test scripts to use the Catch2 compact reporter. Reduces output verbosity in EVG compile-and-test tasks.

Skipping Tests

Catch2 3.3.0 introduced the SKIP() macro to explicitly skip test cases and sections at runtime, which uses the same exception-throwing pattern as FAIL() but marks the test case as "skipped" instead of either "succeeded" or "failed".

This means the following pattern of code in the current test suite:

TEST_CASE("example") {
    if (check_skip_conditions()) {
        WARN("Skipping - Skip Reason");
        return;
    }

    test_actions();
}

can now be written as the more straightforward pattern:

TEST_CASE("example") {
    if (check_skip_conditions()) {
        SKIP("Skip Reason");
    }

    test_actions(); // Not evaluated.
}

This also changes Catch2 output from (using [index_view] test cases as an example):

-------------------------------------------------------------------------------
create_one
  commitQuorum option
-------------------------------------------------------------------------------
src/mongocxx/test/index_view.cpp:180
...............................................................................

src/mongocxx/test/index_view.cpp:182: warning:
  skip: commitQuorum option requires a replica set

===============================================================================
All tests passed (38 assertions in 4 test cases)

to:

-------------------------------------------------------------------------------
create_one
  commitQuorum option
-------------------------------------------------------------------------------
src/mongocxx/test/index_view.cpp:180
...............................................................................

src/mongocxx/test/index_view.cpp:182: SKIPPED:
explicitly with message:
  commitQuorum option requires a replica set

================================================================================
test cases:  4 |  3 passed | 1 skipped
assertions: 38 | 38 passed

Note

The "skipped" metric considers a test case where any section has been skipped as a skipped test case. e.g. given:

TEST_CASE("example") {
  SECTION("one") { CHECK(true); }
  SECTION("two") { CHECK(true); SKIP("evaluate and skip"); }
}

Catch2 reports: test cases: 1 | 1 skipped and assertions: 2 | 2 passed.

This PR also addresses potential issues with test case failure reports due to catchorg/Catch2#1292 (comment), as exposed by introduction of the SKIP() macro to run_unified_format_tests_in_env_dir. (CHECK(fn()) where fn() evaluates its own assertions appears to be somewhat of a Catch2 anti-pattern.)

The --allow-running-no-tests flag is required to avoid an entirely-skipped test executable (no test case succeeded or failed) from being treated as a failure. The removal of this flag (such that at least one test case succeeds per test executable) may be considered in the future, but for now, given our skip-heavy test suite, this flag is used instead.

Compact Reporter

In an effort to reduce the verbosity of CI task output, this PR proposes using Catch2's compact reporter.

Again using [index_view] test cases as example, current output for skipped test cases looks as follows:

-------------------------------------------------------------------------------
create_one
  commitQuorum option
-------------------------------------------------------------------------------
src/mongocxx/test/index_view.cpp:180
...............................................................................

src/mongocxx/test/index_view.cpp:182: warning:
  skip: commitQuorum option requires a replica set

===============================================================================
All tests passed (38 assertions in 4 test cases)

With SKIP() and the compact reporter, the output looks as follows:

src/mongocxx/test/index_view.cpp:182: skipped: 'commitQuorum option requires a replica set'
test cases:  4 |  3 passed | 1 skipped
assertions: 38 | 38 passed

Using the compact reporter reduces total output line count in test tasks by about 2K lines (depending on the task/variant).

The compact reporter does not include test case or section name information. This means some care must be taken to avoid ambiguous skip messages, e.g. (note: no distinguishable information differentiating the skipping of individual tests):

src/mongocxx/test/spec/unified_tests/runner.cpp:1155: skipped: 'none of the runOnRequirements were met'
src/mongocxx/test/spec/unified_tests/runner.cpp:1155: skipped: 'none of the runOnRequirements were met'
src/mongocxx/test/spec/unified_tests/runner.cpp:1155: skipped: 'none of the runOnRequirements were met'

To avoid ambiguity, the SKIP() macro should contain uniquely identifying information in its skip reason, e.g.:

src/mongocxx/test/spec/unified_tests/runner.cpp:1154: skipped: 'description := observeSensitiveCommands' with 1 message: 'observeSensitiveCommands: getnonce is observed with observeSensitiveCommands=true: none of the runOnRequirements were met: [ { "maxServerVersion" : "6.1.99" } ]'
src/mongocxx/test/spec/unified_tests/runner.cpp:1154: skipped: 'description := observeSensitiveCommands' with 1 message: 'observeSensitiveCommands: getnonce is not observed with observeSensitiveCommands=false: none of the runOnRequirements were met: [ { "maxServerVersion" : "6.1.99" } ]'
src/mongocxx/test/spec/unified_tests/runner.cpp:1154: skipped: 'description := observeSensitiveCommands' with 1 message: 'observeSensitiveCommands: getnonce is not observed by default: none of the runOnRequirements were met: [ { "maxServerVersion" : "6.1.99" } ]'

or, preferably, be called within the test case or section that is being skipped for more accurate source location information, e.g.:

src/mongocxx/test/change_streams.cpp:98: skipped: 'change streams require replica set'
src/mongocxx/test/change_streams.cpp:120: skipped: 'change streams require replica set'
src/mongocxx/test/change_streams.cpp:388: skipped: 'change streams require replica set'
...

Most of the substantial changes in this PR pertain to making skip messages uniquely identifiable.

Quirks

When a test case is skipped, informational log messages are truncated to output only the first such message along with the skip reason. These may be used in place of uniquely identifying info in the skip reason itself, although it is still preferable to use the skip reason for reliability. e.g. given:

TEST_CASE("example") {
  INFO("first");
  INFO("second");
  INFO("third");
  SKIP("skip reason"); // Line 5
}

The Catch2 compact reporter outputs:

main.cpp:5: skipped: 'first' with 3 messages: 'skip reason'

When a test case fails, all informational messages are emitted in full. e.g. given:

TEST_CASE("example") {
  INFO("first");
  INFO("second");
  INFO("third");
  FAIL("fail reason"); // Line 5
}

The Catch2 compact reporter outputs:

main.cpp:5: failed: explicitly with 4 messages: 'first' and 'second' and 'third' and 'fail reason'

Given output is typically overwhemingly dominanted by skipped test case messages, the absence of test case or section name(s) on potential assertion failure is considered an acceptable compromise. If further contextual information is required beyond what log messages reveal, the --reporter compact flag may be temporarily disabled in a patch build as needed.

If these quirks of the compact reporter are considered undesirable (more verbose output logs with contextual information included by default is preferable), the changes related to using the compact reporter may be removed from this PR. (However, it is my opinion that the compact reporter encourages writing cleaner tests.)

Further Output Reduction

Some minor adjustments to the compile-and-test scripts further reduce output of EVG tasks by about ~1.5K lines (depending on the task/variant):

  • Repeated invocation of CMake configure and build commands: these have been reduced when able (taking advantage of CMake's support for multiple --target <target> flags).
  • The OUTPUT_QUIET argument is added to execute_process_and_check_result to reduce distcheck output (which was nearly doubling the output line count).
  • Removed a redundant rebuild of examples executables in test.sh.

@eramongodb eramongodb requested a review from kevinAlbs September 9, 2024 15:52
@eramongodb eramongodb self-assigned this Sep 9, 2024
Copy link
Contributor

@vector-of-bool vector-of-bool left a comment

Choose a reason for hiding this comment

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

Good changes! My only comments are relatively minor things.

Comment on lines 74 to 80
set(test_args "")
list(APPEND test_args
--reporter compact
--allow-running-no-tests
)

add_test(NAME bson COMMAND test_bson ${test_args})
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the test_args variable really needed? Can arguments be inlined into the add_test call?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is just for consistency with mongocxx tests. I suppose it can be inlined for now until more bsoncxx test executables are added (if any).

Comment on lines 215 to 226
add_test(NAME driver COMMAND test_driver ${test_args})
add_test(NAME logging COMMAND test_logging ${test_args})
add_test(NAME instance COMMAND test_instance ${test_args})
add_test(NAME crud_specs COMMAND test_crud_specs ${test_args})
add_test(NAME gridfs_specs COMMAND test_gridfs_specs ${test_args})
add_test(NAME client_side_encryption_specs COMMAND test_client_side_encryption_specs ${test_args})
add_test(NAME command_monitoring_specs COMMAND test_command_monitoring_specs ${test_args})
add_test(NAME transactions_specs COMMAND test_transactions_specs ${test_args})
add_test(NAME retryable_reads_spec COMMAND test_retryable_reads_specs ${test_args})
add_test(NAME read_write_concern_specs COMMAND test_read_write_concern_specs ${test_args})
add_test(NAME unified_format_spec COMMAND test_unified_format_spec ${test_args})
add_test(NAME versioned_api COMMAND test_versioned_api ${test_args})
Copy link
Contributor

Choose a reason for hiding this comment

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

Could this be refactored as a loop over the test names?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea. Done.

Comment on lines 218 to 219
} \
((void)0)
Copy link
Contributor

Choose a reason for hiding this comment

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

Minor: Recommend an else here so that the macro expands to a single if statement.

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually: Does this need to be a macro? Does the SKIP need to expand within the body of the test case?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Does this need to be a macro? Does the SKIP need to expand within the body of the test case?

Yes, in order for source location information (filename and line number) to be unique in the skip reason message.

Comment on lines 231 to 239
switch (mongocxx::test_util::client_side_encryption_enabled_or_skip_impl()) { \
case mongocxx::test_util::cseeos_result::enable: \
break; \
case mongocxx::test_util::cseeos_result::skip: \
SKIP("CSE environemnt variables not set"); \
case mongocxx::test_util::cseeos_result::fail: \
FAIL("One or more CSE environment variable is missing"); \
} \
((void)0)
Copy link
Contributor

Choose a reason for hiding this comment

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

Recommend wrapping in if (1) ... else ((void)0) so that the macro acts as a single statement.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Copy link
Collaborator

@kevinAlbs kevinAlbs left a comment

Choose a reason for hiding this comment

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

The efforts to reduce log noise are much appreciated. LGTM with one suggestion and a question.

@eramongodb eramongodb merged commit fb26981 into mongodb:master Sep 11, 2024
44 of 49 checks passed
@eramongodb eramongodb deleted the cxx-catch2 branch September 11, 2024 18:57
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