diff --git a/tests/shell_lock/Makefile b/tests/shell_lock/Makefile new file mode 100644 index 000000000000..25b4a6e440e8 --- /dev/null +++ b/tests/shell_lock/Makefile @@ -0,0 +1,28 @@ +DEVELHELP=0 +include ../Makefile.tests_common + +USEMODULE += shell +USEMODULE += shell_commands + +USEMODULE += shell_lock +USEMODULE += shell_lock_auto_locking + +CFLAGS += -DCONFIG_SHELL_LOCK_PASSWORD=\"password\" +CFLAGS += -DCONFIG_SHELL_LOCK_AUTO_LOCK_TIMEOUT_MS=7000 + +# This config defaults to 1 on native, such that pm_off() would be called as soon as +# shell_run_once is terminated in shell_run_forever. We do not want this behavior for this test. +CFLAGS += -DCONFIG_SHELL_SHUTDOWN_ON_EXIT=0 + +# for z1, socat doesn't work (unknown reason) +ifeq (z1, $(BOARD)) + RIOT_TERMINAL ?= pyterm +endif + +# Use a terminal that does not introduce extra characters into the stream. +RIOT_TERMINAL ?= socat + +include $(RIOTBASE)/Makefile.include + +# the test script skips tests if socat is not used +$(call target-export-variables,$(RIOT_TERMINAL),RIOT_TERMINAL) diff --git a/tests/shell_lock/Makefile.ci b/tests/shell_lock/Makefile.ci new file mode 100644 index 000000000000..cd13c9e96d0d --- /dev/null +++ b/tests/shell_lock/Makefile.ci @@ -0,0 +1,8 @@ +BOARD_INSUFFICIENT_MEMORY := \ + arduino-duemilanove \ + arduino-leonardo \ + arduino-nano \ + arduino-uno \ + atmega328p \ + nucleo-l011k4 \ + # diff --git a/tests/shell_lock/main.c b/tests/shell_lock/main.c new file mode 100644 index 000000000000..663cce29f0ab --- /dev/null +++ b/tests/shell_lock/main.c @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2021 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @file + * @brief Test for the lock behaviour of the shell + * + * @author Hendrik van Essen + * + */ + +#include + +#include "shell.h" + +int main(void) +{ + puts("test_shell_lock"); + + /* define buffer to be used by the shell */ + char line_buf[SHELL_DEFAULT_BUFSIZE]; + + /* start shell */ + shell_run(NULL, line_buf, SHELL_DEFAULT_BUFSIZE); + + return 0; +} diff --git a/tests/shell_lock/tests/01-run.py b/tests/shell_lock/tests/01-run.py new file mode 100755 index 000000000000..fc1c307d6579 --- /dev/null +++ b/tests/shell_lock/tests/01-run.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2021 Freie Universität Berlin +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + +import os +import sys +import time + +from testrunner import run + +PASSWORD_CORRECT = "password" +PASSWORDS_INCORRECT = [ + "pass", + "word", + "asswor", + "passw0rd", + "password_", + "_password" +] + +EXPECTED_HELP = ( + 'Command Description', + '---------------------------------------', + 'reboot Reboot the node', + 'version Prints current RIOT_VERSION', + 'pm interact with layered PM subsystem', + 'lock Lock the shell' +) + +AUTO_LOCK_TIMEOUT_MS = 7000 +PROMPT = '> ' +BOARD = os.environ['BOARD'] + + +def testfunc(child): + + # avoid sending an extra empty line on native. + if BOARD == 'native': + child.crlf = '\n' + + # unlock + child.sendline(PASSWORD_CORRECT) + child.expect_exact('Shell was unlocked.') + child.expect_exact(PROMPT) + + # check we have access + child.sendline('help') + for line in EXPECTED_HELP: + child.expect_exact(line) + + # lock + child.sendline('lock') + child.expect(PROMPT) + + # trigger password prompt + child.sendline('help') + child.expect('The shell is locked. Enter a valid password to unlock.') + child.expect('Password:') + + # test different incorrect passwords + for i, pwd in enumerate(PASSWORDS_INCORRECT): + + # every third incorrect attempt leads to 7 second of sleep, otherwise + # just 1 second + if i > 0 and i % 3 == 0: + timeout = 7 + else: + timeout = 1 + + # some boards react quite slow, give them 2 extra seconds + child.expect_exact(PROMPT, timeout=(timeout + 2)) + child.sendline(pwd) + child.expect_exact('Wrong password') + + # unlock + child.sendline(PASSWORD_CORRECT) + child.expect_exact('Shell was unlocked.') + child.expect_exact(PROMPT) + + # check we have access + child.sendline('help') + for line in EXPECTED_HELP: + child.expect_exact(line) + + # wait until auto_lock locks the shell after + # CONFIG_SHELL_LOCK_AUTO_LOCK_TIMEOUT_MS (+ 1 second buffer time) + time.sleep((AUTO_LOCK_TIMEOUT_MS / 1000.0) + 1) + + # trigger password prompt + child.sendline('help') + child.expect('The shell is locked. Enter a valid password to unlock.') + + +if __name__ == "__main__": + sys.exit(run(testfunc))