Skip to content

Commit 7a2c658

Browse files
etrclaude
andcommitted
TASK-076: address test-quality-reviewer findings
Apply iter-1 validation-fixer changes for the three majors raised by test-quality-reviewer: - Remove unreachable dead assertions in littletest_skip_semantics_test. - Tighten is_psk_unsupported_error coverage via direct unit assertions. - Replace silent always-green branches in IPv6/dual-stack success paths with LT_ASSERT_EQ on curl results. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 1e9a569 commit 7a2c658

2 files changed

Lines changed: 88 additions & 58 deletions

File tree

test/integ/ws_start_stop.cpp

Lines changed: 84 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -764,21 +764,21 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, ipv6_webserver)
764764
std::string s;
765765
CURL *curl = curl_easy_init();
766766
CURLcode res;
767-
curl_easy_setopt(curl, CURLOPT_URL, "http://[::1]:" STR(PORT + 20) "/base");
767+
// TASK-076 fix: STR(PORT + 20) stringifies to "8080 + 20" (not
768+
// "8100"), so we build the URL at runtime with std::to_string.
769+
std::string ipv6_url = "http://[::1]:" + std::to_string(PORT + 20) + "/base";
770+
curl_easy_setopt(curl, CURLOPT_URL, ipv6_url.c_str());
768771
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
769772
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
770773
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
771774
res = curl_easy_perform(curl);
772-
if (res == 0) {
773-
LT_CHECK_EQ(s, "OK");
774-
}
775+
// Once the server is confirmed running, a curl failure is a
776+
// genuine test failure — not an environmental skip.
777+
LT_ASSERT_EQ(res, 0);
778+
LT_CHECK_EQ(s, "OK");
775779
curl_easy_cleanup(curl);
776780
ws.stop();
777781
}
778-
// TASK-076: deleted the duplicate tail-position tautological
779-
// assertion. The catch above now emits SKIP; the success path's
780-
// body-content `LT_CHECK_EQ` inside the curl block carries the
781-
// real signal.
782782
LT_END_AUTO_TEST(ipv6_webserver)
783783

784784
LT_BEGIN_AUTO_TEST(ws_start_stop_suite, dual_stack_webserver)
@@ -798,19 +798,21 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, dual_stack_webserver)
798798
std::string s;
799799
CURL *curl = curl_easy_init();
800800
CURLcode res;
801-
curl_easy_setopt(curl, CURLOPT_URL, "localhost:" STR(PORT + 21) "/base");
801+
// TASK-076 fix: STR(PORT + 21) stringifies to "8080 + 21" (not
802+
// "8101"), so we build the URL at runtime with std::to_string.
803+
std::string ds_url = "localhost:" + std::to_string(PORT + 21) + "/base";
804+
curl_easy_setopt(curl, CURLOPT_URL, ds_url.c_str());
802805
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
803806
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
804807
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
805808
res = curl_easy_perform(curl);
806-
if (res == 0) {
807-
LT_CHECK_EQ(s, "OK");
808-
}
809+
// Once the server is confirmed running, a curl failure is a
810+
// genuine test failure — not an environmental skip.
811+
LT_ASSERT_EQ(res, 0);
812+
LT_CHECK_EQ(s, "OK");
809813
curl_easy_cleanup(curl);
810814
ws.stop();
811815
}
812-
// TASK-076: deleted the duplicate tail-position tautological
813-
// assertion (see ipv6_webserver above).
814816
LT_END_AUTO_TEST(dual_stack_webserver)
815817

