From 5de5884aefd7199282af5d1cd3d1f35df491ef64 Mon Sep 17 00:00:00 2001 From: Gregory Gauthier Date: Fri, 6 Mar 2026 16:37:00 +0000 Subject: [PATCH] test(ci): enable verbose output in CI tests and refactor unit tests - Add `-V` flag to `ctest` in CMake workflow for detailed test output - Refactor `passwdgen_test.cpp` to use manual assertions and expand test cases for better coverage - Remove redundant `test_passwdgen.cpp` file with similar but outdated test implementations --- .gitea/workflows/cmake.yml | 2 +- tests/passwdgen_test.cpp | 225 ++++++++++++++++++++++++++++++------- tests/test_passwdgen.cpp | 198 -------------------------------- 3 files changed, 188 insertions(+), 237 deletions(-) delete mode 100644 tests/test_passwdgen.cpp diff --git a/.gitea/workflows/cmake.yml b/.gitea/workflows/cmake.yml index a011537..05a5584 100644 --- a/.gitea/workflows/cmake.yml +++ b/.gitea/workflows/cmake.yml @@ -52,4 +52,4 @@ jobs: shell: bash # Execute tests defined by the CMake configuration. # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail - run: ctest -C $BUILD_TYPE + run: ctest -C $BUILD_TYPE -V diff --git a/tests/passwdgen_test.cpp b/tests/passwdgen_test.cpp index 40043a9..22f29cc 100644 --- a/tests/passwdgen_test.cpp +++ b/tests/passwdgen_test.cpp @@ -1,48 +1,197 @@ -// tests/passwdgen_test.cpp #include "../include/passwdgen.h" #include -#include -#include +#include +#include +#include +#include -void test_random_string_length() { - for (const int lengths[] = {0, 1, 10, 32, 100}; const auto length : lengths) { - std::string result = random_string(length, false); - assert(result.length() == length); +int main() { + bool all_passed = true; + std::srand(12345); - std::string result_with_punc = random_string(length, true); - assert(result_with_punc.length() == length); - } - std::cout << "Length test passed\n"; -} - -void test_alphanumeric_only() { - const std::regex alphanumeric_only("^[a-zA-Z0-9]*$"); - - for (int i = 0; i < 10; i++) { - std::string result = random_string(20, false); - assert(std::regex_match(result, alphanumeric_only)); - } - std::cout << "Alphanumeric test passed\n"; -} - -void test_with_punctuation() { - bool found_special = false; - - for (int i = 0; i < 100 && !found_special; i++) { - std::string result = random_string(100, true); - if (!std::regex_match(result, std::regex("^[a-zA-Z0-9]*$"))) { - found_special = true; + // LengthZeroReturnsEmpty + { + std::string result = random_string(0, false); + if (result.length() != 0u) { + std::cout << "LengthZeroReturnsEmpty: FAILED" << std::endl; + all_passed = false; + } else { + std::cout << "LengthZeroReturnsEmpty: PASSED" << std::endl; } } - assert(found_special); - std::cout << "Punctuation test passed\n"; -} + // LengthOneNoPuncReturnsAlphanumChar + { + std::string result = random_string(1, false); + std::string alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + if (result.length() != 1u || alphanum.find(result[0]) == std::string::npos) { + std::cout << "LengthOneNoPuncReturnsAlphanumChar: FAILED" << std::endl; + all_passed = false; + } else { + std::cout << "LengthOneNoPuncReturnsAlphanumChar: PASSED" << std::endl; + } + } -int main() { - test_random_string_length(); - test_alphanumeric_only(); - test_with_punctuation(); - std::cout << "All tests passed!\n"; + // ExactLengthNoPunc + { + std::string result = random_string(10, false); + if (result.length() != 10u) { + std::cout << "ExactLengthNoPunc: FAILED" << std::endl; + all_passed = false; + } else { + std::cout << "ExactLengthNoPunc: PASSED" << std::endl; + } + } + + // LengthOneWithPuncReturnsAnyChar + { + std::string result = random_string(1, true); + std::string all_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!\"#$%&'()*+,-./:;<=>?@[\\\\]^_`{|}~"; + if (result.length() != 1u || all_chars.find(result[0]) == std::string::npos) { + std::cout << "LengthOneWithPuncReturnsAnyChar: FAILED" << std::endl; + all_passed = false; + } else { + std::cout << "LengthOneWithPuncReturnsAnyChar: PASSED" << std::endl; + } + } + + // ExactLengthWithPunc + { + std::string result = random_string(20, true); + if (result.length() != 20u) { + std::cout << "ExactLengthWithPunc: FAILED" << std::endl; + all_passed = false; + } else { + std::cout << "ExactLengthWithPunc: PASSED" << std::endl; + } + } + + // VeryLargeLength + { + std::string result = random_string(10000, false); + if (result.length() != 10000u) { + std::cout << "VeryLargeLength: FAILED" << std::endl; + all_passed = false; + } else { + std::cout << "VeryLargeLength: PASSED" << std::endl; + } + } + + // MaxSizeTLength + { + std::size_t large_length = 1000; + std::string result = random_string(large_length, true); + if (result.length() != large_length) { + std::cout << "MaxSizeTLength: FAILED" << std::endl; + all_passed = false; + } else { + std::cout << "MaxSizeTLength: PASSED" << std::endl; + } + } + + // ShowUsageTest + auto capture_cerr = [](auto func, const std::string& prog) -> std::string { + std::stringstream buffer; + std::streambuf* old_cerr = std::cerr.rdbuf(buffer.rdbuf()); + func(prog); + std::cerr.rdbuf(old_cerr); + return buffer.str(); + }; + + // BasicUsageMessage + { + std::string output = capture_cerr([&](const std::string& p){ show_usage(p); }, "passwdgen"); + bool passed = !output.empty() && + output.find("Usage: passwdgen") != std::string::npos && + output.find("--help") != std::string::npos && + output.find("--length") != std::string::npos && + output.find("--punctuation") != std::string::npos; + if (!passed) { + std::cout << "BasicUsageMessage: FAILED" << std::endl; + all_passed = false; + } else { + std::cout << "BasicUsageMessage: PASSED" << std::endl; + } + } + + // CustomProgramName + { + std::string output = capture_cerr([&](const std::string& p){ show_usage(p); }, "./my-password-gen"); + bool passed = output.find("Usage: ./my-password-gen") != std::string::npos; + if (!passed) { + std::cout << "CustomProgramName: FAILED" << std::endl; + all_passed = false; + } else { + std::cout << "CustomProgramName: PASSED" << std::endl; + } + } + + // EmptyProgramName + { + std::string output = capture_cerr([&](const std::string& p){ show_usage(p); }, ""); + bool passed = output.find("Usage: ") != std::string::npos; + if (!passed) { + std::cout << "EmptyProgramName: FAILED" << std::endl; + all_passed = false; + } else { + std::cout << "EmptyProgramName: PASSED" << std::endl; + } + } + + // CharacterSetNoPunc + { + std::string result = random_string(100, false); + std::string alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + bool has_only_alphanum = true; + for (char c : result) { + if (alphanum.find(c) == std::string::npos) { + has_only_alphanum = false; + break; + } + } + if (!has_only_alphanum) { + std::cout << "CharacterSetNoPunc: FAILED" << std::endl; + all_passed = false; + } else { + std::cout << "CharacterSetNoPunc: PASSED" << std::endl; + } + } + + // CharacterSetWithPunc + { + std::string result = random_string(100, true); + std::string specials = "!\"#$%&'()*+,-./:;<=>?@[\\\\]^_`{|}~"; + bool has_special_chars = false; + for (char c : result) { + if (specials.find(c) != std::string::npos) { + has_special_chars = true; + break; + } + } + if (!has_special_chars) { + std::cout << "CharacterSetWithPunc: FAILED" << std::endl; + all_passed = false; + } else { + std::cout << "CharacterSetWithPunc: PASSED" << std::endl; + } + } + + // FullIntegration + { + std::string pwd = random_string(16, true); + if (pwd.length() != 16u) { + std::cout << "FullIntegration: FAILED" << std::endl; + all_passed = false; + } else { + std::cout << "FullIntegration: PASSED" << std::endl; + } + } + + if (all_passed) { + std::cout << "All tests passed!" << std::endl; + } else { + std::cout << "Some tests FAILED!" << std::endl; + return 1; + } return 0; } diff --git a/tests/test_passwdgen.cpp b/tests/test_passwdgen.cpp deleted file mode 100644 index 84874cb..0000000 --- a/tests/test_passwdgen.cpp +++ /dev/null @@ -1,198 +0,0 @@ -#include "../passwdgen.h" -#include -#include -#include -#include -#include -#include - -int main() { - bool all_passed = true; - std::srand(12345); - - // LengthZeroReturnsEmpty - { - std::string result = random_string(0, false); - if (result.length() != 0u) { - std::cout << "LengthZeroReturnsEmpty: FAILED" << std::endl; - all_passed = false; - } else { - std::cout << "LengthZeroReturnsEmpty: PASSED" << std::endl; - } - } - - // LengthOneNoPuncReturnsAlphanumChar - { - std::string result = random_string(1, false); - std::string alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - if (result.length() != 1u || alphanum.find(result[0]) == std::string::npos) { - std::cout << "LengthOneNoPuncReturnsAlphanumChar: FAILED" << std::endl; - all_passed = false; - } else { - std::cout << "LengthOneNoPuncReturnsAlphanumChar: PASSED" << std::endl; - } - } - - // ExactLengthNoPunc - { - std::string result = random_string(10, false); - if (result.length() != 10u) { - std::cout << "ExactLengthNoPunc: FAILED" << std::endl; - all_passed = false; - } else { - std::cout << "ExactLengthNoPunc: PASSED" << std::endl; - } - } - - // LengthOneWithPuncReturnsAnyChar - { - std::string result = random_string(1, true); - std::string all_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!\\"#$%&'()*+,-./:;<=>?@[\\\\]^_`{|}~"; - if (result.length() != 1u || all_chars.find(result[0]) == std::string::npos) { - std::cout << "LengthOneWithPuncReturnsAnyChar: FAILED" << std::endl; - all_passed = false; - } else { - std::cout << "LengthOneWithPuncReturnsAnyChar: PASSED" << std::endl; - } - } - - // ExactLengthWithPunc - { - std::string result = random_string(20, true); - if (result.length() != 20u) { - std::cout << "ExactLengthWithPunc: FAILED" << std::endl; - all_passed = false; - } else { - std::cout << "ExactLengthWithPunc: PASSED" << std::endl; - } - } - - // VeryLargeLength - { - std::string result = random_string(10000, false); - if (result.length() != 10000u) { - std::cout << "VeryLargeLength: FAILED" << std::endl; - all_passed = false; - } else { - std::cout << "VeryLargeLength: PASSED" << std::endl; - } - } - - // MaxSizeTLength - { - std::size_t large_length = 1000; - std::string result = random_string(large_length, true); - if (result.length() != large_length) { - std::cout << "MaxSizeTLength: FAILED" << std::endl; - all_passed = false; - } else { - std::cout << "MaxSizeTLength: PASSED" << std::endl; - } - } - - // ShowUsageTest - auto capture_cerr = [](auto func, const std::string& prog) -> std::string { - std::stringstream buffer; - std::streambuf* old_cerr = std::cerr.rdbuf(buffer.rdbuf()); - func(prog); - std::cerr.rdbuf(old_cerr); - return buffer.str(); - }; - - // BasicUsageMessage - { - std::string output = capture_cerr([&](const std::string& p){ show_usage(p); }, "passwdgen"); - bool passed = !output.empty() && - output.find("Usage: passwdgen") != std::string::npos && - output.find("--help") != std::string::npos && - output.find("--length") != std::string::npos && - output.find("--punctuation") != std::string::npos; - if (!passed) { - std::cout << "BasicUsageMessage: FAILED" << std::endl; - all_passed = false; - } else { - std::cout << "BasicUsageMessage: PASSED" << std::endl; - } - } - - // CustomProgramName - { - std::string output = capture_cerr([&](const std::string& p){ show_usage(p); }, "./my-password-gen"); - bool passed = output.find("Usage: ./my-password-gen") != std::string::npos; - if (!passed) { - std::cout << "CustomProgramName: FAILED" << std::endl; - all_passed = false; - } else { - std::cout << "CustomProgramName: PASSED" << std::endl; - } - } - - // EmptyProgramName - { - std::string output = capture_cerr([&](const std::string& p){ show_usage(p); }, ""); - bool passed = output.find("Usage: ") != std::string::npos; - if (!passed) { - std::cout << "EmptyProgramName: FAILED" << std::endl; - all_passed = false; - } else { - std::cout << "EmptyProgramName: PASSED" << std::endl; - } - } - - // CharacterSetNoPunc - { - std::string result = random_string(100, false); - std::string alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - bool has_only_alphanum = true; - for (char c : result) { - if (alphanum.find(c) == std::string::npos) { - has_only_alphanum = false; - break; - } - } - if (!has_only_alphanum) { - std::cout << "CharacterSetNoPunc: FAILED" << std::endl; - all_passed = false; - } else { - std::cout << "CharacterSetNoPunc: PASSED" << std::endl; - } - } - - // CharacterSetWithPunc - { - std::string result = random_string(100, true); - std::string specials = "!\\"#$%&'()*+,-./:;<=>?@[\\\\]^_`{|}~"; - bool has_special_chars = false; - for (char c : result) { - if (specials.find(c) != std::string::npos) { - has_special_chars = true; - break; - } - } - if (!has_special_chars) { - std::cout << "CharacterSetWithPunc: FAILED" << std::endl; - all_passed = false; - } else { - std::cout << "CharacterSetWithPunc: PASSED" << std::endl; - } - } - - // FullIntegration - { - std::string pwd = random_string(16, true); - if (pwd.length() != 16u) { - std::cout << "FullIntegration: FAILED" << std::endl; - all_passed = false; - } else { - std::cout << "FullIntegration: PASSED" << std::endl; - } - } - - if (all_passed) { - std::cout << "All tests passed!" << std::endl; - } else { - std::cout << "Some tests FAILED!" << std::endl; - return 1; - } - return 0; -} \ No newline at end of file