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

Unscoped info feature #1522

Merged
merged 3 commits into from
Mar 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 60 additions & 2 deletions docs/logging.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<a id="top"></a>
# Logging macros

Additional messages can be logged during a test case. Note that the messages are scoped and thus will not be reported if failure occurs in scope preceding the message declaration. An example:
Additional messages can be logged during a test case. Note that the messages logged with `INFO` are scoped and thus will not be reported if failure occurs in scope preceding the message declaration. An example:

```cpp
TEST_CASE("Foo") {
Expand All @@ -28,6 +28,60 @@ The number is 1
```
When the last `CHECK` fails in the "Bar" test case, then only one message will be printed: `Test case start`.

## Logging without local scope

`UNSCOPED_INFO` is similar to `INFO` with two key differences:

- Lifetime of an unscoped message is not tied to its own scope.
- An unscoped message can be reported by the first following assertion only, regardless of the result of that assertion.

In other words, lifetime of `UNSCOPED_INFO` is limited by the following assertion (or by the end of test case/section, whichever comes first) whereas lifetime of `INFO` is limited by its own scope.

These differences make this macro useful for reporting information from helper functions or inner scopes. An example:

```cpp
void print_some_info() {
UNSCOPED_INFO("Info from helper");
}

TEST_CASE("Baz") {
print_some_info();
for (int i = 0; i < 2; ++i) {
UNSCOPED_INFO("The number is " << i);
}
CHECK(false);
}

TEST_CASE("Qux") {
INFO("First info");
UNSCOPED_INFO("First unscoped info");
CHECK(false);

INFO("Second info");
UNSCOPED_INFO("Second unscoped info");
CHECK(false);
}
```

"Baz" test case prints:
```
Info from helper
The number is 0
The number is 1
```

With "Qux" test case, two messages will be printed when the first `CHECK` fails:
```
First info
First unscoped info
```

"First unscoped info" message will be cleared after the first `CHECK`, while "First info" message will persist until the end of the test case. Therefore, when the second `CHECK` fails, three messages will be printed:
```
First info
Second info
Second unscoped info
```

## Streaming macros

Expand All @@ -43,11 +97,15 @@ These macros come in three forms:

**INFO(** _message expression_ **)**

The message is logged to a buffer, but only reported with the next assertion that is logged. This allows you to log contextual information in case of failures which is not shown during a successful test run (for the console reporter, without -s). Messages are removed from the buffer at the end of their scope, so may be used, for example, in loops.
The message is logged to a buffer, but only reported with next assertions that are logged. This allows you to log contextual information in case of failures which is not shown during a successful test run (for the console reporter, without -s). Messages are removed from the buffer at the end of their scope, so may be used, for example, in loops.

_Note that in Catch2 2.x.x `INFO` can be used without a trailing semicolon as there is a trailing semicolon inside macro.
This semicolon will be removed with next major version. It is highly advised to use a trailing semicolon after `INFO` macro._

**UNSCOPED_INFO(** _message expression_ **)**

Similar to `INFO`, but messages are not limited to their own scope: They are removed from the buffer after each assertion, section or test case, whichever comes first.

**WARN(** _message expression_ **)**

The message is always reported but does not fail the test.
Expand Down
1 change: 1 addition & 0 deletions include/catch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@
#endif // CATCH_CONFIG_DISABLE_MATCHERS

#define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg )
#define UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "UNSCOPED_INFO", msg )
#define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg )
#define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE",__VA_ARGS__ )

Expand Down
4 changes: 4 additions & 0 deletions include/internal/catch_capture.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,10 @@
#define INTERNAL_CATCH_INFO( macroName, log ) \
Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage )( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log );

///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_UNSCOPED_INFO( macroName, log ) \
Catch::getResultCapture().emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log )

