Compare commits

..

2 Commits

Author SHA1 Message Date
Simon Glass
eb3d5e4063 WIP: Disable EFI boot manager for demo
Disable this so that we can use bootstd features.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-06-22 10:26:02 -06:00
Simon Glass
3f1920b3cc WIP: video: Use 800x600 for bochs
Use a smaller display for the video.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-06-22 10:08:00 -06:00
20 changed files with 424 additions and 444 deletions

View File

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

View File

@@ -94,3 +94,6 @@ CONFIG_GENERATE_ACPI_TABLE=y
CONFIG_CMD_DHRYSTONE=y
# CONFIG_GZIP is not set
CONFIG_UNIT_TEST=y
CONFIG_VIDEO_BOCHS_SIZE_X=800
CONFIG_VIDEO_BOCHS_SIZE_Y=600
# CONFIG_BOOTMETH_EFI_BOOTMGR is not set

View File

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

View File

@@ -2,21 +2,19 @@
# Copyright (c) 2015 Stephen Warren
# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
"""Common logic to interact with U-Boot via the console.
# Common logic to interact with U-Boot via the console. This class provides
# 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.
Provides 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.
"""
from collections import namedtuple
import multiplexed_log
import os
import pytest
import re
import sys
import time
import pytest
import spawn
from spawn import BootFail, Timeout, Unexpected, handle_exception
# 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]*\\))')
@@ -28,6 +26,9 @@ pattern_error_please_reset = re.compile('### ERROR ### Please RESET the board ##
pattern_ready_prompt = re.compile('{lab ready in (.*)s: (.*)}')
pattern_lab_mode = re.compile('{lab mode.*}')
PAT_ID = 0
PAT_RE = 1
# Timeout before expecting the console to be ready (in milliseconds)
TIMEOUT_MS = 30000 # Standard timeout
TIMEOUT_CMD_MS = 10000 # Command-echo timeout
@@ -39,77 +40,16 @@ TIMEOUT_CMD_MS = 10000 # Command-echo timeout
# situations.
TIMEOUT_PREPARE_MS = 3 * 60 * 1000
# Named pattern used by this module:
# str: name of pattern
# re.Pattern: Regex to check this pattern in the console output
NamedPattern = namedtuple('PATTERN', 'name,pattern')
# Named patterns we can look for in the console output. These can indicate an
# 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),
bad_pattern_defs = (
('spl_signon', pattern_u_boot_spl_signon),
('main_signon', pattern_u_boot_main_signon),
('stop_autoboot_prompt', pattern_stop_autoboot_prompt),
('unknown_command', pattern_unknown_command),
('error_notification', pattern_error_notification),
('error_please_reset', pattern_error_please_reset),
)
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():
class ConsoleDisableCheck(object):
"""Context manager (for Python's with statement) that temporarily disables
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
@@ -123,14 +63,13 @@ class ConsoleDisableCheck():
def __enter__(self):
self.console.disable_check_count[self.check_type] += 1
self.console.eval_patterns()
self.console.eval_bad_patterns()
def __exit__(self, extype, value, traceback):
self.console.disable_check_count[self.check_type] -= 1
self.console.eval_patterns()
self.console.eval_bad_patterns()
class ConsoleEnableCheck():
class ConsoleEnableCheck(object):
"""Context manager (for Python's with statement) that temporarily enables
the specified console output error check. This is useful when executing a
command that might raise an extra bad pattern, beyond the default bad
@@ -142,40 +81,37 @@ class ConsoleEnableCheck():
self.console = console
self.check_type = check_type
self.check_pattern = check_pattern
self.default_bad_patterns = None
def __enter__(self):
cons = self.console
self.default_bad_patterns = cons.avail_patterns
cons.avail_patterns.append((self.check_type, self.check_pattern))
cons.disable_check_count = {pat.name: 0 for pat in PATTERNS}
cons.eval_patterns()
global bad_pattern_defs
self.default_bad_patterns = bad_pattern_defs
bad_pattern_defs += ((self.check_type, self.check_pattern),)
self.console.disable_check_count = {pat[PAT_ID]: 0 for pat in bad_pattern_defs}
self.console.eval_bad_patterns()
def __exit__(self, extype, value, traceback):
cons = self.console
cons.avail_patterns = self.default_bad_patterns
cons.disable_check_count = {pat.name: 0 for pat in PATTERNS}
cons.eval_patterns()
global bad_pattern_defs
bad_pattern_defs = self.default_bad_patterns
self.console.disable_check_count = {pat[PAT_ID]: 0 for pat in bad_pattern_defs}
self.console.eval_bad_patterns()
class ConsoleSetupTimeout():
class ConsoleSetupTimeout(object):
"""Context manager (for Python's with statement) that temporarily sets up
timeout for specific command. This is useful when execution time is greater
then default 30s."""
def __init__(self, console, timeout):
self.console = console
self.orig_timeout = self.console.timeout
self.console.timeout = timeout
self.p = console.p
self.orig_timeout = self.p.timeout
self.p.timeout = timeout
def __enter__(self):
return self
def __exit__(self, extype, value, traceback):
self.console.timeout = self.orig_timeout
self.p.timeout = self.orig_timeout
class ConsoleBase():
class ConsoleBase(object):
"""The interface through which test functions interact with the U-Boot
console. This primarily involves executing shell commands, capturing their
results, and checking for common error conditions. Some common utilities
@@ -187,49 +123,20 @@ class ConsoleBase():
Can only usefully be called by sub-classes.
Args:
log (multiplexed_log.Logfile): Log to which the U-Boot output is
logged.
config (ArbitraryAttributeContainer): ubman_fix.config, as built by
conftest.py.
max_fifo_fill (int): The max number of characters to send to U-Boot
log: A multiplexed_log.Logfile object, to which the U-Boot output
will be logged.
config: A configuration data structure, as built by conftest.py.
max_fifo_fill: The maximum number of characters to send to U-Boot
command-line before waiting for U-Boot to echo the characters
back. For UART-based HW without HW flow control, this value
should be set less than the UART RX FIFO size to avoid
overflow, assuming that U-Boot can't keep up with full-rate
traffic at the baud rate.
Properties:
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()
Returns:
Nothing.
"""
self.log = log
self.config = config
self.max_fifo_fill = max_fifo_fill
@@ -240,38 +147,26 @@ class ConsoleBase():
self.prompt = self.config.buildconfig['config_sys_prompt'][1:-1]
self.prompt_compiled = re.compile('^' + re.escape(self.prompt), re.MULTILINE)
self.p = None
self.avail_patterns = PATTERNS
self.disable_check_count = {pat.name: 0 for pat in self.avail_patterns}
self.disable_check_count = {pat[PAT_ID]: 0 for pat in bad_pattern_defs}
self.eval_bad_patterns()
self.at_prompt = False
self.at_prompt_logevt = None
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):
"""This is not called, ssubclass must define this.
Return a value to avoid:
console_base.py:348:12: E1128: Assigning result of a function
call, where the function returns None (assignment-from-none)
"""
# This is not called, ssubclass must define this.
# Return a value to avoid:
# console_base.py:348:12: E1128: Assigning result of a function
# call, where the function returns None (assignment-from-none)
return spawn.Spawn([])
def eval_patterns(self):
"""Set up lists of regexes for patterns we don't expect on console"""
self.bad_patterns = [pat.pattern for pat in self.avail_patterns
if not self.disable_check_count[pat.name]]
self.bad_pattern_ids = [pat.name for pat in self.avail_patterns
if not self.disable_check_count[pat.name]]
def eval_bad_patterns(self):
self.bad_patterns = [pat[PAT_RE] for pat in bad_pattern_defs \
if self.disable_check_count[pat[PAT_ID]] == 0]
self.bad_pattern_ids = [pat[PAT_ID] for pat in bad_pattern_defs \
if self.disable_check_count[pat[PAT_ID]] == 0]
def close(self):
"""Terminate the connection to the U-Boot console.
@@ -282,7 +177,11 @@ class ConsoleBase():
Args:
None.
Returns:
Nothing.
"""
if self.p:
self.log.start_section('Stopping U-Boot')
close_type = self.p.close()
@@ -296,32 +195,54 @@ class ConsoleBase():
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.
"""
self.log.info('test.py: Lab mode is active')
self.timeout = TIMEOUT_PREPARE_MS
self.log.info(f'test.py: Lab mode is active')
self.p.timeout = TIMEOUT_PREPARE_MS
self.lab_mode = True
def _wait_for_boot_prompt(self, loop_num=1):
"""Wait for the boot up until command prompt.
This is for internal use only.
def wait_for_boot_prompt(self, loop_num = 1):
"""Wait for the boot up until command prompt. This is for internal use only.
"""
try:
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:
self._wait_for_banner(loop_num)
self.u_boot_version_string = self.after
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:
self.u_boot_version_string = self.p.after
while True:
m = self.expect([self.prompt_compiled, pattern_ready_prompt,
m = self.p.expect([self.prompt_compiled, pattern_ready_prompt,
pattern_stop_autoboot_prompt] + self.bad_patterns)
if m == 0:
self.log.info(f'Found ready prompt {m}')
break
if m == 1:
m = pattern_ready_prompt.search(self.after)
elif m == 1:
m = pattern_ready_prompt.search(self.p.after)
self.u_boot_version_string = m.group(2)
self.log.info('Lab: Board is ready')
self.timeout = TIMEOUT_MS
self.log.info(f'Lab: Board is ready')
self.p.timeout = TIMEOUT_MS
break
if m == 2:
self.log.info(f'Found autoboot prompt {m}')
@@ -330,44 +251,11 @@ class ConsoleBase():
if not self.lab_mode:
raise BootFail('Missing prompt / ready message on console: ' +
self.bad_pattern_ids[m - 3])
self.log.info('U-Boot is ready')
self.log.info(f'U-Boot is ready')
finally:
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,
wait_for_prompt=True, wait_for_reboot=False):
"""Execute a command via the U-Boot console.
@@ -389,25 +277,27 @@ class ConsoleBase():
running command such as "ums".
Args:
cmd (str): The command to send.
wait_for_echo (bool): Indicates whether to wait for U-Boot to
cmd: The command to send.
wait_for_echo: Boolean indicating whether to wait for U-Boot to
echo the command text back to its output.
send_nl (bool): Indicates whether to send a newline character
send_nl: Boolean indicating whether to send a newline character
after the command string.
wait_for_prompt (bool): Indicates whether to wait for the
wait_for_prompt: Boolean indicating whether to wait for the
command prompt to be sent by U-Boot. This typically occurs
immediately after the command has been executed.
wait_for_reboot (bool): Indicates whether to wait U-Boot ro reboot.
If True, wait_for_prompt must also be True.
wait_for_reboot: Boolean indication whether to wait for the
reboot U-Boot. If this sets True, wait_for_prompt must also
be True.
Returns:
If wait_for_prompt == False:
Empty string.
Nothing.
Else:
The output from U-Boot during command execution. In other
words, the text U-Boot emitted between the point it echod the
command string and emitted the subsequent command prompts.
"""
if self.at_prompt and \
self.at_prompt_logevt != self.logstream.logfile.cur_evt:
self.logstream.write(self.prompt, implicit=True)
@@ -431,18 +321,17 @@ class ConsoleBase():
continue
chunk = re.escape(chunk)
chunk = chunk.replace('\\\n', '[\r\n]')
m = self.expect([chunk] + self.bad_patterns)
m = self.p.expect([chunk] + self.bad_patterns)
if m != 0:
self.at_prompt = False
raise BootFail('Failed to get echo on console '
f"(cmd '{cmd}':rem '{rem}'): " +
raise BootFail(f"Failed to get echo on console (cmd '{cmd}':rem '{rem}'): " +
self.bad_pattern_ids[m - 1])
if not wait_for_prompt:
return ''
return
if wait_for_reboot:
self._wait_for_boot_prompt()
self.wait_for_boot_prompt()
else:
m = self.expect([self.prompt_compiled] + self.bad_patterns)
m = self.p.expect([self.prompt_compiled] + self.bad_patterns)
if m != 0:
self.at_prompt = False
raise BootFail('Missing prompt on console: ' +
@@ -451,7 +340,7 @@ class ConsoleBase():
self.at_prompt_logevt = self.logstream.logfile.cur_evt
# Only strip \r\n; space/TAB might be significant if testing
# indentation.
return self.before.strip('\r\n')
return self.p.before.strip('\r\n')
except Timeout as exc:
handle_exception(self.config, self, self.log, exc,
f"Lab failure: Timeout executing '{cmd}'", True)
@@ -463,7 +352,6 @@ class ConsoleBase():
raise
finally:
self.log.timestamp()
return ''
def run_command_list(self, cmds):
"""Run a list of commands.
@@ -472,7 +360,7 @@ class ConsoleBase():
for each command in a list.
Args:
cmds (list of str): List of commands
cmd: List of commands (each a string).
Returns:
A list of output strings from each command, one element for each
command.
@@ -515,12 +403,16 @@ class ConsoleBase():
location in the log file.
Args:
text (str or re): The text to wait for; either a string (containing
raw text, not a regular expression) or an re object.
text: The text to wait for; either a string (containing raw text,
not a regular expression) or an re object.
Returns:
Nothing.
"""
if isinstance(text, str):
if type(text) == type(''):
text = re.escape(text)
m = self.expect([text] + self.bad_patterns)
m = self.p.expect([text] + self.bad_patterns)
if m != 0:
raise Unexpected(
"Unexpected pattern found on console (exp '{text}': " +
@@ -536,7 +428,14 @@ class ConsoleBase():
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
occur. This function will do that.
Args:
None.
Returns:
Nothing.
"""
# 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
# wait_for() failed (and hence the output has already been logged), or
@@ -544,13 +443,13 @@ class ConsoleBase():
if not self.p:
return
orig_timeout = self.timeout
orig_timeout = self.p.timeout
try:
# Drain the log for a relatively short time.
self.timeout = 1000
self.p.timeout = 1000
# Wait for something U-Boot will likely never send. This will
# cause the console output to be read and logged.
self.expect(['This should never match U-Boot output'])
self.p.expect(['This should never match U-Boot output'])
except:
# We expect a timeout, since U-Boot won't print what we waited
# for. Squash it when it happens.
@@ -564,7 +463,7 @@ class ConsoleBase():
# correctly terminate any log sections, etc.
pass
finally:
self.timeout = orig_timeout
self.p.timeout = orig_timeout
def ensure_spawned(self, expect_reset=False):
"""Ensure a connection to a correctly running U-Boot instance.
@@ -575,15 +474,19 @@ class ConsoleBase():
This is an internal function and should not be called directly.
Args:
expect_reset (bool): Indicates whether this boot is expected
expect_reset: Boolean indication whether this boot is expected
to be reset while the 1st boot process after main boot before
prompt. False by default.
Returns:
Nothing.
"""
if self.p:
# Reset the console timeout value as some tests may change
# its default value during the execution
if not self.config.gdbserver:
self.timeout = TIMEOUT_MS
self.p.timeout = TIMEOUT_MS
return
try:
self.log.start_section('Starting U-Boot')
@@ -594,8 +497,8 @@ class ConsoleBase():
# future, possibly per-test to be optimal. This works for 'help'
# on board 'seaboard'.
if not self.config.gdbserver:
self.timeout = TIMEOUT_MS
self.logfile_read = self.logstream
self.p.timeout = TIMEOUT_MS
self.p.logfile_read = self.logstream
if self.config.use_running_system:
# Send an empty command to set up the 'expect' logic. This has
# the side effect of ensuring that there was no partial command
@@ -606,7 +509,7 @@ class ConsoleBase():
loop_num = 2
else:
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_logevt = self.logstream.logfile.cur_evt
except Exception as ex:
@@ -624,7 +527,14 @@ class ConsoleBase():
connection with a fresh U-Boot instance.
This is an internal function and should not be called directly.
Args:
None.
Returns:
Nothing.
"""
try:
if self.p:
self.p.close()
@@ -632,16 +542,6 @@ class ConsoleBase():
pass
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):
"""Shut down and restart U-Boot."""
self.cleanup_spawn()
@@ -654,7 +554,7 @@ class ConsoleBase():
The output produced by ensure_spawed(), as a string.
"""
if self.p:
return self.get_expect_output()
return self.p.get_expect_output()
return None
def validate_version_string_in_text(self, text):
@@ -664,12 +564,13 @@ class ConsoleBase():
duplicating the signon text regex in a test function.
Args:
text (str): The command output text to check.
text: The command output text to check.
Raises:
Assertion if the validation fails.
Returns:
Nothing. An exception is raised 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):
"""Temporarily disable an error check of U-Boot's output.
@@ -678,12 +579,13 @@ class ConsoleBase():
temporarily disables a particular console output error check.
Args:
check_type (str): The type of error-check to disable, see
bad_pattern_defs
check_type: The type of error-check to disable. Valid values may
be found in self.disable_check_count above.
Returns:
A context manager object.
"""
return ConsoleDisableCheck(self, check_type)
def enable_check(self, check_type, check_pattern):
@@ -694,14 +596,14 @@ class ConsoleBase():
arguments form a new element of bad_pattern_defs defined above.
Args:
check_type (str): The type of error-check to disable, see
bad_pattern_defs
check_pattern (re.Pattern): Regex for text error / bad pattern
check_type: The type of error-check or bad pattern to enable.
check_pattern: The regexes for text error pattern or bad pattern
to be checked.
Returns:
A context manager object.
"""
return ConsoleEnableCheck(self, check_type, check_pattern)
def temporary_timeout(self, timeout):
@@ -711,83 +613,10 @@ class ConsoleBase():
temporarily change timeout.
Args:
timeout (int): Time in milliseconds.
timeout: Time in milliseconds.
Returns:
A context manager object.
"""
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?).
# 16 is a common FIFO size.
# HW flow control would mean this could be infinite.
super().__init__(log, config, max_fifo_fill=16)
super(ConsoleExecAttach, self).__init__(log, config, max_fifo_fill=16)
with self.log.section('flash'):
self.log.action('Flashing U-Boot')

View File

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

View File

@@ -3,29 +3,13 @@
"""
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 os
import re
import pty
import pytest
import signal
import select
import sys
@@ -36,20 +20,72 @@ import traceback
# Character to send (twice) to exit the terminal
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:
"""Represents the stdio of a freshly created sub-process. Commands may be
sent to the process, and responses waited for.
Members:
output: accumulated output from expect()
"""
def __init__(self, args, cwd=None, decode_signal=False):
"""Spawn (fork/exec) the sub-process.
Args:
args (list of str): processs arguments. argv[0] is the command to
args: array of processs arguments. argv[0] is the command to
execute.
cwd (str or None): the directory to run the process in, or None for
no change.
cwd: the directory to run the process in, or None for no change.
decode_signal (bool): True to indicate the exception number when
something goes wrong
@@ -60,6 +96,14 @@ class Spawn:
self.waited = False
self.exit_code = 0
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()
if self.pid == 0:
@@ -84,7 +128,7 @@ class Spawn:
isatty = os.isatty(sys.stdout.fileno())
# with --capture=tee-sys we cannot call fileno()
except io.UnsupportedOperation:
except io.UnsupportedOperation as exc:
pass
if isatty:
new = termios.tcgetattr(self.fd)
@@ -108,8 +152,12 @@ class Spawn:
"""Send unix signal "sig" to the child process.
Args:
sig (int): The signal number to send
sig: The signal number to send.
Returns:
Nothing.
"""
os.kill(self.pid, sig)
def checkalive(self):
@@ -121,6 +169,7 @@ class Spawn:
0 if process is alive, else exit code of process
string describing what happened ('' or 'status/signal n')
"""
if self.waited:
return False, self.exit_code, self.exit_info
@@ -131,19 +180,22 @@ class Spawn:
if os.WIFEXITED(status):
self.exit_code = os.WEXITSTATUS(status)
self.exit_info = f'status {self.exit_code}'
self.exit_info = 'status %d' % self.exit_code
elif os.WIFSIGNALED(status):
signum = os.WTERMSIG(status)
self.exit_code = -signum
self.exit_info = f'signal {signum} ({signal.Signals(signum).name})'
self.exit_info = 'signal %d (%s)' % (signum, signal.Signals(signum).name)
self.waited = True
return False, self.exit_code, self.exit_info
def isalive(self):
"""Determine whether the child process is still running.
Args:
None.
Returns:
bool: indicating whether process is alive
Boolean indicating whether process is alive.
"""
return self.checkalive()[0]
@@ -151,8 +203,12 @@ class Spawn:
"""Send data to the sub-process's stdin.
Args:
data (str): The data to send to the process.
data: The data to send to the process.
Returns:
Nothing.
"""
os.write(self.fd, data.encode(errors='replace'))
def receive(self, num_bytes):
@@ -177,10 +233,78 @@ class Spawn:
alive, _, info = self.checkalive()
if alive:
raise err
raise ValueError(f'U-Boot exited with {info}') from err
raise ValueError('U-Boot exited with %s' % info)
raise
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):
"""Close the stdio connection to the sub-process.
@@ -211,3 +335,11 @@ class Spawn:
time.sleep(0.1)
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('bootmenu 2', wait_for_prompt=False)
for i in ('U-Boot Boot Menu', 'test 1', 'test 2', 'test 3', 'autoboot'):
ubman.expect([i])
ubman.p.expect([i])
# Press enter key to execute default entry
response = ubman.run_command(cmd='\x0d', wait_for_echo=False, send_nl=False)
assert 'ok 2' in response
ubman.run_command('bootmenu 2', wait_for_prompt=False)
ubman.expect(['autoboot'])
ubman.p.expect(['autoboot'])
# 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,
send_nl=False)
assert 'ok 1' in response
ubman.run_command('bootmenu 2', wait_for_prompt=False)
ubman.expect(['autoboot'])
ubman.p.expect(['autoboot'])
# 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,
send_nl=False)
assert 'ok 3' in response
ubman.run_command('bootmenu 2; echo rc:$?', wait_for_prompt=False)
ubman.expect(['autoboot'])
ubman.p.expect(['autoboot'])
# Press the escape key
response = ubman.run_command(cmd='\x1b', wait_for_echo=False, send_nl=False)
assert 'ok' not in response

View File

@@ -16,14 +16,14 @@ def test_distro(ubman):
with ubman.log.section('Grub'):
# Wait for grub to come up and offset a menu
ubman.expect(['Try or Install Ubuntu'])
ubman.p.expect(['Try or Install Ubuntu'])
# Press 'e' to edit the command line
ubman.log.info("Pressing 'e'")
ubman.run_command('e', wait_for_prompt=False, send_nl=False)
# Wait until we see the editor appear
ubman.expect(['/casper/initrd'])
ubman.p.expect(['/casper/initrd'])
# 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
@@ -48,14 +48,14 @@ def test_distro(ubman):
# Tell grub to boot
ubman.log.info("boot")
ubman.ctrl('X')
ubman.expect(['Booting a command list'])
ubman.p.expect(['Booting a command list'])
with ubman.log.section('Linux'):
# Linux should start immediately
ubman.expect(['Linux version'])
ubman.p.expect(['Linux version'])
with ubman.log.section('Ubuntu'):
# Shortly later, we should see this banner
ubman.expect(['Welcome to .*Ubuntu 24.04.1 LTS.*!'])
ubman.p.expect(['Welcome to .*Ubuntu 24.04.1 LTS.*!'])
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='bootefi selftest', wait_for_prompt=False)
if ubman.expect(['Summary: 0 failures', 'Press any key']):
if ubman.p.expect(['Summary: 0 failures', 'Press any key']):
raise Exception('Failures occurred during the EFI selftest')
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='test "${efi_test}" = x && setenv serial# 0')
ubman.run_command(cmd='bootefi selftest ${fdtcontroladdr}', wait_for_prompt=False)
if ubman.expect(['serial-number:', 'U-Boot']):
if ubman.p.expect(['serial-number:', 'U-Boot']):
raise Exception('serial-number missing in device tree')
ubman.restart_uboot()
@@ -56,7 +56,7 @@ def test_efi_selftest_watchdog_reboot(ubman):
assert '\'watchdog reboot\'' in output
ubman.run_command(cmd='setenv efi_selftest watchdog reboot')
ubman.run_command(cmd='bootefi selftest', wait_for_prompt=False)
if ubman.expect(['resetting', 'U-Boot']):
if ubman.p.expect(['resetting', 'U-Boot']):
raise Exception('Reset failed in \'watchdog reboot\' test')
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='bootefi selftest', wait_for_prompt=False)
if ubman.expect([r'To terminate type \'x\'']):
if ubman.p.expect([r'To terminate type \'x\'']):
raise Exception('No prompt for \'text input\' test')
ubman.drain_console()
# EOT
ubman.run_command(cmd=chr(4), wait_for_echo=False,
send_nl=False, wait_for_prompt=False)
if ubman.expect([r'Unicode char 4 \(unknown\), scan code 0 \(Null\)']):
if ubman.p.expect([r'Unicode char 4 \(unknown\), scan code 0 \(Null\)']):
raise Exception('EOT failed in \'text input\' test')
ubman.drain_console()
# BS
ubman.run_command(cmd=chr(8), wait_for_echo=False,
send_nl=False, wait_for_prompt=False)
if ubman.expect([r'Unicode char 8 \(BS\), scan code 0 \(Null\)']):
if ubman.p.expect([r'Unicode char 8 \(BS\), scan code 0 \(Null\)']):
raise Exception('BS failed in \'text input\' test')
ubman.drain_console()
# TAB
ubman.run_command(cmd=chr(9), wait_for_echo=False,
send_nl=False, wait_for_prompt=False)
if ubman.expect([r'Unicode char 9 \(TAB\), scan code 0 \(Null\)']):
if ubman.p.expect([r'Unicode char 9 \(TAB\), scan code 0 \(Null\)']):
raise Exception('BS failed in \'text input\' test')
ubman.drain_console()
# a
ubman.run_command(cmd='a', wait_for_echo=False, send_nl=False,
wait_for_prompt=False)
if ubman.expect([r'Unicode char 97 \(\'a\'\), scan code 0 \(Null\)']):
if ubman.p.expect([r'Unicode char 97 \(\'a\'\), scan code 0 \(Null\)']):
raise Exception('\'a\' failed in \'text input\' test')
ubman.drain_console()
# UP escape sequence
ubman.run_command(cmd=chr(27) + '[A', wait_for_echo=False,
send_nl=False, wait_for_prompt=False)
if ubman.expect([r'Unicode char 0 \(Null\), scan code 1 \(Up\)']):
if ubman.p.expect([r'Unicode char 0 \(Null\), scan code 1 \(Up\)']):
raise Exception('UP failed in \'text input\' test')
ubman.drain_console()
# Euro sign
ubman.run_command(cmd=b'\xe2\x82\xac'.decode(), wait_for_echo=False,
send_nl=False, wait_for_prompt=False)
if ubman.expect([r'Unicode char 8364 \(\'']):
if ubman.p.expect([r'Unicode char 8364 \(\'']):
raise Exception('Euro sign failed in \'text input\' test')
ubman.drain_console()
ubman.run_command(cmd='x', wait_for_echo=False, send_nl=False,
wait_for_prompt=False)
if ubman.expect(['Summary: 0 failures', 'Press any key']):
if ubman.p.expect(['Summary: 0 failures', 'Press any key']):
raise Exception('Failures occurred during the EFI selftest')
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='bootefi selftest', wait_for_prompt=False)
if ubman.expect([r'To terminate type \'CTRL\+x\'']):
if ubman.p.expect([r'To terminate type \'CTRL\+x\'']):
raise Exception('No prompt for \'text input\' test')
ubman.drain_console()
# EOT
ubman.run_command(cmd=chr(4), wait_for_echo=False,
send_nl=False, wait_for_prompt=False)
if ubman.expect([r'Unicode char 100 \(\'d\'\), scan code 0 \(CTRL\+Null\)']):
if ubman.p.expect([r'Unicode char 100 \(\'d\'\), scan code 0 \(CTRL\+Null\)']):
raise Exception('EOT failed in \'text input\' test')
ubman.drain_console()
# BS
ubman.run_command(cmd=chr(8), wait_for_echo=False,
send_nl=False, wait_for_prompt=False)
if ubman.expect([r'Unicode char 8 \(BS\), scan code 0 \(\+Null\)']):
if ubman.p.expect([r'Unicode char 8 \(BS\), scan code 0 \(\+Null\)']):
raise Exception('BS failed in \'text input\' test')
ubman.drain_console()
# TAB
ubman.run_command(cmd=chr(9), wait_for_echo=False,
send_nl=False, wait_for_prompt=False)
if ubman.expect([r'Unicode char 9 \(TAB\), scan code 0 \(\+Null\)']):
if ubman.p.expect([r'Unicode char 9 \(TAB\), scan code 0 \(\+Null\)']):
raise Exception('TAB failed in \'text input\' test')
ubman.drain_console()
# a
ubman.run_command(cmd='a', wait_for_echo=False, send_nl=False,
wait_for_prompt=False)
if ubman.expect([r'Unicode char 97 \(\'a\'\), scan code 0 \(Null\)']):
if ubman.p.expect([r'Unicode char 97 \(\'a\'\), scan code 0 \(Null\)']):
raise Exception('\'a\' failed in \'text input\' test')
ubman.drain_console()
# UP escape sequence
ubman.run_command(cmd=chr(27) + '[A', wait_for_echo=False,
send_nl=False, wait_for_prompt=False)
if ubman.expect([r'Unicode char 0 \(Null\), scan code 1 \(\+Up\)']):
if ubman.p.expect([r'Unicode char 0 \(Null\), scan code 1 \(\+Up\)']):
raise Exception('UP failed in \'text input\' test')
ubman.drain_console()
# Euro sign
ubman.run_command(cmd=b'\xe2\x82\xac'.decode(), wait_for_echo=False,
send_nl=False, wait_for_prompt=False)
if ubman.expect([r'Unicode char 8364 \(\'']):
if ubman.p.expect([r'Unicode char 8364 \(\'']):
raise Exception('Euro sign failed in \'text input\' test')
ubman.drain_console()
# SHIFT+ALT+FN 5
ubman.run_command(cmd=b'\x1b\x5b\x31\x35\x3b\x34\x7e'.decode(),
wait_for_echo=False, send_nl=False,
wait_for_prompt=False)
if ubman.expect([r'Unicode char 0 \(Null\), scan code 15 \(SHIFT\+ALT\+FN 5\)']):
if ubman.p.expect([r'Unicode char 0 \(Null\), scan code 15 \(SHIFT\+ALT\+FN 5\)']):
raise Exception('SHIFT+ALT+FN 5 failed in \'text input\' test')
ubman.drain_console()
ubman.run_command(cmd=chr(24), wait_for_echo=False, send_nl=False,
wait_for_prompt=False)
if ubman.expect(['Summary: 0 failures', 'Press any key']):
if ubman.p.expect(['Summary: 0 failures', 'Press any key']):
raise Exception('Failures occurred during the EFI selftest')
ubman.restart_uboot()
@@ -192,6 +192,6 @@ def test_efi_selftest_tcg2(ubman):
assert '\'tcg2\'' in output
ubman.run_command(cmd='setenv efi_selftest tcg2')
ubman.run_command(cmd='bootefi selftest', wait_for_prompt=False)
if ubman.expect(['Summary: 0 failures', 'Press any key']):
if ubman.p.expect(['Summary: 0 failures', 'Press any key']):
raise Exception('Failures occurred during the EFI selftest')
ubman.restart_uboot()

View File

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

View File

@@ -194,7 +194,8 @@ def test_net_tftpboot_boot(ubman):
# 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
# no matter whether the kernel boot passed or failed.
ubman.shutdown_required()
ubman.drain_console()
ubman.cleanup_spawn()
def setup_pxe_boot(ubman):
f = ubman.config.env.get('env__net_pxe_bootable_file', None)
@@ -256,7 +257,8 @@ def test_net_pxe_boot(ubman):
ubman.run_command(pxe_boot_cmd, wait_for_prompt=False)
ubman.wait_for(pattern)
finally:
ubman.shutdown_required()
ubman.drain_console()
ubman.cleanup_spawn()
@pytest.mark.buildconfigspec('cmd_pxe')
def test_net_pxe_boot_config(ubman):
@@ -316,7 +318,7 @@ def test_net_pxe_boot_config(ubman):
# should not boot it and come out to u-boot prompt
ubman.wait_for('Enter choice:')
ubman.run_command(local_label, wait_for_prompt=False)
expected_str = ubman.expect([exp_str_local])
expected_str = ubman.p.expect([exp_str_local])
assert (
expected_str == 0
), f'Expected string: {exp_str_local} did not match!'
@@ -327,14 +329,15 @@ def test_net_pxe_boot_config(ubman):
ubman.run_command(pxe_boot_cmd, wait_for_prompt=False)
ubman.wait_for('Enter choice:')
ubman.run_command(empty_label, wait_for_prompt=False)
expected_str = ubman.expect([exp_str_empty])
expected_str = ubman.p.expect([exp_str_empty])
assert (
expected_str == 0
), f'Expected string: {exp_str_empty} did not match!'
ubman.wait_for(pattern)
finally:
ubman.shutdown_required()
ubman.drain_console()
ubman.cleanup_spawn()
@pytest.mark.buildconfigspec('cmd_pxe')
def test_net_pxe_boot_config_invalid(ubman):
@@ -386,11 +389,12 @@ def test_net_pxe_boot_config_invalid(ubman):
# label and if it fails it should load the default label to boot
ubman.wait_for('Enter choice:')
ubman.run_command(invalid_label, wait_for_prompt=False)
expected_str = ubman.expect([exp_str_invalid])
expected_str = ubman.p.expect([exp_str_invalid])
assert (
expected_str == 0
), f'Expected string: {exp_str_invalid} did not match!'
ubman.wait_for(pattern)
finally:
ubman.shutdown_required()
ubman.drain_console()
ubman.cleanup_spawn()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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