1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

Merge pull request #21036 from maribu/tests/sys/shell/fix-test

tests/sys/shell: make test script more robust
This commit is contained in:
Teufelchen 2024-11-27 11:25:09 +00:00 committed by GitHub
commit 5fa8f65c44
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 88 additions and 20 deletions

View File

@ -6,6 +6,9 @@ USEMODULE += shell_cmds_default
USEMODULE += ps USEMODULE += ps
USEMODULE += ztimer_msec USEMODULE += ztimer_msec
# JSON help is needed by test script
USEMODULE += shell_builtin_cmd_help_json
# Use a terminal that does not introduce extra characters into the stream. # Use a terminal that does not introduce extra characters into the stream.
RIOT_TERMINAL ?= socat RIOT_TERMINAL ?= socat

View File

@ -6,28 +6,30 @@
# General Public License v2.1. See the file LICENSE in the top level # General Public License v2.1. See the file LICENSE in the top level
# directory for more details. # directory for more details.
import sys import json
import os import os
import sys
from testrunner import run from testrunner import run
EXPECTED_HELP = ( # This is the minimum subset of commands expected to be available on all
'Command Description', # boards. The test will still pass if additional commands are present, as
'---------------------------------------', # `shell_cmds_default` may pull in board specific commands.
'bufsize Get the shell\'s buffer size', EXPECTED_CMDS = {
'start_test starts a test', 'bufsize': 'Get the shell\'s buffer size',
'end_test ends a test', 'start_test': 'starts a test',
'echo prints the input command', 'end_test': 'ends a test',
'empty print nothing on command', 'echo': 'prints the input command',
'periodic periodically print command', 'empty': 'print nothing on command',
'app_metadata Returns application metadata', 'periodic': 'periodically print command',
'pm interact with layered PM subsystem', 'app_metadata': 'Returns application metadata',
'ps Prints information about running threads.', 'pm': 'interact with layered PM subsystem',
'reboot Reboot the node', 'ps': 'Prints information about running threads.',
'version Prints current RIOT_VERSION', 'reboot': 'Reboot the node',
'xfa_test1 xfa test command 1', 'version': 'Prints current RIOT_VERSION',
'xfa_test2 xfa test command 2' 'xfa_test1': 'xfa test command 1',
) 'xfa_test2': 'xfa test command 2',
}
EXPECTED_PS = ( EXPECTED_PS = (
'\tpid | state Q | pri', '\tpid | state Q | pri',
@ -83,7 +85,7 @@ CMDS = (
('echo escaped\\ space', '"echo""escaped space"'), ('echo escaped\\ space', '"echo""escaped space"'),
('echo escape within \'\\s\\i\\n\\g\\l\\e\\q\\u\\o\\t\\e\'', '"echo""escape""within""singlequote"'), ('echo escape within \'\\s\\i\\n\\g\\l\\e\\q\\u\\o\\t\\e\'', '"echo""escape""within""singlequote"'),
('echo escape within "\\d\\o\\u\\b\\l\\e\\q\\u\\o\\t\\e"', '"echo""escape""within""doublequote"'), ('echo escape within "\\d\\o\\u\\b\\l\\e\\q\\u\\o\\t\\e"', '"echo""escape""within""doublequote"'),
("""echo "t\e st" "\\"" '\\'' a\ b""", '"echo""te st"""""\'""a b"'), # noqa: W605 ("""echo "t\\e st" "\\"" '\\'' a\ b""", '"echo""te st"""""\'""a b"'), # noqa: W605
# test correct quoting # test correct quoting
('echo "hello"world', '"echo""helloworld"'), ('echo "hello"world', '"echo""helloworld"'),
@ -103,7 +105,6 @@ CMDS = (
# test default commands # test default commands
('ps', EXPECTED_PS), ('ps', EXPECTED_PS),
('help', EXPECTED_HELP),
# test commands added to shell_commands_xfa # test commands added to shell_commands_xfa
('xfa_test1', '[XFA TEST 1 OK]'), ('xfa_test1', '[XFA TEST 1 OK]'),
@ -151,6 +152,68 @@ def check_cmd(child, cmd, expected):
child.expect_exact(line) child.expect_exact(line)
def check_help(child):
"""
Runs the `help_json` and `help` command to check if the list of commands
and descriptions match and contain a list of expected commands.
"""
# Run help_json to get the list of commands present
child.expect(PROMPT)
child.sendline('help_json')
# expect JSON object as response the covers the whole line
child.expect(r"(\{[^\n\r]*\})\r\n")
# use a set to track which expected commands were already covered
cmds_expected = set(EXPECTED_CMDS)
# record actually present commands (which may be more than the expected
# ones) and their descriptions in here
cmds_actual = set()
desc_actual = {}
# parse the commands and iterate over the list
cmdlist = json.loads(child.match.group(1))["cmds"]
for item in cmdlist:
# for expected commands, validate the description and ensure they
# are listed exactly once
if item['cmd'] in EXPECTED_CMDS:
assert item['cmd'] in cmds_expected, f"command {item['cmd']} listed twice"
assert item['desc'] == EXPECTED_CMDS[item['cmd']], f"description of {item['cmd']} not expected"
cmds_expected.remove(item['cmd'])
# populate the list of actually present commands and their description
cmds_actual.add(item['cmd'])
desc_actual[item['cmd']] = item['desc']
assert len(cmds_expected) == 0, f"commands {cmds_expected} missing"
# Now: Run regular help and expect the same commands as help_json
child.expect(PROMPT)
child.sendline('help')
# expect the header first
child.expect_exact('Command Description\r\n')
child.expect_exact('---------------------------------------\r\n')
# loop until the set of actually present commands assembled from the JSON
# is empty. We remove each command from the set when we process it, so that
# we can detect duplicates
while len(cmds_actual) > 0:
# parse line into command and description
child.expect(r"([a-z0-9_-]*)[ \t]*(.*)\r\n")
cmd = child.match.group(1)
desc = child.match.group(2)
# expect the command to be in the set got from the JSON. Then remove
# it, so that a duplicated line would trigger the assert
assert cmd in cmds_actual, f"Command \"{cmd}\" unexpected or listed twice in help"
cmds_actual.remove(cmd)
# description should match the one got from JSON
assert desc == desc_actual[cmd], f"Description for \"{cmd}\" not matching"
def check_startup(child): def check_startup(child):
child.sendline(CONTROL_C) child.sendline(CONTROL_C)
child.expect_exact(PROMPT) child.expect_exact(PROMPT)
@ -247,6 +310,8 @@ def testfunc(child):
check_cmd(child, cmd, expected) check_cmd(child, cmd, expected)
check_help(child)
if RIOT_TERMINAL in CLEANTERMS: if RIOT_TERMINAL in CLEANTERMS:
for cmd, expected in CMDS_CLEANTERM: for cmd, expected in CMDS_CLEANTERM:
check_cmd(child, cmd, expected) check_cmd(child, cmd, expected)