Compare commits

...

16 Commits
ofnode ... tesc

Author SHA1 Message Date
Simon Glass
4bae88d177 sandbox: Fix pinctrl warning on startup
With the test devicetree, the following warning is shown on startup:

pinctrl_select_state_full() sandbox_serial serial:
  pinctrl_select_state_full: uclass_get_device_by_phandle_id: err=-19

Add the required bootph tags so that the pinctrl-single driver is set
up before relocation, to avoid this warning.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-07-23 13:17:08 -06:00
Simon Glass
a636969d81 test/py: Add a proper function to shut down U-Boot
Some tests boot out of U-Boot or perform such other action that it can
no-longer be used for tests. Provide a function to handle this, rather
than having tests do it themselves.

Signed-off-by: Simon Glass <sjg@chromium.org>
Series-to: u-boot
Cover-letter:
test/py: Some code tidy-updates
This series includes various improvements to the test/py close:

- Fix some pylint warnings
- Log the PYTHONPATH when no py hooks are found
- Improve how the pattern list is handled
- Move 'expect' handling into ConsoleBase
- Add an exported function to shutdown U-Boot in tests
- A few other minor things
END
2025-07-23 13:17:07 -06:00
Simon Glass
d1640335e1 test/py: Move timeouts to console_base
These timeouts are all handled in console_base now, so drop the
self.p.timeout and just use self.timeout

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-07-23 13:13:25 -06:00
Simon Glass
c30ea35d4e test/py: Split up _wait_for_boot_prompt
The banner-detection code is quite large and is not needed in lab mode.
Move it into its own function.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-07-23 13:13:25 -06:00
Simon Glass
e314a5583c test/py: Move expect() function into console_base
This function is only used from console_base and it is always accessed
via a 'self.p.expect()' expression, which is confusing.

Checking for expected output doesn't really relate to spawning in any
case, so move this function to console_base

Move the exception handling as well, since it is all related to
expect(). Also move variables which relate to the 'expect'
functionality.

Tidy up the imports while we are here.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-07-23 13:13:25 -06:00
Simon Glass
4a63f49f2f test/py: Provide an expect() function for use by tests
At present some tests are directly calling the Spawn() object in order
to check for required output. This is a bit messy. Add a function to the
ubman fixture to handle this.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-07-23 13:13:25 -06:00
Simon Glass
950b4b4b54 test/py: Maintain a list of available patterns
Some tests want to augment the list, so rather than using the global
PATTERNS values, create an avail_patterns property in ConsoleBase

With this we can avoid changing the global.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-07-23 13:13:25 -06:00
Simon Glass
ffa2c74a90 test/py: Rename bad_pattern_defs to PATTERNS
This name is quite confusing. Not all of the patterns are necessarily
bad. For example, main_signon is expected on start-up.

Rename it to the more neutral 'PATTERNS', using capitals since it is a
global. Rename eval_bad_patterns() also.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-07-23 13:13:25 -06:00
Simon Glass
06d51b3ea3 test/py: Create a named tuple for the pattern list
It is a bit clumsy to have to subscript a list. Use a named tuple
instead.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-07-23 13:13:25 -06:00
Simon Glass
b1d49b9d8f test/py: Tidy up pylint warnings in spawn
There are quite a few warnings which makes it confusing when editing
this file. Resolve the easy ones, with these remaining:

  55:0: R0913: Too many arguments (7/5) (too-many-arguments)
  91:0: R0902: Too many instance attributes (14/7) (too-many-instance-attributes)
  136:12: W0702: No exception type(s) specified (bare-except)
  246:4: R0912: Too many branches (14/12) (too-many-branches)

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-07-23 13:13:25 -06:00
Simon Glass
3c099ee898 test/py: Tidy up pylint warnings in console_sandbox
There are quite a few warnings which makes it confusing when editing
this file. Resolve them.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-07-23 13:13:25 -06:00
Simon Glass
0365f1afa4 test/py: Tidy up pylint warnings in console_sandbox
There are quite a few warnings which makes it confusing when editing
this file. Resolve the easy ones, leaving:

  125:0: R0902: Too many instance attributes (14/7) (too-many-instance-attributes)
  212:4: R0912: Too many branches (14/12) (too-many-branches)
  271:4: R0913: Too many arguments (6/5) (too-many-arguments)
  271:4: R0912: Too many branches (13/12) (too-many-branches)
  454:8: W0702: No exception type(s) specified (bare-except)
  531:8: W0702: No exception type(s) specified (bare-except)

Rename wait_for_boot_prompt() so it is clear that it is an internal
function.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-07-23 13:13:25 -06:00
Simon Glass
92a8b5ab4a test/py: Tidy up comments in ConsoleBase()
Some of the attributes are missing comments. Add these and tidy up a
few existing ones.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-07-23 13:13:25 -06:00
Simon Glass
03d4507bcb test/py: Tidy up pylint warnings in console_base
There are quite a few warnings which makes it confusing when editing
this file. Resolve the easy ones, leaving:

  125:0: R0902: Too many instance attributes (14/7) (too-many-instance-attributes)
  212:4: R0912: Too many branches (14/12) (too-many-branches)
  271:4: R0913: Too many arguments (6/5) (too-many-arguments)
  271:4: R0912: Too many branches (13/12) (too-many-branches)
  454:8: W0702: No exception type(s) specified (bare-except)
  531:8: W0702: No exception type(s) specified (bare-except)

Rename wait_for_boot_prompt() so it is clear that it is an internal
function.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-07-23 13:13:25 -06:00
Simon Glass
1c2d31d4ee test/py: Log the PYTHONPATH on error
When no hook scripts are found, log the PYTHONPATH to aid debugging. Use
a separate variable to avoid an error on Python 3.10 and a pylint 3.3.4
warning:

   E0001: Parsing failed: 'f-string expression part cannot include a
      backslash (conftest, line 311)' (syntax-error)

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-07-23 13:13:25 -06:00
Simon Glass
d48881ed22 tools: Move to version 0.0.7
Update all tools to the new version, since there have been quite a lot
of changes recently.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-06-22 15:33:21 -06:00
19 changed files with 444 additions and 421 deletions

View File