///////////////////////////////////////////////////////////////////////////////
// Although this is matcher-based, it can be used with just a string
#define INTERNAL_CATCH_THROWS_STR_MATCHES( macroName, resultDisposition, matcher, ... ) \
Expand Down
3 changes: 3 additions & 0 deletions include/internal/catch_interfaces_capture.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ namespace Catch {
struct SectionInfo;
struct SectionEndInfo;
struct MessageInfo;
struct MessageBuilder;
struct Counts;
struct BenchmarkInfo;
struct BenchmarkStats;
Expand All @@ -46,6 +47,8 @@ namespace Catch {
virtual void pushScopedMessage( MessageInfo const& message ) = 0;
virtual void popScopedMessage( MessageInfo const& message ) = 0;

virtual void emplaceUnscopedMessage( MessageBuilder const& builder ) = 0;

virtual void handleFatalErrorCondition( StringRef message ) = 0;

virtual void handleExpr
Expand Down
10 changes: 8 additions & 2 deletions include/internal/catch_message.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,20 @@ namespace Catch {


ScopedMessage::ScopedMessage( MessageBuilder const& builder )
: m_info( builder.m_info )
: m_info( builder.m_info ), m_moved()
{
m_info.message = builder.m_stream.str();
getResultCapture().pushScopedMessage( m_info );
}

ScopedMessage::ScopedMessage( ScopedMessage&& old )
: m_info( old.m_info ), m_moved()
{
old.m_moved = true;
}

ScopedMessage::~ScopedMessage() {
if ( !uncaught_exceptions() ){
if ( !uncaught_exceptions() && !m_moved ){
getResultCapture().popScopedMessage(m_info);
}
}
Expand Down
3 changes: 3 additions & 0 deletions include/internal/catch_message.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,12 @@ namespace Catch {
class ScopedMessage {
public:
explicit ScopedMessage( MessageBuilder const& builder );
ScopedMessage( ScopedMessage& duplicate ) = delete;
ScopedMessage( ScopedMessage&& old );
~ScopedMessage();

MessageInfo m_info;
bool m_moved;
};

class Capturer {
Expand Down
10 changes: 10 additions & 0 deletions include/internal/catch_run_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ namespace Catch {
// and should be let to clear themselves out.
static_cast<void>(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals)));

if (result.getResultType() != ResultWas::Warning)
m_messageScopes.clear();

ozars marked this conversation as resolved.
Show resolved Hide resolved
// Reset working state
resetAssertionInfo();
m_lastResult = result;
Expand Down Expand Up @@ -215,6 +218,7 @@ namespace Catch {

m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions));
m_messages.clear();
m_messageScopes.clear();
}

void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) {
Expand All @@ -241,6 +245,10 @@ namespace Catch {
m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end());
}

void RunContext::emplaceUnscopedMessage( MessageBuilder const& builder ) {
m_messageScopes.emplace_back( builder );
}

std::string RunContext::getCurrentTestName() const {
return m_activeTestCase
? m_activeTestCase->getTestCaseInfo().name
Expand Down Expand Up @@ -301,6 +309,7 @@ namespace Catch {
m_lastAssertionPassed = true;
++m_totals.assertions.passed;
resetAssertionInfo();
m_messageScopes.clear();
}

bool RunContext::aborting() const {
Expand Down Expand Up @@ -352,6 +361,7 @@ namespace Catch {
m_testCaseTracker->close();
handleUnfinishedSections();
m_messages.clear();
m_messageScopes.clear();

SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions);
m_reporter->sectionEnded(testCaseSectionStats);
Expand Down
3 changes: 3 additions & 0 deletions include/internal/catch_run_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ namespace Catch {
void pushScopedMessage( MessageInfo const& message ) override;
void popScopedMessage( MessageInfo const& message ) override;

void emplaceUnscopedMessage( MessageBuilder const& builder ) override;

std::string getCurrentTestName() const override;

const AssertionResult* getLastResult() const override;
Expand Down Expand Up @@ -135,6 +137,7 @@ namespace Catch {
Totals m_totals;
IStreamingReporterPtr m_reporter;
std::vector<MessageInfo> m_messages;
std::vector<ScopedMessage> m_messageScopes; /* Keeps owners of so-called unscoped messages. */
AssertionInfo m_lastAssertionInfo;
std::vector<SectionEndInfo> m_unfinishedSections;
std::vector<ITracker*> m_activeSections;
Expand Down
16 changes: 15 additions & 1 deletion projects/SelfTest/Baselines/compact.sw.approved.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1285,6 +1285,7 @@ Misc.tests.cpp:<line number>: passed:
Misc.tests.cpp:<line number>: passed:
loose text artifact
Message.tests.cpp:<line number>: failed: explicitly with 1 message: 'Previous info should not be seen'
Message.tests.cpp:<line number>: failed: explicitly with 1 message: 'previous unscoped info SHOULD not be seen'
Misc.tests.cpp:<line number>: passed: l == std::numeric_limits<long long>::max() for: 9223372036854775807 (0x<hex digits>)
==
9223372036854775807 (0x<hex digits>)
Expand All @@ -1306,6 +1307,8 @@ Misc.tests.cpp:<line number>: failed: ( fib[i] % 2 ) == 0 for: 1 == 0 with 1 mes
Misc.tests.cpp:<line number>: passed: ( fib[i] % 2 ) == 0 for: 0 == 0 with 1 message: 'Testing if fib[5] (8) is even'
Misc.tests.cpp:<line number>: failed: ( fib[i] % 2 ) == 0 for: 1 == 0 with 1 message: 'Testing if fib[6] (13) is even'
Misc.tests.cpp:<line number>: failed: ( fib[i] % 2 ) == 0 for: 1 == 0 with 1 message: 'Testing if fib[7] (21) is even'
Message.tests.cpp:<line number>: warning: 'info' with 2 messages: 'unscoped info' and 'and warn may mix'
Message.tests.cpp:<line number>: warning: 'info' with 2 messages: 'unscoped info' and 'they are not cleared after warnings'
Misc.tests.cpp:<line number>: failed: a == b for: 1 == 2
Misc.tests.cpp:<line number>: passed: a != b for: 1 != 2
Misc.tests.cpp:<line number>: passed: a < b for: 1 < 2
Expand All @@ -1315,13 +1318,22 @@ Misc.tests.cpp:<line number>: passed: a != b for: 1 != 2
Tricky.tests.cpp:<line number>: passed: s == "7" for: "7" == "7"
Tricky.tests.cpp:<line number>: passed: ti == typeid(int) for: {?} == {?}
Misc.tests.cpp:<line number>: passed:
Message.tests.cpp:<line number>: passed: true with 1 message: 'this MAY be seen only for the FIRST assertion IF info is printed for passing assertions'
Message.tests.cpp:<line number>: passed: true with 1 message: 'this MAY be seen only for the SECOND assertion IF info is printed for passing assertions'
Message.tests.cpp:<line number>: failed: false with 1 message: 'this SHOULD be seen'
Misc.tests.cpp:<line number>: passed: makeString( false ) != static_cast<char*>(0) for: "valid string" != {null string}
Misc.tests.cpp:<line number>: passed: makeString( true ) == static_cast<char*>(0) for: {null string} == {null string}
Tricky.tests.cpp:<line number>: passed: ptr.get() == 0 for: 0 == 0
ToStringPair.tests.cpp:<line number>: passed: ::Catch::Detail::stringify( pair ) == "{ { 42, \"Arthur\" }, { \"Ford\", 24 } }" for: "{ { 42, "Arthur" }, { "Ford", 24 } }"
==
"{ { 42, "Arthur" }, { "Ford", 24 } }"
Tricky.tests.cpp:<line number>: passed: p == 0 for: 0 == 0
Message.tests.cpp:<line number>: passed: true with 1 message: 'this MAY be seen IF info is printed for passing assertions'
Message.tests.cpp:<line number>: failed: false with 2 messages: 'this SHOULD be seen' and 'this SHOULD also be seen'
Message.tests.cpp:<line number>: failed: false with 1 message: 'this SHOULD be seen only ONCE'
Message.tests.cpp:<line number>: passed: true
Message.tests.cpp:<line number>: passed: true with 1 message: 'this MAY also be seen only ONCE IF info is printed for passing assertions'
Message.tests.cpp:<line number>: passed: true
Misc.tests.cpp:<line number>: passed: a != b for: 1 != 2
Misc.tests.cpp:<line number>: passed: b != a for: 2 != 1
Misc.tests.cpp:<line number>: passed: a != b for: 1 != 2
Expand All @@ -1341,6 +1353,8 @@ String.tests.cpp:<line number>: passed: Catch::replaceInPlace( s, "'", "|'" ) fo
String.tests.cpp:<line number>: passed: s == "didn|'t" for: "didn|'t" == "didn|'t"
Misc.tests.cpp:<line number>: failed: false with 1 message: '3'
Message.tests.cpp:<line number>: failed: false with 2 messages: 'hi' and 'i := 7'
Message.tests.cpp:<line number>: failed: false with 4 messages: 'Count 1 to 3...' and '1' and '2' and '3'
Message.tests.cpp:<line number>: failed: false with 4 messages: 'Count 4 to 6...' and '4' and '5' and '6'
ToStringGeneral.tests.cpp:<line number>: passed: Catch::Detail::stringify( emptyMap ) == "{ }" for: "{ }" == "{ }"
ToStringGeneral.tests.cpp:<line number>: passed: Catch::Detail::stringify( map ) == "{ { \"one\", 1 } }" for: "{ { "one", 1 } }" == "{ { "one", 1 } }"
ToStringGeneral.tests.cpp:<line number>: passed: Catch::Detail::stringify( map ) == "{ { \"abc\", 1 }, { \"def\", 2 }, { \"ghi\", 3 } }" for: "{ { "abc", 1 }, { "def", 2 }, { "ghi", 3 } }"
Expand Down Expand Up @@ -1462,5 +1476,5 @@ Misc.tests.cpp:<line number>: passed: v.size() == 5 for: 5 == 5
Misc.tests.cpp:<line number>: passed: v.capacity() >= 5 for: 5 >= 5
Misc.tests.cpp:<line number>: passed:
Misc.tests.cpp:<line number>: passed:
Failed 70 test cases, failed 130 assertions.
Failed 77 test cases, failed 138 assertions.

82 changes: 80 additions & 2 deletions projects/SelfTest/Baselines/console.std.approved.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1056,6 +1056,16 @@ Message.tests.cpp:<line number>: FAILED:
explicitly with message:
Previous info should not be seen

-------------------------------------------------------------------------------
just failure after unscoped info
-------------------------------------------------------------------------------
Message.tests.cpp:<line number>
...............................................................................

Message.tests.cpp:<line number>: FAILED:
explicitly with message:
previous unscoped info SHOULD not be seen

-------------------------------------------------------------------------------
looped SECTION tests
b is currently: 0
Expand Down Expand Up @@ -1128,6 +1138,18 @@ with expansion:
with message:
Testing if fib[7] (21) is even

-------------------------------------------------------------------------------
mix info, unscoped info and warning
-------------------------------------------------------------------------------
Message.tests.cpp:<line number>
...............................................................................

Message.tests.cpp:<line number>: warning:
and warn may mix

Message.tests.cpp:<line number>: warning:
they are not cleared after warnings

-------------------------------------------------------------------------------
more nested SECTION tests
doesn't equal
Expand All @@ -1141,6 +1163,40 @@ Misc.tests.cpp:<line number>: FAILED:
with expansion:
1 == 2

-------------------------------------------------------------------------------
not prints unscoped info from previous failures
-------------------------------------------------------------------------------
Message.tests.cpp:<line number>
...............................................................................

Message.tests.cpp:<line number>: FAILED:
REQUIRE( false )
with message:
this SHOULD be seen

-------------------------------------------------------------------------------
prints unscoped info on failure
-------------------------------------------------------------------------------
Message.tests.cpp:<line number>
...............................................................................

Message.tests.cpp:<line number>: FAILED:
REQUIRE( false )
with messages:
this SHOULD be seen
this SHOULD also be seen

-------------------------------------------------------------------------------
prints unscoped info only for the first assertion
-------------------------------------------------------------------------------
Message.tests.cpp:<line number>
...............................................................................

Message.tests.cpp:<line number>: FAILED:
CHECK( false )
with message:
this SHOULD be seen only ONCE

-------------------------------------------------------------------------------
send a single char to INFO
-------------------------------------------------------------------------------
Expand All @@ -1164,6 +1220,28 @@ with messages:
hi
i := 7

-------------------------------------------------------------------------------
stacks unscoped info in loops
-------------------------------------------------------------------------------
Message.tests.cpp:<line number>
...............................................................................

Message.tests.cpp:<line number>: FAILED:
CHECK( false )
with messages:
Count 1 to 3...
1
2
3

Message.tests.cpp:<line number>: FAILED:
CHECK( false )
with messages:
Count 4 to 6...
4
5
6

-------------------------------------------------------------------------------
string literals of different sizes can be compared
-------------------------------------------------------------------------------
Expand All @@ -1186,6 +1264,6 @@ due to unexpected exception with message:
Why would you throw a std::string?

===============================================================================
test cases: 247 | 186 passed | 57 failed | 4 failed as expected
assertions: 1381 | 1244 passed | 116 failed | 21 failed as expected
test cases: 255 | 189 passed | 62 failed | 4 failed as expected
assertions: 1393 | 1250 passed | 122 failed | 21 failed as expected

Loading