mirror of
https://github.com/alexhopeoconnor/WiFiManager.git
synced 2026-10-22 10:42:10 +10:00
test: add portal hardware lifecycle coverage
This commit is contained in:
@@ -4,7 +4,8 @@
|
||||
|
||||
// Test process() doesn't block - verify it must be called periodically
|
||||
void test_nonblocking_process() {
|
||||
Serial.println("[TEST] Testing process() calls (non-blocking behavior)...");
|
||||
constexpr unsigned long kMaximumProcessDurationMs = 250;
|
||||
unsigned long maximumObservedDurationMs = 0;
|
||||
|
||||
WiFiManager wm;
|
||||
|
||||
@@ -18,10 +19,13 @@ void test_nonblocking_process() {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
unsigned long start = millis();
|
||||
wm.process();
|
||||
unsigned long elapsed = millis() - start;
|
||||
const unsigned long elapsed = millis() - start;
|
||||
maximumObservedDurationMs = max(maximumObservedDurationMs, elapsed);
|
||||
|
||||
// Each call should be fast (< 100ms) - verifies non-blocking behavior
|
||||
TEST_ASSERT_LESS_THAN(100, elapsed);
|
||||
// A normal cooperative call is quick, but DNS and Wi-Fi service may
|
||||
// briefly run on ESP8266. This still catches a truly blocking portal.
|
||||
TEST_ASSERT_LESS_THAN_MESSAGE(kMaximumProcessDurationMs, elapsed,
|
||||
"process() exceeded its bounded service time");
|
||||
|
||||
delay(10);
|
||||
}
|
||||
@@ -31,6 +35,7 @@ void test_nonblocking_process() {
|
||||
|
||||
wm.stopConfigPortal();
|
||||
|
||||
Serial.printf("[METRIC] WM_PROCESS max_elapsed_ms=%lu\n", maximumObservedDurationMs);
|
||||
Serial.println("[TEST] Non-blocking process() test completed successfully");
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
#include <Arduino.h>
|
||||
#include <WiFiManager.h>
|
||||
#include <unity.h>
|
||||
#include <vector>
|
||||
|
||||
#include "../test_main.h"
|
||||
|
||||
namespace {
|
||||
|
||||
struct HeapSnapshot {
|
||||
uint32_t freeHeap;
|
||||
uint32_t largestBlock;
|
||||
uint8_t fragmentation;
|
||||
};
|
||||
|
||||
HeapSnapshot captureHeap(const char* label) {
|
||||
HeapSnapshot snapshot{
|
||||
ESP.getFreeHeap(),
|
||||
#if defined(ESP8266)
|
||||
ESP.getMaxFreeBlockSize(),
|
||||
ESP.getHeapFragmentation(),
|
||||
#else
|
||||
ESP.getMaxAllocHeap(),
|
||||
0,
|
||||
#endif
|
||||
};
|
||||
|
||||
Serial.printf("[METRIC] WM_HEAP label=%s free=%lu largest=%lu fragmentation=%u\n",
|
||||
label,
|
||||
static_cast<unsigned long>(snapshot.freeHeap),
|
||||
static_cast<unsigned long>(snapshot.largestBlock),
|
||||
snapshot.fragmentation);
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
uint32_t allowedHeapDrift() {
|
||||
#if defined(ESP8266)
|
||||
return 1024;
|
||||
#else
|
||||
return 4096;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t allowedLargestBlockDrift() {
|
||||
#if defined(ESP8266)
|
||||
return 1024;
|
||||
#else
|
||||
return 4096;
|
||||
#endif
|
||||
}
|
||||
|
||||
void assertRecovered(const HeapSnapshot& settled, const HeapSnapshot& final) {
|
||||
const uint32_t freeFloor = settled.freeHeap > allowedHeapDrift()
|
||||
? settled.freeHeap - allowedHeapDrift()
|
||||
: 0;
|
||||
const uint32_t blockFloor = settled.largestBlock > allowedLargestBlockDrift()
|
||||
? settled.largestBlock - allowedLargestBlockDrift()
|
||||
: 0;
|
||||
|
||||
TEST_ASSERT_GREATER_OR_EQUAL_UINT32_MESSAGE(
|
||||
freeFloor, final.freeHeap,
|
||||
"Portal start/stop cycles retained too much heap after warm-up");
|
||||
TEST_ASSERT_GREATER_OR_EQUAL_UINT32_MESSAGE(
|
||||
blockFloor, final.largestBlock,
|
||||
"Portal start/stop cycles degraded the largest contiguous allocation");
|
||||
}
|
||||
|
||||
void startAndStopPortal(WiFiManager& wm, const char* ssid) {
|
||||
wm.startConfigPortal(ssid);
|
||||
wm.process();
|
||||
TEST_ASSERT_TRUE_MESSAGE(wm.getConfigPortalActive(), "Portal should start");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(wm.getServer(), "Portal server should exist");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(wm.getDNSServer(), "Portal DNS server should exist");
|
||||
|
||||
wm.stopConfigPortal();
|
||||
wm.process();
|
||||
TEST_ASSERT_FALSE_MESSAGE(wm.getConfigPortalActive(), "Portal should stop");
|
||||
TEST_ASSERT_NULL_MESSAGE(wm.getServer(), "Portal server should be released");
|
||||
TEST_ASSERT_NULL_MESSAGE(wm.getDNSServer(), "Portal DNS server should be released");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void test_portal_resource_recovery() {
|
||||
Serial.println("[TEST] Measuring portal resource recovery...");
|
||||
|
||||
WiFiManager wm;
|
||||
wm.setConfigPortalTimeout(20);
|
||||
|
||||
// Warm the Wi-Fi core once before establishing the comparison point. The
|
||||
// SDK is allowed to retain its own one-time allocations; the test detects
|
||||
// repeated decline after that settled point instead.
|
||||
startAndStopPortal(wm, "WM-Memory-Warmup");
|
||||
delay(200);
|
||||
const HeapSnapshot settled = captureHeap("settled");
|
||||
|
||||
for (uint8_t cycle = 0; cycle < 20; ++cycle) {
|
||||
const String ssid = String("WM-Memory-") + String(cycle);
|
||||
wm.startConfigPortal(ssid.c_str());
|
||||
wm.process();
|
||||
TEST_ASSERT_TRUE_MESSAGE(wm.getConfigPortalActive(), "Portal should start during cycle");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(wm.getServer(), "Server should exist during cycle");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(wm.getDNSServer(), "DNS should exist during cycle");
|
||||
if (cycle == 0) {
|
||||
captureHeap("portal-active");
|
||||
}
|
||||
|
||||
wm.stopConfigPortal();
|
||||
wm.process();
|
||||
TEST_ASSERT_FALSE_MESSAGE(wm.getConfigPortalActive(), "Portal should stop during cycle");
|
||||
TEST_ASSERT_NULL_MESSAGE(wm.getServer(), "Server should be released during cycle");
|
||||
TEST_ASSERT_NULL_MESSAGE(wm.getDNSServer(), "DNS should be released during cycle");
|
||||
captureHeap("cycle-stopped");
|
||||
}
|
||||
|
||||
delay(200);
|
||||
const HeapSnapshot final = captureHeap("final");
|
||||
assertRecovered(settled, final);
|
||||
|
||||
Serial.println("[TEST] Portal resource recovery test completed successfully");
|
||||
}
|
||||
|
||||
void test_scan_result_storage_released_when_portal_closes() {
|
||||
Serial.println("[TEST] Testing scan-result storage release on portal close...");
|
||||
|
||||
WiFiManager wm;
|
||||
wm.setConfigPortalTimeout(20);
|
||||
wm.startConfigPortal("WM-Scan-Storage");
|
||||
wm.process();
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
std::vector<WiFiManager::WiFiScanNetwork> results;
|
||||
for (uint8_t i = 0; i < 24; ++i) {
|
||||
results.push_back({String("Network-") + String(i), -30 - i, static_cast<uint8_t>(i % 2)});
|
||||
}
|
||||
wm.wmTestInjectScanResults(results);
|
||||
captureHeap("scan-cache-held");
|
||||
TEST_ASSERT_GREATER_THAN_UINT32(0, wm.getScanResults().capacity());
|
||||
#endif
|
||||
|
||||
wm.stopConfigPortal();
|
||||
wm.process();
|
||||
TEST_ASSERT_EQUAL_UINT32(0, wm.getScanResults().size());
|
||||
TEST_ASSERT_EQUAL_UINT32(0, wm.getScanResults().capacity());
|
||||
captureHeap("scan-storage-cleared");
|
||||
|
||||
Serial.println("[TEST] Scan-result storage release test completed successfully");
|
||||
}
|
||||
|
||||
void test_real_async_scan_completes() {
|
||||
Serial.println("[TEST] Testing real asynchronous Wi-Fi scan completion...");
|
||||
|
||||
WiFiManager wm;
|
||||
wm.setConfigPortalTimeout(30);
|
||||
wm.startConfigPortal("WM-Real-Scan");
|
||||
wm.process();
|
||||
TEST_ASSERT_TRUE_MESSAGE(wm.getConfigPortalActive(), "Portal should be active for scanning");
|
||||
captureHeap("scan-portal-active");
|
||||
|
||||
wm.requestAsyncScan(true);
|
||||
captureHeap("scan-requested");
|
||||
const uint32_t deadline = millis() + 25000UL;
|
||||
while (wm.isScanRunning() && millis() < deadline) {
|
||||
wm.process();
|
||||
delay(20);
|
||||
}
|
||||
|
||||
const WiFiManager::WiFiScanRuntimeState scan = wm.getScanSnapshot();
|
||||
Serial.printf("[METRIC] WM_SCAN state=%u result=%d count=%d elapsed=%lu\n",
|
||||
static_cast<unsigned>(scan.state),
|
||||
scan.lastScanResult,
|
||||
static_cast<int>(wm.getScanResults().size()),
|
||||
static_cast<unsigned long>(millis() - scan.startedAt));
|
||||
captureHeap("scan-complete-cache");
|
||||
|
||||
const bool stillRunning = wm.isScanRunning();
|
||||
const WiFiManager::wm_scan_state_t state = wm.getScanState();
|
||||
wm.stopConfigPortal();
|
||||
|
||||
captureHeap("scan-storage-cleared");
|
||||
TEST_ASSERT_FALSE_MESSAGE(stillRunning, "Async scan exceeded its completion deadline");
|
||||
TEST_ASSERT_EQUAL_MESSAGE(WiFiManager::WM_SCAN_COMPLETE, state,
|
||||
"Real scan must complete rather than enter failed/timeout state");
|
||||
|
||||
Serial.println("[TEST] Real asynchronous Wi-Fi scan test completed successfully");
|
||||
}
|
||||
Reference in New Issue
Block a user