#include #include "unity.h" #include "net/nl_wifi_sm.h" static nl_wifi_sm_t sm; static nl_wifi_actions_t a; void setUp(void) { memset(&sm, 0, sizeof sm); } void tearDown(void) {} static void test_start_without_credentials_opens_ap(void) { a = nl_wifi_sm_start(&sm, false, false, false, 0); TEST_ASSERT_TRUE(a.start_ap); TEST_ASSERT_TRUE(a.pulse_on); TEST_ASSERT_FALSE(a.connect); TEST_ASSERT_EQUAL(NL_WIFI_ST_AP_ONBOARDING, sm.state); TEST_ASSERT_TRUE(nl_wifi_sm_open_access(&sm)); TEST_ASSERT_EQUAL_STRING("ap", nl_wifi_sm_mode(&sm)); } static void test_start_with_credentials_connects(void) { a = nl_wifi_sm_start(&sm, true, false, false, 0); TEST_ASSERT_TRUE(a.connect); TEST_ASSERT_FALSE(a.use_test_creds); TEST_ASSERT_FALSE(a.start_ap); TEST_ASSERT_FALSE(nl_wifi_sm_open_access(&sm)); a = nl_wifi_sm_event(&sm, NL_WIFI_EV_CONNECTED, NL_WIFI_ERR_NONE, 3000); TEST_ASSERT_EQUAL(NL_WIFI_ST_STA_CONNECTED, sm.state); TEST_ASSERT_EQUAL_STRING("sta", nl_wifi_sm_mode(&sm)); } static void test_retry_with_backoff(void) { nl_wifi_sm_start(&sm, true, false, false, 0); nl_wifi_sm_event(&sm, NL_WIFI_EV_JOIN_FAILED, NL_WIFI_ERR_AUTH, 1000); TEST_ASSERT_FALSE(nl_wifi_sm_tick(&sm, 1999).connect); TEST_ASSERT_TRUE(nl_wifi_sm_tick(&sm, 2000).connect); nl_wifi_sm_event(&sm, NL_WIFI_EV_JOIN_FAILED, NL_WIFI_ERR_AUTH, 3000); TEST_ASSERT_FALSE(nl_wifi_sm_tick(&sm, 4999).connect); TEST_ASSERT_TRUE(nl_wifi_sm_tick(&sm, 5000).connect); /* 2 s */ nl_wifi_sm_event(&sm, NL_WIFI_EV_JOIN_FAILED, NL_WIFI_ERR_AUTH, 6000); TEST_ASSERT_TRUE(nl_wifi_sm_tick(&sm, 10000).connect); /* 4 s */ for (int i = 0; i < 10; i++) nl_wifi_sm_event(&sm, NL_WIFI_EV_JOIN_FAILED, NL_WIFI_ERR_AUTH, 20000); TEST_ASSERT_FALSE(nl_wifi_sm_tick(&sm, 79999).connect); TEST_ASSERT_TRUE(nl_wifi_sm_tick(&sm, 80000).connect); /* capped at 60 s */ } static void test_fallback_after_five_minutes(void) { nl_wifi_sm_start(&sm, true, false, false, 0); nl_wifi_sm_event(&sm, NL_WIFI_EV_JOIN_FAILED, NL_WIFI_ERR_NOT_FOUND, 1000); a = nl_wifi_sm_tick(&sm, NL_WIFI_FALLBACK_MS - 1); TEST_ASSERT_FALSE(a.start_ap); a = nl_wifi_sm_tick(&sm, NL_WIFI_FALLBACK_MS); TEST_ASSERT_TRUE(a.start_ap); TEST_ASSERT_FALSE(a.pulse_on); TEST_ASSERT_EQUAL(NL_WIFI_ST_APSTA_FALLBACK, sm.state); TEST_ASSERT_EQUAL_STRING("apsta", nl_wifi_sm_mode(&sm)); TEST_ASSERT_FALSE(nl_wifi_sm_open_access(&sm)); /* retries continue */ nl_wifi_sm_event(&sm, NL_WIFI_EV_JOIN_FAILED, NL_WIFI_ERR_NOT_FOUND, 400000); TEST_ASSERT_TRUE(nl_wifi_sm_tick(&sm, 460000).connect); /* reconnect closes the AP 10 s later */ a = nl_wifi_sm_event(&sm, NL_WIFI_EV_CONNECTED, NL_WIFI_ERR_NONE, 500000); TEST_ASSERT_FALSE(a.stop_ap); TEST_ASSERT_EQUAL(NL_WIFI_ST_STA_CONNECTED, sm.state); TEST_ASSERT_FALSE(nl_wifi_sm_tick(&sm, 509999).stop_ap); TEST_ASSERT_TRUE(nl_wifi_sm_tick(&sm, 510000).stop_ap); TEST_ASSERT_EQUAL_STRING("sta", nl_wifi_sm_mode(&sm)); } static void test_disconnect_while_connected(void) { nl_wifi_sm_start(&sm, true, false, false, 0); nl_wifi_sm_event(&sm, NL_WIFI_EV_CONNECTED, NL_WIFI_ERR_NONE, 1000); nl_wifi_sm_event(&sm, NL_WIFI_EV_DISCONNECTED, NL_WIFI_ERR_NONE, 100000); TEST_ASSERT_EQUAL(NL_WIFI_ST_STA_CONNECTING, sm.state); TEST_ASSERT_TRUE(nl_wifi_sm_tick(&sm, 101000).connect); /* fallback timer restarts at the disconnect */ TEST_ASSERT_FALSE(nl_wifi_sm_tick(&sm, 100000 + NL_WIFI_FALLBACK_MS - 1).start_ap); TEST_ASSERT_TRUE(nl_wifi_sm_tick(&sm, 100000 + NL_WIFI_FALLBACK_MS).start_ap); } static void test_onboarding_success(void) { nl_wifi_sm_start(&sm, false, false, false, 0); a = nl_wifi_sm_onboard(&sm, 5000); TEST_ASSERT_FALSE(a.start_ap); /* already up */ TEST_ASSERT_TRUE(a.connect); TEST_ASSERT_TRUE(a.use_test_creds); TEST_ASSERT_EQUAL(NL_WIFI_ST_ONBOARD_TEST, sm.state); TEST_ASSERT_TRUE(nl_wifi_sm_open_access(&sm)); TEST_ASSERT_EQUAL_STRING("apsta", nl_wifi_sm_mode(&sm)); TEST_ASSERT_FALSE(nl_wifi_sm_event(&sm, NL_WIFI_EV_DISCONNECTED, NL_WIFI_ERR_NONE, 6000).save_test_creds); a = nl_wifi_sm_event(&sm, NL_WIFI_EV_CONNECTED, NL_WIFI_ERR_NONE, 9000); TEST_ASSERT_TRUE(a.save_test_creds); TEST_ASSERT_TRUE(a.pulse_off); TEST_ASSERT_EQUAL(NL_WIFI_ST_STA_CONNECTED, sm.state); TEST_ASSERT_FALSE(nl_wifi_sm_open_access(&sm)); TEST_ASSERT_FALSE(nl_wifi_sm_tick(&sm, 9000 + NL_WIFI_AP_CLOSE_ONBOARD_MS - 1).stop_ap); TEST_ASSERT_TRUE(nl_wifi_sm_tick(&sm, 9000 + NL_WIFI_AP_CLOSE_ONBOARD_MS).stop_ap); } static void test_onboarding_failure_without_credentials(void) { nl_wifi_sm_start(&sm, false, false, false, 0); nl_wifi_sm_onboard(&sm, 5000); a = nl_wifi_sm_event(&sm, NL_WIFI_EV_JOIN_FAILED, NL_WIFI_ERR_AUTH, 8000); TEST_ASSERT_FALSE(a.pulse_off); TEST_ASSERT_FALSE(a.stop_ap); TEST_ASSERT_FALSE(a.connect); TEST_ASSERT_TRUE(a.disconnect); /* I1: stray station attempt must not linger */ TEST_ASSERT_EQUAL(NL_WIFI_ST_AP_ONBOARDING, sm.state); TEST_ASSERT_EQUAL(NL_WIFI_ERR_AUTH, sm.error); TEST_ASSERT_EQUAL_STRING("auth", nl_wifi_error_name(sm.error)); TEST_ASSERT_TRUE(nl_wifi_sm_open_access(&sm)); } static void test_onboarding_timeout(void) { nl_wifi_sm_start(&sm, false, false, false, 0); nl_wifi_sm_onboard(&sm, 5000); TEST_ASSERT_EQUAL(NL_WIFI_ST_ONBOARD_TEST, sm.state); nl_wifi_sm_tick(&sm, 5000 + NL_WIFI_ONBOARD_TIMEOUT_MS - 1); TEST_ASSERT_EQUAL(NL_WIFI_ST_ONBOARD_TEST, sm.state); a = nl_wifi_sm_tick(&sm, 5000 + NL_WIFI_ONBOARD_TIMEOUT_MS); TEST_ASSERT_EQUAL(NL_WIFI_ST_AP_ONBOARDING, sm.state); TEST_ASSERT_EQUAL_STRING("timeout", nl_wifi_error_name(sm.error)); TEST_ASSERT_TRUE(a.disconnect); /* I1: timed-out onboarding test leaves no station attempt running */ } static void test_onboarding_connected_event_while_ap_onboarding_is_stray(void) { /* I1: fail_test() returns to AP_ONBOARDING; a late CONNECTED for the timed-out * attempt must be treated as stray and disconnected, not accepted. */ nl_wifi_sm_start(&sm, false, false, false, 0); nl_wifi_sm_onboard(&sm, 5000); nl_wifi_sm_tick(&sm, 5000 + NL_WIFI_ONBOARD_TIMEOUT_MS); /* -> AP_ONBOARDING */ a = nl_wifi_sm_event(&sm, NL_WIFI_EV_CONNECTED, NL_WIFI_ERR_NONE, 40000); TEST_ASSERT_TRUE(a.disconnect); TEST_ASSERT_EQUAL(NL_WIFI_ST_AP_ONBOARDING, sm.state); } static void test_onboarding_while_connected_falls_back_to_old_credentials(void) { nl_wifi_sm_start(&sm, true, false, false, 0); nl_wifi_sm_event(&sm, NL_WIFI_EV_CONNECTED, NL_WIFI_ERR_NONE, 1000); a = nl_wifi_sm_onboard(&sm, 2000); TEST_ASSERT_TRUE(a.start_ap); TEST_ASSERT_FALSE(a.pulse_on); TEST_ASSERT_TRUE(a.use_test_creds); TEST_ASSERT_FALSE(nl_wifi_sm_open_access(&sm)); a = nl_wifi_sm_event(&sm, NL_WIFI_EV_JOIN_FAILED, NL_WIFI_ERR_NOT_FOUND, 4000); TEST_ASSERT_TRUE(a.connect); TEST_ASSERT_FALSE(a.use_test_creds); TEST_ASSERT_EQUAL(NL_WIFI_ST_APSTA_FALLBACK, sm.state); TEST_ASSERT_EQUAL_STRING("not_found", nl_wifi_error_name(sm.error)); a = nl_wifi_sm_event(&sm, NL_WIFI_EV_CONNECTED, NL_WIFI_ERR_NONE, 6000); TEST_ASSERT_FALSE(a.save_test_creds); TEST_ASSERT_TRUE(nl_wifi_sm_tick(&sm, 16000).stop_ap); } static void test_onboard_while_test_running_disconnects_stale_attempt(void) { /* M1: a repeated onboarding request while a test connect is already in flight * must disconnect first so stale events from the previous attempt don't count. */ nl_wifi_sm_start(&sm, false, false, false, 0); nl_wifi_sm_onboard(&sm, 5000); TEST_ASSERT_EQUAL(NL_WIFI_ST_ONBOARD_TEST, sm.state); a = nl_wifi_sm_onboard(&sm, 6000); TEST_ASSERT_TRUE(a.disconnect); TEST_ASSERT_TRUE(a.connect); TEST_ASSERT_TRUE(a.use_test_creds); TEST_ASSERT_EQUAL(NL_WIFI_ST_ONBOARD_TEST, sm.state); } static void test_error_names(void) { TEST_ASSERT_NULL(nl_wifi_error_name(NL_WIFI_ERR_NONE)); TEST_ASSERT_EQUAL_STRING("not_found", nl_wifi_error_name(NL_WIFI_ERR_NOT_FOUND)); } static void test_connect_without_any_event_retries(void) { nl_wifi_sm_start(&sm, true, false, false, 0); TEST_ASSERT_FALSE(nl_wifi_sm_tick(&sm, 29999).connect); TEST_ASSERT_FALSE(nl_wifi_sm_tick(&sm, 30000).connect); /* retry scheduled at 31000 */ TEST_ASSERT_TRUE(nl_wifi_sm_tick(&sm, 31000).connect); /* next timeout: 31000 + 30000 + 2000 backoff = 63000 */ TEST_ASSERT_FALSE(nl_wifi_sm_tick(&sm, 62999).connect); TEST_ASSERT_TRUE(nl_wifi_sm_tick(&sm, 63000).connect); /* fallback after 300000 ms, and attempts continue */ a = nl_wifi_sm_tick(&sm, 300000); TEST_ASSERT_TRUE(a.start_ap); TEST_ASSERT_EQUAL(NL_WIFI_ST_APSTA_FALLBACK, sm.state); TEST_ASSERT_EQUAL_STRING("apsta", nl_wifi_sm_mode(&sm)); /* another attempt fires later */ a = nl_wifi_sm_tick(&sm, 360000); TEST_ASSERT_TRUE(a.connect); } static void test_standalone_opens_ap_without_connecting(void) { a = nl_wifi_sm_start(&sm, false, true, false, 0); TEST_ASSERT_TRUE(a.start_ap); TEST_ASSERT_FALSE(a.connect); TEST_ASSERT_EQUAL(NL_WIFI_ST_STANDALONE, sm.state); TEST_ASSERT_EQUAL_STRING("ap", nl_wifi_sm_mode(&sm)); } /* Der Standalone-AP ist KEIN offener Onboarding-AP. Haengt diese Zusicherung nicht, * koennte jeder in Funkreichweite die Lampe ohne Token flashen oder zuruecksetzen. */ static void test_standalone_is_not_open_access(void) { nl_wifi_sm_start(&sm, false, true, false, 0); TEST_ASSERT_FALSE(nl_wifi_sm_open_access(&sm)); } /* Das Fenster, das der Ruhezustand nicht abdeckt: Onboarding-Test aus dem Standalone-Modus. * Dann ist state == ONBOARD_TEST und have_creds == false - ohne !standalone in open_access() * fiele der Tokenschutz bis zu 30 s lang, waehrend der passwortgeschuetzte AP oben steht. */ static void test_standalone_onboard_test_is_not_open_access(void) { nl_wifi_sm_start(&sm, false, true, false, 0); nl_wifi_sm_onboard(&sm, 1000); TEST_ASSERT_EQUAL(NL_WIFI_ST_ONBOARD_TEST, sm.state); TEST_ASSERT_FALSE(sm.have_creds); TEST_ASSERT_FALSE(nl_wifi_sm_open_access(&sm)); } /* Gespeicherte WLAN-Daten aendern nichts: wer standalone gewaehlt hat, will keinen * Verbindungsversuch, auch wenn von frueher noch Zugangsdaten herumliegen. */ static void test_standalone_wins_over_stored_credentials(void) { a = nl_wifi_sm_start(&sm, true, true, false, 0); TEST_ASSERT_FALSE(a.connect); TEST_ASSERT_EQUAL(NL_WIFI_ST_STANDALONE, sm.state); } /* Kein Rueckfall und kein Wiederholungsversuch: die Maschine darf im Standalone-Modus * nie von selbst anfangen, ein WLAN zu suchen. */ static void test_standalone_never_falls_back(void) { nl_wifi_sm_start(&sm, true, true, false, 0); a = nl_wifi_sm_tick(&sm, NL_WIFI_FALLBACK_MS + 60000); TEST_ASSERT_FALSE(a.connect); TEST_ASSERT_FALSE(a.start_ap); TEST_ASSERT_FALSE(a.stop_ap); TEST_ASSERT_EQUAL(NL_WIFI_ST_STANDALONE, sm.state); } /* Ein verirrter Erfolg eines alten Versuchs darf die Lampe nicht aus ihrem AP holen. */ static void test_standalone_drops_a_stray_connect(void) { nl_wifi_sm_start(&sm, true, true, false, 0); a = nl_wifi_sm_event(&sm, NL_WIFI_EV_CONNECTED, NL_WIFI_ERR_NONE, 1000); TEST_ASSERT_TRUE(a.disconnect); TEST_ASSERT_EQUAL(NL_WIFI_ST_STANDALONE, sm.state); } /* Der Puls ist zeitlich begrenzt, und das ist keine Kosmetik: nl_engine_pulse() legt sich * ueber die normale Ausgabe und uebermalt auch sACN. Am 2026-10-01 am EVK beobachtet - * ein gueltiger sACN-Strom kam an, waehrend die Lampe weiter den Puls zeigte. Ein * dauerhafter Puls machte sACN im Standalone-Modus unbrauchbar. */ static void test_standalone_pulse_stops_by_itself(void) { a = nl_wifi_sm_start(&sm, false, true, true, 0); TEST_ASSERT_TRUE(a.pulse_on); TEST_ASSERT_FALSE(nl_wifi_sm_tick(&sm, NL_WIFI_STANDALONE_PULSE_MS - 1).pulse_off); TEST_ASSERT_TRUE(nl_wifi_sm_tick(&sm, NL_WIFI_STANDALONE_PULSE_MS).pulse_off); /* und danach nicht noch einmal */ TEST_ASSERT_FALSE(nl_wifi_sm_tick(&sm, NL_WIFI_STANDALONE_PULSE_MS + 5000).pulse_off); } static void test_standalone_without_pulse_stays_dark(void) { a = nl_wifi_sm_start(&sm, false, true, false, 0); TEST_ASSERT_FALSE(a.pulse_on); TEST_ASSERT_FALSE(nl_wifi_sm_tick(&sm, NL_WIFI_STANDALONE_PULSE_MS).pulse_off); } /* Erfolg: die Lampe wechselt ins WLAN UND verlaesst den Standalone-Modus dauerhaft. * Ohne clear_standalone staende sie nach dem naechsten Neustart wieder im eigenen AP. */ static void test_onboarding_from_standalone_clears_the_mode(void) { nl_wifi_sm_start(&sm, false, true, false, 0); nl_wifi_sm_onboard(&sm, 1000); a = nl_wifi_sm_event(&sm, NL_WIFI_EV_CONNECTED, NL_WIFI_ERR_NONE, 2000); TEST_ASSERT_TRUE(a.save_test_creds); TEST_ASSERT_TRUE(a.clear_standalone); TEST_ASSERT_FALSE(sm.standalone); TEST_ASSERT_EQUAL(NL_WIFI_ST_STA_CONNECTED, sm.state); } /* Fehlschlag: zurueck in den EIGENEN AP, nicht in den offenen Onboarding-AP. Sonst * verloere ein Tippfehler im WLAN-Passwort den vom Nutzer gesetzten AP-Namen samt * Passwort und entsicherte die Lampe, weil open_access() dann wieder true waere. */ static void test_failed_onboarding_returns_to_standalone(void) { nl_wifi_sm_start(&sm, false, true, false, 0); nl_wifi_sm_onboard(&sm, 1000); a = nl_wifi_sm_event(&sm, NL_WIFI_EV_JOIN_FAILED, NL_WIFI_ERR_AUTH, 2000); TEST_ASSERT_EQUAL(NL_WIFI_ST_STANDALONE, sm.state); TEST_ASSERT_TRUE(sm.standalone); TEST_ASSERT_FALSE(nl_wifi_sm_open_access(&sm)); TEST_ASSERT_FALSE(a.clear_standalone); TEST_ASSERT_FALSE(a.connect); TEST_ASSERT_TRUE(a.disconnect); TEST_ASSERT_TRUE(a.start_ap); TEST_ASSERT_EQUAL(NL_WIFI_ERR_AUTH, sm.error); } /* Auch der Zeitablauf des Tests fuehrt zurueck in den eigenen AP. */ static void test_onboarding_timeout_returns_to_standalone(void) { nl_wifi_sm_start(&sm, false, true, false, 0); nl_wifi_sm_onboard(&sm, 1000); a = nl_wifi_sm_tick(&sm, 1000 + NL_WIFI_ONBOARD_TIMEOUT_MS); TEST_ASSERT_EQUAL(NL_WIFI_ST_STANDALONE, sm.state); TEST_ASSERT_EQUAL(NL_WIFI_ERR_TIMEOUT, sm.error); TEST_ASSERT_FALSE(a.connect); TEST_ASSERT_TRUE(a.disconnect); TEST_ASSERT_TRUE(a.start_ap); TEST_ASSERT_FALSE(nl_wifi_sm_open_access(&sm)); } /* Der bisherige Weg bleibt unberuehrt: ohne Standalone und ohne gespeicherte Daten * landet ein gescheiterter Test weiterhin im offenen Onboarding-AP. */ static void test_failed_onboarding_without_standalone_opens_ap(void) { nl_wifi_sm_start(&sm, false, false, false, 0); nl_wifi_sm_onboard(&sm, 1000); nl_wifi_sm_event(&sm, NL_WIFI_EV_JOIN_FAILED, NL_WIFI_ERR_AUTH, 2000); TEST_ASSERT_EQUAL(NL_WIFI_ST_AP_ONBOARDING, sm.state); TEST_ASSERT_TRUE(nl_wifi_sm_open_access(&sm)); } /* Aus dem laufenden Stationsbetrieb in den eigenen AP. */ static void test_switching_to_standalone_at_runtime(void) { nl_wifi_sm_start(&sm, true, false, false, 0); nl_wifi_sm_event(&sm, NL_WIFI_EV_CONNECTED, NL_WIFI_ERR_NONE, 1000); a = nl_wifi_sm_standalone(&sm, true, 2000); TEST_ASSERT_TRUE(a.disconnect); TEST_ASSERT_TRUE(a.start_ap); TEST_ASSERT_TRUE(a.pulse_on); TEST_ASSERT_EQUAL(NL_WIFI_ST_STANDALONE, sm.state); TEST_ASSERT_TRUE(sm.standalone); TEST_ASSERT_TRUE(nl_wifi_sm_tick(&sm, 2000 + NL_WIFI_STANDALONE_PULSE_MS).pulse_off); } /* Zuruecknehmen MIT gespeicherten Zugangsdaten: ab ins WLAN, nicht in den offenen AP. */ static void test_leaving_standalone_with_credentials_connects(void) { nl_wifi_sm_start(&sm, true, true, false, 0); a = nl_wifi_sm_leave_standalone(&sm, true, 1000); TEST_ASSERT_TRUE(a.connect); TEST_ASSERT_FALSE(a.start_ap); TEST_ASSERT_FALSE(nl_wifi_sm_open_access(&sm)); TEST_ASSERT_FALSE(sm.standalone); TEST_ASSERT_EQUAL(NL_WIFI_ST_STA_CONNECTING, sm.state); } /* Zuruecknehmen OHNE Zugangsdaten: offener Onboarding-AP, damit man wieder herankommt. */ static void test_leaving_standalone_without_credentials_opens_onboarding(void) { nl_wifi_sm_start(&sm, false, true, false, 0); a = nl_wifi_sm_leave_standalone(&sm, false, 1000); TEST_ASSERT_TRUE(a.start_ap); TEST_ASSERT_TRUE(a.pulse_on); TEST_ASSERT_EQUAL(NL_WIFI_ST_AP_ONBOARDING, sm.state); TEST_ASSERT_TRUE(nl_wifi_sm_open_access(&sm)); } /* Laeuft beim Start eines Onboarding-Tests noch der Startpuls, raeumt onboard() ihn ab: * pulse_off sofort, danach kein verwaister pulse_off mehr -- weder nach einem * gescheiterten noch nach einem gelungenen Test. */ static void test_onboard_cancels_running_start_pulse(void) { nl_wifi_sm_start(&sm, false, true, true, 0); a = nl_wifi_sm_onboard(&sm, 1000); TEST_ASSERT_TRUE(a.pulse_off); TEST_ASSERT_FALSE(sm.pulse_armed); TEST_ASSERT_FALSE(sm.pulsing); TEST_ASSERT_FALSE(nl_wifi_sm_tick(&sm, NL_WIFI_STANDALONE_PULSE_MS).pulse_off); a = nl_wifi_sm_event(&sm, NL_WIFI_EV_CONNECTED, NL_WIFI_ERR_NONE, 6000); TEST_ASSERT_FALSE(a.pulse_off); TEST_ASSERT_FALSE(nl_wifi_sm_tick(&sm, 20000).pulse_off); } /* Der eigentliche Spec-Fall: gespeicherte Zugangsdaten UND Standalone, der Test scheitert. * Die Lampe darf nicht in den Rueckfall (APSTA_FALLBACK) mit den alten Daten gehen und * damit ihren eigenen AP verlassen, sondern bleibt im Standalone-AP. */ static void test_failed_onboarding_with_credentials_stays_standalone(void) { nl_wifi_sm_start(&sm, true, true, false, 0); nl_wifi_sm_onboard(&sm, 1000); a = nl_wifi_sm_event(&sm, NL_WIFI_EV_JOIN_FAILED, NL_WIFI_ERR_AUTH, 2000); TEST_ASSERT_EQUAL(NL_WIFI_ST_STANDALONE, sm.state); TEST_ASSERT_FALSE(a.connect); TEST_ASSERT_TRUE(a.disconnect); TEST_ASSERT_TRUE(a.start_ap); TEST_ASSERT_FALSE(nl_wifi_sm_open_access(&sm)); } static void test_onboarding_timeout_with_credentials_stays_standalone(void) { nl_wifi_sm_start(&sm, true, true, false, 0); nl_wifi_sm_onboard(&sm, 1000); a = nl_wifi_sm_tick(&sm, 1000 + NL_WIFI_ONBOARD_TIMEOUT_MS); TEST_ASSERT_EQUAL(NL_WIFI_ST_STANDALONE, sm.state); TEST_ASSERT_FALSE(a.connect); TEST_ASSERT_TRUE(a.disconnect); TEST_ASSERT_FALSE(nl_wifi_sm_open_access(&sm)); } /* Nach gelungenem Onboarding laeuft noch der AP-Stopptimer. Schaltet man jetzt auf * Standalone, muss er weg sein, sonst schaltet tick() den AP ab und die Lampe ist * ueber nichts mehr erreichbar. */ static void test_standalone_cancels_pending_ap_stop(void) { nl_wifi_sm_start(&sm, false, false, false, 0); nl_wifi_sm_onboard(&sm, 1000); nl_wifi_sm_event(&sm, NL_WIFI_EV_CONNECTED, NL_WIFI_ERR_NONE, 2000); nl_wifi_sm_standalone(&sm, false, 3000); a = nl_wifi_sm_tick(&sm, 2000 + NL_WIFI_AP_CLOSE_ONBOARD_MS); TEST_ASSERT_FALSE(a.stop_ap); TEST_ASSERT_TRUE(sm.ap_up); } /* Ein unbefristeter Puls aus dem Onboarding-AP muss beim Wechsel ohne Puls enden, denn * im Standalone-Modus gibt es keinen Timer mehr, der ihn beendete. */ static void test_standalone_without_pulse_ends_running_pulse(void) { nl_wifi_sm_start(&sm, false, false, false, 0); TEST_ASSERT_TRUE(sm.pulsing); a = nl_wifi_sm_standalone(&sm, false, 1000); TEST_ASSERT_TRUE(a.pulse_off); TEST_ASSERT_FALSE(a.pulse_on); TEST_ASSERT_FALSE(sm.pulsing); TEST_ASSERT_FALSE(nl_wifi_sm_tick(&sm, 1000 + NL_WIFI_STANDALONE_PULSE_MS).pulse_off); } /* onboard() aus dem Onboarding-AP laesst den unbefristeten Puls in Ruhe. */ static void test_onboard_from_ap_onboarding_keeps_pulse(void) { nl_wifi_sm_start(&sm, false, false, false, 0); a = nl_wifi_sm_onboard(&sm, 1000); TEST_ASSERT_FALSE(a.pulse_off); TEST_ASSERT_TRUE(sm.pulsing); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_start_without_credentials_opens_ap); RUN_TEST(test_start_with_credentials_connects); RUN_TEST(test_retry_with_backoff); RUN_TEST(test_fallback_after_five_minutes); RUN_TEST(test_disconnect_while_connected); RUN_TEST(test_onboarding_success); RUN_TEST(test_onboarding_failure_without_credentials); RUN_TEST(test_onboarding_timeout); RUN_TEST(test_onboarding_connected_event_while_ap_onboarding_is_stray); RUN_TEST(test_onboarding_while_connected_falls_back_to_old_credentials); RUN_TEST(test_onboard_while_test_running_disconnects_stale_attempt); RUN_TEST(test_error_names); RUN_TEST(test_connect_without_any_event_retries); RUN_TEST(test_standalone_opens_ap_without_connecting); RUN_TEST(test_standalone_is_not_open_access); RUN_TEST(test_standalone_onboard_test_is_not_open_access); RUN_TEST(test_standalone_wins_over_stored_credentials); RUN_TEST(test_standalone_never_falls_back); RUN_TEST(test_standalone_drops_a_stray_connect); RUN_TEST(test_standalone_pulse_stops_by_itself); RUN_TEST(test_standalone_without_pulse_stays_dark); RUN_TEST(test_onboarding_from_standalone_clears_the_mode); RUN_TEST(test_failed_onboarding_returns_to_standalone); RUN_TEST(test_onboarding_timeout_returns_to_standalone); RUN_TEST(test_failed_onboarding_without_standalone_opens_ap); RUN_TEST(test_switching_to_standalone_at_runtime); RUN_TEST(test_leaving_standalone_with_credentials_connects); RUN_TEST(test_leaving_standalone_without_credentials_opens_onboarding); RUN_TEST(test_onboard_cancels_running_start_pulse); RUN_TEST(test_failed_onboarding_with_credentials_stays_standalone); RUN_TEST(test_onboarding_timeout_with_credentials_stays_standalone); RUN_TEST(test_standalone_cancels_pending_ap_stop); RUN_TEST(test_standalone_without_pulse_ends_running_pulse); RUN_TEST(test_onboard_from_ap_onboarding_keeps_pulse); return UNITY_END(); }