diff --git a/components/console/commands.c b/components/console/commands.c index e7abf297cd..5e63db87b4 100644 --- a/components/console/commands.c +++ b/components/console/commands.c @@ -220,6 +220,26 @@ static struct { struct arg_end *end; } help_args; +static void print_arg_help(cmd_item_t *it) +{ + /* First line: command name and hint + * Pad all the hints to the same column + */ + const char *hint = (it->hint) ? it->hint : ""; + printf("%-s %s\n", it->command, hint); + /* Second line: print help. + * Argtable has a nice helper function for this which does line + * wrapping. + */ + printf(" "); // arg_print_formatted does not indent the first line + arg_print_formatted(stdout, 2, 78, it->help); + /* Finally, print the list of arguments */ + if (it->argtable) { + arg_print_glossary(stdout, (void **) it->argtable, " %12s %s\n"); + } + printf("\n"); +} + static int help_command(int argc, char **argv) { int nerrors = arg_parse(argc, argv, (void **) &help_args); @@ -230,34 +250,40 @@ static int help_command(int argc, char **argv) } cmd_item_t *it; + int ret_value = 1; - /* Print summary of each command */ - SLIST_FOREACH(it, &s_cmd_list, next) { - if (it->help == NULL) { - continue; + if (help_args.help_cmd->count == 0) { + /* Print summary of each command */ + SLIST_FOREACH(it, &s_cmd_list, next) { + if (it->help == NULL) { + continue; + } + print_arg_help(it); } - if (strlen(help_args.help_cmd->sval[0]) > 0 && - strcmp(help_args.help_cmd->sval[0], it->command) != 0) { - continue; + ret_value = 0; + } else { + /* Print summary of given command */ + bool found_command = false; + SLIST_FOREACH(it, &s_cmd_list, next) { + if (it->help == NULL) { + continue; + } + if (strcmp(help_args.help_cmd->sval[0], it->command) == 0) { + print_arg_help(it); + found_command = true; + ret_value = 0; + break; + } } - /* First line: command name and hint - * Pad all the hints to the same column - */ - const char *hint = (it->hint) ? it->hint : ""; - printf("%-s %s\n", it->command, hint); - /* Second line: print help. - * Argtable has a nice helper function for this which does line - * wrapping. - */ - printf(" "); // arg_print_formatted does not indent the first line - arg_print_formatted(stdout, 2, 78, it->help); - /* Finally, print the list of arguments */ - if (it->argtable) { - arg_print_glossary(stdout, (void **) it->argtable, " %12s %s\n"); + + /* If given command has not been found, print error message*/ + if (!found_command) { + printf("help: Unrecognized option '%s'. Please use correct command as argument " + "or type 'help' only to print help for all commands\n", help_args.help_cmd->sval[0]); } - printf("\n"); } - return 0; + + return ret_value; } esp_err_t esp_console_register_help_command(void) @@ -267,7 +293,8 @@ esp_err_t esp_console_register_help_command(void) esp_console_cmd_t command = { .command = "help", - .help = "Print the list of registered commands", + .help = "Print the summary of all registered commands if no arguments " + "are given, otherwise print summary of given command.", .func = &help_command, .argtable = &help_args }; diff --git a/components/console/esp_console.h b/components/console/esp_console.h index 7ad8155a6b..304dc423b1 100644 --- a/components/console/esp_console.h +++ b/components/console/esp_console.h @@ -275,7 +275,9 @@ const char *esp_console_get_hint(const char *buf, int *color, int *bold); * @brief Register a 'help' command * * Default 'help' command prints the list of registered commands along with - * hints and help strings. + * hints and help strings if no additional argument is given. If an additional + * argument is given, the help command will look for a command with the same + * name and only print the hints and help strings of that command. * * @return * - ESP_OK on success diff --git a/components/console/test_apps/console/main/test_console.c b/components/console/test_apps/console/main/test_console.c index bcb87e92bf..db7685027a 100644 --- a/components/console/test_apps/console/main/test_console.c +++ b/components/console/test_apps/console/main/test_console.c @@ -48,6 +48,12 @@ static int do_cmd_quit(int argc, char **argv) return 0; } +static esp_console_cmd_t s_quit_cmd = { + .command = "quit", + .help = "Quit REPL environment", + .func = &do_cmd_quit +}; + // Enter "quit" to exit REPL environment /* Marked as ignore since it cannot run as a normal unity test case ran separately in test_console_repl */ @@ -57,17 +63,25 @@ TEST_CASE("esp console repl test", "[console][ignore]") 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)); - esp_console_cmd_t cmd = { - .command = "quit", - .help = "Quit REPL environment", - .func = &do_cmd_quit - }; - TEST_ESP_OK(esp_console_cmd_register(&cmd)); + TEST_ESP_OK(esp_console_cmd_register(&s_quit_cmd)); TEST_ESP_OK(esp_console_start_repl(s_repl)); vTaskDelay(pdMS_TO_TICKS(2000)); } +TEST_CASE("esp console help command", "[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(&s_quit_cmd)); + TEST_ESP_OK(esp_console_register_help_command()); + + TEST_ESP_OK(esp_console_start_repl(s_repl)); + vTaskDelay(pdMS_TO_TICKS(5000)); +} + TEST_CASE("esp console init/deinit test, minimal config", "[console]") { /* Test with minimal init config */ diff --git a/components/console/test_apps/console/pytest_console.py b/components/console/test_apps/console/pytest_console.py index 796fa9a8e2..0dfdd13526 100644 --- a/components/console/test_apps/console/pytest_console.py +++ b/components/console/test_apps/console/pytest_console.py @@ -5,6 +5,44 @@ import pytest from pytest_embedded import Dut +def do_test_quit(dut: Dut) -> None: + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('"esp console repl test"') + + dut.expect_exact('esp>', timeout=5) + dut.write('quit') + + dut.expect_exact('ByeBye', timeout=5) + + +def do_test_help_generic(dut: Dut) -> None: + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('"esp console help command"') + + dut.expect_exact('esp>', timeout=5) + dut.write('help') + + dut.expect_exact('quit', timeout=5) + dut.expect_exact('Quit REPL environment', timeout=5) + + dut.expect(r'help\s+\[\]', timeout=5) + + # Note: repl seems to do the line breaks by itself, this needs to be adjusted if repl changes its line width + dut.expect_exact('Print the summary of all registered commands if no arguments are given,', timeout=5) + dut.expect_exact('otherwise print summary of given command.', timeout=5) + dut.expect(r'\s+Name of command\s+esp>', timeout=5) + + +def do_test_help_quit(dut: Dut) -> None: + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('"esp console help command"') + + dut.expect_exact('esp>', timeout=5) + dut.write('help quit') + + dut.expect(r'quit\s+Quit REPL environment\s+esp>', timeout=5) + + @pytest.mark.generic @pytest.mark.supported_targets def test_console(dut: Dut) -> None: @@ -14,13 +52,19 @@ def test_console(dut: Dut) -> None: @pytest.mark.generic @pytest.mark.supported_targets def test_console_repl(dut: Dut) -> None: - dut.expect_exact('Press ENTER to see the list of tests') - dut.write('"esp console repl test"') + do_test_quit(dut) - dut.expect_exact('esp>') - dut.write('quit') - dut.expect_exact('ByeBye') +@pytest.mark.generic +@pytest.mark.supported_targets +def test_console_help_generic(dut: Dut) -> None: + do_test_help_generic(dut) + + +@pytest.mark.generic +@pytest.mark.supported_targets +def test_console_help_quit(dut: Dut) -> None: + do_test_help_quit(dut) @pytest.mark.host_test @@ -35,7 +79,18 @@ def test_console_qemu(dut: Dut) -> None: @pytest.mark.qemu @pytest.mark.esp32 def test_console_repl_qemu(dut: Dut) -> None: - dut.expect_exact('Press ENTER to see the list of tests') - dut.confirm_write('"esp console repl test"', expect_pattern='esp>') - dut.confirm_write('quit', expect_pattern='ByeBye') - dut.expect_unity_test_output() + do_test_quit(dut) + + +@pytest.mark.host_test +@pytest.mark.qemu +@pytest.mark.esp32 +def test_console_help_generic_qemu(dut: Dut) -> None: + do_test_help_generic(dut) + + +@pytest.mark.host_test +@pytest.mark.qemu +@pytest.mark.esp32 +def test_console_help_quit_qemu(dut: Dut) -> None: + do_test_help_quit(dut)