@@ -1780,6 +1780,7 @@
pinctrl-single-pins { pinctrl-single-pins {
compatible = "pinctrl-single"; compatible = "pinctrl-single";
bootph-some-ram;
reg = <0x0000 0x238>; reg = <0x0000 0x238>;
#pinctrl-cells = <1>; #pinctrl-cells = <1>;
pinctrl-single,register-width = <32>; pinctrl-single,register-width = <32>;
@@ -1799,6 +1800,7 @@
}; };
pinmux_uart0_pins: pinmux_uart0_pins { pinmux_uart0_pins: pinmux_uart0_pins {
bootph-some-ram;
pinctrl-single,pins = < pinctrl-single,pins = <
0x70 0x30 0x70 0x30
0x74 0x00 0x74 0x00

View File

@@ -25,7 +25,7 @@ import re
from _pytest.runner import runtestprotocol from _pytest.runner import runtestprotocol
import subprocess import subprocess
import sys import sys
from spawn import BootFail, Timeout, Unexpected, handle_exception from console_base import BootFail, Timeout, Unexpected, handle_exception
import time import time
# Globals: The HTML log file, and the top-level fixture # Globals: The HTML log file, and the top-level fixture
@@ -308,6 +308,8 @@ def pytest_configure(config):
log.info(f"Loaded {module}") log.info(f"Loaded {module}")
if not_found: if not_found:
paths = '\n'.join(sys.path)
log.info(f"PYTHONPATH: {paths}")
log.warning(f"Failed to find modules: {' '.join(not_found)}") log.warning(f"Failed to find modules: {' '.join(not_found)}")
ubconfig.buildconfig = dict() ubconfig.buildconfig = dict()

View File

@@ -2,19 +2,21 @@
# Copyright (c) 2015 Stephen Warren # Copyright (c) 2015 Stephen Warren
# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. # Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
# Common logic to interact with U-Boot via the console. This class provides """Common logic to interact with U-Boot via the console.
# the interface that tests use to execute U-Boot shell commands and wait for
# their results. Sub-classes exist to perform board-type-specific setup
# operations, such as spawning a sub-process for Sandbox, or attaching to the
# serial console of real hardware.
import multiplexed_log Provides the interface that tests use to execute U-Boot shell commands and wait
import os for their results. Sub-classes exist to perform board-type-specific setup
import pytest operations, such as spawning a sub-process for Sandbox, or attaching to the
serial console of real hardware.
"""
from collections import namedtuple
import re import re
import sys import sys
import time
import pytest
import spawn import spawn
from spawn import BootFail, Timeout, Unexpected, handle_exception
# Regexes for text we expect U-Boot to send to the console. # Regexes for text we expect U-Boot to send to the console.
pattern_u_boot_spl_signon = re.compile('(U-Boot Concept SPL \\d{4}\\.\\d{2}[^\r\n]*\\))') pattern_u_boot_spl_signon = re.compile('(U-Boot Concept SPL \\d{4}\\.\\d{2}[^\r\n]*\\))')
@@ -26,9 +28,6 @@ pattern_error_please_reset = re.compile('### ERROR ### Please RESET the board ##
pattern_ready_prompt = re.compile('{lab ready in (.*)s: (.*)}') pattern_ready_prompt = re.compile('{lab ready in (.*)s: (.*)}')
pattern_lab_mode = re.compile('{lab mode.*}') pattern_lab_mode = re.compile('{lab mode.*}')
PAT_ID = 0
PAT_RE = 1
# Timeout before expecting the console to be ready (in milliseconds) # Timeout before expecting the console to be ready (in milliseconds)
TIMEOUT_MS = 30000 # Standard timeout TIMEOUT_MS = 30000 # Standard timeout
TIMEOUT_CMD_MS = 10000 # Command-echo timeout TIMEOUT_CMD_MS = 10000 # Command-echo timeout
@@ -40,16 +39,77 @@ TIMEOUT_CMD_MS = 10000 # Command-echo timeout
# situations. # situations.
TIMEOUT_PREPARE_MS = 3 * 60 * 1000 TIMEOUT_PREPARE_MS = 3 * 60 * 1000
bad_pattern_defs = ( # Named pattern used by this module:
('spl_signon', pattern_u_boot_spl_signon), # str: name of pattern
('main_signon', pattern_u_boot_main_signon), # re.Pattern: Regex to check this pattern in the console output
('stop_autoboot_prompt', pattern_stop_autoboot_prompt), NamedPattern = namedtuple('PATTERN', 'name,pattern')
('unknown_command', pattern_unknown_command),
('error_notification', pattern_error_notification), # Named patterns we can look for in the console output. These can indicate an
('error_please_reset', pattern_error_please_reset), # error has occurred
PATTERNS = (
NamedPattern('spl_signon', pattern_u_boot_spl_signon),
NamedPattern('main_signon', pattern_u_boot_main_signon),
NamedPattern('stop_autoboot_prompt', pattern_stop_autoboot_prompt),
NamedPattern('unknown_command', pattern_unknown_command),
NamedPattern('error_notification', pattern_error_notification),
NamedPattern('error_please_reset', pattern_error_please_reset),
) )
class ConsoleDisableCheck(object):
class Timeout(Exception):
"""An exception sub-class that indicates that a timeout occurred."""
class BootFail(Exception):
"""An exception sub-class that indicates that a boot failure occurred.
This is used when a bad pattern is seen when waiting for the boot prompt.
It is regarded as fatal, to avoid trying to boot the again and again to no
avail.
"""
class Unexpected(Exception):
"""An exception sub-class that indicates that unexpected test was seen."""
def handle_exception(ubconfig, console, log, err, name, fatal, output=''):
"""Handle an exception from the console
Exceptions can occur when there is unexpected output or due to the board
crashing or hanging. Some exceptions are likely fatal, where retrying will
just chew up time to no available. In those cases it is best to cause
further tests be skipped.
Args:
ubconfig (ArbitraryAttributeContainer): ubconfig object
log (Logfile): Place to log errors
console (ConsoleBase): Console to clean up, if fatal
err (Exception): Exception which was thrown
name (str): Name of problem, to log
fatal (bool): True to abort all tests
output (str): Extra output to report on boot failure. This can show the
target's console output as it tried to boot
"""
msg = f'{name}: '
if fatal:
msg += 'Marking connection bad - no other tests will run'
else:
msg += 'Assuming that lab is healthy'
print(msg)
log.error(msg)
log.error(f'Error: {err}')
if output:
msg += f'; output {output}'
if fatal:
ubconfig.connection_ok = False
console.cleanup_spawn()
pytest.exit(msg)
class ConsoleDisableCheck():
"""Context manager (for Python's with statement) that temporarily disables """Context manager (for Python's with statement) that temporarily disables
the specified console output error check. This is useful when deliberately the specified console output error check. This is useful when deliberately
executing a command that is known to trigger one of the error checks, in executing a command that is known to trigger one of the error checks, in
@@ -63,13 +123,14 @@ class ConsoleDisableCheck(object):
def __enter__(self): def __enter__(self):
self.console.disable_check_count[self.check_type] += 1 self.console.disable_check_count[self.check_type] += 1
self.console.eval_bad_patterns() self.console.eval_patterns()
def __exit__(self, extype, value, traceback): def __exit__(self, extype, value, traceback):
self.console.disable_check_count[self.check_type] -= 1 self.console.disable_check_count[self.check_type] -= 1
self.console.eval_bad_patterns() self.console.eval_patterns()
class ConsoleEnableCheck(object):
class ConsoleEnableCheck():
"""Context manager (for Python's with statement) that temporarily enables """Context manager (for Python's with statement) that temporarily enables
the specified console output error check. This is useful when executing a the specified console output error check. This is useful when executing a
command that might raise an extra bad pattern, beyond the default bad command that might raise an extra bad pattern, beyond the default bad
@@ -81,37 +142,40 @@ class ConsoleEnableCheck(object):
self.console = console self.console = console
self.check_type = check_type self.check_type = check_type
self.check_pattern = check_pattern self.check_pattern = check_pattern
self.default_bad_patterns = None
def __enter__(self): def __enter__(self):
global bad_pattern_defs cons = self.console
self.default_bad_patterns = bad_pattern_defs self.default_bad_patterns = cons.avail_patterns
bad_pattern_defs += ((self.check_type, self.check_pattern),) cons.avail_patterns.append((self.check_type, self.check_pattern))
self.console.disable_check_count = {pat[PAT_ID]: 0 for pat in bad_pattern_defs} cons.disable_check_count = {pat.name: 0 for pat in PATTERNS}
self.console.eval_bad_patterns() cons.eval_patterns()
def __exit__(self, extype, value, traceback): def __exit__(self, extype, value, traceback):
global bad_pattern_defs cons = self.console
bad_pattern_defs = self.default_bad_patterns cons.avail_patterns = self.default_bad_patterns
self.console.disable_check_count = {pat[PAT_ID]: 0 for pat in bad_pattern_defs} cons.disable_check_count = {pat.name: 0 for pat in PATTERNS}
self.console.eval_bad_patterns() cons.eval_patterns()
class ConsoleSetupTimeout(object):
class ConsoleSetupTimeout():
"""Context manager (for Python's with statement) that temporarily sets up """Context manager (for Python's with statement) that temporarily sets up
timeout for specific command. This is useful when execution time is greater timeout for specific command. This is useful when execution time is greater
then default 30s.""" then default 30s."""
def __init__(self, console, timeout): def __init__(self, console, timeout):
self.p = console.p self.console = console
self.orig_timeout = self.p.timeout self.orig_timeout = self.console.timeout
self.p.timeout = timeout self.console.timeout = timeout
def __enter__(self): def __enter__(self):
return self return self
def __exit__(self, extype, value, traceback): def __exit__(self, extype, value, traceback):
self.p.timeout = self.orig_timeout self.console.timeout = self.orig_timeout
class ConsoleBase(object):
class ConsoleBase():
"""The interface through which test functions interact with the U-Boot """The interface through which test functions interact with the U-Boot
console. This primarily involves executing shell commands, capturing their console. This primarily involves executing shell commands, capturing their
results, and checking for common error conditions. Some common utilities results, and checking for common error conditions. Some common utilities
@@ -123,20 +187,49 @@ class ConsoleBase(object):
Can only usefully be called by sub-classes. Can only usefully be called by sub-classes.
Args: Args:
log: A multiplexed_log.Logfile object, to which the U-Boot output log (multiplexed_log.Logfile): Log to which the U-Boot output is
will be logged. logged.
config: A configuration data structure, as built by conftest.py. config (ArbitraryAttributeContainer): ubman_fix.config, as built by
max_fifo_fill: The maximum number of characters to send to U-Boot conftest.py.
max_fifo_fill (int): The max number of characters to send to U-Boot
command-line before waiting for U-Boot to echo the characters command-line before waiting for U-Boot to echo the characters
back. For UART-based HW without HW flow control, this value back. For UART-based HW without HW flow control, this value
should be set less than the UART RX FIFO size to avoid should be set less than the UART RX FIFO size to avoid
overflow, assuming that U-Boot can't keep up with full-rate overflow, assuming that U-Boot can't keep up with full-rate
traffic at the baud rate. traffic at the baud rate.
Returns: Properties:
Nothing. logstream (LogfileStream): Log stream being used
prompt (str): Prompt string expected from U-Boot
p (spawn.Spawn): Means of communicating with running U-Boot via a
console
avail_patterns (list of NamedPattern): Normally the same as
PATTERNS but can be adjusted by tests
disable_check_count: dict of 'nest counts' for patterns
key (str): NamedPattern.name
value (int): 0 if not disabled, >0 for the number of 'requests
to disable' that have been received for this pattern
at_prompt (bool): True if the running U-Boot is at a prompt and
thus ready to receive commands
at_prompt_logevt (int): Logstream event number when the prompt was
detected. This is used to avoid logging the prompt twice
lab_mode (bool): True if the lab is responsible for getting U-Boot
to a prompt, i.e. able to process commands on the console
u_boot_version_string (str): Version string obtained from U-Boot as
it booted. In lab mode this is provided by
pattern_ready_prompt
buf (str): Buffer of characters received from the console, still to
be processed
output (str); All data received from the console
before (str): Data before the matching string
after (str): String which patches the expected output
timeout (str): Timeout in seconds before giving up and aborting the
test
logfile_read (multiplexed_log.Logfile): Logfile used for logging
output
re_vt100 (re.Regex): Regex for filtering out vt100 characters
output: accumulated output from expect()
""" """
self.log = log self.log = log
self.config = config self.config = config
self.max_fifo_fill = max_fifo_fill self.max_fifo_fill = max_fifo_fill
@@ -147,26 +240,38 @@ class ConsoleBase(object):
self.prompt = self.config.buildconfig['config_sys_prompt'][1:-1] self.prompt = self.config.buildconfig['config_sys_prompt'][1:-1]
self.prompt_compiled = re.compile('^' + re.escape(self.prompt), re.MULTILINE) self.prompt_compiled = re.compile('^' + re.escape(self.prompt), re.MULTILINE)
self.p = None self.p = None
self.disable_check_count = {pat[PAT_ID]: 0 for pat in bad_pattern_defs} self.avail_patterns = PATTERNS
self.eval_bad_patterns() self.disable_check_count = {pat.name: 0 for pat in self.avail_patterns}
self.at_prompt = False self.at_prompt = False
self.at_prompt_logevt = None self.at_prompt_logevt = None
self.lab_mode = False self.lab_mode = False
self.u_boot_version_string = None
self.buf = ''
self.output = ''
self.before = ''
self.after = ''
self.timeout = None
self.logfile_read = None
# http://stackoverflow.com/questions/7857352/python-regex-to-match-vt100-escape-sequences
self.re_vt100 = re.compile(r'(\x1b\[|\x9b)[^@-_]*[@-_]|\x1b[@-_]', re.I)
self.eval_patterns()
def get_spawn(self): def get_spawn(self):
# This is not called, ssubclass must define this. """This is not called, ssubclass must define this.
# Return a value to avoid:
# console_base.py:348:12: E1128: Assigning result of a function Return a value to avoid:
# call, where the function returns None (assignment-from-none) console_base.py:348:12: E1128: Assigning result of a function
call, where the function returns None (assignment-from-none)
"""
return spawn.Spawn([]) return spawn.Spawn([])
def eval_patterns(self):
def eval_bad_patterns(self): """Set up lists of regexes for patterns we don't expect on console"""
self.bad_patterns = [pat[PAT_RE] for pat in bad_pattern_defs \ self.bad_patterns = [pat.pattern for pat in self.avail_patterns
if self.disable_check_count[pat[PAT_ID]] == 0] if not self.disable_check_count[pat.name]]
self.bad_pattern_ids = [pat[PAT_ID] for pat in bad_pattern_defs \ self.bad_pattern_ids = [pat.name for pat in self.avail_patterns
if self.disable_check_count[pat[PAT_ID]] == 0] if not self.disable_check_count[pat.name]]
def close(self): def close(self):
"""Terminate the connection to the U-Boot console. """Terminate the connection to the U-Boot console.
@@ -177,11 +282,7 @@ class ConsoleBase(object):
Args: Args:
None. None.
Returns:
Nothing.
""" """
if self.p: if self.p:
self.log.start_section('Stopping U-Boot') self.log.start_section('Stopping U-Boot')
close_type = self.p.close() close_type = self.p.close()
@@ -195,54 +296,32 @@ class ConsoleBase(object):
This tells us that we will get a 'lab ready' message when the board is This tells us that we will get a 'lab ready' message when the board is
ready for use. We don't need to look for signon messages. ready for use. We don't need to look for signon messages.
""" """
self.log.info(f'test.py: Lab mode is active') self.log.info('test.py: Lab mode is active')
self.p.timeout = TIMEOUT_PREPARE_MS self.timeout = TIMEOUT_PREPARE_MS
self.lab_mode = True self.lab_mode = True
def wait_for_boot_prompt(self, loop_num = 1): def _wait_for_boot_prompt(self, loop_num=1):
"""Wait for the boot up until command prompt. This is for internal use only. """Wait for the boot up until command prompt.
This is for internal use only.
""" """
try: try:
self.log.info('Waiting for U-Boot to be ready') self.log.info('Waiting for U-Boot to be ready')
bcfg = self.config.buildconfig
config_spl_serial = bcfg.get('config_spl_serial', 'n') == 'y'
env_spl_skipped = self.config.env.get('env__spl_skipped', False)
env_spl_banner_times = self.config.env.get('env__spl_banner_times', 1)
while not self.lab_mode and loop_num > 0:
loop_num -= 1
while config_spl_serial and not env_spl_skipped and env_spl_banner_times > 0:
m = self.p.expect([pattern_u_boot_spl_signon,
pattern_lab_mode] + self.bad_patterns)
if m == 1:
self.set_lab_mode()
break
elif m != 0:
raise BootFail('Bad pattern found on SPL console: ' +
self.bad_pattern_ids[m - 1])
env_spl_banner_times -= 1
if not self.lab_mode:
m = self.p.expect([pattern_u_boot_main_signon,
pattern_lab_mode] + self.bad_patterns)
if m == 1:
self.set_lab_mode()
elif m != 0:
raise BootFail('Bad pattern found on console: ' +
self.bad_pattern_ids[m - 1])
if not self.lab_mode: if not self.lab_mode:
self.u_boot_version_string = self.p.after self._wait_for_banner(loop_num)
self.u_boot_version_string = self.after
while True: while True:
m = self.p.expect([self.prompt_compiled, pattern_ready_prompt, m = self.expect([self.prompt_compiled, pattern_ready_prompt,
pattern_stop_autoboot_prompt] + self.bad_patterns) pattern_stop_autoboot_prompt] + self.bad_patterns)
if m == 0: if m == 0:
self.log.info(f'Found ready prompt {m}') self.log.info(f'Found ready prompt {m}')
break break
elif m == 1: if m == 1:
m = pattern_ready_prompt.search(self.p.after) m = pattern_ready_prompt.search(self.after)
self.u_boot_version_string = m.group(2) self.u_boot_version_string = m.group(2)
self.log.info(f'Lab: Board is ready') self.log.info('Lab: Board is ready')
self.p.timeout = TIMEOUT_MS self.timeout = TIMEOUT_MS
break break
if m == 2: if m == 2:
self.log.info(f'Found autoboot prompt {m}') self.log.info(f'Found autoboot prompt {m}')
@@ -251,13 +330,46 @@ class ConsoleBase(object):
if not self.lab_mode: if not self.lab_mode:
raise BootFail('Missing prompt / ready message on console: ' + raise BootFail('Missing prompt / ready message on console: ' +
self.bad_pattern_ids[m - 3]) self.bad_pattern_ids[m - 3])
self.log.info(f'U-Boot is ready') self.log.info('U-Boot is ready')
finally: finally:
self.log.timestamp() self.log.timestamp()
def _wait_for_banner(self, loop_num):
"""Wait for a U-Boot banner to appear on the console
Args:
loop_num (int): Number of times to expect a banner (used for when
U-Boot is expected to start up and then reset itself)
"""
bcfg = self.config.buildconfig
config_spl_serial = bcfg.get('config_spl_serial', 'n') == 'y'
env_spl_skipped = self.config.env.get('env__spl_skipped', False)
env_spl_banner_times = self.config.env.get('env__spl_banner_times', 1)
while loop_num > 0:
loop_num -= 1
while config_spl_serial and not env_spl_skipped and env_spl_banner_times > 0:
m = self.expect([pattern_u_boot_spl_signon,
pattern_lab_mode] + self.bad_patterns)
if m == 1:
self.set_lab_mode()
break
if m != 0:
raise BootFail('Bad pattern found on SPL console: ' +
self.bad_pattern_ids[m - 1])
env_spl_banner_times -= 1
if not self.lab_mode:
m = self.expect([pattern_u_boot_main_signon,
pattern_lab_mode] + self.bad_patterns)
if m == 1:
self.set_lab_mode()
elif m != 0:
raise BootFail('Bad pattern found on console: ' +
self.bad_pattern_ids[m - 1])
def run_command(self, cmd, wait_for_echo=True, send_nl=True, def run_command(self, cmd, wait_for_echo=True, send_nl=True,
wait_for_prompt=True, wait_for_reboot=False): wait_for_prompt=True, wait_for_reboot=False):
"""Execute a command via the U-Boot console. """Execute a command via the U-Boot console.
The command is always sent to U-Boot. The command is always sent to U-Boot.
@@ -277,27 +389,25 @@ class ConsoleBase(object):
running command such as "ums". running command such as "ums".
Args: Args:
cmd: The command to send. cmd (str): The command to send.
wait_for_echo: Boolean indicating whether to wait for U-Boot to wait_for_echo (bool): Indicates whether to wait for U-Boot to
echo the command text back to its output. echo the command text back to its output.
send_nl: Boolean indicating whether to send a newline character send_nl (bool): Indicates whether to send a newline character
after the command string. after the command string.
wait_for_prompt: Boolean indicating whether to wait for the wait_for_prompt (bool): Indicates whether to wait for the
command prompt to be sent by U-Boot. This typically occurs command prompt to be sent by U-Boot. This typically occurs
immediately after the command has been executed. immediately after the command has been executed.
wait_for_reboot: Boolean indication whether to wait for the wait_for_reboot (bool): Indicates whether to wait U-Boot ro reboot.
reboot U-Boot. If this sets True, wait_for_prompt must also If True, wait_for_prompt must also be True.
be True.
Returns: Returns:
If wait_for_prompt == False: If wait_for_prompt == False:
Nothing. Empty string.
Else: Else:
The output from U-Boot during command execution. In other The output from U-Boot during command execution. In other
words, the text U-Boot emitted between the point it echod the words, the text U-Boot emitted between the point it echod the
command string and emitted the subsequent command prompts. command string and emitted the subsequent command prompts.
""" """
if self.at_prompt and \ if self.at_prompt and \
self.at_prompt_logevt != self.logstream.logfile.cur_evt: self.at_prompt_logevt != self.logstream.logfile.cur_evt:
self.logstream.write(self.prompt, implicit=True) self.logstream.write(self.prompt, implicit=True)
@@ -321,17 +431,18 @@ class ConsoleBase(object):
continue continue
chunk = re.escape(chunk) chunk = re.escape(chunk)
chunk = chunk.replace('\\\n', '[\r\n]') chunk = chunk.replace('\\\n', '[\r\n]')
m = self.p.expect([chunk] + self.bad_patterns) m = self.expect([chunk] + self.bad_patterns)
if m != 0: if m != 0:
self.at_prompt = False self.at_prompt = False
raise BootFail(f"Failed to get echo on console (cmd '{cmd}':rem '{rem}'): " + raise BootFail('Failed to get echo on console '
self.bad_pattern_ids[m - 1]) f"(cmd '{cmd}':rem '{rem}'): " +
self.bad_pattern_ids[m - 1])
if not wait_for_prompt: if not wait_for_prompt:
return return ''
if wait_for_reboot: if wait_for_reboot:
self.wait_for_boot_prompt() self._wait_for_boot_prompt()
else: else:
m = self.p.expect([self.prompt_compiled] + self.bad_patterns) m = self.expect([self.prompt_compiled] + self.bad_patterns)
if m != 0: if m != 0:
self.at_prompt = False self.at_prompt = False
raise BootFail('Missing prompt on console: ' + raise BootFail('Missing prompt on console: ' +
@@ -340,7 +451,7 @@ class ConsoleBase(object):
self.at_prompt_logevt = self.logstream.logfile.cur_evt self.at_prompt_logevt = self.logstream.logfile.cur_evt
# Only strip \r\n; space/TAB might be significant if testing # Only strip \r\n; space/TAB might be significant if testing
# indentation. # indentation.
return self.p.before.strip('\r\n') return self.before.strip('\r\n')
except Timeout as exc: except Timeout as exc:
handle_exception(self.config, self, self.log, exc, handle_exception(self.config, self, self.log, exc,
f"Lab failure: Timeout executing '{cmd}'", True) f"Lab failure: Timeout executing '{cmd}'", True)
@@ -352,6 +463,7 @@ class ConsoleBase(object):
raise raise
finally: finally:
self.log.timestamp() self.log.timestamp()
return ''
def run_command_list(self, cmds): def run_command_list(self, cmds):
"""Run a list of commands. """Run a list of commands.
@@ -360,7 +472,7 @@ class ConsoleBase(object):
for each command in a list. for each command in a list.
Args: Args:
cmd: List of commands (each a string). cmds (list of str): List of commands
Returns: Returns:
A list of output strings from each command, one element for each A list of output strings from each command, one element for each
command. command.
@@ -403,16 +515,12 @@ class ConsoleBase(object):
location in the log file. location in the log file.
Args: Args:
text: The text to wait for; either a string (containing raw text, text (str or re): The text to wait for; either a string (containing
not a regular expression) or an re object. raw text, not a regular expression) or an re object.
Returns:
Nothing.
""" """
if isinstance(text, str):
if type(text) == type(''):
text = re.escape(text) text = re.escape(text)
m = self.p.expect([text] + self.bad_patterns) m = self.expect([text] + self.bad_patterns)
if m != 0: if m != 0:
raise Unexpected( raise Unexpected(
"Unexpected pattern found on console (exp '{text}': " + "Unexpected pattern found on console (exp '{text}': " +
@@ -428,14 +536,7 @@ class ConsoleBase(object):
exists. In such a case, it is useful to log U-Boot's console output exists. In such a case, it is useful to log U-Boot's console output
in case U-Boot printed clues as to why the host-side even did not in case U-Boot printed clues as to why the host-side even did not
occur. This function will do that. occur. This function will do that.
Args:
None.
Returns:
Nothing.
""" """
# If we are already not connected to U-Boot, there's nothing to drain. # If we are already not connected to U-Boot, there's nothing to drain.
# This should only happen when a previous call to run_command() or # This should only happen when a previous call to run_command() or
# wait_for() failed (and hence the output has already been logged), or # wait_for() failed (and hence the output has already been logged), or
@@ -443,13 +544,13 @@ class ConsoleBase(object):
if not self.p: if not self.p:
return return
orig_timeout = self.p.timeout orig_timeout = self.timeout
try: try:
# Drain the log for a relatively short time. # Drain the log for a relatively short time.
self.p.timeout = 1000 self.timeout = 1000
# Wait for something U-Boot will likely never send. This will # Wait for something U-Boot will likely never send. This will
# cause the console output to be read and logged. # cause the console output to be read and logged.
self.p.expect(['This should never match U-Boot output']) self.expect(['This should never match U-Boot output'])
except: except:
# We expect a timeout, since U-Boot won't print what we waited # We expect a timeout, since U-Boot won't print what we waited
# for. Squash it when it happens. # for. Squash it when it happens.
@@ -463,7 +564,7 @@ class ConsoleBase(object):
# correctly terminate any log sections, etc. # correctly terminate any log sections, etc.
pass pass
finally: finally:
self.p.timeout = orig_timeout self.timeout = orig_timeout
def ensure_spawned(self, expect_reset=False): def ensure_spawned(self, expect_reset=False):
"""Ensure a connection to a correctly running U-Boot instance. """Ensure a connection to a correctly running U-Boot instance.
@@ -474,19 +575,15 @@ class ConsoleBase(object):
This is an internal function and should not be called directly. This is an internal function and should not be called directly.
Args: Args:
expect_reset: Boolean indication whether this boot is expected expect_reset (bool): Indicates whether this boot is expected
to be reset while the 1st boot process after main boot before to be reset while the 1st boot process after main boot before
prompt. False by default. prompt. False by default.
Returns:
Nothing.
""" """
if self.p: if self.p:
# Reset the console timeout value as some tests may change # Reset the console timeout value as some tests may change
# its default value during the execution # its default value during the execution
if not self.config.gdbserver: if not self.config.gdbserver:
self.p.timeout = TIMEOUT_MS self.timeout = TIMEOUT_MS
return return
try: try:
self.log.start_section('Starting U-Boot') self.log.start_section('Starting U-Boot')
@@ -497,8 +594,8 @@ class ConsoleBase(object):
# future, possibly per-test to be optimal. This works for 'help' # future, possibly per-test to be optimal. This works for 'help'
# on board 'seaboard'. # on board 'seaboard'.
if not self.config.gdbserver: if not self.config.gdbserver:
self.p.timeout = TIMEOUT_MS self.timeout = TIMEOUT_MS
self.p.logfile_read = self.logstream self.logfile_read = self.logstream
if self.config.use_running_system: if self.config.use_running_system:
# Send an empty command to set up the 'expect' logic. This has # Send an empty command to set up the 'expect' logic. This has
# the side effect of ensuring that there was no partial command # the side effect of ensuring that there was no partial command
@@ -509,7 +606,7 @@ class ConsoleBase(object):
loop_num = 2 loop_num = 2
else: else:
loop_num = 1 loop_num = 1
self.wait_for_boot_prompt(loop_num = loop_num) self._wait_for_boot_prompt(loop_num = loop_num)
self.at_prompt = True self.at_prompt = True
self.at_prompt_logevt = self.logstream.logfile.cur_evt self.at_prompt_logevt = self.logstream.logfile.cur_evt
except Exception as ex: except Exception as ex:
@@ -527,14 +624,7 @@ class ConsoleBase(object):
connection with a fresh U-Boot instance. connection with a fresh U-Boot instance.
This is an internal function and should not be called directly. This is an internal function and should not be called directly.
Args:
None.
Returns:
Nothing.
""" """
try: try:
if self.p: if self.p:
self.p.close() self.p.close()
@@ -542,6 +632,16 @@ class ConsoleBase(object):
pass pass
self.p = None self.p = None
def shutdown_required(self):
"""Called to shut down the running U-Boot
Some tests make changes to U-Boot which cannot be undone within the
test, such as booting an operating system. This function shuts down
U-Boot so that a new one will be started for any future tests
"""
self.drain_console()
self.cleanup_spawn()
def restart_uboot(self, expect_reset=False): def restart_uboot(self, expect_reset=False):
"""Shut down and restart U-Boot.""" """Shut down and restart U-Boot."""
self.cleanup_spawn() self.cleanup_spawn()
@@ -554,7 +654,7 @@ class ConsoleBase(object):
The output produced by ensure_spawed(), as a string. The output produced by ensure_spawed(), as a string.
""" """
if self.p: if self.p:
return self.p.get_expect_output() return self.get_expect_output()
return None return None
def validate_version_string_in_text(self, text): def validate_version_string_in_text(self, text):
@@ -564,13 +664,12 @@ class ConsoleBase(object):
duplicating the signon text regex in a test function. duplicating the signon text regex in a test function.
Args: Args:
text: The command output text to check. text (str): The command output text to check.
Returns: Raises:
Nothing. An exception is raised if the validation fails. Assertion if the validation fails.
""" """
assert self.u_boot_version_string in text
assert(self.u_boot_version_string in text)
def disable_check(self, check_type): def disable_check(self, check_type):
"""Temporarily disable an error check of U-Boot's output. """Temporarily disable an error check of U-Boot's output.
@@ -579,13 +678,12 @@ class ConsoleBase(object):
temporarily disables a particular console output error check. temporarily disables a particular console output error check.
Args: Args:
check_type: The type of error-check to disable. Valid values may check_type (str): The type of error-check to disable, see
be found in self.disable_check_count above. bad_pattern_defs
Returns: Returns:
A context manager object. A context manager object.
""" """
return ConsoleDisableCheck(self, check_type) return ConsoleDisableCheck(self, check_type)
def enable_check(self, check_type, check_pattern): def enable_check(self, check_type, check_pattern):
@@ -596,14 +694,14 @@ class ConsoleBase(object):
arguments form a new element of bad_pattern_defs defined above. arguments form a new element of bad_pattern_defs defined above.
Args: Args:
check_type: The type of error-check or bad pattern to enable. check_type (str): The type of error-check to disable, see
check_pattern: The regexes for text error pattern or bad pattern bad_pattern_defs
check_pattern (re.Pattern): Regex for text error / bad pattern
to be checked. to be checked.
Returns: Returns:
A context manager object. A context manager object.
""" """
return ConsoleEnableCheck(self, check_type, check_pattern) return ConsoleEnableCheck(self, check_type, check_pattern)
def temporary_timeout(self, timeout): def temporary_timeout(self, timeout):
@@ -613,10 +711,83 @@ class ConsoleBase(object):
temporarily change timeout. temporarily change timeout.
Args: Args:
timeout: Time in milliseconds. timeout (int): Time in milliseconds.
Returns: Returns:
A context manager object. A context manager object.
""" """
return ConsoleSetupTimeout(self, timeout) return ConsoleSetupTimeout(self, timeout)
def expect(self, patterns):
"""Wait for the sub-process to emit specific data.
This function waits for the process to emit one pattern from the
supplied list of patterns, or for a timeout to occur.
Args:
patterns (list of str or regex.Regex): Patterns we expect to
see in the sub-process' stdout.
Returns:
int: index within the patterns array of the pattern the process
emitted.
Notable exceptions:
Timeout, if the process did not emit any of the patterns within
the expected time.
"""
for pi, pat in enumerate(patterns):
if isinstance(pat, str):
patterns[pi] = re.compile(pat)
tstart_s = time.time()
try:
while True:
earliest_m = None
earliest_pi = None
for pi, pat in enumerate(patterns):
m = pat.search(self.buf)
if not m:
continue
if earliest_m and m.start() >= earliest_m.start():
continue
earliest_m = m
earliest_pi = pi
if earliest_m:
pos = earliest_m.start()
posafter = earliest_m.end()
self.before = self.buf[:pos]
self.after = self.buf[pos:posafter]
self.output += self.buf[:posafter]
self.buf = self.buf[posafter:]
return earliest_pi
tnow_s = time.time()
if self.timeout:
tdelta_ms = (tnow_s - tstart_s) * 1000
poll_maxwait = self.timeout - tdelta_ms
if tdelta_ms > self.timeout:
raise Timeout()
else:
poll_maxwait = None
events = self.p.poll.poll(poll_maxwait)
if not events:
raise Timeout()
c = self.p.receive(1024)
if self.logfile_read:
self.logfile_read.write(c)
self.buf += c
# count=0 is supposed to be the default, which indicates
# unlimited substitutions, but in practice the version of
# Python in Ubuntu 14.04 appears to default to count=2!
self.buf = self.re_vt100.sub('', self.buf, count=1000000)
finally:
if self.logfile_read:
self.logfile_read.flush()
def get_expect_output(self):
"""Return the output read by expect()
Returns:
The output processed by expect(), as a string.
"""
return self.output

View File

@@ -34,7 +34,7 @@ class ConsoleExecAttach(ConsoleBase):
# 1 would be safe anywhere, but is very slow (a pexpect issue?). # 1 would be safe anywhere, but is very slow (a pexpect issue?).
# 16 is a common FIFO size. # 16 is a common FIFO size.
# HW flow control would mean this could be infinite. # HW flow control would mean this could be infinite.
super(ConsoleExecAttach, self).__init__(log, config, max_fifo_fill=16) super().__init__(log, config, max_fifo_fill=16)
with self.log.section('flash'): with self.log.section('flash'):
self.log.action('Flashing U-Boot') self.log.action('Flashing U-Boot')

View File

@@ -18,14 +18,11 @@ class ConsoleSandbox(ConsoleBase):
"""Initialize a U-Boot console connection. """Initialize a U-Boot console connection.
Args: Args:
log: A multiplexed_log.Logfile instance. log (multiplexed_log.Logfile): Log file to write to
config: A "configuration" object as defined in conftest.py. config (ArbitraryAttributeContainer): ubconfig "configuration"
object as defined in conftest.py
Returns:
Nothing.
""" """
super().__init__(log, config, max_fifo_fill=1024)
super(ConsoleSandbox, self).__init__(log, config, max_fifo_fill=1024)
self.sandbox_flags = [] self.sandbox_flags = []
self.use_dtb = True self.use_dtb = True
@@ -35,13 +32,9 @@ class ConsoleSandbox(ConsoleBase):
A new sandbox process is created, so that U-Boot begins running from A new sandbox process is created, so that U-Boot begins running from
scratch. scratch.
Args:
None.
Returns: Returns:
A spawn.Spawn object that is attached to U-Boot. A spawn.Spawn object that is attached to U-Boot.
""" """
bcfg = self.config.buildconfig bcfg = self.config.buildconfig
config_spl = bcfg.get('config_spl', 'n') == 'y' config_spl = bcfg.get('config_spl', 'n') == 'y'
config_vpl = bcfg.get('config_vpl', 'n') == 'y' config_vpl = bcfg.get('config_vpl', 'n') == 'y'
@@ -64,16 +57,16 @@ class ConsoleSandbox(ConsoleBase):
"""Run U-Boot with the given command-line flags """Run U-Boot with the given command-line flags
Args: Args:
flags: List of flags to pass, each a string flags (list of str): List of flags to pass
expect_reset: Boolean indication whether this boot is expected expect_reset (bool): Indication whether this boot is expected
to be reset while the 1st boot process after main boot before to be reset while the 1st boot process after main boot before
prompt. False by default. prompt. False by default.
use_dtb: True to use a device tree file, False to run without one use_dtb (bool): True to use a device tree file, False to run without
one
Returns: Returns:
A spawn.Spawn object that is attached to U-Boot. A spawn.Spawn object that is attached to U-Boot.
""" """
try: try:
self.sandbox_flags = flags self.sandbox_flags = flags
self.use_dtb = use_dtb self.use_dtb = use_dtb
@@ -86,13 +79,9 @@ class ConsoleSandbox(ConsoleBase):
"""Send a specific Unix signal to the sandbox process. """Send a specific Unix signal to the sandbox process.
Args: Args:
sig: The Unix signal to send to the process. sig (int): Unix signal to send to the process
Returns:
Nothing.
""" """
self.log.action(f'kill {sig}')
self.log.action('kill %d' % sig)
self.p.kill(sig) self.p.kill(sig)
def validate_exited(self): def validate_exited(self):
@@ -101,16 +90,12 @@ class ConsoleSandbox(ConsoleBase):
If required, this function waits a reasonable time for the process to If required, this function waits a reasonable time for the process to
exit. exit.
Args:
None.
Returns: Returns:
Boolean indicating whether the process has exited. Boolean indicating whether the process has exited.
""" """
p = self.p p = self.p
self.p = None self.p = None
for i in range(100): for _ in range(100):
ret = not p.isalive() ret = not p.isalive()
if ret: if ret:
break break

View File

@@ -3,13 +3,29 @@
""" """
Logic to spawn a sub-process and interact with its stdio. Logic to spawn a sub-process and interact with its stdio.
This is used by console_board and console_sandbox
- console_board (for real hardware): Spawns 'u-boot-test-console' and provides
access to the console input/output
- console_sandbox (for sandbox): Spawns 'u-boot' and provides access to the
console input/output
In both cases, Spawn provides a way to send console commands and receive the
response from U-Boot. An expect() function helps to simplify things for the
higher levels.
The code in this file should be generic, i.e. not specific to sandbox or real
hardware.
Within the console_*py files, self.p is used to refer to the Spawn() object,
perhaps short for 'process'.
""" """
import io import io
import os import os
import re import re
import pty import pty
import pytest
import signal import signal
import select import select
import sys import sys
@@ -20,72 +36,20 @@ import traceback
# Character to send (twice) to exit the terminal # Character to send (twice) to exit the terminal
EXIT_CHAR = 0x1d # FS (Ctrl + ]) EXIT_CHAR = 0x1d # FS (Ctrl + ])
class Timeout(Exception):
"""An exception sub-class that indicates that a timeout occurred."""
class BootFail(Exception):
"""An exception sub-class that indicates that a boot failure occurred.
This is used when a bad pattern is seen when waiting for the boot prompt.
It is regarded as fatal, to avoid trying to boot the again and again to no
avail.
"""
class Unexpected(Exception):
"""An exception sub-class that indicates that unexpected test was seen."""
def handle_exception(ubconfig, console, log, err, name, fatal, output=''):
"""Handle an exception from the console
Exceptions can occur when there is unexpected output or due to the board
crashing or hanging. Some exceptions are likely fatal, where retrying will
just chew up time to no available. In those cases it is best to cause
further tests be skipped.
Args:
ubconfig (ArbitraryAttributeContainer): ubconfig object
log (Logfile): Place to log errors
console (ConsoleBase): Console to clean up, if fatal
err (Exception): Exception which was thrown
name (str): Name of problem, to log
fatal (bool): True to abort all tests
output (str): Extra output to report on boot failure. This can show the
target's console output as it tried to boot
"""
msg = f'{name}: '
if fatal:
msg += 'Marking connection bad - no other tests will run'
else:
msg += 'Assuming that lab is healthy'
print(msg)
log.error(msg)
log.error(f'Error: {err}')
if output:
msg += f'; output {output}'
if fatal:
ubconfig.connection_ok = False
console.cleanup_spawn()
pytest.exit(msg)
class Spawn: class Spawn:
"""Represents the stdio of a freshly created sub-process. Commands may be """Represents the stdio of a freshly created sub-process. Commands may be
sent to the process, and responses waited for. sent to the process, and responses waited for.
Members:
output: accumulated output from expect()
""" """
def __init__(self, args, cwd=None, decode_signal=False): def __init__(self, args, cwd=None, decode_signal=False):
"""Spawn (fork/exec) the sub-process. """Spawn (fork/exec) the sub-process.
Args: Args:
args: array of processs arguments. argv[0] is the command to args (list of str): processs arguments. argv[0] is the command to
execute. execute.
cwd: the directory to run the process in, or None for no change. cwd (str or None): the directory to run the process in, or None for
no change.
decode_signal (bool): True to indicate the exception number when decode_signal (bool): True to indicate the exception number when
something goes wrong something goes wrong
@@ -96,14 +60,6 @@ class Spawn:
self.waited = False self.waited = False
self.exit_code = 0 self.exit_code = 0
self.exit_info = '' self.exit_info = ''
self.buf = ''
self.output = ''
self.logfile_read = None
self.before = ''
self.after = ''
self.timeout = None
# http://stackoverflow.com/questions/7857352/python-regex-to-match-vt100-escape-sequences
self.re_vt100 = re.compile(r'(\x1b\[|\x9b)[^@-_]*[@-_]|\x1b[@-_]', re.I)
(self.pid, self.fd) = pty.fork() (self.pid, self.fd) = pty.fork()
if self.pid == 0: if self.pid == 0:
@@ -128,7 +84,7 @@ class Spawn:
isatty = os.isatty(sys.stdout.fileno()) isatty = os.isatty(sys.stdout.fileno())
# with --capture=tee-sys we cannot call fileno() # with --capture=tee-sys we cannot call fileno()
except io.UnsupportedOperation as exc: except io.UnsupportedOperation:
pass pass
if isatty: if isatty:
new = termios.tcgetattr(self.fd) new = termios.tcgetattr(self.fd)
@@ -152,12 +108,8 @@ class Spawn:
"""Send unix signal "sig" to the child process. """Send unix signal "sig" to the child process.
Args: Args:
sig: The signal number to send. sig (int): The signal number to send
Returns:
Nothing.
""" """
os.kill(self.pid, sig) os.kill(self.pid, sig)
def checkalive(self): def checkalive(self):
@@ -169,7 +121,6 @@ class Spawn:
0 if process is alive, else exit code of process 0 if process is alive, else exit code of process
string describing what happened ('' or 'status/signal n') string describing what happened ('' or 'status/signal n')
""" """
if self.waited: if self.waited:
return False, self.exit_code, self.exit_info return False, self.exit_code, self.exit_info
@@ -180,22 +131,19 @@ class Spawn:
if os.WIFEXITED(status): if os.WIFEXITED(status):
self.exit_code = os.WEXITSTATUS(status) self.exit_code = os.WEXITSTATUS(status)
self.exit_info = 'status %d' % self.exit_code self.exit_info = f'status {self.exit_code}'
elif os.WIFSIGNALED(status): elif os.WIFSIGNALED(status):
signum = os.WTERMSIG(status) signum = os.WTERMSIG(status)
self.exit_code = -signum self.exit_code = -signum
self.exit_info = 'signal %d (%s)' % (signum, signal.Signals(signum).name) self.exit_info = f'signal {signum} ({signal.Signals(signum).name})'
self.waited = True self.waited = True
return False, self.exit_code, self.exit_info return False, self.exit_code, self.exit_info
def isalive(self): def isalive(self):
"""Determine whether the child process is still running. """Determine whether the child process is still running.
Args:
None.
Returns: Returns:
Boolean indicating whether process is alive. bool: indicating whether process is alive
""" """
return self.checkalive()[0] return self.checkalive()[0]
@@ -203,12 +151,8 @@ class Spawn:
"""Send data to the sub-process's stdin. """Send data to the sub-process's stdin.
Args: Args:
data: The data to send to the process. data (str): The data to send to the process.
Returns:
Nothing.
""" """
os.write(self.fd, data.encode(errors='replace')) os.write(self.fd, data.encode(errors='replace'))
def receive(self, num_bytes): def receive(self, num_bytes):
@@ -233,78 +177,10 @@ class Spawn:
alive, _, info = self.checkalive() alive, _, info = self.checkalive()
if alive: if alive:
raise err raise err
raise ValueError('U-Boot exited with %s' % info) raise ValueError(f'U-Boot exited with {info}') from err
raise raise
return c return c
def expect(self, patterns):
"""Wait for the sub-process to emit specific data.
This function waits for the process to emit one pattern from the
supplied list of patterns, or for a timeout to occur.
Args:
patterns: A list of strings or regex objects that we expect to
see in the sub-process' stdout.
Returns:
The index within the patterns array of the pattern the process
emitted.
Notable exceptions:
Timeout, if the process did not emit any of the patterns within
the expected time.
"""
for pi in range(len(patterns)):
if type(patterns[pi]) == type(''):
patterns[pi] = re.compile(patterns[pi])
tstart_s = time.time()
try:
while True:
earliest_m = None
earliest_pi = None
for pi in range(len(patterns)):
pattern = patterns[pi]
m = pattern.search(self.buf)
if not m:
continue
if earliest_m and m.start() >= earliest_m.start():
continue
earliest_m = m
earliest_pi = pi
if earliest_m:
pos = earliest_m.start()
posafter = earliest_m.end()
self.before = self.buf[:pos]
self.after = self.buf[pos:posafter]
self.output += self.buf[:posafter]
self.buf = self.buf[posafter:]
return earliest_pi
tnow_s = time.time()
if self.timeout:
tdelta_ms = (tnow_s - tstart_s) * 1000
poll_maxwait = self.timeout - tdelta_ms
if tdelta_ms > self.timeout:
raise Timeout()
else:
poll_maxwait = None
events = self.poll.poll(poll_maxwait)
if not events:
raise Timeout()
c = self.receive(1024)
if self.logfile_read:
self.logfile_read.write(c)
self.buf += c
# count=0 is supposed to be the default, which indicates
# unlimited substitutions, but in practice the version of
# Python in Ubuntu 14.04 appears to default to count=2!
self.buf = self.re_vt100.sub('', self.buf, count=1000000)
finally:
if self.logfile_read:
self.logfile_read.flush()
def close(self): def close(self):
"""Close the stdio connection to the sub-process. """Close the stdio connection to the sub-process.
@@ -335,11 +211,3 @@ class Spawn:
time.sleep(0.1) time.sleep(0.1)
return 'timeout' return 'timeout'
def get_expect_output(self):
"""Return the output read by expect()
Returns:
The output processed by expect(), as a string.
"""
return self.output

View File

@@ -18,24 +18,24 @@ def test_bootmenu(ubman):
ubman.run_command('setenv bootmenu_2 test 3=echo ok 3') ubman.run_command('setenv bootmenu_2 test 3=echo ok 3')
ubman.run_command('bootmenu 2', wait_for_prompt=False) ubman.run_command('bootmenu 2', wait_for_prompt=False)
for i in ('U-Boot Boot Menu', 'test 1', 'test 2', 'test 3', 'autoboot'): for i in ('U-Boot Boot Menu', 'test 1', 'test 2', 'test 3', 'autoboot'):
ubman.p.expect([i]) ubman.expect([i])
# Press enter key to execute default entry # Press enter key to execute default entry
response = ubman.run_command(cmd='\x0d', wait_for_echo=False, send_nl=False) response = ubman.run_command(cmd='\x0d', wait_for_echo=False, send_nl=False)
assert 'ok 2' in response assert 'ok 2' in response
ubman.run_command('bootmenu 2', wait_for_prompt=False) ubman.run_command('bootmenu 2', wait_for_prompt=False)
ubman.p.expect(['autoboot']) ubman.expect(['autoboot'])
# Press up key to select prior entry followed by the enter key # Press up key to select prior entry followed by the enter key
response = ubman.run_command(cmd='\x1b\x5b\x41\x0d', wait_for_echo=False, response = ubman.run_command(cmd='\x1b\x5b\x41\x0d', wait_for_echo=False,
send_nl=False) send_nl=False)
assert 'ok 1' in response assert 'ok 1' in response
ubman.run_command('bootmenu 2', wait_for_prompt=False) ubman.run_command('bootmenu 2', wait_for_prompt=False)
ubman.p.expect(['autoboot']) ubman.expect(['autoboot'])
# Press down key to select next entry followed by the enter key # Press down key to select next entry followed by the enter key
response = ubman.run_command(cmd='\x1b\x5b\x42\x0d', wait_for_echo=False, response = ubman.run_command(cmd='\x1b\x5b\x42\x0d', wait_for_echo=False,
send_nl=False) send_nl=False)
assert 'ok 3' in response assert 'ok 3' in response
ubman.run_command('bootmenu 2; echo rc:$?', wait_for_prompt=False) ubman.run_command('bootmenu 2; echo rc:$?', wait_for_prompt=False)
ubman.p.expect(['autoboot']) ubman.expect(['autoboot'])
# Press the escape key # Press the escape key
response = ubman.run_command(cmd='\x1b', wait_for_echo=False, send_nl=False) response = ubman.run_command(cmd='\x1b', wait_for_echo=False, send_nl=False)
assert 'ok' not in response assert 'ok' not in response

View File

@@ -16,14 +16,14 @@ def test_distro(ubman):
with ubman.log.section('Grub'): with ubman.log.section('Grub'):
# Wait for grub to come up and offset a menu # Wait for grub to come up and offset a menu
ubman.p.expect(['Try or Install Ubuntu']) ubman.expect(['Try or Install Ubuntu'])
# Press 'e' to edit the command line # Press 'e' to edit the command line
ubman.log.info("Pressing 'e'") ubman.log.info("Pressing 'e'")
ubman.run_command('e', wait_for_prompt=False, send_nl=False) ubman.run_command('e', wait_for_prompt=False, send_nl=False)
# Wait until we see the editor appear # Wait until we see the editor appear
ubman.p.expect(['/casper/initrd']) ubman.expect(['/casper/initrd'])
# Go down to the 'linux' line. Avoid using down-arrow as that includes # Go down to the 'linux' line. Avoid using down-arrow as that includes
# an Escape character, which may be parsed by Grub as such, causing it # an Escape character, which may be parsed by Grub as such, causing it
@@ -48,14 +48,14 @@ def test_distro(ubman):
# Tell grub to boot # Tell grub to boot
ubman.log.info("boot") ubman.log.info("boot")
ubman.ctrl('X') ubman.ctrl('X')
ubman.p.expect(['Booting a command list']) ubman.expect(['Booting a command list'])
with ubman.log.section('Linux'): with ubman.log.section('Linux'):
# Linux should start immediately # Linux should start immediately
ubman.p.expect(['Linux version']) ubman.expect(['Linux version'])
with ubman.log.section('Ubuntu'): with ubman.log.section('Ubuntu'):
# Shortly later, we should see this banner # Shortly later, we should see this banner
ubman.p.expect(['Welcome to .*Ubuntu 24.04.1 LTS.*!']) ubman.expect(['Welcome to .*Ubuntu 24.04.1 LTS.*!'])
ubman.restart_uboot() ubman.restart_uboot()

View File

@@ -16,7 +16,7 @@ def test_efi_selftest_base(ubman):
""" """
ubman.run_command(cmd='setenv efi_selftest') ubman.run_command(cmd='setenv efi_selftest')
ubman.run_command(cmd='bootefi selftest', wait_for_prompt=False) ubman.run_command(cmd='bootefi selftest', wait_for_prompt=False)
if ubman.p.expect(['Summary: 0 failures', 'Press any key']): if ubman.expect(['Summary: 0 failures', 'Press any key']):
raise Exception('Failures occurred during the EFI selftest') raise Exception('Failures occurred during the EFI selftest')
ubman.restart_uboot() ubman.restart_uboot()
@@ -39,7 +39,7 @@ def test_efi_selftest_device_tree(ubman):
ubman.run_command(cmd='setenv efi_test "${serial#}x"') ubman.run_command(cmd='setenv efi_test "${serial#}x"')
ubman.run_command(cmd='test "${efi_test}" = x && setenv serial# 0') ubman.run_command(cmd='test "${efi_test}" = x && setenv serial# 0')
ubman.run_command(cmd='bootefi selftest ${fdtcontroladdr}', wait_for_prompt=False) ubman.run_command(cmd='bootefi selftest ${fdtcontroladdr}', wait_for_prompt=False)
if ubman.p.expect(['serial-number:', 'U-Boot']): if ubman.expect(['serial-number:', 'U-Boot']):
raise Exception('serial-number missing in device tree') raise Exception('serial-number missing in device tree')
ubman.restart_uboot() ubman.restart_uboot()
@@ -56,7 +56,7 @@ def test_efi_selftest_watchdog_reboot(ubman):
assert '\'watchdog reboot\'' in output assert '\'watchdog reboot\'' in output
ubman.run_command(cmd='setenv efi_selftest watchdog reboot') ubman.run_command(cmd='setenv efi_selftest watchdog reboot')
ubman.run_command(cmd='bootefi selftest', wait_for_prompt=False) ubman.run_command(cmd='bootefi selftest', wait_for_prompt=False)
if ubman.p.expect(['resetting', 'U-Boot']): if ubman.expect(['resetting', 'U-Boot']):
raise Exception('Reset failed in \'watchdog reboot\' test') raise Exception('Reset failed in \'watchdog reboot\' test')
ubman.run_command(cmd='', send_nl=False, wait_for_reboot=True) ubman.run_command(cmd='', send_nl=False, wait_for_reboot=True)
@@ -70,48 +70,48 @@ def test_efi_selftest_text_input(ubman):
""" """
ubman.run_command(cmd='setenv efi_selftest text input') ubman.run_command(cmd='setenv efi_selftest text input')
ubman.run_command(cmd='bootefi selftest', wait_for_prompt=False) ubman.run_command(cmd='bootefi selftest', wait_for_prompt=False)
if ubman.p.expect([r'To terminate type \'x\'']): if ubman.expect([r'To terminate type \'x\'']):
raise Exception('No prompt for \'text input\' test') raise Exception('No prompt for \'text input\' test')
ubman.drain_console() ubman.drain_console()
# EOT # EOT
ubman.run_command(cmd=chr(4), wait_for_echo=False, ubman.run_command(cmd=chr(4), wait_for_echo=False,
send_nl=False, wait_for_prompt=False) send_nl=False, wait_for_prompt=False)
if ubman.p.expect([r'Unicode char 4 \(unknown\), scan code 0 \(Null\)']): if ubman.expect([r'Unicode char 4 \(unknown\), scan code 0 \(Null\)']):
raise Exception('EOT failed in \'text input\' test') raise Exception('EOT failed in \'text input\' test')
ubman.drain_console() ubman.drain_console()
# BS # BS
ubman.run_command(cmd=chr(8), wait_for_echo=False, ubman.run_command(cmd=chr(8), wait_for_echo=False,
send_nl=False, wait_for_prompt=False) send_nl=False, wait_for_prompt=False)
if ubman.p.expect([r'Unicode char 8 \(BS\), scan code 0 \(Null\)']): if ubman.expect([r'Unicode char 8 \(BS\), scan code 0 \(Null\)']):
raise Exception('BS failed in \'text input\' test') raise Exception('BS failed in \'text input\' test')
ubman.drain_console() ubman.drain_console()
# TAB # TAB
ubman.run_command(cmd=chr(9), wait_for_echo=False, ubman.run_command(cmd=chr(9), wait_for_echo=False,
send_nl=False, wait_for_prompt=False) send_nl=False, wait_for_prompt=False)
if ubman.p.expect([r'Unicode char 9 \(TAB\), scan code 0 \(Null\)']): if ubman.expect([r'Unicode char 9 \(TAB\), scan code 0 \(Null\)']):
raise Exception('BS failed in \'text input\' test') raise Exception('BS failed in \'text input\' test')
ubman.drain_console() ubman.drain_console()
# a # a
ubman.run_command(cmd='a', wait_for_echo=False, send_nl=False, ubman.run_command(cmd='a', wait_for_echo=False, send_nl=False,
wait_for_prompt=False) wait_for_prompt=False)
if ubman.p.expect([r'Unicode char 97 \(\'a\'\), scan code 0 \(Null\)']): if ubman.expect([r'Unicode char 97 \(\'a\'\), scan code 0 \(Null\)']):
raise Exception('\'a\' failed in \'text input\' test') raise Exception('\'a\' failed in \'text input\' test')
ubman.drain_console() ubman.drain_console()
# UP escape sequence # UP escape sequence
ubman.run_command(cmd=chr(27) + '[A', wait_for_echo=False, ubman.run_command(cmd=chr(27) + '[A', wait_for_echo=False,
send_nl=False, wait_for_prompt=False) send_nl=False, wait_for_prompt=False)
if ubman.p.expect([r'Unicode char 0 \(Null\), scan code 1 \(Up\)']): if ubman.expect([r'Unicode char 0 \(Null\), scan code 1 \(Up\)']):
raise Exception('UP failed in \'text input\' test') raise Exception('UP failed in \'text input\' test')
ubman.drain_console() ubman.drain_console()
# Euro sign # Euro sign
ubman.run_command(cmd=b'\xe2\x82\xac'.decode(), wait_for_echo=False, ubman.run_command(cmd=b'\xe2\x82\xac'.decode(), wait_for_echo=False,
send_nl=False, wait_for_prompt=False) send_nl=False, wait_for_prompt=False)
if ubman.p.expect([r'Unicode char 8364 \(\'']): if ubman.expect([r'Unicode char 8364 \(\'']):
raise Exception('Euro sign failed in \'text input\' test') raise Exception('Euro sign failed in \'text input\' test')
ubman.drain_console() ubman.drain_console()
ubman.run_command(cmd='x', wait_for_echo=False, send_nl=False, ubman.run_command(cmd='x', wait_for_echo=False, send_nl=False,
wait_for_prompt=False) wait_for_prompt=False)
if ubman.p.expect(['Summary: 0 failures', 'Press any key']): if ubman.expect(['Summary: 0 failures', 'Press any key']):
raise Exception('Failures occurred during the EFI selftest') raise Exception('Failures occurred during the EFI selftest')
ubman.restart_uboot() ubman.restart_uboot()
@@ -125,55 +125,55 @@ def test_efi_selftest_text_input_ex(ubman):
""" """
ubman.run_command(cmd='setenv efi_selftest extended text input') ubman.run_command(cmd='setenv efi_selftest extended text input')
ubman.run_command(cmd='bootefi selftest', wait_for_prompt=False) ubman.run_command(cmd='bootefi selftest', wait_for_prompt=False)
if ubman.p.expect([r'To terminate type \'CTRL\+x\'']): if ubman.expect([r'To terminate type \'CTRL\+x\'']):
raise Exception('No prompt for \'text input\' test') raise Exception('No prompt for \'text input\' test')
ubman.drain_console() ubman.drain_console()
# EOT # EOT
ubman.run_command(cmd=chr(4), wait_for_echo=False, ubman.run_command(cmd=chr(4), wait_for_echo=False,
send_nl=False, wait_for_prompt=False) send_nl=False, wait_for_prompt=False)
if ubman.p.expect([r'Unicode char 100 \(\'d\'\), scan code 0 \(CTRL\+Null\)']): if ubman.expect([r'Unicode char 100 \(\'d\'\), scan code 0 \(CTRL\+Null\)']):
raise Exception('EOT failed in \'text input\' test') raise Exception('EOT failed in \'text input\' test')
ubman.drain_console() ubman.drain_console()
# BS # BS
ubman.run_command(cmd=chr(8), wait_for_echo=False, ubman.run_command(cmd=chr(8), wait_for_echo=False,
send_nl=False, wait_for_prompt=False) send_nl=False, wait_for_prompt=False)
if ubman.p.expect([r'Unicode char 8 \(BS\), scan code 0 \(\+Null\)']): if ubman.expect([r'Unicode char 8 \(BS\), scan code 0 \(\+Null\)']):
raise Exception('BS failed in \'text input\' test') raise Exception('BS failed in \'text input\' test')
ubman.drain_console() ubman.drain_console()
# TAB # TAB
ubman.run_command(cmd=chr(9), wait_for_echo=False, ubman.run_command(cmd=chr(9), wait_for_echo=False,
send_nl=False, wait_for_prompt=False) send_nl=False, wait_for_prompt=False)
if ubman.p.expect([r'Unicode char 9 \(TAB\), scan code 0 \(\+Null\)']): if ubman.expect([r'Unicode char 9 \(TAB\), scan code 0 \(\+Null\)']):
raise Exception('TAB failed in \'text input\' test') raise Exception('TAB failed in \'text input\' test')
ubman.drain_console() ubman.drain_console()
# a # a
ubman.run_command(cmd='a', wait_for_echo=False, send_nl=False, ubman.run_command(cmd='a', wait_for_echo=False, send_nl=False,
wait_for_prompt=False) wait_for_prompt=False)
if ubman.p.expect([r'Unicode char 97 \(\'a\'\), scan code 0 \(Null\)']): if ubman.expect([r'Unicode char 97 \(\'a\'\), scan code 0 \(Null\)']):
raise Exception('\'a\' failed in \'text input\' test') raise Exception('\'a\' failed in \'text input\' test')
ubman.drain_console() ubman.drain_console()
# UP escape sequence # UP escape sequence
ubman.run_command(cmd=chr(27) + '[A', wait_for_echo=False, ubman.run_command(cmd=chr(27) + '[A', wait_for_echo=False,
send_nl=False, wait_for_prompt=False) send_nl=False, wait_for_prompt=False)
if ubman.p.expect([r'Unicode char 0 \(Null\), scan code 1 \(\+Up\)']): if ubman.expect([r'Unicode char 0 \(Null\), scan code 1 \(\+Up\)']):
raise Exception('UP failed in \'text input\' test') raise Exception('UP failed in \'text input\' test')
ubman.drain_console() ubman.drain_console()
# Euro sign # Euro sign
ubman.run_command(cmd=b'\xe2\x82\xac'.decode(), wait_for_echo=False, ubman.run_command(cmd=b'\xe2\x82\xac'.decode(), wait_for_echo=False,
send_nl=False, wait_for_prompt=False) send_nl=False, wait_for_prompt=False)
if ubman.p.expect([r'Unicode char 8364 \(\'']): if ubman.expect([r'Unicode char 8364 \(\'']):
raise Exception('Euro sign failed in \'text input\' test') raise Exception('Euro sign failed in \'text input\' test')
ubman.drain_console() ubman.drain_console()
# SHIFT+ALT+FN 5 # SHIFT+ALT+FN 5
ubman.run_command(cmd=b'\x1b\x5b\x31\x35\x3b\x34\x7e'.decode(), ubman.run_command(cmd=b'\x1b\x5b\x31\x35\x3b\x34\x7e'.decode(),
wait_for_echo=False, send_nl=False, wait_for_echo=False, send_nl=False,
wait_for_prompt=False) wait_for_prompt=False)
if ubman.p.expect([r'Unicode char 0 \(Null\), scan code 15 \(SHIFT\+ALT\+FN 5\)']): if ubman.expect([r'Unicode char 0 \(Null\), scan code 15 \(SHIFT\+ALT\+FN 5\)']):
raise Exception('SHIFT+ALT+FN 5 failed in \'text input\' test') raise Exception('SHIFT+ALT+FN 5 failed in \'text input\' test')
ubman.drain_console() ubman.drain_console()
ubman.run_command(cmd=chr(24), wait_for_echo=False, send_nl=False, ubman.run_command(cmd=chr(24), wait_for_echo=False, send_nl=False,
wait_for_prompt=False) wait_for_prompt=False)
if ubman.p.expect(['Summary: 0 failures', 'Press any key']): if ubman.expect(['Summary: 0 failures', 'Press any key']):
raise Exception('Failures occurred during the EFI selftest') raise Exception('Failures occurred during the EFI selftest')
ubman.restart_uboot() ubman.restart_uboot()
@@ -192,6 +192,6 @@ def test_efi_selftest_tcg2(ubman):
assert '\'tcg2\'' in output assert '\'tcg2\'' in output
ubman.run_command(cmd='setenv efi_selftest tcg2') ubman.run_command(cmd='setenv efi_selftest tcg2')
ubman.run_command(cmd='bootefi selftest', wait_for_prompt=False) ubman.run_command(cmd='bootefi selftest', wait_for_prompt=False)
if ubman.p.expect(['Summary: 0 failures', 'Press any key']): if ubman.expect(['Summary: 0 failures', 'Press any key']):
raise Exception('Failures occurred during the EFI selftest') raise Exception('Failures occurred during the EFI selftest')
ubman.restart_uboot() ubman.restart_uboot()

View File

@@ -62,7 +62,7 @@ def test_efi_eficonfig(ubman):
wait_for_echo=False, send_nl=False) wait_for_echo=False, send_nl=False)
if expect_str is not None: if expect_str is not None:
for i in expect_str: for i in expect_str:
ubman.p.expect([i]) ubman.expect([i])
def press_up_down_enter_and_wait(up_count, down_count, enter, expect_str): def press_up_down_enter_and_wait(up_count, down_count, enter, expect_str):
# press UP key # press UP key
@@ -80,7 +80,7 @@ def test_efi_eficonfig(ubman):
# wait expected output # wait expected output
if expect_str is not None: if expect_str is not None:
for i in expect_str: for i in expect_str:
ubman.p.expect([i]) ubman.expect([i])
def press_escape_key(wait_prompt): def press_escape_key(wait_prompt):
ubman.run_command(cmd='\x1b', wait_for_prompt=wait_prompt, wait_for_echo=False, send_nl=False) ubman.run_command(cmd='\x1b', wait_for_prompt=wait_prompt, wait_for_echo=False, send_nl=False)
@@ -92,7 +92,7 @@ def test_efi_eficonfig(ubman):
def check_current_is_maintenance_menu(): def check_current_is_maintenance_menu():
for i in ('UEFI Maintenance Menu', 'Add Boot Option', 'Edit Boot Option', for i in ('UEFI Maintenance Menu', 'Add Boot Option', 'Edit Boot Option',
'Change Boot Order', 'Delete Boot Option', 'Quit'): 'Change Boot Order', 'Delete Boot Option', 'Quit'):
ubman.p.expect([i]) ubman.expect([i])
""" Unit test for "eficonfig" command """ Unit test for "eficonfig" command
The menu-driven interface is used to set up UEFI load options. The menu-driven interface is used to set up UEFI load options.
@@ -117,12 +117,12 @@ def test_efi_eficonfig(ubman):
ubman.run_command('eficonfig', wait_for_prompt=False) ubman.run_command('eficonfig', wait_for_prompt=False)
for i in ('UEFI Maintenance Menu', 'Add Boot Option', 'Edit Boot Option', for i in ('UEFI Maintenance Menu', 'Add Boot Option', 'Edit Boot Option',
'Change Boot Order', 'Delete Boot Option', 'Quit'): 'Change Boot Order', 'Delete Boot Option', 'Quit'):
ubman.p.expect([i]) ubman.expect([i])
# Select "Add Boot Option" # Select "Add Boot Option"
press_enter_key(False) press_enter_key(False)
for i in ('Add Boot Option', 'Description:', 'File', 'Initrd File', 'Optional Data', for i in ('Add Boot Option', 'Description:', 'File', 'Initrd File', 'Optional Data',
'Save', 'Quit'): 'Save', 'Quit'):
ubman.p.expect([i]) ubman.expect([i])
press_escape_key(False) press_escape_key(False)
check_current_is_maintenance_menu() check_current_is_maintenance_menu()
# return to U-Boot console # return to U-Boot console
@@ -140,7 +140,7 @@ def test_efi_eficonfig(ubman):
# Change the Boot Order # Change the Boot Order
press_up_down_enter_and_wait(0, 2, True, 'Quit') press_up_down_enter_and_wait(0, 2, True, 'Quit')
for i in ('host 0:1', 'Save', 'Quit'): for i in ('host 0:1', 'Save', 'Quit'):
ubman.p.expect([i]) ubman.expect([i])
# disable auto generated boot option for succeeding test # disable auto generated boot option for succeeding test
ubman.run_command(cmd=' ', wait_for_prompt=False, ubman.run_command(cmd=' ', wait_for_prompt=False,
wait_for_echo=False, send_nl=False) wait_for_echo=False, send_nl=False)
@@ -182,7 +182,7 @@ def test_efi_eficonfig(ubman):
send_user_input_and_wait('nocolor', None) send_user_input_and_wait('nocolor', None)
for i in ('Description: test 1', 'File: host 0:1/initrddump.efi', for i in ('Description: test 1', 'File: host 0:1/initrddump.efi',
'Initrd File: host 0:1/initrd-1.img', 'Optional Data: nocolor', 'Save', 'Quit'): 'Initrd File: host 0:1/initrd-1.img', 'Optional Data: nocolor', 'Save', 'Quit'):
ubman.p.expect([i]) ubman.expect([i])
# Save the Boot Option # Save the Boot Option
press_up_down_enter_and_wait(0, 4, True, None) press_up_down_enter_and_wait(0, 4, True, None)
@@ -231,7 +231,7 @@ def test_efi_eficonfig(ubman):
send_user_input_and_wait('nocolor', None) send_user_input_and_wait('nocolor', None)
for i in ('Description: test 2', 'File: host 0:1/initrddump.efi', for i in ('Description: test 2', 'File: host 0:1/initrddump.efi',
'Initrd File: host 0:1/initrd-2.img', 'Optional Data: nocolor', 'Save', 'Quit'): 'Initrd File: host 0:1/initrd-2.img', 'Optional Data: nocolor', 'Save', 'Quit'):
ubman.p.expect([i]) ubman.expect([i])
# Save the Boot Option # Save the Boot Option
press_up_down_enter_and_wait(0, 4, True, 'Quit') press_up_down_enter_and_wait(0, 4, True, 'Quit')
@@ -243,7 +243,7 @@ def test_efi_eficonfig(ubman):
ubman.run_command(cmd='+', wait_for_prompt=False, ubman.run_command(cmd='+', wait_for_prompt=False,
wait_for_echo=False, send_nl=False) wait_for_echo=False, send_nl=False)
for i in ('test 2', 'test 1', 'host 0:1', 'Save', 'Quit'): for i in ('test 2', 'test 1', 'host 0:1', 'Save', 'Quit'):
ubman.p.expect([i]) ubman.expect([i])
# Save the BootOrder # Save the BootOrder
press_up_down_enter_and_wait(0, 3, True, None) press_up_down_enter_and_wait(0, 3, True, None)
check_current_is_maintenance_menu() check_current_is_maintenance_menu()
@@ -265,12 +265,12 @@ def test_efi_eficonfig(ubman):
press_up_down_enter_and_wait(0, 2, True, None) press_up_down_enter_and_wait(0, 2, True, None)
# Check the current BootOrder # Check the current BootOrder
for i in ('test 2', 'test 1', 'host 0:1', 'Save', 'Quit'): for i in ('test 2', 'test 1', 'host 0:1', 'Save', 'Quit'):
ubman.p.expect([i]) ubman.expect([i])
# move 'test 2' to the second entry # move 'test 2' to the second entry
ubman.run_command(cmd='-', wait_for_prompt=False, ubman.run_command(cmd='-', wait_for_prompt=False,
wait_for_echo=False, send_nl=False) wait_for_echo=False, send_nl=False)
for i in ('test 1', 'test 2', 'host 0:1', 'Save', 'Quit'): for i in ('test 1', 'test 2', 'host 0:1', 'Save', 'Quit'):
ubman.p.expect([i]) ubman.expect([i])
# Save the BootOrder # Save the BootOrder
press_up_down_enter_and_wait(0, 2, True, None) press_up_down_enter_and_wait(0, 2, True, None)
check_current_is_maintenance_menu() check_current_is_maintenance_menu()
@@ -291,12 +291,12 @@ def test_efi_eficonfig(ubman):
press_up_down_enter_and_wait(0, 3, True, None) press_up_down_enter_and_wait(0, 3, True, None)
# Check the current BootOrder # Check the current BootOrder
for i in ('test 1', 'test 2', 'Quit'): for i in ('test 1', 'test 2', 'Quit'):
ubman.p.expect([i]) ubman.expect([i])
# Delete 'test 2' # Delete 'test 2'
press_up_down_enter_and_wait(0, 1, True, None) press_up_down_enter_and_wait(0, 1, True, None)
for i in ('test 1', 'Quit'): for i in ('test 1', 'Quit'):
ubman.p.expect([i]) ubman.expect([i])
press_escape_key(False) press_escape_key(False)
check_current_is_maintenance_menu() check_current_is_maintenance_menu()
# Return to U-Boot console # Return to U-Boot console
@@ -310,11 +310,11 @@ def test_efi_eficonfig(ubman):
press_up_down_enter_and_wait(0, 1, True, None) press_up_down_enter_and_wait(0, 1, True, None)
# Check the current BootOrder # Check the current BootOrder
for i in ('test 1', 'Quit'): for i in ('test 1', 'Quit'):
ubman.p.expect([i]) ubman.expect([i])
press_up_down_enter_and_wait(0, 0, True, None) press_up_down_enter_and_wait(0, 0, True, None)
for i in ('Description: test 1', 'File: host 0:1/initrddump.efi', for i in ('Description: test 1', 'File: host 0:1/initrddump.efi',
'Initrd File: host 0:1/initrd-1.img', 'Optional Data: nocolor', 'Save', 'Quit'): 'Initrd File: host 0:1/initrd-1.img', 'Optional Data: nocolor', 'Save', 'Quit'):
ubman.p.expect([i]) ubman.expect([i])
# Press the enter key to select 'Description:' entry, then enter Description # Press the enter key to select 'Description:' entry, then enter Description
press_up_down_enter_and_wait(0, 0, True, 'Enter description:') press_up_down_enter_and_wait(0, 0, True, 'Enter description:')
@@ -343,7 +343,7 @@ def test_efi_eficonfig(ubman):
send_user_input_and_wait('', None) send_user_input_and_wait('', None)
for i in ('Description: test 3', 'File: host 0:1/initrddump.efi', for i in ('Description: test 3', 'File: host 0:1/initrddump.efi',
'Initrd File: host 0:1/initrd-2.img', 'Optional Data:', 'Save', 'Quit'): 'Initrd File: host 0:1/initrd-2.img', 'Optional Data:', 'Save', 'Quit'):
ubman.p.expect([i]) ubman.expect([i])
# Save the Boot Option # Save the Boot Option
press_up_down_enter_and_wait(0, 4, True, 'Quit') press_up_down_enter_and_wait(0, 4, True, 'Quit')
@@ -367,7 +367,7 @@ def test_efi_eficonfig(ubman):
press_up_down_enter_and_wait(0, 3, True, None) press_up_down_enter_and_wait(0, 3, True, None)
# Check the current BootOrder # Check the current BootOrder
for i in ('test 3', 'Quit'): for i in ('test 3', 'Quit'):
ubman.p.expect([i]) ubman.expect([i])
# Delete 'test 3' # Delete 'test 3'
press_up_down_enter_and_wait(0, 0, True, 'Quit') press_up_down_enter_and_wait(0, 0, True, 'Quit')

View File

@@ -194,8 +194,7 @@ def test_net_tftpboot_boot(ubman):
# This forces the console object to be shutdown, so any subsequent # This forces the console object to be shutdown, so any subsequent
# test will reset the board back into U-Boot. We want to force this # test will reset the board back into U-Boot. We want to force this
# no matter whether the kernel boot passed or failed. # no matter whether the kernel boot passed or failed.
ubman.drain_console() ubman.shutdown_required()
ubman.cleanup_spawn()
def setup_pxe_boot(ubman): def setup_pxe_boot(ubman):
f = ubman.config.env.get('env__net_pxe_bootable_file', None) f = ubman.config.env.get('env__net_pxe_bootable_file', None)
@@ -257,8 +256,7 @@ def test_net_pxe_boot(ubman):
ubman.run_command(pxe_boot_cmd, wait_for_prompt=False) ubman.run_command(pxe_boot_cmd, wait_for_prompt=False)
ubman.wait_for(pattern) ubman.wait_for(pattern)
finally: finally:
ubman.drain_console() ubman.shutdown_required()
ubman.cleanup_spawn()
@pytest.mark.buildconfigspec('cmd_pxe') @pytest.mark.buildconfigspec('cmd_pxe')
def test_net_pxe_boot_config(ubman): def test_net_pxe_boot_config(ubman):
@@ -318,7 +316,7 @@ def test_net_pxe_boot_config(ubman):
# should not boot it and come out to u-boot prompt # should not boot it and come out to u-boot prompt
ubman.wait_for('Enter choice:') ubman.wait_for('Enter choice:')
ubman.run_command(local_label, wait_for_prompt=False) ubman.run_command(local_label, wait_for_prompt=False)
expected_str = ubman.p.expect([exp_str_local]) expected_str = ubman.expect([exp_str_local])
assert ( assert (
expected_str == 0 expected_str == 0
), f'Expected string: {exp_str_local} did not match!' ), f'Expected string: {exp_str_local} did not match!'
@@ -329,15 +327,14 @@ def test_net_pxe_boot_config(ubman):
ubman.run_command(pxe_boot_cmd, wait_for_prompt=False) ubman.run_command(pxe_boot_cmd, wait_for_prompt=False)
ubman.wait_for('Enter choice:') ubman.wait_for('Enter choice:')
ubman.run_command(empty_label, wait_for_prompt=False) ubman.run_command(empty_label, wait_for_prompt=False)
expected_str = ubman.p.expect([exp_str_empty]) expected_str = ubman.expect([exp_str_empty])
assert ( assert (
expected_str == 0 expected_str == 0
), f'Expected string: {exp_str_empty} did not match!' ), f'Expected string: {exp_str_empty} did not match!'
ubman.wait_for(pattern) ubman.wait_for(pattern)
finally: finally:
ubman.drain_console() ubman.shutdown_required()
ubman.cleanup_spawn()
@pytest.mark.buildconfigspec('cmd_pxe') @pytest.mark.buildconfigspec('cmd_pxe')
def test_net_pxe_boot_config_invalid(ubman): def test_net_pxe_boot_config_invalid(ubman):
@@ -389,12 +386,11 @@ def test_net_pxe_boot_config_invalid(ubman):
# label and if it fails it should load the default label to boot # label and if it fails it should load the default label to boot
ubman.wait_for('Enter choice:') ubman.wait_for('Enter choice:')
ubman.run_command(invalid_label, wait_for_prompt=False) ubman.run_command(invalid_label, wait_for_prompt=False)
expected_str = ubman.p.expect([exp_str_invalid]) expected_str = ubman.expect([exp_str_invalid])
assert ( assert (
expected_str == 0 expected_str == 0
), f'Expected string: {exp_str_invalid} did not match!' ), f'Expected string: {exp_str_invalid} did not match!'
ubman.wait_for(pattern) ubman.wait_for(pattern)
finally: finally:
ubman.drain_console() ubman.shutdown_required()
ubman.cleanup_spawn()

View File

@@ -27,10 +27,10 @@ def test_exception_reset(ubman):
"""Test that SIGILL causes a reset.""" """Test that SIGILL causes a reset."""
ubman.run_command('exception undefined', wait_for_prompt=False) ubman.run_command('exception undefined', wait_for_prompt=False)
m = ubman.p.expect(['resetting ...', 'U-Boot']) m = ubman.expect(['resetting ...', 'U-Boot'])
if m != 0: if m != 0:
raise Exception('SIGILL did not lead to reset') raise Exception('SIGILL did not lead to reset')
m = ubman.p.expect(['U-Boot', '=>']) m = ubman.expect(['U-Boot', '=>'])
if m != 0: if m != 0:
raise Exception('SIGILL did not lead to reset') raise Exception('SIGILL did not lead to reset')
ubman.restart_uboot() ubman.restart_uboot()

View File

@@ -61,7 +61,7 @@ def check_env(ubman, var_name, var_value):
assert ret_code(ubman).endswith('0') assert ret_code(ubman).endswith('0')
else: else:
ubman.p.send(f'printenv {var_name}\n') ubman.p.send(f'printenv {var_name}\n')
output = ubman.p.expect(['not defined']) output = ubman.expect(['not defined'])
assert output == 0 assert output == 0
assert ret_code(ubman).endswith('1') assert ret_code(ubman).endswith('1')

View File

@@ -218,5 +218,4 @@ def test_zynqmp_rpu_app_load_negative(ubman):
disable_cpus(ubman, cpu_nums) disable_cpus(ubman, cpu_nums)
# This forces the console object to be shutdown, so any subsequent test # This forces the console object to be shutdown, so any subsequent test
# will reset the board back into U-Boot. # will reset the board back into U-Boot.
ubman.drain_console() ubman.shutdown_required()
ubman.cleanup_spawn()

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "binary-manager" name = "binary-manager"
version = "0.0.6" version = "0.0.7"
authors = [ authors = [
{ name="Simon Glass", email="sjg@chromium.org" }, { name="Simon Glass", email="sjg@chromium.org" },
] ]

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "buildman" name = "buildman"
version = "0.0.6" version = "0.0.7"
authors = [ authors = [
{ name="Simon Glass", email="sjg@chromium.org" }, { name="Simon Glass", email="sjg@chromium.org" },
] ]

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "dtoc" name = "dtoc"
version = "0.0.6" version = "0.0.7"
authors = [ authors = [
{ name="Simon Glass", email="sjg@chromium.org" }, { name="Simon Glass", email="sjg@chromium.org" },
] ]

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "patch-manager" name = "patch-manager"
version = "0.0.6" version = "0.0.7"
authors = [ authors = [
{ name="Simon Glass", email="sjg@chromium.org" }, { name="Simon Glass", email="sjg@chromium.org" },
] ]

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "u_boot_pylib" name = "u_boot_pylib"
version = "0.0.6" version = "0.0.7"
authors = [ authors = [
{ name="Simon Glass", email="sjg@chromium.org" }, { name="Simon Glass", email="sjg@chromium.org" },
] ]