#include #include #include // Include mocks first #include "Arduino.h" #include "WiFi.h" #include "Preferences.h" #include "config.h" // Define all extern globals required by mock headers uint32_t g_millis_value; int g_digital_read_value; int g_wifi_status; // operation.h uses g_show_setup_qr_count under UNIT_TEST int g_show_setup_qr_count; // Include the functions under test #include "../../src/operation.h" void reset_state() { g_millis_value = 0; g_digital_read_value = HIGH; // button not pressed g_wifi_status = WL_CONNECTED; g_show_setup_qr_count = 0; } void setUp() { reset_state(); } void tearDown() {} // ── FW-14: attempt_wifi returns true when WiFi connects immediately ─────────── void test_fw14_attempt_wifi_returns_true_on_connect() { g_wifi_status = WL_CONNECTED; bool result = attempt_wifi("myssid", "mypass"); TEST_ASSERT_TRUE(result); } // ── FW-15: attempt_wifi returns false after timeout ─────────────────────────── // millis() auto-increments by 10 on each call; after enough iterations the // elapsed time exceeds WIFI_TIMEOUT_MS (30000 ms). void test_fw15_attempt_wifi_returns_false_on_timeout() { g_wifi_status = 6; // WL_DISCONNECTED — never connects, never terminal-fails g_millis_value = 0; bool result = attempt_wifi("myssid", "mypass"); TEST_ASSERT_FALSE(result); // Sanity: we actually waited the full timeout, not bailed early TEST_ASSERT_GREATER_THAN(WIFI_TIMEOUT_MS, g_millis_value); } // ── FW-15a: attempt_wifi bails fast on WL_CONNECT_FAILED (bad PSK) ──────────── // Without fast-fail the user would wait the full 30 s before the red retry // screen repaints; with it, failure surfaces in seconds. void test_fw15a_attempt_wifi_returns_false_on_auth_fail() { g_wifi_status = WL_CONNECT_FAILED; g_millis_value = 0; bool result = attempt_wifi("myssid", "wrongpass"); TEST_ASSERT_FALSE(result); TEST_ASSERT_LESS_THAN(WIFI_TIMEOUT_MS, g_millis_value); } // ── FW-15b: attempt_wifi bails fast on WL_NO_SSID_AVAIL (network not visible) ─ void test_fw15b_attempt_wifi_returns_false_on_no_ssid() { g_wifi_status = WL_NO_SSID_AVAIL; g_millis_value = 0; bool result = attempt_wifi("notthere", "anypass"); TEST_ASSERT_FALSE(result); TEST_ASSERT_LESS_THAN(WIFI_TIMEOUT_MS, g_millis_value); } // ── FW-16: loop() state-machine (WiFi-credential submission path) ───────────── // This test is deferred: loop() orchestrates DNS, WebServer, and WiFi // together in a single function, making it impractical to unit-test without // a larger integration harness. The provisioning behavior is covered at the // integration / hardware level. // Placeholder: always passes as a reminder. void test_fw16_loop_state_machine_deferred() { TEST_PASS_MESSAGE("FW-16 deferred: loop() state machine requires integration harness"); } // ── FW-17: check_reset_button returns true when button held past threshold ──── // g_digital_read_value = LOW keeps the while-loop spinning; millis() // auto-increments by 10 per call and will exceed RESET_HOLD_MS (5000 ms). void test_fw17_reset_button_held_returns_true() { g_digital_read_value = LOW; g_millis_value = 0; bool result = check_reset_button(); TEST_ASSERT_TRUE(result); } // ── FW-18: check_reset_button returns false when button not pressed ─────────── void test_fw18_reset_button_not_pressed_returns_false() { g_digital_read_value = HIGH; // button not pressed — loop exits immediately bool result = check_reset_button(); TEST_ASSERT_FALSE(result); } int main(int argc, char** argv) { UNITY_BEGIN(); RUN_TEST(test_fw14_attempt_wifi_returns_true_on_connect); RUN_TEST(test_fw15_attempt_wifi_returns_false_on_timeout); RUN_TEST(test_fw15a_attempt_wifi_returns_false_on_auth_fail); RUN_TEST(test_fw15b_attempt_wifi_returns_false_on_no_ssid); RUN_TEST(test_fw16_loop_state_machine_deferred); RUN_TEST(test_fw17_reset_button_held_returns_true); RUN_TEST(test_fw18_reset_button_not_pressed_returns_false); return UNITY_END(); }