open_access() fordert zusaetzlich !standalone, damit der Tokenschutz auch waehrend eines Onboarding-Tests aus dem Standalone-Modus erhalten bleibt.
311 lines
13 KiB
C
311 lines
13 KiB
C
#include <string.h>
|
|
#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);
|
|
}
|
|
|
|
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);
|
|
return UNITY_END();
|
|
}
|