summaryrefslogtreecommitdiff
path: root/test/py/u_boot_spawn.py
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2024-10-15 09:31:50 -0600
committerTom Rini <trini@konsulko.com>2024-10-15 11:38:44 -0600
commita198e8bb6cb74672a11f6b02bc583efa873ba4bf (patch)
tree641c334bf728cb2331fa8eaef000ec951e6baf67 /test/py/u_boot_spawn.py
parent4378732d5658accd07d2fb245417262a23f578ce (diff)
parent3f6a0c890b9121f8517784bc288922a1269ceb94 (diff)
Merge patch series "test: Minor fixes to test.py"
Simon Glass <sjg@chromium.org> says: This series collects together the patches from the Labgrid series which are not related to Labgrid, or at least can be applied independently of using Labgrid to run the lab. Link: https://lore.kernel.org/r/20241010002907.19383-1-sjg@chromium.org
Diffstat (limited to 'test/py/u_boot_spawn.py')
-rw-r--r--test/py/u_boot_spawn.py88
1 files changed, 76 insertions, 12 deletions
diff --git a/test/py/u_boot_spawn.py b/test/py/u_boot_spawn.py
index 97e95e07c80..24d369035e5 100644
--- a/test/py/u_boot_spawn.py
+++ b/test/py/u_boot_spawn.py
@@ -8,6 +8,7 @@ Logic to spawn a sub-process and interact with its stdio.
import os
import re
import pty
+import pytest
import signal
import select
import time
@@ -16,6 +17,54 @@ import traceback
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.
@@ -137,6 +186,32 @@ class Spawn:
os.write(self.fd, data.encode(errors='replace'))
+ def receive(self, num_bytes):
+ """Receive data from the sub-process's stdin.
+
+ Args:
+ num_bytes (int): Maximum number of bytes to read
+
+ Returns:
+ str: The data received
+
+ Raises:
+ ValueError if U-Boot died
+ """
+ try:
+ c = os.read(self.fd, num_bytes).decode(errors='replace')
+ except OSError as err:
+ # With sandbox, try to detect when U-Boot exits when it
+ # shouldn't and explain why. This is much more friendly than
+ # just dying with an I/O error
+ if self.decode_signal and err.errno == 5: # I/O error
+ alive, _, info = self.checkalive()
+ if alive:
+ raise 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.
@@ -193,18 +268,7 @@ class Spawn:
events = self.poll.poll(poll_maxwait)
if not events:
raise Timeout()
- try:
- c = os.read(self.fd, 1024).decode(errors='replace')
- except OSError as err:
- # With sandbox, try to detect when U-Boot exits when it
- # shouldn't and explain why. This is much more friendly than
- # just dying with an I/O error
- if self.decode_signal and err.errno == 5: # I/O error
- alive, _, info = self.checkalive()
- if alive:
- raise err
- raise ValueError('U-Boot exited with %s' % info)
- raise
+ c = self.receive(1024)
if self.logfile_read:
self.logfile_read.write(c)
self.buf += c