diff --git a/doc/doctest.md b/doc/doctest.md new file mode 100644 index 00000000..4f0b7c7f --- /dev/null +++ b/doc/doctest.md @@ -0,0 +1,55 @@ +# Doctest integration + +rapidcheck comes with a basic integration for the [doctest](https://github.com/doctest/doctest) library. + +## Usage + +This support is available through the `extras/doctest` module. You can either +add the `extras/doctest/include` directory directly to your include path: + +```cmake +add_subdirectory(rapidcheck) +set include_directories(rapidcheck/extras/doctest/include) +add_executable(MyTest main.cpp) +``` + +...or else link against the `rapidcheck_doctest` cmake target: + +```cmake +add_subdirectory(rapidcheck) +add_executable(MyTest main.cpp) +target_link_libraries(MyTest rapidcheck_doctest) +``` + +Either way, you can then write: + +```cpp +#include +#include "rapidcheck.h" +#include "rapidcheck/doctest.h' +``` + +## Reference + +### `rc::doctest::check("My test description", []{return true;}, /*verbose=*/true)` + +The `rc::doctest::check` function is a drop-in replacement for `rc::check` that reports its success or failure to the `doctest` test runner for inclusion in the statistics gathered for a test run. + +The third parameter is optional and defaults to `false`. + +```cpp +TEST_CASE("001: My first test case") +{ + rc::doctest::check("integer addition is commutative", + [](int a, int b) + { + return a + b == b + a); // true for success, false for failure + }); + + // no problem mixing rapidcheck tests with other doctest assertions + SUB_CASE("Normal doctest stuff") + { + REQUIRE(1 == 1); + } +} +``` \ No newline at end of file diff --git a/extras/CMakeLists.txt b/extras/CMakeLists.txt index d3316486..16fb262a 100644 --- a/extras/CMakeLists.txt +++ b/extras/CMakeLists.txt @@ -1,4 +1,4 @@ -# Since +# Since option(RC_INSTALL_ALL_EXTRAS "Add all possible integrations without requiring the initialization of all the submodules in ext." OFF) @@ -7,6 +7,11 @@ if (RC_ENABLE_CATCH OR RC_ENABLE_TESTS OR RC_INSTALL_ALL_EXTRAS) add_subdirectory(catch) endif() +option(RC_ENABLE_DOCTEST "Build DocTest support" OFF) +if (RC_ENABLE_DOCTEST OR RC_ENABLE_TESTS OR RC_INSTALL_ALL_EXTRAS) + add_subdirectory(doctest) +endif() + option(RC_ENABLE_GMOCK "Build Google Mock integration" OFF) if (RC_ENABLE_GMOCK OR RC_INSTALL_ALL_EXTRAS) add_subdirectory(gmock) diff --git a/extras/doctest/CMakeLists.txt b/extras/doctest/CMakeLists.txt new file mode 100644 index 00000000..9c41d913 --- /dev/null +++ b/extras/doctest/CMakeLists.txt @@ -0,0 +1,11 @@ +add_library(rapidcheck_doctest INTERFACE) +target_link_libraries(rapidcheck_doctest INTERFACE rapidcheck) +target_include_directories(rapidcheck_doctest INTERFACE + $ + $ +) + +# An INTERFACE library does not need to install anything but its headers +# and information on its targets. +install(TARGETS rapidcheck_doctest EXPORT rapidcheckConfig) +install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) diff --git a/extras/doctest/include/rapidcheck/doctest.h b/extras/doctest/include/rapidcheck/doctest.h new file mode 100644 index 00000000..65229cd1 --- /dev/null +++ b/extras/doctest/include/rapidcheck/doctest.h @@ -0,0 +1,109 @@ +#pragma once + +#include +#include + +#include + +namespace rc::doctest { + +/** + * Checks the given predicate by applying it to randomly generated arguments. + * + * Quotes the given description string if the predicate can be falsified. + * + * Traces a progress message to 'stdout' if the flag 'v' is true. + * + * Like the function 'rc::check', but integrates with 'doctest' to include its + * result in the statistics that are gathered for a test run. + * + * For example: + * + * TEST_CASE("addition is commutative") + * { + * wol::test::check("a+b == b+a", [](int a, int b) { return a+b == b+a; }); + * } + * + * @param d A description of the predicate being checked. + * @param t A predicate to check. + * @param v A flag requesting verbose output. + * + * @see https://github.com/emil-e/rapidcheck/blob/master/doc/properties.md + * for more on 'rc::check', on which this function is modeled. + * + * @see https://github.com/emil-e/rapidcheck/blob/master/doc/catch.md + * for more on the integration of 'rapidcheck' and 'catch', on which + * this implementation is based. + */ +template +void check(const char* d, + testable&& t, + bool v = false, + std::source_location s = std::source_location::current()) +{ + using namespace rc::detail; + using namespace doctest::detail; + + DOCTEST_SUBCASE(d) + { + auto r = checkTestable(std::forward(t)); + + if (r.template is()) + { + if (!r.template get().distribution.empty() || v) + { + std::cout << "- " << d << std::endl; + printResultMessage(r, std::cout); + std::cout << std::endl; + } + + REQUIRE(true); + } + else + { + std::ostringstream o; + printResultMessage(r, o << '\n'); + DOCTEST_INFO(o.str()); + ResultBuilder b(doctest::assertType::DT_CHECK, s.file_name(), s.line(), s.function_name()); + DOCTEST_ASSERT_LOG_REACT_RETURN(b); + } + } +} + +/** + * Checks the given predicate by applying it to randomly generated arguments. + * + * Quotes the given description string if the predicate can be falsified. + * + * Traces a progress message to 'stdout' if the flag 'v' is true. + * + * Like the function 'rc::check', but integrates with 'doctest' to include its + * result in the statitics that are gathered for a test run. + * + * For example: + * + * TEST_CASE("addition is commutative") + * { + * wol::test::check("a+b == b+a", [](int a, int b) { return a+b == b+a; }); + * } + * + * @param t A predicate to check. + * @param v A flag requesting verbose output. + * + * @see https://github.com/emil-e/rapidcheck/blob/master/doc/properties.md + * for more on 'rc::check', on which this function is modeled. + * + * @see https://github.com/emil-e/rapidcheck/blob/master/doc/catch.md + * for more on the integration of 'rapidcheck' and 'catch', on which + * this implementation is based. + */ +template +inline +void check(testable&& t, + bool v = false, + std::source_location s = std::source_location::current()) +{ + check("", t, v ,s); +} + +} // namespace rc::doctest