Skip to content

Commit

Permalink
Merge pull request RIOT-OS#20964 from maribu/tests/rust_libs/improve-…
Browse files Browse the repository at this point in the history
…test-robustness

sys/shell: cmds_json builtin command
  • Loading branch information
maribu authored Nov 13, 2024
2 parents 5f661f4 + b605347 commit b9ba3ee
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 23 deletions.
1 change: 1 addition & 0 deletions makefiles/pseudomodules.inc.mk
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,7 @@ PSEUDOMODULES += servo_timer
PSEUDOMODULES += servo_saul
## @}

PSEUDOMODULES += shell_builtin_cmd_help_json
PSEUDOMODULES += shell_cmd_app_metadata
PSEUDOMODULES += shell_cmd_at30tse75x
PSEUDOMODULES += shell_cmd_benchmark_udp
Expand Down
19 changes: 19 additions & 0 deletions sys/include/shell.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,25 @@
* there is no expectation of security of the system when an attacker gains
* access to the shell.
*
* ## Usage
*
* Enable the `shell` module e.g. by adding the following snippet to your
* applications `Makefile`.
*
* ```
* USEMODULE += shell
* ```
*
* And run the shell using @ref shell_run_forever e.g. from the `main` thread
* after everything is set up. This call will never return.
*
* ## Builtin Commands
*
* The commands `help` and `help_json` are builtins that print the list of
* available commands: The former prints a human readable table and is always
* available, the latter requires module `shell_builtin_cmd_help_json` to be
* used and will give the same info machine readable.
*
* @{
*
* @file
Expand Down
36 changes: 36 additions & 0 deletions sys/shell/shell.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,38 @@ static shell_command_handler_t find_handler(
return handler;
}

static void print_commands_json(const shell_command_t *cmd_list)
{
bool first = true;

printf("{\"cmds\": [");

if (cmd_list) {
for (const shell_command_t *entry = cmd_list; entry->name != NULL; entry++) {
if (first) {
first = false;
}
else {
printf(", ");
}
printf("{\"cmd\": \"%s\", \"desc\": \"%s\"}", entry->name, entry->desc);
}
}

unsigned n = XFA_LEN(shell_command_xfa_t*, shell_commands_xfa);
for (unsigned i = 0; i < n; i++) {
if (first) {
first = false;
}
else {
printf(", ");
}
const volatile shell_command_xfa_t *entry = shell_commands_xfa[i];
printf("{\"cmd\": \"%s\", \"desc\": \"%s\"}", entry->name, entry->desc);
}
puts("]}");
}

static void print_commands(const shell_command_t *entry)
{
for (; entry->name != NULL; entry++) {
Expand Down Expand Up @@ -343,6 +375,10 @@ int shell_handle_input_line(const shell_command_t *command_list, char *line)
print_help(command_list);
return 0;
}
else if (IS_USED(MODULE_SHELL_BUILTIN_CMD_HELP_JSON)
&& !strcmp("help_json", argv[0])) {
print_commands_json(command_list);
}
else {
printf("shell: command not found: %s\n", argv[0]);
}
Expand Down
1 change: 1 addition & 0 deletions tests/rust_libs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ include ../Makefile.tests_common

USEMODULE += shell
USEMODULE += shell_democommands
USEMODULE += shell_builtin_cmd_help_json # for automated testing
USEMODULE += ztimer_msec

FEATURES_REQUIRED += rust_target
Expand Down
53 changes: 30 additions & 23 deletions tests/rust_libs/tests/01-run.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,25 @@
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.

import json
import sys
from testrunner import run


EXPECTED_HELP = (
'Command Description',
'---------------------------------------',
'bufsize Get the shell\'s buffer size',
'start_test starts a test',
'end_test ends a test',
'echo prints the input command',
'empty print nothing on command',
'hello_world Print a greeting',
'xfa_test1 xfa test command 1',
'xfa_test2 xfa test command 2',
)
EXPECTED_CMDS = {
'bufsize': 'Get the shell\'s buffer size',
'start_test': 'starts a test',
'end_test': 'ends a test',
'echo': 'prints the input command',
'empty': 'print nothing on command',
'periodic': 'periodically print command',
'hello_world': 'Print a greeting',
'xfa_test1': 'xfa test command 1',
'xfa_test2': 'xfa test command 2',
}

PROMPT = '> '

CMDS = (
('start_test', '[TEST_START]'),

# test default commands
('help', EXPECTED_HELP),

('end_test', '[TEST_END]'),
)

CMDS_REGEX = {'ps.rs'}


Expand All @@ -49,10 +40,26 @@ def check_cmd(child, cmd, expected):
child.expect_exact(line)


def check_cmd_list(child):
child.expect(PROMPT)
child.sendline('help_json')
child.expect(r"(\{[^\n\r]*\})\r\n")
cmdlist = json.loads(child.match.group(1))["cmds"]
cmds = set(EXPECTED_CMDS)
for item in cmdlist:
assert item['cmd'] in EXPECTED_CMDS, f"command {item['cmd']} not expected"
assert item['cmd'] in cmds, f"command {item['cmd']} listed twice"
assert item['desc'] == EXPECTED_CMDS[item['cmd']], f"description of {item['cmd']} not expected"
cmds.remove(item['cmd'])

assert len(cmds) == 0, f"commands {cmds} missing"


def testfunc(child):
# loop other defined commands and expected output
for cmd, expected in CMDS:
check_cmd(child, cmd, expected)
check_cmd(child, 'start_test', '[TEST_START]')
check_cmd_list(child)
check_cmd(child, 'end_test', '[TEST_END]')


if __name__ == "__main__":
Expand Down

0 comments on commit b9ba3ee

Please sign in to comment.