Skip to content

Commit 747e3e0

Browse files
committed
feat(esp_wifi): Add unit test for writing wifi config in nvs
This commit adds a unit test for using wifi station and softap by flashing wifi config directly into nvs using NVS Partition generator Utility (using csv file).
1 parent 9f4eb5c commit 747e3e0

File tree

14 files changed

+404
-2
lines changed

14 files changed

+404
-2
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ components/**/build/
3737
components/**/build_*_*/
3838
components/**/sdkconfig
3939
components/**/sdkconfig.old
40+
components/**/test_apps/wifi_nvs_config/nvs_data_suffix.csv
4041

4142
# Example project files
4243
examples/**/build/

components/esp_wifi/test_apps/.build-test-rules.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@
33
components/esp_wifi/test_apps/:
44
disable:
55
- if: SOC_WIFI_SUPPORTED != 1
6+
7+
components/esp_wifi/test_apps/wifi_nvs_config:
8+
disable:
9+
- if: SOC_WIFI_SUPPORTED != 1
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#This is the project CMakeLists.txt file for the test subproject
2+
cmake_minimum_required(VERSION 3.16)
3+
4+
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components")
5+
6+
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
7+
set(COMPONENTS main)
8+
9+
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
10+
11+
if($ENV{CI_PIPELINE_ID})
12+
idf_build_set_property(COMPILE_DEFINITIONS TEST_SUFFIX_STR="_$ENV{CI_PIPELINE_ID}" APPEND)
13+
endif()
14+
15+
if(DEFINED ENV{CI_PIPELINE_ID})
16+
set(TEST_SUFFIX_STR "_$ENV{CI_PIPELINE_ID}")
17+
else()
18+
string(TIMESTAMP TEST_SUFFIX_STR "%Y%m%d%H%M%S")
19+
endif()
20+
21+
execute_process(
22+
COMMAND python3 ${CMAKE_SOURCE_DIR}/update_csv_suffix.py ${TEST_SUFFIX_STR}
23+
)
24+
25+
project(wifi_nvs_conn_test)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-S2 | ESP32-S3 |
2+
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- |
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
idf_component_register(SRC_DIRS .
2+
PRIV_INCLUDE_DIRS . ${CMAKE_CURRENT_BINARY_DIR}
3+
PRIV_REQUIRES cmock test_utils nvs_flash esp_wifi esp_event
4+
WHOLE_ARCHIVE)
5+
6+
# Create a NVS image from the contents of the `nvs_data` CSV file
7+
# that fits the partition named 'nvs'. FLASH_IN_PROJECT indicates that
8+
# the generated image should be flashed when the entire project is flashed to
9+
# the target with 'idf.py -p PORT flash'.
10+
nvs_create_partition_image(nvs ../nvs_data_suffix.csv FLASH_IN_PROJECT)
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Unlicense OR CC0-1.0
5+
*/
6+
#include "unity.h"
7+
#include "nvs_flash.h"
8+
#include "nvs.h"
9+
#include "esp_err.h"
10+
#include "esp_netif.h"
11+
#include "esp_wifi.h"
12+
13+
#include "esp_heap_caps.h"
14+
15+
// Some resources are lazy allocated in wifi and lwip
16+
#define TEST_MEMORY_LEAK_THRESHOLD (-1536)
17+
18+
static size_t before_free_8bit;
19+
static size_t before_free_32bit;
20+
21+
static void check_leak(size_t before_free, size_t after_free, const char *type)
22+
{
23+
ssize_t delta = after_free - before_free;
24+
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
25+
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
26+
}
27+
28+
void setUp(void)
29+
{
30+
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
31+
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
32+
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
33+
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
34+
}
35+
36+
void tearDown(void)
37+
{
38+
ESP_ERROR_CHECK(esp_wifi_deinit());
39+
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
40+
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
41+
check_leak(before_free_8bit, after_free_8bit, "8BIT");
42+
check_leak(before_free_32bit, after_free_32bit, "32BIT");
43+
}
44+
45+
void app_main(void)
46+
{
47+
ESP_ERROR_CHECK(nvs_flash_init());
48+
ESP_ERROR_CHECK(esp_netif_init());
49+
unity_run_menu();
50+
ESP_ERROR_CHECK(esp_netif_deinit());
51+
ESP_ERROR_CHECK(nvs_flash_deinit());
52+
}
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Unlicense OR CC0-1.0
5+
*
6+
* This test code is in the Public Domain (or CC0 licensed, at your option.)
7+
*
8+
* Unless required by applicable law or agreed to in writing, this
9+
* software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
10+
* CONDITIONS OF ANY KIND, either express or implied.
11+
*/
12+
13+
#include <stdio.h>
14+
#include <string.h>
15+
#include "unity.h"
16+
#include "esp_mac.h"
17+
#include "esp_event.h"
18+
#include "esp_wifi.h"
19+
#include "esp_wifi_types.h"
20+
#include "esp_log.h"
21+
#include "nvs_flash.h"
22+
#include "test_utils.h"
23+
#include "memory_checks.h"
24+
#include "freertos/task.h"
25+
#include "freertos/event_groups.h"
26+
27+
#define CONNECT_TIMEOUT_MS (10000)
28+
29+
#define GOT_IP_EVENT (1)
30+
#define WIFI_DISCONNECT_EVENT (1<<1)
31+
#define WIFI_STA_CONNECTED (1<<2)
32+
#define WIFI_AP_STA_CONNECTED (1<<3)
33+
34+
#define EVENT_HANDLER_FLAG_DO_NOT_AUTO_RECONNECT 0x00000001
35+
36+
static uint32_t wifi_event_handler_flag;
37+
static const char* TAG = "test_wifi";
38+
static esp_netif_t* s_ap_netif = NULL;
39+
static esp_netif_t* s_sta_netif = NULL;
40+
41+
static EventGroupHandle_t wifi_events;
42+
static void stop_wifi(void);
43+
44+
static void wifi_ap_event_handler(void* arg, esp_event_base_t event_base,
45+
int32_t event_id, void* event_data)
46+
{
47+
ESP_LOGI(TAG, "wifi event handler: %"PRIi32, event_id);
48+
switch (event_id) {
49+
case WIFI_EVENT_AP_START:
50+
ESP_LOGI(TAG, "WIFI_EVENT_AP_START");
51+
break;
52+
case WIFI_EVENT_AP_STACONNECTED:
53+
ESP_LOGI(TAG, "WIFI_EVENT_AP_STACONNECTED");
54+
if (wifi_events) {
55+
xEventGroupSetBits(wifi_events, WIFI_AP_STA_CONNECTED);
56+
}
57+
break;
58+
case WIFI_EVENT_AP_STADISCONNECTED:
59+
ESP_LOGI(TAG, "WIFI_EVENT_AP_STADISCONNECTED");
60+
ESP_LOGI(TAG, "sta disconnected");
61+
break;
62+
default:
63+
break;
64+
}
65+
return;
66+
}
67+
68+
static void wifi_sta_event_handler(void* arg, esp_event_base_t event_base,
69+
int32_t event_id, void* event_data)
70+
{
71+
ESP_LOGI(TAG, "wifi event handler: %"PRIi32, event_id);
72+
switch (event_id) {
73+
case WIFI_EVENT_STA_START:
74+
ESP_LOGI(TAG, "WIFI_EVENT_STA_START");
75+
// make sure softap has started
76+
vTaskDelay(1000 / portTICK_PERIOD_MS);
77+
TEST_ESP_OK(esp_wifi_connect());
78+
ESP_LOGI(TAG, "start esp_wifi_connect");
79+
break;
80+
case WIFI_EVENT_STA_CONNECTED:
81+
ESP_LOGI(TAG, "WIFI_EVENT_STA_CONNECTED");
82+
if (wifi_events) {
83+
xEventGroupSetBits(wifi_events, WIFI_STA_CONNECTED);
84+
}
85+
break;
86+
case WIFI_EVENT_STA_DISCONNECTED:
87+
ESP_LOGI(TAG, "WIFI_EVENT_STA_DISCONNECTED");
88+
wifi_event_sta_disconnected_t *event = (wifi_event_sta_disconnected_t *)event_data;
89+
ESP_LOGI(TAG, "disconnect reason: %u", event->reason);
90+
if (!(EVENT_HANDLER_FLAG_DO_NOT_AUTO_RECONNECT & wifi_event_handler_flag)) {
91+
TEST_ESP_OK(esp_wifi_connect());
92+
}
93+
if (wifi_events) {
94+
xEventGroupSetBits(wifi_events, WIFI_DISCONNECT_EVENT);
95+
}
96+
break;
97+
default:
98+
break;
99+
}
100+
return;
101+
}
102+
103+
static void ip_event_handler(void* arg, esp_event_base_t event_base,
104+
int32_t event_id, void* event_data)
105+
{
106+
ip_event_got_ip_t *event;
107+
108+
ESP_LOGI(TAG, "ip event handler");
109+
switch (event_id) {
110+
case IP_EVENT_STA_GOT_IP:
111+
event = (ip_event_got_ip_t*)event_data;
112+
ESP_LOGI(TAG, "IP_EVENT_STA_GOT_IP");
113+
ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
114+
if (wifi_events) {
115+
xEventGroupSetBits(wifi_events, GOT_IP_EVENT);
116+
}
117+
break;
118+
default:
119+
break;
120+
}
121+
return;
122+
}
123+
124+
static esp_err_t event_init(void)
125+
{
126+
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, &ip_event_handler, NULL));
127+
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
128+
esp_wifi_init(&cfg);
129+
return ESP_OK;
130+
}
131+
132+
static esp_err_t event_deinit(void)
133+
{
134+
ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, ESP_EVENT_ANY_ID, &ip_event_handler));
135+
ESP_ERROR_CHECK(esp_event_loop_delete_default());
136+
return ESP_OK;
137+
}
138+
139+
#define EMPH_STR(s) "****** "s" ******"
140+
141+
static void start_wifi(void)
142+
{
143+
144+
event_init();
145+
146+
if (wifi_events == NULL) {
147+
wifi_events = xEventGroupCreate();
148+
}
149+
xEventGroupClearBits(wifi_events, 0x00ffffff);
150+
151+
TEST_ESP_OK(esp_wifi_start());
152+
153+
}
154+
155+
static void stop_wifi(void)
156+
{
157+
ESP_LOGI(TAG, "Stopping wifi now");
158+
TEST_ESP_OK(esp_wifi_stop());
159+
event_deinit();
160+
if (wifi_events) {
161+
vEventGroupDelete(wifi_events);
162+
wifi_events = NULL;
163+
}
164+
vTaskDelay(500 / portTICK_PERIOD_MS);
165+
}
166+
167+
static void test_wifi_connection_sta(void)
168+
{
169+
EventBits_t bits;
170+
ESP_ERROR_CHECK(esp_event_loop_create_default());
171+
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_sta_event_handler, NULL));
172+
s_sta_netif = esp_netif_create_default_wifi_sta();
173+
esp_wifi_set_mode(WIFI_MODE_STA);
174+
175+
start_wifi();
176+
177+
wifi_config_t cfg;
178+
esp_wifi_get_config(WIFI_IF_STA, &cfg);
179+
ESP_LOGI(TAG, "STA mode, %s %s", cfg.sta.ssid, cfg.sta.password);
180+
181+
bits = xEventGroupWaitBits(wifi_events, GOT_IP_EVENT, 1, 0, CONNECT_TIMEOUT_MS / portTICK_PERIOD_MS);
182+
TEST_ASSERT(bits & GOT_IP_EVENT);
183+
184+
xEventGroupClearBits(wifi_events, WIFI_DISCONNECT_EVENT);
185+
wifi_event_handler_flag |= EVENT_HANDLER_FLAG_DO_NOT_AUTO_RECONNECT;
186+
187+
// Wait for 60s and the stop wifi
188+
vTaskDelay(60000 / portTICK_PERIOD_MS);
189+
esp_netif_destroy_default_wifi(s_sta_netif);
190+
191+
stop_wifi();
192+
}
193+
194+
static void test_wifi_connection_softap(void)
195+
{
196+
EventBits_t bits;
197+
ESP_ERROR_CHECK(esp_event_loop_create_default());
198+
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_ap_event_handler, NULL));
199+
s_ap_netif = esp_netif_create_default_wifi_ap();
200+
201+
esp_wifi_set_mode(WIFI_MODE_AP);
202+
start_wifi();
203+
204+
wifi_config_t cfg;
205+
esp_wifi_get_config(WIFI_IF_AP, &cfg);
206+
ESP_LOGI(TAG, "AP mode, %s %s", cfg.ap.ssid, cfg.ap.password);
207+
208+
// wait station connected
209+
bits = xEventGroupWaitBits(wifi_events, WIFI_AP_STA_CONNECTED, 1, 0, CONNECT_TIMEOUT_MS / portTICK_PERIOD_MS);
210+
TEST_ASSERT(bits & WIFI_AP_STA_CONNECTED);
211+
212+
// wait 70s (longer than station side)
213+
vTaskDelay((60000 + CONNECT_TIMEOUT_MS) / portTICK_PERIOD_MS);
214+
esp_netif_destroy_default_wifi(s_ap_netif);
215+
216+
stop_wifi();
217+
}
218+
219+
TEST_CASE_MULTIPLE_DEVICES("test wifi connection with station and AP", "[wifi][timeout=90]", test_wifi_connection_sta, test_wifi_connection_softap);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
key,type,encoding,value
2+
nvs.net80211,namespace,,
3+
sta.ssid,data,blob_sz_fill(32;0x00),TESTSSID
4+
sta.pswd,data,blob_fill(64;0x00),TESTPASSWORD
5+
ap.ssid,data,blob_sz_fill(32;0x00),TESTSSID
6+
ap.passwd,data,blob_fill(64;0x00),TESTPASSWORD
7+
ap.authmode,data,u8,3
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Name, Type, SubType, Offset, Size, Flags
2+
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3+
# Name, Type, SubType, Offset, Size, Flags
4+
nvs, data, nvs, 0x9000, 0x6000,
5+
phy_init, data, phy, 0xf000, 0x1000,
6+
factory, app, factory, 0x10000, 1M,
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
2+
# SPDX-License-Identifier: Unlicense OR CC0-1.0
3+
import pytest
4+
from pytest_embedded_idf.unity_tester import CaseTester
5+
from pytest_embedded_idf.utils import idf_parametrize
6+
7+
8+
@pytest.mark.wifi_two_dut
9+
@pytest.mark.parametrize('count', [2], indirect=True)
10+
@idf_parametrize(
11+
'target',
12+
['esp32', 'esp32c3', 'esp32c5', 'esp32c6', 'esp32c61', 'esp32s2', 'esp32s3'],
13+
indirect=['target'],
14+
)
15+
def test_wifi_nvs_connect_cases(case_tester: CaseTester) -> None: # type: ignore
16+
case_tester.run_all_cases()
17+
18+
19+
@pytest.mark.wifi_two_dut
20+
@pytest.mark.xtal_26mhz
21+
@pytest.mark.parametrize(
22+
'count, config, baud',
23+
[
24+
(2, 'esp32c2_xtal26m', '74880'),
25+
],
26+
indirect=True,
27+
)
28+
@idf_parametrize('target', ['esp32c2'], indirect=['target'])
29+
def test_wifi_nvs_connect_cases_esp32c2_xtal26m(case_tester: CaseTester) -> None:
30+
case_tester.run_all_cases()

0 commit comments

Comments
 (0)