diff --git a/docs/logging.md b/docs/logging.md index 39ae5c7a..423ce6a8 100644 --- a/docs/logging.md +++ b/docs/logging.md @@ -57,20 +57,37 @@ The message is reported and the test case fails. AS `FAIL`, but does not abort the test -## Quickly capture a variable value +## Quickly capture value of variables or expressions -**CAPTURE(** _expression_ **)** +**CAPTURE(** _expression1_, _expression2_, ... **)** -Sometimes you just want to log the name and value of a variable. While you can easily do this with the INFO macro, above, as a convenience the CAPTURE macro handles the stringising of the variable name for you (actually it works with any expression, not just variables). +Sometimes you just want to log a value of variable, or expression. For +convenience, we provide the `CAPTURE` macro, that can take a variable, +or an expression, and prints out that variable/expression and its value +at the time of capture. -E.g. -```c++ -CAPTURE( theAnswer ); +e.g. `CAPTURE( theAnswer );` will log message "theAnswer := 42", while +```cpp +int a = 1, b = 2, c = 3; +CAPTURE( a, b, c, a + b, c > b, a == 1); +``` +will log a total of 6 messages: +``` +a := 1 +b := 2 +c := 3 +a + b := 3 +c > b := true +a == 1 := true ``` -This would log something like: +You can also capture expressions that use commas inside parentheses +(e.g. function calls), brackets, or braces (e.g. initializers). To +properly capture expression that contains template parameters list +(in other words, it contains commas between angle brackets), you need +to enclose the expression inside parentheses: +`CAPTURE( (std::pair{1, 2}) );` -
"theAnswer := 42"
--- diff --git a/include/internal/catch_message.cpp b/include/internal/catch_message.cpp index df905bf6..98a4dae1 100644 --- a/include/internal/catch_message.cpp +++ b/include/internal/catch_message.cpp @@ -11,6 +11,7 @@ #include "catch_uncaught_exceptions.h" #include +#include namespace Catch { @@ -60,19 +61,48 @@ namespace Catch { Capturer::Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ) { - auto start = std::string::npos; - for( size_t pos = 0; pos <= names.size(); ++pos ) { + auto trimmed = [&] (size_t start, size_t end) { + while (names[start] == ',' || isspace(names[start])) { + ++start; + } + while (names[end] == ',' || isspace(names[end])) { + --end; + } + return names.substr(start, end - start + 1); + }; + + size_t start = 0; + std::stack openings; + for (size_t pos = 0; pos < names.size(); ++pos) { char c = names[pos]; - if( pos == names.size() || c == ' ' || c == '\t' || c == ',' || c == ']' ) { - if( start != std::string::npos ) { - m_messages.push_back( MessageInfo( macroName, lineInfo, resultType ) ); - m_messages.back().message = names.substr( start, pos-start) + " := "; - start = std::string::npos; + switch (c) { + case '[': + case '{': + case '(': + // It is basically impossible to disambiguate between + // comparison and start of template args in this context +// case '<': + openings.push(c); + break; + case ']': + case '}': + case ')': +// case '>': + openings.pop(); + break; + case ',': + if (start != pos && openings.size() == 0) { + m_messages.emplace_back(macroName, lineInfo, resultType); + m_messages.back().message = trimmed(start, pos); + m_messages.back().message += " := "; + start = pos; } } - else if( c != '[' && c != ']' && start == std::string::npos ) - start = pos; } + assert(openings.size() == 0 && "Mismatched openings"); + m_messages.emplace_back(macroName, lineInfo, resultType); + m_messages.back().message = trimmed(start, names.size() - 1); + m_messages.back().message += " := "; } Capturer::~Capturer() { if ( !uncaught_exceptions() ){ @@ -82,7 +112,7 @@ namespace Catch { } } - void Capturer::captureValue( size_t index, StringRef value ) { + void Capturer::captureValue( size_t index, std::string const& value ) { assert( index < m_messages.size() ); m_messages[index].message += value; m_resultCapture.pushScopedMessage( m_messages[index] ); diff --git a/include/internal/catch_message.h b/include/internal/catch_message.h index 73f9efd1..e81069bf 100644 --- a/include/internal/catch_message.h +++ b/include/internal/catch_message.h @@ -77,16 +77,16 @@ namespace Catch { Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ); ~Capturer(); - void captureValue( size_t index, StringRef value ); + void captureValue( size_t index, std::string const& value ); template - void captureValues( size_t index, T&& value ) { + void captureValues( size_t index, T const& value ) { captureValue( index, Catch::Detail::stringify( value ) ); } template - void captureValues( size_t index, T&& value, Ts&&... values ) { - captureValues( index, value ); + void captureValues( size_t index, T const& value, Ts const&... values ) { + captureValue( index, Catch::Detail::stringify(value) ); captureValues( index+1, values... ); } }; diff --git a/projects/SelfTest/Baselines/compact.sw.approved.txt b/projects/SelfTest/Baselines/compact.sw.approved.txt index 496ead91..1b32b42e 100644 --- a/projects/SelfTest/Baselines/compact.sw.approved.txt +++ b/projects/SelfTest/Baselines/compact.sw.approved.txt @@ -228,6 +228,8 @@ Approx.tests.cpp:: passed: NAN != Approx(NAN) for: nanf != Approx( Approx.tests.cpp:: passed: !(NAN == Approx(NAN)) for: !(nanf == Approx( nan )) Tricky.tests.cpp:: passed: y.v == 0 for: 0 == 0 Tricky.tests.cpp:: passed: 0 == y.v for: 0 == 0 +Message.tests.cpp:: passed: with 7 messages: 'a := 1' and 'b := 2' and 'c := 3' and 'a + b := 3' and 'a+b := 3' and 'c > b := true' and 'a == 1 := true' +Message.tests.cpp:: passed: with 7 messages: 'std::vector{1, 2, 3}[0, 1, 2] := 3' and 'std::vector{1, 2, 3}[(0, 1)] := 2' and 'std::vector{1, 2, 3}[0] := 1' and '(helper_1436{12, -12}) := { 12, -12 }' and '(helper_1436(-12, 12)) := { -12, 12 }' and '(1, 2) := 2' and '(2, 3) := 3' ToStringGeneral.tests.cpp:: passed: true with 1 message: 'i := 2' ToStringGeneral.tests.cpp:: passed: true with 1 message: '3' ToStringGeneral.tests.cpp:: passed: tab == '\t' for: '\t' == '\t' diff --git a/projects/SelfTest/Baselines/console.std.approved.txt b/projects/SelfTest/Baselines/console.std.approved.txt index f48a98ec..5e82417e 100644 --- a/projects/SelfTest/Baselines/console.std.approved.txt +++ b/projects/SelfTest/Baselines/console.std.approved.txt @@ -1126,6 +1126,6 @@ due to unexpected exception with message: Why would you throw a std::string? =============================================================================== -test cases: 226 | 170 passed | 52 failed | 4 failed as expected -assertions: 1308 | 1176 passed | 111 failed | 21 failed as expected +test cases: 228 | 172 passed | 52 failed | 4 failed as expected +assertions: 1310 | 1178 passed | 111 failed | 21 failed as expected diff --git a/projects/SelfTest/Baselines/console.sw.approved.txt b/projects/SelfTest/Baselines/console.sw.approved.txt index e99ea050..28638425 100644 --- a/projects/SelfTest/Baselines/console.sw.approved.txt +++ b/projects/SelfTest/Baselines/console.sw.approved.txt @@ -2108,6 +2108,38 @@ Tricky.tests.cpp:: PASSED: with expansion: 0 == 0 +------------------------------------------------------------------------------- +CAPTURE can deal with complex expressions +------------------------------------------------------------------------------- +Message.tests.cpp: +............................................................................... + +Message.tests.cpp:: PASSED: +with messages: + a := 1 + b := 2 + c := 3 + a + b := 3 + a+b := 3 + c > b := true + a == 1 := true + +------------------------------------------------------------------------------- +CAPTURE can deal with complex expressions involving commas +------------------------------------------------------------------------------- +Message.tests.cpp: +............................................................................... + +Message.tests.cpp:: PASSED: +with messages: + std::vector{1, 2, 3}[0, 1, 2] := 3 + std::vector{1, 2, 3}[(0, 1)] := 2 + std::vector{1, 2, 3}[0] := 1 + (helper_1436{12, -12}) := { 12, -12 } + (helper_1436(-12, 12)) := { -12, 12 } + (1, 2) := 2 + (2, 3) := 3 + ------------------------------------------------------------------------------- Capture and info messages Capture should stringify like assertions @@ -10432,6 +10464,6 @@ Misc.tests.cpp: Misc.tests.cpp:: PASSED: =============================================================================== -test cases: 226 | 157 passed | 65 failed | 4 failed as expected -assertions: 1322 | 1176 passed | 125 failed | 21 failed as expected +test cases: 228 | 159 passed | 65 failed | 4 failed as expected +assertions: 1324 | 1178 passed | 125 failed | 21 failed as expected diff --git a/projects/SelfTest/Baselines/junit.sw.approved.txt b/projects/SelfTest/Baselines/junit.sw.approved.txt index 5984b3db..cb03cdb7 100644 --- a/projects/SelfTest/Baselines/junit.sw.approved.txt +++ b/projects/SelfTest/Baselines/junit.sw.approved.txt @@ -1,7 +1,7 @@ - + @@ -141,6 +141,8 @@ Exception.tests.cpp: + + diff --git a/projects/SelfTest/Baselines/xml.sw.approved.txt b/projects/SelfTest/Baselines/xml.sw.approved.txt index d89b4e18..7e02b772 100644 --- a/projects/SelfTest/Baselines/xml.sw.approved.txt +++ b/projects/SelfTest/Baselines/xml.sw.approved.txt @@ -1986,6 +1986,54 @@ + + + a := 1 + + + b := 2 + + + c := 3 + + + a + b := 3 + + + a+b := 3 + + + c > b := true + + + a == 1 := true + + + + + + std::vector<int>{1, 2, 3}[0, 1, 2] := 3 + + + std::vector<int>{1, 2, 3}[(0, 1)] := 2 + + + std::vector<int>{1, 2, 3}[0] := 1 + + + (helper_1436<int, int>{12, -12}) := { 12, -12 } + + + (helper_1436<int, int>(-12, 12)) := { -12, 12 } + + + (1, 2) := 2 + + + (2, 3) := 3 + + +
@@ -12051,7 +12099,7 @@ loose text artifact
- + - + diff --git a/projects/SelfTest/UsageTests/Message.tests.cpp b/projects/SelfTest/UsageTests/Message.tests.cpp index f3ac02a1..002fb875 100644 --- a/projects/SelfTest/UsageTests/Message.tests.cpp +++ b/projects/SelfTest/UsageTests/Message.tests.cpp @@ -9,10 +9,6 @@ #include "catch.hpp" #include -#ifdef __clang__ -#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" -#endif - TEST_CASE( "INFO and WARN do not abort tests", "[messages][.]" ) { INFO( "this is a " << "message" ); // This should output the message if a failure occurs WARN( "this is a " << "warning" ); // This should always output the message but then continue @@ -135,3 +131,60 @@ TEST_CASE( "Pointers can be converted to strings", "[messages][.][approvals]" ) WARN( "actual address of p: " << &p ); WARN( "toString(p): " << ::Catch::Detail::stringify( &p ) ); } + +TEST_CASE( "CAPTURE can deal with complex expressions", "[messages][capture]" ) { + int a = 1; + int b = 2; + int c = 3; + CAPTURE( a, b, c, a + b, a+b, c > b, a == 1 ); + SUCCEED(); +} + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-value" // In (1, 2), the "1" is unused ... +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-value" // All the comma operators are side-effect free +#endif +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4709) // comma in indexing operator +#endif + +template +struct helper_1436 { + helper_1436(T1 t1, T2 t2): + t1{ t1 }, + t2{ t2 } + {} + T1 t1; + T2 t2; +}; + +template +std::ostream& operator<<(std::ostream& out, helper_1436 const& helper) { + out << "{ " << helper.t1 << ", " << helper.t2 << " }"; + return out; +} + +TEST_CASE("CAPTURE can deal with complex expressions involving commas", "[messages][capture]") { + CAPTURE(std::vector{1, 2, 3}[0, 1, 2], + std::vector{1, 2, 3}[(0, 1)], + std::vector{1, 2, 3}[0]); + CAPTURE((helper_1436{12, -12}), + (helper_1436(-12, 12))); + CAPTURE( (1, 2), (2, 3) ); + SUCCEED(); +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif +#ifdef _MSC_VER +#pragma warning(pop) +#endif