summaryrefslogtreecommitdiff
path: root/test/py/tests
diff options
context:
space:
mode:
Diffstat (limited to 'test/py/tests')
-rw-r--r--test/py/tests/test_000_version.py2
-rw-r--r--test/py/tests/test_dfu.py279
-rw-r--r--test/py/tests/test_env.py66
-rw-r--r--test/py/tests/test_help.py2
-rw-r--r--test/py/tests/test_hush_if_test.py10
-rw-r--r--test/py/tests/test_md.py13
-rw-r--r--test/py/tests/test_net.py155
-rw-r--r--test/py/tests/test_sandbox_exit.py6
-rw-r--r--test/py/tests/test_shell_basics.py12
-rw-r--r--test/py/tests/test_sleep.py8
-rw-r--r--test/py/tests/test_ums.py243
-rw-r--r--test/py/tests/test_unknown_cmd.py4
12 files changed, 688 insertions, 112 deletions
diff --git a/test/py/tests/test_000_version.py b/test/py/tests/test_000_version.py
index d262f05..43a02e7 100644
--- a/test/py/tests/test_000_version.py
+++ b/test/py/tests/test_000_version.py
@@ -9,7 +9,7 @@
# command prompt.
def test_version(u_boot_console):
- '''Test that the "version" command prints the U-Boot version.'''
+ """Test that the "version" command prints the U-Boot version."""
# "version" prints the U-Boot sign-on message. This is usually considered
# an error, so that any unexpected reboot causes an error. Here, this
diff --git a/test/py/tests/test_dfu.py b/test/py/tests/test_dfu.py
new file mode 100644
index 0000000..093e8d0
--- /dev/null
+++ b/test/py/tests/test_dfu.py
@@ -0,0 +1,279 @@
+# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+#
+# SPDX-License-Identifier: GPL-2.0
+
+# Test U-Boot's "dfu" command. The test starts DFU in U-Boot, waits for USB
+# device enumeration on the host, executes dfu-util multiple times to test
+# various transfer sizes, many of which trigger USB driver edge cases, and
+# finally aborts the "dfu" command in U-Boot.
+
+import os
+import os.path
+import pytest
+import u_boot_utils
+
+"""
+Note: This test relies on:
+
+a) boardenv_* to contain configuration values to define which USB ports are
+available for testing. Without this, this test will be automatically skipped.
+For example:
+
+env__usb_dev_ports = (
+ {
+ "fixture_id": "micro_b",
+ "tgt_usb_ctlr": "0",
+ "host_usb_dev_node": "/dev/usbdev-p2371-2180",
+ # This parameter is optional /if/ you only have a single board
+ # attached to your host at a time.
+ "host_usb_port_path": "3-13",
+ },
+)
+
+env__dfu_configs = (
+ # eMMC, partition 1
+ {
+ "fixture_id": "emmc",
+ "alt_info": "/dfu_test.bin ext4 0 1;/dfu_dummy.bin ext4 0 1",
+ "cmd_params": "mmc 0",
+ # This value is optional.
+ # If present, it specified the set of transfer sizes tested.
+ # If missing, a default list of sizes will be used, which covers
+ # various useful corner cases.
+ # Manually specifying test sizes is useful if you wish to test 4 DFU
+ # configurations, but don't want to test every single transfer size
+ # on each, to avoid bloating the overall time taken by testing.
+ "test_sizes": (63, 64, 65),
+ },
+)
+
+b) udev rules to set permissions on devices nodes, so that sudo is not
+required. For example:
+
+ACTION=="add", SUBSYSTEM=="block", SUBSYSTEMS=="usb", KERNELS=="3-13", MODE:="666"
+
+(You may wish to change the group ID instead of setting the permissions wide
+open. All that matters is that the user ID running the test can access the
+device.)
+"""
+
+# The set of file sizes to test. These values trigger various edge-cases such
+# as one less than, equal to, and one greater than typical USB max packet
+# sizes, and similar boundary conditions.
+test_sizes_default = (
+ 64 - 1,
+ 64,
+ 64 + 1,
+ 128 - 1,
+ 128,
+ 128 + 1,
+ 960 - 1,
+ 960,
+ 960 + 1,
+ 4096 - 1,
+ 4096,
+ 4096 + 1,
+ 1024 * 1024 - 1,
+ 1024 * 1024,
+ 8 * 1024 * 1024,
+)
+
+first_usb_dev_port = None
+
+@pytest.mark.buildconfigspec('cmd_dfu')
+def test_dfu(u_boot_console, env__usb_dev_port, env__dfu_config):
+ """Test the "dfu" command; the host system must be able to enumerate a USB
+ device when "dfu" is running, various DFU transfers are tested, and the
+ USB device must disappear when "dfu" is aborted.
+
+ Args:
+ u_boot_console: A U-Boot console connection.
+ env__usb_dev_port: The single USB device-mode port specification on
+ which to run the test. See the file-level comment above for
+ details of the format.
+ env__dfu_config: The single DFU (memory region) configuration on which
+ to run the test. See the file-level comment above for details
+ of the format.
+
+ Returns:
+ Nothing.
+ """
+
+ def start_dfu():
+ """Start U-Boot's dfu shell command.
+
+ This also waits for the host-side USB enumeration process to complete.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+ """
+
+ fh = u_boot_utils.attempt_to_open_file(
+ env__usb_dev_port['host_usb_dev_node'])
+ if fh:
+ fh.close()
+ raise Exception('USB device present before dfu command invoked')
+
+ u_boot_console.log.action(
+ 'Starting long-running U-Boot dfu shell command')
+
+ cmd = 'setenv dfu_alt_info "%s"' % env__dfu_config['alt_info']
+ u_boot_console.run_command(cmd)
+
+ cmd = 'dfu 0 ' + env__dfu_config['cmd_params']
+ u_boot_console.run_command(cmd, wait_for_prompt=False)
+ u_boot_console.log.action('Waiting for DFU USB device to appear')
+ fh = u_boot_utils.wait_until_open_succeeds(
+ env__usb_dev_port['host_usb_dev_node'])
+ fh.close()
+
+ def stop_dfu(ignore_errors):
+ """Stop U-Boot's dfu shell command from executing.
+
+ This also waits for the host-side USB de-enumeration process to
+ complete.
+
+ Args:
+ ignore_errors: Ignore any errors. This is useful if an error has
+ already been detected, and the code is performing best-effort
+ cleanup. In this case, we do not want to mask the original
+ error by "honoring" any new errors.
+
+ Returns:
+ Nothing.
+ """
+
+ try:
+ u_boot_console.log.action(
+ 'Stopping long-running U-Boot dfu shell command')
+ u_boot_console.ctrlc()
+ u_boot_console.log.action(
+ 'Waiting for DFU USB device to disappear')
+ u_boot_utils.wait_until_file_open_fails(
+ env__usb_dev_port['host_usb_dev_node'], ignore_errors)
+ except:
+ if not ignore_errors:
+ raise
+
+ def run_dfu_util(alt_setting, fn, up_dn_load_arg):
+ """Invoke dfu-util on the host.
+
+ Args:
+ alt_setting: The DFU "alternate setting" identifier to interact
+ with.
+ fn: The host-side file name to transfer.
+ up_dn_load_arg: '-U' or '-D' depending on whether a DFU upload or
+ download operation should be performed.
+
+ Returns:
+ Nothing.
+ """
+
+ cmd = ['dfu-util', '-a', str(alt_setting), up_dn_load_arg, fn]
+ if 'host_usb_port_path' in env__usb_dev_port:
+ cmd += ['-p', env__usb_dev_port['host_usb_port_path']]
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+ u_boot_console.wait_for('Ctrl+C to exit ...')
+
+ def dfu_write(alt_setting, fn):
+ """Write a file to the target board using DFU.
+
+ Args:
+ alt_setting: The DFU "alternate setting" identifier to interact
+ with.
+ fn: The host-side file name to transfer.
+
+ Returns:
+ Nothing.
+ """
+
+ run_dfu_util(alt_setting, fn, '-D')
+
+ def dfu_read(alt_setting, fn):
+ """Read a file from the target board using DFU.
+
+ Args:
+ alt_setting: The DFU "alternate setting" identifier to interact
+ with.
+ fn: The host-side file name to transfer.
+
+ Returns:
+ Nothing.
+ """
+
+ # dfu-util fails reads/uploads if the host file already exists
+ if os.path.exists(fn):
+ os.remove(fn)
+ run_dfu_util(alt_setting, fn, '-U')
+
+ def dfu_write_read_check(size):
+ """Test DFU transfers of a specific size of data
+
+ This function first writes data to the board then reads it back and
+ compares the written and read back data. Measures are taken to avoid
+ certain types of false positives.
+
+ Args:
+ size: The data size to test.
+
+ Returns:
+ Nothing.
+ """
+
+ test_f = u_boot_utils.PersistentRandomFile(u_boot_console,
+ 'dfu_%d.bin' % size, size)
+ readback_fn = u_boot_console.config.result_dir + '/dfu_readback.bin'
+
+ u_boot_console.log.action('Writing test data to DFU primary ' +
+ 'altsetting')
+ dfu_write(0, test_f.abs_fn)
+
+ u_boot_console.log.action('Writing dummy data to DFU secondary ' +
+ 'altsetting to clear DFU buffers')
+ dfu_write(1, dummy_f.abs_fn)
+
+ u_boot_console.log.action('Reading DFU primary altsetting for ' +
+ 'comparison')
+ dfu_read(0, readback_fn)
+
+ u_boot_console.log.action('Comparing written and read data')
+ written_hash = test_f.content_hash
+ read_back_hash = u_boot_utils.md5sum_file(readback_fn, size)
+ assert(written_hash == read_back_hash)
+
+ # This test may be executed against multiple USB ports. The test takes a
+ # long time, so we don't want to do the whole thing each time. Instead,
+ # execute the full test on the first USB port, and perform a very limited
+ # test on other ports. In the limited case, we solely validate that the
+ # host PC can enumerate the U-Boot USB device.
+ global first_usb_dev_port
+ if not first_usb_dev_port:
+ first_usb_dev_port = env__usb_dev_port
+ if env__usb_dev_port == first_usb_dev_port:
+ sizes = env__dfu_config.get('test_sizes', test_sizes_default)
+ else:
+ sizes = []
+
+ dummy_f = u_boot_utils.PersistentRandomFile(u_boot_console,
+ 'dfu_dummy.bin', 1024)
+
+ ignore_cleanup_errors = True
+ try:
+ start_dfu()
+
+ u_boot_console.log.action(
+ 'Overwriting DFU primary altsetting with dummy data')
+ dfu_write(0, dummy_f.abs_fn)
+
+ for size in sizes:
+ with u_boot_console.log.section('Data size %d' % size):
+ dfu_write_read_check(size)
+ # Make the status of each sub-test obvious. If the test didn't
+ # pass, an exception was thrown so this code isn't executed.
+ u_boot_console.log.status_pass('OK')
+ ignore_cleanup_errors = False
+ finally:
+ stop_dfu(ignore_cleanup_errors)
diff --git a/test/py/tests/test_env.py b/test/py/tests/test_env.py
index a3e8dd3..c41aa5a 100644
--- a/test/py/tests/test_env.py
+++ b/test/py/tests/test_env.py
@@ -10,34 +10,34 @@ import pytest
# FIXME: This might be useful for other tests;
# perhaps refactor it into ConsoleBase or some other state object?
class StateTestEnv(object):
- '''Container that represents the state of all U-Boot environment variables.
+ """Container that represents the state of all U-Boot environment variables.
This enables quick determination of existant/non-existant variable
names.
- '''
+ """
def __init__(self, u_boot_console):
- '''Initialize a new StateTestEnv object.
+ """Initialize a new StateTestEnv object.
Args:
u_boot_console: A U-Boot console.
Returns:
Nothing.
- '''
+ """
self.u_boot_console = u_boot_console
self.get_env()
self.set_var = self.get_non_existent_var()
def get_env(self):
- '''Read all current environment variables from U-Boot.
+ """Read all current environment variables from U-Boot.
Args:
None.
Returns:
Nothing.
- '''
+ """
response = self.u_boot_console.run_command('printenv')
self.env = {}
@@ -48,27 +48,27 @@ class StateTestEnv(object):
self.env[var] = value
def get_existent_var(self):
- '''Return the name of an environment variable that exists.
+ """Return the name of an environment variable that exists.
Args:
None.
Returns:
The name of an environment variable.
- '''
+ """
for var in self.env:
return var
def get_non_existent_var(self):
- '''Return the name of an environment variable that does not exist.
+ """Return the name of an environment variable that does not exist.
Args:
None.
Returns:
The name of an environment variable.
- '''
+ """
n = 0
while True:
@@ -77,63 +77,67 @@ class StateTestEnv(object):
return var
n += 1
-@pytest.fixture(scope='module')
+ste = None
+@pytest.fixture(scope='function')
def state_test_env(u_boot_console):
- '''pytest fixture to provide a StateTestEnv object to tests.'''
+ """pytest fixture to provide a StateTestEnv object to tests."""
- return StateTestEnv(u_boot_console)
+ global ste
+ if not ste:
+ ste = StateTestEnv(u_boot_console)
+ return ste
def unset_var(state_test_env, var):
- '''Unset an environment variable.
+ """Unset an environment variable.
This both executes a U-Boot shell command and updates a StateTestEnv
object.
Args:
- state_test_env: The StateTestEnv object to updata.
+ state_test_env: The StateTestEnv object to update.
var: The variable name to unset.
Returns:
Nothing.
- '''
+ """
state_test_env.u_boot_console.run_command('setenv %s' % var)
if var in state_test_env.env:
del state_test_env.env[var]
def set_var(state_test_env, var, value):
- '''Set an environment variable.
+ """Set an environment variable.
This both executes a U-Boot shell command and updates a StateTestEnv
object.
Args:
- state_test_env: The StateTestEnv object to updata.
+ state_test_env: The StateTestEnv object to update.
var: The variable name to set.
value: The value to set the variable to.
Returns:
Nothing.
- '''
+ """
state_test_env.u_boot_console.run_command('setenv %s "%s"' % (var, value))
state_test_env.env[var] = value
def validate_empty(state_test_env, var):
- '''Validate that a variable is not set, using U-Boot shell commands.
+ """Validate that a variable is not set, using U-Boot shell commands.
Args:
var: The variable name to test.
Returns:
Nothing.
- '''
+ """
response = state_test_env.u_boot_console.run_command('echo $%s' % var)
assert response == ''
def validate_set(state_test_env, var, value):
- '''Validate that a variable is set, using U-Boot shell commands.
+ """Validate that a variable is set, using U-Boot shell commands.
Args:
var: The variable name to test.
@@ -141,7 +145,7 @@ def validate_set(state_test_env, var, value):
Returns:
Nothing.
- '''
+ """
# echo does not preserve leading, internal, or trailing whitespace in the
# value. printenv does, and hence allows more complete testing.
@@ -149,20 +153,20 @@ def validate_set(state_test_env, var, value):
assert response == ('%s=%s' % (var, value))
def test_env_echo_exists(state_test_env):
- '''Test echoing a variable that exists.'''
+ """Test echoing a variable that exists."""
var = state_test_env.get_existent_var()
value = state_test_env.env[var]
validate_set(state_test_env, var, value)
def test_env_echo_non_existent(state_test_env):
- '''Test echoing a variable that doesn't exist.'''
+ """Test echoing a variable that doesn't exist."""
var = state_test_env.set_var
validate_empty(state_test_env, var)
def test_env_printenv_non_existent(state_test_env):
- '''Test printenv error message for non-existant variables.'''
+ """Test printenv error message for non-existant variables."""
var = state_test_env.set_var
c = state_test_env.u_boot_console
@@ -171,14 +175,14 @@ def test_env_printenv_non_existent(state_test_env):
assert(response == '## Error: "%s" not defined' % var)
def test_env_unset_non_existent(state_test_env):
- '''Test unsetting a nonexistent variable.'''
+ """Test unsetting a nonexistent variable."""
var = state_test_env.get_non_existent_var()
unset_var(state_test_env, var)
validate_empty(state_test_env, var)
def test_env_set_non_existent(state_test_env):
- '''Test set a non-existant variable.'''
+ """Test set a non-existant variable."""
var = state_test_env.set_var
value = 'foo'
@@ -186,7 +190,7 @@ def test_env_set_non_existent(state_test_env):
validate_set(state_test_env, var, value)
def test_env_set_existing(state_test_env):
- '''Test setting an existant variable.'''
+ """Test setting an existant variable."""
var = state_test_env.set_var
value = 'bar'
@@ -194,14 +198,14 @@ def test_env_set_existing(state_test_env):
validate_set(state_test_env, var, value)
def test_env_unset_existing(state_test_env):
- '''Test unsetting a variable.'''
+ """Test unsetting a variable."""
var = state_test_env.set_var
unset_var(state_test_env, var)
validate_empty(state_test_env, var)
def test_env_expansion_spaces(state_test_env):
- '''Test expanding a variable that contains a space in its value.'''
+ """Test expanding a variable that contains a space in its value."""
var_space = None
var_test = None
diff --git a/test/py/tests/test_help.py b/test/py/tests/test_help.py
index 894f3b5..420090c 100644
--- a/test/py/tests/test_help.py
+++ b/test/py/tests/test_help.py
@@ -4,6 +4,6 @@
# SPDX-License-Identifier: GPL-2.0
def test_help(u_boot_console):
- '''Test that the "help" command can be executed.'''
+ """Test that the "help" command can be executed."""
u_boot_console.run_command('help')
diff --git a/test/py/tests/test_hush_if_test.py b/test/py/tests/test_hush_if_test.py
index cf4c3ae..8b88425 100644
--- a/test/py/tests/test_hush_if_test.py
+++ b/test/py/tests/test_hush_if_test.py
@@ -95,7 +95,7 @@ subtests = (
)
def exec_hush_if(u_boot_console, expr, result):
- '''Execute a shell "if" command, and validate its result.'''
+ """Execute a shell "if" command, and validate its result."""
cmd = 'if ' + expr + '; then echo true; else echo false; fi'
response = u_boot_console.run_command(cmd)
@@ -103,7 +103,7 @@ def exec_hush_if(u_boot_console, expr, result):
@pytest.mark.buildconfigspec('sys_hush_parser')
def test_hush_if_test_setup(u_boot_console):
- '''Set up environment variables used during the "if" tests.'''
+ """Set up environment variables used during the "if" tests."""
u_boot_console.run_command('setenv ut_var_nonexistent')
u_boot_console.run_command('setenv ut_var_exists 1')
@@ -111,13 +111,13 @@ def test_hush_if_test_setup(u_boot_console):
@pytest.mark.buildconfigspec('sys_hush_parser')
@pytest.mark.parametrize('expr,result', subtests)
def test_hush_if_test(u_boot_console, expr, result):
- '''Test a single "if test" condition.'''
+ """Test a single "if test" condition."""
exec_hush_if(u_boot_console, expr, result)
@pytest.mark.buildconfigspec('sys_hush_parser')
def test_hush_if_test_teardown(u_boot_console):
- '''Clean up environment variables used during the "if" tests.'''
+ """Clean up environment variables used during the "if" tests."""
u_boot_console.run_command('setenv ut_var_exists')
@@ -126,7 +126,7 @@ def test_hush_if_test_teardown(u_boot_console):
# Of those, only UMS currently allows file removal though.
@pytest.mark.boardspec('sandbox')
def test_hush_if_test_host_file_exists(u_boot_console):
- '''Test the "if test -e" shell command.'''
+ """Test the "if test -e" shell command."""
test_file = u_boot_console.config.result_dir + \
'/creating_this_file_breaks_u_boot_tests'
diff --git a/test/py/tests/test_md.py b/test/py/tests/test_md.py
index 94603c7..5fe2582 100644
--- a/test/py/tests/test_md.py
+++ b/test/py/tests/test_md.py
@@ -4,13 +4,14 @@
# SPDX-License-Identifier: GPL-2.0
import pytest
+import u_boot_utils
@pytest.mark.buildconfigspec('cmd_memory')
def test_md(u_boot_console):
- '''Test that md reads memory as expected, and that memory can be modified
- using the mw command.'''
+ """Test that md reads memory as expected, and that memory can be modified
+ using the mw command."""
- ram_base = u_boot_console.find_ram_base()
+ ram_base = u_boot_utils.find_ram_base(u_boot_console)
addr = '%08x' % ram_base
val = 'a5f09876'
expected_response = addr + ': ' + val
@@ -23,10 +24,10 @@ def test_md(u_boot_console):
@pytest.mark.buildconfigspec('cmd_memory')
def test_md_repeat(u_boot_console):
- '''Test command repeat (via executing an empty command) operates correctly
- for "md"; the command must repeat and dump an incrementing address.'''
+ """Test command repeat (via executing an empty command) operates correctly
+ for "md"; the command must repeat and dump an incrementing address."""
- ram_base = u_boot_console.find_ram_base()
+ ram_base = u_boot_utils.find_ram_base(u_boot_console)
addr_base = '%08x' % ram_base
words = 0x10
addr_repeat = '%08x' % (ram_base + (words * 4))
diff --git a/test/py/tests/test_net.py b/test/py/tests/test_net.py
new file mode 100644
index 0000000..07393eb
--- /dev/null
+++ b/test/py/tests/test_net.py
@@ -0,0 +1,155 @@
+# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+#
+# SPDX-License-Identifier: GPL-2.0
+
+# Test various network-related functionality, such as the dhcp, ping, and
+# tftpboot commands.
+
+import pytest
+import u_boot_utils
+
+"""
+Note: This test relies on boardenv_* containing configuration values to define
+which the network environment available for testing. Without this, this test
+will be automatically skipped.
+
+For example:
+
+# Boolean indicating whether the Ethernet device is attached to USB, and hence
+# USB enumeration needs to be performed prior to network tests.
+# This variable may be omitted if its value is False.
+env__net_uses_usb = False
+
+# Boolean indicating whether the Ethernet device is attached to PCI, and hence
+# PCI enumeration needs to be performed prior to network tests.
+# This variable may be omitted if its value is False.
+env__net_uses_pci = True
+
+# True if a DHCP server is attached to the network, and should be tested.
+# If DHCP testing is not possible or desired, this variable may be omitted or
+# set to False.
+env__net_dhcp_server = True
+
+# A list of environment variables that should be set in order to configure a
+# static IP. If solely relying on DHCP, this variable may be omitted or set to
+# an empty list.
+env__net_static_env_vars = [
+ ("ipaddr", "10.0.0.100"),
+ ("netmask", "255.255.255.0"),
+ ("serverip", "10.0.0.1"),
+]
+
+# Details regarding a file that may be read from a TFTP server. This variable
+# may be omitted or set to None if TFTP testing is not possible or desired.
+env__net_tftp_readable_file = {
+ "fn": "ubtest-readable.bin",
+ "size": 5058624,
+ "crc32": "c2244b26",
+}
+"""
+
+net_set_up = False
+
+def test_net_pre_commands(u_boot_console):
+ """Execute any commands required to enable network hardware.
+
+ These commands are provided by the boardenv_* file; see the comment at the
+ beginning of this file.
+ """
+
+ init_usb = u_boot_console.config.env.get('env__net_uses_usb', False)
+ if init_usb:
+ u_boot_console.run_command('usb start')
+
+ init_pci = u_boot_console.config.env.get('env__net_uses_pci', False)
+ if init_pci:
+ u_boot_console.run_command('pci enum')
+
+@pytest.mark.buildconfigspec('cmd_dhcp')
+def test_net_dhcp(u_boot_console):
+ """Test the dhcp command.
+
+ The boardenv_* file may be used to enable/disable this test; see the
+ comment at the beginning of this file.
+ """
+
+ test_dhcp = u_boot_console.config.env.get('env__net_dhcp_server', False)
+ if not test_dhcp:
+ pytest.skip('No DHCP server available')
+
+ u_boot_console.run_command('setenv autoload no')
+ output = u_boot_console.run_command('dhcp')
+ assert 'DHCP client bound to address ' in output
+
+ global net_set_up
+ net_set_up = True
+
+@pytest.mark.buildconfigspec('net')
+def test_net_setup_static(u_boot_console):
+ """Set up a static IP configuration.
+
+ The configuration is provided by the boardenv_* file; see the comment at
+ the beginning of this file.
+ """
+
+ env_vars = u_boot_console.config.env.get('env__net_static_env_vars', None)
+ if not env_vars:
+ pytest.skip('No static network configuration is defined')
+
+ for (var, val) in env_vars:
+ u_boot_console.run_command('setenv %s %s' % (var, val))
+
+ global net_set_up
+ net_set_up = True
+
+@pytest.mark.buildconfigspec('cmd_ping')
+def test_net_ping(u_boot_console):
+ """Test the ping command.
+
+ The $serverip (as set up by either test_net_dhcp or test_net_setup_static)
+ is pinged. The test validates that the host is alive, as reported by the
+ ping command's output.
+ """
+
+ if not net_set_up:
+ pytest.skip('Network not initialized')
+
+ output = u_boot_console.run_command('ping $serverip')
+ assert 'is alive' in output
+
+@pytest.mark.buildconfigspec('cmd_net')
+def test_net_tftpboot(u_boot_console):
+ """Test the tftpboot command.
+
+ A file is downloaded from the TFTP server, its size and optionally its
+ CRC32 are validated.
+
+ The details of the file to download are provided by the boardenv_* file;
+ see the comment at the beginning of this file.
+ """
+
+ if not net_set_up:
+ pytest.skip('Network not initialized')
+
+ f = u_boot_console.config.env.get('env__net_tftp_readable_file', None)
+ if not f:
+ pytest.skip('No TFTP readable file to read')
+
+ addr = u_boot_utils.find_ram_base(u_boot_console)
+ fn = f['fn']
+ output = u_boot_console.run_command('tftpboot %x %s' % (addr, fn))
+ expected_text = 'Bytes transferred = '
+ sz = f.get('size', None)
+ if sz:
+ expected_text += '%d' % sz
+ assert expected_text in output
+
+ expected_crc = f.get('crc32', None)
+ if not expected_crc:
+ return
+
+ if u_boot_console.config.buildconfig.get('config_cmd_crc32', 'n') != 'y':
+ return
+
+ output = u_boot_console.run_command('crc32 %x $filesize' % addr)
+ assert expected_crc in output
diff --git a/test/py/tests/test_sandbox_exit.py b/test/py/tests/test_sandbox_exit.py
index 2aa8eb4..d1aa308 100644
--- a/test/py/tests/test_sandbox_exit.py
+++ b/test/py/tests/test_sandbox_exit.py
@@ -9,16 +9,14 @@ import signal
@pytest.mark.boardspec('sandbox')
@pytest.mark.buildconfigspec('reset')
def test_reset(u_boot_console):
- '''Test that the "reset" command exits sandbox process.'''
+ """Test that the "reset" command exits sandbox process."""
u_boot_console.run_command('reset', wait_for_prompt=False)
assert(u_boot_console.validate_exited())
- u_boot_console.ensure_spawned()
@pytest.mark.boardspec('sandbox')
def test_ctrl_c(u_boot_console):
- '''Test that sending SIGINT to sandbox causes it to exit.'''
+ """Test that sending SIGINT to sandbox causes it to exit."""
u_boot_console.kill(signal.SIGINT)
assert(u_boot_console.validate_exited())
- u_boot_console.ensure_spawned()
diff --git a/test/py/tests/test_shell_basics.py b/test/py/tests/test_shell_basics.py
index 719ce61..702e5e2 100644
--- a/test/py/tests/test_shell_basics.py
+++ b/test/py/tests/test_shell_basics.py
@@ -5,13 +5,13 @@
# Test basic shell functionality, such as commands separate by semi-colons.
def test_shell_execute(u_boot_console):
- '''Test any shell command.'''
+ """Test any shell command."""
response = u_boot_console.run_command('echo hello')
assert response.strip() == 'hello'
def test_shell_semicolon_two(u_boot_console):
- '''Test two shell commands separate by a semi-colon.'''
+ """Test two shell commands separate by a semi-colon."""
cmd = 'echo hello; echo world'
response = u_boot_console.run_command(cmd)
@@ -19,8 +19,8 @@ def test_shell_semicolon_two(u_boot_console):
assert response.index('hello') < response.index('world')
def test_shell_semicolon_three(u_boot_console):
- '''Test three shell commands separate by a semi-colon, with variable
- expansion dependencies between them.'''
+ """Test three shell commands separate by a semi-colon, with variable
+ expansion dependencies between them."""
cmd = 'setenv list 1; setenv list ${list}2; setenv list ${list}3; ' + \
'echo ${list}'
@@ -29,9 +29,9 @@ def test_shell_semicolon_three(u_boot_console):
u_boot_console.run_command('setenv list')
def test_shell_run(u_boot_console):
- '''Test the "run" shell command.'''
+ """Test the "run" shell command."""
- u_boot_console.run_command('setenv foo \"setenv monty 1; setenv python 2\"')
+ u_boot_console.run_command('setenv foo "setenv monty 1; setenv python 2"')
u_boot_console.run_command('run foo')
response = u_boot_console.run_command('echo $monty')
assert response.strip() == '1'
diff --git a/test/py/tests/test_sleep.py b/test/py/tests/test_sleep.py
index 64f1ddf..74add89 100644
--- a/test/py/tests/test_sleep.py
+++ b/test/py/tests/test_sleep.py
@@ -6,12 +6,8 @@ import pytest
import time
def test_sleep(u_boot_console):
- '''Test the sleep command, and validate that it sleeps for approximately
- the correct amount of time.'''
-
- # Do this before we time anything, to make sure U-Boot is already running.
- # Otherwise, the system boot time is included in the time measurement.
- u_boot_console.ensure_spawned()
+ """Test the sleep command, and validate that it sleeps for approximately
+ the correct amount of time."""
# 3s isn't too long, but is enough to cross a few second boundaries.
sleep_time = 3
diff --git a/test/py/tests/test_ums.py b/test/py/tests/test_ums.py
index a137221..8c3ee2b 100644
--- a/test/py/tests/test_ums.py
+++ b/test/py/tests/test_ums.py
@@ -2,28 +2,58 @@
#
# SPDX-License-Identifier: GPL-2.0
-# Test U-Boot's "ums" command. At present, this test only ensures that a UMS
-# device can be enumerated by the host/test machine. In the future, this test
-# should be enhanced to validate disk IO.
+# Test U-Boot's "ums" command. The test starts UMS in U-Boot, waits for USB
+# device enumeration on the host, reads a small block of data from the UMS
+# block device, optionally mounts a partition and performs filesystem-based
+# read/write tests, and finally aborts the "ums" command in U-Boot.
import os
+import os.path
import pytest
+import re
import time
+import u_boot_utils
-'''
+"""
Note: This test relies on:
a) boardenv_* to contain configuration values to define which USB ports are
available for testing. Without this, this test will be automatically skipped.
For example:
+# Leave this list empty if you have no block_devs below with writable
+# partitions defined.
+env__mount_points = (
+ "/mnt/ubtest-mnt-p2371-2180-na",
+)
+
env__usb_dev_ports = (
- {'tgt_usb_ctlr': '0', 'host_ums_dev_node': '/dev/disk/by-path/pci-0000:00:14.0-usb-0:13:1.0-scsi-0:0:0:0'},
+ {
+ "fixture_id": "micro_b",
+ "tgt_usb_ctlr": "0",
+ "host_ums_dev_node": "/dev/disk/by-path/pci-0000:00:14.0-usb-0:13:1.0-scsi-0:0:0:0",
+ },
)
env__block_devs = (
- {'type': 'mmc', 'id': '0'}, # eMMC; always present
- {'type': 'mmc', 'id': '1'}, # SD card; present since I plugged one in
+ # eMMC; always present
+ {
+ "fixture_id": "emmc",
+ "type": "mmc",
+ "id": "0",
+ # The following two properties are optional.
+ # If present, the partition will be mounted and a file written-to and
+ # read-from it. If missing, only a simple block read test will be
+ # performed.
+ "writable_fs_partition": 1,
+ "writable_fs_subdir": "tmp/",
+ },
+ # SD card; present since I plugged one in
+ {
+ "fixture_id": "sd",
+ "type": "mmc",
+ "id": "1"
+ },
)
b) udev rules to set permissions on devices nodes, so that sudo is not
@@ -34,47 +64,42 @@ ACTION=="add", SUBSYSTEM=="block", SUBSYSTEMS=="usb", KERNELS=="3-13", MODE:="66
(You may wish to change the group ID instead of setting the permissions wide
open. All that matters is that the user ID running the test can access the
device.)
-'''
-def open_ums_device(host_ums_dev_node):
- '''Attempt to open a device node, returning either the opened file handle,
- or None on any error.'''
+c) /etc/fstab entries to allow the block device to be mounted without requiring
+root permissions. For example:
- try:
- return open(host_ums_dev_node, 'rb')
- except:
- return None
-
-def wait_for_ums_device(host_ums_dev_node):
- '''Continually attempt to open the device node exported by the "ums"
- command, and either return the opened file handle, or raise an exception
- after a timeout.'''
-
- for i in xrange(100):
- fh = open_ums_device(host_ums_dev_node)
- if fh:
- return fh
- time.sleep(0.1)
- raise Exception('UMS device did not appear')
-
-def wait_for_ums_device_gone(host_ums_dev_node):
- '''Continually attempt to open the device node exported by the "ums"
- command, and either return once the device has disappeared, or raise an
- exception if it does not before a timeout occurs.'''
-
- for i in xrange(100):
- fh = open_ums_device(host_ums_dev_node)
- if not fh:
- return
- fh.close()
- time.sleep(0.1)
- raise Exception('UMS device did not disappear')
+/dev/disk/by-path/pci-0000:00:14.0-usb-0:13:1.0-scsi-0:0:0:0-part1 /mnt/ubtest-mnt-p2371-2180-na ext4 noauto,user,nosuid,nodev
+
+This entry is only needed if any block_devs above contain a
+writable_fs_partition value.
+"""
@pytest.mark.buildconfigspec('cmd_usb_mass_storage')
def test_ums(u_boot_console, env__usb_dev_port, env__block_devs):
- '''Test the "ums" command; the host system must be able to enumerate a UMS
- device when "ums" is running, and this device must disappear when "ums" is
- aborted.'''
+ """Test the "ums" command; the host system must be able to enumerate a UMS
+ device when "ums" is running, block and optionally file I/O are tested,
+ and this device must disappear when "ums" is aborted.
+
+ Args:
+ u_boot_console: A U-Boot console connection.
+ env__usb_dev_port: The single USB device-mode port specification on
+ which to run the test. See the file-level comment above for
+ details of the format.
+ env__block_devs: The list of block devices that the target U-Boot
+ device has attached. See the file-level comment above for details
+ of the format.
+
+ Returns:
+ Nothing.
+ """
+
+ have_writable_fs_partition = 'writable_fs_partition' in env__block_devs[0]
+ if not have_writable_fs_partition:
+ # If 'writable_fs_subdir' is missing, we'll skip all parts of the
+ # testing which mount filesystems.
+ u_boot_console.log.warning(
+ 'boardenv missing "writable_fs_partition"; ' +
+ 'UMS testing will be limited.')
tgt_usb_ctlr = env__usb_dev_port['tgt_usb_ctlr']
host_ums_dev_node = env__usb_dev_port['host_ums_dev_node']
@@ -84,11 +109,129 @@ def test_ums(u_boot_console, env__usb_dev_port, env__block_devs):
# device list here. We'll test each block device somewhere else.
tgt_dev_type = env__block_devs[0]['type']
tgt_dev_id = env__block_devs[0]['id']
+ if have_writable_fs_partition:
+ mount_point = u_boot_console.config.env['env__mount_points'][0]
+ mount_subdir = env__block_devs[0]['writable_fs_subdir']
+ part_num = env__block_devs[0]['writable_fs_partition']
+ host_ums_part_node = '%s-part%d' % (host_ums_dev_node, part_num)
+ else:
+ host_ums_part_node = host_ums_dev_node
+
+ test_f = u_boot_utils.PersistentRandomFile(u_boot_console, 'ums.bin',
+ 1024 * 1024);
+ if have_writable_fs_partition:
+ mounted_test_fn = mount_point + '/' + mount_subdir + test_f.fn
+
+ def start_ums():
+ """Start U-Boot's ums shell command.
+
+ This also waits for the host-side USB enumeration process to complete.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+ """
+
+ u_boot_console.log.action(
+ 'Starting long-running U-Boot ums shell command')
+ cmd = 'ums %s %s %s' % (tgt_usb_ctlr, tgt_dev_type, tgt_dev_id)
+ u_boot_console.run_command(cmd, wait_for_prompt=False)
+ u_boot_console.wait_for(re.compile('UMS: LUN.*[\r\n]'))
+ fh = u_boot_utils.wait_until_open_succeeds(host_ums_part_node)
+ u_boot_console.log.action('Reading raw data from UMS device')
+ fh.read(4096)
+ fh.close()
+
+ def mount():
+ """Mount the block device that U-Boot exports.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+ """
+
+ u_boot_console.log.action('Mounting exported UMS device')
+ cmd = ('/bin/mount', host_ums_part_node)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+
+ def umount(ignore_errors):
+ """Unmount the block device that U-Boot exports.
- cmd = 'ums %s %s %s' % (tgt_usb_ctlr, tgt_dev_type, tgt_dev_id)
- u_boot_console.run_command('ums 0 mmc 0', wait_for_prompt=False)
- fh = wait_for_ums_device(host_ums_dev_node)
- fh.read(4096)
- fh.close()
- u_boot_console.ctrlc()
- wait_for_ums_device_gone(host_ums_dev_node)
+ Args:
+ ignore_errors: Ignore any errors. This is useful if an error has
+ already been detected, and the code is performing best-effort
+ cleanup. In this case, we do not want to mask the original
+ error by "honoring" any new errors.
+
+ Returns:
+ Nothing.
+ """
+
+ u_boot_console.log.action('Unmounting UMS device')
+ cmd = ('/bin/umount', host_ums_part_node)
+ u_boot_utils.run_and_log(u_boot_console, cmd, ignore_errors)
+
+ def stop_ums(ignore_errors):
+ """Stop U-Boot's ums shell command from executing.
+
+ This also waits for the host-side USB de-enumeration process to
+ complete.
+
+ Args:
+ ignore_errors: Ignore any errors. This is useful if an error has
+ already been detected, and the code is performing best-effort
+ cleanup. In this case, we do not want to mask the original
+ error by "honoring" any new errors.
+
+ Returns:
+ Nothing.
+ """
+
+ u_boot_console.log.action(
+ 'Stopping long-running U-Boot ums shell command')
+ u_boot_console.ctrlc()
+ u_boot_utils.wait_until_file_open_fails(host_ums_part_node,
+ ignore_errors)
+
+ ignore_cleanup_errors = True
+ try:
+ start_ums()
+ if not have_writable_fs_partition:
+ # Skip filesystem-based testing if not configured
+ return
+ try:
+ mount()
+ u_boot_console.log.action('Writing test file via UMS')
+ cmd = ('rm', '-f', mounted_test_fn)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+ if os.path.exists(mounted_test_fn):
+ raise Exception('Could not rm target UMS test file')
+ cmd = ('cp', test_f.abs_fn, mounted_test_fn)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+ ignore_cleanup_errors = False
+ finally:
+ umount(ignore_errors=ignore_cleanup_errors)
+ finally:
+ stop_ums(ignore_errors=ignore_cleanup_errors)
+
+ ignore_cleanup_errors = True
+ try:
+ start_ums()
+ try:
+ mount()
+ u_boot_console.log.action('Reading test file back via UMS')
+ read_back_hash = u_boot_utils.md5sum_file(mounted_test_fn)
+ cmd = ('rm', '-f', mounted_test_fn)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+ ignore_cleanup_errors = False
+ finally:
+ umount(ignore_errors=ignore_cleanup_errors)
+ finally:
+ stop_ums(ignore_errors=ignore_cleanup_errors)
+
+ written_hash = test_f.content_hash
+ assert(written_hash == read_back_hash)
diff --git a/test/py/tests/test_unknown_cmd.py b/test/py/tests/test_unknown_cmd.py
index 2de93e0..c27ab49 100644
--- a/test/py/tests/test_unknown_cmd.py
+++ b/test/py/tests/test_unknown_cmd.py
@@ -4,8 +4,8 @@
# SPDX-License-Identifier: GPL-2.0
def test_unknown_command(u_boot_console):
- '''Test that executing an unknown command causes U-Boot to print an
- error.'''
+ """Test that executing an unknown command causes U-Boot to print an
+ error."""
# The "unknown command" error is actively expected here,
# so error detection for it is disabled.