816818
LT_BEGIN_AUTO_TEST(ws_start_stop_suite, bind_address_ipv4)
@@ -840,39 +842,42 @@ LT_END_AUTO_TEST(bind_address_ipv4)
840842
// Test bind_address with IPv6 address string (covers IPv6 branch in create_webserver.cpp)
841843
LT_BEGIN_AUTO_TEST(ws_start_stop_suite, bind_address_ipv6_string)
842844
int port = PORT + 31;
843-
// This tests the IPv6 branch in bind_address
844-
// Note: This may fail if IPv6 is not available on the system
845+
// TASK-076 fix: webserver is non-movable so we use a shared_ptr to
846+
// separate construction / start (environmental SKIP) from the curl
847+
// block (unconditional assertion). Previously a single catch(...)
848+
// swallowed assert_unattended and turned a curl failure into a SKIP.
849+
std::shared_ptr<httpserver::webserver> ws_ptr;
845850
try {
846-
httpserver::webserver ws{httpserver::create_webserver(port).bind_address("::1")};
847-
auto ok = std::make_shared<ok_resource>();
848-
ws.register_path("base", ok);
849-
ws.start(false);
850-
if (ws.is_running()) {
851-
curl_global_init(CURL_GLOBAL_ALL);
852-
std::string s;
853-
CURL *curl = curl_easy_init();
854-
CURLcode res;
855-
std::string url = "http://[::1]:" + std::to_string(port) + "/base";
856-
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
857-
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
858-
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
859-
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
860-
res = curl_easy_perform(curl);
861-
if (res == 0) {
862-
LT_CHECK_EQ(s, "OK");
863-
}
864-
curl_easy_cleanup(curl);
865-
ws.stop();
866-
}
851+
ws_ptr = std::make_shared<httpserver::webserver>(
852+
httpserver::create_webserver(port).bind_address("::1"));
867853
} catch (...) {
868-
// TASK-076: IPv6 may not be available on the host; emit SKIP
869-
// rather than tautological-pass.
870-
LT_SKIP_IF(true, "IPv6 bind unavailable on this host");
854+
LT_SKIP_IF(true, "IPv6 bind: construction failed on this host");
855+
}
856+
auto ok = std::make_shared<ok_resource>();
857+
ws_ptr->register_path("base", ok);
858+
try {
859+
ws_ptr->start(false);
860+
} catch (...) {
861+
LT_SKIP_IF(true, "IPv6 bind: start failed on this host");
862+
}
863+
if (ws_ptr->is_running()) {
864+
curl_global_init(CURL_GLOBAL_ALL);
865+
std::string s;
866+
CURL *curl = curl_easy_init();
867+
CURLcode res;
868+
std::string url = "http://[::1]:" + std::to_string(port) + "/base";
869+
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
870+
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
871+
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
872+
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
873+
res = curl_easy_perform(curl);
874+
// Once the server is confirmed running, a curl failure is a
875+
// genuine test failure — not an environmental skip.
876+
LT_ASSERT_EQ(res, 0);
877+
LT_CHECK_EQ(s, "OK");
878+
curl_easy_cleanup(curl);
879+
ws_ptr->stop();
871880
}
872-
// TASK-076: deleted the tail-position tautological assertion. The
873-
// catch above now distinguishes "IPv6 absent" (SKIP) from
874-
// "implementation broken" (the catch wouldn't fire; the
875-
// inside-`is_running()` LT_CHECK_EQ on /base carries the signal).
876881
LT_END_AUTO_TEST(bind_address_ipv6_string)
877882

878883
#ifdef HAVE_GNUTLS
@@ -1504,6 +1509,41 @@ bool is_psk_unsupported_error(const std::exception& e) {
15041509
msg.find("disabled") != std::string::npos;
15051510
}
15061511

