Просмотр исходного кода

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
Jakob Hasse 2 лет назад
Родитель
Сommit
0b595f3c77

+ 69 - 22
components/console/commands.c

@@ -215,41 +215,88 @@ esp_err_t esp_console_run(const char *cmdline, int *cmd_ret)
     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)
 {
+    int nerrors = arg_parse(argc, argv, (void **) &help_args);
+
+    if (nerrors != 0) {
+        arg_print_errors(stderr, help_args.end, argv[0]);
+        return 1;
+    }
+
     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);
         }
-        /* 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");
+        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]);
         }
-        printf("\n");
     }
-    return 0;
+
+    return ret_value;
 }
 
 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 = {
         .command = "help",
-        .help = "Print the list of registered commands",
-        .func = &help_command
+        .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
     };
     return esp_console_cmd_register(&command);
 }

+ 3 - 1
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

+ 20 - 6
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 */

+ 64 - 9
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+\[<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.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)