test(firmware): broaden native-test coverage + gcov instrumentation
Extract the pre-first-image retry from normal_operation() into a templated bootstrap_loop() helper in operation.h so the loop body becomes unit-testable. Add four tests against it: two that verify the X-Panel-Id header is sent on every poll and matches the compile-time PANEL_ID (a silent server-side mis-routing risk if dropped), and two that exercise the loop's exit-on-deep-sleep vs. iterate-while-204 behaviour. Wire --coverage into env:native-test (compile + link via a post-script) so `gcovr -r . --filter src/` produces a real number, and ignore the stray *.gcov files gcovr drops at the repo root. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -534,6 +534,80 @@ void test_fw17b_missing_sha256_header_skips_verification() {
|
||||
TEST_ASSERT_EQUAL(0, prefs.getInt(NVS_KEY_ERR_BORDER, -1));
|
||||
}
|
||||
|
||||
// FW-PANEL-A: X-Panel-Id header sent on every poll. V2 (13.3") shipped with
|
||||
// this header but no test covered it; if a future refactor accidentally
|
||||
// drops the addHeader call, the server's DeviceModel routing breaks silently
|
||||
// (a 13.3" frame would be served 7.3" images cropped to 800x480).
|
||||
void test_fw_panel_id_header_sent_on_every_poll() {
|
||||
g_http_response_headers["X-Image-Id"] = "1";
|
||||
normal_operation_impl(String("mac"), http, String("url"), prefs);
|
||||
TEST_ASSERT_TRUE_MESSAGE(
|
||||
g_http_request_headers.find("X-Panel-Id") != g_http_request_headers.end(),
|
||||
"X-Panel-Id header must be present on every poll");
|
||||
}
|
||||
|
||||
// FW-PANEL-B: Header value matches the compile-time PANEL_ID. In the native-
|
||||
// test build PANEL_ID is "unknown" (no env -D flag) — that's the fallback
|
||||
// path; the real production envs set the panel-specific id via build_flags.
|
||||
void test_fw_panel_id_header_value_matches_macro() {
|
||||
g_http_response_headers["X-Image-Id"] = "1";
|
||||
normal_operation_impl(String("mac"), http, String("url"), prefs);
|
||||
TEST_ASSERT_EQUAL_STRING(PANEL_ID, g_http_request_headers["X-Panel-Id"].c_str());
|
||||
}
|
||||
|
||||
// FW-BOOT-LOOP-A: bootstrap_loop exits once the wrapped poll deep-sleeps
|
||||
// (i.e. an image has arrived and impl entered esp_deep_sleep_start). In
|
||||
// production esp_deep_sleep_start never returns; the test mock just sets
|
||||
// g_deep_sleep_started and returns. Without the in-test break the loop
|
||||
// would spin forever.
|
||||
void test_fw_bootstrap_loop_exits_after_deep_sleep() {
|
||||
g_http_response_headers["X-Image-Id"] = "1";
|
||||
g_http_response_headers["X-Interval-Ms"] = "300000";
|
||||
g_http_body = "BINDATA";
|
||||
|
||||
int iterations = 0;
|
||||
bootstrap_loop([&]() {
|
||||
iterations++;
|
||||
normal_operation_impl(String("mac"), http, String("url"), prefs);
|
||||
});
|
||||
|
||||
// One poll succeeded → deep_sleep flagged → loop returned. If the loop
|
||||
// didn't honor the flag we'd never reach this assertion (test timeout).
|
||||
TEST_ASSERT_EQUAL(1, iterations);
|
||||
TEST_ASSERT_TRUE(g_deep_sleep_started);
|
||||
}
|
||||
|
||||
// FW-BOOT-LOOP-B: pre-image-arrived path — impl returns WITHOUT deep_sleep,
|
||||
// so the loop would iterate in production. In the test we drive the
|
||||
// iteration count via a side channel: the callable bumps a counter and
|
||||
// flips the response code mid-run, exercising the retry behaviour without
|
||||
// trapping the test forever.
|
||||
void test_fw_bootstrap_loop_iterates_when_no_image() {
|
||||
g_http_get_code = 204; // no image available yet
|
||||
// No img_id in NVS, so impl returns without arming deep sleep.
|
||||
|
||||
int iterations = 0;
|
||||
bootstrap_loop([&]() {
|
||||
iterations++;
|
||||
normal_operation_impl(String("mac"), http, String("url"), prefs);
|
||||
// After the third no-image poll, simulate the image arriving so the
|
||||
// loop can exit cleanly. In production the loop runs until the
|
||||
// server flips to 200 — we just compress the timeline.
|
||||
if (iterations == 3) {
|
||||
g_http_get_code = 200;
|
||||
g_http_response_headers["X-Image-Id"] = "7";
|
||||
g_http_response_headers["X-Interval-Ms"] = "300000";
|
||||
g_http_body = "BINDATA";
|
||||
}
|
||||
});
|
||||
|
||||
// Three 204 spins + one 200 spin = 4 total iterations before the loop
|
||||
// saw the deep-sleep flag and broke out. Locks in that the loop does
|
||||
// NOT exit on a no-image return (would freeze the device on the QR).
|
||||
TEST_ASSERT_EQUAL(4, iterations);
|
||||
TEST_ASSERT_TRUE(g_deep_sleep_started);
|
||||
}
|
||||
|
||||
// FW-12/13: AP SSID derivation via ap_ssid_from_mac()
|
||||
void test_fw12_ap_ssid_from_mac_aabbcc() {
|
||||
String ssid = ap_ssid_from_mac(String("AA:BB:CC:DD:EE:FF"));
|
||||
@@ -583,5 +657,9 @@ int main(int argc, char** argv) {
|
||||
RUN_TEST(test_fw16_304_with_draw_needed_redraws);
|
||||
RUN_TEST(test_fw17a_sha256_mismatch_skips_draw_and_keeps_old_img_id);
|
||||
RUN_TEST(test_fw17b_missing_sha256_header_skips_verification);
|
||||
RUN_TEST(test_fw_panel_id_header_sent_on_every_poll);
|
||||
RUN_TEST(test_fw_panel_id_header_value_matches_macro);
|
||||
RUN_TEST(test_fw_bootstrap_loop_exits_after_deep_sleep);
|
||||
RUN_TEST(test_fw_bootstrap_loop_iterates_when_no_image);
|
||||
return UNITY_END();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user