Skip to content

Commit a70f444

Browse files
committed
feat(esp_commands): Add tests to the esp_commands component
1 parent 485aa0f commit a70f444

File tree

8 files changed

+351
-0
lines changed

8 files changed

+351
-0
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
cmake_minimum_required(VERSION 3.16)
2+
3+
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
4+
set(COMPONENTS main)
5+
project(esp_commands_test)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
idf_component_register(SRCS "test_esp_commands.c" "test_main.c"
2+
PRIV_INCLUDE_DIRS "." "include"
3+
PRIV_REQUIRES unity
4+
WHOLE_ARCHIVE)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
dependencies:
2+
espressif/esp_commands:
3+
version: "*"
4+
override_path: "../.."
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#pragma once
8+
9+
#ifdef __cplusplus
10+
extern "C" {
11+
#endif
12+
13+
#define NB_OF_REGISTERED_CMD 8
14+
15+
#define GET_NAME(NAME, SUFFIX) NAME##SUFFIX
16+
#define GET_STR(STR) #STR
17+
18+
#define CREATE_CMD_FUNC(NAME) \
19+
static int GET_NAME(NAME, _func)(void *ctx, int argc, char **argv) { \
20+
printf(GET_STR(NAME) GET_STR(_func)); \
21+
printf("\n"); \
22+
return 0; \
23+
}
24+
25+
#define CREATE_FUNC(NAME, SUFFIX) \
26+
static const char *GET_NAME(NAME, SUFFIX)(void) { \
27+
return #NAME #SUFFIX; \
28+
}
29+
30+
/* static command functions*/
31+
CREATE_CMD_FUNC(cmd_a)
32+
CREATE_CMD_FUNC(cmd_b)
33+
CREATE_CMD_FUNC(cmd_c)
34+
CREATE_CMD_FUNC(cmd_d)
35+
CREATE_CMD_FUNC(cmd_e)
36+
CREATE_CMD_FUNC(cmd_f)
37+
CREATE_CMD_FUNC(cmd_g)
38+
CREATE_CMD_FUNC(cmd_h)
39+
40+
/* static hint functions*/
41+
CREATE_FUNC(cmd_a, _hint)
42+
CREATE_FUNC(cmd_b, _hint)
43+
CREATE_FUNC(cmd_c, _hint)
44+
CREATE_FUNC(cmd_d, _hint)
45+
CREATE_FUNC(cmd_e, _hint)
46+
CREATE_FUNC(cmd_f, _hint)
47+
CREATE_FUNC(cmd_g, _hint)
48+
CREATE_FUNC(cmd_h, _hint)
49+
50+
/* static glossary functions*/
51+
CREATE_FUNC(cmd_a, _glossary)
52+
CREATE_FUNC(cmd_b, _glossary)
53+
CREATE_FUNC(cmd_c, _glossary)
54+
CREATE_FUNC(cmd_d, _glossary)
55+
CREATE_FUNC(cmd_e, _glossary)
56+
CREATE_FUNC(cmd_f, _glossary)
57+
CREATE_FUNC(cmd_g, _glossary)
58+
CREATE_FUNC(cmd_h, _glossary)
59+
60+
/* command registration */
61+
ESP_COMMAND_REGISTER(cmd_a, group_1, GET_STR(cmd_a_help), cmd_a_func, NULL, cmd_a_hint, cmd_a_glossary);
62+
ESP_COMMAND_REGISTER(cmd_b, group_1, GET_STR(cmd_b_help), cmd_b_func, NULL, cmd_b_hint, cmd_b_glossary);
63+
ESP_COMMAND_REGISTER(cmd_c, group_2, GET_STR(cmd_c_help), cmd_c_func, NULL, cmd_c_hint, cmd_c_glossary);
64+
ESP_COMMAND_REGISTER(cmd_d, group_2, GET_STR(cmd_d_help), cmd_d_func, NULL, cmd_d_hint, cmd_d_glossary);
65+
ESP_COMMAND_REGISTER(cmd_e, group_3, GET_STR(cmd_e_help), cmd_e_func, NULL, cmd_e_hint, cmd_e_glossary);
66+
ESP_COMMAND_REGISTER(cmd_f, group_3, GET_STR(cmd_f_help), cmd_f_func, NULL, cmd_f_hint, cmd_f_glossary);
67+
ESP_COMMAND_REGISTER(cmd_g, group_4, GET_STR(cmd_g_help), cmd_g_func, NULL, cmd_g_hint, cmd_g_glossary);
68+
ESP_COMMAND_REGISTER(cmd_h, group_4, GET_STR(cmd_h_help), cmd_h_func, NULL, cmd_h_hint, cmd_h_glossary);
69+
70+
#ifdef __cplusplus
71+
}
72+
#endif
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <string.h>
8+
#include <stdio.h>
9+
#include <pthread.h>
10+
#include "unity.h"
11+
#include "esp_commands.h"
12+
#include "test_esp_commands_utils.h"
13+
14+
/*
15+
* IMPORTANT:
16+
* - 8 commands are created in test_esp_commands_utils.h (cmd_a - cmd_h)
17+
* - the commands are divided in 4 groups (group_1 - group_4)
18+
* - each group contains 2 commands.
19+
* - group_1 contains cmd_a and cmd_b,
20+
* [...]
21+
* - group_4 contains cmd_g and cmd_h
22+
*/
23+
24+
static void test_setup(void)
25+
{
26+
const esp_commands_config_t config = ESP_COMMANDS_CONFIG_DEFAULT();
27+
TEST_ASSERT_EQUAL(ESP_OK, esp_commands_update_config(&config));
28+
}
29+
30+
TEST_CASE("help command - called without command set", "[esp_commands]")
31+
{
32+
test_setup();
33+
34+
/* call esp_commands_execute to run help command with verbosity 0 */
35+
int cmd_ret = -1;
36+
TEST_ASSERT_EQUAL(ESP_OK, esp_commands_execute(NULL, "help -v 0", &cmd_ret));
37+
TEST_ASSERT_EQUAL(0, cmd_ret);
38+
39+
/* call esp_commands_execute to run help command with verbosity 1 */
40+
cmd_ret = -1;
41+
TEST_ASSERT_EQUAL(ESP_OK, esp_commands_execute(NULL, "help -v 1", &cmd_ret));
42+
TEST_ASSERT_EQUAL(0, cmd_ret);
43+
44+
/* call esp_commands_execute to run help command on a registered command */
45+
cmd_ret = -1;
46+
TEST_ASSERT_EQUAL(ESP_OK, esp_commands_execute(NULL, "help cmd_a -v 0", &cmd_ret));
47+
TEST_ASSERT_EQUAL(0, cmd_ret);
48+
TEST_ASSERT_EQUAL(ESP_OK, esp_commands_execute(NULL, "help cmd_a -v 1", &cmd_ret));
49+
TEST_ASSERT_EQUAL(0, cmd_ret);
50+
51+
/* call esp_commands_execute to run help command on an unregistered command */
52+
cmd_ret = -1;
53+
TEST_ASSERT_EQUAL(ESP_OK, esp_commands_execute(NULL, "help cmd_w", &cmd_ret));
54+
TEST_ASSERT_EQUAL(1, cmd_ret);
55+
56+
/* call esp_commands_execute to run help command on a registered command with wrong
57+
* verbosity syntax */
58+
cmd_ret = -1;
59+
TEST_ASSERT_EQUAL(ESP_OK, esp_commands_execute(NULL, "help cmd_a -v=1", &cmd_ret));
60+
TEST_ASSERT_EQUAL(1, cmd_ret);
61+
62+
/* call esp_commands_execute to run help command with too many command names */
63+
cmd_ret = -1;
64+
TEST_ASSERT_EQUAL(ESP_OK, esp_commands_execute(NULL, "help cmd_a cmd_b -v 1", &cmd_ret));
65+
TEST_ASSERT_EQUAL(1, cmd_ret);
66+
}
67+
68+
TEST_CASE("test command set error handling", "[esp_commands]")
69+
{
70+
test_setup();
71+
72+
/* create a command set with NULL passed as list of command id */
73+
TEST_ASSERT_NULL(esp_commands_create_cmd_set(NULL, 2, FIELD_ACCESSOR(group)));
74+
75+
/* create a command set with 0 as size of list of command id */
76+
const char *group_set_a[] = {"b", "group_4"};
77+
TEST_ASSERT_NULL(esp_commands_create_cmd_set(group_set_a, 0, FIELD_ACCESSOR(group)));
78+
79+
/* concatenate 2 NULL sets */
80+
TEST_ASSERT_NULL(esp_commands_concat_cmd_set(NULL, NULL));
81+
82+
/* redefinition of esp_command_set_t so we can access the fields
83+
* and test their values */
84+
typedef struct cmd_set {
85+
esp_command_t **cmd_ptr_set;
86+
size_t cmd_set_size;
87+
} cmd_set_t;
88+
89+
/* pass wrong command name in array, expect a non null command set handle with 0 items in it*/
90+
const char *group_set_b[] = {"group2", "group4"};
91+
esp_command_set_handle_t group_set_handle_b = esp_commands_create_cmd_set(group_set_b, 2, FIELD_ACCESSOR(group));
92+
cmd_set_t *cmd_set = (cmd_set_t *)group_set_handle_b;
93+
TEST_ASSERT_NOT_NULL(group_set_handle_b);
94+
TEST_ASSERT_NULL(cmd_set->cmd_ptr_set);
95+
TEST_ASSERT_EQUAL(0, cmd_set->cmd_set_size);
96+
97+
esp_commands_destroy_cmd_set(&group_set_handle_b);
98+
}
99+
100+
typedef struct cmd_test_sequence {
101+
const char *cmd_list[NB_OF_REGISTERED_CMD];
102+
int expected_ret_val[NB_OF_REGISTERED_CMD];
103+
} cmd_test_sequence_t;
104+
105+
static void run_cmd_test(esp_command_set_handle_t handle, const char **cmd_list, const int *expected_ret_val, size_t nb_cmds)
106+
{
107+
for (size_t i = 0; i < nb_cmds; i++) {
108+
int cmd_ret = -1;
109+
esp_err_t expected = expected_ret_val[i] == 0 ? ESP_OK : ESP_ERR_NOT_FOUND;
110+
TEST_ASSERT_EQUAL(expected, esp_commands_execute(handle, cmd_list[i], &cmd_ret));
111+
TEST_ASSERT_EQUAL(expected_ret_val[i], cmd_ret);
112+
}
113+
}
114+
115+
TEST_CASE("test command set by group, by name and concatenate", "[esp_commands]")
116+
{
117+
test_setup();
118+
119+
const char *cmd_list[] = {"cmd_a", "cmd_b", "cmd_c", "cmd_d", "cmd_e", "cmd_f", "cmd_g", "cmd_h"};
120+
const size_t nb_cmds = sizeof(cmd_list) / sizeof(cmd_list[0]);
121+
int expected_ret_val[nb_cmds];
122+
123+
/* create sets by group */
124+
const char *group_set_a[] = {"group_1", "group_3"};
125+
esp_command_set_handle_t handle_set_a = ESP_COMMANDS_CREATE_CMD_SET(group_set_a, FIELD_ACCESSOR(group));
126+
TEST_ASSERT_NOT_NULL(handle_set_a);
127+
128+
const char *group_set_b[] = {"group_2", "group_4"};
129+
esp_command_set_handle_t handle_set_b = ESP_COMMANDS_CREATE_CMD_SET(group_set_b, FIELD_ACCESSOR(group));
130+
TEST_ASSERT_NOT_NULL(handle_set_b);
131+
132+
/* test set_a by group */
133+
int tmp_ret[] = {0, 0, -1, -1, 0, 0, -1, -1};
134+
memcpy(expected_ret_val, tmp_ret, sizeof(tmp_ret));
135+
run_cmd_test(handle_set_a, cmd_list, expected_ret_val, nb_cmds);
136+
137+
/* test set_b by group */
138+
int tmp_ret_b[] = {-1, -1, 0, 0, -1, -1, 0, 0};
139+
memcpy(expected_ret_val, tmp_ret_b, sizeof(tmp_ret_b));
140+
run_cmd_test(handle_set_b, cmd_list, expected_ret_val, nb_cmds);
141+
142+
/* test help command */
143+
int cmd_ret;
144+
TEST_ASSERT_EQUAL(ESP_OK, esp_commands_execute(handle_set_a, "help", &cmd_ret));
145+
TEST_ASSERT_EQUAL(0, cmd_ret);
146+
TEST_ASSERT_EQUAL(ESP_OK, esp_commands_execute(handle_set_b, "help", &cmd_ret));
147+
TEST_ASSERT_EQUAL(0, cmd_ret);
148+
149+
/* destroy sets */
150+
esp_commands_destroy_cmd_set(&handle_set_a);
151+
esp_commands_destroy_cmd_set(&handle_set_b);
152+
153+
/* create sets by name */
154+
const char *cmd_name_set_a[] = {"cmd_a", "cmd_b", "cmd_c"};
155+
handle_set_a = esp_commands_create_cmd_set(cmd_name_set_a, 3, FIELD_ACCESSOR(name));
156+
TEST_ASSERT_NOT_NULL(handle_set_a);
157+
158+
const char *cmd_name_set_b[] = {"cmd_f", "cmd_g", "cmd_h"};
159+
handle_set_b = esp_commands_create_cmd_set(cmd_name_set_b, 3, FIELD_ACCESSOR(name));
160+
TEST_ASSERT_NOT_NULL(handle_set_b);
161+
162+
int tmp_ret2[] = {0, 0, 0, -1, -1, -1, -1, -1};
163+
memcpy(expected_ret_val, tmp_ret2, sizeof(tmp_ret2));
164+
run_cmd_test(handle_set_a, cmd_list, expected_ret_val, nb_cmds);
165+
166+
int tmp_ret3[] = {-1, -1, -1, -1, -1, 0, 0, 0};
167+
memcpy(expected_ret_val, tmp_ret3, sizeof(tmp_ret3));
168+
run_cmd_test(handle_set_b, cmd_list, expected_ret_val, nb_cmds);
169+
170+
/* concatenate sets */
171+
esp_command_set_handle_t handle_set_c = esp_commands_concat_cmd_set(handle_set_a, handle_set_b);
172+
TEST_ASSERT_NOT_NULL(handle_set_c);
173+
174+
int tmp_ret4[] = {0, 0, 0, -1, -1, 0, 0, 0};
175+
memcpy(expected_ret_val, tmp_ret4, sizeof(tmp_ret4));
176+
run_cmd_test(handle_set_c, cmd_list, expected_ret_val, nb_cmds);
177+
178+
esp_commands_destroy_cmd_set(&handle_set_c);
179+
}
180+
181+
static int dummy_cmd_func(void *context, int argc, char **argv)
182+
{
183+
printf("dynamic command called\n");
184+
return 0; // always return success
185+
}
186+
187+
TEST_CASE("test dynamic command registration and execution", "[esp_commands]")
188+
{
189+
test_setup();
190+
191+
const char *cmd_list[] = {"cmd_1", "cmd_2", "cmd_3", "cmd_4", "cmd_5", "cmd_6", "cmd_7", "cmd_8"};
192+
const size_t nb_cmds = sizeof(cmd_list) / sizeof(cmd_list[0]);
193+
int expected_ret_val[nb_cmds];
194+
195+
/* dynamically register commands */
196+
for (size_t i = 0; i < nb_cmds; i++) {
197+
esp_command_t cmd = {
198+
.name = cmd_list[i],
199+
.group = (i % 2 == 0) ? "group_a" : "group_b",
200+
.help = "dummy help",
201+
.func = dummy_cmd_func, // implement a simple dummy function returning i%2
202+
.func_ctx = NULL,
203+
.hint_cb = NULL,
204+
.glossary_cb = NULL
205+
};
206+
TEST_ASSERT_EQUAL(ESP_OK, esp_commands_register_cmd(&cmd));
207+
}
208+
209+
/* test execution by group_a */
210+
const char *group_set[] = {"group_a"};
211+
esp_command_set_handle_t handle_group1 = ESP_COMMANDS_CREATE_CMD_SET(group_set, FIELD_ACCESSOR(group));
212+
TEST_ASSERT_NOT_NULL(handle_group1);
213+
214+
int tmp_ret[] = {0, -1, 0, -1, 0, -1, 0, -1};
215+
memcpy(expected_ret_val, tmp_ret, sizeof(tmp_ret));
216+
run_cmd_test(handle_group1, cmd_list, expected_ret_val, nb_cmds);
217+
218+
/* test execution by command name */
219+
const char *cmd_name_set[] = {"cmd_1", "cmd_2", "cmd_3"};
220+
esp_command_set_handle_t handle_name_set = esp_commands_create_cmd_set(cmd_name_set, 3, FIELD_ACCESSOR(name));
221+
TEST_ASSERT_NOT_NULL(handle_name_set);
222+
223+
int tmp_ret2[] = {0, 0, 0, -1, -1, -1, -1, -1};
224+
memcpy(expected_ret_val, tmp_ret2, sizeof(tmp_ret2));
225+
run_cmd_test(handle_name_set, cmd_list, expected_ret_val, nb_cmds);
226+
227+
/* unregister dynamically registered commands */
228+
for (size_t i = 0; i < nb_cmds; i++) {
229+
TEST_ASSERT_EQUAL(ESP_OK, esp_commands_unregister_cmd(cmd_list[i]));
230+
}
231+
232+
esp_commands_destroy_cmd_set(&handle_group1);
233+
esp_commands_destroy_cmd_set(&handle_name_set);
234+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include "unity.h"
8+
#include "unity_test_runner.h"
9+
#include "esp_heap_caps.h"
10+
#include "unity_test_utils_memory.h"
11+
12+
void setUp(void)
13+
{
14+
unity_utils_record_free_mem();
15+
}
16+
17+
void tearDown(void)
18+
{
19+
unity_utils_evaluate_leaks_direct(0);
20+
}
21+
22+
void app_main(void)
23+
{
24+
printf("Running esp_commands component tests\n");
25+
unity_run_menu();
26+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import pytest
2+
3+
@pytest.mark.generic
4+
def test_esp_commands(dut) -> None:
5+
dut.run_all_single_board_cases()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
CONFIG_ESP_TASK_WDT_EN=n

0 commit comments

Comments
 (0)