diff options
author | onqtam <vik.kirilov@gmail.com> | 2018-07-03 20:31:18 +0300 |
---|---|---|
committer | onqtam <vik.kirilov@gmail.com> | 2018-08-23 16:02:57 +0300 |
commit | 8171c4843a794347a7e809dec4043b9aa70c95f3 (patch) | |
tree | 8a64e5a9381ead22fec79035c316bdc17b090316 | |
parent | 6b05c20ce9ca05a51e3c6f07ab11f788ffd9b548 (diff) |
polished the feature for allowing the use of asserts outside of a testing context (also example and docs) - relates #114
-rw-r--r-- | doc/markdown/assertions.md | 12 | ||||
-rw-r--r-- | doc/markdown/faq.md | 2 | ||||
-rw-r--r-- | doc/markdown/features.md | 5 | ||||
-rw-r--r-- | doc/markdown/roadmap.md | 8 | ||||
-rw-r--r-- | doctest/doctest.h | 305 | ||||
-rw-r--r-- | doctest/parts/doctest_fwd.h | 180 | ||||
-rw-r--r-- | doctest/parts/doctest_impl.h | 125 | ||||
-rw-r--r-- | examples/all_features/CMakeLists.txt | 1 | ||||
-rw-r--r-- | examples/all_features/asserts_used_outside_of_tests.cpp | 79 | ||||
-rw-r--r-- | examples/all_features/main.cpp | 2 | ||||
-rw-r--r-- | examples/all_features/test_output/asserts_used_outside_of_tests.cpp.txt | 21 | ||||
-rw-r--r-- | scripts/playground/main.cpp | 52 | ||||
-rw-r--r-- | scripts/playground/test.cpp | 17 | ||||
-rw-r--r-- | scripts/playground/test_output/playground.txt | 47 | ||||
-rw-r--r-- | scripts/random_dev_notes.md | 25 |
15 files changed, 434 insertions, 447 deletions
diff --git a/doc/markdown/assertions.md b/doc/markdown/assertions.md index 4fbbd33f..927d82c3 100644 --- a/doc/markdown/assertions.md +++ b/doc/markdown/assertions.md @@ -116,6 +116,18 @@ Expects that no exception is thrown during evaluation of the expression. Note that these asserts also have a ```_MESSAGE``` form - like ```CHECK_THROWS_MESSAGE(expression, message)``` - these work identically to the ```_MESSAGE``` form of the normal macros (```CHECK_MESSAGE(a < b, "this shouldn't fail")```) described earlier. +## Using asserts out of a testing context + +Asserts can be used outside of a testing context (in code not being called from a ```TEST_CASE()```) - just like ```assert()``` from the ```<cassert>``` header. + +A ```doctest::Context``` object still has to be created somewhere and set as the default one using the ```setAsDefaultForAssertsOutOfTestCases()``` method - and then asserts will work. A handler can be registered by calling the ```setAssertHandler()``` method on the context object. If no handler is set then ```std::abort()``` is called on failure. + +The results would be best when using the [**fast asserts**](assertions.md#fast-asserts) coupled with the [**```DOCTEST_CONFIG_SUPER_FAST_ASSERTS```**](configuration.md#doctest_config_super_fast_asserts) config identifier and by defining your own macro aliases - like shown [**here**](../../examples/all_features/doctest_proxy.h). + +Checkout the [**example**](../../examples/all_features/asserts_used_outside_of_tests.cpp) showcasing how that is done. For more information see the [**issue for the feature request**](https://github.com/onqtam/doctest/issues/114). + +Currently [**logging macros**](logging.md) cannot be used for extra context for asserts outside of a test run. That means that the ```_MESSAGE``` variants of asserts are also not usable - since they are just a packed ```INFO()``` with an assert right after it. + ## Floating point comparisons When comparing floating point numbers - especially if at least one of them has been computed - great care must be taken to allow for rounding errors and inexact representations. diff --git a/doc/markdown/faq.md b/doc/markdown/faq.md index 6c405d29..a7e71345 100644 --- a/doc/markdown/faq.md +++ b/doc/markdown/faq.md @@ -62,7 +62,7 @@ There are only 2 drawbacks of this approach: These 2 things can be considered negligible if you are dealing mainly with arithmetic (expressions are unlikely to throw exceptions) and all the tests usually pass (you don't need to often navigate to a failing assert with a debugger attached) -If you want better aliases for the asserts instead of the long ones you could use [**```DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES```**](configuration.md#doctest_config_no_short_macro_names) and then define your aliases like this: ```#define CHECK_EQ DOCTEST_FAST_CHECK_EQ``` (example in [**here**](../../examples/all_features/alternative_macros.cpp)). +If you want better aliases for the asserts instead of the long ones you could use [**```DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES```**](configuration.md#doctest_config_no_short_macro_names) and then define your aliases like this: ```#define CHECK_EQ DOCTEST_FAST_CHECK_EQ``` (example in [**here**](../../examples/all_features/alternative_macros.cpp) and [**here**](../../examples/all_features/doctest_proxy.h)). ### Is doctest thread-aware? diff --git a/doc/markdown/features.md b/doc/markdown/features.md index 1c0dbd49..3df924af 100644 --- a/doc/markdown/features.md +++ b/doc/markdown/features.md @@ -50,7 +50,8 @@ - binaries (exe/dll) can use the test runner of another binary - so tests end up in a single registry - [**example**](../../examples/executable_dll_and_plugin/) - supports [**BDD style**](testcases.md) tests - one core [**assertion macro**](assertions.md) for comparisons - standard C++ operators are used for the comparison (less than, equal, greater than...) - yet the full expression is decomposed and left and right values of the expression are logged -- assertion macros for [**exceptions**](assertions.md) - if something should or shouldn't throw +- asserts can be used [**outside of a testing context**](assertions.md#using-asserts-out-of-a-testing-context) - [**example**](../../examples/all_features/asserts_used_outside_of_tests.cpp) +- assertion macros for [**exceptions**](assertions.md#exceptions) - if something should or shouldn't throw - floating point comparison support - see the [**```Approx()```**](assertions.md#floating-point-comparisons) helper - powerful mechanism for [**stringification**](stringification.md) of user types - including [**exceptions**](stringification.md#translating-exceptions)! - tests can be grouped in [**test suites**](testcases.md#test-suites) @@ -68,7 +69,7 @@ - colored output in the console - controlling the order of test execution - different ```doctest::Context```s can be created and ran many times within a single execution of the program -- ability to query if code is currently being ran in a test - ```doctest::isRunningInTest()``` +- ability to query if code is currently being ran in a test - ```doctest::is_running_in_test``` There is a list of planned features which are all important and big - see the [**roadmap**](roadmap.md). diff --git a/doc/markdown/roadmap.md b/doc/markdown/roadmap.md index 8c18e5eb..2b26c18e 100644 --- a/doc/markdown/roadmap.md +++ b/doc/markdown/roadmap.md @@ -34,6 +34,10 @@ Planned features for future releases - order changes constantly... ### For 2.1: +- header with extensions + - demangling with the use of the cxxabi header + - stringification of types from std + - esoteric reporters - matchers - should investigate what they are - look at google test/mock and Catch (also predicates and boost test) - convolution support for the assertion macros (with a predicate) - Value-Parameterized test cases @@ -97,6 +101,8 @@ Planned features for future releases - order changes constantly... ### Things that are being considered but not part of the roadmap yet: +- add LIKELY & friends for the conditions of asserts - look at BOOST_LIKELY, ppk_assert, foonathan/debug_assert, etc +- ability for users to register their own command line options and access them later on - fix this: https://github.com/catchorg/Catch2/issues/1292 - FakeIt mocking integration - like [catch](https://github.com/eranpeer/FakeIt/tree/master/config/catch) (also checkout [this](https://github.com/ujiro99/doctest-sample)) - look into https://github.com/cpp-testing/GUnit - https://www.youtube.com/watch?v=NVrZjT5lW5o @@ -148,6 +154,7 @@ Planned features for future releases - order changes constantly... - make the _MESSAGE assert macros work with variadic arguments - and maybe write the ones for binary/unary/fast asserts as well - move from operator "<<" to "<=" for capturing the left operand when decomposing binary expressions with templates - think about silencing warnings about unused variables when DOCTEST_CONFIG_DISABLE is used - see commit 6b61e8aa3818c5ea100cedc1bb48a60ea10df6e8 or issue #61 + - also this: ```(void)(true ? (void)0 : ((void)(expression)))``` - think about optionally using ```<typeinfo>``` and libcxxabi for demangling so users don't have to use ```TYPE_TO_STRING()``` - handle more complex expressions - ```CHECK(foo() == 1 || bar() == 2);``` - add [[noreturn]] to MessageBuilder::react() - and actually make a separate function (react2) for the FAIL() case @@ -156,6 +163,7 @@ Planned features for future releases - order changes constantly... ### Things that are very unlikely to enter the roadmap: +- rethink static code analysis suppressions - users shouldn't have to use the same flags for code which uses doctest macros/types - think about removing the binary asserts (leaving only the fast binary asserts) because normal asserts + no try/catch in asserts are almost the same - move the "react()" part (the one that throws for REQUIRE asserts - or for when "abort-after=<int>" is reached) to a function call in the while() part of the asserts - stop using underscores for the begining of identifiers - the anonymous variables - against the standard... diff --git a/doctest/doctest.h b/doctest/doctest.h index fd2b14d8..6cfcb750 100644 --- a/doctest/doctest.h +++ b/doctest/doctest.h @@ -415,10 +415,6 @@ typedef decltype(nullptr) nullptr_t; #endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS namespace doctest { -namespace detail { - // the function type this library works with - typedef void (*funcType)(); -} // namespace detail DOCTEST_INTERFACE extern bool is_running_in_test; @@ -540,32 +536,33 @@ namespace assertType { is_check = 2, is_require = 4, - is_throws = 8, - is_throws_as = 16, - is_nothrow = 32, + is_normal = 8, + is_throws = 16, + is_throws_as = 32, + is_nothrow = 64, - is_fast = 64, // not checked anywhere - used just to distinguish the types - is_false = 128, - is_unary = 256, + is_fast = 128, // not checked anywhere - used just to distinguish the types + is_false = 256, + is_unary = 512, - is_eq = 512, - is_ne = 1024, + is_eq = 1024, + is_ne = 2048, - is_lt = 2048, - is_gt = 4096, + is_lt = 4096, + is_gt = 8192, - is_ge = 8192, - is_le = 16384, + is_ge = 16384, + is_le = 32768, // macro types - DT_WARN = is_warn, - DT_CHECK = is_check, - DT_REQUIRE = is_require, + DT_WARN = is_normal | is_warn, + DT_CHECK = is_normal | is_check, + DT_REQUIRE = is_normal | is_require, - DT_WARN_FALSE = is_false | is_warn, - DT_CHECK_FALSE = is_false | is_check, - DT_REQUIRE_FALSE = is_false | is_require, + DT_WARN_FALSE = is_normal | is_false | is_warn, + DT_CHECK_FALSE = is_normal | is_false | is_check, + DT_REQUIRE_FALSE = is_normal | is_false | is_require, DT_WARN_THROWS = is_throws | is_warn, DT_CHECK_THROWS = is_throws | is_check, @@ -579,69 +576,68 @@ namespace assertType { DT_CHECK_NOTHROW = is_nothrow | is_check, DT_REQUIRE_NOTHROW = is_nothrow | is_require, - DT_WARN_EQ = is_eq | is_warn, - DT_CHECK_EQ = is_eq | is_check, - DT_REQUIRE_EQ = is_eq | is_require, + DT_WARN_EQ = is_normal | is_eq | is_warn, + DT_CHECK_EQ = is_normal | is_eq | is_check, + DT_REQUIRE_EQ = is_normal | is_eq | is_require, - DT_WARN_NE = is_ne | is_warn, - DT_CHECK_NE = is_ne | is_check, - DT_REQUIRE_NE = is_ne | is_require, + DT_WARN_NE = is_normal | is_ne | is_warn, + DT_CHECK_NE = is_normal | is_ne | is_check, + DT_REQUIRE_NE = is_normal | is_ne | is_require, - DT_WARN_GT = is_gt | is_warn, - DT_CHECK_GT = is_gt | is_check, - DT_REQUIRE_GT = is_gt | is_require, + DT_WARN_GT = is_normal | is_gt | is_warn, + DT_CHECK_GT = is_normal | is_gt | is_check, + DT_REQUIRE_GT = is_normal | is_gt | is_require, - DT_WARN_LT = is_lt | is_warn, - DT_CHECK_LT = is_lt | is_check, - DT_REQUIRE_LT = is_lt | is_require, + DT_WARN_LT = is_normal | is_lt | is_warn, + DT_CHECK_LT = is_normal | is_lt | is_check, + DT_REQUIRE_LT = is_normal | is_lt | is_require, - DT_WARN_GE = is_ge | is_warn, - DT_CHECK_GE = is_ge | is_check, - DT_REQUIRE_GE = is_ge | is_require, + DT_WARN_GE = is_normal | is_ge | is_warn, + DT_CHECK_GE = is_normal | is_ge | is_check, + DT_REQUIRE_GE = is_normal | is_ge | is_require, - DT_WARN_LE = is_le | is_warn, - DT_CHECK_LE = is_le | is_check, - DT_REQUIRE_LE = is_le | is_require, + DT_WARN_LE = is_normal | is_le | is_warn, + DT_CHECK_LE = is_normal | is_le | is_check, + DT_REQUIRE_LE = is_normal | is_le | is_require, - DT_WARN_UNARY = is_unary | is_warn, - DT_CHECK_UNARY = is_unary | is_check, - DT_REQUIRE_UNARY = is_unary | is_require, + DT_WARN_UNARY = is_normal | is_unary | is_warn, + DT_CHECK_UNARY = is_normal | is_unary | is_check, + DT_REQUIRE_UNARY = is_normal | is_unary | is_require, - DT_WARN_UNARY_FALSE = is_false | is_unary | is_warn, - DT_CHECK_UNARY_FALSE = is_false | is_unary | is_check, - DT_REQUIRE_UNARY_FALSE = is_false | is_unary | is_require, + DT_WARN_UNARY_FALSE = is_normal | is_false | is_unary | is_warn, + DT_CHECK_UNARY_FALSE = is_normal | is_false | is_unary | is_check, + DT_REQUIRE_UNARY_FALSE = is_normal | is_false | is_unary | is_require, - DT_FAST_WARN_EQ = is_fast | is_eq | is_warn, - DT_FAST_CHECK_EQ = is_fast | is_eq | is_check, - DT_FAST_REQUIRE_EQ = is_fast | is_eq | is_require, + DT_FAST_WARN_EQ = is_normal | is_fast | is_eq | is_warn, + DT_FAST_CHECK_EQ = is_normal | is_fast | is_eq | is_check, + DT_FAST_REQUIRE_EQ = is_normal | is_fast | is_eq | is_require, + DT_FAST_WARN_NE = is_normal | is_fast | is_ne | is_warn, + DT_FAST_CHECK_NE = is_normal | is_fast | is_ne | is_check, + DT_FAST_REQUIRE_NE = is_normal | is_fast | is_ne | is_require, - DT_FAST_WARN_NE = is_fast | is_ne | is_warn, - DT_FAST_CHECK_NE = is_fast | is_ne | is_check, - DT_FAST_REQUIRE_NE = is_fast | is_ne | is_require, + DT_FAST_WARN_GT = is_normal | is_fast | is_gt | is_warn, + DT_FAST_CHECK_GT = is_normal | is_fast | is_gt | is_check, + DT_FAST_REQUIRE_GT = is_normal | is_fast | is_gt | is_require, - DT_FAST_WARN_GT = is_fast | is_gt | is_warn, - DT_FAST_CHECK_GT = is_fast | is_gt | is_check, - DT_FAST_REQUIRE_GT = is_fast | is_gt | is_require, + DT_FAST_WARN_LT = is_normal | is_fast | is_lt | is_warn, + DT_FAST_CHECK_LT = is_normal | is_fast | is_lt | is_check, + DT_FAST_REQUIRE_LT = is_normal | is_fast | is_lt | is_require, - DT_FAST_WARN_LT = is_fast | is_lt | is_warn, - DT_FAST_CHECK_LT = is_fast | is_lt | is_check, - DT_FAST_REQUIRE_LT = is_fast | is_lt | is_require, + DT_FAST_WARN_GE = is_normal | is_fast | is_ge | is_warn, + DT_FAST_CHECK_GE = is_normal | is_fast | is_ge | is_check, + DT_FAST_REQUIRE_GE = is_normal | is_fast | is_ge | is_require, - DT_FAST_WARN_GE = is_fast | is_ge | is_warn, - DT_FAST_CHECK_GE = is_fast | is_ge | is_check, - DT_FAST_REQUIRE_GE = is_fast | is_ge | is_require, + DT_FAST_WARN_LE = is_normal | is_fast | is_le | is_warn, + DT_FAST_CHECK_LE = is_normal | is_fast | is_le | is_check, + DT_FAST_REQUIRE_LE = is_normal | is_fast | is_le | is_require, - DT_FAST_WARN_LE = is_fast | is_le | is_warn, - DT_FAST_CHECK_LE = is_fast | is_le | is_check, - DT_FAST_REQUIRE_LE = is_fast | is_le | is_require, + DT_FAST_WARN_UNARY = is_normal | is_fast | is_unary | is_warn, + DT_FAST_CHECK_UNARY = is_normal | is_fast | is_unary | is_check, + DT_FAST_REQUIRE_UNARY = is_normal | is_fast | is_unary | is_require, - DT_FAST_WARN_UNARY = is_fast | is_unary | is_warn, - DT_FAST_CHECK_UNARY = is_fast | is_unary | is_check, - DT_FAST_REQUIRE_UNARY = is_fast | is_unary | is_require, - - DT_FAST_WARN_UNARY_FALSE = is_fast | is_false | is_unary | is_warn, - DT_FAST_CHECK_UNARY_FALSE = is_fast | is_false | is_unary | is_check, - DT_FAST_REQUIRE_UNARY_FALSE = is_fast | is_false | is_unary | is_require + DT_FAST_WARN_UNARY_FALSE = is_normal | is_fast | is_false | is_unary | is_warn, + DT_FAST_CHECK_UNARY_FALSE = is_normal | is_fast | is_false | is_unary | is_check, + DT_FAST_REQUIRE_UNARY_FALSE = is_normal | is_fast | is_false | is_unary | is_require }; } // namespace assertType @@ -679,7 +675,7 @@ struct DOCTEST_INTERFACE AssertData String m_exception; // for normal asserts - String m_decomposition; + String m_decomp; // for specific exception-related asserts bool m_threw_as; @@ -989,6 +985,8 @@ private: DOCTEST_INTERFACE String toString(const Approx& in); +DOCTEST_INTERFACE const ContextOptions* getContextOptions(); + #if !defined(DOCTEST_CONFIG_DISABLE) namespace detail { @@ -1015,8 +1013,6 @@ namespace detail { DOCTEST_INTERFACE bool checkIfShouldThrow(assertType::Enum at); DOCTEST_INTERFACE void fastAssertThrowIfFlagSet(int flags); - DOCTEST_INTERFACE const ContextOptions* getContextOptions(); - struct DOCTEST_INTERFACE Subcase { SubcaseSignature m_signature; @@ -1042,7 +1038,7 @@ namespace detail { bool res = op_macro(lhs, rhs); \ if(m_at & assertType::is_false) \ res = !res; \ - if(!res || doctest::detail::getContextOptions()->success) \ + if(!res || doctest::getContextOptions()->success) \ return Result(res, stringifyBinaryExpr(lhs, op_str, rhs)); \ return Result(res); \ } @@ -1058,7 +1054,7 @@ namespace detail { struct DOCTEST_INTERFACE Result { bool m_passed; - String m_decomposition; + String m_decomp; Result(bool passed, const String& decomposition = String()); @@ -1261,9 +1257,11 @@ namespace detail { } }; + typedef void (*funcType)(); + struct DOCTEST_INTERFACE TestCase : public TestCaseData { - detail::funcType m_test; // a function pointer to the test case + funcType m_test; // a function pointer to the test case const char* m_type; // for templated test cases - gets appended to the real name int m_template_id; // an ID used to distinguish between the different versions of a templated test case @@ -1292,8 +1290,9 @@ namespace detail { }; // forward declarations of functions used by the macros - DOCTEST_INTERFACE int regTest(const TestCase& tc); - DOCTEST_INTERFACE int setTestSuite(const TestSuite& ts); + DOCTEST_INTERFACE int regTest(const TestCase& tc); + DOCTEST_INTERFACE int setTestSuite(const TestSuite& ts); + DOCTEST_INTERFACE bool isDebuggerActive(); namespace binaryAssertComparison { enum Enum @@ -1336,7 +1335,7 @@ namespace detail { const DOCTEST_REF_WRAP(R) rhs) { m_failed = !RelationalComparator<comparison, L, R>()(lhs, rhs); if(m_failed || getContextOptions()->success) - m_decomposition = stringifyBinaryExpr(lhs, ", ", rhs); + m_decomp = stringifyBinaryExpr(lhs, ", ", rhs); } template <typename L> @@ -1347,7 +1346,7 @@ namespace detail { m_failed = !m_failed; if(m_failed || getContextOptions()->success) - m_decomposition = toString(val); + m_decomp = toString(val); } void unexpectedExceptionOccurred(); @@ -1387,11 +1386,13 @@ namespace detail { if(!is_running_in_test) { \ if(failed) { \ ResultBuilder rb(at, file, line, expr); \ - rb.m_failed = failed; \ - rb.m_decomposition = decomp; \ + rb.m_failed = failed; \ + rb.m_decomp = decomp; \ failed_out_of_a_testing_context(rb); \ int res = checkIfShouldThrow(at) ? assertAction::shouldthrow : 0; \ - res |= !getContextOptions()->no_breaks ? assertAction::dbgbreak : 0; \ + res |= (isDebuggerActive() && !getContextOptions()->no_breaks) ? \ + assertAction::dbgbreak : \ + 0; \ DOCTEST_FAST_ASSERT_REACT; \ } \ DOCTEST_FAST_ASSERT_RETURN(0); \ @@ -1402,7 +1403,7 @@ namespace detail { ResultBuilder rb(at, file, line, expr); \ rb.m_failed = failed; \ if(rb.m_failed || getContextOptions()->success) \ - rb.m_decomposition = decomp; \ + rb.m_decomp = decomp; \ int res = rb.log() ? assertAction::dbgbreak : 0; \ if(rb.m_failed && checkIfShouldThrow(at)) \ res |= assertAction::shouldthrow; \ @@ -1475,8 +1476,7 @@ namespace detail { String (*m_translateFunction)(T); }; - DOCTEST_INTERFACE void registerExceptionTranslatorImpl( - const IExceptionTranslator* translateFunction); + DOCTEST_INTERFACE void registerExceptionTranslatorImpl(const IExceptionTranslator* et); // FIX FOR VISUAL STUDIO VERSIONS PRIOR TO 2015 - they failed to compile the call to operator<< with // std::ostream passed as a reference noting that there is a use of an undefined type (which there isn't) @@ -2082,7 +2082,7 @@ constexpr T to_lvalue = x; #define DOCTEST_ASSERT_THROWS(expr, assert_type) \ do { \ - if(!doctest::detail::getContextOptions()->no_throw) { \ + if(!doctest::getContextOptions()->no_throw) { \ doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ __LINE__, #expr); \ try { \ @@ -2094,7 +2094,7 @@ constexpr T to_lvalue = x; #define DOCTEST_ASSERT_THROWS_AS(expr, assert_type, ...) \ do { \ - if(!doctest::detail::getContextOptions()->no_throw) { \ + if(!doctest::getContextOptions()->no_throw) { \ doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ __LINE__, #expr, #__VA_ARGS__); \ try { \ @@ -2109,7 +2109,7 @@ constexpr T to_lvalue = x; #define DOCTEST_ASSERT_NOTHROW(expr, assert_type) \ do { \ - if(!doctest::detail::getContextOptions()->no_throw) { \ + if(!doctest::getContextOptions()->no_throw) { \ doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ __LINE__, #expr); \ try { \ @@ -2762,6 +2762,12 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN #include <stdint.h> #endif // !MSVC +#ifdef DOCTEST_PLATFORM_MAC +#include <sys/types.h> +#include <unistd.h> +#include <sys/sysctl.h> +#endif // DOCTEST_PLATFORM_MAC + DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END // counts the number of elements in a C array @@ -3240,6 +3246,7 @@ bool operator>(const Approx& lhs, double rhs) { return lhs.m_value > rhs && lhs String toString(const Approx& in) { return String("Approx( ") + doctest::toString(in.m_value) + " )"; } +const ContextOptions* getContextOptions() { return DOCTEST_BRANCH_ON_DISABLED(0, g_contextState); } } // namespace doctest @@ -3364,8 +3371,8 @@ namespace detail { return true; if((at & assertType::is_check) //!OCLINT bitwise operator in conditional - && g_contextState->abort_after > 0 && - g_contextState->numAssertsFailed >= g_contextState->abort_after) + && getContextOptions()->abort_after > 0 && + g_contextState->numAssertsFailed >= getContextOptions()->abort_after) return true; return false; @@ -3480,7 +3487,6 @@ namespace { Timer g_timer; } // namespace namespace detail { - const ContextOptions* getContextOptions() { return g_contextState; } Subcase::Subcase(const char* name, const char* file, int line) : m_signature(name, file, line) { @@ -3527,7 +3533,7 @@ namespace detail { Result::Result(bool passed, const String& decomposition) : m_passed(passed) - , m_decomposition(decomposition) {} + , m_decomp(decomposition) {} DOCTEST_DEFINE_DEFAULTS(Result); DOCTEST_DEFINE_COPIES(Result); @@ -3686,8 +3692,8 @@ namespace { ((void)s); // for DOCTEST_CONFIG_COLORS_NONE or DOCTEST_CONFIG_COLORS_WINDOWS ((void)code); // for DOCTEST_CONFIG_COLORS_NONE #ifdef DOCTEST_CONFIG_COLORS_ANSI - if(g_contextState->no_colors || - (isatty(STDOUT_FILENO) == false && g_contextState->force_colors == false)) + if(getContextOptions()->no_colors || + (isatty(STDOUT_FILENO) == false && getContextOptions()->force_colors == false)) return; auto col = ""; @@ -3713,8 +3719,8 @@ namespace { #endif // DOCTEST_CONFIG_COLORS_ANSI #ifdef DOCTEST_CONFIG_COLORS_WINDOWS - if(g_contextState->no_colors || - (isatty(fileno(stdout)) == false && g_contextState->force_colors == false)) + if(getContextOptions()->no_colors || + (isatty(fileno(stdout)) == false && getContextOptions()->force_colors == false)) return; #define DOCTEST_SET_ATTR(x) SetConsoleTextAttribute(g_stdoutHandle, x | g_origBgAttrs) @@ -3784,10 +3790,44 @@ namespace detail { return 0; } - void registerExceptionTranslatorImpl(const IExceptionTranslator* translateFunction) { - if(std::find(getExceptionTranslators().begin(), getExceptionTranslators().end(), - translateFunction) == getExceptionTranslators().end()) - getExceptionTranslators().push_back(translateFunction); +#ifdef DOCTEST_PLATFORM_MAC + // The following function is taken directly from the following technical note: + // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html + // Returns true if the current process is being debugged (either + // running under the debugger or has a debugger attached post facto). + bool isDebuggerActive() { + int mib[4]; + kinfo_proc info; + size_t size; + // Initialize the flags so that, if sysctl fails for some bizarre + // reason, we get a predictable result. + info.kp_proc.p_flag = 0; + // Initialize mib, which tells sysctl the info we want, in this case + // we're looking for information about a specific process ID. + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + // Call sysctl. + size = sizeof(info); + if(sysctl(mib, DOCTEST_COUNTOF(mib), &info, &size, 0, 0) != 0) { + fprintf(stderr, "\n** Call to sysctl failed - unable to determine if debugger is " + "active **\n\n"); + return false; + } + // We're being debugged if the P_TRACED flag is set. + return ((info.kp_proc.p_flag & P_TRACED) != 0); + } +#elif DOCTEST_MSVC || defined(__MINGW32__) + bool isDebuggerActive() { return ::IsDebuggerPresent() != 0; } +#else + bool isDebuggerActive() { return false; } +#endif // Platform + + void registerExceptionTranslatorImpl(const IExceptionTranslator* et) { + if(std::find(getExceptionTranslators().begin(), getExceptionTranslators().end(), et) == + getExceptionTranslators().end()) + getExceptionTranslators().push_back(et); } void writeStringToStream(std::ostream* s, const String& str) { *s << str; } @@ -4032,48 +4072,8 @@ namespace { } // namespace -// TODO: wtf? these are in namespace doctest::detail - and don't error ?!?! perhaps it's all C externs.. -#ifdef DOCTEST_PLATFORM_MAC -#include <sys/types.h> -#include <unistd.h> -#include <sys/sysctl.h> -#endif // DOCTEST_PLATFORM_MAC - namespace { using namespace detail; -#ifdef DOCTEST_PLATFORM_MAC - // The following function is taken directly from the following technical note: - // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html - // Returns true if the current process is being debugged (either - // running under the debugger or has a debugger attached post facto). - bool isDebuggerActive() { - int mib[4]; - kinfo_proc info; - size_t size; - // Initialize the flags so that, if sysctl fails for some bizarre - // reason, we get a predictable result. - info.kp_proc.p_flag = 0; - // Initialize mib, which tells sysctl the info we want, in this case - // we're looking for information about a specific process ID. - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_PID; - mib[3] = getpid(); - // Call sysctl. - size = sizeof(info); - if(sysctl(mib, DOCTEST_COUNTOF(mib), &info, &size, 0, 0) != 0) { - fprintf(stderr, "\n** Call to sysctl failed - unable to determine if debugger is " - "active **\n\n"); - return false; - } - // We're being debugged if the P_TRACED flag is set. - return ((info.kp_proc.p_flag & P_TRACED) != 0); - } -#elif DOCTEST_MSVC || defined(__MINGW32__) - bool isDebuggerActive() { return ::IsDebuggerPresent() != 0; } -#else - bool isDebuggerActive() { return false; } -#endif // Platform #ifdef DOCTEST_PLATFORM_WINDOWS #define DOCTEST_OUTPUT_DEBUG_STRING(text) ::OutputDebugStringA(text) @@ -4114,6 +4114,7 @@ namespace { #endif // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH } // namespace namespace detail { + ResultBuilder::ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr, const char* exception_type) { m_test_case = g_contextState->currentTest; @@ -4134,8 +4135,8 @@ namespace detail { DOCTEST_DEFINE_DEFAULTS(ResultBuilder); void ResultBuilder::setResult(const Result& res) { - m_decomposition = res.m_decomposition; - m_failed = !res.m_passed; + m_decomp = res.m_decomp; + m_failed = !res.m_passed; } void ResultBuilder::unexpectedExceptionOccurred() { @@ -4162,7 +4163,8 @@ namespace detail { failed_out_of_a_testing_context(*this); } - return m_failed && isDebuggerActive() && !g_contextState->no_breaks; // break into debugger + return m_failed && isDebuggerActive() && + !getContextOptions()->no_breaks; // break into debugger } void ResultBuilder::react() const { @@ -4199,7 +4201,7 @@ namespace detail { addFailedAssert(m_severity); } - return isDebuggerActive() && !g_contextState->no_breaks && !isWarn; // break into debugger + return isDebuggerActive() && !getContextOptions()->no_breaks && !isWarn; // break } void MessageBuilder::react() { @@ -4457,8 +4459,7 @@ namespace { if(rb.m_threw) s << rb.m_exception << "\n"; else - s << " values: " << assertString(rb.m_at) << "( " << rb.m_decomposition - << " )\n"; + s << " values: " << assertString(rb.m_at) << "( " << rb.m_decomp << " )\n"; } log_contexts(); @@ -4485,7 +4486,7 @@ namespace { : ConsoleReporter(in) {} void printVersion() { - if(g_contextState->no_version == false) + if(getContextOptions()->no_version == false) s << Color::Cyan << "[doctest] " << Color::None << "doctest version is \"" << DOCTEST_VERSION_STR << "\"\n"; } @@ -4575,11 +4576,11 @@ namespace { void output_query_results() { separator_to_stream(); - if(g_contextState->count || g_contextState->list_test_cases) { + if(getContextOptions()->count || getContextOptions()->list_test_cases) { s << Color::Cyan << "[doctest] " << Color::None << "unskipped test cases passing the current filters: " << g_contextState->numTestCasesPassingFilters << "\n"; - } else if(g_contextState->list_test_suites) { + } else if(getContextOptions()->list_test_suites) { s << Color::Cyan << "[doctest] " << Color::None << "unskipped test cases passing the current filters: " << g_contextState->numTestCasesPassingFilters << "\n"; @@ -4613,7 +4614,7 @@ namespace { #define DOCTEST_DEBUG_OUTPUT_WINDOW_REPORTER_OVERRIDE(func, type) \ void func(type in) override { \ if(isDebuggerActive()) { \ - bool with_col = g_contextState->no_colors; \ + bool with_col = getContextOptions()->no_colors; \ g_contextState->no_colors = false; \ ConsoleReporter::func(in); \ DOCTEST_OUTPUT_DEBUG_STRING(oss.str().c_str()); \ diff --git a/doctest/parts/doctest_fwd.h b/doctest/parts/doctest_fwd.h index e50b3893..9f55205f 100644 --- a/doctest/parts/doctest_fwd.h +++ b/doctest/parts/doctest_fwd.h @@ -412,10 +412,6 @@ typedef decltype(nullptr) nullptr_t; #endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS namespace doctest { -namespace detail { - // the function type this library works with - typedef void (*funcType)(); -} // namespace detail DOCTEST_INTERFACE extern bool is_running_in_test; @@ -537,32 +533,33 @@ namespace assertType { is_check = 2, is_require = 4, - is_throws = 8, - is_throws_as = 16, - is_nothrow = 32, + is_normal = 8, + is_throws = 16, + is_throws_as = 32, + is_nothrow = 64, - is_fast = 64, // not checked anywhere - used just to distinguish the types - is_false = 128, - is_unary = 256, + is_fast = 128, // not checked anywhere - used just to distinguish the types + is_false = 256, + is_unary = 512, - is_eq = 512, - is_ne = 1024, + is_eq = 1024, + is_ne = 2048, - is_lt = 2048, - is_gt = 4096, + is_lt = 4096, + is_gt = 8192, - is_ge = 8192, - is_le = 16384, + is_ge = 16384, + is_le = 32768, // macro types - DT_WARN = is_warn, - DT_CHECK = is_check, - DT_REQUIRE = is_require, + DT_WARN = is_normal | is_warn, + DT_CHECK = is_normal | is_check, + DT_REQUIRE = is_normal | is_require, - DT_WARN_FALSE = is_false | is_warn, - DT_CHECK_FALSE = is_false | is_check, - DT_REQUIRE_FALSE = is_false | is_require, + DT_WARN_FALSE = is_normal | is_false | is_warn, + DT_CHECK_FALSE = is_normal | is_false | is_check, + DT_REQUIRE_FALSE = is_normal | is_false | is_require, DT_WARN_THROWS = is_throws | is_warn, DT_CHECK_THROWS = is_throws | is_check, @@ -576,69 +573,68 @@ namespace assertType { DT_CHECK_NOTHROW = is_nothrow | is_check, DT_REQUIRE_NOTHROW = is_nothrow | is_require, - DT_WARN_EQ = is_eq | is_warn, - DT_CHECK_EQ = is_eq | is_check, - DT_REQUIRE_EQ = is_eq | is_require, - - DT_WARN_NE = is_ne | is_warn, - DT_CHECK_NE = is_ne | is_check, - DT_REQUIRE_NE = is_ne | is_require, + DT_WARN_EQ = is_normal | is_eq | is_warn, + DT_CHECK_EQ = is_normal | is_eq | is_check, + DT_REQUIRE_EQ = is_normal | is_eq | is_require, - DT_WARN_GT = is_gt | is_warn, - DT_CHECK_GT = is_gt | is_check, - DT_REQUIRE_GT = is_gt | is_require, + DT_WARN_NE = is_normal | is_ne | is_warn, + DT_CHECK_NE = is_normal | is_ne | is_check, + DT_REQUIRE_NE = is_normal | is_ne | is_require, - DT_WARN_LT = is_lt | is_warn, - DT_CHECK_LT = is_lt | is_check, - DT_REQUIRE_LT = is_lt | is_require, + DT_WARN_GT = is_normal | is_gt | is_warn, + DT_CHECK_GT = is_normal | is_gt | is_check, + DT_REQUIRE_GT = is_normal | is_gt | is_require, - DT_WARN_GE = is_ge | is_warn, - DT_CHECK_GE = is_ge | is_check, - DT_REQUIRE_GE = is_ge | is_require, + DT_WARN_LT = is_normal | is_lt | is_warn, + DT_CHECK_LT = is_normal | is_lt | is_check, + DT_REQUIRE_LT = is_normal | is_lt | is_require, - DT_WARN_LE = is_le | is_warn, - DT_CHECK_LE = is_le | is_check, - DT_REQUIRE_LE = is_le | is_require, + DT_WARN_GE = is_normal | is_ge | is_warn, + DT_CHECK_GE = is_normal | is_ge | is_check, + DT_REQUIRE_GE = is_normal | is_ge | is_require, - DT_WARN_UNARY = is_unary | is_warn, - DT_CHECK_UNARY = is_unary | is_check, - DT_REQUIRE_UNARY = is_unary | is_require, + DT_WARN_LE = is_normal | is_le | is_warn, + DT_CHECK_LE = is_normal | is_le | is_check, + DT_REQUIRE_LE = is_normal | is_le | is_require, - DT_WARN_UNARY_FALSE = is_false | is_unary | is_warn, - DT_CHECK_UNARY_FALSE = is_false | is_unary | is_check, - DT_REQUIRE_UNARY_FALSE = is_false | is_unary | is_require, + DT_WARN_UNARY = is_normal | is_unary | is_warn, + DT_CHECK_UNARY = is_normal | is_unary | is_check, + DT_REQUIRE_UNARY = is_normal | is_unary | is_require, - DT_FAST_WARN_EQ = is_fast | is_eq | is_warn, - DT_FAST_CHECK_EQ = is_fast | is_eq | is_check, - DT_FAST_REQUIRE_EQ = is_fast | is_eq | is_require, + DT_WARN_UNARY_FALSE = is_normal | is_false | is_unary | is_warn, + DT_CHECK_UNARY_FALSE = is_normal | is_false | is_unary | is_check, + DT_REQUIRE_UNARY_FALSE = is_normal | is_false | is_unary | is_require, - DT_FAST_WARN_NE = is_fast | is_ne | is_warn, - DT_FAST_CHECK_NE = is_fast | is_ne | is_check, - DT_FAST_REQUIRE_NE = is_fast | is_ne | is_require, + DT_FAST_WARN_EQ = is_normal | is_fast | is_eq | is_warn, + DT_FAST_CHECK_EQ = is_normal | is_fast | is_eq | is_check, + DT_FAST_REQUIRE_EQ = is_normal | is_fast | is_eq | is_require, + DT_FAST_WARN_NE = is_normal | is_fast | is_ne | is_warn, + DT_FAST_CHECK_NE = is_normal | is_fast | is_ne | is_check, + DT_FAST_REQUIRE_NE = is_normal | is_fast | is_ne | is_require, - DT_FAST_WARN_GT = is_fast | is_gt | is_warn, - DT_FAST_CHECK_GT = is_fast | is_gt | is_check, - DT_FAST_REQUIRE_GT = is_fast | is_gt | is_require, + DT_FAST_WARN_GT = is_normal | is_fast | is_gt | is_warn, + DT_FAST_CHECK_GT = is_normal | is_fast | is_gt | is_check, + DT_FAST_REQUIRE_GT = is_normal | is_fast | is_gt | is_require, - DT_FAST_WARN_LT = is_fast | is_lt | is_warn, - DT_FAST_CHECK_LT = is_fast | is_lt | is_check, - DT_FAST_REQUIRE_LT = is_fast | is_lt | is_require, + DT_FAST_WARN_LT = is_normal | is_fast | is_lt | is_warn, + DT_FAST_CHECK_LT = is_normal | is_fast | is_lt | is_check, + DT_FAST_REQUIRE_LT = is_normal | is_fast | is_lt | is_require, - DT_FAST_WARN_GE = is_fast | is_ge | is_warn, - DT_FAST_CHECK_GE = is_fast | is_ge | is_check, - DT_FAST_REQUIRE_GE = is_fast | is_ge | is_require, + DT_FAST_WARN_GE = is_normal | is_fast | is_ge | is_warn, + DT_FAST_CHECK_GE = is_normal | is_fast | is_ge | is_check, + DT_FAST_REQUIRE_GE = is_normal | is_fast | is_ge | is_require, - DT_FAST_WARN_LE = is_fast | is_le | is_warn, - DT_FAST_CHECK_LE = is_fast | is_le | is_check, - DT_FAST_REQUIRE_LE = is_fast | is_le | is_require, + DT_FAST_WARN_LE = is_normal | is_fast | is_le | is_warn, + DT_FAST_CHECK_LE = is_normal | is_fast | is_le | is_check, + DT_FAST_REQUIRE_LE = is_normal | is_fast | is_le | is_require, - DT_FAST_WARN_UNARY = is_fast | is_unary | is_warn, - DT_FAST_CHECK_UNARY = is_fast | is_unary | is_check, - DT_FAST_REQUIRE_UNARY = is_fast | is_unary | is_require, + DT_FAST_WARN_UNARY = is_normal | is_fast | is_unary | is_warn, + DT_FAST_CHECK_UNARY = is_normal | is_fast | is_unary | is_check, + DT_FAST_REQUIRE_UNARY = is_normal | is_fast | is_unary | is_require, - DT_FAST_WARN_UNARY_FALSE = is_fast | is_false | is_unary | is_warn, - DT_FAST_CHECK_UNARY_FALSE = is_fast | is_false | is_unary | is_check, - DT_FAST_REQUIRE_UNARY_FALSE = is_fast | is_false | is_unary | is_require + DT_FAST_WARN_UNARY_FALSE = is_normal | is_fast | is_false | is_unary | is_warn, + DT_FAST_CHECK_UNARY_FALSE = is_normal | is_fast | is_false | is_unary | is_check, + DT_FAST_REQUIRE_UNARY_FALSE = is_normal | is_fast | is_false | is_unary | is_require }; } // namespace assertType @@ -676,7 +672,7 @@ struct DOCTEST_INTERFACE AssertData String m_exception; // for normal asserts - String m_decomposition; + String m_decomp; // for specific exception-related asserts bool m_threw_as; @@ -986,6 +982,8 @@ private: DOCTEST_INTERFACE String toString(const Approx& in); +DOCTEST_INTERFACE const ContextOptions* getContextOptions(); + #if !defined(DOCTEST_CONFIG_DISABLE) namespace detail { @@ -1012,8 +1010,6 @@ namespace detail { DOCTEST_INTERFACE bool checkIfShouldThrow(assertType::Enum at); DOCTEST_INTERFACE void fastAssertThrowIfFlagSet(int flags); - DOCTEST_INTERFACE const ContextOptions* getContextOptions(); - struct DOCTEST_INTERFACE Subcase { SubcaseSignature m_signature; @@ -1039,7 +1035,7 @@ namespace detail { bool res = op_macro(lhs, rhs); \ if(m_at & assertType::is_false) \ res = !res; \ - if(!res || doctest::detail::getContextOptions()->success) \ + if(!res || doctest::getContextOptions()->success) \ return Result(res, stringifyBinaryExpr(lhs, op_str, rhs)); \ return Result(res); \ } @@ -1055,7 +1051,7 @@ namespace detail { struct DOCTEST_INTERFACE Result { bool m_passed; - String m_decomposition; + String m_decomp; Result(bool passed, const String& decomposition = String()); @@ -1258,9 +1254,11 @@ namespace detail { } }; + typedef void (*funcType)(); + struct DOCTEST_INTERFACE TestCase : public TestCaseData { - detail::funcType m_test; // a function pointer to the test case + funcType m_test; // a function pointer to the test case const char* m_type; // for templated test cases - gets appended to the real name int m_template_id; // an ID used to distinguish between the different versions of a templated test case @@ -1289,8 +1287,9 @@ namespace detail { }; // forward declarations of functions used by the macros - DOCTEST_INTERFACE int regTest(const TestCase& tc); - DOCTEST_INTERFACE int setTestSuite(const TestSuite& ts); + DOCTEST_INTERFACE int regTest(const TestCase& tc); + DOCTEST_INTERFACE int setTestSuite(const TestSuite& ts); + DOCTEST_INTERFACE bool isDebuggerActive(); namespace binaryAssertComparison { enum Enum @@ -1333,7 +1332,7 @@ namespace detail { const DOCTEST_REF_WRAP(R) rhs) { m_failed = !RelationalComparator<comparison, L, R>()(lhs, rhs); if(m_failed || getContextOptions()->success) - m_decomposition = stringifyBinaryExpr(lhs, ", ", rhs); + m_decomp = stringifyBinaryExpr(lhs, ", ", rhs); } template <typename L> @@ -1344,7 +1343,7 @@ namespace detail { m_failed = !m_failed; if(m_failed || getContextOptions()->success) - m_decomposition = toString(val); + m_decomp = toString(val); } void unexpectedExceptionOccurred(); @@ -1384,11 +1383,13 @@ namespace detail { if(!is_running_in_test) { \ if(failed) { \ ResultBuilder rb(at, file, line, expr); \ - rb.m_failed = failed; \ - rb.m_decomposition = decomp; \ + rb.m_failed = failed; \ + rb.m_decomp = decomp; \ failed_out_of_a_testing_context(rb); \ int res = checkIfShouldThrow(at) ? assertAction::shouldthrow : 0; \ - res |= !getContextOptions()->no_breaks ? assertAction::dbgbreak : 0; \ + res |= (isDebuggerActive() && !getContextOptions()->no_breaks) ? \ + assertAction::dbgbreak : \ + 0; \ DOCTEST_FAST_ASSERT_REACT; \ } \ DOCTEST_FAST_ASSERT_RETURN(0); \ @@ -1399,7 +1400,7 @@ namespace detail { ResultBuilder rb(at, file, line, expr); \ rb.m_failed = failed; \ if(rb.m_failed || getContextOptions()->success) \ - rb.m_decomposition = decomp; \ + rb.m_decomp = decomp; \ int res = rb.log() ? assertAction::dbgbreak : 0; \ if(rb.m_failed && checkIfShouldThrow(at)) \ res |= assertAction::shouldthrow; \ @@ -1472,8 +1473,7 @@ namespace detail { String (*m_translateFunction)(T); }; - DOCTEST_INTERFACE void registerExceptionTranslatorImpl( - const IExceptionTranslator* translateFunction); + DOCTEST_INTERFACE void registerExceptionTranslatorImpl(const IExceptionTranslator* et); // FIX FOR VISUAL STUDIO VERSIONS PRIOR TO 2015 - they failed to compile the call to operator<< with // std::ostream passed as a reference noting that there is a use of an undefined type (which there isn't) @@ -2079,7 +2079,7 @@ constexpr T to_lvalue = x; #define DOCTEST_ASSERT_THROWS(expr, assert_type) \ do { \ - if(!doctest::detail::getContextOptions()->no_throw) { \ + if(!doctest::getContextOptions()->no_throw) { \ doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ __LINE__, #expr); \ try { \ @@ -2091,7 +2091,7 @@ constexpr T to_lvalue = x; #define DOCTEST_ASSERT_THROWS_AS(expr, assert_type, ...) \ do { \ - if(!doctest::detail::getContextOptions()->no_throw) { \ + if(!doctest::getContextOptions()->no_throw) { \ doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ __LINE__, #expr, #__VA_ARGS__); \ try { \ @@ -2106,7 +2106,7 @@ constexpr T to_lvalue = x; #define DOCTEST_ASSERT_NOTHROW(expr, assert_type) \ do { \ - if(!doctest::detail::getContextOptions()->no_throw) { \ + if(!doctest::getContextOptions()->no_throw) { \ doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ __LINE__, #expr); \ try { \ diff --git a/doctest/parts/doctest_impl.h b/doctest/parts/doctest_impl.h index 57cc9d97..e554b923 100644 --- a/doctest/parts/doctest_impl.h +++ b/doctest/parts/doctest_impl.h @@ -114,6 +114,12 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN #include <stdint.h> #endif // !MSVC +#ifdef DOCTEST_PLATFORM_MAC +#include <sys/types.h> +#include <unistd.h> +#include <sys/sysctl.h> +#endif // DOCTEST_PLATFORM_MAC + DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END // counts the number of elements in a C array @@ -592,6 +598,7 @@ bool operator>(const Approx& lhs, double rhs) { return lhs.m_value > rhs && lhs String toString(const Approx& in) { return String("Approx( ") + doctest::toString(in.m_value) + " )"; } +const ContextOptions* getContextOptions() { return DOCTEST_BRANCH_ON_DISABLED(0, g_contextState); } } // namespace doctest @@ -716,8 +723,8 @@ namespace detail { return true; if((at & assertType::is_check) //!OCLINT bitwise operator in conditional - && g_contextState->abort_after > 0 && - g_contextState->numAssertsFailed >= g_contextState->abort_after) + && getContextOptions()->abort_after > 0 && + g_contextState->numAssertsFailed >= getContextOptions()->abort_after) return true; return false; @@ -832,7 +839,6 @@ namespace { Timer g_timer; } // namespace namespace detail { - const ContextOptions* getContextOptions() { return g_contextState; } Subcase::Subcase(const char* name, const char* file, int line) : m_signature(name, file, line) { @@ -879,7 +885,7 @@ namespace detail { Result::Result(bool passed, const String& decomposition) : m_passed(passed) - , m_decomposition(decomposition) {} + , m_decomp(decomposition) {} DOCTEST_DEFINE_DEFAULTS(Result); DOCTEST_DEFINE_COPIES(Result); @@ -1038,8 +1044,8 @@ namespace { ((void)s); // for DOCTEST_CONFIG_COLORS_NONE or DOCTEST_CONFIG_COLORS_WINDOWS ((void)code); // for DOCTEST_CONFIG_COLORS_NONE #ifdef DOCTEST_CONFIG_COLORS_ANSI - if(g_contextState->no_colors || - (isatty(STDOUT_FILENO) == false && g_contextState->force_colors == false)) + if(getContextOptions()->no_colors || + (isatty(STDOUT_FILENO) == false && getContextOptions()->force_colors == false)) return; auto col = ""; @@ -1065,8 +1071,8 @@ namespace { #endif // DOCTEST_CONFIG_COLORS_ANSI #ifdef DOCTEST_CONFIG_COLORS_WINDOWS - if(g_contextState->no_colors || - (isatty(fileno(stdout)) == false && g_contextState->force_colors == false)) + if(getContextOptions()->no_colors || + (isatty(fileno(stdout)) == false && getContextOptions()->force_colors == false)) return; #define DOCTEST_SET_ATTR(x) SetConsoleTextAttribute(g_stdoutHandle, x | g_origBgAttrs) @@ -1136,10 +1142,44 @@ namespace detail { return 0; } - void registerExceptionTranslatorImpl(const IExceptionTranslator* translateFunction) { - if(std::find(getExceptionTranslators().begin(), getExceptionTranslators().end(), - translateFunction) == getExceptionTranslators().end()) - getExceptionTranslators().push_back(translateFunction); +#ifdef DOCTEST_PLATFORM_MAC + // The following function is taken directly from the following technical note: + // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html + // Returns true if the current process is being debugged (either + // running under the debugger or has a debugger attached post facto). + bool isDebuggerActive() { + int mib[4]; + kinfo_proc info; + size_t size; + // Initialize the flags so that, if sysctl fails for some bizarre + // reason, we get a predictable result. + info.kp_proc.p_flag = 0; + // Initialize mib, which tells sysctl the info we want, in this case + // we're looking for information about a specific process ID. + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + // Call sysctl. + size = sizeof(info); + if(sysctl(mib, DOCTEST_COUNTOF(mib), &info, &size, 0, 0) != 0) { + fprintf(stderr, "\n** Call to sysctl failed - unable to determine if debugger is " + "active **\n\n"); + return false; + } + // We're being debugged if the P_TRACED flag is set. + return ((info.kp_proc.p_flag & P_TRACED) != 0); + } +#elif DOCTEST_MSVC || defined(__MINGW32__) + bool isDebuggerActive() { return ::IsDebuggerPresent() != 0; } +#else + bool isDebuggerActive() { return false; } +#endif // Platform + + void registerExceptionTranslatorImpl(const IExceptionTranslator* et) { + if(std::find(getExceptionTranslators().begin(), getExceptionTranslators().end(), et) == + getExceptionTranslators().end()) + getExceptionTranslators().push_back(et); } void writeStringToStream(std::ostream* s, const String& str) { *s << str; } @@ -1384,48 +1424,8 @@ namespace { } // namespace -// TODO: wtf? these are in namespace doctest::detail - and don't error ?!?! perhaps it's all C externs.. -#ifdef DOCTEST_PLATFORM_MAC -#include <sys/types.h> -#include <unistd.h> -#include <sys/sysctl.h> -#endif // DOCTEST_PLATFORM_MAC - namespace { using namespace detail; -#ifdef DOCTEST_PLATFORM_MAC - // The following function is taken directly from the following technical note: - // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html - // Returns true if the current process is being debugged (either - // running under the debugger or has a debugger attached post facto). - bool isDebuggerActive() { - int mib[4]; - kinfo_proc info; - size_t size; - // Initialize the flags so that, if sysctl fails for some bizarre - // reason, we get a predictable result. - info.kp_proc.p_flag = 0; - // Initialize mib, which tells sysctl the info we want, in this case - // we're looking for information about a specific process ID. - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_PID; - mib[3] = getpid(); - // Call sysctl. - size = sizeof(info); - if(sysctl(mib, DOCTEST_COUNTOF(mib), &info, &size, 0, 0) != 0) { - fprintf(stderr, "\n** Call to sysctl failed - unable to determine if debugger is " - "active **\n\n"); - return false; - } - // We're being debugged if the P_TRACED flag is set. - return ((info.kp_proc.p_flag & P_TRACED) != 0); - } -#elif DOCTEST_MSVC || defined(__MINGW32__) - bool isDebuggerActive() { return ::IsDebuggerPresent() != 0; } -#else - bool isDebuggerActive() { return false; } -#endif // Platform #ifdef DOCTEST_PLATFORM_WINDOWS #define DOCTEST_OUTPUT_DEBUG_STRING(text) ::OutputDebugStringA(text) @@ -1466,6 +1466,7 @@ namespace { #endif // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH } // namespace namespace detail { + ResultBuilder::ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr, const char* exception_type) { m_test_case = g_contextState->currentTest; @@ -1486,8 +1487,8 @@ namespace detail { DOCTEST_DEFINE_DEFAULTS(ResultBuilder); void ResultBuilder::setResult(const Result& res) { - m_decomposition = res.m_decomposition; - m_failed = !res.m_passed; + m_decomp = res.m_decomp; + m_failed = !res.m_passed; } void ResultBuilder::unexpectedExceptionOccurred() { @@ -1514,7 +1515,8 @@ namespace detail { failed_out_of_a_testing_context(*this); } - return m_failed && isDebuggerActive() && !g_contextState->no_breaks; // break into debugger + return m_failed && isDebuggerActive() && + !getContextOptions()->no_breaks; // break into debugger } void ResultBuilder::react() const { @@ -1551,7 +1553,7 @@ namespace detail { addFailedAssert(m_severity); } - return isDebuggerActive() && !g_contextState->no_breaks && !isWarn; // break into debugger + return isDebuggerActive() && !getContextOptions()->no_breaks && !isWarn; // break } void MessageBuilder::react() { @@ -1809,8 +1811,7 @@ namespace { if(rb.m_threw) s << rb.m_exception << "\n"; else - s << " values: " << assertString(rb.m_at) << "( " << rb.m_decomposition - << " )\n"; + s << " values: " << assertString(rb.m_at) << "( " << rb.m_decomp << " )\n"; } log_contexts(); @@ -1837,7 +1838,7 @@ namespace { : ConsoleReporter(in) {} void printVersion() { - if(g_contextState->no_version == false) + if(getContextOptions()->no_version == false) s << Color::Cyan << "[doctest] " << Color::None << "doctest version is \"" << DOCTEST_VERSION_STR << "\"\n"; } @@ -1927,11 +1928,11 @@ namespace { void output_query_results() { separator_to_stream(); - if(g_contextState->count || g_contextState->list_test_cases) { + if(getContextOptions()->count || getContextOptions()->list_test_cases) { s << Color::Cyan << "[doctest] " << Color::None << "unskipped test cases passing the current filters: " << g_contextState->numTestCasesPassingFilters << "\n"; - } else if(g_contextState->list_test_suites) { + } else if(getContextOptions()->list_test_suites) { s << Color::Cyan << "[doctest] " << Color::None << "unskipped test cases passing the current filters: " << g_contextState->numTestCasesPassingFilters << "\n"; @@ -1965,7 +1966,7 @@ namespace { #define DOCTEST_DEBUG_OUTPUT_WINDOW_REPORTER_OVERRIDE(func, type) \ void func(type in) override { \ if(isDebuggerActive()) { \ - bool with_col = g_contextState->no_colors; \ + bool with_col = getContextOptions()->no_colors; \ g_contextState->no_colors = false; \ ConsoleReporter::func(in); \ DOCTEST_OUTPUT_DEBUG_STRING(oss.str().c_str()); \ diff --git a/examples/all_features/CMakeLists.txt b/examples/all_features/CMakeLists.txt index 907a6d21..1b9cfbef 100644 --- a/examples/all_features/CMakeLists.txt +++ b/examples/all_features/CMakeLists.txt @@ -21,6 +21,7 @@ set(files logging.cpp templated_test_cases.cpp test_cases_and_suites.cpp + asserts_used_outside_of_tests.cpp ) # add the executable diff --git a/examples/all_features/asserts_used_outside_of_tests.cpp b/examples/all_features/asserts_used_outside_of_tests.cpp new file mode 100644 index 00000000..d2f3ba91 --- /dev/null +++ b/examples/all_features/asserts_used_outside_of_tests.cpp @@ -0,0 +1,79 @@ +#define DOCTEST_CONFIG_SUPER_FAST_ASSERTS // defined so the fast asserts are crazy fast - both for compilation and execution +#include "doctest.h" + +DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN +#include <iostream> +#include <cstring> +#include <algorithm> +DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END + +// some function which uses asserts not just for unit testing but also for ensuring contracts in production code +static void some_func() { + FAST_CHECK_EQ(true, false); + FAST_CHECK_UNARY(false); + FAST_CHECK_UNARY_FALSE(true); + + CHECK_EQ(true, false); + CHECK_UNARY(false); + CHECK_UNARY_FALSE(true); + + CHECK(false); + CHECK_THROWS(((void)false)); +} + +void some_program_code(int argc, char** argv); // obligatory fwd decl to silence a warning... +void some_program_code(int argc, char** argv) { + // return if the current test from the doctest CMake tests is not for this file + if(std::find_if(argv, argv + argc, [](const char* str) { + return strcmp(str, "-sf=*asserts_used_outside_of_tests.cpp") == 0; + }) == argv + argc) + return; + + // construct a context + doctest::Context context; + context.applyCommandLine(argc, argv); + + // sets the context as the default one - so asserts used outside of a testing context do not crash + context.setAsDefaultForAssertsOutOfTestCases(); + + // set a handler with a signature: void(const doctest::AssertData&) + // without setting a handler we would get std::abort() called when an assert fails + context.setAssertHandler([](const doctest::AssertData& ad) { + using namespace doctest; + + // here we can choose what to do: + // - log the failed assert + // - throw an exception + // - call std::abort() or std::terminate() + + auto getFailString = [](assertType::Enum at) { + if(at & assertType::is_warn) + return "WARNING: "; + if(at & assertType::is_check) + return "ERROR: "; + if(at & assertType::is_require) + return "FATAL ERROR: "; + return ""; + }; + + std::cout << Color::LightGrey << ad.m_file << "(" << ad.m_line << "): "; + std::cout << Color::Red << getFailString(ad.m_at); + + // handling only normal (comparison and unary) asserts - nothing exceptions-related + if(ad.m_at & assertType::is_normal) { + std::cout << Color::Cyan << assertString(ad.m_at) << "( " << ad.m_expr << " ) "; + std::cout << Color::None << (ad.m_threw ? "THREW exception: " : "is NOT correct!\n"); + if(ad.m_threw) + std::cout << ad.m_exception; + else + std::cout << " values: " << assertString(ad.m_at) << "( " << ad.m_decomp << " )"; + } else { + std::cout << Color::None << "an assert dealing with exceptions has failed!"; + } + + std::cout << "\n"; + }); + + // call the function with asserts out of a testing context - the above handler will be called on failure + some_func(); +} diff --git a/examples/all_features/main.cpp b/examples/all_features/main.cpp index 513dfe03..f1c2af75 100644 --- a/examples/all_features/main.cpp +++ b/examples/all_features/main.cpp @@ -4,6 +4,7 @@ #include "header.h" int program(); +void some_program_code(int argc, char** argv); int main(int argc, char** argv) { doctest::Context context; @@ -28,6 +29,7 @@ int main(int argc, char** argv) { context.clearFilters(); // removes all filters added up to this point int client_stuff_return_code = program(); + some_program_code(argc, argv); // your program - if the testing framework is integrated in your production code return res + client_stuff_return_code; // the result from doctest is propagated here as well diff --git a/examples/all_features/test_output/asserts_used_outside_of_tests.cpp.txt b/examples/all_features/test_output/asserts_used_outside_of_tests.cpp.txt new file mode 100644 index 00000000..97331b44 --- /dev/null +++ b/examples/all_features/test_output/asserts_used_outside_of_tests.cpp.txt @@ -0,0 +1,21 @@ +[doctest] run with "--help" for options +=============================================================================== +[doctest] test cases: 0 | 0 passed | 0 failed | +[doctest] assertions: 0 | 0 passed | 0 failed | +[doctest] Status: SUCCESS! +Program code. +D:\doctest\examples\all_features\asserts_used_outside_of_tests.cpp(12): ERROR: FAST_CHECK_EQ( true, false ) is NOT correct! + values: FAST_CHECK_EQ( true, false ) +D:\doctest\examples\all_features\asserts_used_outside_of_tests.cpp(13): ERROR: FAST_CHECK_UNARY( false ) is NOT correct! + values: FAST_CHECK_UNARY( false ) +D:\doctest\examples\all_features\asserts_used_outside_of_tests.cpp(14): ERROR: FAST_CHECK_UNARY_FALSE( true ) is NOT correct! + values: FAST_CHECK_UNARY_FALSE( true ) +D:\doctest\examples\all_features\asserts_used_outside_of_tests.cpp(16): ERROR: CHECK_EQ( true, false ) is NOT correct! + values: CHECK_EQ( true, false ) +D:\doctest\examples\all_features\asserts_used_outside_of_tests.cpp(17): ERROR: CHECK_UNARY( false ) is NOT correct! + values: CHECK_UNARY( false ) +D:\doctest\examples\all_features\asserts_used_outside_of_tests.cpp(18): ERROR: CHECK_UNARY_FALSE( true ) is NOT correct! + values: CHECK_UNARY_FALSE( true ) +D:\doctest\examples\all_features\asserts_used_outside_of_tests.cpp(20): ERROR: CHECK( false ) is NOT correct! + values: CHECK( false ) +D:\doctest\examples\all_features\asserts_used_outside_of_tests.cpp(21): ERROR: an assert dealing with exceptions has failed! diff --git a/scripts/playground/main.cpp b/scripts/playground/main.cpp index 7ff274d5..af44367b 100644 --- a/scripts/playground/main.cpp +++ b/scripts/playground/main.cpp @@ -1,51 +1,5 @@ #include "parts/doctest_impl.h" -static const char* getSuccessOrFailString(doctest::assertType::Enum at) { - using namespace doctest; - if(at & assertType::is_warn) //!OCLINT bitwise operator in conditional - return "WARNING: "; - if(at & assertType::is_check) //!OCLINT bitwise operator in conditional - return "ERROR: "; - if(at & assertType::is_require) //!OCLINT bitwise operator in conditional - return "FATAL ERROR: "; - return ""; -} - -static void handler(const doctest::AssertData& ad) { - using namespace doctest; - - auto& s = std::cout; - auto& rb = ad; - - s << Color::LightGrey << ad.m_file << (detail::getContextOptions()->gnu_file_line ? ":" : "(") - << ad.m_line << (detail::getContextOptions()->gnu_file_line ? ":" : "): "); - s << Color::Red << getSuccessOrFailString(ad.m_at); - - if((rb.m_at & assertType::is_throws_as) == 0) //!OCLINT bitwise operator in conditional - s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << " ) " << Color::None; - - if(rb.m_at & assertType::is_throws) { //!OCLINT bitwise operator in conditional - s << (rb.m_threw ? "threw as expected!" : "did NOT throw at all!") << "\n"; - } else if(rb.m_at & assertType::is_throws_as) { //!OCLINT bitwise operator in conditional - s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", " - << rb.m_exception_type << " ) " << Color::None - << (rb.m_threw ? - (rb.m_threw_as ? "threw as expected!" : "threw a DIFFERENT exception: ") : - "did NOT throw at all!") - << Color::Cyan << rb.m_exception << "\n"; - } else if(rb.m_at & assertType::is_nothrow) { //!OCLINT bitwise operator in conditional - s << (rb.m_threw ? "THREW exception: " : "didn't throw!") << Color::Cyan << rb.m_exception - << "\n"; - } else { - s << (rb.m_threw ? "THREW exception: " : - (!rb.m_failed ? "is correct!\n" : "is NOT correct!\n")); - if(rb.m_threw) - s << rb.m_exception << "\n"; - else - s << " values: " << assertString(rb.m_at) << "( " << rb.m_decomposition << " )\n"; - } -} - int main(int argc, char** argv) { doctest::Context context; @@ -60,12 +14,6 @@ int main(int argc, char** argv) { // overrides context.setOption("order-by", "file"); // sort the test cases by their name - // required so asserts can be used outside of a 'run' context - context.setAsDefaultForAssertsOutOfTestCases(); - context.setAssertHandler(handler); - void some_func(); - some_func(); - int res = context.run(); // run if(context.shouldExit()) // important - query flags (and --exit) rely on the user doing this diff --git a/scripts/playground/test.cpp b/scripts/playground/test.cpp index e40535dd..043e3be7 100644 --- a/scripts/playground/test.cpp +++ b/scripts/playground/test.cpp @@ -1,23 +1,6 @@ -//#define DOCTEST_CONFIG_SUPER_FAST_ASSERTS #include "parts/doctest_fwd.h" DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN #include <iostream> using namespace std; DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END - -void some_func(); -void some_func() { - FAST_CHECK_EQ(true, false); - FAST_CHECK_UNARY(false); - FAST_CHECK_UNARY_FALSE(true); - - CHECK_EQ(true, false); - CHECK_UNARY(false); - CHECK_UNARY_FALSE(true); - - CHECK(false); - CHECK_THROWS(((void)false)); -} - -TEST_CASE("some test case lololo!") { some_func(); } diff --git a/scripts/playground/test_output/playground.txt b/scripts/playground/test_output/playground.txt deleted file mode 100644 index cc0cc904..00000000 --- a/scripts/playground/test_output/playground.txt +++ /dev/null @@ -1,47 +0,0 @@ -D:\doctest\scripts\playground\test.cpp(11): ERROR: FAST_CHECK_EQ( true, false ) is NOT correct! - values: FAST_CHECK_EQ( true, false ) -D:\doctest\scripts\playground\test.cpp(12): ERROR: FAST_CHECK_UNARY( false ) is NOT correct! - values: FAST_CHECK_UNARY( false ) -D:\doctest\scripts\playground\test.cpp(13): ERROR: FAST_CHECK_UNARY_FALSE( true ) is NOT correct! - values: FAST_CHECK_UNARY_FALSE( true ) -D:\doctest\scripts\playground\test.cpp(15): ERROR: CHECK_EQ( true, false ) is NOT correct! - values: CHECK_EQ( true, false ) -D:\doctest\scripts\playground\test.cpp(16): ERROR: CHECK_UNARY( false ) is NOT correct! - values: CHECK_UNARY( false ) -D:\doctest\scripts\playground\test.cpp(17): ERROR: CHECK_UNARY_FALSE( true ) is NOT correct! - values: CHECK_UNARY_FALSE( true ) -D:\doctest\scripts\playground\test.cpp(19): ERROR: CHECK( false ) is NOT correct! - values: CHECK( false ) -D:\doctest\scripts\playground\test.cpp(20): ERROR: CHECK_THROWS( ((void)false) ) did NOT throw at all! -[doctest] run with "--help" for options -=============================================================================== -test.cpp(0): -TEST CASE: some test case lololo! - -test.cpp(0): ERROR: FAST_CHECK_EQ( true, false ) is NOT correct! - values: FAST_CHECK_EQ( true, false ) - -test.cpp(0): ERROR: FAST_CHECK_UNARY( false ) is NOT correct! - values: FAST_CHECK_UNARY( false ) - -test.cpp(0): ERROR: FAST_CHECK_UNARY_FALSE( true ) is NOT correct! - values: FAST_CHECK_UNARY_FALSE( true ) - -test.cpp(0): ERROR: CHECK_EQ( true, false ) is NOT correct! - values: CHECK_EQ( true, false ) - -test.cpp(0): ERROR: CHECK_UNARY( false ) is NOT correct! - values: CHECK_UNARY( false ) - -test.cpp(0): ERROR: CHECK_UNARY_FALSE( true ) is NOT correct! - values: CHECK_UNARY_FALSE( true ) - -test.cpp(0): ERROR: CHECK( false ) is NOT correct! - values: CHECK( false ) - -test.cpp(0): ERROR: CHECK_THROWS( ((void)false) ) did NOT throw at all! - -=============================================================================== -[doctest] test cases: 1 | 0 passed | 1 failed | 0 skipped -[doctest] assertions: 8 | 0 passed | 8 failed | -[doctest] Status: FAILURE! diff --git a/scripts/random_dev_notes.md b/scripts/random_dev_notes.md index 9d53a4c9..149798b6 100644 --- a/scripts/random_dev_notes.md +++ b/scripts/random_dev_notes.md @@ -67,9 +67,7 @@ thread sanitizer tests coverage documentation... :( - also faq - -profit :D + also update faq - differences with catch, thread safety further improvement of compile and link times - moved the body of absolutely every non-templated function out of the interface part of the header and into the implementation part (including implicitly generated special member functions!) (also using anonymous namespaces for most of the test runner) @@ -78,33 +76,12 @@ ask in the reddit thread what from the roadmap they would like to see next -OMG!!! THIS!!! -https://github.com/onqtam/doctest/issues/114 -isRunningInTest() changed to is_running_in_test - a bool... for performance reasons - relates #56 - - - -header with extensions - - demangling - - stringification of types from std - - esoteric reporters - - - make sure all relevant parts of the reporters can be accessed from the doctest namespace without the need for detail fix discoverability of subcases -add LIKELY & friends for the conditions of asserts - look at BOOST_LIKELY, ppk_assert, foonathan/debug_assert, etc - -try this for silencing unused variables & stuff when disabled: (void)(true ? (void)0 : ((void)(expression))) - -user command line options...? - https://github.com/catchorg/Catch2/commit/de36b2ada6e4593a9a32c4c86cd47d4bc002b148 -rethink static code analysis suppressions - users shouldn't have to use the same flags for code which uses doctest macros/types - try to forward declare std::string and specialize the string maker for it or something like that https://github.com/philsquared/Catch/issues/980 |