Repair WiFi power-policy CI fixtures and run ownership checks earlier

This commit is contained in:
mikecarper committed 2026-10-12 15:55:04 -07:00
1 parent 05a675a57a
commit 42e5c0c07b
4 files changed
+83 -3

No files matched your search

+3 -3
View File
@@ -217,7 +217,7 @@ jobs:
- name: Verify transactional telemetry preferences and legacy route recovery
run: python3 -B test/test_telemetry_tx_persistence.py -v
- name: Verify MQTT start results and connection-attempt accounting
- name: Verify MQTT and WiFi ownership, recovery and power policy
run: |
python3 -B test/test_mqtt_transport_results.py -v
python3 -B test/test_mqtt_tls_security.py -v
@@ -231,6 +231,8 @@ jobs:
python3 -B test/test_mqtt_wifi_network_power.py -v
python3 -B test/test_companion_wifi_network_power.py -v
python3 -B test/test_webconfig_network_power.py -v
python3 -B test/test_companion_ap_ownership.py -v
python3 -B test/test_heltec_v4_wifi_setup_page.py -v
- name: Verify DNS lease restoration and bounded diagnostics
run: python3 -B test/test_network_dns.py -v
@@ -616,8 +618,6 @@ jobs:
python3 -B test/test_webconfig_station_ownership.py -v
python3 -B test/test_mqtt_canonical_wifi.py -v
python3 -B test/test_mqtt_ap_ownership.py -v
python3 -B test/test_companion_ap_ownership.py -v
python3 -B test/test_heltec_v4_wifi_setup_page.py -v
python3 -B test/test_sensor_webconfig_capabilities.py -v
python3 -B test/test_webconfig_ui_runtime.py
python3 -B test/test_webconfig_lost_reply.py -v
+47
View File
@@ -3,6 +3,7 @@
#include <cstring>
#include <string>
#include "helpers/WiFiReconnectPolicy.h"
#include "helpers/WiFiNetworkPowerPolicy.h"
#define WIFI_DEBUG_PRINTLN(...) ((void)0)
using wifi_mode_t=int;using esp_err_t=int;
constexpr int ESP_OK=0,ESP_FAIL=-1,ESP_ERR_WIFI_NOT_INIT=-2;
@@ -59,6 +60,8 @@ void saveCompanionWiFi(){}
char configured_wifi_ssid[8]="mesh",configured_wifi_password[8]="secret";
bool companion_wifi_has_credentials=true,companion_wifi_requested=true,companion_wifi_active=true;
bool companion_wifi_services_stopped=true;
bool companion_wifi_background_stopped=false,companion_wifi_espnow_restore_pending=true;
bool companion_wifi_webconfig_start_pending=false;
bool companion_wifi_credential_reload_pending=true;
unsigned long companion_wifi_credential_reload_at=1;
bool wifi_setup_recovery_mode=false,wifi_setup_attempted=true;
@@ -70,6 +73,10 @@ void applyCompanionWiFiPowerSave(){++power_writes;}
constexpr int TCP_PORT=1234;
struct Interface{void begin(int){++socket_starts;}void enable(){}}wifi_interface;
void ota_console_start(){++console_starts;}
@POWER_GUARDS@
#ifdef WITH_WEBCONFIG
@AUTO_WEBCONFIG@
#endif
@RELOAD@
@START_WIFI@
void serviceStation(){
@@ -129,5 +136,45 @@ int main(int argc,char** argv){
assert(companion_wifi_active&&!companion_wifi_services_stopped&&board.inhibit_sleep);
assert(mode_writes==1&&joins==1&&socket_starts==1&&console_starts==1&&power_writes==1);
startCompanionWiFi();assert(mode_writes==1&&socket_starts==1);
}else if(scenario=="suspended"){
auto& power=mesh::wifi::networkPower();
power.setManaged(true);power.setSuspended(true);
board.ota=false;companion_wifi_active=false;sdk_mode=WIFI_STA;
serviceCompanionWiFiCredentialReload();startCompanionWiFi();
assert(mode_writes==0&&disconnects==0&&joins==0&&credential_loads==0);
assert(companion_wifi_credential_reload_pending&&companion_wifi_credential_reload_at==1);
assert(!companion_wifi_active&&companion_wifi_requested&&companion_wifi_services_stopped);
assert(companion_wifi_espnow_restore_pending);
assert(socket_starts==0&&console_starts==0&&power_writes==0&&!board.inhibit_sleep);
assert(!companionWiFiAutomaticRecoveryAllowed());
// A fresh foreground hold admits startup, but only after AP teardown.
power.setHold(mesh::wifi::NetworkPowerHold::CompanionClient,true);
startCompanionWiFi();
assert(companion_wifi_active&&!companion_wifi_services_stopped&&board.inhibit_sleep);
assert(mode_writes==1&&joins==1&&socket_starts==1&&console_starts==1&&power_writes==1);
assert(!companion_wifi_espnow_restore_pending);
serviceCompanionWiFiCredentialReload();
assert(!companion_wifi_credential_reload_pending&&companion_wifi_credential_reload_at==0);
assert(credential_loads==1&&mode_writes==2&&disconnects==1&&joins==2);
power.setHold(mesh::wifi::NetworkPowerHold::CompanionClient,false);
power.setManaged(false);
assert(companionWiFiAutomaticRecoveryAllowed()&&!companionWiFiBackgroundSuspended());
}else if(scenario=="suspended_ap"){
auto& power=mesh::wifi::networkPower();
power.setManaged(true);power.setSuspended(true);
companion_wifi_active=false;
for(bool ota:{false,true}){
board.ota=ota;
power.setHold(mesh::wifi::NetworkPowerHold::WebConfig,!ota);
assert(!companionWiFiBackgroundSuspended());
for(int mode:{WIFI_AP,WIFI_AP_STA}){
sdk_mode=mode;serviceCompanionWiFiCredentialReload();startCompanionWiFi();
assert_untouched();
assert(companion_wifi_credential_reload_pending&&companion_wifi_credential_reload_at==1);
assert(companion_wifi_espnow_restore_pending&&credential_loads==0);
assert(!companion_wifi_active&&companion_wifi_requested&&companion_wifi_services_stopped);
assert(socket_starts==0&&console_starts==0&&power_writes==0&&!board.inhibit_sleep);
}
}
}else assert(false);
}
+15
View File
@@ -20,6 +20,11 @@ class CompanionAccessPointOwnershipTest(unittest.TestCase):
main = (ROOT / "examples/companion_radio/main.cpp").read_text(encoding="ascii")
reload = method(main, " static void serviceCompanionWiFiCredentialReload(")
start_wifi = method(main, " static void startCompanionWiFi(")
power_guards = "\n".join(method(main, signature) for signature in (
" static bool companionWiFiBackgroundSuspended(",
" static bool companionWiFiAutomaticRecoveryAllowed(",
))
auto_webconfig = method(main, " static void startCompanionWiFiAutoWebConfig(")
start = main.index(" const unsigned long wifi_now = millis();")
end = main.index("#ifdef WITH_MQTT_BRIDGE", start)
station_loop = main[start:end]
@@ -27,6 +32,8 @@ class CompanionAccessPointOwnershipTest(unittest.TestCase):
guard = method(policy, "inline bool stationMutationAllowed(")
fixture = (ROOT / "test/fixtures/companion_ap_ownership.cpp").read_text(encoding="ascii")
return (fixture.replace("@STATION_GUARD@", guard).replace("@RELOAD@", reload)
.replace("@POWER_GUARDS@", power_guards)
.replace("@AUTO_WEBCONFIG@", auto_webconfig)
.replace("@START_WIFI@", start_wifi).replace("@STATION_LOOP@", station_loop))
def run_fixture(self, scenario, source=None, webconfig=True):
@@ -64,6 +71,14 @@ class CompanionAccessPointOwnershipTest(unittest.TestCase):
self.run_fixture("setup_handoff", webconfig=False)
self.run_fixture("unrelated_setup")
def test_managed_suspension_retains_reload_and_wifi_start_intent(self):
self.run_fixture("suspended")
self.run_fixture("suspended", webconfig=False)
def test_foreground_and_ota_holds_do_not_bypass_ap_ownership(self):
self.run_fixture("suspended_ap")
self.run_fixture("suspended_ap", webconfig=False)
def test_reverted_retry_guard_removes_the_ota_ap(self):
source = self.fixture().replace(
"if (mesh::wifi::stationMutationAllowed(setup_station_handoff)) {", "if (true) {")
+18
View File
@@ -104,6 +104,7 @@ class HeltecV4WiFiSetupPageTest(unittest.TestCase):
#include <cstdint>
#include <cstddef>
#include <helpers/WirelessControl.h>
#include <helpers/WiFiNetworkPowerPolicy.h>
#define UI_WIFI_SETUP_HOME_PAGE 1
#define UI_NO_DISCOVER_SCREEN 1
#define UI_NO_HIBERNATE 1
@@ -140,22 +141,37 @@ struct HomeScreen : UIScreen {
};
int main() {
UITask task; HomeScreen home(&task);
auto& network_power = mesh::wifi::networkPower();
network_power.setManaged(true);
network_power.setSuspended(true);
assert(network_power.backgroundSuspended());
assert(!network_power.consumeProbeRequest());
assert(home.handleInput(static_cast<char>(KEY_NEXT)));
assert(home._page == HomeScreen::FIRST);
assert(!companion_wifi_setup_requested && !companion_wifi_setup_stop_requested);
assert(!network_power.consumeProbeRequest());
assert(home.handleInput(static_cast<char>(KEY_PREV)));
assert(home._page == HomeScreen::WIFI_SETUP);
assert(!network_power.consumeProbeRequest());
assert(home.handleInput(KEY_ENTER));
assert(companion_wifi_setup_requested && !companion_wifi_setup_stop_requested);
assert(network_power.consumeProbeRequest());
assert(!network_power.consumeProbeRequest());
// The UI hands off a probe; only the network owner may resume transports.
assert(network_power.backgroundSuspended());
WebConfigServer::active = true;
assert(home.handleInput(KEY_ENTER));
assert(!companion_wifi_setup_requested && companion_wifi_setup_stop_requested);
assert(!network_power.consumeProbeRequest());
assert(home.handleInput(static_cast<char>(KEY_RIGHT)));
assert(home._page == HomeScreen::FIRST);
assert(!companion_wifi_setup_requested && companion_wifi_setup_stop_requested);
assert(!network_power.consumeProbeRequest());
assert(home.handleInput(static_cast<char>(KEY_LEFT)));
assert(home._page == HomeScreen::WIFI_SETUP);
assert(!network_power.consumeProbeRequest());
requestCompanionWiFiSetupStop();
assert(!network_power.consumeProbeRequest());
struct Backend : mesh::wireless::Backend {
uint8_t available() const override { return mesh::wireless::WiFi; }
uint8_t enabled() const override { return mesh::wireless::WiFi; }
@@ -168,6 +184,8 @@ int main() {
mesh::wireless::control().service(250);
requestCompanionWiFiSetup();
assert(!companion_wifi_setup_requested && companion_wifi_setup_stop_requested);
assert(!network_power.consumeProbeRequest());
assert(network_power.backgroundSuspended());
}
'''
source = (source.replace("@REQUESTS@", requests).replace("@PAGES@", page_enum)