From 07500940f2a7510de1efa8d154746d6a6186d8de Mon Sep 17 00:00:00 2001 From: "Maarten L. Hekkelman" Date: Fri, 17 Nov 2023 19:36:45 +0100 Subject: [PATCH] Replace Boost.Test with Catch2 --- .github/workflows/cmake-multi-platform.yml | 16 - CMakeLists.txt | 18 +- changelog | 1 + test/unit-test.cpp | 325 +++++++++++---------- 4 files changed, 189 insertions(+), 171 deletions(-) diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index 86aa542..3b057a3 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -54,23 +54,7 @@ jobs: run: | echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" - - name: Install dependencies Ubuntu - if: matrix.os == 'ubuntu-latest' - run: > - sudo apt-get update && sudo apt-get install libboost-dev - - - name: Install Boost Windows - if: matrix.os == 'windows-latest' - uses: MarkusJx/install-boost@v2.4.4 - id: install-boost - with: - boost_version: 1.82.0 - platform_version: 2019 - toolset: msvc - - name: Configure CMake - env: - BOOST_ROOT: ${{ steps.install-boost.outputs.BOOST_ROOT }} run: > cmake -B ${{ steps.strings.outputs.build-output-dir }} -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} diff --git a/CMakeLists.txt b/CMakeLists.txt index b6e0dfe..c2a2d73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -129,11 +129,23 @@ install( DESTINATION ${CONFIG_LOC}) if(BUILD_TESTING) - find_package(Boost REQUIRED) + find_package(Catch2 3 QUIET) + + if(NOT Catch2_FOUND) + Include(FetchContent) + + FetchContent_Declare( + Catch2 + GIT_REPOSITORY https://github.com/catchorg/Catch2.git + GIT_TAG v3.4.0 # or a later release + ) + + FetchContent_MakeAvailable(Catch2) + endif() add_executable(libmcfp-unit-test ${PROJECT_SOURCE_DIR}/test/unit-test.cpp) - target_link_libraries(libmcfp-unit-test libmcfp::libmcfp Boost::boost) + target_link_libraries(libmcfp-unit-test libmcfp::libmcfp Catch2::Catch2) if(MSVC) # Specify unwind semantics so that MSVC knowns how to handle exceptions @@ -141,7 +153,7 @@ if(BUILD_TESTING) endif() add_test(NAME libmcfp-unit-test - COMMAND $ -- ${PROJECT_SOURCE_DIR}/test) + COMMAND $ --work-dir ${PROJECT_SOURCE_DIR}/test) endif() if(BUILD_DOCUMENTATION) diff --git a/changelog b/changelog index 832710a..55e724d 100644 --- a/changelog +++ b/changelog @@ -1,5 +1,6 @@ Version 1.2.5 - Replace test for _MSC_VER with more generic _WIN32 +- Replace Boost.Test with Catch2 Version 1.2.4 - Simpler get (added a version without template arguments) diff --git a/test/unit-test.cpp b/test/unit-test.cpp index 557f737..2791c5b 100644 --- a/test/unit-test.cpp +++ b/test/unit-test.cpp @@ -24,33 +24,54 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define BOOST_TEST_ALTERNATIVE_INIT_API -#include +#include +#include +#include #include #include -namespace tt = boost::test_tools; -namespace utf = boost::unit_test; namespace fs = std::filesystem; fs::path gTestDir = fs::current_path(); // -------------------------------------------------------------------- -bool init_unit_test() +// bool init_unit_test() +// { +// // not a test, just initialize test dir +// if (boost::unit_test::framework::master_test_suite().argc == 2) +// gTestDir = boost::unit_test::framework::master_test_suite().argv[1]; + +// return true; +// } + +int main(int argc, char *argv[]) { - // not a test, just initialize test dir - if (boost::unit_test::framework::master_test_suite().argc == 2) - gTestDir = boost::unit_test::framework::master_test_suite().argv[1]; + Catch::Session session; // There must be exactly one instance + + // Build a new parser on top of Catch2's + using namespace Catch::Clara; + auto cli = session.cli() // Get Catch2's command line parser + | Opt(gTestDir, "work-dir") // bind variable to a new option, with a hint string + ["-d"]["--work-dir"] // the option names it will respond to + ("Test directory"); // description string for the help output + + // Now pass the new composite back to Catch2 so it uses that + session.cli(cli); - return true; + // Let Catch2 (using Clara) parse the command line + int returnCode = session.applyCommandLine(argc, argv); + if (returnCode != 0) // Indicates a command line error + return returnCode; + + return session.run(); } // -------------------------------------------------------------------- -BOOST_AUTO_TEST_CASE(t_1, * utf::tolerance(0.001)) +TEST_CASE("t_1") { int argc = 3; const char *const argv[] = { @@ -66,22 +87,22 @@ BOOST_AUTO_TEST_CASE(t_1, * utf::tolerance(0.001)) mcfp::make_option("param_int_2", 1, ""), mcfp::make_option("param_float", ""), mcfp::make_option("param_float_2", 3.14f, "")); - + config.parse(argc, argv); - BOOST_CHECK(config.has("flag")); - BOOST_CHECK(not config.has("flag2")); + REQUIRE(config.has("flag")); + REQUIRE(config.has("flag2") == false); - BOOST_CHECK_EQUAL(config.get("param_int_2"), 1); - BOOST_CHECK_THROW(config.get("param_int_2"), std::system_error); - BOOST_CHECK_THROW(config.get("param_int"), std::system_error); + REQUIRE(config.get("param_int_2") == 1); + REQUIRE_THROWS_AS(config.get("param_int_2"), std::system_error); + REQUIRE_THROWS_AS(config.get("param_int"), std::system_error); - BOOST_TEST(config.get("param_float_2") == 3.14); - BOOST_CHECK_THROW(config.get("param_float_2"), std::system_error); - BOOST_CHECK_THROW(config.get("param_float"), std::system_error); + REQUIRE_THAT(config.get("param_float_2"), Catch::Matchers::WithinRel(3.14, 0.1)); + REQUIRE_THROWS_AS(config.get("param_float_2"), std::system_error); + REQUIRE_THROWS_AS(config.get("param_float"), std::system_error); } -BOOST_AUTO_TEST_CASE(t_2) +TEST_CASE("t_2") { int argc = 3; const char *const argv[] = { @@ -93,13 +114,13 @@ BOOST_AUTO_TEST_CASE(t_2) config.init( "test [options]", mcfp::make_option("verbose,v", "")); - + config.parse(argc, argv); - BOOST_CHECK_EQUAL(config.count("verbose"), 5); + REQUIRE(config.count("verbose") == 5); } -BOOST_AUTO_TEST_CASE(t_3) +TEST_CASE("t_3") { int argc = 2; const char *const argv[] = { @@ -111,14 +132,14 @@ BOOST_AUTO_TEST_CASE(t_3) config.init( "test [options]", mcfp::make_option("param_int", "")); - + config.parse(argc, argv); - BOOST_CHECK(config.has("param_int")); - BOOST_CHECK_EQUAL(config.get("param_int"), 42); + REQUIRE(config.has("param_int")); + REQUIRE(config.get("param_int") == 42); } -BOOST_AUTO_TEST_CASE(t_4) +TEST_CASE("t_4") { int argc = 3; const char *const argv[] = { @@ -130,115 +151,115 @@ BOOST_AUTO_TEST_CASE(t_4) config.init( "test [options]", mcfp::make_option("param_int", "")); - + config.parse(argc, argv); - BOOST_CHECK(config.has("param_int")); - BOOST_CHECK_EQUAL(config.get("param_int"), 42); + REQUIRE(config.has("param_int")); + REQUIRE(config.get("param_int") == 42); } -BOOST_AUTO_TEST_CASE(t_5) +TEST_CASE("t_5") { const char *const argv[] = { "test", "-i", "42", "-j43", nullptr }; - int argc = sizeof(argv) / sizeof(char*); - + int argc = sizeof(argv) / sizeof(char *); + auto &config = mcfp::config::instance(); config.init( "test [options]", mcfp::make_option("nr1,i", ""), mcfp::make_option("nr2,j", "")); - + config.parse(argc, argv); - BOOST_CHECK(config.has("nr1")); - BOOST_CHECK(config.has("nr2")); + REQUIRE(config.has("nr1")); + REQUIRE(config.has("nr2")); - BOOST_CHECK_EQUAL(config.get("nr1"), 42); - BOOST_CHECK_EQUAL(config.get("nr2"), 43); + REQUIRE(config.get("nr1") == 42); + REQUIRE(config.get("nr2") == 43); } -BOOST_AUTO_TEST_CASE(t_6) +TEST_CASE("t_6") { const char *const argv[] = { "test", "-i", "42", "-j43", "foo", "bar", nullptr }; - int argc = sizeof(argv) / sizeof(char*); - + int argc = sizeof(argv) / sizeof(char *); + auto &config = mcfp::config::instance(); config.init( "test [options]", mcfp::make_option("nr1,i", ""), mcfp::make_option("nr2,j", "")); - + config.parse(argc, argv); - BOOST_CHECK(config.has("nr1")); - BOOST_CHECK(config.has("nr2")); + REQUIRE(config.has("nr1")); + REQUIRE(config.has("nr2")); - BOOST_CHECK_EQUAL(config.get("nr1"), 42); - BOOST_CHECK_EQUAL(config.get("nr2"), 43); + REQUIRE(config.get("nr1") == 42); + REQUIRE(config.get("nr2") == 43); - BOOST_CHECK_EQUAL(config.operands().size(), 2); - BOOST_CHECK_EQUAL(config.operands().front(), "foo"); - BOOST_CHECK_EQUAL(config.operands().back(), "bar"); + REQUIRE(config.operands().size() == 2); + REQUIRE(config.operands().front() == "foo"); + REQUIRE(config.operands().back() == "bar"); } -BOOST_AUTO_TEST_CASE(t_7) +TEST_CASE("t_7") { const char *const argv[] = { "test", "--", "-i", "42", "-j43", "foo", "bar", nullptr }; - int argc = sizeof(argv) / sizeof(char*) - 1; - + int argc = sizeof(argv) / sizeof(char *) - 1; + auto &config = mcfp::config::instance(); config.init( "test [options]", mcfp::make_option("nr1,i", ""), mcfp::make_option("nr2,j", "")); - + config.parse(argc, argv); - BOOST_CHECK(not config.has("nr1")); - BOOST_CHECK(not config.has("nr2")); + REQUIRE(not config.has("nr1")); + REQUIRE(not config.has("nr2")); - BOOST_CHECK_EQUAL(config.operands().size(), 5); + REQUIRE(config.operands().size() == 5); auto compare = std::vector{ argv[2], argv[3], argv[4], argv[5], argv[6] }; - BOOST_CHECK(config.operands() == compare); + REQUIRE(config.operands() == compare); } -BOOST_AUTO_TEST_CASE(t_8) +TEST_CASE("t_8") { const char *const argv[] = { "test", "-i", "foo", "-jbar", nullptr }; - int argc = sizeof(argv) / sizeof(char*) - 1; - + int argc = sizeof(argv) / sizeof(char *) - 1; + auto &config = mcfp::config::instance(); config.init( "test [options]", - mcfp::make_option("i", ""), + mcfp::make_option("i", ""), mcfp::make_option("j", ""), mcfp::make_option("k", "baz", "")); - + config.parse(argc, argv); - BOOST_CHECK(config.has("i")); - BOOST_CHECK_EQUAL(config.get("i"), "foo"); - BOOST_CHECK(config.has("j")); - BOOST_CHECK_EQUAL(config.get("j"), "bar"); + REQUIRE(config.has("i")); + REQUIRE(config.get("i") == "foo"); + REQUIRE(config.has("j")); + REQUIRE(config.get("j") == "bar"); - BOOST_CHECK(config.has("k")); - BOOST_CHECK_EQUAL(config.get("k"), "baz"); + REQUIRE(config.has("k")); + REQUIRE(config.get("k") == "baz"); } -BOOST_AUTO_TEST_CASE(t_9) +TEST_CASE("t_9") { auto &config = mcfp::config::instance(); @@ -246,33 +267,33 @@ BOOST_AUTO_TEST_CASE(t_9) config.init( "test [options]", - mcfp::make_option("i", "First option"), + mcfp::make_option("i", "First option"), mcfp::make_option("j", "This is the second option"), mcfp::make_option("a-very-long-option-name,k", "baz", "And, you guessed it, this must be option three.")); - -// std::stringstream ss; -// int fd = open("/dev/null", O_RDWR); -// dup2(fd, STDOUT_FILENO); + // std::stringstream ss; -// ss << config << std::endl; + // int fd = open("/dev/null", O_RDWR); + // dup2(fd, STDOUT_FILENO); -// const char kExpected[] = R"(usage: test [options] -// -i arg First option -// -j arg This is the second option -// -k [ --a-very-long-option-name ] arg (=baz) -// And, you guessed it, this must be -// option three. + // ss << config << std::endl; -// )"; + // const char kExpected[] = R"(usage: test [options] + // -i arg First option + // -j arg This is the second option + // -k [ --a-very-long-option-name ] arg (=baz) + // And, you guessed it, this must be + // option three. -// std::cerr << '>' << kExpected << '<' << std::endl; -// std::cerr << '>' << ss.str() << '<' << std::endl; + // )"; -// BOOST_CHECK_EQUAL(ss.str(), kExpected); + // std::cerr << '>' << kExpected << '<' << std::endl; + // std::cerr << '>' << ss.str() << '<' << std::endl; + + // REQUIRE(ss.str() == kExpected); } -BOOST_AUTO_TEST_CASE(t_10) +TEST_CASE("t_10") { std::string s1 = R"(SPDX-License-Identifier: BSD-2-Clause @@ -292,8 +313,8 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND for (auto line : ww) os << line << std::endl; - - BOOST_CHECK_EQUAL(os.str(), R"(SPDX-License-Identifier: BSD-2-Clause + + REQUIRE(os.str() == R"(SPDX-License-Identifier: BSD-2-Clause Copyright (c) 2022 Maarten L. Hekkelman @@ -320,92 +341,92 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. )"); } -BOOST_AUTO_TEST_CASE(t_11) +TEST_CASE("t_11") { const char *const argv[] = { "test", "-faap", "-fnoot", "-fmies", nullptr }; - int argc = sizeof(argv) / sizeof(char*) - 1; + int argc = sizeof(argv) / sizeof(char *) - 1; auto &config = mcfp::config::instance(); config.init( "test [options]", mcfp::make_option>("file,f", "")); - + config.parse(argc, argv); - BOOST_CHECK_EQUAL(config.count("file"), 3); - + REQUIRE(config.count("file") == 3); + std::vector files = config.get>("file"); - BOOST_CHECK_EQUAL(files.size(), 3); - BOOST_CHECK_EQUAL(files[0], "aap"); - BOOST_CHECK_EQUAL(files[1], "noot"); - BOOST_CHECK_EQUAL(files[2], "mies"); + REQUIRE(files.size() == 3); + REQUIRE(files[0] == "aap"); + REQUIRE(files[1] == "noot"); + REQUIRE(files[2] == "mies"); } -BOOST_AUTO_TEST_CASE(t_12) +TEST_CASE("t_12") { const char *const argv[] = { "test", "--aap", nullptr }; - int argc = sizeof(argv) / sizeof(char*) - 1; + int argc = sizeof(argv) / sizeof(char *) - 1; auto &config = mcfp::config::instance(); config.init( "test [options]", mcfp::make_option>("file,f", "")); - + std::error_code ec; config.parse(argc, argv, ec); - BOOST_CHECK(ec == mcfp::config_error::unknown_option); + REQUIRE(ec == mcfp::config_error::unknown_option); config.set_ignore_unknown(true); ec = {}; config.parse(argc, argv, ec); - BOOST_CHECK(not ec); + REQUIRE(not ec); } -BOOST_AUTO_TEST_CASE(t_13) +TEST_CASE("t_13") { const char *const argv[] = { "test", "--test=bla", nullptr }; - int argc = sizeof(argv) / sizeof(char*) - 1; + int argc = sizeof(argv) / sizeof(char *) - 1; auto &config = mcfp::config::instance(); config.init( "test [options]", mcfp::make_option("test", "")); - - BOOST_CHECK_NO_THROW(config.parse(argc, argv)); - BOOST_TEST(config.has("test")); - BOOST_TEST(config.get("test") == "bla"); + REQUIRE_NOTHROW(config.parse(argc, argv)); + + REQUIRE(config.has("test")); + REQUIRE(config.get("test") == "bla"); } -BOOST_AUTO_TEST_CASE(t_14) +TEST_CASE("t_14") { const char *const argv[] = { "test", "-test=bla", nullptr }; - int argc = sizeof(argv) / sizeof(char*) - 1; + int argc = sizeof(argv) / sizeof(char *) - 1; auto &config = mcfp::config::instance(); config.init( "test [options]", mcfp::make_option("test", "")); - - BOOST_CHECK_THROW(config.parse(argc, argv), std::system_error); + + REQUIRE_THROWS_AS(config.parse(argc, argv), std::system_error); } // -------------------------------------------------------------------- -BOOST_AUTO_TEST_CASE(file_1, * utf::tolerance(0.001)) +TEST_CASE("file_1") { const std::string_view config_file{ R"( # This is a test configuration @@ -419,7 +440,7 @@ verbose struct membuf : public std::streambuf { - membuf(char * text, size_t length) + membuf(char *text, size_t length) { this->setg(text, text, text + length); } @@ -431,51 +452,51 @@ verbose config.init( "test [options]", - mcfp::make_option("aap", ""), + mcfp::make_option("aap", ""), mcfp::make_option("noot", ""), mcfp::make_option("mies", ""), mcfp::make_option("pi", ""), mcfp::make_option("s", ""), mcfp::make_option("verbose,v", "")); - + std::error_code ec; config.parse_config_file(is, ec); - BOOST_CHECK(not ec); + REQUIRE(not ec); - BOOST_CHECK(config.has("aap")); - BOOST_CHECK_EQUAL(config.get("aap"), "1"); + REQUIRE(config.has("aap")); + REQUIRE(config.get("aap") == "1"); - BOOST_CHECK(config.has("noot")); - BOOST_CHECK_EQUAL(config.get("noot"), 2); + REQUIRE(config.has("noot")); + REQUIRE(config.get("noot") == 2); - BOOST_CHECK(config.has("pi")); - BOOST_TEST(config.get("pi") == 3.14); + REQUIRE(config.has("pi")); + REQUIRE_THAT(config.get("pi"), Catch::Matchers::WithinRel(3.14, 0.1)); - BOOST_CHECK(config.has("s")); - BOOST_CHECK_EQUAL(config.get("s"), "hello, world!"); + REQUIRE(config.has("s")); + REQUIRE(config.get("s") == "hello, world!"); - BOOST_CHECK(config.has("verbose")); + REQUIRE(config.has("verbose")); } -BOOST_AUTO_TEST_CASE(file_2) +TEST_CASE("file_2") { auto &config = mcfp::config::instance(); - std::tuple tests[] = { + std::tuple tests[] = { { "aap !", "aap", make_error_code(mcfp::config_error::invalid_config_file) }, { "aap=aap", "aap", {} }, { "aap", "aap", make_error_code(mcfp::config_error::missing_argument_for_option) }, { "verbose=1", "verbose", make_error_code(mcfp::config_error::option_does_not_accept_argument) }, - + }; for (const auto &[config_file, option, err] : tests) { struct membuf : public std::streambuf { - membuf(char * text, size_t length) + membuf(char *text, size_t length) { this->setg(text, text, text + length); } @@ -486,77 +507,77 @@ BOOST_AUTO_TEST_CASE(file_2) std::error_code ec; config.init( "test [options]", - mcfp::make_option("aap", ""), + mcfp::make_option("aap", ""), mcfp::make_option("noot", ""), mcfp::make_option("pi", ""), mcfp::make_option("s", ""), mcfp::make_option("verbose,v", "")); - + config.parse_config_file(is, ec); - BOOST_CHECK(ec == err); + REQUIRE(ec == err); if (ec == std::errc()) - BOOST_CHECK(config.has(option)); + REQUIRE(config.has(option)); } } -BOOST_AUTO_TEST_CASE(file_3) +TEST_CASE("file_3") { auto &config = mcfp::config::instance(); config.init( "test [options]", - mcfp::make_option("aap", ""), + mcfp::make_option("aap", ""), mcfp::make_option("noot", ""), mcfp::make_option("config", "")); - + std::error_code ec; const char *const argv[] = { "test", "--aap=aap", "--noot=42", "--config=unit-test.conf", nullptr }; - int argc = sizeof(argv) / sizeof(char*) - 1; + int argc = sizeof(argv) / sizeof(char *) - 1; config.parse(argc, argv); config.parse_config_file("config", "bla-bla.conf", { gTestDir.string() }, ec); - BOOST_CHECK(not ec); + REQUIRE(not ec); - BOOST_CHECK(config.has("aap")); - BOOST_CHECK_EQUAL(config.get("aap"), "aap"); + REQUIRE(config.has("aap")); + REQUIRE(config.get("aap") == "aap"); - BOOST_CHECK(config.has("noot")); - BOOST_CHECK_EQUAL(config.get("noot"), 42); + REQUIRE(config.has("noot")); + REQUIRE(config.get("noot") == 42); } -BOOST_AUTO_TEST_CASE(file_4) +TEST_CASE("file_4") { auto &config = mcfp::config::instance(); config.init( "test [options]", - mcfp::make_option("aap", ""), + mcfp::make_option("aap", ""), mcfp::make_option("noot", ""), mcfp::make_option("config", "")); - + std::error_code ec; const char *const argv[] = { "test", "--aap=aap", nullptr }; - int argc = sizeof(argv) / sizeof(char*) - 1; + int argc = sizeof(argv) / sizeof(char *) - 1; config.parse(argc, argv); config.parse_config_file("config", "unit-test.conf", { gTestDir.string() }, ec); - BOOST_CHECK(not ec); + REQUIRE(not ec); - BOOST_CHECK(config.has("aap")); - BOOST_CHECK_EQUAL(config.get("aap"), "aap"); + REQUIRE(config.has("aap")); + REQUIRE(config.get("aap") == "aap"); - BOOST_CHECK(config.has("noot")); - BOOST_CHECK_EQUAL(config.get("noot"), 3); + REQUIRE(config.has("noot")); + REQUIRE(config.get("noot") == 3); } \ No newline at end of file