#include #include "../memory_store.h" #include "wifi_controller.h" using namespace roro; using State = WifiController::State; using Action = WifiController::Action; struct Fixture { MemoryStore store; SavedNetworks saved{store}; WifiController wifi{saved}; Fixture() { saved.load(); saved.add("home", "password1"); saved.add("work", "password2"); } }; void setUp() {} void tearDown() {} static void assertState(State s, const WifiController& w) { TEST_ASSERT_EQUAL(static_cast(s), static_cast(w.state())); } static void assertAction(Action expected, const WifiController::Step& step) { TEST_ASSERT_EQUAL(static_cast(expected), static_cast(step.action)); } void test_disabled_wifi_stays_off() { Fixture f; assertAction(Action::None, f.wifi.update(0, false)); assertState(State::Off, f.wifi); } void test_enabling_starts_a_scan() { Fixture f; assertAction(Action::StartScan, f.wifi.update(0, true)); assertState(State::Scanning, f.wifi); } void test_scan_joins_the_strongest_saved_network() { Fixture f; f.wifi.update(0, true); auto step = f.wifi.scanDone({{"cafe", -30}, {"home", -70}, {"work", -55}, {"home", -50}}, 100); assertAction(Action::Connect, step); TEST_ASSERT_EQUAL_STRING("home", step.ssid.c_str()); // -50 beats work's -55 TEST_ASSERT_EQUAL_STRING("password1", step.password.c_str()); assertState(State::Connecting, f.wifi); } void test_no_saved_network_in_range_waits_then_rescans_with_growing_delay() { Fixture f; f.wifi.update(0, true); assertAction(Action::None, f.wifi.scanDone({{"cafe", -30}}, 1000)); assertState(State::Waiting, f.wifi); assertAction(Action::None, f.wifi.update(10999, true)); assertAction(Action::StartScan, f.wifi.update(11000, true)); // 10 s f.wifi.scanDone({}, 12000); assertAction(Action::None, f.wifi.update(41999, true)); assertAction(Action::StartScan, f.wifi.update(42000, true)); // then 30 s } void test_connected_resets_the_retry_delay() { Fixture f; f.wifi.update(0, true); f.wifi.scanDone({}, 0); f.wifi.update(10000, true); f.wifi.scanDone({{"home", -50}}, 10000); f.wifi.connected(11000); assertState(State::Connected, f.wifi); TEST_ASSERT_EQUAL_STRING("home", f.wifi.ssid().c_str()); f.wifi.disconnected(20000); assertState(State::Scanning, f.wifi); } void test_losing_the_connection_rescans_immediately() { Fixture f; f.wifi.update(0, true); f.wifi.scanDone({{"home", -50}}, 0); f.wifi.connected(100); auto step = f.wifi.disconnected(5000); assertAction(Action::StartScan, step); } void test_connect_timeout_counts_as_failure() { Fixture f; f.wifi.update(0, true); f.wifi.scanDone({{"home", -50}}, 0); assertAction(Action::None, f.wifi.update(14999, true)); assertAction(Action::Disconnect, f.wifi.update(15000, true)); assertState(State::Waiting, f.wifi); } void test_disabling_disconnects_and_turns_off() { Fixture f; f.wifi.update(0, true); f.wifi.scanDone({{"home", -50}}, 0); f.wifi.connected(100); assertAction(Action::RadioOff, f.wifi.update(200, false)); assertState(State::Off, f.wifi); } void test_monitoring_takes_over_and_reconnects_afterwards() { Fixture f; f.wifi.update(0, true); f.wifi.scanDone({{"home", -50}}, 0); f.wifi.connected(100); assertAction(Action::StartMonitor, f.wifi.requestMonitor()); assertState(State::Monitoring, f.wifi); assertAction(Action::None, f.wifi.update(60000, true)); // stays monitoring assertAction(Action::StopMonitorAndScan, f.wifi.releaseMonitor(true)); assertState(State::Scanning, f.wifi); } void test_monitoring_works_even_when_wifi_is_disabled() { Fixture f; f.wifi.update(0, false); assertAction(Action::StartMonitor, f.wifi.requestMonitor()); assertAction(Action::StopMonitorAndOff, f.wifi.releaseMonitor(false)); assertState(State::Off, f.wifi); } void test_late_scan_or_connect_results_are_ignored_while_monitoring() { Fixture f; f.wifi.update(0, true); f.wifi.requestMonitor(); assertAction(Action::None, f.wifi.scanDone({{"home", -50}}, 10)); f.wifi.connected(20); assertState(State::Monitoring, f.wifi); } void test_hidden_networks_are_tried_when_no_visible_one_is_in_range() { MemoryStore store; SavedNetworks saved(store); saved.load(); saved.add("home", "password1"); saved.add("hideA", "password2", true); saved.add("hideB", "password3", true); WifiController wifi(saved); wifi.update(0, true); auto step = wifi.scanDone({{"cafe", -30}}, 0); assertAction(Action::Connect, step); TEST_ASSERT_EQUAL_STRING("hideA", step.ssid.c_str()); wifi.disconnected(100); // failed wifi.update(10100, true); TEST_ASSERT_EQUAL_STRING("hideB", wifi.scanDone({}, 10100).ssid.c_str()); // the next one in turn } void test_visible_saved_network_beats_hidden_ones() { MemoryStore store; SavedNetworks saved(store); saved.load(); saved.add("hideA", "password2", true); saved.add("home", "password1"); WifiController wifi(saved); wifi.update(0, true); TEST_ASSERT_EQUAL_STRING("home", wifi.scanDone({{"home", -80}}, 0).ssid.c_str()); } void test_retry_now_cuts_the_wait_short() { Fixture f; f.wifi.update(0, true); f.wifi.scanDone({}, 0); f.wifi.retryNow(500); assertAction(Action::StartScan, f.wifi.update(500, true)); } void test_no_saved_networks_keeps_the_radio_off() { MemoryStore store; SavedNetworks saved(store); saved.load(); WifiController wifi(saved); assertAction(Action::None, wifi.update(0, true)); assertState(State::Off, wifi); saved.add("home", "password1"); assertAction(Action::StartScan, wifi.update(100, true)); } void test_forgetting_the_last_network_turns_the_radio_off() { Fixture f; f.wifi.update(0, true); f.wifi.scanDone({{"home", -50}}, 0); f.wifi.connected(100); f.saved.forget("home"); f.saved.forget("work"); assertAction(Action::RadioOff, f.wifi.update(200, true)); } int main() { UNITY_BEGIN(); RUN_TEST(test_disabled_wifi_stays_off); RUN_TEST(test_enabling_starts_a_scan); RUN_TEST(test_scan_joins_the_strongest_saved_network); RUN_TEST(test_no_saved_network_in_range_waits_then_rescans_with_growing_delay); RUN_TEST(test_connected_resets_the_retry_delay); RUN_TEST(test_losing_the_connection_rescans_immediately); RUN_TEST(test_connect_timeout_counts_as_failure); RUN_TEST(test_disabling_disconnects_and_turns_off); RUN_TEST(test_monitoring_takes_over_and_reconnects_afterwards); RUN_TEST(test_monitoring_works_even_when_wifi_is_disabled); RUN_TEST(test_late_scan_or_connect_results_are_ignored_while_monitoring); RUN_TEST(test_hidden_networks_are_tried_when_no_visible_one_is_in_range); RUN_TEST(test_visible_saved_network_beats_hidden_ones); RUN_TEST(test_retry_now_cuts_the_wait_short); RUN_TEST(test_no_saved_networks_keeps_the_radio_off); RUN_TEST(test_forgetting_the_last_network_turns_the_radio_off); return UNITY_END(); }