Merge branch 'contrib/github_pr_11945' into 'master'

Added Help command parameter for displaying only a specific command (GitHub PR)

Closes IDFGH-10725

See merge request espressif/esp-idf!25329
This commit is contained in:
Jakob Hasse
2023-08-16 11:16:54 +08:00
4 changed files with 159 additions and 41 deletions

View File

@ -215,41 +215,88 @@ esp_err_t esp_console_run(const char *cmdline, int *cmd_ret)
return ESP_OK; return ESP_OK;
} }
static struct {
struct arg_str *help_cmd;
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) static int help_command(int argc, char **argv)
{ {
cmd_item_t *it; int nerrors = arg_parse(argc, argv, (void **) &help_args);
/* Print summary of each command */ if (nerrors != 0) {
SLIST_FOREACH(it, &s_cmd_list, next) { arg_print_errors(stderr, help_args.end, argv[0]);
if (it->help == NULL) { return 1;
continue;
}
/* 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");
} }
return 0;
cmd_item_t *it;
int ret_value = 1;
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);
}
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;
}
}
/* 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]);
}
}
return ret_value;
} }
esp_err_t esp_console_register_help_command(void) esp_err_t esp_console_register_help_command(void)
{ {
help_args.help_cmd = arg_str0(NULL, NULL, "<string>", "Name of command");
help_args.end = arg_end(1);
esp_console_cmd_t command = { esp_console_cmd_t command = {
.command = "help", .command = "help",
.help = "Print the list of registered commands", .help = "Print the summary of all registered commands if no arguments "
.func = &help_command "are given, otherwise print summary of given command.",
.func = &help_command,
.argtable = &help_args
}; };
return esp_console_cmd_register(&command); return esp_console_cmd_register(&command);
} }

View File

@ -275,7 +275,9 @@ const char *esp_console_get_hint(const char *buf, int *color, int *bold);
* @brief Register a 'help' command * @brief Register a 'help' command
* *
* Default 'help' command prints the list of registered commands along with * 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 * @return
* - ESP_OK on success * - ESP_OK on success

View File

@ -48,6 +48,12 @@ static int do_cmd_quit(int argc, char **argv)
return 0; 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 // Enter "quit" to exit REPL environment
/* Marked as ignore since it cannot run as a normal unity test case /* Marked as ignore since it cannot run as a normal unity test case
ran separately in test_console_repl */ 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(); 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_new_repl_uart(&uart_config, &repl_config, &s_repl));
esp_console_cmd_t cmd = { TEST_ESP_OK(esp_console_cmd_register(&s_quit_cmd));
.command = "quit",
.help = "Quit REPL environment",
.func = &do_cmd_quit
};
TEST_ESP_OK(esp_console_cmd_register(&cmd));
TEST_ESP_OK(esp_console_start_repl(s_repl)); TEST_ESP_OK(esp_console_start_repl(s_repl));
vTaskDelay(pdMS_TO_TICKS(2000)); 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_CASE("esp console init/deinit test, minimal config", "[console]")
{ {
/* Test with minimal init config */ /* Test with minimal init config */

View File

@ -5,6 +5,44 @@ import pytest
from pytest_embedded import Dut 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+\[<string>\]', 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'<string>\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.generic
@pytest.mark.supported_targets @pytest.mark.supported_targets
def test_console(dut: Dut) -> None: def test_console(dut: Dut) -> None:
@ -14,13 +52,19 @@ def test_console(dut: Dut) -> None:
@pytest.mark.generic @pytest.mark.generic
@pytest.mark.supported_targets @pytest.mark.supported_targets
def test_console_repl(dut: Dut) -> None: def test_console_repl(dut: Dut) -> None:
dut.expect_exact('Press ENTER to see the list of tests') do_test_quit(dut)
dut.write('"esp console repl test"')
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 @pytest.mark.host_test
@ -35,7 +79,18 @@ def test_console_qemu(dut: Dut) -> None:
@pytest.mark.qemu @pytest.mark.qemu
@pytest.mark.esp32 @pytest.mark.esp32
def test_console_repl_qemu(dut: Dut) -> None: def test_console_repl_qemu(dut: Dut) -> None:
dut.expect_exact('Press ENTER to see the list of tests') do_test_quit(dut)
dut.confirm_write('"esp console repl test"', expect_pattern='esp>')
dut.confirm_write('quit', expect_pattern='ByeBye')
dut.expect_unity_test_output() @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)