From 9a5ca8fac661a9a55958aa04dadddc057a4e3583 Mon Sep 17 00:00:00 2001 From: Xiaoyu Liu Date: Tue, 21 May 2024 16:37:27 +0800 Subject: [PATCH] feat(console): Add an API function to de-register the specified console command --- components/console/commands.c | 20 +++++++- components/console/esp_console.h | 10 ++++ .../test_apps/console/main/test_console.c | 41 ++++++++++++++++ .../test_apps/console/pytest_console.py | 48 +++++++++++++++++++ 4 files changed, 117 insertions(+), 2 deletions(-) diff --git a/components/console/commands.c b/components/console/commands.c index 4ac0725dc7..a07e743e12 100644 --- a/components/console/commands.c +++ b/components/console/commands.c @@ -94,6 +94,23 @@ esp_err_t esp_console_deinit(void) return ESP_OK; } +void esp_console_rm_item_free_hint(cmd_item_t *item) +{ + SLIST_REMOVE(&s_cmd_list, item, cmd_item_, next); + free(item->hint); +} + +esp_err_t esp_console_cmd_deregister(const char *cmd_name) +{ + cmd_item_t *item = (cmd_item_t *)find_command_by_name(cmd_name); + if (item == NULL) { + return ESP_ERR_INVALID_ARG; + } + esp_console_rm_item_free_hint(item); + heap_caps_free(item); + return ESP_OK; +} + esp_err_t esp_console_cmd_register(const esp_console_cmd_t *cmd) { cmd_item_t *item = NULL; @@ -116,8 +133,7 @@ esp_err_t esp_console_cmd_register(const esp_console_cmd_t *cmd) } } else { // remove from list and free the old hint, because we will alloc new hint for the command - SLIST_REMOVE(&s_cmd_list, item, cmd_item_, next); - free(item->hint); + esp_console_rm_item_free_hint(item); } item->command = cmd->command; item->help = cmd->help; diff --git a/components/console/esp_console.h b/components/console/esp_console.h index 6fd3b81424..0f0891f044 100644 --- a/components/console/esp_console.h +++ b/components/console/esp_console.h @@ -235,6 +235,16 @@ typedef struct { */ esp_err_t esp_console_cmd_register(const esp_console_cmd_t *cmd); +/** + * @brief Deregister console command + * @param cmd_name Name of the command to be deregistered. Must not be NULL, must not contain spaces. + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if command is not registered + */ +esp_err_t esp_console_cmd_deregister(const char *cmd_name); + /** * @brief Run command line * @param cmdline command line (command name followed by a number of arguments) diff --git a/components/console/test_apps/console/main/test_console.c b/components/console/test_apps/console/main/test_console.c index 36e22553b8..1e56db049c 100644 --- a/components/console/test_apps/console/main/test_console.c +++ b/components/console/test_apps/console/main/test_console.c @@ -307,3 +307,44 @@ TEST_CASE("esp console help command - --verbose sub command", "[console][ignore] TEST_ESP_OK(esp_console_start_repl(s_repl)); vTaskDelay(pdMS_TO_TICKS(5000)); } + +TEST_CASE("esp console deregister commands", "[console][ignore]") +{ + esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT(); + esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); + TEST_ESP_OK(esp_console_new_repl_uart(&uart_config, &repl_config, &s_repl)); + + TEST_ESP_OK(esp_console_cmd_register(&cmd_a)); + TEST_ESP_OK(esp_console_cmd_register(&s_quit_cmd)); + TEST_ESP_OK(esp_console_register_help_command()); + TEST_ESP_OK(esp_console_cmd_register(&cmd_z)); + // deregister a non-existing cmd, should return "ESP_ERR_INVALID_ARG" + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_console_cmd_deregister("abcdefg")); + // deregister cmd_a + TEST_ESP_OK(esp_console_cmd_deregister(cmd_a.command)); + TEST_ESP_OK(esp_console_start_repl(s_repl)); + vTaskDelay(pdMS_TO_TICKS(5000)); +} + +TEST_CASE("esp console re-register commands", "[console][ignore]") +{ + esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT(); + esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); + TEST_ESP_OK(esp_console_new_repl_uart(&uart_config, &repl_config, &s_repl)); + + TEST_ESP_OK(esp_console_cmd_register(&cmd_a)); + TEST_ESP_OK(esp_console_cmd_register(&s_quit_cmd)); + TEST_ESP_OK(esp_console_cmd_register(&cmd_z)); + TEST_ESP_OK(esp_console_register_help_command()); + + // deregister cmd_z and cmd_a + TEST_ESP_OK(esp_console_cmd_deregister(cmd_z.command)); + TEST_ESP_OK(esp_console_cmd_deregister(cmd_a.command)); + + // re-register cmd_z and cmd_a + TEST_ESP_OK(esp_console_cmd_register(&cmd_z)); + TEST_ESP_OK(esp_console_cmd_register(&cmd_a)); + + TEST_ESP_OK(esp_console_start_repl(s_repl)); + vTaskDelay(pdMS_TO_TICKS(5000)); +} diff --git a/components/console/test_apps/console/pytest_console.py b/components/console/test_apps/console/pytest_console.py index be79490c1c..a2e99fddd7 100644 --- a/components/console/test_apps/console/pytest_console.py +++ b/components/console/test_apps/console/pytest_console.py @@ -261,3 +261,51 @@ def test_console_help_verbose_subcommand(dut: Dut, test_on: str) -> None: # verify help --verbose=1 subcommand dut.write('help --verbose=1') dut.expect_exact(help_verbose_info) + + +@pytest.mark.parametrize( + 'config', [ + pytest.param('defaults'), + ] +) +@pytest.mark.parametrize( + 'test_on', [ + pytest.param('host', marks=[pytest.mark.linux, pytest.mark.host_test]), + pytest.param('target', marks=[pytest.mark.esp32, pytest.mark.esp32c3, pytest.mark.generic]), + pytest.param('qemu', marks=[pytest.mark.esp32, pytest.mark.host_test, pytest.mark.qemu]), + ] +) +def test_console_help_deregister(dut: Dut, test_on: str) -> None: + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('"esp console deregister commands"') + + dut.expect_exact('esp>', timeout=5) + dut.write('help') + + # in the test sequence, command a is registered before registering command z, then + # command a is deregistered; therefore, console shall not print command a's description + cmd_a_description, cmd_z_description = 'should appear first in help', 'should appear last in help' + dut.expect_exact(cmd_z_description, not_matching=cmd_a_description) + + +@pytest.mark.parametrize( + 'config', [ + pytest.param('defaults'), + ] +) +@pytest.mark.parametrize( + 'test_on', [ + pytest.param('host', marks=[pytest.mark.linux, pytest.mark.host_test]), + pytest.param('target', marks=[pytest.mark.esp32, pytest.mark.esp32c3, pytest.mark.generic]), + pytest.param('qemu', marks=[pytest.mark.esp32, pytest.mark.host_test, pytest.mark.qemu]), + ] +) +def test_console_help_re_register(dut: Dut, test_on: str) -> None: + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('"esp console re-register commands"') + + dut.expect_exact('esp>', timeout=5) + dut.write('help') + + dut.expect_exact('should appear last in help', timeout=5) + dut.expect_exact('should appear first in help', timeout=5)