From 8c1cf3d12e8088cb0680c046bd1e552cb3649fef Mon Sep 17 00:00:00 2001 From: Gonzalo Busto Musi Date: Sun, 26 Nov 2023 13:16:54 +0100 Subject: [PATCH 01/24] feat(suite)!: Stop on fail in Suite. --- zero/ifc/test-suite/suite.cppm | 39 ++++++++++++++++++++++------------ zero/main.cpp | 8 ++++--- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/zero/ifc/test-suite/suite.cppm b/zero/ifc/test-suite/suite.cppm index f4abfae..2e68e07 100644 --- a/zero/ifc/test-suite/suite.cppm +++ b/zero/ifc/test-suite/suite.cppm @@ -25,14 +25,19 @@ struct TestResults { // Forward declarations export struct TestSuite; export struct TestCase; -void runTest(const TestCase* testCase, TestResults& testResults); -void runFreeTestCases(); +export enum TestRunBehavior { + FAIL_FAST, // Abort current test suite or free tests upon a single failure + FAIL_NUC // Abort all tests and suites upon a single failure +}; +bool runTest(const TestCase* testCase, TestRunBehavior behavior, TestResults& testResults); +bool runFreeTestCases(TestRunBehavior behavior); // Top-level containers. They hold pointers to the types to avoid: // `arithmetic on a pointer to an incomplete type` std::vector testSuites; std::vector freeTestCases; + export { /** * Common group of related test cases, identified by a unique string @@ -115,12 +120,15 @@ export { } // Function to run all the test cases and suites - void RUN_TESTS() { - if (!freeTestCases.empty()) - runFreeTestCases(); + void RUN_TESTS(TestRunBehavior behavior) { + if (!freeTestCases.empty()) { + if (runFreeTestCases(behavior) && behavior == FAIL_NUC) + return; + } std::cout << "\nRunning test suites. Total suites found: " << testSuites.size() << std::endl; + for (const auto& test_suite : testSuites) { std::cout << "Running test suite: \033[38;5;165m" << test_suite->uuid << "\033[m"; @@ -128,7 +136,7 @@ export { for (const auto& warning : test_suite->results.warnings) std::cout << "\n " << warning << std::endl; for (const auto& test_case : test_suite->cases) - runTest(test_case, test_suite->results); + runTest(test_case, behavior, test_suite->results); std::cout << "\nTest suite [" << test_suite->uuid << "] summary:" << std::endl; std::cout << " \033[32mPassed:\033[0m " << test_suite->results.passed << std::endl; @@ -137,21 +145,24 @@ export { } } -void runFreeTestCases() { +bool runFreeTestCases(TestRunBehavior behavior) { + bool anyFailed = false; TestResults freeTestsResults; std::cout << "Running free tests: " << std::endl; - for (const auto& testCase : freeTestCases) { - runTest(testCase, freeTestsResults); - std::cout << std::endl; - } + for (const auto& testCase : freeTestCases) { + if (!runTest(testCase, behavior, freeTestsResults)) { + anyFailed = true; + if (behavior == FAIL_FAST) {break;} + } std::cout << "Free tests summary:" << std::endl; std::cout << " \033[32mPassed:\033[0m " << freeTestsResults.passed << std::endl; std::cout << " \033[31mFailed:\033[0m " << freeTestsResults.failed << std::endl; - + } + return anyFailed; } -void runTest(const TestCase* const testCase, TestResults& results) { +bool runTest(const TestCase* const testCase, TestResults& results) { std::cout << "\n Running test: \033[38;5;117m" << testCase->name << "\033[0m"; try { @@ -159,8 +170,10 @@ void runTest(const TestCase* const testCase, TestResults& results) { testCase->fn(); std::cout << " ... Result => \033[32mPassed!\033[0m"; results.passed++; + return true; } catch (const std::exception& ex) { std::cout << " ... Result => \033[31mFailed\033[0m: " << ex.what(); results.failed++; + return false; } } \ No newline at end of file diff --git a/zero/main.cpp b/zero/main.cpp index 5693430..3c27917 100644 --- a/zero/main.cpp +++ b/zero/main.cpp @@ -36,8 +36,8 @@ int main() { // run_containers_examples(); // run_output_iterator_examples(); // run_quantities_examples(); - run_formatter_and_stylize_examples(); - run_print_examples(); + // run_formatter_and_stylize_examples(); + // run_print_examples(); // Register a new test case using a function pointer. TEST_CASE("Addition Test With Pointers", testPtrsAddition); @@ -45,7 +45,9 @@ int main() { // Users can register a new test case using lambdas, avoiding writing standalone functions TEST_CASE("Subtraction Test", []() { int result = 5 - 3; + assertEquals(2, result); assertEquals(122435, result); + assertEquals(2, result); }); // Registering test cases into test suites, to group and relate tests that makes sense to exists @@ -60,7 +62,7 @@ int main() { TEST_CASE(suite, "Addition Test", testAddition); // Don't forget to call this free function, to run all the tests written! - RUN_TESTS(); + RUN_TESTS(FAIL_FAST); return 0; } From e47670673bc71e492f4f1681904d597538ee8826 Mon Sep 17 00:00:00 2001 From: Gonzalo Busto Musi Date: Sun, 26 Nov 2023 16:32:41 +0100 Subject: [PATCH 02/24] feat(tests): More tests More tests and another suite case have been added to test the different RUN_TESTS options. --- zero/main.cpp | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/zero/main.cpp b/zero/main.cpp index 3c27917..ec034a5 100644 --- a/zero/main.cpp +++ b/zero/main.cpp @@ -22,8 +22,28 @@ void run_print_examples(); void testAddition() { int result = 2 + 2; assertEquals(4, result); + assertEquals(4, result); + assertEquals(4, result); +} + +// Let's define some more example test functions using the assertion function +void testSubtraction() { + int result = 3 - 2; + assertEquals(1, result); + assertEquals(23, result); + assertEquals(1, result); } + +// Let's define even more example test functions using the assertion function +void testMultiplication() { + int result = 2 * 2; + assertEquals(4, result); + assertEquals(4, result); + assertEquals(4, result); +} + + // Passing two pointers to compare if the values that they point to are equals void testPtrsAddition() { int result = 2 + 2; @@ -46,7 +66,6 @@ int main() { TEST_CASE("Subtraction Test", []() { int result = 5 - 3; assertEquals(2, result); - assertEquals(122435, result); assertEquals(2, result); }); @@ -60,9 +79,23 @@ int main() { // Forces a warning that alerts the user that the test will be discarded, since already // exists one with the same identifier in the given suite TEST_CASE(suite, "Addition Test", testAddition); + // Register a test case designed to fail, useful for testing the behavior + // of RUN_TESTS with different failure modes. + TEST_CASE(suite, "Subtraction Test", testSubtraction); + + // Register additional test cases to verify the functionality of RUN_TESTS + // under different conditions. + TEST_CASE(suite, "Multiplication Test", testMultiplication); + + // Create another test suite to further validate the behavior of RUN_TESTS + // with multiple suites, especially under different failure modes. + TestSuite anotherSuite {"Another Suite"}; + TEST_CASE(anotherSuite, "Addition Test", testAddition); + TEST_CASE(anotherSuite, "Subtraction Test", testSubtraction); + TEST_CASE(anotherSuite, "Multiplication Test", testMultiplication); // Don't forget to call this free function, to run all the tests written! - RUN_TESTS(FAIL_FAST); + RUN_TESTS(FAIL_NUC); return 0; } From 070830928405fd4e63ac5d5c9014ed96f4798b03 Mon Sep 17 00:00:00 2001 From: Gonzalo Busto Musi Date: Sun, 26 Nov 2023 16:47:38 +0100 Subject: [PATCH 03/24] fix(suite): Fixed errors that prevented compiling. Bugs that prevented compilation have been fixed. ENUM TestRunBehavior names have been changed and relevant documentation has been added. --- zero/ifc/test-suite/suite.cppm | 68 ++++++++++++++++++++++++++++------ 1 file changed, 56 insertions(+), 12 deletions(-) diff --git a/zero/ifc/test-suite/suite.cppm b/zero/ifc/test-suite/suite.cppm index 2e68e07..f6fb6b5 100644 --- a/zero/ifc/test-suite/suite.cppm +++ b/zero/ifc/test-suite/suite.cppm @@ -25,12 +25,42 @@ struct TestResults { // Forward declarations export struct TestSuite; export struct TestCase; +/** + * @enum TestRunBehavior + * @brief Defines the behavior of the test runner upon encountering test failures. + * + * This enum allows selecting different strategies for test execution, + * useful for various testing scenarios ranging from comprehensive error analysis + * to immediate failure response. + */ export enum TestRunBehavior { - FAIL_FAST, // Abort current test suite or free tests upon a single failure - FAIL_NUC // Abort all tests and suites upon a single failure + /** + * @brief Execute all tests regardless of failures. + * + * Use this mode when a complete test run is needed to gather full information + * about the system's state or to understand the full extent of the issues. + */ + CONTINUE_ON_ERROR, + + /** + * @brief Halt the execution of the current test suite or free tests upon a failure and proceed to the next suite or free test. + * + * This mode stops the execution of the current suite or set of free tests immediately upon encountering a failure, + * then proceeds to the next suite or free test. It's beneficial for quickly bypassing problematic tests + * while still executing the remaining tests. + */ + HALT_SUITE_ON_FAIL, + + /** + * @brief Abort all testing activities immediately upon any failure. + * + * Choose this mode when a single failure indicates a critical system issue, + * necessitating immediate attention and halting further tests until resolution. + */ + ABORT_ALL_ON_FAIL }; -bool runTest(const TestCase* testCase, TestRunBehavior behavior, TestResults& testResults); -bool runFreeTestCases(TestRunBehavior behavior); +bool runTest(const TestCase* testCase, TestResults& testResults); +bool runFreeTestCases(const TestRunBehavior behavior); // Top-level containers. They hold pointers to the types to avoid: // `arithmetic on a pointer to an incomplete type` @@ -120,7 +150,7 @@ export { } // Function to run all the test cases and suites - void RUN_TESTS(TestRunBehavior behavior) { + void RUN_TESTS(const TestRunBehavior behavior) { if (!freeTestCases.empty()) { if (runFreeTestCases(behavior) && behavior == FAIL_NUC) return; @@ -135,8 +165,17 @@ export { for (const auto& warning : test_suite->results.warnings) std::cout << "\n " << warning << std::endl; - for (const auto& test_case : test_suite->cases) - runTest(test_case, behavior, test_suite->results); + for (const auto& test_case : test_suite->cases) { + if (!runTest(test_case, test_suite->results)) { + if (behavior == FAIL_FAST) break; + if (behavior == FAIL_NUC) { + std::cout << "\nTest suite [" << test_suite->uuid << "] summary:" << std::endl; + std::cout << " \033[32mPassed:\033[0m " << test_suite->results.passed << std::endl; + std::cout << " \033[31mFailed:\033[0m " << test_suite->results.failed << std::endl; + return; + } + } + } std::cout << "\nTest suite [" << test_suite->uuid << "] summary:" << std::endl; std::cout << " \033[32mPassed:\033[0m " << test_suite->results.passed << std::endl; @@ -145,17 +184,22 @@ export { } } -bool runFreeTestCases(TestRunBehavior behavior) { +bool runFreeTestCases(const TestRunBehavior behavior) { bool anyFailed = false; TestResults freeTestsResults; std::cout << "Running free tests: " << std::endl; for (const auto& testCase : freeTestCases) { - if (!runTest(testCase, behavior, freeTestsResults)) { + if (!runTest(testCase, freeTestsResults)) { anyFailed = true; - if (behavior == FAIL_FAST) {break;} + if (behavior == FAIL_FAST){ + std::cout << "\n Free tests summary:" << std::endl; + std::cout << " \033[32mPassed:\033[0m " << freeTestsResults.passed << std::endl; + std::cout << " \033[31mFailed:\033[0m " << freeTestsResults.failed << std::endl; + break; + } } - std::cout << "Free tests summary:" << std::endl; + std::cout << "\n Free tests summary:" << std::endl; std::cout << " \033[32mPassed:\033[0m " << freeTestsResults.passed << std::endl; std::cout << " \033[31mFailed:\033[0m " << freeTestsResults.failed << std::endl; } @@ -174,6 +218,6 @@ bool runTest(const TestCase* const testCase, TestResults& results) { } catch (const std::exception& ex) { std::cout << " ... Result => \033[31mFailed\033[0m: " << ex.what(); results.failed++; - return false; + return false; } } \ No newline at end of file From c4a3c6dd1a65654f780cfbafb26967df4693a0ec Mon Sep 17 00:00:00 2001 From: Gonzalo Busto Musi Date: Sun, 26 Nov 2023 16:50:45 +0100 Subject: [PATCH 04/24] feat: Added default behavior un RUN_TESTS --- zero/ifc/test-suite/suite.cppm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zero/ifc/test-suite/suite.cppm b/zero/ifc/test-suite/suite.cppm index f6fb6b5..9d9c170 100644 --- a/zero/ifc/test-suite/suite.cppm +++ b/zero/ifc/test-suite/suite.cppm @@ -150,7 +150,7 @@ export { } // Function to run all the test cases and suites - void RUN_TESTS(const TestRunBehavior behavior) { + void RUN_TESTS(const TestRunBehavior behavior = ABORT_ALL_ON_FAIL) { if (!freeTestCases.empty()) { if (runFreeTestCases(behavior) && behavior == FAIL_NUC) return; From 1d388e9f03dbecb81565550e8197d98bbde6944a Mon Sep 17 00:00:00 2001 From: Gonzalo Busto Musi Date: Sun, 26 Nov 2023 16:52:39 +0100 Subject: [PATCH 05/24] fix: :bug: Fixes in conditionals of enum values. --- zero/ifc/test-suite/suite.cppm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/zero/ifc/test-suite/suite.cppm b/zero/ifc/test-suite/suite.cppm index 9d9c170..900a27c 100644 --- a/zero/ifc/test-suite/suite.cppm +++ b/zero/ifc/test-suite/suite.cppm @@ -152,7 +152,7 @@ export { // Function to run all the test cases and suites void RUN_TESTS(const TestRunBehavior behavior = ABORT_ALL_ON_FAIL) { if (!freeTestCases.empty()) { - if (runFreeTestCases(behavior) && behavior == FAIL_NUC) + if (runFreeTestCases(behavior) && behavior == ABORT_ALL_ON_FAIL) return; } std::cout @@ -167,8 +167,8 @@ export { std::cout << "\n " << warning << std::endl; for (const auto& test_case : test_suite->cases) { if (!runTest(test_case, test_suite->results)) { - if (behavior == FAIL_FAST) break; - if (behavior == FAIL_NUC) { + if (behavior == HALT_SUITE_ON_FAIL) break; + if (behavior == ABORT_ALL_ON_FAIL) { std::cout << "\nTest suite [" << test_suite->uuid << "] summary:" << std::endl; std::cout << " \033[32mPassed:\033[0m " << test_suite->results.passed << std::endl; std::cout << " \033[31mFailed:\033[0m " << test_suite->results.failed << std::endl; @@ -191,7 +191,7 @@ bool runFreeTestCases(const TestRunBehavior behavior) { for (const auto& testCase : freeTestCases) { if (!runTest(testCase, freeTestsResults)) { anyFailed = true; - if (behavior == FAIL_FAST){ + if (behavior == HALT_SUITE_ON_FAIL){ std::cout << "\n Free tests summary:" << std::endl; std::cout << " \033[32mPassed:\033[0m " << freeTestsResults.passed << std::endl; std::cout << " \033[31mFailed:\033[0m " << freeTestsResults.failed << std::endl; From 7fa33d12f7472ed563c36c25bf1b97362b561f80 Mon Sep 17 00:00:00 2001 From: Gonzalo Busto Musi Date: Sun, 26 Nov 2023 16:53:09 +0100 Subject: [PATCH 06/24] refactor: Use of default value on RUN_TESTS --- zero/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zero/main.cpp b/zero/main.cpp index ec034a5..8c16635 100644 --- a/zero/main.cpp +++ b/zero/main.cpp @@ -95,7 +95,7 @@ int main() { TEST_CASE(anotherSuite, "Multiplication Test", testMultiplication); // Don't forget to call this free function, to run all the tests written! - RUN_TESTS(FAIL_NUC); + RUN_TESTS(); return 0; } From 4090f99b2314c54d71c3859165634038b0933681 Mon Sep 17 00:00:00 2001 From: Gonzalo Busto Musi Date: Sun, 26 Nov 2023 20:27:42 +0100 Subject: [PATCH 07/24] feat(suite): Added output on FreeTest. An output has been added in the FreeTest in case of error and with behavior "ABORT_ALL_ON_FAIL". --- zero/ifc/test-suite/suite.cppm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zero/ifc/test-suite/suite.cppm b/zero/ifc/test-suite/suite.cppm index 900a27c..991d5e0 100644 --- a/zero/ifc/test-suite/suite.cppm +++ b/zero/ifc/test-suite/suite.cppm @@ -152,8 +152,10 @@ export { // Function to run all the test cases and suites void RUN_TESTS(const TestRunBehavior behavior = ABORT_ALL_ON_FAIL) { if (!freeTestCases.empty()) { - if (runFreeTestCases(behavior) && behavior == ABORT_ALL_ON_FAIL) + if (runFreeTestCases(behavior) && behavior == ABORT_ALL_ON_FAIL){ + std::cout << "\n\033[38;5;196m[Abort]\033[0m All further tests are aborted due to a failure in free tests.\n"; return; + } } std::cout << "\nRunning test suites. Total suites found: " << testSuites.size() From 3a005a7a25d0df25912554709e089f21e64a7cec Mon Sep 17 00:00:00 2001 From: Gonzalo Busto Musi Date: Sun, 26 Nov 2023 20:57:37 +0100 Subject: [PATCH 08/24] refactor(suite): Output for freetest. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Se ha movido y modificado el output de error en caso de error en freetest con el modo 'ABORT_ALL_ON_FAIL'. Se ha aƱadido output de warning en caso de error en freetest con el modo 'HALT_SUITE_ON_FAIL'. --- zero/ifc/test-suite/suite.cppm | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/zero/ifc/test-suite/suite.cppm b/zero/ifc/test-suite/suite.cppm index 991d5e0..3f06f39 100644 --- a/zero/ifc/test-suite/suite.cppm +++ b/zero/ifc/test-suite/suite.cppm @@ -152,10 +152,7 @@ export { // Function to run all the test cases and suites void RUN_TESTS(const TestRunBehavior behavior = ABORT_ALL_ON_FAIL) { if (!freeTestCases.empty()) { - if (runFreeTestCases(behavior) && behavior == ABORT_ALL_ON_FAIL){ - std::cout << "\n\033[38;5;196m[Abort]\033[0m All further tests are aborted due to a failure in free tests.\n"; - return; - } + if (runFreeTestCases(behavior) && behavior == ABORT_ALL_ON_FAIL) return; } std::cout << "\nRunning test suites. Total suites found: " << testSuites.size() @@ -190,21 +187,32 @@ bool runFreeTestCases(const TestRunBehavior behavior) { bool anyFailed = false; TestResults freeTestsResults; std::cout << "Running free tests: " << std::endl; - for (const auto& testCase : freeTestCases) { + + for (const auto& testCase : freeTestCases) { if (!runTest(testCase, freeTestsResults)) { anyFailed = true; - if (behavior == HALT_SUITE_ON_FAIL){ - std::cout << "\n Free tests summary:" << std::endl; - std::cout << " \033[32mPassed:\033[0m " << freeTestsResults.passed << std::endl; - std::cout << " \033[31mFailed:\033[0m " << freeTestsResults.failed << std::endl; + if (behavior == ABORT_ALL_ON_FAIL || behavior == HALT_SUITE_ON_FAIL) { break; - } + } } + } - std::cout << "\n Free tests summary:" << std::endl; + std::cout << "\nFree tests summary:" << std::endl; std::cout << " \033[32mPassed:\033[0m " << freeTestsResults.passed << std::endl; std::cout << " \033[31mFailed:\033[0m " << freeTestsResults.failed << std::endl; + + if (anyFailed) { + if (behavior == HALT_SUITE_ON_FAIL) { + std::cout << "\n\033[1;38;5;214m================================================\n"; + std::cout << "[Halt Free Tests] Stopping further free tests due to a failure.\n"; + std::cout << "================================================\033[0m\n"; + } else if (behavior == ABORT_ALL_ON_FAIL) { + std::cout << "\n\033[1;38;5;196m================================================\n"; + std::cout << "[Abort] All further tests are aborted due to a failure in free tests.\n"; + std::cout << "================================================\033[0m\n"; + } } + return anyFailed; } From 533d73773df391fdefe13fbceee8920eec4e0e44 Mon Sep 17 00:00:00 2001 From: Gonzalo Busto Musi Date: Sun, 26 Nov 2023 22:11:44 +0100 Subject: [PATCH 09/24] refactor(suite): :art: New function for suite process. The code in which the suite tests were processed has been moved to its own function. --- zero/ifc/test-suite/suite.cppm | 53 +++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/zero/ifc/test-suite/suite.cppm b/zero/ifc/test-suite/suite.cppm index 3f06f39..16096c4 100644 --- a/zero/ifc/test-suite/suite.cppm +++ b/zero/ifc/test-suite/suite.cppm @@ -61,6 +61,7 @@ export enum TestRunBehavior { }; bool runTest(const TestCase* testCase, TestResults& testResults); bool runFreeTestCases(const TestRunBehavior behavior); +void runSuiteTestCases(const TestRunBehavior behavior); // Top-level containers. They hold pointers to the types to avoid: // `arithmetic on a pointer to an incomplete type` @@ -154,35 +155,41 @@ export { if (!freeTestCases.empty()) { if (runFreeTestCases(behavior) && behavior == ABORT_ALL_ON_FAIL) return; } - std::cout - << "\nRunning test suites. Total suites found: " << testSuites.size() - << std::endl; - - - for (const auto& test_suite : testSuites) { - std::cout << "Running test suite: \033[38;5;165m" << test_suite->uuid << "\033[m"; - - for (const auto& warning : test_suite->results.warnings) - std::cout << "\n " << warning << std::endl; - for (const auto& test_case : test_suite->cases) { - if (!runTest(test_case, test_suite->results)) { - if (behavior == HALT_SUITE_ON_FAIL) break; - if (behavior == ABORT_ALL_ON_FAIL) { - std::cout << "\nTest suite [" << test_suite->uuid << "] summary:" << std::endl; - std::cout << " \033[32mPassed:\033[0m " << test_suite->results.passed << std::endl; - std::cout << " \033[31mFailed:\033[0m " << test_suite->results.failed << std::endl; - return; - } + runSuiteTestCases(behavior); + } +} + +void runSuiteTestCases(const TestRunBehavior behavior) { +std::cout + << "\nRunning test suites. Total suites found: " << testSuites.size() + << std::endl; + + + for (const auto& test_suite : testSuites) { + std::cout << "Running test suite: \033[38;5;165m" << test_suite->uuid << "\033[m"; + + for (const auto& warning : test_suite->results.warnings) + std::cout << "\n " << warning << std::endl; + for (const auto& test_case : test_suite->cases) { + if (!runTest(test_case, test_suite->results)) { + if (behavior == HALT_SUITE_ON_FAIL) break; + if (behavior == ABORT_ALL_ON_FAIL) { + std::cout << "\nTest suite [" << test_suite->uuid << "] summary:" << std::endl; + std::cout << " \033[32mPassed:\033[0m " << test_suite->results.passed << std::endl; + std::cout << " \033[31mFailed:\033[0m " << test_suite->results.failed << std::endl; + return; } } - - std::cout << "\nTest suite [" << test_suite->uuid << "] summary:" << std::endl; - std::cout << " \033[32mPassed:\033[0m " << test_suite->results.passed << std::endl; - std::cout << " \033[31mFailed:\033[0m " << test_suite->results.failed << std::endl; } + + std::cout << "\nTest suite [" << test_suite->uuid << "] summary:" << std::endl; + std::cout << " \033[32mPassed:\033[0m " << test_suite->results.passed << std::endl; + std::cout << " \033[31mFailed:\033[0m " << test_suite->results.failed << std::endl; } } + + bool runFreeTestCases(const TestRunBehavior behavior) { bool anyFailed = false; TestResults freeTestsResults; From 854863a928fb3ef8a4de5bca92408df54728274a Mon Sep 17 00:00:00 2001 From: Gonzalo Busto Musi Date: Thu, 30 Nov 2023 01:06:36 +0100 Subject: [PATCH 10/24] refactor: :construction: Added tests to validate suite logic. --- zero/main.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/zero/main.cpp b/zero/main.cpp index 8c16635..64a75f9 100644 --- a/zero/main.cpp +++ b/zero/main.cpp @@ -48,7 +48,9 @@ void testMultiplication() { void testPtrsAddition() { int result = 2 + 2; int expected = 4; + int wrongExpected = 16; assertEquals(&expected, &result); + assertEquals(&wrongExpected, &result); } // Driver code @@ -59,6 +61,13 @@ int main() { // run_formatter_and_stylize_examples(); // run_print_examples(); + TEST_CASE("Multiplication Test", []() { + int result = 5 * 3; + assertEquals(15, result); + assertEquals(15, result); + }); + + // Register a new test case using a function pointer. TEST_CASE("Addition Test With Pointers", testPtrsAddition); @@ -95,7 +104,7 @@ int main() { TEST_CASE(anotherSuite, "Multiplication Test", testMultiplication); // Don't forget to call this free function, to run all the tests written! - RUN_TESTS(); + RUN_TESTS(CONTINUE_ON_ERROR); return 0; } From 50dbf179f5400657bdf84a4b06a4ca81abe63d80 Mon Sep 17 00:00:00 2001 From: Gonzalo Busto Musi Date: Thu, 7 Dec 2023 22:44:45 +0100 Subject: [PATCH 11/24] feat(suite): Throw exception on test error In case of an error in the tests an exception is thrown. This exception is thrown at different times depending on the error mode used. --- zero/ifc/test-suite/suite.cppm | 398 ++++++++++++++++++--------------- 1 file changed, 218 insertions(+), 180 deletions(-) diff --git a/zero/ifc/test-suite/suite.cppm b/zero/ifc/test-suite/suite.cppm index 16096c4..67a38bd 100644 --- a/zero/ifc/test-suite/suite.cppm +++ b/zero/ifc/test-suite/suite.cppm @@ -6,20 +6,26 @@ * with confidence, without depending on some third-party * changing standards, implementations while avoiding the * typical dependency pitfalls -*/ + */ export module tsuite; export import :assertions; import std; +class TestSuiteException : public std::runtime_error { +public: + TestSuiteException(const std::string &message) + : std::runtime_error(message) {} +}; + /** * */ struct TestResults { - int passed = 0; - int failed = 0; - std::vector warnings {}; + int passed = 0; + int failed = 0; + std::vector warnings{}; }; // Forward declarations @@ -27,214 +33,246 @@ export struct TestSuite; export struct TestCase; /** * @enum TestRunBehavior - * @brief Defines the behavior of the test runner upon encountering test failures. + * @brief Defines the behavior of the test runner upon encountering test + * failures. * - * This enum allows selecting different strategies for test execution, - * useful for various testing scenarios ranging from comprehensive error analysis - * to immediate failure response. + * This enum allows selecting different strategies for test execution, + * useful for various testing scenarios ranging from comprehensive error + * analysis to immediate failure response. */ export enum TestRunBehavior { - /** - * @brief Execute all tests regardless of failures. - * - * Use this mode when a complete test run is needed to gather full information - * about the system's state or to understand the full extent of the issues. - */ - CONTINUE_ON_ERROR, + /** + * @brief Execute all tests regardless of failures. + * + * Use this mode when a complete test run is needed to gather full information + * about the system's state or to understand the full extent of the issues. + */ + CONTINUE_ON_ERROR, - /** - * @brief Halt the execution of the current test suite or free tests upon a failure and proceed to the next suite or free test. - * - * This mode stops the execution of the current suite or set of free tests immediately upon encountering a failure, - * then proceeds to the next suite or free test. It's beneficial for quickly bypassing problematic tests - * while still executing the remaining tests. - */ - HALT_SUITE_ON_FAIL, + /** + * @brief Halt the execution of the current test suite or free tests upon a + * failure and proceed to the next suite or free test. + * + * This mode stops the execution of the current suite or set of free tests + * immediately upon encountering a failure, then proceeds to the next suite or + * free test. It's beneficial for quickly bypassing problematic tests while + * still executing the remaining tests. + */ + HALT_SUITE_ON_FAIL, - /** - * @brief Abort all testing activities immediately upon any failure. - * - * Choose this mode when a single failure indicates a critical system issue, - * necessitating immediate attention and halting further tests until resolution. - */ - ABORT_ALL_ON_FAIL + /** + * @brief Abort all testing activities immediately upon any failure. + * + * Choose this mode when a single failure indicates a critical system issue, + * necessitating immediate attention and halting further tests until + * resolution. + */ + ABORT_ALL_ON_FAIL }; -bool runTest(const TestCase* testCase, TestResults& testResults); +bool runTest(const TestCase *testCase, TestResults &testResults); bool runFreeTestCases(const TestRunBehavior behavior); void runSuiteTestCases(const TestRunBehavior behavior); +void checkForTestErrors(const bool freeTestsErrors); // Top-level containers. They hold pointers to the types to avoid: // `arithmetic on a pointer to an incomplete type` -std::vector testSuites; -std::vector freeTestCases; - +std::vector testSuites; +std::vector freeTestCases; export { + /** + * Common group of related test cases, identified by a unique string + */ + struct TestSuite { + std::string uuid; + std::vector cases{}; + TestResults results{}; + + TestSuite() = delete; /** - * Common group of related test cases, identified by a unique string + * @short This ctr shouldn't exist, since {@link std::string} has a + * constructor for convert cstr to std::string + * @bug Clang16 under windows linking against libc++ with mingw, + * when the TestSuite new instance receives a const char* in a different + * file than the main file (in particular from a module), Clang refuses to + * compile saying that there's no viable ctr for TestSuite receiving a const + * char* + * + * For further @details @see + * https://github.com/llvm/llvm-project/issues/64211 */ - struct TestSuite { - std::string uuid; - std::vector cases {}; - TestResults results {}; - - TestSuite() = delete; - /** - * @short This ctr shouldn't exist, since {@link std::string} has a constructor - * for convert cstr to std::string - * @bug Clang16 under windows linking against libc++ with mingw, - * when the TestSuite new instance receives a const char* in a different file - * than the main file (in particular from a module), Clang refuses to compile - * saying that there's no viable ctr for TestSuite receiving a const char* - * - * For further @details @see https://github.com/llvm/llvm-project/issues/64211 - */ - constexpr explicit TestSuite(const char* uuid) - : uuid(std::move(uuid)) {} - constexpr explicit TestSuite(std::string uuid) - : uuid(std::move(uuid)) {} - - TestSuite(const TestSuite& rhs) = delete; - TestSuite (TestSuite&& rhs) = delete; - - friend bool operator==(const TestSuite& lhs, const TestSuite& rhs) { - return lhs.uuid == rhs.uuid; - } - }; + constexpr explicit TestSuite(const char *uuid) : uuid(std::move(uuid)) {} + constexpr explicit TestSuite(std::string uuid) : uuid(std::move(uuid)) {} + + TestSuite(const TestSuite &rhs) = delete; + TestSuite(TestSuite &&rhs) = delete; + + friend bool operator==(const TestSuite &lhs, const TestSuite &rhs) { + return lhs.uuid == rhs.uuid; + } + }; + + /** + * @struct Holds the data for a particular user's test case. + */ + struct TestCase { + std::string name; + std::function fn; /** - * @struct Holds the data for a particular user's test case. + * @note waiting for Clang to implement the {@link std::function] + * constructors as *constexpr*, so we can make the {@link TestCase] + * constexpr-constructible + * + * @details constexpr constructor's 2nd parameter type 'std::function' is not a literal type constexpr TestCase(std::string name, + * std::function fn) + * + * 'function' is not literal because it is not an aggregate and has + * no constexpr constructors other than copy or move constructors class + * _LIBCPP_TEMPLATE_VIS function<_Rp(_ArgTypes...)> */ - struct TestCase { - std::string name; - std::function fn; - - /** - * @note waiting for Clang to implement the {@link std::function] constructors as *constexpr*, - * so we can make the {@link TestCase] constexpr-constructible - * - * @details constexpr constructor's 2nd parameter type 'std::function' is not a literal type - * constexpr TestCase(std::string name, std::function fn) - * - * 'function' is not literal because it is not an aggregate and has no constexpr constructors other than copy or move constructors - * class _LIBCPP_TEMPLATE_VIS function<_Rp(_ArgTypes...)> - */ - TestCase(std::string name, std::function fn) - : name(std::move(name)), fn(std::move(fn)) {} - }; - - void TEST_CASE(const std::string& tname, const std::function tfunc) { - freeTestCases.push_back(new TestCase(tname, tfunc)); - } + TestCase(std::string name, std::function fn) + : name(std::move(name)), fn(std::move(fn)) {} + }; + + void TEST_CASE(const std::string &tname, const std::function tfunc) { + freeTestCases.push_back(new TestCase(tname, tfunc)); + } + + void TEST_CASE(TestSuite & tsuite, const std::string &tname, + const std::function tfunc) { + auto it = std::find_if( + tsuite.cases.begin(), tsuite.cases.end(), + [&](const TestCase *tcase) { return tcase->name == tname; }); - void TEST_CASE(TestSuite& tsuite, const std::string& tname, const std::function tfunc) { - auto it = std::find_if(tsuite.cases.begin(), tsuite.cases.end(), [&](const TestCase* tcase) { - return tcase->name == tname; - }); - - /// Check that the user didn't registered a test case with the same identifier - if (it == tsuite.cases.end()) - tsuite.cases.emplace_back(new TestCase(tname, tfunc)); - else - tsuite.results.warnings.emplace_back( - "\033[38;5;220m[Warning\033[0m in suite: \033[38;5;165m" + - tsuite.uuid + "\033[0m\033[38;5;220m]\033[0m " - "Already exists a test case with the name: \033[38;5;117m" - + tname + "\033[0m. Skipping test case." - ); - /// If this is the first time that the suite is being registered - auto suites_it = std::find_if(testSuites.begin(), testSuites.end(), [&](const TestSuite* suite) { - return suite->uuid == tsuite.uuid; - }); - if (suites_it == testSuites.end()) - testSuites.push_back(&tsuite); + /// Check that the user didn't registered a test case with the same + /// identifier + if (it == tsuite.cases.end()) + tsuite.cases.emplace_back(new TestCase(tname, tfunc)); + else + tsuite.results.warnings.emplace_back( + "\033[38;5;220m[Warning\033[0m in suite: \033[38;5;165m" + + tsuite.uuid + + "\033[0m\033[38;5;220m]\033[0m " + "Already exists a test case with the name: \033[38;5;117m" + + tname + "\033[0m. Skipping test case."); + /// If this is the first time that the suite is being registered + auto suites_it = std::find_if( + testSuites.begin(), testSuites.end(), + [&](const TestSuite *suite) { return suite->uuid == tsuite.uuid; }); + if (suites_it == testSuites.end()) + testSuites.push_back(&tsuite); + } + + // Function to run all the test cases and suites + void RUN_TESTS(const TestRunBehavior behavior = ABORT_ALL_ON_FAIL) { + bool freeTestsErrors = false; + if (!freeTestCases.empty()) { + freeTestsErrors = runFreeTestCases(behavior); } + runSuiteTestCases(behavior); - // Function to run all the test cases and suites - void RUN_TESTS(const TestRunBehavior behavior = ABORT_ALL_ON_FAIL) { - if (!freeTestCases.empty()) { - if (runFreeTestCases(behavior) && behavior == ABORT_ALL_ON_FAIL) return; - } - runSuiteTestCases(behavior); - } + checkForTestErrors(freeTestsErrors); + } } void runSuiteTestCases(const TestRunBehavior behavior) { -std::cout - << "\nRunning test suites. Total suites found: " << testSuites.size() - << std::endl; - - - for (const auto& test_suite : testSuites) { - std::cout << "Running test suite: \033[38;5;165m" << test_suite->uuid << "\033[m"; - - for (const auto& warning : test_suite->results.warnings) - std::cout << "\n " << warning << std::endl; - for (const auto& test_case : test_suite->cases) { - if (!runTest(test_case, test_suite->results)) { - if (behavior == HALT_SUITE_ON_FAIL) break; - if (behavior == ABORT_ALL_ON_FAIL) { - std::cout << "\nTest suite [" << test_suite->uuid << "] summary:" << std::endl; - std::cout << " \033[32mPassed:\033[0m " << test_suite->results.passed << std::endl; - std::cout << " \033[31mFailed:\033[0m " << test_suite->results.failed << std::endl; - return; - } - } - } + std::cout << "\nRunning test suites. Total suites found: " + << testSuites.size() << std::endl; - std::cout << "\nTest suite [" << test_suite->uuid << "] summary:" << std::endl; - std::cout << " \033[32mPassed:\033[0m " << test_suite->results.passed << std::endl; - std::cout << " \033[31mFailed:\033[0m " << test_suite->results.failed << std::endl; - } -} + for (const auto &test_suite : testSuites) { + std::cout << "Running test suite: \033[38;5;165m" << test_suite->uuid + << "\033[m"; + for (const auto &warning : test_suite->results.warnings) + std::cout << "\n " << warning << std::endl; + for (const auto &test_case : test_suite->cases) { + if (!runTest(test_case, test_suite->results)) { + if (behavior == HALT_SUITE_ON_FAIL) + break; + if (behavior == ABORT_ALL_ON_FAIL) { + std::cout << "\nTest suite [" << test_suite->uuid + << "] summary:" << std::endl; + std::cout << " \033[32mPassed:\033[0m " + << test_suite->results.passed << std::endl; + std::cout << " \033[31mFailed:\033[0m " + << test_suite->results.failed << std::endl; + } + } + } + std::cout << "\nTest suite [" << test_suite->uuid + << "] summary:" << std::endl; + std::cout << " \033[32mPassed:\033[0m " << test_suite->results.passed + << std::endl; + std::cout << " \033[31mFailed:\033[0m " << test_suite->results.failed + << std::endl; + } +} bool runFreeTestCases(const TestRunBehavior behavior) { - bool anyFailed = false; - TestResults freeTestsResults; - std::cout << "Running free tests: " << std::endl; - - for (const auto& testCase : freeTestCases) { - if (!runTest(testCase, freeTestsResults)) { - anyFailed = true; - if (behavior == ABORT_ALL_ON_FAIL || behavior == HALT_SUITE_ON_FAIL) { - break; - } - } + bool anyFailed = false; + TestResults freeTestsResults; + std::cout << "Running free tests: " << std::endl; + + for (const auto &testCase : freeTestCases) { + if (!runTest(testCase, freeTestsResults)) { + anyFailed = true; + if (behavior == ABORT_ALL_ON_FAIL || behavior == HALT_SUITE_ON_FAIL) { + break; + } } + } - std::cout << "\nFree tests summary:" << std::endl; - std::cout << " \033[32mPassed:\033[0m " << freeTestsResults.passed << std::endl; - std::cout << " \033[31mFailed:\033[0m " << freeTestsResults.failed << std::endl; - - if (anyFailed) { - if (behavior == HALT_SUITE_ON_FAIL) { - std::cout << "\n\033[1;38;5;214m================================================\n"; - std::cout << "[Halt Free Tests] Stopping further free tests due to a failure.\n"; - std::cout << "================================================\033[0m\n"; - } else if (behavior == ABORT_ALL_ON_FAIL) { - std::cout << "\n\033[1;38;5;196m================================================\n"; - std::cout << "[Abort] All further tests are aborted due to a failure in free tests.\n"; - std::cout << "================================================\033[0m\n"; - } + std::cout << "\nFree tests summary:" << std::endl; + std::cout << " \033[32mPassed:\033[0m " << freeTestsResults.passed + << std::endl; + std::cout << " \033[31mFailed:\033[0m " << freeTestsResults.failed + << std::endl; + + if (anyFailed) { + if (behavior == HALT_SUITE_ON_FAIL) { + std::cout << "\n\033[1;38;5;214m=========================================" + "=======\n"; + std::cout << "[Halt Free Tests] Stopping further free tests due to a " + "failure.\n"; + std::cout << "================================================\033[0m\n"; + } else if (behavior == ABORT_ALL_ON_FAIL) { + std::cout << "\n\033[1;38;5;196m=========================================" + "=======\n"; + std::cout << "[Abort] All further tests are aborted due to a failure in " + "free tests.\n"; + std::cout << "================================================\033[0m\n"; + throw TestSuiteException("Tests aborted due to failure."); } + } - return anyFailed; + return anyFailed; } -bool runTest(const TestCase* const testCase, TestResults& results) { - std::cout << "\n Running test: \033[38;5;117m" << testCase->name << "\033[0m"; - - try { - // Call the test function - testCase->fn(); - std::cout << " ... Result => \033[32mPassed!\033[0m"; - results.passed++; - return true; - } catch (const std::exception& ex) { - std::cout << " ... Result => \033[31mFailed\033[0m: " << ex.what(); - results.failed++; - return false; - } -} \ No newline at end of file +bool runTest(const TestCase *const testCase, TestResults &results) { + std::cout << "\n Running test: \033[38;5;117m" << testCase->name + << "\033[0m"; + + try { + // Call the test function + testCase->fn(); + std::cout << " ... Result => \033[32mPassed!\033[0m"; + results.passed++; + return true; + } catch (const std::exception &ex) { + std::cout << " ... Result => \033[31mFailed\033[0m: " << ex.what(); + results.failed++; + return false; + } +} + +void checkForTestErrors(const bool freeTestsErrors) { + bool suiteTestsErrors = std::any_of( + testSuites.begin(), testSuites.end(), + [](const TestSuite *suite) { return suite->results.failed > 0; }); + + if (suiteTestsErrors || freeTestsErrors) + throw TestSuiteException("There are some errors in the tests"); +} From bdb87098e8c17a15ff6147e128a2396678173e1f Mon Sep 17 00:00:00 2001 From: Gonzalo Busto Musi Date: Sat, 9 Dec 2023 17:25:18 +0100 Subject: [PATCH 12/24] feat: Added output for halted suite Added output when a test fail on a suite on HALT_SUITE_ON_FAIL mode. Reformat file. --- zero/ifc/test-suite/suite.cppm | 455 +++++++++++++++++---------------- 1 file changed, 240 insertions(+), 215 deletions(-) diff --git a/zero/ifc/test-suite/suite.cppm b/zero/ifc/test-suite/suite.cppm index 67a38bd..9b6327c 100644 --- a/zero/ifc/test-suite/suite.cppm +++ b/zero/ifc/test-suite/suite.cppm @@ -14,18 +14,18 @@ export import :assertions; import std; class TestSuiteException : public std::runtime_error { -public: - TestSuiteException(const std::string &message) - : std::runtime_error(message) {} + public: + TestSuiteException(const std::string &message) + : std::runtime_error(message) {} }; /** * */ struct TestResults { - int passed = 0; - int failed = 0; - std::vector warnings{}; + int passed = 0; + int failed = 0; + std::vector warnings{}; }; // Forward declarations @@ -41,33 +41,34 @@ export struct TestCase; * analysis to immediate failure response. */ export enum TestRunBehavior { - /** - * @brief Execute all tests regardless of failures. - * - * Use this mode when a complete test run is needed to gather full information - * about the system's state or to understand the full extent of the issues. - */ - CONTINUE_ON_ERROR, - - /** - * @brief Halt the execution of the current test suite or free tests upon a - * failure and proceed to the next suite or free test. - * - * This mode stops the execution of the current suite or set of free tests - * immediately upon encountering a failure, then proceeds to the next suite or - * free test. It's beneficial for quickly bypassing problematic tests while - * still executing the remaining tests. - */ - HALT_SUITE_ON_FAIL, - - /** - * @brief Abort all testing activities immediately upon any failure. - * - * Choose this mode when a single failure indicates a critical system issue, - * necessitating immediate attention and halting further tests until - * resolution. - */ - ABORT_ALL_ON_FAIL + /** + * @brief Execute all tests regardless of failures. + * + * Use this mode when a complete test run is needed to gather full + * information about the system's state or to understand the full extent of + * the issues. + */ + CONTINUE_ON_ERROR, + + /** + * @brief Halt the execution of the current test suite or free tests upon a + * failure and proceed to the next suite or free test. + * + * This mode stops the execution of the current suite or set of free tests + * immediately upon encountering a failure, then proceeds to the next suite + * or free test. It's beneficial for quickly bypassing problematic tests + * while still executing the remaining tests. + */ + HALT_SUITE_ON_FAIL, + + /** + * @brief Abort all testing activities immediately upon any failure. + * + * Choose this mode when a single failure indicates a critical system issue, + * necessitating immediate attention and halting further tests until + * resolution. + */ + ABORT_ALL_ON_FAIL }; bool runTest(const TestCase *testCase, TestResults &testResults); bool runFreeTestCases(const TestRunBehavior behavior); @@ -80,199 +81,223 @@ std::vector testSuites; std::vector freeTestCases; export { - /** - * Common group of related test cases, identified by a unique string - */ - struct TestSuite { - std::string uuid; - std::vector cases{}; - TestResults results{}; - - TestSuite() = delete; - /** - * @short This ctr shouldn't exist, since {@link std::string} has a - * constructor for convert cstr to std::string - * @bug Clang16 under windows linking against libc++ with mingw, - * when the TestSuite new instance receives a const char* in a different - * file than the main file (in particular from a module), Clang refuses to - * compile saying that there's no viable ctr for TestSuite receiving a const - * char* - * - * For further @details @see - * https://github.com/llvm/llvm-project/issues/64211 - */ - constexpr explicit TestSuite(const char *uuid) : uuid(std::move(uuid)) {} - constexpr explicit TestSuite(std::string uuid) : uuid(std::move(uuid)) {} - - TestSuite(const TestSuite &rhs) = delete; - TestSuite(TestSuite &&rhs) = delete; - - friend bool operator==(const TestSuite &lhs, const TestSuite &rhs) { - return lhs.uuid == rhs.uuid; - } - }; - - /** - * @struct Holds the data for a particular user's test case. - */ - struct TestCase { - std::string name; - std::function fn; - - /** - * @note waiting for Clang to implement the {@link std::function] - * constructors as *constexpr*, so we can make the {@link TestCase] - * constexpr-constructible - * - * @details constexpr constructor's 2nd parameter type 'std::function' is not a literal type constexpr TestCase(std::string name, - * std::function fn) - * - * 'function' is not literal because it is not an aggregate and has - * no constexpr constructors other than copy or move constructors class - * _LIBCPP_TEMPLATE_VIS function<_Rp(_ArgTypes...)> - */ - TestCase(std::string name, std::function fn) - : name(std::move(name)), fn(std::move(fn)) {} - }; - - void TEST_CASE(const std::string &tname, const std::function tfunc) { - freeTestCases.push_back(new TestCase(tname, tfunc)); - } - - void TEST_CASE(TestSuite & tsuite, const std::string &tname, - const std::function tfunc) { - auto it = std::find_if( - tsuite.cases.begin(), tsuite.cases.end(), - [&](const TestCase *tcase) { return tcase->name == tname; }); - - /// Check that the user didn't registered a test case with the same - /// identifier - if (it == tsuite.cases.end()) - tsuite.cases.emplace_back(new TestCase(tname, tfunc)); - else - tsuite.results.warnings.emplace_back( - "\033[38;5;220m[Warning\033[0m in suite: \033[38;5;165m" + - tsuite.uuid + - "\033[0m\033[38;5;220m]\033[0m " - "Already exists a test case with the name: \033[38;5;117m" + - tname + "\033[0m. Skipping test case."); - /// If this is the first time that the suite is being registered - auto suites_it = std::find_if( - testSuites.begin(), testSuites.end(), - [&](const TestSuite *suite) { return suite->uuid == tsuite.uuid; }); - if (suites_it == testSuites.end()) - testSuites.push_back(&tsuite); - } - - // Function to run all the test cases and suites - void RUN_TESTS(const TestRunBehavior behavior = ABORT_ALL_ON_FAIL) { - bool freeTestsErrors = false; - if (!freeTestCases.empty()) { - freeTestsErrors = runFreeTestCases(behavior); - } - runSuiteTestCases(behavior); - - checkForTestErrors(freeTestsErrors); - } + /** + * Common group of related test cases, identified by a unique string + */ + struct TestSuite { + std::string uuid; + std::vector cases{}; + TestResults results{}; + + TestSuite() = delete; + /** + * @short This ctr shouldn't exist, since {@link std::string} has a + * constructor for convert cstr to std::string + * @bug Clang16 under windows linking against libc++ with mingw, + * when the TestSuite new instance receives a const char* in a different + * file than the main file (in particular from a module), Clang refuses + * to compile saying that there's no viable ctr for TestSuite receiving + * a const char* + * + * For further @details @see + * https://github.com/llvm/llvm-project/issues/64211 + */ + constexpr explicit TestSuite(const char *uuid) + : uuid(std::move(uuid)) {} + constexpr explicit TestSuite(std::string uuid) + : uuid(std::move(uuid)) {} + + TestSuite(const TestSuite &rhs) = delete; + TestSuite(TestSuite &&rhs) = delete; + + friend bool operator==(const TestSuite &lhs, const TestSuite &rhs) { + return lhs.uuid == rhs.uuid; + } + }; + + /** + * @struct Holds the data for a particular user's test case. + */ + struct TestCase { + std::string name; + std::function fn; + + /** + * @note waiting for Clang to implement the {@link std::function] + * constructors as *constexpr*, so we can make the {@link TestCase] + * constexpr-constructible + * + * @details constexpr constructor's 2nd parameter type + * 'std::function' is not a literal type constexpr TestCase(std::string name, + * std::function fn) + * + * 'function' is not literal because it is not an aggregate and + * has no constexpr constructors other than copy or move constructors + * class _LIBCPP_TEMPLATE_VIS function<_Rp(_ArgTypes...)> + */ + TestCase(std::string name, std::function fn) + : name(std::move(name)), fn(std::move(fn)) {} + }; + + void TEST_CASE(const std::string &tname, + const std::function tfunc) { + freeTestCases.push_back(new TestCase(tname, tfunc)); + } + + void TEST_CASE(TestSuite & tsuite, const std::string &tname, + const std::function tfunc) { + auto it = std::find_if( + tsuite.cases.begin(), tsuite.cases.end(), + [&](const TestCase *tcase) { return tcase->name == tname; }); + + /// Check that the user didn't registered a test case with the same + /// identifier + if (it == tsuite.cases.end()) + tsuite.cases.emplace_back(new TestCase(tname, tfunc)); + else + tsuite.results.warnings.emplace_back( + "\033[38;5;220m[Warning\033[0m in suite: \033[38;5;165m" + + tsuite.uuid + + "\033[0m\033[38;5;220m]\033[0m " + "Already exists a test case with the name: \033[38;5;117m" + + tname + "\033[0m. Skipping test case."); + /// If this is the first time that the suite is being registered + auto suites_it = std::find_if( + testSuites.begin(), testSuites.end(), + [&](const TestSuite *suite) { return suite->uuid == tsuite.uuid; }); + if (suites_it == testSuites.end()) + testSuites.push_back(&tsuite); + } + + // Function to run all the test cases and suites + void RUN_TESTS(const TestRunBehavior behavior = ABORT_ALL_ON_FAIL) { + bool freeTestsErrors = false; + if (!freeTestCases.empty()) { + freeTestsErrors = runFreeTestCases(behavior); + } + runSuiteTestCases(behavior); + + checkForTestErrors(freeTestsErrors); + } } void runSuiteTestCases(const TestRunBehavior behavior) { - std::cout << "\nRunning test suites. Total suites found: " - << testSuites.size() << std::endl; - - for (const auto &test_suite : testSuites) { - std::cout << "Running test suite: \033[38;5;165m" << test_suite->uuid - << "\033[m"; - - for (const auto &warning : test_suite->results.warnings) - std::cout << "\n " << warning << std::endl; - for (const auto &test_case : test_suite->cases) { - if (!runTest(test_case, test_suite->results)) { - if (behavior == HALT_SUITE_ON_FAIL) - break; - if (behavior == ABORT_ALL_ON_FAIL) { - std::cout << "\nTest suite [" << test_suite->uuid - << "] summary:" << std::endl; - std::cout << " \033[32mPassed:\033[0m " - << test_suite->results.passed << std::endl; - std::cout << " \033[31mFailed:\033[0m " - << test_suite->results.failed << std::endl; - } - } - } - - std::cout << "\nTest suite [" << test_suite->uuid - << "] summary:" << std::endl; - std::cout << " \033[32mPassed:\033[0m " << test_suite->results.passed - << std::endl; - std::cout << " \033[31mFailed:\033[0m " << test_suite->results.failed - << std::endl; - } + std::cout << "\nRunning test suites. Total suites found: " + << testSuites.size() << std::endl; + + for (const auto &test_suite : testSuites) { + std::cout << "Running test suite: \033[38;5;165m" << test_suite->uuid + << "\033[m"; + + for (const auto &warning : test_suite->results.warnings) + std::cout << "\n " << warning << std::endl; + for (const auto &test_case : test_suite->cases) { + if (!runTest(test_case, test_suite->results)) { + + if (behavior == HALT_SUITE_ON_FAIL) { + std::cout << "\n\033[1;38;5;214m===========================" + "==============" + "=======\n"; + std::cout << "[Halt Suite Tests] Stopping further tests of " + "the suite " + "\033[38;5;165m" + << test_suite->uuid + << "\033[0m\033[1;38;5;214m due to a failure.\n"; + std::cout << "=============================================" + "===\033[0m\n"; + break; + } + + if (behavior == ABORT_ALL_ON_FAIL) { + std::cout << "\nTest suite [" << test_suite->uuid + << "] summary:" << std::endl; + std::cout << " \033[32mPassed:\033[0m " + << test_suite->results.passed << std::endl; + std::cout << " \033[31mFailed:\033[0m " + << test_suite->results.failed << std::endl; + } + } + } + + std::cout << "\nTest suite [" << test_suite->uuid + << "] summary:" << std::endl; + std::cout << " \033[32mPassed:\033[0m " << test_suite->results.passed + << std::endl; + std::cout << " \033[31mFailed:\033[0m " << test_suite->results.failed + << std::endl; + } } bool runFreeTestCases(const TestRunBehavior behavior) { - bool anyFailed = false; - TestResults freeTestsResults; - std::cout << "Running free tests: " << std::endl; - - for (const auto &testCase : freeTestCases) { - if (!runTest(testCase, freeTestsResults)) { - anyFailed = true; - if (behavior == ABORT_ALL_ON_FAIL || behavior == HALT_SUITE_ON_FAIL) { - break; - } - } - } - - std::cout << "\nFree tests summary:" << std::endl; - std::cout << " \033[32mPassed:\033[0m " << freeTestsResults.passed - << std::endl; - std::cout << " \033[31mFailed:\033[0m " << freeTestsResults.failed - << std::endl; - - if (anyFailed) { - if (behavior == HALT_SUITE_ON_FAIL) { - std::cout << "\n\033[1;38;5;214m=========================================" - "=======\n"; - std::cout << "[Halt Free Tests] Stopping further free tests due to a " - "failure.\n"; - std::cout << "================================================\033[0m\n"; - } else if (behavior == ABORT_ALL_ON_FAIL) { - std::cout << "\n\033[1;38;5;196m=========================================" - "=======\n"; - std::cout << "[Abort] All further tests are aborted due to a failure in " - "free tests.\n"; - std::cout << "================================================\033[0m\n"; - throw TestSuiteException("Tests aborted due to failure."); - } - } - - return anyFailed; + bool anyFailed = false; + TestResults freeTestsResults; + std::cout << "Running free tests: " << std::endl; + + for (const auto &testCase : freeTestCases) { + if (!runTest(testCase, freeTestsResults)) { + anyFailed = true; + if (behavior == ABORT_ALL_ON_FAIL || + behavior == HALT_SUITE_ON_FAIL) { + break; + } + } + } + + std::cout << "\nFree tests summary:" << std::endl; + std::cout << " \033[32mPassed:\033[0m " << freeTestsResults.passed + << std::endl; + std::cout << " \033[31mFailed:\033[0m " << freeTestsResults.failed + << std::endl; + + if (anyFailed) { + if (behavior == HALT_SUITE_ON_FAIL) { + std::cout + << "\n\033[1;38;5;214m=========================================" + "=======\n"; + std::cout + << "[Halt Free Tests] Stopping further free tests due to a " + "failure.\n"; + std::cout + << "================================================\033[0m\n"; + } else if (behavior == ABORT_ALL_ON_FAIL) { + std::cout + << "\n\033[1;38;5;196m=========================================" + "=======\n"; + std::cout + << "[Abort] All further tests are aborted due to a failure in " + "free tests.\n"; + std::cout + << "================================================\033[0m\n"; + throw TestSuiteException("Tests aborted due to failure."); + } + } + + return anyFailed; } bool runTest(const TestCase *const testCase, TestResults &results) { - std::cout << "\n Running test: \033[38;5;117m" << testCase->name - << "\033[0m"; - - try { - // Call the test function - testCase->fn(); - std::cout << " ... Result => \033[32mPassed!\033[0m"; - results.passed++; - return true; - } catch (const std::exception &ex) { - std::cout << " ... Result => \033[31mFailed\033[0m: " << ex.what(); - results.failed++; - return false; - } + std::cout << "\n Running test: \033[38;5;117m" << testCase->name + << "\033[0m"; + + try { + // Call the test function + testCase->fn(); + std::cout << " ... Result => \033[32mPassed!\033[0m"; + results.passed++; + return true; + } catch (const std::exception &ex) { + std::cout << " ... Result => \033[31mFailed\033[0m: " << ex.what(); + results.failed++; + return false; + } } void checkForTestErrors(const bool freeTestsErrors) { - bool suiteTestsErrors = std::any_of( - testSuites.begin(), testSuites.end(), - [](const TestSuite *suite) { return suite->results.failed > 0; }); + bool suiteTestsErrors = std::any_of( + testSuites.begin(), testSuites.end(), + [](const TestSuite *suite) { return suite->results.failed > 0; }); - if (suiteTestsErrors || freeTestsErrors) - throw TestSuiteException("There are some errors in the tests"); + if (suiteTestsErrors || freeTestsErrors) + throw TestSuiteException("There are some errors in the tests"); } From 2413128249e33b83374d27a12de84920b17b9206 Mon Sep 17 00:00:00 2001 From: Gonzalo Busto Musi Date: Sat, 9 Dec 2023 17:33:00 +0100 Subject: [PATCH 13/24] fix: :bug: Exit on default mode Fixed a bug on ABORT_ALL_ON_FAIL mode when a test fails in a suite. Added output. --- zero/ifc/test-suite/suite.cppm | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/zero/ifc/test-suite/suite.cppm b/zero/ifc/test-suite/suite.cppm index 9b6327c..cc236f6 100644 --- a/zero/ifc/test-suite/suite.cppm +++ b/zero/ifc/test-suite/suite.cppm @@ -216,6 +216,16 @@ void runSuiteTestCases(const TestRunBehavior behavior) { << test_suite->results.passed << std::endl; std::cout << " \033[31mFailed:\033[0m " << test_suite->results.failed << std::endl; + + std::cout << "\n\033[1;38;5;196m===========================" + "==============" + "=======\n"; + std::cout << "[Abort] All further tests are aborted due to " + "a failure in " + "a test in this suite.\n"; + std::cout << "=============================================" + "===\033[0m\n"; + return; } } } From 6a0a01b1880dbec0c2b392871623e1f6cb4c7664 Mon Sep 17 00:00:00 2001 From: Gonzalo Busto Musi Date: Sat, 9 Dec 2023 17:36:17 +0100 Subject: [PATCH 14/24] refactor: Default on RUN_TESTS() Using Default value on RUN_TESTS(). Added comments. --- zero/main.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/zero/main.cpp b/zero/main.cpp index 64a75f9..240ebdc 100644 --- a/zero/main.cpp +++ b/zero/main.cpp @@ -69,7 +69,8 @@ int main() { // Register a new test case using a function pointer. - TEST_CASE("Addition Test With Pointers", testPtrsAddition); + // Comment this line if you don't want failed tests in the freetests + // TEST_CASE("Addition Test With Pointers", testPtrsAddition); // Users can register a new test case using lambdas, avoiding writing standalone functions TEST_CASE("Subtraction Test", []() { @@ -104,7 +105,8 @@ int main() { TEST_CASE(anotherSuite, "Multiplication Test", testMultiplication); // Don't forget to call this free function, to run all the tests written! - RUN_TESTS(CONTINUE_ON_ERROR); + // Options are: CONTINUE_ON_ERROR, HALT_SUITE_ON_FAIL, ABORT_ALL_ON_FAIL + RUN_TESTS(); return 0; } From 5417501a738f08d06b5f8afe58df484424413498 Mon Sep 17 00:00:00 2001 From: Gonzalo Busto Musi Date: Sat, 9 Dec 2023 17:37:26 +0100 Subject: [PATCH 15/24] chore: Added .clang-format --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index db19af5..f5c0f37 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ compile_commands.json notes.txt *.exe .cache +.clang-format From 98c7bb40c844a03e67435aff74f99b00fb4beed5 Mon Sep 17 00:00:00 2001 From: Gonzalo Busto Musi Date: Fri, 15 Dec 2023 12:41:32 +0100 Subject: [PATCH 16/24] refactor: Substitution of exception by exit(1) The exception is eliminated in favor of exit(1) since error output is already returned. --- zero/ifc/test-suite/suite.cppm | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/zero/ifc/test-suite/suite.cppm b/zero/ifc/test-suite/suite.cppm index cc236f6..28d9e8d 100644 --- a/zero/ifc/test-suite/suite.cppm +++ b/zero/ifc/test-suite/suite.cppm @@ -13,11 +13,6 @@ export import :assertions; import std; -class TestSuiteException : public std::runtime_error { - public: - TestSuiteException(const std::string &message) - : std::runtime_error(message) {} -}; /** * @@ -279,7 +274,7 @@ bool runFreeTestCases(const TestRunBehavior behavior) { "free tests.\n"; std::cout << "================================================\033[0m\n"; - throw TestSuiteException("Tests aborted due to failure."); + std::exit(1); } } @@ -303,11 +298,12 @@ bool runTest(const TestCase *const testCase, TestResults &results) { } } + void checkForTestErrors(const bool freeTestsErrors) { + bool suiteTestsErrors = std::any_of( testSuites.begin(), testSuites.end(), [](const TestSuite *suite) { return suite->results.failed > 0; }); - - if (suiteTestsErrors || freeTestsErrors) - throw TestSuiteException("There are some errors in the tests"); -} + std::cout << freeTestsErrors << " " << suiteTestsErrors; + if (suiteTestsErrors || freeTestsErrors) std::exit(1); +} \ No newline at end of file From e6ed16865b5d60164dbc1afeec4ff0914c157e26 Mon Sep 17 00:00:00 2001 From: Gonzalo Busto Musi Date: Fri, 15 Dec 2023 13:50:28 +0100 Subject: [PATCH 17/24] docs: Docs for TestResults. --- zero/ifc/test-suite/suite.cppm | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/zero/ifc/test-suite/suite.cppm b/zero/ifc/test-suite/suite.cppm index 28d9e8d..de2153c 100644 --- a/zero/ifc/test-suite/suite.cppm +++ b/zero/ifc/test-suite/suite.cppm @@ -15,7 +15,24 @@ import std; /** + * @struct TestResults + * @brief Holds the results of test execution. * + * This structure is used to track the outcomes of a series of tests, + * including the number of passed and failed tests, along with any warnings + * that might have been generated during test execution. + * + * @var passed + * The count of tests that have successfully passed. + * + * @var failed + * The count of tests that have failed. + * + * @var warnings + * A list of warning messages generated during test execution. Warnings are + * used to notify about non-critical issues or potential problems in the tests, + * which do not necessarily constitute test failures. For example, a warning + * can be used to indicate the duplication of a test case. */ struct TestResults { int passed = 0; From e030309f16998c933f5ec968bc327000a87f7413 Mon Sep 17 00:00:00 2001 From: Gonzalo Busto Musi Date: Fri, 15 Dec 2023 14:02:17 +0100 Subject: [PATCH 18/24] docs: Docs for checkForTestErrors. --- zero/ifc/test-suite/suite.cppm | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/zero/ifc/test-suite/suite.cppm b/zero/ifc/test-suite/suite.cppm index de2153c..8782384 100644 --- a/zero/ifc/test-suite/suite.cppm +++ b/zero/ifc/test-suite/suite.cppm @@ -85,6 +85,28 @@ export enum TestRunBehavior { bool runTest(const TestCase *testCase, TestResults &testResults); bool runFreeTestCases(const TestRunBehavior behavior); void runSuiteTestCases(const TestRunBehavior behavior); + +/** + * @brief Checks for errors post test execution based on test run behavior. + * + * This function is designed to be called after all tests have been executed under + * certain TestRunBehaviors (CONTINUE_ON_ERROR, HALT_SUITE_ON_FAIL). It determines if + * any errors occurred during the test runs. + * + * In scenarios where tests are allowed to continue despite failures (CONTINUE_ON_ERROR) + * or where test execution is halted only for the current suite upon failure + * (HALT_SUITE_ON_FAIL), this function provides a final check to ascertain if any errors + * were encountered during the entire testing process. + * + * The function evaluates two sources of potential errors: + * 1. Free test errors, indicated by the boolean parameter 'freeTestsErrors'. + * 2. Suite test errors, determined by examining all test suites for any failures. + * + * If errors are found in either free tests or test suites, the function terminates the + * program with an exit code of 1, signaling an error condition. + * + * @param freeTestsErrors Boolean indicating if there were errors in the free tests. + */ void checkForTestErrors(const bool freeTestsErrors); // Top-level containers. They hold pointers to the types to avoid: From 2113a109eb71948af6117382cc548bcaf225836b Mon Sep 17 00:00:00 2001 From: Gonzalo Busto Musi Date: Sat, 16 Dec 2023 15:25:32 +0100 Subject: [PATCH 19/24] docs: Documentation for test functions. --- zero/ifc/test-suite/suite.cppm | 41 ++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/zero/ifc/test-suite/suite.cppm b/zero/ifc/test-suite/suite.cppm index 8782384..01039e8 100644 --- a/zero/ifc/test-suite/suite.cppm +++ b/zero/ifc/test-suite/suite.cppm @@ -82,8 +82,49 @@ export enum TestRunBehavior { */ ABORT_ALL_ON_FAIL }; + +/** + * @brief Executes a single test case and updates the test results. + * + * This function runs an individual test case and captures its success or failure. + * It updates the passed and failed count in the provided TestResults object. + * If the test case throws an exception, it is caught and treated as a test failure. + * + * @param testCase Pointer to the @ref TestCase to be executed. + * @param testResults Reference to a @ref TestResults object where the outcome (pass/fail) + * of the test will be recorded. + * @return Returns true if the test case passed, false if it failed. + */ bool runTest(const TestCase *testCase, TestResults &testResults); + +/** + * @brief Executes all free-standing test cases based on the specified behavior. + * + * A "free-standing test" refers to a test case that is not part of any test suite. + * These are individual tests executed independently, without being grouped in a suite. + * This function iterates over and executes all such free-standing test cases. The behavior + * of the function upon encountering a failed test is determined by the @ref TestRunBehavior + * parameter. It can either continue running the remaining tests or halt/abort execution. + * + * @param behavior The @ref TestRunBehavior (e.g., CONTINUE_ON_ERROR, HALT_SUITE_ON_FAIL, + * ABORT_ALL_ON_FAIL) that determines the function's response to test + * failures. + * @return Returns true if any test case failed, false otherwise. + */ bool runFreeTestCases(const TestRunBehavior behavior); + +/** + * @brief Executes all test cases within test suites based on the specified behavior. + * + * This function iterates over all registered test suites, executing the test cases + * within each suite. The execution behavior upon encountering a test failure is + * governed by the @ref TestRunBehavior parameter. Depending on this parameter, the function + * can continue with the next tests/suites, halt the current suite, or abort all tests. + * + * @param behavior The @ref TestRunBehavior (e.g., CONTINUE_ON_ERROR, HALT_SUITE_ON_FAIL, + * ABORT_ALL_ON_FAIL) that influences the function's handling of test + * failures. + */ void runSuiteTestCases(const TestRunBehavior behavior); /** From 5f9f265feac92f61d4259f626e2ea5d4627df780 Mon Sep 17 00:00:00 2001 From: Gonzalo Busto Musi Date: Sat, 16 Dec 2023 20:10:27 +0100 Subject: [PATCH 20/24] docs: :memo: Documentation update to reflect the modes. The test suite documentation has been updated, adding information on test execution modes. Updated the example of use with more tests, to be able to see more clearly how the modes work in the result images. --- .../tsuite_results_ABORT_ALL_ON_FAIL_mode.png | Bin 0 -> 11484 bytes .../tsuite_results_CONTINUE_ON_ERROR_mode.png | Bin 0 -> 40754 bytes ...tsuite_results_HALT_SUITE_ON_FAIL_mode.png | Bin 0 -> 42731 bytes zero/ifc/test-suite/README.md | 81 +++++++++++++++--- 4 files changed, 69 insertions(+), 12 deletions(-) create mode 100644 zero/assets/tsuite_results_ABORT_ALL_ON_FAIL_mode.png create mode 100644 zero/assets/tsuite_results_CONTINUE_ON_ERROR_mode.png create mode 100644 zero/assets/tsuite_results_HALT_SUITE_ON_FAIL_mode.png diff --git a/zero/assets/tsuite_results_ABORT_ALL_ON_FAIL_mode.png b/zero/assets/tsuite_results_ABORT_ALL_ON_FAIL_mode.png new file mode 100644 index 0000000000000000000000000000000000000000..68f9eec3a81c1064e25477035f18a816f444f04c GIT binary patch literal 11484 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 literal 0 HcmV?d00001 diff --git a/zero/assets/tsuite_results_CONTINUE_ON_ERROR_mode.png b/zero/assets/tsuite_results_CONTINUE_ON_ERROR_mode.png new file mode 100644 index 0000000000000000000000000000000000000000..44c4e91be8c2bc47f5977fb03a56d342910988ad GIT binary patch literal 40754 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 E4O6TwP5=M^ literal 0 HcmV?d00001 diff --git a/zero/assets/tsuite_results_HALT_SUITE_ON_FAIL_mode.png b/zero/assets/tsuite_results_HALT_SUITE_ON_FAIL_mode.png new file mode 100644 index 0000000000000000000000000000000000000000..7964bf63e7297cb1c38527906d031d00e4fef70b GIT binary patch literal 42731 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 Date: Sat, 16 Dec 2023 20:13:38 +0100 Subject: [PATCH 21/24] chore: :fire: An obsolete image is deleted. The old example image of the output of the previous example code in the README is removed, as it does not reflect the actual code written and the modes are not reflected. --- zero/assets/tsuite_results.png | Bin 32108 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 zero/assets/tsuite_results.png diff --git a/zero/assets/tsuite_results.png b/zero/assets/tsuite_results.png deleted file mode 100644 index 0e43d4f6c13238dc6d88c9aecba28f082620da80..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32108 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 Date: Sat, 16 Dec 2023 20:17:20 +0100 Subject: [PATCH 22/24] docs: :memo: Removed excess code in example --- zero/ifc/test-suite/README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/zero/ifc/test-suite/README.md b/zero/ifc/test-suite/README.md index 003ef7b..9033d73 100644 --- a/zero/ifc/test-suite/README.md +++ b/zero/ifc/test-suite/README.md @@ -113,11 +113,6 @@ void testPtrsAddition() { // Driver code int main() { - // run_containers_examples(); - // run_output_iterator_examples(); - // run_quantities_examples(); - // run_formatter_and_stylize_examples(); - // run_print_examples(); TEST_CASE("Multiplication Test", []() { int result = 5 * 3; From b4f3a58acf5cb6762c1eff0c91cf37251b17b60d Mon Sep 17 00:00:00 2001 From: Gonzalo Busto Musi Date: Sat, 16 Dec 2023 20:52:44 +0100 Subject: [PATCH 23/24] docs: :memo: Information on modes is added. Due to an oversight this information had not been added in one of the previous commits. --- zero/ifc/test-suite/README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/zero/ifc/test-suite/README.md b/zero/ifc/test-suite/README.md index 9033d73..f3ad231 100644 --- a/zero/ifc/test-suite/README.md +++ b/zero/ifc/test-suite/README.md @@ -65,6 +65,21 @@ Test cases within a suite can be registered using the `TEST_CASE(...)` function, just like standalone tests, but choosing the overload that receives as its first parameter a reference to the test suite. +### **Test Modes** +In the pursuit of flexibility and control over the test execution process, our testing framework offers different modes of operation. These modes determine the test runner's behavior in response to test failures, allowing users to tailor the testing process according to their specific needs or the nature of the tests being run. + +1. **CONTINUE_ON_ERROR** + - **Description**: This mode ensures that all tests are executed regardless of any failures encountered. It's ideal for comprehensive error analysis or understanding the full extent of issues in the codebase. + - **Use Case**: Opt for this mode when a complete analysis of the system's state is required, particularly to identify all potential problems in a single test run. + +2. **HALT_SUITE_ON_FAIL** + - **Description**: In this mode, the test suite halts the execution of the current suite or free tests upon encountering a failure, then proceeds to the next suite or free test. It allows for a quick bypass of problematic tests while still performing remaining tests. + - **Use Case**: Useful for scenarios where quickly identifying and addressing failures is important, without getting bogged down by tests in a problematic suite. + +3. **ABORT_ALL_ON_FAIL (Default mode)** + - **Description**: This mode adopts a zero-tolerance approach towards test failures. As soon as any test fails, it immediately halts all further testing activities. + - **Use Case**: Use this mode to avoid wasting time on further tests when there's already a known issue that needs fixing. + ## **Example of usage** Here's an example of how to use the custom test-suite to write and run test cases: From d0fa0c67de2794c433e461fb217734b858a63950 Mon Sep 17 00:00:00 2001 From: Gonzalo Busto Musi Date: Sat, 16 Dec 2023 21:32:07 +0100 Subject: [PATCH 24/24] docs: :memo: Added test suite reference. --- README.md | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/README.md b/README.md index c364f54..d119c0c 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ Quick description, and links to the documentation of all the libraries of the pr - [Text](./zero/ifc/text/README.md) - Offers utilities for text manipulation, string splitting, formatting, styling, and print to console functionalities. - [Types](./zero/ifc/types/README.md) - Information about types and traits for types - [Math](./zero/ifc/math/README.md) - Mathematical operations and functions +- [Test Suite](./zero/ifc/test-suite/README.md) - A flexible and simple test-suite. ## Sponsorship @@ -48,16 +49,6 @@ system in a real world environment, so people can profit by taking examples of h in a big scale project. Also, we introduce the changes of the latest releases available here, so it serves as well as a kind of latest up-to-date guide. -## Testing tools - -We are using [`catch2`](https://github.com/catchorg/Catch2) as the testing suite for `Zero`, in its *header-only* version. - -We made a try to replace it with `boost::ut`, that spots that is compatible with modules, but we are getting all kind of errors in the process of convert the provided header to a precompiled module, so we will stay -with `catch2` for now. - -Also, we are looking for generate a precompiled header of the Catch's suite in order to decrement -the tests compilation time. - ## General guidelines and API design This entry is a collection of both the things that we're looking for, or focusing on