1512+
// TASK-076: pin is_psk_unsupported_error triage logic so a future
1513+
// change in MHD's error strings or in the helper predicate is caught
1514+
// before it silently masks/surfaces PSK failures.
1515+
//
1516+
// Helper: wrap a literal string as a std::exception so we can pass it
1517+
// to is_psk_unsupported_error without starting a webserver.
1518+
namespace {
1519+
struct fake_exception : public std::exception {
1520+
explicit fake_exception(const char* msg) : msg_(msg) {}
1521+
const char* what() const noexcept override { return msg_; }
1522+
const char* msg_;
1523+
};
1524+
} // namespace
1525+
1526+
// (a) "PSK not supported" → true
1527+
LT_BEGIN_AUTO_TEST(ws_start_stop_suite, is_psk_unsupported_error_psk_not_supported)
1528+
LT_CHECK_EQ(is_psk_unsupported_error(fake_exception("PSK not supported")), true);
1529+
LT_END_AUTO_TEST(is_psk_unsupported_error_psk_not_supported)
1530+
1531+
// (b) "psk disabled" → true (lowercase variant)
1532+
LT_BEGIN_AUTO_TEST(ws_start_stop_suite, is_psk_unsupported_error_psk_disabled)
1533+
LT_CHECK_EQ(is_psk_unsupported_error(fake_exception("psk disabled")), true);
1534+
LT_END_AUTO_TEST(is_psk_unsupported_error_psk_disabled)
1535+
1536+
// (c) "TLS handshake failed" → false (no PSK keyword at all)
1537+
LT_BEGIN_AUTO_TEST(ws_start_stop_suite, is_psk_unsupported_error_tls_handshake_failed)
1538+
LT_CHECK_EQ(is_psk_unsupported_error(fake_exception("TLS handshake failed")), false);
1539+
LT_END_AUTO_TEST(is_psk_unsupported_error_tls_handshake_failed)
1540+
1541+
// (d) "PSK credential error" → false (PSK present but no 'unsupported' token;
1542+
// must NOT be treated as a skip — real PSK config failures should surface)
1543+
LT_BEGIN_AUTO_TEST(ws_start_stop_suite, is_psk_unsupported_error_psk_credential_error)
1544+
LT_CHECK_EQ(is_psk_unsupported_error(fake_exception("PSK credential error")), false);
1545+
LT_END_AUTO_TEST(is_psk_unsupported_error_psk_credential_error)
1546+
15071547
// Test PSK credential handler setup
15081548
LT_BEGIN_AUTO_TEST(ws_start_stop_suite, psk_handler_setup)
15091549
int port = PORT + 28;

test/unit/littletest_skip_semantics_test.cpp

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,12 @@ LT_BEGIN_SUITE(littletest_skip_semantics_suite)
2424
LT_END_SUITE(littletest_skip_semantics_suite)
2525

2626
// Cycle 1.a — LT_SKIP increments the runner's skip counter and halts
27-
// the enclosing test body. We split the post-skip "must not run"
28-
// assertion into a sibling test because LT_SKIP terminates the test by
29-
// throwing a skip_unattended (mirroring assert_unattended). To observe
30-
// the counter delta we register a probe sibling test that runs before
31-
// the skipping test in registration order.
27+
// the enclosing test body. LT_SKIP throws skip_unattended so no code
28+
// after it can execute; the counter-delta assertion is therefore
29+
// delegated to `observe_prior_skip_was_counted` below, which reads the
30+
// cumulative skip total in a fresh test body after this one has run.
3231
LT_BEGIN_AUTO_TEST(littletest_skip_semantics_suite, skip_macro_increments_skip_counter)
33-
int before = littletest::auto_test_runner.get_skips();
3432
LT_SKIP("intentional skip — Cycle 1 sentinel");
35-
// Unreachable: LT_SKIP throws, so the line below must not execute.
36-
// If it does, the test framework would record a CHECK FAILURE here
37-
// because we'd be asserting `after == before + 1` after the
38-
// post-skip body still ran — which is precisely what the contract
39-
// forbids.
40-
int after = littletest::auto_test_runner.get_skips();
41-
LT_CHECK_EQ(after, before + 1);
42-
LT_FAIL("LT_SKIP did not halt test body");
4333
LT_END_AUTO_TEST(skip_macro_increments_skip_counter)
4434

4535
// Cycle 1.b — LT_SKIP_IF(false, ...) is a no-op; control flows through.

0 commit comments

Comments
 (0)