From adb3d2eb37366842e342165b84aa42f88caec70d Mon Sep 17 00:00:00 2001 From: hobovsky Date: Mon, 22 Jan 2024 01:27:29 +0100 Subject: [PATCH 01/26] Test possibilities of Catch2 reporter --- catch2/tests/codewars_reporter.cpp | 57 ++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/catch2/tests/codewars_reporter.cpp b/catch2/tests/codewars_reporter.cpp index d0e0d6a..f6ac77f 100644 --- a/catch2/tests/codewars_reporter.cpp +++ b/catch2/tests/codewars_reporter.cpp @@ -1,17 +1,28 @@ -#include -#include -#include -#include -#include - #include +#include + // TODO Complete custom reporter // - https://github.com/catchorg/Catch2/blob/devel/docs/reporters.md // - https://github.com/catchorg/Catch2/blob/devel/docs/reporter-events.md // - https://github.com/catchorg/Catch2/blob/devel/docs/test-cases-and-sections.md class CodewarsReporter : public Catch::StreamingReporterBase { + +private: + bool inTest = false; + public: + + CodewarsReporter(Catch::ReporterConfig&& config) : + StreamingReporterBase{ CATCH_MOVE(config) } { + + //m_preferences.shouldRedirectStdOut = true; + // TBD: Do we want to report all assertions? XML reporter does + // not, but for machine-parseable reporters I think the answer + // should be yes. + m_preferences.shouldReportAllAssertions = true; + } + using StreamingReporterBase::StreamingReporterBase; static std::string getDescription() { @@ -19,39 +30,55 @@ class CodewarsReporter : public Catch::StreamingReporterBase { } // Emitted before the first test case is executed. - void testRunStarting(Catch::TestRunInfo const& testRunInfo) override { + void testRunStarting(Catch::TestRunInfo const& testRunInfo) override { std::cout << "testRunStarting " << testRunInfo.name << '\n'; } // Emitted after all the test cases have been executed. - void testRunEnded(__attribute__((unused)) Catch::TestRunStats const& testRunStats) override { + void testRunEnded( Catch::TestRunStats const& testRunStats) override { std::cout << "testRunEnded\n"; } - void sectionStarting(__attribute__((unused)) Catch::SectionInfo const& sectionInfo) override { + void sectionStarting( Catch::SectionInfo const& sectionInfo) override { // Cannot be used for group? Each testcase has implicit section. + if (inTest) return; std::cout << "\n" << sectionInfo.name << '\n'; } - void sectionEnded(__attribute__((unused)) Catch::SectionStats const& sectionStats) override { + void sectionEnded( Catch::SectionStats const& sectionStats) override { + if (inTest) return; std::cout << "\n\n"; } - void testCaseStarting(__attribute__((unused)) Catch::TestCaseInfo const& testInfo) override { - std::cout << "\n" << testInfo.name << '\n'; + void testCaseStarting( Catch::TestCaseInfo const& testInfo) override { + if (inTest) return; + std::cout << "\n" << testInfo.name << '\n'; } - void testCaseEnded(__attribute__((unused)) Catch::TestCaseStats const& testCaseStats) override { + void testCaseEnded( Catch::TestCaseStats const& testCaseStats) override { + if (inTest) return; std::cout << "\n\n"; } void testCasePartialStarting(Catch::TestCaseInfo const& testInfo, uint64_t partNumber) override { - std::cout << "TestCaseStartingPartial: " << testInfo.name << '#' << partNumber << '\n'; + std::cout << "\n" << testInfo.name << '#' << partNumber << '\n'; + // inTest = true; } void testCasePartialEnded(Catch::TestCaseStats const& testCaseStats, uint64_t partNumber) override { - std::cout << "TestCasePartialEnded: " << testCaseStats.testInfo->name << '#' << partNumber << '\n'; + if (inTest) return; + std::cout << "\n\n"; + } + + void assertionStarting(Catch::AssertionInfo const& info) override { + std::cout << "\nAssertion" << '\n'; } + void assertionEnded(Catch::AssertionStats const& statss) override { + auto stats = statss.assertionResult; + std::cout << '\n' << (stats.isOk() ? " " : "") << (stats.hasMessage() ? stats.getMessage() : (stats.isOk() ? "Test passed" : "Test failed")) << '\n'; + std::cout << "\n\n"; + } + }; From a422cfdc0464ec4cbcf32b80a06a2509d6db1945 Mon Sep 17 00:00:00 2001 From: hobovsky Date: Mon, 22 Jan 2024 01:28:12 +0100 Subject: [PATCH 02/26] CW preprocessor for solution and preloaded snippets --- gtest/preprocessor/cwpp.cpp | 80 +++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 gtest/preprocessor/cwpp.cpp diff --git a/gtest/preprocessor/cwpp.cpp b/gtest/preprocessor/cwpp.cpp new file mode 100644 index 0000000..233c300 --- /dev/null +++ b/gtest/preprocessor/cwpp.cpp @@ -0,0 +1,80 @@ +#include +#include +#include +#include + +using std::filesystem::exists; +using std::filesystem::path; + +int main(int argc, char* argv[]) +{ + if (argc != 3) { + std::cerr << "Usage:\n\tcwpp [PRELOADED|SOLUTION] [path to cw snippet]"; + return 1; + } + + std::string snippetTag = argv[1]; + if(snippetTag != "PRELOADED" && snippetTag != "SOLUTION") { + std::cerr << "Usage:\n\tcwpp [PRELOADED|SOLUTION] [path to cw snippet]"; + return 1; + } + + auto snippetFile = path(argv[2]); + if (!exists(snippetFile)) { + std::cerr << "Snippet file [" << snippetFile << "] does not exist."; + return 1; + } + std::ifstream inputStream(snippetFile); + std::ofstream headerStream(".tmp_header"), implStream(".tmp_cpp"); + + enum LastTag { NONE, HEADER, CPP }; + + bool hasHeaderTag = false; + bool hasImplTag = false; + LastTag lastTag = NONE; + + std::string headerTag = snippetTag == "SOLUTION" ? "//! CW_SOLUTION_H" : "//! CW_PRELOADED_H"; + std::string implTag = snippetTag == "SOLUTION" ? "//! CW_SOLUTION_CPP" : "//! CW_PRELOADED_CPP"; + + std::string line; + while (std::getline(inputStream, line)) { + if (line == headerTag) { + hasHeaderTag = true; + lastTag = HEADER; + } else if (line == implTag) { + hasImplTag = true; + lastTag = CPP; + } else if (lastTag == HEADER) { + headerStream << line << std::endl; + } else if (lastTag == CPP) { + implStream << line << std::endl; + } + } + inputStream.close(); + headerStream.close(); + implStream.close(); + + std::string targetHeader, targetImpl; + if (snippetTag == "SOLUTION") { + targetHeader = "include/solution.h"; + targetImpl = "src/solution.cpp"; + } + else { + targetHeader = "include/preloaded.h"; + targetImpl = "src/preloaded.cpp"; + } + + if (hasHeaderTag && hasImplTag) { + std::filesystem::copy_file(path(".tmp_header"), path(targetHeader)); + std::filesystem::copy_file(path(".tmp_cpp"), path(targetImpl)); + } else if (!hasHeaderTag && !hasImplTag) { + std::filesystem::copy_file(path(snippetFile), path(targetHeader)); + std::filesystem::copy_file(path(snippetFile), path(targetImpl)); + } else if (hasHeaderTag && !hasImplTag) { + std::filesystem::copy_file(path(".tmp_header"), path(targetHeader)); + std::ofstream(targetImpl).close(); + } else if (!hasHeaderTag && hasImplTag) { + std::ofstream(targetHeader).close(); + std::filesystem::copy_file(path(".tmp_cpp"), path(targetImpl)); + } +} From 30eac8c3614ab48a2a14878692d9af636cbad8ba Mon Sep 17 00:00:00 2001 From: hobovsky Date: Mon, 22 Jan 2024 01:28:57 +0100 Subject: [PATCH 03/26] Setup using preprocessed snippets --- gtest/include/challenge.h | 15 ----- gtest/snippets/preloaded | 0 gtest/snippets/solution | 22 +++++++ gtest/src/CMakeLists.txt | 5 +- gtest/src/solution.cpp | 16 ----- gtest/tests/CMakeLists.txt | 3 +- gtest/tests/main.cpp | 9 +++ gtest/tests/test_solution.cpp | 121 +++++++++++++++++++--------------- 8 files changed, 103 insertions(+), 88 deletions(-) delete mode 100644 gtest/include/challenge.h create mode 100644 gtest/snippets/preloaded create mode 100644 gtest/snippets/solution delete mode 100644 gtest/src/solution.cpp diff --git a/gtest/include/challenge.h b/gtest/include/challenge.h deleted file mode 100644 index 79722ce..0000000 --- a/gtest/include/challenge.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -// A simple monotonic counter. -class Counter { - private: - int counter_; - - public: - // Creates a counter that starts at 0. - Counter() : counter_(0) {} - // Returns the current counter value, and increments it. - int Increment(); - // Returns the current counter value, and decrements it. - int Decrement(); -}; diff --git a/gtest/snippets/preloaded b/gtest/snippets/preloaded new file mode 100644 index 0000000..e69de29 diff --git a/gtest/snippets/solution b/gtest/snippets/solution new file mode 100644 index 0000000..8d7c812 --- /dev/null +++ b/gtest/snippets/solution @@ -0,0 +1,22 @@ +//! CW_SOLUTION_H +#include +#include +#include + +template std::vector uniqueInOrder(const std::vector& iterable){ + std::vector res = iterable; + res.erase(std::unique(begin(res), end(res)), end(res)); + return res; +} +std::vector uniqueInOrder(const std::string& iterable); + +//! CW_SOLUTION_CPP +#include +#include +#include + +std::vector uniqueInOrder(const std::string& iterable){ + std::string res = iterable; + res.erase(std::unique(begin(res), end(res)), end(res)); + return std::vector{begin(res), end(res)}; +} \ No newline at end of file diff --git a/gtest/src/CMakeLists.txt b/gtest/src/CMakeLists.txt index bd71121..82e1f39 100644 --- a/gtest/src/CMakeLists.txt +++ b/gtest/src/CMakeLists.txt @@ -11,8 +11,9 @@ FetchContent_MakeAvailable(fmt) find_package(Threads REQUIRED) -add_library(challenge solution.cpp) +add_library(challenge solution.cpp preloaded.cpp) target_include_directories(challenge PUBLIC ../include) target_compile_features(challenge PUBLIC cxx_std_20) target_compile_options(challenge PUBLIC -Wall -Wextra -O2) -target_link_libraries(challenge PUBLIC m crypto dl sqlite3 tbb fmt Threads::Threads) +# target_link_libraries(challenge PUBLIC m crypto dl sqlite3 tbb fmt Threads::Threads) +target_link_libraries(challenge PUBLIC m dl fmt Threads::Threads) diff --git a/gtest/src/solution.cpp b/gtest/src/solution.cpp deleted file mode 100644 index 50991b8..0000000 --- a/gtest/src/solution.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include - -// Returns the current counter value, and increments it. -int Counter::Increment() { - return counter_++; -} - -// Returns the current counter value, and decrements it. -// counter can not be less than 0, return 0 in this case -int Counter::Decrement() { - if (counter_ == 0) { - return counter_; - } else { - return counter_--; - } -} diff --git a/gtest/tests/CMakeLists.txt b/gtest/tests/CMakeLists.txt index 138c297..1b39279 100644 --- a/gtest/tests/CMakeLists.txt +++ b/gtest/tests/CMakeLists.txt @@ -20,7 +20,8 @@ FetchContent_MakeAvailable(fmt) add_executable(tests main.cpp test_solution.cpp) target_compile_features(tests PRIVATE cxx_std_20) target_compile_options(tests PRIVATE -Wall -Wextra -O2) -target_link_libraries(tests PRIVATE challenge gtest gmock m crypto dl sqlite3 tbb fmt Threads::Threads) +# target_link_libraries(tests PRIVATE challenge gtest gmock m crypto dl sqlite3 tbb fmt Threads::Threads) +target_link_libraries(tests PRIVATE challenge gtest gmock m dl fmt Threads::Threads) # Not really necessary for CR, but allows running tests through CMake add_test(NAME tests COMMAND tests) diff --git a/gtest/tests/main.cpp b/gtest/tests/main.cpp index 59b3d1f..445acab 100644 --- a/gtest/tests/main.cpp +++ b/gtest/tests/main.cpp @@ -5,6 +5,7 @@ using ::testing::EmptyTestEventListener; using ::testing::InitGoogleTest; using ::testing::Test; using ::testing::TestCase; +using ::testing::TestSuite; using ::testing::TestEventListeners; using ::testing::TestInfo; using ::testing::TestResult; @@ -21,6 +22,14 @@ class QualifiedReporter : public EmptyTestEventListener { fflush(stdout); } + void OnTestSuiteStart(const TestSuite& test_suite) override { + fprintf(stdout, "%s", test_suite.name()); + } + void OnTestSuiteEnd(const TestSuite& test_suite) override { + fprintf(stdout, "\n%ld\n", test_suite.elapsed_time()); + fflush(stdout); + } + // TODO Group by `test_case_name`? It's not identifiers so uniqueness is not guaranteed. // Called before a test starts. void OnTestStart(const TestInfo& test_info) override { diff --git a/gtest/tests/test_solution.cpp b/gtest/tests/test_solution.cpp index 91b4615..ef66980 100644 --- a/gtest/tests/test_solution.cpp +++ b/gtest/tests/test_solution.cpp @@ -1,64 +1,77 @@ #include #include -#include -namespace { -TEST(Vector_size_resize, resizing_bigger_changes_size_and_capacity) { - std::vector v( 5 ); - EXPECT_EQ( v.size(), 5 ); - EXPECT_GE( v.capacity(), 5 ); +#include "solution.h" - v.resize( 10 ); - EXPECT_EQ( v.size(), 10 ); - EXPECT_GE( v.capacity(), 10 ); -} - -TEST(Vector_size_resize, resizing_smaller_changes_size_but_not_capacity) { - std::vector v( 5 ); - EXPECT_EQ( v.size(), 5 ); - EXPECT_GE( v.capacity(), 5 ); - v.resize( 0 ); - - EXPECT_EQ( v.size(), 0 ); - EXPECT_GE( v.capacity(), 5 ); -} - -TEST(Vector_size_resize, reserving_bigger_changes_capacity_but_not_size) { - std::vector v( 5 ); - EXPECT_EQ( v.size(), 5 ); - EXPECT_GE( v.capacity(), 5 ); - v.reserve( 10 ); - - EXPECT_EQ( v.size(), 5 ); - EXPECT_GE( v.capacity(), 10 ); -} +#include +#include +#include -TEST(Vector_size_resize, reserving_smaller_does_not_change_size_or_capacity) { - std::vector v( 5 ); - EXPECT_EQ( v.size(), 5 ); - EXPECT_GE( v.capacity(), 5 ); - v.reserve( 0 ); - - EXPECT_EQ( v.size(), 5 ); - EXPECT_GE( v.capacity(), 5 ); +auto randint(int min, int max) { + static std::random_device rd; + static std::mt19937 rng(rd()); + std::uniform_int_distribution uni(min, max); + return uni(rng); } -TEST(Counter, Increment) { // Tests the Increment() method. - Counter c; - - // Test that counter 0 returns 0 - EXPECT_EQ(0, c.Decrement()); - - // EXPECT_EQ() evaluates its arguments exactly once, so they - // can have side effects. - - EXPECT_EQ(0, c.Increment()); - EXPECT_EQ(1, c.Increment()); - EXPECT_EQ(2, c.Increment()); - - EXPECT_EQ(3, c.Decrement()); +std::string printVec(const std::vector& vec) { + std::string res = "'" + std::string(1, vec[0]) + "'"; + for (unsigned int i = 1; i < vec.size(); i++) res += ", '" + std::string(1, vec[i]) + "'"; + return res; } - - +std::string printVec(const std::vector& vec) { + std::string res = std::to_string(vec[0]); + for (unsigned int i = 1; i < vec.size(); i++) res += ", " + std::to_string(vec[i]); + return res; } +using VI = std::vector; +using VC = std::vector; + +TEST(UniqueInOrder, BasicTests) { + ASSERT_EQ(uniqueInOrder(""), VC{}); + ASSERT_EQ(uniqueInOrder("AA"), VC{ 'A' }); + ASSERT_EQ(uniqueInOrder("A"), VC{ 'A' }); + ASSERT_EQ(uniqueInOrder("AAAABBBCCDAABBB"), (VC{ 'A', 'B', 'C', 'D', 'A', 'B' })); + ASSERT_EQ(uniqueInOrder("AADD"), (VC{ 'A', 'D' })); + ASSERT_EQ(uniqueInOrder("AAD"), (VC{ 'A', 'D' })); + ASSERT_EQ(uniqueInOrder("ADD"), (VC{ 'A', 'D' })); + ASSERT_EQ(uniqueInOrder("ABBCcAD"), (VC{ 'A', 'B', 'C', 'c', 'A', 'D' })); + ASSERT_EQ(uniqueInOrder(VI{ 1,2,3,3 }), (VI{ 1,2,3 })); + ASSERT_EQ(uniqueInOrder(VC{ 'a','b','b' }), (VC{ 'a','b' })); +}; + template std::vector sol(const std::vector&iterable) { + std::vector res = iterable; + res.erase(std::unique(begin(res), end(res)), end(res)); + return res; + } + + std::vector sol(const std::string & iterable) { + std::string res = iterable; + res.erase(std::unique(begin(res), end(res)), end(res)); + return std::vector{begin(res), end(res)}; + } + TEST(UniqueInOrder, RandomTests) { + std::string base = "abcXYZ"; + for (unsigned int i = 0; i < 40; i++) { + unsigned int testCase = randint(0, 2), inputLen = randint(3, 40); + if (testCase == 0) { + std::string iterable; + for (; inputLen--;) iterable += base[randint(0, base.size() - 1)]; + std::cout << "Testing for '" << iterable << "'\n\n"; + ASSERT_EQ(uniqueInOrder(iterable), sol(iterable)); + } + else if (testCase == 1) { + std::vector iterable; + for (; inputLen--;) iterable.push_back(base[randint(0, base.size() - 1)]); + std::cout << "Testing for [" << printVec(iterable) << "]\n\n"; + ASSERT_EQ(uniqueInOrder(iterable), sol(iterable)); + } + else { + std::vector iterable; + for (; inputLen--;) iterable.push_back(randint(-5, 5)); + std::cout << "Testing for [" << printVec(iterable) << "]\n\n"; + ASSERT_EQ(uniqueInOrder(iterable), sol(iterable)); + } + } + }; \ No newline at end of file From 53a751786df29e208b881d7d4d87153c44a17743 Mon Sep 17 00:00:00 2001 From: hobovsky Date: Mon, 22 Jan 2024 01:29:19 +0100 Subject: [PATCH 04/26] Dockerfile - initial attempt --- gtest/Dockerfile | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 gtest/Dockerfile diff --git a/gtest/Dockerfile b/gtest/Dockerfile new file mode 100644 index 0000000..0c74854 --- /dev/null +++ b/gtest/Dockerfile @@ -0,0 +1,36 @@ +# FROM alpine:3.14 +# RUN apk add --no-cache clang binutils gtest + +FROM alpine:latest + +RUN apk update \ + && apk upgrade \ + && apk add --no-cache \ + clang \ + clang-dev \ + alpine-sdk \ + dpkg \ + cmake \ + ccache \ + gtest \ + gtest-dev \ + ninja + +RUN ln -sf /usr/bin/clang /usr/bin/cc \ + && ln -sf /usr/bin/clang++ /usr/bin/c++ \ + && update-alternatives --install /usr/bin/cc cc /usr/bin/clang 10\ + && update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 10\ + && update-alternatives --auto cc \ + && update-alternatives --auto c++ \ + && update-alternatives --display cc \ + && update-alternatives --display c++ \ + && ls -l /usr/bin/cc /usr/bin/c++ \ + && cc --version \ + && c++ --version + +WORKDIR /code +COPY . . + +# ENV APP_NAME='' + +# ENTRYPOINT ["/code/entrypoint.sh"] \ No newline at end of file From 766d0d9e1f6614d513200956636e09a46dda6546 Mon Sep 17 00:00:00 2001 From: hobovsky Date: Mon, 22 Jan 2024 11:13:05 +0100 Subject: [PATCH 05/26] Organization of snippets --- gtest/distribute-snippets.sh | 6 ++++++ gtest/snippets/{preloaded => preloaded.snippet} | 0 gtest/snippets/{solution => solution.snippet} | 0 .../test_solution.cpp => snippets/test_solution.snippet} | 0 4 files changed, 6 insertions(+) create mode 100644 gtest/distribute-snippets.sh rename gtest/snippets/{preloaded => preloaded.snippet} (100%) rename gtest/snippets/{solution => solution.snippet} (100%) rename gtest/{tests/test_solution.cpp => snippets/test_solution.snippet} (100%) diff --git a/gtest/distribute-snippets.sh b/gtest/distribute-snippets.sh new file mode 100644 index 0000000..ca03b74 --- /dev/null +++ b/gtest/distribute-snippets.sh @@ -0,0 +1,6 @@ +#!/bin/sh +cp snippets/test_solution.snippet tests/test_solution.cpp + +clang++ -o preprocessor/cwpp -O2 preprocessor/cwpp.cpp +./preprocessor/cwpp SOLUTION snippets/solution.snippet +./preprocessor/cwpp PRELOADED snippets/preloaded.snippet diff --git a/gtest/snippets/preloaded b/gtest/snippets/preloaded.snippet similarity index 100% rename from gtest/snippets/preloaded rename to gtest/snippets/preloaded.snippet diff --git a/gtest/snippets/solution b/gtest/snippets/solution.snippet similarity index 100% rename from gtest/snippets/solution rename to gtest/snippets/solution.snippet diff --git a/gtest/tests/test_solution.cpp b/gtest/snippets/test_solution.snippet similarity index 100% rename from gtest/tests/test_solution.cpp rename to gtest/snippets/test_solution.snippet From 5321634c7591aa47c6da4aff6ae2522aab6e6581 Mon Sep 17 00:00:00 2001 From: hobovsky Date: Mon, 22 Jan 2024 11:13:44 +0100 Subject: [PATCH 06/26] make scripts runnable in my docker --- gtest/Dockerfile | 3 --- gtest/configure.sh | 2 +- gtest/run-all.sh | 2 ++ gtest/run-tests.sh | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) create mode 100644 gtest/run-all.sh diff --git a/gtest/Dockerfile b/gtest/Dockerfile index 0c74854..4362f1c 100644 --- a/gtest/Dockerfile +++ b/gtest/Dockerfile @@ -1,6 +1,3 @@ -# FROM alpine:3.14 -# RUN apk add --no-cache clang binutils gtest - FROM alpine:latest RUN apk update \ diff --git a/gtest/configure.sh b/gtest/configure.sh index f9bd0b8..6646ddb 100755 --- a/gtest/configure.sh +++ b/gtest/configure.sh @@ -1,2 +1,2 @@ -#!/bin/bash +#!/bin/sh cmake -S . -B build -G Ninja diff --git a/gtest/run-all.sh b/gtest/run-all.sh new file mode 100644 index 0000000..4a4c79c --- /dev/null +++ b/gtest/run-all.sh @@ -0,0 +1,2 @@ +#!/bin/sh +./distribute-snippets.sh && ./configure.sh && ./run-tests.sh \ No newline at end of file diff --git a/gtest/run-tests.sh b/gtest/run-tests.sh index eead123..e680ada 100755 --- a/gtest/run-tests.sh +++ b/gtest/run-tests.sh @@ -1,3 +1,3 @@ -#!/bin/bash +#!/bin/sh cmake --build build && ./build/tests/tests From 0ae9f21fa9b4f0a45b2dd9e37f8de0abcc58dfce Mon Sep 17 00:00:00 2001 From: hobovsky Date: Mon, 22 Jan 2024 20:00:23 +0100 Subject: [PATCH 07/26] Organize examples of migrated kata --- gtest/README.md | 7 + .../preloaded.snippet | 15 +++ .../snippets-originalExample/solution.snippet | 16 +++ .../test_solution.snippet | 64 +++++++++ .../snippets-uniqueInOrder/preloaded.snippet | 0 gtest/snippets-uniqueInOrder/solution.snippet | 22 ++++ .../test_solution.snippet | 77 +++++++++++ gtest/snippets/preloaded.snippet | 15 +++ gtest/snippets/solution.snippet | 32 ++--- gtest/snippets/test_solution.snippet | 121 ++++++++---------- 10 files changed, 283 insertions(+), 86 deletions(-) create mode 100644 gtest/snippets-originalExample/preloaded.snippet create mode 100644 gtest/snippets-originalExample/solution.snippet create mode 100644 gtest/snippets-originalExample/test_solution.snippet create mode 100644 gtest/snippets-uniqueInOrder/preloaded.snippet create mode 100644 gtest/snippets-uniqueInOrder/solution.snippet create mode 100644 gtest/snippets-uniqueInOrder/test_solution.snippet diff --git a/gtest/README.md b/gtest/README.md index 0ce5b82..cf22d7d 100644 --- a/gtest/README.md +++ b/gtest/README.md @@ -8,3 +8,10 @@ cmake --build build && ./build/tests/tests Use `./configure.sh` and `./run-tests.sh`. We'll configure and build an example project while building the container image so each submission will only build the submitted code. + + +## Example kata + +The `snippets-*` directories contain showcases of kata which could be migrated to Google Test and the proposed organization of snippets: +- `snippets-originalExample` - the original example of Google Test tests prepared by kazk. +- `snippets-uniqueInOrder` - a kata which requires separate cpp file and header file for solution, because it requires users to implement two functions: one is a template, and one is a "normal" function. It uses CW preprocessor markers to split submitted solution snippet into two files. \ No newline at end of file diff --git a/gtest/snippets-originalExample/preloaded.snippet b/gtest/snippets-originalExample/preloaded.snippet new file mode 100644 index 0000000..5463ce5 --- /dev/null +++ b/gtest/snippets-originalExample/preloaded.snippet @@ -0,0 +1,15 @@ +#pragma once + +// A simple monotonic counter. +class Counter { + private: + int counter_; + + public: + // Creates a counter that starts at 0. + Counter() : counter_(0) {} + // Returns the current counter value, and increments it. + int Increment(); + // Returns the current counter value, and decrements it. + int Decrement(); +}; \ No newline at end of file diff --git a/gtest/snippets-originalExample/solution.snippet b/gtest/snippets-originalExample/solution.snippet new file mode 100644 index 0000000..50991b8 --- /dev/null +++ b/gtest/snippets-originalExample/solution.snippet @@ -0,0 +1,16 @@ +#include + +// Returns the current counter value, and increments it. +int Counter::Increment() { + return counter_++; +} + +// Returns the current counter value, and decrements it. +// counter can not be less than 0, return 0 in this case +int Counter::Decrement() { + if (counter_ == 0) { + return counter_; + } else { + return counter_--; + } +} diff --git a/gtest/snippets-originalExample/test_solution.snippet b/gtest/snippets-originalExample/test_solution.snippet new file mode 100644 index 0000000..69c1bb2 --- /dev/null +++ b/gtest/snippets-originalExample/test_solution.snippet @@ -0,0 +1,64 @@ +#include +#include +#include + +namespace { +TEST(Vector_size_resize, resizing_bigger_changes_size_and_capacity) { + std::vector v( 5 ); + EXPECT_EQ( v.size(), 5 ); + EXPECT_GE( v.capacity(), 5 ); + + v.resize( 10 ); + EXPECT_EQ( v.size(), 10 ); + EXPECT_GE( v.capacity(), 10 ); +} + +TEST(Vector_size_resize, resizing_smaller_changes_size_but_not_capacity) { + std::vector v( 5 ); + EXPECT_EQ( v.size(), 5 ); + EXPECT_GE( v.capacity(), 5 ); + v.resize( 0 ); + + EXPECT_EQ( v.size(), 0 ); + EXPECT_GE( v.capacity(), 5 ); +} + +TEST(Vector_size_resize, reserving_bigger_changes_capacity_but_not_size) { + std::vector v( 5 ); + EXPECT_EQ( v.size(), 5 ); + EXPECT_GE( v.capacity(), 5 ); + v.reserve( 10 ); + + EXPECT_EQ( v.size(), 5 ); + EXPECT_GE( v.capacity(), 10 ); +} + +TEST(Vector_size_resize, reserving_smaller_does_not_change_size_or_capacity) { + std::vector v( 5 ); + EXPECT_EQ( v.size(), 5 ); + EXPECT_GE( v.capacity(), 5 ); + v.reserve( 0 ); + + EXPECT_EQ( v.size(), 5 ); + EXPECT_GE( v.capacity(), 5 ); +} + +TEST(Counter, Increment) { // Tests the Increment() method. + Counter c; + + // Test that counter 0 returns 0 + EXPECT_EQ(0, c.Decrement()); + + // EXPECT_EQ() evaluates its arguments exactly once, so they + // can have side effects. + + EXPECT_EQ(0, c.Increment()); + EXPECT_EQ(1, c.Increment()); + EXPECT_EQ(2, c.Increment()); + + EXPECT_EQ(3, c.Decrement()); +} + + + +} \ No newline at end of file diff --git a/gtest/snippets-uniqueInOrder/preloaded.snippet b/gtest/snippets-uniqueInOrder/preloaded.snippet new file mode 100644 index 0000000..e69de29 diff --git a/gtest/snippets-uniqueInOrder/solution.snippet b/gtest/snippets-uniqueInOrder/solution.snippet new file mode 100644 index 0000000..8d7c812 --- /dev/null +++ b/gtest/snippets-uniqueInOrder/solution.snippet @@ -0,0 +1,22 @@ +//! CW_SOLUTION_H +#include +#include +#include + +template std::vector uniqueInOrder(const std::vector& iterable){ + std::vector res = iterable; + res.erase(std::unique(begin(res), end(res)), end(res)); + return res; +} +std::vector uniqueInOrder(const std::string& iterable); + +//! CW_SOLUTION_CPP +#include +#include +#include + +std::vector uniqueInOrder(const std::string& iterable){ + std::string res = iterable; + res.erase(std::unique(begin(res), end(res)), end(res)); + return std::vector{begin(res), end(res)}; +} \ No newline at end of file diff --git a/gtest/snippets-uniqueInOrder/test_solution.snippet b/gtest/snippets-uniqueInOrder/test_solution.snippet new file mode 100644 index 0000000..ef66980 --- /dev/null +++ b/gtest/snippets-uniqueInOrder/test_solution.snippet @@ -0,0 +1,77 @@ +#include +#include + +#include "solution.h" + +#include +#include +#include + +auto randint(int min, int max) { + static std::random_device rd; + static std::mt19937 rng(rd()); + std::uniform_int_distribution uni(min, max); + return uni(rng); +} + +std::string printVec(const std::vector& vec) { + std::string res = "'" + std::string(1, vec[0]) + "'"; + for (unsigned int i = 1; i < vec.size(); i++) res += ", '" + std::string(1, vec[i]) + "'"; + return res; +} + +std::string printVec(const std::vector& vec) { + std::string res = std::to_string(vec[0]); + for (unsigned int i = 1; i < vec.size(); i++) res += ", " + std::to_string(vec[i]); + return res; +} +using VI = std::vector; +using VC = std::vector; + +TEST(UniqueInOrder, BasicTests) { + ASSERT_EQ(uniqueInOrder(""), VC{}); + ASSERT_EQ(uniqueInOrder("AA"), VC{ 'A' }); + ASSERT_EQ(uniqueInOrder("A"), VC{ 'A' }); + ASSERT_EQ(uniqueInOrder("AAAABBBCCDAABBB"), (VC{ 'A', 'B', 'C', 'D', 'A', 'B' })); + ASSERT_EQ(uniqueInOrder("AADD"), (VC{ 'A', 'D' })); + ASSERT_EQ(uniqueInOrder("AAD"), (VC{ 'A', 'D' })); + ASSERT_EQ(uniqueInOrder("ADD"), (VC{ 'A', 'D' })); + ASSERT_EQ(uniqueInOrder("ABBCcAD"), (VC{ 'A', 'B', 'C', 'c', 'A', 'D' })); + ASSERT_EQ(uniqueInOrder(VI{ 1,2,3,3 }), (VI{ 1,2,3 })); + ASSERT_EQ(uniqueInOrder(VC{ 'a','b','b' }), (VC{ 'a','b' })); +}; + template std::vector sol(const std::vector&iterable) { + std::vector res = iterable; + res.erase(std::unique(begin(res), end(res)), end(res)); + return res; + } + + std::vector sol(const std::string & iterable) { + std::string res = iterable; + res.erase(std::unique(begin(res), end(res)), end(res)); + return std::vector{begin(res), end(res)}; + } + TEST(UniqueInOrder, RandomTests) { + std::string base = "abcXYZ"; + for (unsigned int i = 0; i < 40; i++) { + unsigned int testCase = randint(0, 2), inputLen = randint(3, 40); + if (testCase == 0) { + std::string iterable; + for (; inputLen--;) iterable += base[randint(0, base.size() - 1)]; + std::cout << "Testing for '" << iterable << "'\n\n"; + ASSERT_EQ(uniqueInOrder(iterable), sol(iterable)); + } + else if (testCase == 1) { + std::vector iterable; + for (; inputLen--;) iterable.push_back(base[randint(0, base.size() - 1)]); + std::cout << "Testing for [" << printVec(iterable) << "]\n\n"; + ASSERT_EQ(uniqueInOrder(iterable), sol(iterable)); + } + else { + std::vector iterable; + for (; inputLen--;) iterable.push_back(randint(-5, 5)); + std::cout << "Testing for [" << printVec(iterable) << "]\n\n"; + ASSERT_EQ(uniqueInOrder(iterable), sol(iterable)); + } + } + }; \ No newline at end of file diff --git a/gtest/snippets/preloaded.snippet b/gtest/snippets/preloaded.snippet index e69de29..5463ce5 100644 --- a/gtest/snippets/preloaded.snippet +++ b/gtest/snippets/preloaded.snippet @@ -0,0 +1,15 @@ +#pragma once + +// A simple monotonic counter. +class Counter { + private: + int counter_; + + public: + // Creates a counter that starts at 0. + Counter() : counter_(0) {} + // Returns the current counter value, and increments it. + int Increment(); + // Returns the current counter value, and decrements it. + int Decrement(); +}; \ No newline at end of file diff --git a/gtest/snippets/solution.snippet b/gtest/snippets/solution.snippet index 8d7c812..50991b8 100644 --- a/gtest/snippets/solution.snippet +++ b/gtest/snippets/solution.snippet @@ -1,22 +1,16 @@ -//! CW_SOLUTION_H -#include -#include -#include +#include -template std::vector uniqueInOrder(const std::vector& iterable){ - std::vector res = iterable; - res.erase(std::unique(begin(res), end(res)), end(res)); - return res; +// Returns the current counter value, and increments it. +int Counter::Increment() { + return counter_++; } -std::vector uniqueInOrder(const std::string& iterable); -//! CW_SOLUTION_CPP -#include -#include -#include - -std::vector uniqueInOrder(const std::string& iterable){ - std::string res = iterable; - res.erase(std::unique(begin(res), end(res)), end(res)); - return std::vector{begin(res), end(res)}; -} \ No newline at end of file +// Returns the current counter value, and decrements it. +// counter can not be less than 0, return 0 in this case +int Counter::Decrement() { + if (counter_ == 0) { + return counter_; + } else { + return counter_--; + } +} diff --git a/gtest/snippets/test_solution.snippet b/gtest/snippets/test_solution.snippet index ef66980..69c1bb2 100644 --- a/gtest/snippets/test_solution.snippet +++ b/gtest/snippets/test_solution.snippet @@ -1,77 +1,64 @@ #include #include +#include -#include "solution.h" +namespace { +TEST(Vector_size_resize, resizing_bigger_changes_size_and_capacity) { + std::vector v( 5 ); + EXPECT_EQ( v.size(), 5 ); + EXPECT_GE( v.capacity(), 5 ); -#include -#include -#include + v.resize( 10 ); + EXPECT_EQ( v.size(), 10 ); + EXPECT_GE( v.capacity(), 10 ); +} + +TEST(Vector_size_resize, resizing_smaller_changes_size_but_not_capacity) { + std::vector v( 5 ); + EXPECT_EQ( v.size(), 5 ); + EXPECT_GE( v.capacity(), 5 ); + v.resize( 0 ); -auto randint(int min, int max) { - static std::random_device rd; - static std::mt19937 rng(rd()); - std::uniform_int_distribution uni(min, max); - return uni(rng); + EXPECT_EQ( v.size(), 0 ); + EXPECT_GE( v.capacity(), 5 ); } -std::string printVec(const std::vector& vec) { - std::string res = "'" + std::string(1, vec[0]) + "'"; - for (unsigned int i = 1; i < vec.size(); i++) res += ", '" + std::string(1, vec[i]) + "'"; - return res; +TEST(Vector_size_resize, reserving_bigger_changes_capacity_but_not_size) { + std::vector v( 5 ); + EXPECT_EQ( v.size(), 5 ); + EXPECT_GE( v.capacity(), 5 ); + v.reserve( 10 ); + + EXPECT_EQ( v.size(), 5 ); + EXPECT_GE( v.capacity(), 10 ); } -std::string printVec(const std::vector& vec) { - std::string res = std::to_string(vec[0]); - for (unsigned int i = 1; i < vec.size(); i++) res += ", " + std::to_string(vec[i]); - return res; +TEST(Vector_size_resize, reserving_smaller_does_not_change_size_or_capacity) { + std::vector v( 5 ); + EXPECT_EQ( v.size(), 5 ); + EXPECT_GE( v.capacity(), 5 ); + v.reserve( 0 ); + + EXPECT_EQ( v.size(), 5 ); + EXPECT_GE( v.capacity(), 5 ); } -using VI = std::vector; -using VC = std::vector; - -TEST(UniqueInOrder, BasicTests) { - ASSERT_EQ(uniqueInOrder(""), VC{}); - ASSERT_EQ(uniqueInOrder("AA"), VC{ 'A' }); - ASSERT_EQ(uniqueInOrder("A"), VC{ 'A' }); - ASSERT_EQ(uniqueInOrder("AAAABBBCCDAABBB"), (VC{ 'A', 'B', 'C', 'D', 'A', 'B' })); - ASSERT_EQ(uniqueInOrder("AADD"), (VC{ 'A', 'D' })); - ASSERT_EQ(uniqueInOrder("AAD"), (VC{ 'A', 'D' })); - ASSERT_EQ(uniqueInOrder("ADD"), (VC{ 'A', 'D' })); - ASSERT_EQ(uniqueInOrder("ABBCcAD"), (VC{ 'A', 'B', 'C', 'c', 'A', 'D' })); - ASSERT_EQ(uniqueInOrder(VI{ 1,2,3,3 }), (VI{ 1,2,3 })); - ASSERT_EQ(uniqueInOrder(VC{ 'a','b','b' }), (VC{ 'a','b' })); -}; - template std::vector sol(const std::vector&iterable) { - std::vector res = iterable; - res.erase(std::unique(begin(res), end(res)), end(res)); - return res; - } - - std::vector sol(const std::string & iterable) { - std::string res = iterable; - res.erase(std::unique(begin(res), end(res)), end(res)); - return std::vector{begin(res), end(res)}; - } - TEST(UniqueInOrder, RandomTests) { - std::string base = "abcXYZ"; - for (unsigned int i = 0; i < 40; i++) { - unsigned int testCase = randint(0, 2), inputLen = randint(3, 40); - if (testCase == 0) { - std::string iterable; - for (; inputLen--;) iterable += base[randint(0, base.size() - 1)]; - std::cout << "Testing for '" << iterable << "'\n\n"; - ASSERT_EQ(uniqueInOrder(iterable), sol(iterable)); - } - else if (testCase == 1) { - std::vector iterable; - for (; inputLen--;) iterable.push_back(base[randint(0, base.size() - 1)]); - std::cout << "Testing for [" << printVec(iterable) << "]\n\n"; - ASSERT_EQ(uniqueInOrder(iterable), sol(iterable)); - } - else { - std::vector iterable; - for (; inputLen--;) iterable.push_back(randint(-5, 5)); - std::cout << "Testing for [" << printVec(iterable) << "]\n\n"; - ASSERT_EQ(uniqueInOrder(iterable), sol(iterable)); - } - } - }; \ No newline at end of file + +TEST(Counter, Increment) { // Tests the Increment() method. + Counter c; + + // Test that counter 0 returns 0 + EXPECT_EQ(0, c.Decrement()); + + // EXPECT_EQ() evaluates its arguments exactly once, so they + // can have side effects. + + EXPECT_EQ(0, c.Increment()); + EXPECT_EQ(1, c.Increment()); + EXPECT_EQ(2, c.Increment()); + + EXPECT_EQ(3, c.Decrement()); +} + + + +} \ No newline at end of file From eccb2d82f0181eb69be89f6225987c4d7a2b20e6 Mon Sep 17 00:00:00 2001 From: hobovsky Date: Mon, 22 Jan 2024 20:11:25 +0100 Subject: [PATCH 08/26] Update locations of files in the original example to my proposed solution --- gtest/README.md | 2 +- gtest/snippets-originalExample/solution.snippet | 2 +- gtest/snippets-originalExample/test_solution.snippet | 2 +- gtest/snippets/solution.snippet | 2 +- gtest/snippets/test_solution.snippet | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/gtest/README.md b/gtest/README.md index cf22d7d..06b245b 100644 --- a/gtest/README.md +++ b/gtest/README.md @@ -13,5 +13,5 @@ We'll configure and build an example project while building the container image ## Example kata The `snippets-*` directories contain showcases of kata which could be migrated to Google Test and the proposed organization of snippets: -- `snippets-originalExample` - the original example of Google Test tests prepared by kazk. +- `snippets-originalExample` - the original example of Google Test tests prepared by kazk. It uses no CW preprocessor markers, and only `preloaded.h` and `solution.cpp` are relevant. `preloaded.cpp` and `solution.h` are also saved, but are irrelevant, and cause no problems when buiding and testing the kata. - `snippets-uniqueInOrder` - a kata which requires separate cpp file and header file for solution, because it requires users to implement two functions: one is a template, and one is a "normal" function. It uses CW preprocessor markers to split submitted solution snippet into two files. \ No newline at end of file diff --git a/gtest/snippets-originalExample/solution.snippet b/gtest/snippets-originalExample/solution.snippet index 50991b8..8bcfe79 100644 --- a/gtest/snippets-originalExample/solution.snippet +++ b/gtest/snippets-originalExample/solution.snippet @@ -1,4 +1,4 @@ -#include +#include // Returns the current counter value, and increments it. int Counter::Increment() { diff --git a/gtest/snippets-originalExample/test_solution.snippet b/gtest/snippets-originalExample/test_solution.snippet index 69c1bb2..9d3ae01 100644 --- a/gtest/snippets-originalExample/test_solution.snippet +++ b/gtest/snippets-originalExample/test_solution.snippet @@ -1,6 +1,6 @@ #include #include -#include +#include namespace { TEST(Vector_size_resize, resizing_bigger_changes_size_and_capacity) { diff --git a/gtest/snippets/solution.snippet b/gtest/snippets/solution.snippet index 50991b8..8bcfe79 100644 --- a/gtest/snippets/solution.snippet +++ b/gtest/snippets/solution.snippet @@ -1,4 +1,4 @@ -#include +#include // Returns the current counter value, and increments it. int Counter::Increment() { diff --git a/gtest/snippets/test_solution.snippet b/gtest/snippets/test_solution.snippet index 69c1bb2..9d3ae01 100644 --- a/gtest/snippets/test_solution.snippet +++ b/gtest/snippets/test_solution.snippet @@ -1,6 +1,6 @@ #include #include -#include +#include namespace { TEST(Vector_size_resize, resizing_bigger_changes_size_and_capacity) { From 602b6b3de41e9ba0371a8ae7873b2fef9d72514c Mon Sep 17 00:00:00 2001 From: hobovsky Date: Mon, 22 Jan 2024 23:17:28 +0100 Subject: [PATCH 09/26] Add example "Cafeteria" --- gtest/README.md | 3 +- gtest/snippets-Cafeteria/preloaded.snippet | 67 ++++++++++++++++ gtest/snippets-Cafeteria/solution.snippet | 42 ++++++++++ .../snippets-Cafeteria/test_solution.snippet | 80 +++++++++++++++++++ 4 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 gtest/snippets-Cafeteria/preloaded.snippet create mode 100644 gtest/snippets-Cafeteria/solution.snippet create mode 100644 gtest/snippets-Cafeteria/test_solution.snippet diff --git a/gtest/README.md b/gtest/README.md index 06b245b..300f04e 100644 --- a/gtest/README.md +++ b/gtest/README.md @@ -14,4 +14,5 @@ We'll configure and build an example project while building the container image The `snippets-*` directories contain showcases of kata which could be migrated to Google Test and the proposed organization of snippets: - `snippets-originalExample` - the original example of Google Test tests prepared by kazk. It uses no CW preprocessor markers, and only `preloaded.h` and `solution.cpp` are relevant. `preloaded.cpp` and `solution.h` are also saved, but are irrelevant, and cause no problems when buiding and testing the kata. -- `snippets-uniqueInOrder` - a kata which requires separate cpp file and header file for solution, because it requires users to implement two functions: one is a template, and one is a "normal" function. It uses CW preprocessor markers to split submitted solution snippet into two files. \ No newline at end of file +- `snippets-uniqueInOrder` - a kata which requires separate cpp file and header file for solution, because it requires users to implement two functions: one is a template, and one is a "normal" function. It uses CW preprocessor markers to split submitted solution snippet into two files. +- `snippets-Cafeteria` - a kata with no preprocessing markers, with a complex preloaded, where both solution and preloaded are treated as headers, and the cpp part of both gets (apparently) ignored. It also defines custom `operator <<` for the preloaded types, which is used by GTest to present instances in failure messages. To be honest, I am ot sure why this example works because I expected it to fail with linker errors caused by standalone definitions of `operator<<`. I need to take a better look. diff --git a/gtest/snippets-Cafeteria/preloaded.snippet b/gtest/snippets-Cafeteria/preloaded.snippet new file mode 100644 index 0000000..2c13930 --- /dev/null +++ b/gtest/snippets-Cafeteria/preloaded.snippet @@ -0,0 +1,67 @@ +#ifndef CW_PRELOADED_INCLUDED +#define CW_PRELOADED_INCLUDED + +#include +#include +#include + +struct Milk { + float fat; + + bool operator ==(const Milk &other) const { + return fat == other.fat; + } +}; + +std::ostream &operator<<(std::ostream &stream, const Milk &milk) { + stream << "Milk(" << milk.fat << ")"; + return stream; +} + + + +struct Sugar { + std::string sort; + + bool operator ==(const Sugar &other) const { + return sort == other.sort; + } +}; + +std::ostream &operator<<(std::ostream &stream, const Sugar &sugar) { + return (stream << "Sugar('" << sugar.sort << "')"); +} + + + +struct Coffee { + std::string sort; + std::vector milk; + std::vector sugar; + + bool operator==(const Coffee &other) const { + return sort == other.sort && milk == other.milk && sugar == other.sugar; + } +}; + +std::ostream &operator<<(std::ostream &stream, const Coffee &coffee) { + stream << "Coffee('" << coffee.sort << "', ["; + + for (unsigned i = 0; i < coffee.milk.size(); i++) { + if (i) stream << ", "; + stream << coffee.milk[i]; + } + + stream << "], ["; + + for (unsigned i = 0; i < coffee.sugar.size(); i++) { + if (i) stream << ", "; + stream << coffee.sugar[i]; + } + + stream << "])"; + + return stream; +} + +#endif // CW_PRELOADED_INCLUDED \ No newline at end of file diff --git a/gtest/snippets-Cafeteria/solution.snippet b/gtest/snippets-Cafeteria/solution.snippet new file mode 100644 index 0000000..7e64ec8 --- /dev/null +++ b/gtest/snippets-Cafeteria/solution.snippet @@ -0,0 +1,42 @@ +#include +#include + +#include + +class CoffeeBuilder { + public: + std::string sort; + std::vector milk; + std::vector sugar; + + CoffeeBuilder set_black_coffee() { + sort = "Black"; + return *this; + } + + CoffeeBuilder set_cubano_coffee() { + sort = "Cubano"; + sugar.push_back({"Brown"}); + return *this; + } + + CoffeeBuilder set_antoccino_coffee() { + sort = "Americano"; + milk.push_back({0.5}); + return *this; + } + + CoffeeBuilder with_milk(float n) { + milk.push_back({n}); + return *this; + } + + CoffeeBuilder with_sugar(const std::string &s) { + sugar.push_back({s}); + return *this; + } + + Coffee build() { + return {sort, milk, sugar}; + } +}; \ No newline at end of file diff --git a/gtest/snippets-Cafeteria/test_solution.snippet b/gtest/snippets-Cafeteria/test_solution.snippet new file mode 100644 index 0000000..35083c8 --- /dev/null +++ b/gtest/snippets-Cafeteria/test_solution.snippet @@ -0,0 +1,80 @@ +#include +#include +#include +#include + +#include +#include +#include + +namespace { + TEST(Fixed_Tests, Tests) { + { + Coffee actual = CoffeeBuilder().set_black_coffee().with_sugar("Regular").with_milk(3.2).build(); + Coffee expected = {"Black", {{3.3}}, {{"Regular"}}}; + EXPECT_EQ(actual, expected) << expected; + } + + { + Coffee actual = CoffeeBuilder().set_antoccino_coffee().build(); + Coffee expected = {"Americano", {{0.5}}, {}}; + EXPECT_EQ(actual, expected); + } + + { + Coffee actual = CoffeeBuilder().set_cubano_coffee().build(); + Coffee expected = {"Cubano", {}, {{"Brown"}}}; + EXPECT_EQ(actual, expected); + } + } + + + auto randint(unsigned min, unsigned max) { + static std::random_device rd; + static std::mt19937 rng(rd()); + std::uniform_int_distribution uni(min, max); + return uni(rng); + } + + std::string randstr(unsigned n) { + static std::string base = "abcdefghijklmnopqrstuvwxyz"; + std::stringstream ss; + while (n--) ss << base[randint(0, base.size() - 1)]; + return ss.str(); + } + + TEST(Random_Tests, Tests) { + for (int i = 0; i < 100; i++) { + Coffee c; + CoffeeBuilder cb {}; + + int type = randint(0, 2); + if (type == 0) { + c.sort = "Black"; + cb = cb.set_black_coffee(); + } else if (type == 1) { + c.sort = "Americano"; + c.milk.push_back({0.5}); + cb = cb.set_antoccino_coffee(); + } else { + c.sort = "Cubano"; + c.sugar.push_back({"Brown"}); + cb = cb.set_cubano_coffee(); + } + + for (int i = randint(0, 2); i; i--) { + float n = randint(1, 30) / 10.0; + c.milk.push_back({n}); + cb = cb.with_milk(n); + } + + for (int i = randint(0, 2); i; i--) { + std::string s = randstr(randint(3, 5)); + c.sugar.push_back({s}); + cb = cb.with_sugar(s); + } + + EXPECT_EQ(cb.build(), c) << c; + } + } +} \ No newline at end of file From 3f6333a43282f8f19386c03abc9050239f57cda0 Mon Sep 17 00:00:00 2001 From: hobovsky Date: Wed, 24 Jan 2024 17:50:21 +0100 Subject: [PATCH 10/26] Remove preprocessor from the Docker image, copy processed files into a container instead --- gtest/Dockerfile | 2 +- gtest/README.md | 20 ++++- gtest/build_image.bat | 1 + gtest/distribute-snippets.sh | 6 -- gtest/{ => docker_image}/CMakeLists.txt | 0 gtest/{ => docker_image}/configure.sh | 0 gtest/docker_image/run-kata.sh | 3 + gtest/{ => docker_image}/run-tests.sh | 0 gtest/{ => docker_image}/src/CMakeLists.txt | 0 gtest/{ => docker_image}/tests/CMakeLists.txt | 0 gtest/{ => docker_image}/tests/main.cpp | 0 .../Cafeteria/include/preloaded.h} | 0 .../Cafeteria/include/solution.h} | 0 .../Cafeteria/src/preloaded.cpp} | 0 gtest/kata_code/Cafeteria/src/solution.cpp | 0 .../Cafeteria/tests/test_solution.cpp} | 0 .../OriginalExample/include/preloaded.h} | 0 .../OriginalExample/include/solution.h | 0 .../OriginalExample/src/preloaded.cpp | 0 .../OriginalExample/src/solution.cpp} | 0 .../OriginalExample/tests/test_solution.cpp} | 0 .../UniqueInOrder/include/preloaded.h | 0 .../UniqueInOrder/include/solution.h | 10 +++ .../kata_code/UniqueInOrder/src/preloaded.cpp | 0 .../kata_code/UniqueInOrder/src/solution.cpp | 9 +++ .../UniqueInOrder/tests/test_solution.cpp} | 0 gtest/preprocessor/cwpp.ts | 23 ++++++ gtest/run-all.sh | 2 - gtest/run_image.bat | 8 ++ gtest/snippets/Cafeteria/preloaded.snippet | 67 ++++++++++++++++ gtest/snippets/Cafeteria/solution.snippet | 47 +++++++++++ .../snippets/Cafeteria/test_solution.snippet | 80 +++++++++++++++++++ .../{ => OriginalExample}/preloaded.snippet | 7 +- .../{ => OriginalExample}/solution.snippet | 0 .../test_solution.snippet | 0 .../snippets/UniqueInOrder/preloaded.snippet | 0 .../UniqueInOrder}/solution.snippet | 7 +- .../UniqueInOrder/test_solution.snippet | 77 ++++++++++++++++++ 38 files changed, 353 insertions(+), 16 deletions(-) create mode 100644 gtest/build_image.bat delete mode 100644 gtest/distribute-snippets.sh rename gtest/{ => docker_image}/CMakeLists.txt (100%) rename gtest/{ => docker_image}/configure.sh (100%) mode change 100755 => 100644 create mode 100644 gtest/docker_image/run-kata.sh rename gtest/{ => docker_image}/run-tests.sh (100%) mode change 100755 => 100644 rename gtest/{ => docker_image}/src/CMakeLists.txt (100%) rename gtest/{ => docker_image}/tests/CMakeLists.txt (100%) rename gtest/{ => docker_image}/tests/main.cpp (100%) rename gtest/{snippets-Cafeteria/preloaded.snippet => kata_code/Cafeteria/include/preloaded.h} (100%) rename gtest/{snippets-Cafeteria/solution.snippet => kata_code/Cafeteria/include/solution.h} (100%) rename gtest/{snippets-uniqueInOrder/preloaded.snippet => kata_code/Cafeteria/src/preloaded.cpp} (100%) create mode 100644 gtest/kata_code/Cafeteria/src/solution.cpp rename gtest/{snippets-Cafeteria/test_solution.snippet => kata_code/Cafeteria/tests/test_solution.cpp} (100%) rename gtest/{snippets-originalExample/preloaded.snippet => kata_code/OriginalExample/include/preloaded.h} (100%) create mode 100644 gtest/kata_code/OriginalExample/include/solution.h create mode 100644 gtest/kata_code/OriginalExample/src/preloaded.cpp rename gtest/{snippets-originalExample/solution.snippet => kata_code/OriginalExample/src/solution.cpp} (100%) rename gtest/{snippets-originalExample/test_solution.snippet => kata_code/OriginalExample/tests/test_solution.cpp} (100%) create mode 100644 gtest/kata_code/UniqueInOrder/include/preloaded.h create mode 100644 gtest/kata_code/UniqueInOrder/include/solution.h create mode 100644 gtest/kata_code/UniqueInOrder/src/preloaded.cpp create mode 100644 gtest/kata_code/UniqueInOrder/src/solution.cpp rename gtest/{snippets-uniqueInOrder/test_solution.snippet => kata_code/UniqueInOrder/tests/test_solution.cpp} (100%) create mode 100644 gtest/preprocessor/cwpp.ts delete mode 100644 gtest/run-all.sh create mode 100644 gtest/run_image.bat create mode 100644 gtest/snippets/Cafeteria/preloaded.snippet create mode 100644 gtest/snippets/Cafeteria/solution.snippet create mode 100644 gtest/snippets/Cafeteria/test_solution.snippet rename gtest/snippets/{ => OriginalExample}/preloaded.snippet (79%) rename gtest/snippets/{ => OriginalExample}/solution.snippet (100%) rename gtest/snippets/{ => OriginalExample}/test_solution.snippet (100%) create mode 100644 gtest/snippets/UniqueInOrder/preloaded.snippet rename gtest/{snippets-uniqueInOrder => snippets/UniqueInOrder}/solution.snippet (87%) create mode 100644 gtest/snippets/UniqueInOrder/test_solution.snippet diff --git a/gtest/Dockerfile b/gtest/Dockerfile index 4362f1c..1b464a6 100644 --- a/gtest/Dockerfile +++ b/gtest/Dockerfile @@ -26,7 +26,7 @@ RUN ln -sf /usr/bin/clang /usr/bin/cc \ && c++ --version WORKDIR /code -COPY . . +COPY docker_image . # ENV APP_NAME='' diff --git a/gtest/README.md b/gtest/README.md index 300f04e..14cc214 100644 --- a/gtest/README.md +++ b/gtest/README.md @@ -1,5 +1,15 @@ # GoogleTest +## Scripts for local Docker + +Locally I use Windows, that's why my scripts are batch files. +- `build-image.bat` builds an image to run C++ kata, +- `run-image.bat` runs code of a kata in a container. Use `run-image KATA-CODE` to run, whenre `KATA-CODE` is a name of one of subdirectories in `kata_code` directory. + +## Docker image + +Kata code is built and run with: + ```bash cmake -S . -B build -G Ninja cmake --build build && ./build/tests/tests @@ -9,10 +19,12 @@ Use `./configure.sh` and `./run-tests.sh`. We'll configure and build an example project while building the container image so each submission will only build the submitted code. +The `snippets` directory contains snippets of example kata as they are submitted, before preprocessing. + +The `kata_code` directory contains source file of a kata as produced by the CW preprocessor. The files are cp-ed into a container. ## Example kata -The `snippets-*` directories contain showcases of kata which could be migrated to Google Test and the proposed organization of snippets: -- `snippets-originalExample` - the original example of Google Test tests prepared by kazk. It uses no CW preprocessor markers, and only `preloaded.h` and `solution.cpp` are relevant. `preloaded.cpp` and `solution.h` are also saved, but are irrelevant, and cause no problems when buiding and testing the kata. -- `snippets-uniqueInOrder` - a kata which requires separate cpp file and header file for solution, because it requires users to implement two functions: one is a template, and one is a "normal" function. It uses CW preprocessor markers to split submitted solution snippet into two files. -- `snippets-Cafeteria` - a kata with no preprocessing markers, with a complex preloaded, where both solution and preloaded are treated as headers, and the cpp part of both gets (apparently) ignored. It also defines custom `operator <<` for the preloaded types, which is used by GTest to present instances in failure messages. To be honest, I am ot sure why this example works because I expected it to fail with linker errors caused by standalone definitions of `operator<<`. I need to take a better look. +- `OriginalExample` - the original example of Google Test tests prepared by kazk. It uses no CW preprocessor markers, and only `preloaded.h` and `solution.cpp` are relevant. `preloaded.cpp` and `solution.h` are also saved, but are irrelevant, and cause no problems when buiding and testing the kata. +- `UniqueInOrder` - a kata which requires separate cpp file and header file for solution, because it requires users to implement two functions: one is a template, and one is a "normal" function. It uses CW preprocessor markers to split submitted solution snippet into two files. +- `Cafeteria` - a kata with no preprocessing markers, with a complex preloaded, where both solution and preloaded are treated as headers, and the cpp part of both gets (apparently) ignored. It also defines custom `operator <<` for the preloaded types, which is used by GTest to present instances in failure messages. To be honest, I am not sure why this example works because I expected it to fail with linker errors caused by standalone definitions of `operator<<`. I need to take a better look. diff --git a/gtest/build_image.bat b/gtest/build_image.bat new file mode 100644 index 0000000..9c249d1 --- /dev/null +++ b/gtest/build_image.bat @@ -0,0 +1 @@ +docker build --tag cw-gtest-cpp . \ No newline at end of file diff --git a/gtest/distribute-snippets.sh b/gtest/distribute-snippets.sh deleted file mode 100644 index ca03b74..0000000 --- a/gtest/distribute-snippets.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh -cp snippets/test_solution.snippet tests/test_solution.cpp - -clang++ -o preprocessor/cwpp -O2 preprocessor/cwpp.cpp -./preprocessor/cwpp SOLUTION snippets/solution.snippet -./preprocessor/cwpp PRELOADED snippets/preloaded.snippet diff --git a/gtest/CMakeLists.txt b/gtest/docker_image/CMakeLists.txt similarity index 100% rename from gtest/CMakeLists.txt rename to gtest/docker_image/CMakeLists.txt diff --git a/gtest/configure.sh b/gtest/docker_image/configure.sh old mode 100755 new mode 100644 similarity index 100% rename from gtest/configure.sh rename to gtest/docker_image/configure.sh diff --git a/gtest/docker_image/run-kata.sh b/gtest/docker_image/run-kata.sh new file mode 100644 index 0000000..4aa9cda --- /dev/null +++ b/gtest/docker_image/run-kata.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +./configure.sh && ./run-tests.sh \ No newline at end of file diff --git a/gtest/run-tests.sh b/gtest/docker_image/run-tests.sh old mode 100755 new mode 100644 similarity index 100% rename from gtest/run-tests.sh rename to gtest/docker_image/run-tests.sh diff --git a/gtest/src/CMakeLists.txt b/gtest/docker_image/src/CMakeLists.txt similarity index 100% rename from gtest/src/CMakeLists.txt rename to gtest/docker_image/src/CMakeLists.txt diff --git a/gtest/tests/CMakeLists.txt b/gtest/docker_image/tests/CMakeLists.txt similarity index 100% rename from gtest/tests/CMakeLists.txt rename to gtest/docker_image/tests/CMakeLists.txt diff --git a/gtest/tests/main.cpp b/gtest/docker_image/tests/main.cpp similarity index 100% rename from gtest/tests/main.cpp rename to gtest/docker_image/tests/main.cpp diff --git a/gtest/snippets-Cafeteria/preloaded.snippet b/gtest/kata_code/Cafeteria/include/preloaded.h similarity index 100% rename from gtest/snippets-Cafeteria/preloaded.snippet rename to gtest/kata_code/Cafeteria/include/preloaded.h diff --git a/gtest/snippets-Cafeteria/solution.snippet b/gtest/kata_code/Cafeteria/include/solution.h similarity index 100% rename from gtest/snippets-Cafeteria/solution.snippet rename to gtest/kata_code/Cafeteria/include/solution.h diff --git a/gtest/snippets-uniqueInOrder/preloaded.snippet b/gtest/kata_code/Cafeteria/src/preloaded.cpp similarity index 100% rename from gtest/snippets-uniqueInOrder/preloaded.snippet rename to gtest/kata_code/Cafeteria/src/preloaded.cpp diff --git a/gtest/kata_code/Cafeteria/src/solution.cpp b/gtest/kata_code/Cafeteria/src/solution.cpp new file mode 100644 index 0000000..e69de29 diff --git a/gtest/snippets-Cafeteria/test_solution.snippet b/gtest/kata_code/Cafeteria/tests/test_solution.cpp similarity index 100% rename from gtest/snippets-Cafeteria/test_solution.snippet rename to gtest/kata_code/Cafeteria/tests/test_solution.cpp diff --git a/gtest/snippets-originalExample/preloaded.snippet b/gtest/kata_code/OriginalExample/include/preloaded.h similarity index 100% rename from gtest/snippets-originalExample/preloaded.snippet rename to gtest/kata_code/OriginalExample/include/preloaded.h diff --git a/gtest/kata_code/OriginalExample/include/solution.h b/gtest/kata_code/OriginalExample/include/solution.h new file mode 100644 index 0000000..e69de29 diff --git a/gtest/kata_code/OriginalExample/src/preloaded.cpp b/gtest/kata_code/OriginalExample/src/preloaded.cpp new file mode 100644 index 0000000..e69de29 diff --git a/gtest/snippets-originalExample/solution.snippet b/gtest/kata_code/OriginalExample/src/solution.cpp similarity index 100% rename from gtest/snippets-originalExample/solution.snippet rename to gtest/kata_code/OriginalExample/src/solution.cpp diff --git a/gtest/snippets-originalExample/test_solution.snippet b/gtest/kata_code/OriginalExample/tests/test_solution.cpp similarity index 100% rename from gtest/snippets-originalExample/test_solution.snippet rename to gtest/kata_code/OriginalExample/tests/test_solution.cpp diff --git a/gtest/kata_code/UniqueInOrder/include/preloaded.h b/gtest/kata_code/UniqueInOrder/include/preloaded.h new file mode 100644 index 0000000..e69de29 diff --git a/gtest/kata_code/UniqueInOrder/include/solution.h b/gtest/kata_code/UniqueInOrder/include/solution.h new file mode 100644 index 0000000..a4b3bb8 --- /dev/null +++ b/gtest/kata_code/UniqueInOrder/include/solution.h @@ -0,0 +1,10 @@ +#include +#include +#include + +template std::vector uniqueInOrder(const std::vector& iterable){ + std::vector res = iterable; + res.erase(std::unique(begin(res), end(res)), end(res)); + return res; +} +std::vector uniqueInOrder(const std::string& iterable); diff --git a/gtest/kata_code/UniqueInOrder/src/preloaded.cpp b/gtest/kata_code/UniqueInOrder/src/preloaded.cpp new file mode 100644 index 0000000..e69de29 diff --git a/gtest/kata_code/UniqueInOrder/src/solution.cpp b/gtest/kata_code/UniqueInOrder/src/solution.cpp new file mode 100644 index 0000000..8a4d969 --- /dev/null +++ b/gtest/kata_code/UniqueInOrder/src/solution.cpp @@ -0,0 +1,9 @@ +#include +#include +#include + +std::vector uniqueInOrder(const std::string& iterable){ + std::string res = iterable; + res.erase(std::unique(begin(res), end(res)), end(res)); + return std::vector{begin(res), end(res)}; +} \ No newline at end of file diff --git a/gtest/snippets-uniqueInOrder/test_solution.snippet b/gtest/kata_code/UniqueInOrder/tests/test_solution.cpp similarity index 100% rename from gtest/snippets-uniqueInOrder/test_solution.snippet rename to gtest/kata_code/UniqueInOrder/tests/test_solution.cpp diff --git a/gtest/preprocessor/cwpp.ts b/gtest/preprocessor/cwpp.ts new file mode 100644 index 0000000..f03a7dc --- /dev/null +++ b/gtest/preprocessor/cwpp.ts @@ -0,0 +1,23 @@ +const splitSnippet = (code: string, name: string): [header: string, impl: string] => { + const lines = code.split(/(?<=\n)/); // Keep LF in each line + const marker = new RegExp( + `^\\s*#\\s*ifndef\\s+${name.toUpperCase()}_INCLUDED` + ); + const start = lines.findIndex((line) => marker.test(line)); + // No header file unless there's a start marker. + if (start === -1) return ["", code]; + + let counter = 1; + const end = lines.findIndex((line, i) => { + if (i > start) { + if (/^\s*#\s*endif\s*/.test(line)) return --counter === 0; + if (/^\s*#\s*if(?:n?def)?\s+/.test(line)) ++counter; + } + return false; + }); + // Put everything in header unless there's a matching `#endif`. + // The error should make more sense that way. + return end === -1 + ? [code, ""] + : [lines.slice(0, end + 1).join(""), lines.slice(end + 1).join("")]; +}; diff --git a/gtest/run-all.sh b/gtest/run-all.sh deleted file mode 100644 index 4a4c79c..0000000 --- a/gtest/run-all.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -./distribute-snippets.sh && ./configure.sh && ./run-tests.sh \ No newline at end of file diff --git a/gtest/run_image.bat b/gtest/run_image.bat new file mode 100644 index 0000000..afc026a --- /dev/null +++ b/gtest/run_image.bat @@ -0,0 +1,8 @@ +set KATA=%1 +set IMAGE=cw-gtest-cpp +set WORKDIR=/code/ +set CREATE_CMD=docker container create --rm -w %WORKDIR% %IMAGE% ./run-kata.sh + +FOR /F %%i IN ('%CREATE_CMD%') DO set CONTAINER=%%i +docker cp ./kata_code/%KATA%/. %CONTAINER%:%WORKDIR% +docker start --attach -i %CONTAINER% diff --git a/gtest/snippets/Cafeteria/preloaded.snippet b/gtest/snippets/Cafeteria/preloaded.snippet new file mode 100644 index 0000000..85b6a42 --- /dev/null +++ b/gtest/snippets/Cafeteria/preloaded.snippet @@ -0,0 +1,67 @@ +#ifndef PRELOADED_INCLUDED +#define PRELOADED_INCLUDED + +#include +#include +#include + +struct Milk { + float fat; + + bool operator ==(const Milk &other) const { + return fat == other.fat; + } +}; + +std::ostream &operator<<(std::ostream &stream, const Milk &milk) { + stream << "Milk(" << milk.fat << ")"; + return stream; +} + + + +struct Sugar { + std::string sort; + + bool operator ==(const Sugar &other) const { + return sort == other.sort; + } +}; + +std::ostream &operator<<(std::ostream &stream, const Sugar &sugar) { + return (stream << "Sugar('" << sugar.sort << "')"); +} + + + +struct Coffee { + std::string sort; + std::vector milk; + std::vector sugar; + + bool operator==(const Coffee &other) const { + return sort == other.sort && milk == other.milk && sugar == other.sugar; + } +}; + +std::ostream &operator<<(std::ostream &stream, const Coffee &coffee) { + stream << "Coffee('" << coffee.sort << "', ["; + + for (unsigned i = 0; i < coffee.milk.size(); i++) { + if (i) stream << ", "; + stream << coffee.milk[i]; + } + + stream << "], ["; + + for (unsigned i = 0; i < coffee.sugar.size(); i++) { + if (i) stream << ", "; + stream << coffee.sugar[i]; + } + + stream << "])"; + + return stream; +} + +#endif // PRELOADED_INCLUDED \ No newline at end of file diff --git a/gtest/snippets/Cafeteria/solution.snippet b/gtest/snippets/Cafeteria/solution.snippet new file mode 100644 index 0000000..9499a78 --- /dev/null +++ b/gtest/snippets/Cafeteria/solution.snippet @@ -0,0 +1,47 @@ +#ifndef SOLUTION_INCLUDED +#define SOLUTION_INCLUDED + +#include +#include + +#include + +class CoffeeBuilder { + public: + std::string sort; + std::vector milk; + std::vector sugar; + + CoffeeBuilder set_black_coffee() { + sort = "Black"; + return *this; + } + + CoffeeBuilder set_cubano_coffee() { + sort = "Cubano"; + sugar.push_back({"Brown"}); + return *this; + } + + CoffeeBuilder set_antoccino_coffee() { + sort = "Americano"; + milk.push_back({0.5}); + return *this; + } + + CoffeeBuilder with_milk(float n) { + milk.push_back({n}); + return *this; + } + + CoffeeBuilder with_sugar(const std::string &s) { + sugar.push_back({s}); + return *this; + } + + Coffee build() { + return {sort, milk, sugar}; + } +}; + +#endif // SOLUTION_INCLUDED \ No newline at end of file diff --git a/gtest/snippets/Cafeteria/test_solution.snippet b/gtest/snippets/Cafeteria/test_solution.snippet new file mode 100644 index 0000000..35083c8 --- /dev/null +++ b/gtest/snippets/Cafeteria/test_solution.snippet @@ -0,0 +1,80 @@ +#include +#include +#include +#include + +#include +#include +#include + +namespace { + TEST(Fixed_Tests, Tests) { + { + Coffee actual = CoffeeBuilder().set_black_coffee().with_sugar("Regular").with_milk(3.2).build(); + Coffee expected = {"Black", {{3.3}}, {{"Regular"}}}; + EXPECT_EQ(actual, expected) << expected; + } + + { + Coffee actual = CoffeeBuilder().set_antoccino_coffee().build(); + Coffee expected = {"Americano", {{0.5}}, {}}; + EXPECT_EQ(actual, expected); + } + + { + Coffee actual = CoffeeBuilder().set_cubano_coffee().build(); + Coffee expected = {"Cubano", {}, {{"Brown"}}}; + EXPECT_EQ(actual, expected); + } + } + + + auto randint(unsigned min, unsigned max) { + static std::random_device rd; + static std::mt19937 rng(rd()); + std::uniform_int_distribution uni(min, max); + return uni(rng); + } + + std::string randstr(unsigned n) { + static std::string base = "abcdefghijklmnopqrstuvwxyz"; + std::stringstream ss; + while (n--) ss << base[randint(0, base.size() - 1)]; + return ss.str(); + } + + TEST(Random_Tests, Tests) { + for (int i = 0; i < 100; i++) { + Coffee c; + CoffeeBuilder cb {}; + + int type = randint(0, 2); + if (type == 0) { + c.sort = "Black"; + cb = cb.set_black_coffee(); + } else if (type == 1) { + c.sort = "Americano"; + c.milk.push_back({0.5}); + cb = cb.set_antoccino_coffee(); + } else { + c.sort = "Cubano"; + c.sugar.push_back({"Brown"}); + cb = cb.set_cubano_coffee(); + } + + for (int i = randint(0, 2); i; i--) { + float n = randint(1, 30) / 10.0; + c.milk.push_back({n}); + cb = cb.with_milk(n); + } + + for (int i = randint(0, 2); i; i--) { + std::string s = randstr(randint(3, 5)); + c.sugar.push_back({s}); + cb = cb.with_sugar(s); + } + + EXPECT_EQ(cb.build(), c) << c; + } + } +} \ No newline at end of file diff --git a/gtest/snippets/preloaded.snippet b/gtest/snippets/OriginalExample/preloaded.snippet similarity index 79% rename from gtest/snippets/preloaded.snippet rename to gtest/snippets/OriginalExample/preloaded.snippet index 5463ce5..b66b3ff 100644 --- a/gtest/snippets/preloaded.snippet +++ b/gtest/snippets/OriginalExample/preloaded.snippet @@ -1,3 +1,6 @@ +#ifdef PRELOADED_INCLUDED +#define PRELOADED_INCLUDED + #pragma once // A simple monotonic counter. @@ -12,4 +15,6 @@ class Counter { int Increment(); // Returns the current counter value, and decrements it. int Decrement(); -}; \ No newline at end of file +}; + +#endif PRELOADED_INCLUDED diff --git a/gtest/snippets/solution.snippet b/gtest/snippets/OriginalExample/solution.snippet similarity index 100% rename from gtest/snippets/solution.snippet rename to gtest/snippets/OriginalExample/solution.snippet diff --git a/gtest/snippets/test_solution.snippet b/gtest/snippets/OriginalExample/test_solution.snippet similarity index 100% rename from gtest/snippets/test_solution.snippet rename to gtest/snippets/OriginalExample/test_solution.snippet diff --git a/gtest/snippets/UniqueInOrder/preloaded.snippet b/gtest/snippets/UniqueInOrder/preloaded.snippet new file mode 100644 index 0000000..e69de29 diff --git a/gtest/snippets-uniqueInOrder/solution.snippet b/gtest/snippets/UniqueInOrder/solution.snippet similarity index 87% rename from gtest/snippets-uniqueInOrder/solution.snippet rename to gtest/snippets/UniqueInOrder/solution.snippet index 8d7c812..622e5ee 100644 --- a/gtest/snippets-uniqueInOrder/solution.snippet +++ b/gtest/snippets/UniqueInOrder/solution.snippet @@ -1,4 +1,6 @@ -//! CW_SOLUTION_H +#ifdef SOLUTION_INCLUDED +#define SOLUTION_INCLUDED + #include #include #include @@ -10,7 +12,8 @@ template std::vector uniqueInOrder(const std::vector& iterabl } std::vector uniqueInOrder(const std::string& iterable); -//! CW_SOLUTION_CPP +#endif SOLUTION_INCLUDED + #include #include #include diff --git a/gtest/snippets/UniqueInOrder/test_solution.snippet b/gtest/snippets/UniqueInOrder/test_solution.snippet new file mode 100644 index 0000000..ef66980 --- /dev/null +++ b/gtest/snippets/UniqueInOrder/test_solution.snippet @@ -0,0 +1,77 @@ +#include +#include + +#include "solution.h" + +#include +#include +#include + +auto randint(int min, int max) { + static std::random_device rd; + static std::mt19937 rng(rd()); + std::uniform_int_distribution uni(min, max); + return uni(rng); +} + +std::string printVec(const std::vector& vec) { + std::string res = "'" + std::string(1, vec[0]) + "'"; + for (unsigned int i = 1; i < vec.size(); i++) res += ", '" + std::string(1, vec[i]) + "'"; + return res; +} + +std::string printVec(const std::vector& vec) { + std::string res = std::to_string(vec[0]); + for (unsigned int i = 1; i < vec.size(); i++) res += ", " + std::to_string(vec[i]); + return res; +} +using VI = std::vector; +using VC = std::vector; + +TEST(UniqueInOrder, BasicTests) { + ASSERT_EQ(uniqueInOrder(""), VC{}); + ASSERT_EQ(uniqueInOrder("AA"), VC{ 'A' }); + ASSERT_EQ(uniqueInOrder("A"), VC{ 'A' }); + ASSERT_EQ(uniqueInOrder("AAAABBBCCDAABBB"), (VC{ 'A', 'B', 'C', 'D', 'A', 'B' })); + ASSERT_EQ(uniqueInOrder("AADD"), (VC{ 'A', 'D' })); + ASSERT_EQ(uniqueInOrder("AAD"), (VC{ 'A', 'D' })); + ASSERT_EQ(uniqueInOrder("ADD"), (VC{ 'A', 'D' })); + ASSERT_EQ(uniqueInOrder("ABBCcAD"), (VC{ 'A', 'B', 'C', 'c', 'A', 'D' })); + ASSERT_EQ(uniqueInOrder(VI{ 1,2,3,3 }), (VI{ 1,2,3 })); + ASSERT_EQ(uniqueInOrder(VC{ 'a','b','b' }), (VC{ 'a','b' })); +}; + template std::vector sol(const std::vector&iterable) { + std::vector res = iterable; + res.erase(std::unique(begin(res), end(res)), end(res)); + return res; + } + + std::vector sol(const std::string & iterable) { + std::string res = iterable; + res.erase(std::unique(begin(res), end(res)), end(res)); + return std::vector{begin(res), end(res)}; + } + TEST(UniqueInOrder, RandomTests) { + std::string base = "abcXYZ"; + for (unsigned int i = 0; i < 40; i++) { + unsigned int testCase = randint(0, 2), inputLen = randint(3, 40); + if (testCase == 0) { + std::string iterable; + for (; inputLen--;) iterable += base[randint(0, base.size() - 1)]; + std::cout << "Testing for '" << iterable << "'\n\n"; + ASSERT_EQ(uniqueInOrder(iterable), sol(iterable)); + } + else if (testCase == 1) { + std::vector iterable; + for (; inputLen--;) iterable.push_back(base[randint(0, base.size() - 1)]); + std::cout << "Testing for [" << printVec(iterable) << "]\n\n"; + ASSERT_EQ(uniqueInOrder(iterable), sol(iterable)); + } + else { + std::vector iterable; + for (; inputLen--;) iterable.push_back(randint(-5, 5)); + std::cout << "Testing for [" << printVec(iterable) << "]\n\n"; + ASSERT_EQ(uniqueInOrder(iterable), sol(iterable)); + } + } + }; \ No newline at end of file From f5d1a4ddb60f67bdd598d632581b9b7794e28022 Mon Sep 17 00:00:00 2001 From: hobovsky Date: Wed, 24 Jan 2024 17:52:41 +0100 Subject: [PATCH 11/26] Add missing newlines around `` --- gtest/docker_image/tests/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gtest/docker_image/tests/main.cpp b/gtest/docker_image/tests/main.cpp index 445acab..29b1926 100644 --- a/gtest/docker_image/tests/main.cpp +++ b/gtest/docker_image/tests/main.cpp @@ -23,7 +23,7 @@ class QualifiedReporter : public EmptyTestEventListener { } void OnTestSuiteStart(const TestSuite& test_suite) override { - fprintf(stdout, "%s", test_suite.name()); + fprintf(stdout, "\n%s\n", test_suite.name()); } void OnTestSuiteEnd(const TestSuite& test_suite) override { fprintf(stdout, "\n%ld\n", test_suite.elapsed_time()); From 18e7a0b22161786676bc6326e4c0460898c83ad7 Mon Sep 17 00:00:00 2001 From: hobovsky Date: Thu, 25 Jan 2024 01:52:02 +0100 Subject: [PATCH 12/26] Example with parametrized tests --- gtest/README.md | 1 + .../ExampleParametrized/include/preloaded.h | 0 .../ExampleParametrized/include/solution.h | 1 + .../ExampleParametrized/src/preloaded.cpp | 0 .../ExampleParametrized/src/solution.cpp | 3 ++ .../tests/test_solution.cpp | 49 +++++++++++++++++++ 6 files changed, 54 insertions(+) create mode 100644 gtest/kata_code/ExampleParametrized/include/preloaded.h create mode 100644 gtest/kata_code/ExampleParametrized/include/solution.h create mode 100644 gtest/kata_code/ExampleParametrized/src/preloaded.cpp create mode 100644 gtest/kata_code/ExampleParametrized/src/solution.cpp create mode 100644 gtest/kata_code/ExampleParametrized/tests/test_solution.cpp diff --git a/gtest/README.md b/gtest/README.md index 14cc214..794c007 100644 --- a/gtest/README.md +++ b/gtest/README.md @@ -25,6 +25,7 @@ The `kata_code` directory contains source file of a kata as produced by the CW p ## Example kata +- `ExampleParametrized` - a set of parametrized/generated tests to evaluate output of the reporter. - `OriginalExample` - the original example of Google Test tests prepared by kazk. It uses no CW preprocessor markers, and only `preloaded.h` and `solution.cpp` are relevant. `preloaded.cpp` and `solution.h` are also saved, but are irrelevant, and cause no problems when buiding and testing the kata. - `UniqueInOrder` - a kata which requires separate cpp file and header file for solution, because it requires users to implement two functions: one is a template, and one is a "normal" function. It uses CW preprocessor markers to split submitted solution snippet into two files. - `Cafeteria` - a kata with no preprocessing markers, with a complex preloaded, where both solution and preloaded are treated as headers, and the cpp part of both gets (apparently) ignored. It also defines custom `operator <<` for the preloaded types, which is used by GTest to present instances in failure messages. To be honest, I am not sure why this example works because I expected it to fail with linker errors caused by standalone definitions of `operator<<`. I need to take a better look. diff --git a/gtest/kata_code/ExampleParametrized/include/preloaded.h b/gtest/kata_code/ExampleParametrized/include/preloaded.h new file mode 100644 index 0000000..e69de29 diff --git a/gtest/kata_code/ExampleParametrized/include/solution.h b/gtest/kata_code/ExampleParametrized/include/solution.h new file mode 100644 index 0000000..f299375 --- /dev/null +++ b/gtest/kata_code/ExampleParametrized/include/solution.h @@ -0,0 +1 @@ +bool evenOrOdd(int n); \ No newline at end of file diff --git a/gtest/kata_code/ExampleParametrized/src/preloaded.cpp b/gtest/kata_code/ExampleParametrized/src/preloaded.cpp new file mode 100644 index 0000000..e69de29 diff --git a/gtest/kata_code/ExampleParametrized/src/solution.cpp b/gtest/kata_code/ExampleParametrized/src/solution.cpp new file mode 100644 index 0000000..f9e5c92 --- /dev/null +++ b/gtest/kata_code/ExampleParametrized/src/solution.cpp @@ -0,0 +1,3 @@ +bool evenOrOdd(int n) { + return n % 2 == 0; +} \ No newline at end of file diff --git a/gtest/kata_code/ExampleParametrized/tests/test_solution.cpp b/gtest/kata_code/ExampleParametrized/tests/test_solution.cpp new file mode 100644 index 0000000..7206665 --- /dev/null +++ b/gtest/kata_code/ExampleParametrized/tests/test_solution.cpp @@ -0,0 +1,49 @@ +#include +#include +#include + +namespace { + TEST(FixedTests, test_0) { + EXPECT_EQ(evenOrOdd(0), true) << "evenOrOdd(0)"; + } + TEST(FixedTests, test_1) { + EXPECT_EQ(evenOrOdd(1), false) << "evenOrOdd(1)"; + } + TEST(FixedTests, test_minus_1) { + EXPECT_EQ(evenOrOdd(-1), false) << "evenOrOdd(-1)"; + } + + typedef std::pair MyTestCase; + class RandomTests: public ::testing::TestWithParam { + public: + int n; + bool exp; + + void SetUp() override { + auto param = GetParam(); + n = param.first; + exp = param.second; + } + }; + + static std::vector generateTests() { + std::vector cases; + for(int i=0; i<5; ++i) { + cases.push_back({ i * 2 + 540, true }); + cases.push_back({ i * 2 + 947, false}); + } + return cases; + } + + TEST_P(RandomTests, PassingCases) { + EXPECT_EQ(evenOrOdd(n), exp) << "evenOrOdd(" << n << ")"; + } + TEST_P(RandomTests, FailingCases) { + EXPECT_EQ(evenOrOdd(n), true) << "evenOrOdd(" << n << ")"; + } + INSTANTIATE_TEST_SUITE_P(BasicForm,RandomTests,::testing::ValuesIn(generateTests())); + INSTANTIATE_TEST_SUITE_P(WithNameGen,RandomTests,::testing::ValuesIn(generateTests()), + [](const testing::TestParamInfo& info) { + return "evenOrOdd_" + std::to_string(info.param.first); + }); +} \ No newline at end of file From 709f7bbc55f8f9ab43134fc2cfd348d26cc805c7 Mon Sep 17 00:00:00 2001 From: hobovsky Date: Thu, 25 Jan 2024 02:20:41 +0100 Subject: [PATCH 13/26] Example with exceptions --- gtest/README.md | 1 + .../ExampleExceptions/include/preloaded.h | 0 .../ExampleExceptions/include/solution.h | 1 + .../ExampleExceptions/src/preloaded.cpp | 0 .../ExampleExceptions/src/solution.cpp | 5 ++++ .../ExampleExceptions/tests/test_solution.cpp | 26 +++++++++++++++++++ 6 files changed, 33 insertions(+) create mode 100644 gtest/kata_code/ExampleExceptions/include/preloaded.h create mode 100644 gtest/kata_code/ExampleExceptions/include/solution.h create mode 100644 gtest/kata_code/ExampleExceptions/src/preloaded.cpp create mode 100644 gtest/kata_code/ExampleExceptions/src/solution.cpp create mode 100644 gtest/kata_code/ExampleExceptions/tests/test_solution.cpp diff --git a/gtest/README.md b/gtest/README.md index 794c007..fe6b5f2 100644 --- a/gtest/README.md +++ b/gtest/README.md @@ -25,6 +25,7 @@ The `kata_code` directory contains source file of a kata as produced by the CW p ## Example kata +- `ExampleExceptions` - a set of test cases to evaluate output of the reporter in case of expected and unexpected exceptions. - `ExampleParametrized` - a set of parametrized/generated tests to evaluate output of the reporter. - `OriginalExample` - the original example of Google Test tests prepared by kazk. It uses no CW preprocessor markers, and only `preloaded.h` and `solution.cpp` are relevant. `preloaded.cpp` and `solution.h` are also saved, but are irrelevant, and cause no problems when buiding and testing the kata. - `UniqueInOrder` - a kata which requires separate cpp file and header file for solution, because it requires users to implement two functions: one is a template, and one is a "normal" function. It uses CW preprocessor markers to split submitted solution snippet into two files. diff --git a/gtest/kata_code/ExampleExceptions/include/preloaded.h b/gtest/kata_code/ExampleExceptions/include/preloaded.h new file mode 100644 index 0000000..e69de29 diff --git a/gtest/kata_code/ExampleExceptions/include/solution.h b/gtest/kata_code/ExampleExceptions/include/solution.h new file mode 100644 index 0000000..6014860 --- /dev/null +++ b/gtest/kata_code/ExampleExceptions/include/solution.h @@ -0,0 +1 @@ +int throwOrNot(int n); \ No newline at end of file diff --git a/gtest/kata_code/ExampleExceptions/src/preloaded.cpp b/gtest/kata_code/ExampleExceptions/src/preloaded.cpp new file mode 100644 index 0000000..e69de29 diff --git a/gtest/kata_code/ExampleExceptions/src/solution.cpp b/gtest/kata_code/ExampleExceptions/src/solution.cpp new file mode 100644 index 0000000..5e1f6e9 --- /dev/null +++ b/gtest/kata_code/ExampleExceptions/src/solution.cpp @@ -0,0 +1,5 @@ +#include +int throwOrNot(int n) { + std::vector v = { 1, 2, 3 }; + return v.at(n); +} \ No newline at end of file diff --git a/gtest/kata_code/ExampleExceptions/tests/test_solution.cpp b/gtest/kata_code/ExampleExceptions/tests/test_solution.cpp new file mode 100644 index 0000000..ccfaa88 --- /dev/null +++ b/gtest/kata_code/ExampleExceptions/tests/test_solution.cpp @@ -0,0 +1,26 @@ +#include +#include + +namespace { + + TEST(ExceptionTests, ExpectedThrownOutsideOfAssertion) { + int actual = throwOrNot(7); + EXPECT_EQ(actual, 8) << "Answer 8 is incorrect, but should not get here"; + } + TEST(ExceptionTests, ExpectedNone_ActualNone) { + int actval = 10; + EXPECT_NO_THROW( actval = throwOrNot(2) ) << "throwOrNot(2) should not throw"; + EXPECT_EQ(actval, 3) << "incorrect answer for throwOrNot(2)"; + } + TEST(ExceptionTests, ExpectedNone_ActualThrown) { + int actval = 10; + EXPECT_NO_THROW( actval = throwOrNot(7) ) << "throwOrNot(7) should not throw"; + EXPECT_EQ(actval, 8) << "incorrect answer for throwOrNot(7)"; + } + TEST(ExceptionTests, ExpectedThrown_ActualNone) { + EXPECT_ANY_THROW( throwOrNot(0) ) << "should throw for throwOrNot(7)"; + } + TEST(ExceptionTests, ExpectedThrown_ActualThrown) { + EXPECT_ANY_THROW( throwOrNot(7) ) << "should throw for throwOrNot(7)"; + } +} \ No newline at end of file From 2cea2131648e1f465218d4b52c6b6cc5dc00e30d Mon Sep 17 00:00:00 2001 From: hobovsky Date: Thu, 25 Jan 2024 02:40:43 +0100 Subject: [PATCH 14/26] Example with crashes --- gtest/kata_code/ExampleCrash/include/preloaded.h | 0 gtest/kata_code/ExampleCrash/include/solution.h | 1 + gtest/kata_code/ExampleCrash/src/preloaded.cpp | 0 gtest/kata_code/ExampleCrash/src/solution.cpp | 4 ++++ .../kata_code/ExampleCrash/tests/test_solution.cpp | 13 +++++++++++++ 5 files changed, 18 insertions(+) create mode 100644 gtest/kata_code/ExampleCrash/include/preloaded.h create mode 100644 gtest/kata_code/ExampleCrash/include/solution.h create mode 100644 gtest/kata_code/ExampleCrash/src/preloaded.cpp create mode 100644 gtest/kata_code/ExampleCrash/src/solution.cpp create mode 100644 gtest/kata_code/ExampleCrash/tests/test_solution.cpp diff --git a/gtest/kata_code/ExampleCrash/include/preloaded.h b/gtest/kata_code/ExampleCrash/include/preloaded.h new file mode 100644 index 0000000..e69de29 diff --git a/gtest/kata_code/ExampleCrash/include/solution.h b/gtest/kata_code/ExampleCrash/include/solution.h new file mode 100644 index 0000000..6f8e0c9 --- /dev/null +++ b/gtest/kata_code/ExampleCrash/include/solution.h @@ -0,0 +1 @@ +int crashOrNot(int n); \ No newline at end of file diff --git a/gtest/kata_code/ExampleCrash/src/preloaded.cpp b/gtest/kata_code/ExampleCrash/src/preloaded.cpp new file mode 100644 index 0000000..e69de29 diff --git a/gtest/kata_code/ExampleCrash/src/solution.cpp b/gtest/kata_code/ExampleCrash/src/solution.cpp new file mode 100644 index 0000000..2159b84 --- /dev/null +++ b/gtest/kata_code/ExampleCrash/src/solution.cpp @@ -0,0 +1,4 @@ +#include +int crashOrNot(int n) { + return n > 0 ? 42 : ((int*)NULL)[1]; +} \ No newline at end of file diff --git a/gtest/kata_code/ExampleCrash/tests/test_solution.cpp b/gtest/kata_code/ExampleCrash/tests/test_solution.cpp new file mode 100644 index 0000000..99a5baf --- /dev/null +++ b/gtest/kata_code/ExampleCrash/tests/test_solution.cpp @@ -0,0 +1,13 @@ +#include +#include + +namespace { + TEST(ExceptionTests, CrashInAssertion) { + EXPECT_EQ(1, crashOrNot(0)) << "incorrect answer for crashOrNot(0)"; + } + + TEST(CrashTests, CrashOutsideOfAssertion) { + int actual = crashOrNot(-13); + EXPECT_EQ(actual, -12) << "Answer -12 is incorrect, but should not get here"; + } +} \ No newline at end of file From c73098248899dd418dbb19cf13ee599608aff182 Mon Sep 17 00:00:00 2001 From: hobovsky Date: Thu, 25 Jan 2024 14:33:07 +0100 Subject: [PATCH 15/26] Add/fix missing includes and macros --- gtest/docker_image/tests/main.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/gtest/docker_image/tests/main.cpp b/gtest/docker_image/tests/main.cpp index 29b1926..3df4561 100644 --- a/gtest/docker_image/tests/main.cpp +++ b/gtest/docker_image/tests/main.cpp @@ -1,6 +1,12 @@ -#include +#include +#include +#include + #include +using ::std::fprintf; +using ::std::fflush; + using ::testing::EmptyTestEventListener; using ::testing::InitGoogleTest; using ::testing::Test; @@ -26,7 +32,7 @@ class QualifiedReporter : public EmptyTestEventListener { fprintf(stdout, "\n%s\n", test_suite.name()); } void OnTestSuiteEnd(const TestSuite& test_suite) override { - fprintf(stdout, "\n%ld\n", test_suite.elapsed_time()); + fprintf(stdout, "\n%" PRId64 "\n", test_suite.elapsed_time()); fflush(stdout); } @@ -53,7 +59,7 @@ class QualifiedReporter : public EmptyTestEventListener { if (result.Passed()) { fprintf(stdout, "\nTest Passed\n"); } - fprintf(stdout, "\n%ld\n", result.elapsed_time()); + fprintf(stdout, "\n%" PRId64 "\n", result.elapsed_time()); fflush(stdout); } From 3b3cd179e0b856376bc87127f0616a0358ab17b3 Mon Sep 17 00:00:00 2001 From: hobovsky Date: Thu, 25 Jan 2024 16:01:27 +0100 Subject: [PATCH 16/26] Showcase of assertions and matchers most useful in context of CW --- .../include/preloaded.h | 0 .../ExampleFailureMessages/include/solution.h | 2 + .../ExampleFailureMessages/src/preloaded.cpp | 0 .../ExampleFailureMessages/src/solution.cpp | 7 ++ .../tests/test_solution.cpp | 68 +++++++++++++++++++ 5 files changed, 77 insertions(+) create mode 100644 gtest/kata_code/ExampleFailureMessages/include/preloaded.h create mode 100644 gtest/kata_code/ExampleFailureMessages/include/solution.h create mode 100644 gtest/kata_code/ExampleFailureMessages/src/preloaded.cpp create mode 100644 gtest/kata_code/ExampleFailureMessages/src/solution.cpp create mode 100644 gtest/kata_code/ExampleFailureMessages/tests/test_solution.cpp diff --git a/gtest/kata_code/ExampleFailureMessages/include/preloaded.h b/gtest/kata_code/ExampleFailureMessages/include/preloaded.h new file mode 100644 index 0000000..e69de29 diff --git a/gtest/kata_code/ExampleFailureMessages/include/solution.h b/gtest/kata_code/ExampleFailureMessages/include/solution.h new file mode 100644 index 0000000..af31bc8 --- /dev/null +++ b/gtest/kata_code/ExampleFailureMessages/include/solution.h @@ -0,0 +1,2 @@ +int alwaysFails(int n); +bool isEven(int n); \ No newline at end of file diff --git a/gtest/kata_code/ExampleFailureMessages/src/preloaded.cpp b/gtest/kata_code/ExampleFailureMessages/src/preloaded.cpp new file mode 100644 index 0000000..e69de29 diff --git a/gtest/kata_code/ExampleFailureMessages/src/solution.cpp b/gtest/kata_code/ExampleFailureMessages/src/solution.cpp new file mode 100644 index 0000000..7967a87 --- /dev/null +++ b/gtest/kata_code/ExampleFailureMessages/src/solution.cpp @@ -0,0 +1,7 @@ +int alwaysFails(int n) { + return n; +} + +bool isEven(int n) { + return alwaysFails(n) % 2 == 0; +} \ No newline at end of file diff --git a/gtest/kata_code/ExampleFailureMessages/tests/test_solution.cpp b/gtest/kata_code/ExampleFailureMessages/tests/test_solution.cpp new file mode 100644 index 0000000..d11b08a --- /dev/null +++ b/gtest/kata_code/ExampleFailureMessages/tests/test_solution.cpp @@ -0,0 +1,68 @@ +#include + +#include +#include + +#include + +using namespace ::testing; + +// Each assertion and matcher is presented with and +// without an optional failure message. + +namespace { + TEST(FailureMessageTests, Assertions) { + EXPECT_TRUE( false ); + EXPECT_TRUE( false ) << "Tested assertion: EXPECT_TRUE"; + + EXPECT_FALSE( true ); + EXPECT_FALSE( true ) << "Tested assertion: EXPECT_FALSE"; + + EXPECT_EQ( alwaysFails(13), 42 ); + EXPECT_EQ( alwaysFails(13), 42 ) << "Tested assertion: EXPECT_EQ"; + + EXPECT_NE( alwaysFails(42), 42 ); + EXPECT_NE( alwaysFails(42), 42 ) << "Tested assertion: EXPECT_NE"; + + EXPECT_LT( alwaysFails(256), 42 ); + EXPECT_LT( alwaysFails(256), 42 ) << "Tested assertion: EXPECT_LT"; + + EXPECT_STREQ( "13", "42" ); + EXPECT_STREQ( "13", "42" ) << "Tested assertion: EXPECT_STREQ"; + + EXPECT_NEAR( (double)alwaysFails(13), 42.0, 1e-3 ); + EXPECT_NEAR( (double)alwaysFails(13), 42.0, 1e-3 ) << "Tested assertion: EXPECT_NEAR"; + + EXPECT_PRED1( isEven, 13 ); + EXPECT_PRED1( isEven, 13 ) << "Tested assertion: EXPECT_PRED1"; + } + + TEST(FailureMessageTests, Matchers) { + EXPECT_THAT( false, ::testing::IsTrue() ); + EXPECT_THAT( false, ::testing::IsTrue() ) << "Tested matcher: IsTrue"; + + EXPECT_THAT( true, IsFalse() ); + EXPECT_THAT( true, IsFalse() ) << "Tested matcher: IsFalse"; + + EXPECT_THAT( alwaysFails(13), Eq(42) ); + EXPECT_THAT( alwaysFails(13), Eq(42) ) << "Tested Matcher: Eq"; + + EXPECT_THAT( alwaysFails(42), Ne(42) ); + EXPECT_THAT( alwaysFails(42), Ne(42) ) << "Tested matcher: Ne"; + + EXPECT_THAT( alwaysFails(256), Lt(42) ); + EXPECT_THAT( alwaysFails(256), Lt(42) ) << "Tested matcher: Lt"; + + EXPECT_THAT( "13", StrEq("42") ); + EXPECT_THAT( "13", StrEq("42") ) << "Tested matcher: StrEq"; + + EXPECT_THAT( (double)alwaysFails(13), DoubleNear(42.0, 1e-3) ); + EXPECT_THAT( (double)alwaysFails(13), DoubleNear(42.0, 1e-3) ) << "Tested matcher: DoubleNear"; + + EXPECT_THAT( std::vector(2), ContainerEq(std::vector(3)) ); + EXPECT_THAT( std::vector(2), ContainerEq(std::vector(3)) ) << "Tested matcher: ContainerEq"; + + EXPECT_THAT( std::vector{2}, WhenSorted(ContainerEq(std::vector{3}))); + EXPECT_THAT( std::vector{2}, WhenSorted(ContainerEq(std::vector{3}))) << "Tested matcher: WhenSorted ContainerEq"; + } +} \ No newline at end of file From 2a1c45a8666745bce52447c244b53e45370ccd93 Mon Sep 17 00:00:00 2001 From: hobovsky Date: Thu, 25 Jan 2024 16:04:58 +0100 Subject: [PATCH 17/26] Update README.md --- gtest/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/gtest/README.md b/gtest/README.md index fe6b5f2..14437ed 100644 --- a/gtest/README.md +++ b/gtest/README.md @@ -26,6 +26,7 @@ The `kata_code` directory contains source file of a kata as produced by the CW p ## Example kata - `ExampleExceptions` - a set of test cases to evaluate output of the reporter in case of expected and unexpected exceptions. +- `ExampleFailureMessages` - showcase of assertions and matchers most useful for CW authors. - `ExampleParametrized` - a set of parametrized/generated tests to evaluate output of the reporter. - `OriginalExample` - the original example of Google Test tests prepared by kazk. It uses no CW preprocessor markers, and only `preloaded.h` and `solution.cpp` are relevant. `preloaded.cpp` and `solution.h` are also saved, but are irrelevant, and cause no problems when buiding and testing the kata. - `UniqueInOrder` - a kata which requires separate cpp file and header file for solution, because it requires users to implement two functions: one is a template, and one is a "normal" function. It uses CW preprocessor markers to split submitted solution snippet into two files. From e022dce7249361e21fa9e73869439ed7d108bab0 Mon Sep 17 00:00:00 2001 From: hobovsky Date: Thu, 25 Jan 2024 16:50:07 +0100 Subject: [PATCH 18/26] Presentation of results for various kinds of test fixtures --- .../ExampleOrganization/include/preloaded.h | 0 .../ExampleOrganization/include/solution.h | 0 .../ExampleOrganization/src/preloaded.cpp | 0 .../ExampleOrganization/src/solution.cpp | 0 .../tests/test_solution.cpp | 99 +++++++++++++++++++ 5 files changed, 99 insertions(+) create mode 100644 gtest/kata_code/ExampleOrganization/include/preloaded.h create mode 100644 gtest/kata_code/ExampleOrganization/include/solution.h create mode 100644 gtest/kata_code/ExampleOrganization/src/preloaded.cpp create mode 100644 gtest/kata_code/ExampleOrganization/src/solution.cpp create mode 100644 gtest/kata_code/ExampleOrganization/tests/test_solution.cpp diff --git a/gtest/kata_code/ExampleOrganization/include/preloaded.h b/gtest/kata_code/ExampleOrganization/include/preloaded.h new file mode 100644 index 0000000..e69de29 diff --git a/gtest/kata_code/ExampleOrganization/include/solution.h b/gtest/kata_code/ExampleOrganization/include/solution.h new file mode 100644 index 0000000..e69de29 diff --git a/gtest/kata_code/ExampleOrganization/src/preloaded.cpp b/gtest/kata_code/ExampleOrganization/src/preloaded.cpp new file mode 100644 index 0000000..e69de29 diff --git a/gtest/kata_code/ExampleOrganization/src/solution.cpp b/gtest/kata_code/ExampleOrganization/src/solution.cpp new file mode 100644 index 0000000..e69de29 diff --git a/gtest/kata_code/ExampleOrganization/tests/test_solution.cpp b/gtest/kata_code/ExampleOrganization/tests/test_solution.cpp new file mode 100644 index 0000000..607a535 --- /dev/null +++ b/gtest/kata_code/ExampleOrganization/tests/test_solution.cpp @@ -0,0 +1,99 @@ +#include +#include + +namespace namespace1 { + + TEST(RegularTestSuite1, Test1) { + ASSERT_EQ(1, 2); + } + + TEST(RegularTestSuite1, Test2) { + ASSERT_EQ(1, 2); + } + + TEST(RegularTestSuite2, Test1) { + ASSERT_EQ(1, 2); + } + + TEST(RegularTestSuite2, Test2) { + ASSERT_EQ(1, 2); + } +} + +namespace namespace2 { + + TEST(RegularTestSuite1, Test1) { + ASSERT_EQ(1, 2); + } + + TEST(RegularTestSuite1, Test2) { + ASSERT_EQ(1, 2); + } + + TEST(RegularTestSuite2, Test1) { + ASSERT_EQ(1, 2); + } + + TEST(RegularTestSuite2, Test2) { + ASSERT_EQ(1, 2); + } +} + +namespace namepace3 { + + class TestFixtureClass1: public ::testing::Test {}; + class TestFixtureClass2: public ::testing::Test {}; + + TEST_F(TestFixtureClass1, Test1) { + ASSERT_EQ(1, 2); + } + TEST_F(TestFixtureClass1, Test2) { + ASSERT_EQ(1, 2); + } + TEST_F(TestFixtureClass2, Test1) { + ASSERT_EQ(1, 2); + } + TEST_F(TestFixtureClass2, Test2) { + ASSERT_EQ(1, 2); + } +} + +namespace namespace4 { + + typedef std::pair MyTestCase; + class ParametrizedTestsFixture1: public ::testing::TestWithParam { }; + class ParametrizedTestsFixture2: public ::testing::TestWithParam { }; + + static std::vector generateTests() { + std::vector cases; + for(int i=0; i<5; ++i) { + cases.push_back({ i * 2 + 540, true }); + cases.push_back({ i * 2 + 947, false}); + } + return cases; + } + + TEST_P(ParametrizedTestsFixture1, TestsSet1) { + ASSERT_EQ(1, 2); + } + TEST_P(ParametrizedTestsFixture1, TestsSet2) { + ASSERT_EQ(1, 2); + } + TEST_P(ParametrizedTestsFixture2, TestsSet1) { + ASSERT_EQ(1, 2); + } + TEST_P(ParametrizedTestsFixture2, TestsSet2) { + ASSERT_EQ(1, 2); + } + + INSTANTIATE_TEST_SUITE_P(BasicForm,ParametrizedTestsFixture1,::testing::ValuesIn(generateTests())); + INSTANTIATE_TEST_SUITE_P(WithNameGen,ParametrizedTestsFixture1,::testing::ValuesIn(generateTests()), + [](const testing::TestParamInfo& info) { + return "parameters_for_fixture1_are_" + std::to_string(info.param.first); + }); + INSTANTIATE_TEST_SUITE_P(BasicForm,ParametrizedTestsFixture2,::testing::ValuesIn(generateTests())); + INSTANTIATE_TEST_SUITE_P(WithNameGen,ParametrizedTestsFixture2,::testing::ValuesIn(generateTests()), + [](const testing::TestParamInfo& info) { + return "parameters_for_fixture2_are_" + std::to_string(info.param.first); + }); +} \ No newline at end of file From 95f6b5c85f9b543c9a1ddfe7d1beffa3fdbe6637 Mon Sep 17 00:00:00 2001 From: hobovsky Date: Thu, 25 Jan 2024 16:50:34 +0100 Subject: [PATCH 19/26] Remove test_Case_name from test titles --- gtest/docker_image/tests/main.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/gtest/docker_image/tests/main.cpp b/gtest/docker_image/tests/main.cpp index 3df4561..ce40d3d 100644 --- a/gtest/docker_image/tests/main.cpp +++ b/gtest/docker_image/tests/main.cpp @@ -36,10 +36,8 @@ class QualifiedReporter : public EmptyTestEventListener { fflush(stdout); } - // TODO Group by `test_case_name`? It's not identifiers so uniqueness is not guaranteed. - // Called before a test starts. void OnTestStart(const TestInfo& test_info) override { - fprintf(stdout, "\n%s %s\n", test_info.test_case_name(), test_info.name()); + fprintf(stdout, "\n%s\n", test_info.name()); fflush(stdout); } From 2d17807d0a3a01afcc976b1735fc8fe9535e302e Mon Sep 17 00:00:00 2001 From: hobovsky Date: Thu, 25 Jan 2024 16:52:24 +0100 Subject: [PATCH 20/26] Presentation of results for various kinds of test fixtures --- gtest/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/gtest/README.md b/gtest/README.md index 14437ed..e9ebb99 100644 --- a/gtest/README.md +++ b/gtest/README.md @@ -27,6 +27,7 @@ The `kata_code` directory contains source file of a kata as produced by the CW p - `ExampleExceptions` - a set of test cases to evaluate output of the reporter in case of expected and unexpected exceptions. - `ExampleFailureMessages` - showcase of assertions and matchers most useful for CW authors. +- `ExampleOrganization` - a set of test fixtures instantiated in various ways evaluate output of the reporter (especially grouping of tests). - `ExampleParametrized` - a set of parametrized/generated tests to evaluate output of the reporter. - `OriginalExample` - the original example of Google Test tests prepared by kazk. It uses no CW preprocessor markers, and only `preloaded.h` and `solution.cpp` are relevant. `preloaded.cpp` and `solution.h` are also saved, but are irrelevant, and cause no problems when buiding and testing the kata. - `UniqueInOrder` - a kata which requires separate cpp file and header file for solution, because it requires users to implement two functions: one is a template, and one is a "normal" function. It uses CW preprocessor markers to split submitted solution snippet into two files. From 9cf36c33591138939070267b68db5c459d4d5954 Mon Sep 17 00:00:00 2001 From: hobovsky Date: Thu, 25 Jan 2024 17:29:59 +0100 Subject: [PATCH 21/26] Move preprocessor out of the GTest folder --- gtest/preprocessor/cwpp.cpp | 80 -------------------- {gtest/preprocessor => preprocessor}/cwpp.ts | 0 2 files changed, 80 deletions(-) delete mode 100644 gtest/preprocessor/cwpp.cpp rename {gtest/preprocessor => preprocessor}/cwpp.ts (100%) diff --git a/gtest/preprocessor/cwpp.cpp b/gtest/preprocessor/cwpp.cpp deleted file mode 100644 index 233c300..0000000 --- a/gtest/preprocessor/cwpp.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include -#include -#include -#include - -using std::filesystem::exists; -using std::filesystem::path; - -int main(int argc, char* argv[]) -{ - if (argc != 3) { - std::cerr << "Usage:\n\tcwpp [PRELOADED|SOLUTION] [path to cw snippet]"; - return 1; - } - - std::string snippetTag = argv[1]; - if(snippetTag != "PRELOADED" && snippetTag != "SOLUTION") { - std::cerr << "Usage:\n\tcwpp [PRELOADED|SOLUTION] [path to cw snippet]"; - return 1; - } - - auto snippetFile = path(argv[2]); - if (!exists(snippetFile)) { - std::cerr << "Snippet file [" << snippetFile << "] does not exist."; - return 1; - } - std::ifstream inputStream(snippetFile); - std::ofstream headerStream(".tmp_header"), implStream(".tmp_cpp"); - - enum LastTag { NONE, HEADER, CPP }; - - bool hasHeaderTag = false; - bool hasImplTag = false; - LastTag lastTag = NONE; - - std::string headerTag = snippetTag == "SOLUTION" ? "//! CW_SOLUTION_H" : "//! CW_PRELOADED_H"; - std::string implTag = snippetTag == "SOLUTION" ? "//! CW_SOLUTION_CPP" : "//! CW_PRELOADED_CPP"; - - std::string line; - while (std::getline(inputStream, line)) { - if (line == headerTag) { - hasHeaderTag = true; - lastTag = HEADER; - } else if (line == implTag) { - hasImplTag = true; - lastTag = CPP; - } else if (lastTag == HEADER) { - headerStream << line << std::endl; - } else if (lastTag == CPP) { - implStream << line << std::endl; - } - } - inputStream.close(); - headerStream.close(); - implStream.close(); - - std::string targetHeader, targetImpl; - if (snippetTag == "SOLUTION") { - targetHeader = "include/solution.h"; - targetImpl = "src/solution.cpp"; - } - else { - targetHeader = "include/preloaded.h"; - targetImpl = "src/preloaded.cpp"; - } - - if (hasHeaderTag && hasImplTag) { - std::filesystem::copy_file(path(".tmp_header"), path(targetHeader)); - std::filesystem::copy_file(path(".tmp_cpp"), path(targetImpl)); - } else if (!hasHeaderTag && !hasImplTag) { - std::filesystem::copy_file(path(snippetFile), path(targetHeader)); - std::filesystem::copy_file(path(snippetFile), path(targetImpl)); - } else if (hasHeaderTag && !hasImplTag) { - std::filesystem::copy_file(path(".tmp_header"), path(targetHeader)); - std::ofstream(targetImpl).close(); - } else if (!hasHeaderTag && hasImplTag) { - std::ofstream(targetHeader).close(); - std::filesystem::copy_file(path(".tmp_cpp"), path(targetImpl)); - } -} diff --git a/gtest/preprocessor/cwpp.ts b/preprocessor/cwpp.ts similarity index 100% rename from gtest/preprocessor/cwpp.ts rename to preprocessor/cwpp.ts From 701a234d0691d8124fc7a21af449a6507a8cbd4e Mon Sep 17 00:00:00 2001 From: hobovsky Date: Thu, 25 Jan 2024 22:34:44 +0100 Subject: [PATCH 22/26] Docker image for Catch2 --- catch2/Dockerfile | 27 ++++++ catch2/README.md | 27 +++++- catch2/build_image.bat | 1 + catch2/docker_image/.gitignore | 1 + catch2/{ => docker_image}/CMakeLists.txt | 0 catch2/{ => docker_image}/configure.sh | 2 +- catch2/docker_image/run-kata.sh | 3 + catch2/{ => docker_image}/run-tests.sh | 2 +- catch2/{ => docker_image}/src/CMakeLists.txt | 5 +- .../{ => docker_image}/tests/CMakeLists.txt | 2 +- .../docker_image/tests/codewars_reporter.cpp | 58 +++++++++++++ .../OriginalExample/include/preloaded.h} | 4 +- .../OriginalExample/include/solution.h | 0 .../OriginalExample/src/preloaded.cpp | 0 .../OriginalExample}/src/solution.cpp | 2 +- .../OriginalExample}/tests/test_solution.cpp | 4 +- catch2/run_image.bat | 8 ++ catch2/tests/codewars_reporter.cpp | 85 ------------------- 18 files changed, 132 insertions(+), 99 deletions(-) create mode 100644 catch2/Dockerfile create mode 100644 catch2/build_image.bat create mode 100644 catch2/docker_image/.gitignore rename catch2/{ => docker_image}/CMakeLists.txt (100%) rename catch2/{ => docker_image}/configure.sh (70%) mode change 100755 => 100644 create mode 100644 catch2/docker_image/run-kata.sh rename catch2/{ => docker_image}/run-tests.sh (84%) mode change 100755 => 100644 rename catch2/{ => docker_image}/src/CMakeLists.txt (68%) rename catch2/{ => docker_image}/tests/CMakeLists.txt (94%) create mode 100644 catch2/docker_image/tests/codewars_reporter.cpp rename catch2/{include/challenge.h => kata_code/OriginalExample/include/preloaded.h} (90%) create mode 100644 catch2/kata_code/OriginalExample/include/solution.h create mode 100644 catch2/kata_code/OriginalExample/src/preloaded.cpp rename catch2/{ => kata_code/OriginalExample}/src/solution.cpp (93%) rename catch2/{ => kata_code/OriginalExample}/tests/test_solution.cpp (97%) create mode 100644 catch2/run_image.bat delete mode 100644 catch2/tests/codewars_reporter.cpp diff --git a/catch2/Dockerfile b/catch2/Dockerfile new file mode 100644 index 0000000..1d385be --- /dev/null +++ b/catch2/Dockerfile @@ -0,0 +1,27 @@ +FROM alpine:latest + +RUN apk update \ + && apk upgrade \ + && apk add --no-cache \ + clang \ + clang-dev \ + alpine-sdk \ + dpkg \ + cmake \ + ccache \ + ninja + +RUN ln -sf /usr/bin/clang /usr/bin/cc \ + && ln -sf /usr/bin/clang++ /usr/bin/c++ \ + && update-alternatives --install /usr/bin/cc cc /usr/bin/clang 10\ + && update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 10\ + && update-alternatives --auto cc \ + && update-alternatives --auto c++ \ + && update-alternatives --display cc \ + && update-alternatives --display c++ \ + && ls -l /usr/bin/cc /usr/bin/c++ \ + && cc --version \ + && c++ --version + +WORKDIR /code +COPY docker_image . diff --git a/catch2/README.md b/catch2/README.md index 7a8d93c..e84bf04 100644 --- a/catch2/README.md +++ b/catch2/README.md @@ -1,4 +1,12 @@ -# Catch2 +# GoogleTest + +## Scripts for local Docker + +Locally I use Windows, that's why my scripts are batch files. +- `build-image.bat` builds an image to run C++ kata, +- `run-image.bat` runs code of a kata in a container. Use `run-image KATA-CODE` to run, whenre `KATA-CODE` is a name of one of subdirectories in `kata_code` directory. + +## Docker image > `CodewarsReporter` is unfinished. @@ -9,10 +17,21 @@ cmake --build build && ./build/tests/tests --reporter=codewars Use `./configure.sh` and `./run-tests.sh`. - We'll configure and build an example project while building the container image so each submission will only build the submitted code. ---- - I haven't figured out the timings of the test events used in the custom reporter yet. The tests can be written more naturally, but it might be difficult to get the output we want. + +The `snippets` directory contains snippets of example kata as they are submitted, before preprocessing. + +The `kata_code` directory contains source file of a kata as produced by the CW preprocessor. The files are cp-ed into a container. + +## Example kata + +- `ExampleExceptions` - a set of test cases to evaluate output of the reporter in case of expected and unexpected exceptions. +- `ExampleFailureMessages` - showcase of assertions and matchers most useful for CW authors. +- `ExampleOrganization` - a set of test fixtures instantiated in various ways evaluate output of the reporter (especially grouping of tests). +- `ExampleParametrized` - a set of parametrized/generated tests to evaluate output of the reporter. +- `OriginalExample` - the original example of Google Test tests prepared by kazk. It uses no CW preprocessor markers, and only `preloaded.h` and `solution.cpp` are relevant. `preloaded.cpp` and `solution.h` are also saved, but are irrelevant, and cause no problems when buiding and testing the kata. +- `UniqueInOrder` - a kata which requires separate cpp file and header file for solution, because it requires users to implement two functions: one is a template, and one is a "normal" function. It uses CW preprocessor markers to split submitted solution snippet into two files. +- `Cafeteria` - a kata with no preprocessing markers, with a complex preloaded, where both solution and preloaded are treated as headers, and the cpp part of both gets (apparently) ignored. It also defines custom `operator <<` for the preloaded types, which is used by GTest to present instances in failure messages. To be honest, I am not sure why this example works because I expected it to fail with linker errors caused by standalone definitions of `operator<<`. I need to take a better look. diff --git a/catch2/build_image.bat b/catch2/build_image.bat new file mode 100644 index 0000000..f46827d --- /dev/null +++ b/catch2/build_image.bat @@ -0,0 +1 @@ +docker build --tag cw-catch2-cpp . \ No newline at end of file diff --git a/catch2/docker_image/.gitignore b/catch2/docker_image/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/catch2/docker_image/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/catch2/CMakeLists.txt b/catch2/docker_image/CMakeLists.txt similarity index 100% rename from catch2/CMakeLists.txt rename to catch2/docker_image/CMakeLists.txt diff --git a/catch2/configure.sh b/catch2/docker_image/configure.sh old mode 100755 new mode 100644 similarity index 70% rename from catch2/configure.sh rename to catch2/docker_image/configure.sh index f9bd0b8..6646ddb --- a/catch2/configure.sh +++ b/catch2/docker_image/configure.sh @@ -1,2 +1,2 @@ -#!/bin/bash +#!/bin/sh cmake -S . -B build -G Ninja diff --git a/catch2/docker_image/run-kata.sh b/catch2/docker_image/run-kata.sh new file mode 100644 index 0000000..4aa9cda --- /dev/null +++ b/catch2/docker_image/run-kata.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +./configure.sh && ./run-tests.sh \ No newline at end of file diff --git a/catch2/run-tests.sh b/catch2/docker_image/run-tests.sh old mode 100755 new mode 100644 similarity index 84% rename from catch2/run-tests.sh rename to catch2/docker_image/run-tests.sh index ffe2095..830b5aa --- a/catch2/run-tests.sh +++ b/catch2/docker_image/run-tests.sh @@ -1,3 +1,3 @@ -#!/bin/bash +#!/bin/sh cmake --build build && ./build/tests/tests --reporter=codewars diff --git a/catch2/src/CMakeLists.txt b/catch2/docker_image/src/CMakeLists.txt similarity index 68% rename from catch2/src/CMakeLists.txt rename to catch2/docker_image/src/CMakeLists.txt index e1e18b4..73fdc2f 100644 --- a/catch2/src/CMakeLists.txt +++ b/catch2/docker_image/src/CMakeLists.txt @@ -11,8 +11,9 @@ FetchContent_Declare( FetchContent_MakeAvailable(fmt) -add_library(challenge solution.cpp) +add_library(challenge preloaded.cpp solution.cpp) target_include_directories(challenge PUBLIC ../include) target_compile_features(challenge PUBLIC cxx_std_20) target_compile_options(challenge PUBLIC -Wall -Wextra -O2) -target_link_libraries(challenge PUBLIC m crypto dl sqlite3 tbb fmt Threads::Threads) +# target_link_libraries(challenge PUBLIC m crypto dl sqlite3 tbb fmt Threads::Threads) +target_link_libraries(challenge PUBLIC m dl fmt Threads::Threads) diff --git a/catch2/tests/CMakeLists.txt b/catch2/docker_image/tests/CMakeLists.txt similarity index 94% rename from catch2/tests/CMakeLists.txt rename to catch2/docker_image/tests/CMakeLists.txt index 70c8a94..d681c4f 100644 --- a/catch2/tests/CMakeLists.txt +++ b/catch2/docker_image/tests/CMakeLists.txt @@ -22,7 +22,7 @@ add_executable(tests codewars_reporter.cpp test_solution.cpp) target_compile_features(tests PRIVATE cxx_std_20) target_compile_options(tests PRIVATE -Wall -Wextra -O2) # target_link_libraries(tests PRIVATE challenge Catch2::Catch2 m crypto dl sqlite3 tbb fmt Threads::Threads) -target_link_libraries(tests PRIVATE challenge Catch2::Catch2WithMain m crypto dl sqlite3 tbb fmt Threads::Threads) +target_link_libraries(tests PRIVATE challenge Catch2::Catch2WithMain m dl fmt Threads::Threads) # Not really necessary for CR, but allows running tests through CMake add_test(NAME tests COMMAND tests) diff --git a/catch2/docker_image/tests/codewars_reporter.cpp b/catch2/docker_image/tests/codewars_reporter.cpp new file mode 100644 index 0000000..5219a3d --- /dev/null +++ b/catch2/docker_image/tests/codewars_reporter.cpp @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include + +#include + +// TODO Complete custom reporter +// - https://github.com/catchorg/Catch2/blob/devel/docs/reporters.md +// - https://github.com/catchorg/Catch2/blob/devel/docs/reporter-events.md +// - https://github.com/catchorg/Catch2/blob/devel/docs/test-cases-and-sections.md +class CodewarsReporter : public Catch::StreamingReporterBase { +public: + using StreamingReporterBase::StreamingReporterBase; + + static std::string getDescription() { + return "Reporter for Codewars"; + } + + // Emitted before the first test case is executed. + void testRunStarting(Catch::TestRunInfo const& testRunInfo) override { + std::cout << "testRunStarting " << testRunInfo.name << '\n'; + } + + // Emitted after all the test cases have been executed. + void testRunEnded(__attribute__((unused)) Catch::TestRunStats const& testRunStats) override { + std::cout << "testRunEnded\n"; + } + + void sectionStarting(__attribute__((unused)) Catch::SectionInfo const& sectionInfo) override { + // Cannot be used for group? Each testcase has implicit section. + std::cout << "\n" << sectionInfo.name << '\n'; + } + + void sectionEnded(__attribute__((unused)) Catch::SectionStats const& sectionStats) override { + std::cout << "\n\n"; + } + + void testCaseStarting(__attribute__((unused)) Catch::TestCaseInfo const& testInfo) override { + std::cout << "\n" << testInfo.name << '\n'; + } + + void testCaseEnded(__attribute__((unused)) Catch::TestCaseStats const& testCaseStats) override { + std::cout << "\n\n"; + } + + void testCasePartialStarting(Catch::TestCaseInfo const& testInfo, uint64_t partNumber) override { + std::cout << "TestCaseStartingPartial: " << testInfo.name << '#' << partNumber << '\n'; + } + + void testCasePartialEnded(Catch::TestCaseStats const& testCaseStats, uint64_t partNumber) override { + std::cout << "TestCasePartialEnded: " << testCaseStats.testInfo->name << '#' << partNumber << '\n'; + } +}; + + +CATCH_REGISTER_REPORTER("codewars", CodewarsReporter) \ No newline at end of file diff --git a/catch2/include/challenge.h b/catch2/kata_code/OriginalExample/include/preloaded.h similarity index 90% rename from catch2/include/challenge.h rename to catch2/kata_code/OriginalExample/include/preloaded.h index b361a40..5463ce5 100644 --- a/catch2/include/challenge.h +++ b/catch2/kata_code/OriginalExample/include/preloaded.h @@ -7,9 +7,9 @@ class Counter { public: // Creates a counter that starts at 0. - Counter() : counter_(1) {} + Counter() : counter_(0) {} // Returns the current counter value, and increments it. int Increment(); // Returns the current counter value, and decrements it. int Decrement(); -}; +}; \ No newline at end of file diff --git a/catch2/kata_code/OriginalExample/include/solution.h b/catch2/kata_code/OriginalExample/include/solution.h new file mode 100644 index 0000000..e69de29 diff --git a/catch2/kata_code/OriginalExample/src/preloaded.cpp b/catch2/kata_code/OriginalExample/src/preloaded.cpp new file mode 100644 index 0000000..e69de29 diff --git a/catch2/src/solution.cpp b/catch2/kata_code/OriginalExample/src/solution.cpp similarity index 93% rename from catch2/src/solution.cpp rename to catch2/kata_code/OriginalExample/src/solution.cpp index 50991b8..8bcfe79 100644 --- a/catch2/src/solution.cpp +++ b/catch2/kata_code/OriginalExample/src/solution.cpp @@ -1,4 +1,4 @@ -#include +#include // Returns the current counter value, and increments it. int Counter::Increment() { diff --git a/catch2/tests/test_solution.cpp b/catch2/kata_code/OriginalExample/tests/test_solution.cpp similarity index 97% rename from catch2/tests/test_solution.cpp rename to catch2/kata_code/OriginalExample/tests/test_solution.cpp index 8027f42..f4356c9 100644 --- a/catch2/tests/test_solution.cpp +++ b/catch2/kata_code/OriginalExample/tests/test_solution.cpp @@ -1,6 +1,6 @@ #include -#include +#include #include @@ -47,4 +47,4 @@ TEST_CASE("Counter") { REQUIRE(c.Increment() == 2); REQUIRE(c.Decrement() == 3); } -} +} \ No newline at end of file diff --git a/catch2/run_image.bat b/catch2/run_image.bat new file mode 100644 index 0000000..a9aef33 --- /dev/null +++ b/catch2/run_image.bat @@ -0,0 +1,8 @@ +set KATA=%1 +set IMAGE=cw-catch2-cpp +set WORKDIR=/code/ +set CREATE_CMD=docker container create --rm -w %WORKDIR% %IMAGE% ./run-kata.sh + +FOR /F %%i IN ('%CREATE_CMD%') DO set CONTAINER=%%i +docker cp ./kata_code/%KATA%/. %CONTAINER%:%WORKDIR% +docker start --attach -i %CONTAINER% diff --git a/catch2/tests/codewars_reporter.cpp b/catch2/tests/codewars_reporter.cpp deleted file mode 100644 index f6ac77f..0000000 --- a/catch2/tests/codewars_reporter.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include - -#include - -// TODO Complete custom reporter -// - https://github.com/catchorg/Catch2/blob/devel/docs/reporters.md -// - https://github.com/catchorg/Catch2/blob/devel/docs/reporter-events.md -// - https://github.com/catchorg/Catch2/blob/devel/docs/test-cases-and-sections.md -class CodewarsReporter : public Catch::StreamingReporterBase { - -private: - bool inTest = false; - -public: - - CodewarsReporter(Catch::ReporterConfig&& config) : - StreamingReporterBase{ CATCH_MOVE(config) } { - - //m_preferences.shouldRedirectStdOut = true; - // TBD: Do we want to report all assertions? XML reporter does - // not, but for machine-parseable reporters I think the answer - // should be yes. - m_preferences.shouldReportAllAssertions = true; - } - - using StreamingReporterBase::StreamingReporterBase; - - static std::string getDescription() { - return "Reporter for Codewars"; - } - - // Emitted before the first test case is executed. - void testRunStarting(Catch::TestRunInfo const& testRunInfo) override { - std::cout << "testRunStarting " << testRunInfo.name << '\n'; - } - - // Emitted after all the test cases have been executed. - void testRunEnded( Catch::TestRunStats const& testRunStats) override { - std::cout << "testRunEnded\n"; - } - - void sectionStarting( Catch::SectionInfo const& sectionInfo) override { - // Cannot be used for group? Each testcase has implicit section. - if (inTest) return; - std::cout << "\n" << sectionInfo.name << '\n'; - } - - void sectionEnded( Catch::SectionStats const& sectionStats) override { - if (inTest) return; - std::cout << "\n\n"; - } - - void testCaseStarting( Catch::TestCaseInfo const& testInfo) override { - if (inTest) return; - std::cout << "\n" << testInfo.name << '\n'; - } - - void testCaseEnded( Catch::TestCaseStats const& testCaseStats) override { - if (inTest) return; - std::cout << "\n\n"; - } - - void testCasePartialStarting(Catch::TestCaseInfo const& testInfo, uint64_t partNumber) override { - std::cout << "\n" << testInfo.name << '#' << partNumber << '\n'; - // inTest = true; - } - - void testCasePartialEnded(Catch::TestCaseStats const& testCaseStats, uint64_t partNumber) override { - if (inTest) return; - std::cout << "\n\n"; - } - - void assertionStarting(Catch::AssertionInfo const& info) override { - std::cout << "\nAssertion" << '\n'; - } - void assertionEnded(Catch::AssertionStats const& statss) override { - auto stats = statss.assertionResult; - std::cout << '\n' << (stats.isOk() ? " " : "") << (stats.hasMessage() ? stats.getMessage() : (stats.isOk() ? "Test passed" : "Test failed")) << '\n'; - std::cout << "\n\n"; - } - -}; - - -CATCH_REGISTER_REPORTER("codewars", CodewarsReporter) From cbfd1df878477e4ea70d14c111ed5625b9e5ce9c Mon Sep 17 00:00:00 2001 From: hobovsky Date: Thu, 25 Jan 2024 23:38:07 +0100 Subject: [PATCH 23/26] Reporter - initial version, all events --- .../docker_image/tests/codewars_reporter.cpp | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/catch2/docker_image/tests/codewars_reporter.cpp b/catch2/docker_image/tests/codewars_reporter.cpp index 5219a3d..3ba5964 100644 --- a/catch2/docker_image/tests/codewars_reporter.cpp +++ b/catch2/docker_image/tests/codewars_reporter.cpp @@ -5,6 +5,7 @@ #include #include +#include // TODO Complete custom reporter // - https://github.com/catchorg/Catch2/blob/devel/docs/reporters.md @@ -14,21 +15,27 @@ class CodewarsReporter : public Catch::StreamingReporterBase { public: using StreamingReporterBase::StreamingReporterBase; + CodewarsReporter( Catch::ReporterConfig&& config ): + StreamingReporterBase( CATCH_MOVE(config) ) { + // This is necessary to emit to syntetic, per-assertion IT's + m_preferences.shouldReportAllAssertions = true; + } + static std::string getDescription() { return "Reporter for Codewars"; } // Emitted before the first test case is executed. void testRunStarting(Catch::TestRunInfo const& testRunInfo) override { - std::cout << "testRunStarting " << testRunInfo.name << '\n'; + std::cout << "\ntestRunStarting " << testRunInfo.name << '\n'; } // Emitted after all the test cases have been executed. void testRunEnded(__attribute__((unused)) Catch::TestRunStats const& testRunStats) override { - std::cout << "testRunEnded\n"; + std::cout << "\ntestRunEnded\n"; } - void sectionStarting(__attribute__((unused)) Catch::SectionInfo const& sectionInfo) override { + void sectionStarting(Catch::SectionInfo const& sectionInfo) override { // Cannot be used for group? Each testcase has implicit section. std::cout << "\n" << sectionInfo.name << '\n'; } @@ -37,8 +44,8 @@ class CodewarsReporter : public Catch::StreamingReporterBase { std::cout << "\n\n"; } - void testCaseStarting(__attribute__((unused)) Catch::TestCaseInfo const& testInfo) override { - std::cout << "\n" << testInfo.name << '\n'; + void testCaseStarting(Catch::TestCaseInfo const& testInfo) override { + std::cout << "\n" << testInfo.name << '\n'; } void testCaseEnded(__attribute__((unused)) Catch::TestCaseStats const& testCaseStats) override { @@ -46,12 +53,24 @@ class CodewarsReporter : public Catch::StreamingReporterBase { } void testCasePartialStarting(Catch::TestCaseInfo const& testInfo, uint64_t partNumber) override { - std::cout << "TestCaseStartingPartial: " << testInfo.name << '#' << partNumber << '\n'; + std::cout << "\n" << testInfo.name << '#' << partNumber << '\n'; } - void testCasePartialEnded(Catch::TestCaseStats const& testCaseStats, uint64_t partNumber) override { - std::cout << "TestCasePartialEnded: " << testCaseStats.testInfo->name << '#' << partNumber << '\n'; + void testCasePartialEnded(__attribute__((unused)) Catch::TestCaseStats const& testCaseStats, __attribute__((unused)) uint64_t partNumber) override { + std::cout << "\n\n"; } + + void assertionStarting(__attribute__((unused)) Catch::AssertionInfo const& assertionInfo ) override { + std::cout << "\nTest\n"; + } + void assertionEnded(Catch::AssertionStats const& assertionStats ) override { + const Catch::AssertionResult& result = assertionStats.assertionResult; + std::string resultTag = result.succeeded() ? "PASSED" : "FAILED"; + std::string message = result.hasMessage() ? static_cast(result.getMessage()) : (result.succeeded() ? "Test passed" : "Test failed"); + std::cout << "\n<" << resultTag << "::>" << message << "\n"; + std::cout << "\n\n"; + } + }; From 9c1962f4e104b74f722468250cdfb10276fa3e68 Mon Sep 17 00:00:00 2001 From: hobovsky Date: Fri, 26 Jan 2024 00:35:35 +0100 Subject: [PATCH 24/26] Examples for organization of tests --- .../ExampleOrganization/include/preloaded.h | 0 .../ExampleOrganization/include/solution.h | 0 .../ExampleOrganization/src/preloaded.cpp | 0 .../ExampleOrganization/src/solution.cpp | 0 .../tests/test_solution.cpp | 143 ++++++++++++++++++ .../OriginalExample/preloaded.snippet | 20 +++ .../snippets/OriginalExample/solution.snippet | 16 ++ .../OriginalExample/test_solution.snippet | 64 ++++++++ 8 files changed, 243 insertions(+) create mode 100644 catch2/kata_code/ExampleOrganization/include/preloaded.h create mode 100644 catch2/kata_code/ExampleOrganization/include/solution.h create mode 100644 catch2/kata_code/ExampleOrganization/src/preloaded.cpp create mode 100644 catch2/kata_code/ExampleOrganization/src/solution.cpp create mode 100644 catch2/kata_code/ExampleOrganization/tests/test_solution.cpp create mode 100644 catch2/snippets/OriginalExample/preloaded.snippet create mode 100644 catch2/snippets/OriginalExample/solution.snippet create mode 100644 catch2/snippets/OriginalExample/test_solution.snippet diff --git a/catch2/kata_code/ExampleOrganization/include/preloaded.h b/catch2/kata_code/ExampleOrganization/include/preloaded.h new file mode 100644 index 0000000..e69de29 diff --git a/catch2/kata_code/ExampleOrganization/include/solution.h b/catch2/kata_code/ExampleOrganization/include/solution.h new file mode 100644 index 0000000..e69de29 diff --git a/catch2/kata_code/ExampleOrganization/src/preloaded.cpp b/catch2/kata_code/ExampleOrganization/src/preloaded.cpp new file mode 100644 index 0000000..e69de29 diff --git a/catch2/kata_code/ExampleOrganization/src/solution.cpp b/catch2/kata_code/ExampleOrganization/src/solution.cpp new file mode 100644 index 0000000..e69de29 diff --git a/catch2/kata_code/ExampleOrganization/tests/test_solution.cpp b/catch2/kata_code/ExampleOrganization/tests/test_solution.cpp new file mode 100644 index 0000000..415485e --- /dev/null +++ b/catch2/kata_code/ExampleOrganization/tests/test_solution.cpp @@ -0,0 +1,143 @@ +#include + +#include +#include + + +TEST_CASE( "TestCase1" ) { + + REQUIRE( 5 == 5 ); + REQUIRE( 12 >= 5 ); + + SECTION( "Outermost section 1" ) { + REQUIRE( 10 == 10 ); + REQUIRE( 15 >= 10 ); + + SECTION( "MidLevel section 11" ) { + REQUIRE( 10 == 10 ); + REQUIRE( 15 >= 10 ); + + SECTION( "Innermost section 111" ) { + REQUIRE( 10 == 10 ); + REQUIRE( 15 >= 10 ); + } + SECTION( "Innermost section 112" ) { + REQUIRE( 10 == 10 ); + REQUIRE( 15 >= 10 ); + } + } + SECTION( "MidLevel section 21" ) { + REQUIRE( 10 == 10 ); + REQUIRE( 15 >= 10 ); + + SECTION( "Innermost section 121" ) { + REQUIRE( 10 == 10 ); + REQUIRE( 15 >= 10 ); + } + SECTION( "Innermost section 221" ) { + REQUIRE( 10 == 10 ); + REQUIRE( 15 >= 10 ); + } + } + } + SECTION( "Outermost section 2" ) { + REQUIRE( 10 == 10 ); + REQUIRE( 15 >= 10 ); + + SECTION( "MidLevel section 12" ) { + REQUIRE( 10 == 10 ); + REQUIRE( 15 >= 10 ); + + SECTION( "Innermost section 112" ) { + REQUIRE( 10 == 10 ); + REQUIRE( 15 >= 10 ); + } + SECTION( "Innermost section 212" ) { + REQUIRE( 10 == 10 ); + REQUIRE( 15 >= 10 ); + } + } + SECTION( "MidLevel section 22" ) { + REQUIRE( 10 == 10 ); + REQUIRE( 15 >= 10 ); + + SECTION( "Innermost section 122" ) { + REQUIRE( 10 == 10 ); + REQUIRE( 15 >= 10 ); + } + SECTION( "Innermost section 122" ) { + REQUIRE( 10 == 10 ); + REQUIRE( 15 >= 10 ); + } + } + } +} + +TEST_CASE( "TestCase2" ) { + + REQUIRE( 5 == 5 ); + REQUIRE( 12 >= 5 ); + + SECTION( "Outermost section 1" ) { + REQUIRE( 10 == 10 ); + REQUIRE( 15 >= 10 ); + + SECTION( "MidLevel section 11" ) { + REQUIRE( 10 == 10 ); + REQUIRE( 15 >= 10 ); + + SECTION( "Innermost section 111" ) { + REQUIRE( 10 == 10 ); + REQUIRE( 15 >= 10 ); + } + SECTION( "Innermost section 112" ) { + REQUIRE( 10 == 10 ); + REQUIRE( 15 >= 10 ); + } + } + SECTION( "MidLevel section 21" ) { + REQUIRE( 10 == 10 ); + REQUIRE( 15 >= 10 ); + + SECTION( "Innermost section 121" ) { + REQUIRE( 10 == 10 ); + REQUIRE( 15 >= 10 ); + } + SECTION( "Innermost section 221" ) { + REQUIRE( 10 == 10 ); + REQUIRE( 15 >= 10 ); + } + } + } + SECTION( "Outermost section 2" ) { + REQUIRE( 10 == 10 ); + REQUIRE( 15 >= 10 ); + + SECTION( "MidLevel section 12" ) { + REQUIRE( 10 == 10 ); + REQUIRE( 15 >= 10 ); + + SECTION( "Innermost section 112" ) { + REQUIRE( 10 == 10 ); + REQUIRE( 15 >= 10 ); + } + SECTION( "Innermost section 212" ) { + REQUIRE( 10 == 10 ); + REQUIRE( 15 >= 10 ); + } + } + SECTION( "MidLevel section 22" ) { + REQUIRE( 10 == 10 ); + REQUIRE( 15 >= 10 ); + + SECTION( "Innermost section 122" ) { + REQUIRE( 10 == 10 ); + REQUIRE( 15 >= 10 ); + } + SECTION( "Innermost section 122" ) { + REQUIRE( 10 == 10 ); + REQUIRE( 15 >= 10 ); + } + } + } +} \ No newline at end of file diff --git a/catch2/snippets/OriginalExample/preloaded.snippet b/catch2/snippets/OriginalExample/preloaded.snippet new file mode 100644 index 0000000..b66b3ff --- /dev/null +++ b/catch2/snippets/OriginalExample/preloaded.snippet @@ -0,0 +1,20 @@ +#ifdef PRELOADED_INCLUDED +#define PRELOADED_INCLUDED + +#pragma once + +// A simple monotonic counter. +class Counter { + private: + int counter_; + + public: + // Creates a counter that starts at 0. + Counter() : counter_(0) {} + // Returns the current counter value, and increments it. + int Increment(); + // Returns the current counter value, and decrements it. + int Decrement(); +}; + +#endif PRELOADED_INCLUDED diff --git a/catch2/snippets/OriginalExample/solution.snippet b/catch2/snippets/OriginalExample/solution.snippet new file mode 100644 index 0000000..8bcfe79 --- /dev/null +++ b/catch2/snippets/OriginalExample/solution.snippet @@ -0,0 +1,16 @@ +#include + +// Returns the current counter value, and increments it. +int Counter::Increment() { + return counter_++; +} + +// Returns the current counter value, and decrements it. +// counter can not be less than 0, return 0 in this case +int Counter::Decrement() { + if (counter_ == 0) { + return counter_; + } else { + return counter_--; + } +} diff --git a/catch2/snippets/OriginalExample/test_solution.snippet b/catch2/snippets/OriginalExample/test_solution.snippet new file mode 100644 index 0000000..9d3ae01 --- /dev/null +++ b/catch2/snippets/OriginalExample/test_solution.snippet @@ -0,0 +1,64 @@ +#include +#include +#include + +namespace { +TEST(Vector_size_resize, resizing_bigger_changes_size_and_capacity) { + std::vector v( 5 ); + EXPECT_EQ( v.size(), 5 ); + EXPECT_GE( v.capacity(), 5 ); + + v.resize( 10 ); + EXPECT_EQ( v.size(), 10 ); + EXPECT_GE( v.capacity(), 10 ); +} + +TEST(Vector_size_resize, resizing_smaller_changes_size_but_not_capacity) { + std::vector v( 5 ); + EXPECT_EQ( v.size(), 5 ); + EXPECT_GE( v.capacity(), 5 ); + v.resize( 0 ); + + EXPECT_EQ( v.size(), 0 ); + EXPECT_GE( v.capacity(), 5 ); +} + +TEST(Vector_size_resize, reserving_bigger_changes_capacity_but_not_size) { + std::vector v( 5 ); + EXPECT_EQ( v.size(), 5 ); + EXPECT_GE( v.capacity(), 5 ); + v.reserve( 10 ); + + EXPECT_EQ( v.size(), 5 ); + EXPECT_GE( v.capacity(), 10 ); +} + +TEST(Vector_size_resize, reserving_smaller_does_not_change_size_or_capacity) { + std::vector v( 5 ); + EXPECT_EQ( v.size(), 5 ); + EXPECT_GE( v.capacity(), 5 ); + v.reserve( 0 ); + + EXPECT_EQ( v.size(), 5 ); + EXPECT_GE( v.capacity(), 5 ); +} + +TEST(Counter, Increment) { // Tests the Increment() method. + Counter c; + + // Test that counter 0 returns 0 + EXPECT_EQ(0, c.Decrement()); + + // EXPECT_EQ() evaluates its arguments exactly once, so they + // can have side effects. + + EXPECT_EQ(0, c.Increment()); + EXPECT_EQ(1, c.Increment()); + EXPECT_EQ(2, c.Increment()); + + EXPECT_EQ(3, c.Decrement()); +} + + + +} \ No newline at end of file From 273c4c8285103950e3d7c36f684e0f25d5fa1133 Mon Sep 17 00:00:00 2001 From: hobovsky Date: Fri, 26 Jan 2024 00:36:11 +0100 Subject: [PATCH 25/26] Update codewars_reporter.cpp Attempt at making the reporter a bit less verbose --- .../docker_image/tests/codewars_reporter.cpp | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/catch2/docker_image/tests/codewars_reporter.cpp b/catch2/docker_image/tests/codewars_reporter.cpp index 3ba5964..8797724 100644 --- a/catch2/docker_image/tests/codewars_reporter.cpp +++ b/catch2/docker_image/tests/codewars_reporter.cpp @@ -12,6 +12,10 @@ // - https://github.com/catchorg/Catch2/blob/devel/docs/reporter-events.md // - https://github.com/catchorg/Catch2/blob/devel/docs/test-cases-and-sections.md class CodewarsReporter : public Catch::StreamingReporterBase { + +private: + unsigned nestedSections = 0; + public: using StreamingReporterBase::StreamingReporterBase; @@ -35,17 +39,22 @@ class CodewarsReporter : public Catch::StreamingReporterBase { std::cout << "\ntestRunEnded\n"; } - void sectionStarting(Catch::SectionInfo const& sectionInfo) override { - // Cannot be used for group? Each testcase has implicit section. - std::cout << "\n" << sectionInfo.name << '\n'; + void sectionStarting(Catch::SectionInfo const& sectionInfo) override { + // Do not emit groups for implicit sections, put results in + // the related test case (or test case partial) + if(nestedSections++ <= 0) + return; + std::cout << "\n[S]" << sectionInfo.name << '\n'; } void sectionEnded(__attribute__((unused)) Catch::SectionStats const& sectionStats) override { + if(--nestedSections <= 0) + return; std::cout << "\n\n"; } void testCaseStarting(Catch::TestCaseInfo const& testInfo) override { - std::cout << "\n" << testInfo.name << '\n'; + std::cout << "\n[TC]" << testInfo.name << '\n'; } void testCaseEnded(__attribute__((unused)) Catch::TestCaseStats const& testCaseStats) override { @@ -53,7 +62,7 @@ class CodewarsReporter : public Catch::StreamingReporterBase { } void testCasePartialStarting(Catch::TestCaseInfo const& testInfo, uint64_t partNumber) override { - std::cout << "\n" << testInfo.name << '#' << partNumber << '\n'; + std::cout << "\n[TCP]" << testInfo.name << '#' << partNumber << '\n'; } void testCasePartialEnded(__attribute__((unused)) Catch::TestCaseStats const& testCaseStats, __attribute__((unused)) uint64_t partNumber) override { @@ -61,7 +70,7 @@ class CodewarsReporter : public Catch::StreamingReporterBase { } void assertionStarting(__attribute__((unused)) Catch::AssertionInfo const& assertionInfo ) override { - std::cout << "\nTest\n"; + std::cout << "\nAssertion: " << assertionInfo.macroName << "(" << assertionInfo.capturedExpression << ")\n"; } void assertionEnded(Catch::AssertionStats const& assertionStats ) override { const Catch::AssertionResult& result = assertionStats.assertionResult; From d6f314da8afe8c379ee11fe0bdafb2c61e0b0a58 Mon Sep 17 00:00:00 2001 From: Kaz Yoshihara Date: Thu, 7 Nov 2024 22:09:51 -0800 Subject: [PATCH 26/26] Fix markers --- gtest/snippets/OriginalExample/preloaded.snippet | 4 ++-- gtest/snippets/UniqueInOrder/solution.snippet | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gtest/snippets/OriginalExample/preloaded.snippet b/gtest/snippets/OriginalExample/preloaded.snippet index b66b3ff..35ffd18 100644 --- a/gtest/snippets/OriginalExample/preloaded.snippet +++ b/gtest/snippets/OriginalExample/preloaded.snippet @@ -1,4 +1,4 @@ -#ifdef PRELOADED_INCLUDED +#ifndef PRELOADED_INCLUDED #define PRELOADED_INCLUDED #pragma once @@ -17,4 +17,4 @@ class Counter { int Decrement(); }; -#endif PRELOADED_INCLUDED +#endif // PRELOADED_INCLUDED diff --git a/gtest/snippets/UniqueInOrder/solution.snippet b/gtest/snippets/UniqueInOrder/solution.snippet index 622e5ee..980621f 100644 --- a/gtest/snippets/UniqueInOrder/solution.snippet +++ b/gtest/snippets/UniqueInOrder/solution.snippet @@ -1,4 +1,4 @@ -#ifdef SOLUTION_INCLUDED +#ifndef SOLUTION_INCLUDED #define SOLUTION_INCLUDED #include @@ -12,7 +12,7 @@ template std::vector uniqueInOrder(const std::vector& iterabl } std::vector uniqueInOrder(const std::string& iterable); -#endif SOLUTION_INCLUDED +#endif // SOLUTION_INCLUDED #include #include