test(ci): enable verbose output in CI tests and refactor unit tests
All checks were successful
CMake / build (push) Successful in 47s

- 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
This commit is contained in:
Gregory Gauthier 2026-03-06 16:37:00 +00:00
parent 55475c4f6f
commit 5de5884aef
3 changed files with 188 additions and 237 deletions

View File

@ -52,4 +52,4 @@ jobs:
shell: bash shell: bash
# Execute tests defined by the CMake configuration. # Execute tests defined by the CMake configuration.
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail # 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

View File

@ -1,48 +1,197 @@
// tests/passwdgen_test.cpp
#include "../include/passwdgen.h" #include "../include/passwdgen.h"
#include <iostream> #include <iostream>
#include <regex> #include <sstream>
#include <cassert> #include <string>
#include <cstdlib>
#include <cstddef>
void test_random_string_length() { int main() {
for (const int lengths[] = {0, 1, 10, 32, 100}; const auto length : lengths) { bool all_passed = true;
std::string result = random_string(length, false); std::srand(12345);
assert(result.length() == length);
std::string result_with_punc = random_string(length, true); // LengthZeroReturnsEmpty
assert(result_with_punc.length() == length); {
} std::string result = random_string(0, false);
std::cout << "Length test passed\n"; if (result.length() != 0u) {
} std::cout << "LengthZeroReturnsEmpty: FAILED" << std::endl;
all_passed = false;
void test_alphanumeric_only() { } else {
const std::regex alphanumeric_only("^[a-zA-Z0-9]*$"); std::cout << "LengthZeroReturnsEmpty: PASSED" << std::endl;
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;
} }
} }
assert(found_special); // LengthOneNoPuncReturnsAlphanumChar
std::cout << "Punctuation test passed\n"; {
} 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() { // ExactLengthNoPunc
test_random_string_length(); {
test_alphanumeric_only(); std::string result = random_string(10, false);
test_with_punctuation(); if (result.length() != 10u) {
std::cout << "All tests passed!\n"; 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; return 0;
} }

View File

@ -1,198 +0,0 @@
#include "../passwdgen.h"
#include <iostream>
#include <sstream>
#include <string>
#include <cstdlib>
#include <cassert>
#include <cstddef>
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() &amp;&amp;
output.find("Usage: passwdgen") != std::string::npos &amp;&amp;
output.find("--help") != std::string::npos &amp;&amp;
output.find("--length") != std::string::npos &amp;&amp;
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;
}