From 05382428552ff272e0b5be711a242e9a6315a210 Mon Sep 17 00:00:00 2001 From: Isaac Yang Date: Tue, 7 Jul 2026 14:08:11 +0800 Subject: [PATCH 01/14] Add raw UART loopback tests Add CE OEM serial raw UART loopback coverage with configurable quick and stress template jobs. Include resource generation, safety checks, documentation, tests, and the PySerial runtime dependency. --- .../series_classic20/snap/snapcraft.yaml | 1 + .../series_classic22/snap/snapcraft.yaml | 1 + .../series_classic24/snap/snapcraft.yaml | 1 + .../series_classic26/snap/snapcraft.yaml | 1 + .../series_uc20/snap/snapcraft.yaml | 1 + .../series_uc22/snap/snapcraft.yaml | 1 + .../series_uc24/snap/snapcraft.yaml | 1 + .../series_uc26/snap/snapcraft.yaml | 1 + .../bin/serial_raw_uart_test.py | 455 ++++++++++++++++++ .../tests/test_serial_raw_uart_test.py | 259 ++++++++++ .../units/serial/README.md | 136 ++++++ .../units/serial/manifest.pxu | 5 + .../units/serial/serial_raw_uart.pxu | 75 +++ .../units/serial/test-plan.pxu | 9 + 14 files changed, 947 insertions(+) create mode 100755 contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/serial_raw_uart_test.py create mode 100644 contrib/checkbox-ce-oem/checkbox-provider-ce-oem/tests/test_serial_raw_uart_test.py create mode 100644 contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/README.md create mode 100644 contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/serial_raw_uart.pxu diff --git a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic20/snap/snapcraft.yaml b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic20/snap/snapcraft.yaml index 97c72933f7..3e72231d76 100644 --- a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic20/snap/snapcraft.yaml +++ b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic20/snap/snapcraft.yaml @@ -44,6 +44,7 @@ parts: - device-tree-compiler - linuxptp - snmp + - python3-serial - gpsd override-prime: | snapcraftctl prime diff --git a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic22/snap/snapcraft.yaml b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic22/snap/snapcraft.yaml index 90c6bdd40c..4a12b06b6f 100644 --- a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic22/snap/snapcraft.yaml +++ b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic22/snap/snapcraft.yaml @@ -45,6 +45,7 @@ parts: - linuxptp - snmp - python3-rpyc + - python3-serial - gpsd override-prime: | snapcraftctl prime diff --git a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic24/snap/snapcraft.yaml b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic24/snap/snapcraft.yaml index 99197191da..dc69132aac 100644 --- a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic24/snap/snapcraft.yaml +++ b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic24/snap/snapcraft.yaml @@ -45,6 +45,7 @@ parts: - linuxptp - snmp - python3-rpyc + - python3-serial - gpsd override-prime: | craftctl default diff --git a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic26/snap/snapcraft.yaml b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic26/snap/snapcraft.yaml index a866c20bc0..5777a177b7 100644 --- a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic26/snap/snapcraft.yaml +++ b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic26/snap/snapcraft.yaml @@ -45,6 +45,7 @@ parts: - linuxptp - snmp - python3-rpyc + - python3-serial - gpsd override-prime: | craftctl default diff --git a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc20/snap/snapcraft.yaml b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc20/snap/snapcraft.yaml index 8f1359ee07..0ac59493fc 100644 --- a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc20/snap/snapcraft.yaml +++ b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc20/snap/snapcraft.yaml @@ -82,6 +82,7 @@ parts: - device-tree-compiler - linuxptp - snmp + - python3-serial - gpsd override-prime: | snapcraftctl prime diff --git a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc22/snap/snapcraft.yaml b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc22/snap/snapcraft.yaml index 991f08d970..259449800f 100644 --- a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc22/snap/snapcraft.yaml +++ b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc22/snap/snapcraft.yaml @@ -83,6 +83,7 @@ parts: - linuxptp - snmp - python3-rpyc + - python3-serial - gpsd override-prime: | snapcraftctl prime diff --git a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc24/snap/snapcraft.yaml b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc24/snap/snapcraft.yaml index 6c54df729d..3b0cb9de62 100644 --- a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc24/snap/snapcraft.yaml +++ b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc24/snap/snapcraft.yaml @@ -83,6 +83,7 @@ parts: - linuxptp - snmp - python3-rpyc + - python3-serial - gpsd override-prime: | craftctl default diff --git a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc26/snap/snapcraft.yaml b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc26/snap/snapcraft.yaml index 9cf77e4978..5592e4abbf 100644 --- a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc26/snap/snapcraft.yaml +++ b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc26/snap/snapcraft.yaml @@ -83,6 +83,7 @@ parts: - linuxptp - snmp - python3-rpyc + - python3-serial - gpsd override-build: | export PYTHONPATH=/snap/checkbox26/current/lib/python3.14/site-packages/:/usr/lib/python3/dist-packages diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/serial_raw_uart_test.py b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/serial_raw_uart_test.py new file mode 100755 index 0000000000..ffafc6c9a8 --- /dev/null +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/serial_raw_uart_test.py @@ -0,0 +1,455 @@ +#!/usr/bin/env python3 +"""List and test raw UART loopback devices for CE OEM serial jobs.""" + +import argparse +import fcntl +import glob +import os +import re +import struct +import time + +import serial + +TIOCGSERIAL = 0x541E +SERIAL_STRUCT_FORMAT = "iiiiiiiiiiiHHi" +RAW_UART_CONFIG_FORMAT = "NAME:TARGET:BAUD" +BAUD_MODES = ("max", "sweep", "autoscan-max") +PING_PATTERN = b"PING" +USB_SERIAL_PREFIXES = ("ttyUSB", "ttyACM") +STANDARD_RATES = [ + 9600, + 19200, + 38400, + 57600, + 115200, + 230400, + 460800, + 500000, + 576000, + 921600, + 1000000, + 1152000, + 1500000, + 2000000, + 2500000, + 3000000, + 3500000, + 4000000, +] +DRIVER_DEV_PREFIX = { + "serial": "ttyS", + "fsl-lpuart": "ttyLP", + "IMX-uart": "ttymxc", + "max310x": "ttyMAX", + "ttyAMA": "ttyAMA", +} + + +class RawUartPort: + """A configured raw UART loopback target.""" + + def __init__(self, name, target, baud): + self.name = name + self.target = target + self.baud = baud + self.target_type = "node" if target.startswith("/dev/") else "addr" + + @property + def name_id(self): + """Return the configured name normalized for generated job IDs.""" + + return sanitize_id(self.name) + + @property + def target_id(self): + """Return the configured target normalized for generated job IDs.""" + + return sanitize_id(self.target) + + +class UartDevice: + """Resolved raw UART device metadata.""" + + def __init__(self, target, dev_node, baud_base, is_console): + self.target = target + self.dev_node = dev_node + self.baud_base = baud_base + self.is_console = is_console + + +class LoopbackResult: + """Result of a single loopback test.""" + + def __init__(self, baud_rate, passed, message): + self.baud_rate = baud_rate + self.passed = passed + self.message = message + + +def sanitize_id(value): + """Return a short identifier safe for generated Checkbox job IDs.""" + + return re.sub(r"[^A-Za-z0-9_.-]+", "_", value).strip("_") + + +def parse_positive_int(raw_value, field_name): + """Parse a positive integer or raise a field-specific ValueError.""" + + try: + value = int(raw_value) + except ValueError as exc: + raise ValueError("{} must be an integer".format(field_name)) from exc + if value < 1: + raise ValueError("{} must be greater than 0".format(field_name)) + return value + + +def parse_baud(raw_value): + """Parse a RAW_UART_PORTS baud value.""" + + normalized = raw_value.lower() + if normalized in BAUD_MODES: + return normalized + return parse_positive_int(raw_value, "BAUD") + + +def parse_raw_uart_port(entry): + """Parse one RAW_UART_PORTS entry.""" + + parts = entry.split(":") + if len(parts) != 3: + raise ValueError( + "invalid format, expected {}".format(RAW_UART_CONFIG_FORMAT) + ) + name, target, baud = parts + if not name: + raise ValueError("NAME must not be empty") + if not sanitize_id(name): + raise ValueError("NAME must contain an ID-safe character") + if not target: + raise ValueError("TARGET must not be empty") + if not sanitize_id(target): + raise ValueError("TARGET must contain an ID-safe character") + return RawUartPort(name=name, target=target, baud=parse_baud(baud)) + + +def parse_raw_uart_ports(config): + """Parse space-separated RAW_UART_PORTS entries.""" + + return [parse_raw_uart_port(entry) for entry in config.split()] + + +def format_baud(baud): + """Return a resource-friendly baud string.""" + + return str(baud) + + +def print_raw_uart_resources(config): + """Print Checkbox resource records for configured raw UART ports.""" + + for port in parse_raw_uart_ports(config): + print("name: {}".format(port.name)) + print("name_id: {}".format(port.name_id)) + print("target: {}".format(port.target)) + print("target_id: {}".format(port.target_id)) + print("target_type: {}".format(port.target_type)) + print("baud: {}".format(format_baud(port.baud))) + print() + + +def normalize_addr(addr): + """Normalize a hardware address to lowercase hex form.""" + + value = addr.lower().strip() + if value.startswith("0x"): + return value + try: + return hex(int(value)) + except ValueError: + return value + + +def is_usb_serial_node(dev_node): + """Return True if a device node looks like a USB serial adapter.""" + + basename = os.path.basename(dev_node) + return basename.startswith(USB_SERIAL_PREFIXES) + + +def get_driver_baud_base(device_path): + """Return the kernel driver baud_base for a serial node, or 0.""" + + try: + fd = os.open(device_path, os.O_RDONLY | os.O_NONBLOCK) + try: + serial_struct = struct.pack("i" * 19, *([0] * 19)) + result = fcntl.ioctl(fd, TIOCGSERIAL, serial_struct) + unpacked = struct.unpack( + SERIAL_STRUCT_FORMAT, + result[: struct.calcsize(SERIAL_STRUCT_FORMAT)], + ) + return unpacked[7] + finally: + os.close(fd) + except (OSError, struct.error): + return 0 + + +def scan_available_uarts(raw_uart_only=True): + """Scan Linux UART driver data and return address-to-node mappings.""" + + results = {} + for filepath in glob.glob("/proc/tty/driver/*"): + driver_name = os.path.basename(filepath) + if raw_uart_only and driver_name == "usb-serial": + continue + dev_prefix = DRIVER_DEV_PREFIX.get(driver_name) + if not dev_prefix and "serial" in driver_name: + dev_prefix = "ttyS" + if not dev_prefix: + continue + + try: + with open(filepath, "r") as driver_file: + for line in driver_file: + match = re.search( + r"^\s*(\d+):.*(?:mmio|port|I/O):\s*" + r"(0x[0-9a-fA-F]+|[0-9]+)", + line, + ) + if not match or "uart:unknown" in line: + continue + index = match.group(1) + addr = normalize_addr(match.group(2)) + results[addr] = "/dev/{}{}".format(dev_prefix, index) + except (OSError, IOError): + continue + return results + + +def get_active_consoles(): + """Return active console tty basenames.""" + + try: + with open("/sys/class/tty/console/active", "r") as console_file: + return set(console_file.read().strip().split()) + except (OSError, IOError): + return set() + + +def resolve_device(target, allow_console=False, raw_uart_only=True): + """Resolve a configured target to a usable raw UART device.""" + + if target.startswith("/dev/"): + dev_node = target + resolved_target = target + else: + resolved_target = normalize_addr(target) + uarts = scan_available_uarts(raw_uart_only=raw_uart_only) + dev_node = uarts.get(resolved_target) + if not dev_node: + raise RuntimeError( + "Raw UART target {} was not found".format(resolved_target) + ) + + if raw_uart_only and is_usb_serial_node(dev_node): + raise RuntimeError( + "USB serial adapters are not allowed for raw UART loopback" + ) + + is_console = os.path.basename(dev_node) in get_active_consoles() + if is_console and not allow_console: + raise RuntimeError("{} is an active system console".format(dev_node)) + + return UartDevice( + target=resolved_target, + dev_node=dev_node, + baud_base=get_driver_baud_base(dev_node), + is_console=is_console, + ) + + +def get_rates_to_test(baud_base, baud): + """Return baud rates for an integer or symbolic baud mode.""" + + if isinstance(baud, int): + return [baud] + if baud == "max": + return [baud_base if baud_base > 0 else 115200] + + limit = baud_base if baud_base > 0 else max(STANDARD_RATES) + rates = [rate for rate in STANDARD_RATES if rate <= limit] + if baud_base > 0 and baud_base not in rates: + rates.append(baud_base) + rates = sorted(set(rates)) + + if baud == "sweep": + return rates + if baud == "autoscan-max": + return list(reversed(rates)) + raise ValueError("unsupported baud mode: {}".format(baud)) + + +def run_ping(serial_conn): + """Run a quick loopback ping on an open serial connection.""" + + serial_conn.timeout = 1.0 + serial_conn.reset_input_buffer() + serial_conn.write(PING_PATTERN) + serial_conn.flush() + time.sleep(0.05) + received = serial_conn.read(len(PING_PATTERN)) + return received == PING_PATTERN + + +def run_stress(serial_conn, count, size, timeout_factor=3.0): + """Run loopback stress traffic on an open serial connection.""" + + byte_time = (size * 10) / serial_conn.baudrate + serial_conn.timeout = max(2.0, byte_time * timeout_factor) + serial_conn.reset_input_buffer() + serial_conn.reset_output_buffer() + + for _index in range(count): + payload = os.urandom(size) + serial_conn.write(payload) + serial_conn.flush() + received = serial_conn.read(size) + if received != payload: + return False + return True + + +def open_serial_and_run(dev_node, baud_rate, run_fn): + """Open a serial node at a baud rate and run a callback.""" + + serial_conn = serial.Serial(dev_node, baud_rate, timeout=1.0) + try: + return run_fn(serial_conn) + finally: + serial_conn.close() + time.sleep(0.1) + + +def run_loopback(target, baud, mode, stress_count=100, stress_size=1024): + """Run quick or stress loopback tests for one target.""" + + device = resolve_device(target) + rates = get_rates_to_test(device.baud_base, baud) + results = [] + + for rate in rates: + try: + if mode == "quick": + passed = open_serial_and_run(device.dev_node, rate, run_ping) + elif mode == "stress": + passed = open_serial_and_run( + device.dev_node, + rate, + lambda serial_conn: run_stress( + serial_conn, stress_count, stress_size + ), + ) + else: + raise ValueError("unsupported mode: {}".format(mode)) + except (OSError, serial.SerialException) as exc: + passed = False + message = str(exc) + else: + message = "PASSED" if passed else "FAILED" + + result = LoopbackResult(rate, passed, message) + results.append(result) + print( + "{}: {} {} baud {}".format( + "PASS" if passed else "FAIL", + device.dev_node, + rate, + message, + ) + ) + + if baud == "autoscan-max" and passed: + print("Max working baud: {}".format(rate)) + return results + + return results + + +def parse_args(argv=None): + """Parse command-line arguments.""" + + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(dest="command") + + list_parser = subparsers.add_parser( + "list", help="Print Checkbox resources from RAW_UART_PORTS" + ) + list_parser.add_argument( + "config", + help=( + "Space-separated entries using the format " + "{}".format(RAW_UART_CONFIG_FORMAT) + ), + ) + + quick_parser = subparsers.add_parser( + "quick", help="Run a raw UART loopback ping test" + ) + quick_parser.add_argument("--target", required=True) + quick_parser.add_argument("--baud", required=True, type=parse_baud) + + stress_parser = subparsers.add_parser( + "stress", help="Run a raw UART loopback stress test" + ) + stress_parser.add_argument("--target", required=True) + stress_parser.add_argument("--baud", required=True, type=parse_baud) + stress_parser.add_argument( + "--count", + required=True, + type=lambda value: parse_positive_int(value, "COUNT"), + ) + stress_parser.add_argument( + "--size", + required=True, + type=lambda value: parse_positive_int(value, "SIZE"), + ) + + return parser, parser.parse_args(argv) + + +def main(argv=None): + parser, args = parse_args(argv) + if not args.command: + parser.print_usage() + raise SystemExit(1) + + try: + if args.command == "list": + print_raw_uart_resources(args.config) + return + if args.command == "quick": + results = run_loopback(args.target, args.baud, "quick") + elif args.command == "stress": + results = run_loopback( + args.target, + args.baud, + "stress", + stress_count=args.count, + stress_size=args.size, + ) + else: + parser.print_usage() + raise SystemExit(1) + except (RuntimeError, ValueError) as exc: + print("Error: {}".format(exc)) + raise SystemExit(1) + + if not all(result.passed for result in results): + raise SystemExit(1) + + +if __name__ == "__main__": + main() diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/tests/test_serial_raw_uart_test.py b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/tests/test_serial_raw_uart_test.py new file mode 100644 index 0000000000..ca70055b5e --- /dev/null +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/tests/test_serial_raw_uart_test.py @@ -0,0 +1,259 @@ +import unittest +from io import StringIO +from unittest import mock + +import serial +import serial_raw_uart_test + + +class FakeSerial: + def __init__(self, baudrate=115200, echo=True): + self.baudrate = baudrate + self.echo = echo + self.closed = False + self.timeout = None + self.last_write = b"" + self.reset_input_called = False + self.reset_output_called = False + + def reset_input_buffer(self): + self.reset_input_called = True + + def reset_output_buffer(self): + self.reset_output_called = True + + def write(self, payload): + self.last_write = payload + + def flush(self): + pass + + def read(self, size): + if self.echo: + return self.last_write[:size] + return b"" + + def close(self): + self.closed = True + + +class SerialRawUartTest(unittest.TestCase): + """Unit tests for serial_raw_uart_test.py.""" + + def test_parse_raw_uart_ports(self): + ports = serial_raw_uart_test.parse_raw_uart_ports( + "uart0:0x2260000:max uart1:/dev/ttyS1:115200" + ) + + self.assertEqual(len(ports), 2) + self.assertEqual(ports[0].name, "uart0") + self.assertEqual(ports[0].name_id, "uart0") + self.assertEqual(ports[0].target, "0x2260000") + self.assertEqual(ports[0].target_id, "0x2260000") + self.assertEqual(ports[0].target_type, "addr") + self.assertEqual(ports[0].baud, "max") + self.assertEqual(ports[1].target_type, "node") + self.assertEqual(ports[1].baud, 115200) + + def test_parse_raw_uart_ports_rejects_malformed_entry(self): + with self.assertRaisesRegex(ValueError, "invalid format"): + serial_raw_uart_test.parse_raw_uart_port("uart0:/dev/ttyS1") + + def test_parse_raw_uart_ports_rejects_bad_baud(self): + with self.assertRaisesRegex(ValueError, "BAUD"): + serial_raw_uart_test.parse_raw_uart_port( + "uart0:/dev/ttyS1:not-a-baud" + ) + + def test_print_raw_uart_resources(self): + output = StringIO() + with mock.patch("sys.stdout", output): + serial_raw_uart_test.print_raw_uart_resources( + "uart0:0x2260000:max uart1:/dev/ttyS1:115200" + ) + + self.assertEqual( + output.getvalue(), + "name: uart0\n" + "name_id: uart0\n" + "target: 0x2260000\n" + "target_id: 0x2260000\n" + "target_type: addr\n" + "baud: max\n\n" + "name: uart1\n" + "name_id: uart1\n" + "target: /dev/ttyS1\n" + "target_id: dev_ttyS1\n" + "target_type: node\n" + "baud: 115200\n\n", + ) + + def test_get_rates_to_test(self): + self.assertEqual( + serial_raw_uart_test.get_rates_to_test(0, 9600), [9600] + ) + self.assertEqual( + serial_raw_uart_test.get_rates_to_test(0, "max"), [115200] + ) + self.assertEqual( + serial_raw_uart_test.get_rates_to_test(115200, "sweep"), + [9600, 19200, 38400, 57600, 115200], + ) + self.assertEqual( + serial_raw_uart_test.get_rates_to_test(115200, "autoscan-max"), + [115200, 57600, 38400, 19200, 9600], + ) + + @mock.patch( + "serial_raw_uart_test.get_driver_baud_base", return_value=3000000 + ) + @mock.patch("serial_raw_uart_test.get_active_consoles", return_value=set()) + def test_resolve_device_by_node(self, mock_consoles, mock_baud_base): + device = serial_raw_uart_test.resolve_device("/dev/ttyS1") + + self.assertEqual(device.target, "/dev/ttyS1") + self.assertEqual(device.dev_node, "/dev/ttyS1") + self.assertEqual(device.baud_base, 3000000) + self.assertFalse(device.is_console) + + @mock.patch( + "serial_raw_uart_test.scan_available_uarts", + return_value={"0x2260000": "/dev/ttyS0"}, + ) + @mock.patch( + "serial_raw_uart_test.get_driver_baud_base", return_value=115200 + ) + @mock.patch("serial_raw_uart_test.get_active_consoles", return_value=set()) + def test_resolve_device_by_address( + self, mock_consoles, mock_baud_base, mock_scan + ): + device = serial_raw_uart_test.resolve_device("0x2260000") + + self.assertEqual(device.target, "0x2260000") + self.assertEqual(device.dev_node, "/dev/ttyS0") + self.assertEqual(device.baud_base, 115200) + + def test_resolve_device_blocks_usb_serial_node(self): + with self.assertRaisesRegex(RuntimeError, "USB serial"): + serial_raw_uart_test.resolve_device("/dev/ttyUSB0") + + @mock.patch( + "serial_raw_uart_test.get_active_consoles", return_value={"ttyS0"} + ) + def test_resolve_device_blocks_console(self, mock_consoles): + with self.assertRaisesRegex(RuntimeError, "active system console"): + serial_raw_uart_test.resolve_device("/dev/ttyS0") + + def test_run_ping_success(self): + serial_conn = FakeSerial(echo=True) + + self.assertTrue(serial_raw_uart_test.run_ping(serial_conn)) + self.assertTrue(serial_conn.reset_input_called) + + def test_run_ping_failure(self): + self.assertFalse(serial_raw_uart_test.run_ping(FakeSerial(echo=False))) + + @mock.patch("os.urandom", return_value=b"abcd") + def test_run_stress_success(self, mock_urandom): + serial_conn = FakeSerial(echo=True) + + self.assertTrue( + serial_raw_uart_test.run_stress(serial_conn, count=2, size=4) + ) + self.assertTrue(serial_conn.reset_input_called) + self.assertTrue(serial_conn.reset_output_called) + + @mock.patch("os.urandom", return_value=b"abcd") + def test_run_stress_failure(self, mock_urandom): + self.assertFalse( + serial_raw_uart_test.run_stress( + FakeSerial(echo=False), count=2, size=4 + ) + ) + + @mock.patch("time.sleep") + @mock.patch("serial.Serial") + def test_open_serial_and_run_closes_connection( + self, mock_serial, mock_sleep + ): + serial_conn = FakeSerial() + mock_serial.return_value = serial_conn + + result = serial_raw_uart_test.open_serial_and_run( + "/dev/ttyS0", 115200, lambda conn: conn.baudrate + ) + + self.assertEqual(result, 115200) + self.assertTrue(serial_conn.closed) + mock_serial.assert_called_once_with("/dev/ttyS0", 115200, timeout=1.0) + + @mock.patch("serial_raw_uart_test.open_serial_and_run") + @mock.patch("serial_raw_uart_test.resolve_device") + def test_run_loopback_autoscan_stops_at_first_pass( + self, mock_resolve, mock_open + ): + mock_resolve.return_value = serial_raw_uart_test.UartDevice( + "0x2260000", "/dev/ttyS0", 115200, False + ) + mock_open.side_effect = [False, True] + + output = StringIO() + with mock.patch("sys.stdout", output): + results = serial_raw_uart_test.run_loopback( + "0x2260000", "autoscan-max", "quick" + ) + + self.assertEqual( + [result.baud_rate for result in results], [115200, 57600] + ) + self.assertFalse(results[0].passed) + self.assertTrue(results[1].passed) + self.assertIn("Max working baud: 57600", output.getvalue()) + + @mock.patch("serial_raw_uart_test.run_loopback") + def test_quick_command_exits_nonzero_on_failure(self, mock_run_loopback): + mock_run_loopback.return_value = [ + serial_raw_uart_test.LoopbackResult(115200, False, "FAILED") + ] + + with self.assertRaises(SystemExit) as cm: + serial_raw_uart_test.main( + ["quick", "--target", "/dev/ttyS0", "--baud", "115200"] + ) + + self.assertEqual(cm.exception.code, 1) + + @mock.patch("serial_raw_uart_test.run_loopback") + def test_stress_command_uses_count_and_size(self, mock_run_loopback): + mock_run_loopback.return_value = [ + serial_raw_uart_test.LoopbackResult(115200, True, "PASSED") + ] + + serial_raw_uart_test.main( + [ + "stress", + "--target", + "/dev/ttyS0", + "--baud", + "115200", + "--count", + "50", + "--size", + "512", + ] + ) + + mock_run_loopback.assert_called_once_with( + "/dev/ttyS0", + 115200, + "stress", + stress_count=50, + stress_size=512, + ) + + def test_serial_exception_is_available_for_mocks(self): + self.assertTrue(hasattr(serial, "SerialException")) + + +if __name__ == "__main__": + unittest.main() diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/README.md b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/README.md new file mode 100644 index 0000000000..5551dd64c4 --- /dev/null +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/README.md @@ -0,0 +1,136 @@ +# Serial raw UART loopback tests + +The serial raw UART loopback tests check that one or more raw UART ports can +transmit and receive data when each port has its TX and RX pins connected +together. + +## Test plan + +The generated jobs are included by `ce-oem-serial-automated` through these +template IDs: + +```text +ce-oem-serial/raw-uart-loopback-quick-tests +ce-oem-serial/raw-uart-loopback-stress-tests +``` + +The jobs also use `flags: also-after-suspend`, so Checkbox can generate +before-suspend and after-suspend variants for suspend flows. + +## Manifest + +Enable the manifest gate before running the generated jobs: + +```text +has_serial_raw_uart_loopback: True +``` + +If this manifest value is not `True`, the generated raw UART jobs are not +selected even when `RAW_UART_PORTS` is configured. + +## Environment + +Set `RAW_UART_PORTS` in the Checkbox configuration. The value is a +space-separated list of entries with this format: + +```text +NAME:TARGET:BAUD +``` + +Fields: + +| Field | Description | +| --- | --- | +| `NAME` | Short unique name for the UART. It is normalized for the generated job ID. | +| `TARGET` | Hardware address such as `0x2260000`, or a device node such as `/dev/ttyS1`. | +| `BAUD` | Integer baud rate, `max`, `sweep`, or `autoscan-max`. | + +Examples: + +```text +RAW_UART_PORTS=uart0:0x2260000:max +RAW_UART_PORTS="uart0:0x2260000:max uart1:/dev/ttyS1:115200" +RAW_UART_PORTS="uart0:0x2260000:sweep uart1:/dev/ttyS1:autoscan-max" +``` + +Stress jobs also support optional environment overrides. The PXU command uses +these defaults when the variables are not set: + +```text +RAW_UART_STRESS_COUNT=100 +RAW_UART_STRESS_SIZE=1024 +``` + +Example override: + +```text +RAW_UART_STRESS_COUNT=50 +RAW_UART_STRESS_SIZE=512 +``` + +## Resource input and output + +Input: + +```bash +serial_raw_uart_test.py list "uart0:0x2260000:max uart1:/dev/ttyS1:115200" +``` + +Output: + +```text +name: uart0 +name_id: uart0 +target: 0x2260000 +target_id: 0x2260000 +target_type: addr +baud: max + +name: uart1 +name_id: uart1 +target: /dev/ttyS1 +target_id: dev_ttyS1 +target_type: node +baud: 115200 +``` + +## Test input and output + +Quick test input: + +```bash +serial_raw_uart_test.py quick --target 0x2260000 --baud max +``` + +Example success output: + +```text +PASS: /dev/ttyS0 115200 baud PASSED +``` + +Example failure output: + +```text +FAIL: /dev/ttyS0 115200 baud FAILED +``` + +Stress test input: + +```bash +serial_raw_uart_test.py stress --target /dev/ttyS1 --baud 115200 --count 100 --size 1024 +``` + +Example success output: + +```text +PASS: /dev/ttyS1 115200 baud PASSED +``` + +## Safety defaults + +The helper is conservative for certification runs: + +* USB serial adapters are blocked for raw UART loopback. +* Active system console UARTs are blocked. +* Hardware-address targets are resolved from Linux UART driver data. +* Device-node targets are used directly after the same safety checks. diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/manifest.pxu b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/manifest.pxu index 73c84b2f4f..1eca23035e 100644 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/manifest.pxu +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/manifest.pxu @@ -7,3 +7,8 @@ unit: manifest entry id: has_serial_console_loopback _name: Does serial console port loopback to itself? value-type: bool + +unit: manifest entry +id: has_serial_raw_uart_loopback +_name: Has raw UART ports connected for loopback? +value-type: bool diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/serial_raw_uart.pxu b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/serial_raw_uart.pxu new file mode 100644 index 0000000000..49bee2adc8 --- /dev/null +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/serial_raw_uart.pxu @@ -0,0 +1,75 @@ +id: ce-oem-serial/raw-uart-list +_summary: + Generates raw UART loopback resources from user configuration +_description: + Generates one resource for each raw UART listed in RAW_UART_PORTS. + NAME:TARGET:BAUD +plugin: resource +estimated_duration: 1.0 +environ: + RAW_UART_PORTS +command: + if [ -z "$RAW_UART_PORTS" ]; then + exit 0 + fi + serial_raw_uart_test.py list "$RAW_UART_PORTS" + +unit: template +template-resource: ce-oem-serial/raw-uart-list +template-unit: job +template-engine: jinja2 +template-id: ce-oem-serial/raw-uart-loopback-quick-tests +id: ce-oem-serial/raw-uart-loopback-quick-{{ name_id }}-{{ target_id }} +imports: from com.canonical.plainbox import manifest +requires: + manifest.has_serial_raw_uart_loopback == 'True' +_template-summary: + Quick raw UART loopback tests +_summary: Quick raw UART loopback test for {{ name }} +_purpose: + To check that raw UART {{ name }} can send and receive loopback data. +_description: + This test opens {{ target }} as a raw UART loopback target and verifies a + short transmit/receive ping at baud setting {{ baud }}. +plugin: shell +user: root +category_id: com.canonical.certification::serial +estimated_duration: 10 +flags: also-after-suspend +command: + serial_raw_uart_test.py quick \ + --target "{{ target }}" \ + --baud "{{ baud }}" + +unit: template +template-resource: ce-oem-serial/raw-uart-list +template-unit: job +template-engine: jinja2 +template-id: ce-oem-serial/raw-uart-loopback-stress-tests +id: ce-oem-serial/raw-uart-loopback-stress-{{ name_id }}-{{ target_id }} +imports: from com.canonical.plainbox import manifest +requires: + manifest.has_serial_raw_uart_loopback == 'True' +_template-summary: + Stress raw UART loopback tests +_summary: Stress raw UART loopback test for {{ name }} +_purpose: + To check that raw UART {{ name }} can send and receive repeated loopback + payloads. +_description: + This test opens {{ target }} as a raw UART loopback target and verifies + repeated transmit/receive payloads at baud setting {{ baud }}. +plugin: shell +user: root +category_id: com.canonical.certification::serial +estimated_duration: 60 +flags: also-after-suspend +environ: + RAW_UART_STRESS_COUNT + RAW_UART_STRESS_SIZE +command: + serial_raw_uart_test.py stress \ + --target "{{ target }}" \ + --baud "{{ baud }}" \ + --count "${RAW_UART_STRESS_COUNT:-100}" \ + --size "${RAW_UART_STRESS_SIZE:-1024}" diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/test-plan.pxu b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/test-plan.pxu index fd8ac4aef1..9730f432b8 100644 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/test-plan.pxu +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/test-plan.pxu @@ -19,9 +19,12 @@ _description: bootstrap_include: ce-oem-serial/serial-list ce-oem-serial/serial-console-list + ce-oem-serial/raw-uart-list include: ce-oem-serial/serial-transmit-data-tests ce-oem-serial/serial-console-tests + ce-oem-serial/raw-uart-loopback-quick-tests + ce-oem-serial/raw-uart-loopback-stress-tests id: before-suspend-ce-oem-serial-automated unit: test plan @@ -31,9 +34,12 @@ _description: bootstrap_include: ce-oem-serial/serial-list ce-oem-serial/serial-console-list + ce-oem-serial/raw-uart-list include: ce-oem-serial/serial-transmit-data-.* ce-oem-serial/serial-console-(?!list\b).*$ + ce-oem-serial/raw-uart-loopback-quick-tests + ce-oem-serial/raw-uart-loopback-stress-tests id: after-suspend-ce-oem-serial-automated unit: test plan @@ -43,9 +49,12 @@ _description: bootstrap_include: ce-oem-serial/serial-list ce-oem-serial/serial-console-list + ce-oem-serial/raw-uart-list include: after-suspend-ce-oem-serial/serial-transmit-data-.* after-suspend-ce-oem-serial/serial-console-.* + after-suspend-ce-oem-serial/raw-uart-loopback-quick-tests + after-suspend-ce-oem-serial/raw-uart-loopback-stress-tests id: ce-oem-serial-stress unit: test plan From 55544b19f695e6b981af68bea5194c882fd79b89 Mon Sep 17 00:00:00 2001 From: Isaac Yang Date: Tue, 7 Jul 2026 16:22:27 +0800 Subject: [PATCH 02/14] Refine raw UART self-loopback tests Rename raw UART loopback coverage to self-loopback and document the required jumper connection. Add sweep-down support for quick and stress jobs, defaulting to enabled unless RAW_UART_SWEEP_DOWN is explicitly false. --- .../bin/serial_raw_uart_test.py | 58 ++++++++++--- .../tests/test_serial_raw_uart_test.py | 87 +++++++++++++++++++ .../units/serial/README.md | 67 ++++++++++++-- .../units/serial/manifest.pxu | 4 +- .../units/serial/serial_raw_uart.pxu | 57 ++++++++---- .../units/serial/test-plan.pxu | 12 +-- 6 files changed, 240 insertions(+), 45 deletions(-) diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/serial_raw_uart_test.py b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/serial_raw_uart_test.py index ffafc6c9a8..ce23a717be 100755 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/serial_raw_uart_test.py +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/serial_raw_uart_test.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""List and test raw UART loopback devices for CE OEM serial jobs.""" +"""List and test raw UART self-loopback devices for CE OEM serial jobs.""" import argparse import fcntl @@ -47,7 +47,7 @@ class RawUartPort: - """A configured raw UART loopback target.""" + """A configured raw UART self-loopback target.""" def __init__(self, name, target, baud): self.name = name @@ -292,8 +292,19 @@ def get_rates_to_test(baud_base, baud): raise ValueError("unsupported baud mode: {}".format(baud)) +def get_sweep_down_rates(baud): + """Return descending standard rates from the configured baud to 9600.""" + + if not isinstance(baud, int): + raise ValueError("--sweep-down requires an integer baud rate") + rates = [rate for rate in STANDARD_RATES if 9600 <= rate <= baud] + if baud not in rates: + rates.append(baud) + return sorted(set(rates), reverse=True) + + def run_ping(serial_conn): - """Run a quick loopback ping on an open serial connection.""" + """Run a quick self-loopback ping on an open serial connection.""" serial_conn.timeout = 1.0 serial_conn.reset_input_buffer() @@ -305,7 +316,7 @@ def run_ping(serial_conn): def run_stress(serial_conn, count, size, timeout_factor=3.0): - """Run loopback stress traffic on an open serial connection.""" + """Run self-loopback stress traffic on an open serial connection.""" byte_time = (size * 10) / serial_conn.baudrate serial_conn.timeout = max(2.0, byte_time * timeout_factor) @@ -333,11 +344,22 @@ def open_serial_and_run(dev_node, baud_rate, run_fn): time.sleep(0.1) -def run_loopback(target, baud, mode, stress_count=100, stress_size=1024): - """Run quick or stress loopback tests for one target.""" +def run_loopback( + target, + baud, + mode, + stress_count=100, + stress_size=1024, + sweep_down=False, +): + """Run quick or stress self-loopback tests for one target.""" device = resolve_device(target) - rates = get_rates_to_test(device.baud_base, baud) + rates = ( + get_sweep_down_rates(baud) + if sweep_down + else get_rates_to_test(device.baud_base, baud) + ) results = [] for rate in rates: @@ -396,16 +418,26 @@ def parse_args(argv=None): ) quick_parser = subparsers.add_parser( - "quick", help="Run a raw UART loopback ping test" + "quick", help="Run a raw UART self-loopback ping test" ) quick_parser.add_argument("--target", required=True) quick_parser.add_argument("--baud", required=True, type=parse_baud) + quick_parser.add_argument( + "--sweep-down", + action="store_true", + help="Sweep from --baud down to 9600; requires integer --baud", + ) stress_parser = subparsers.add_parser( - "stress", help="Run a raw UART loopback stress test" + "stress", help="Run a raw UART self-loopback stress test" ) stress_parser.add_argument("--target", required=True) stress_parser.add_argument("--baud", required=True, type=parse_baud) + stress_parser.add_argument( + "--sweep-down", + action="store_true", + help="Sweep from --baud down to 9600; requires integer --baud", + ) stress_parser.add_argument( "--count", required=True, @@ -431,7 +463,12 @@ def main(argv=None): print_raw_uart_resources(args.config) return if args.command == "quick": - results = run_loopback(args.target, args.baud, "quick") + results = run_loopback( + args.target, + args.baud, + "quick", + sweep_down=args.sweep_down, + ) elif args.command == "stress": results = run_loopback( args.target, @@ -439,6 +476,7 @@ def main(argv=None): "stress", stress_count=args.count, stress_size=args.size, + sweep_down=args.sweep_down, ) else: parser.print_usage() diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/tests/test_serial_raw_uart_test.py b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/tests/test_serial_raw_uart_test.py index ca70055b5e..c550f9c403 100644 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/tests/test_serial_raw_uart_test.py +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/tests/test_serial_raw_uart_test.py @@ -104,6 +104,20 @@ def test_get_rates_to_test(self): [115200, 57600, 38400, 19200, 9600], ) + def test_get_sweep_down_rates(self): + self.assertEqual( + serial_raw_uart_test.get_sweep_down_rates(115200), + [115200, 57600, 38400, 19200, 9600], + ) + self.assertEqual( + serial_raw_uart_test.get_sweep_down_rates(123456), + [123456, 115200, 57600, 38400, 19200, 9600], + ) + + def test_sweep_down_requires_integer_baud(self): + with self.assertRaisesRegex(ValueError, "requires an integer baud"): + serial_raw_uart_test.get_sweep_down_rates("max") + @mock.patch( "serial_raw_uart_test.get_driver_baud_base", return_value=3000000 ) @@ -210,6 +224,24 @@ def test_run_loopback_autoscan_stops_at_first_pass( self.assertTrue(results[1].passed) self.assertIn("Max working baud: 57600", output.getvalue()) + @mock.patch("serial_raw_uart_test.open_serial_and_run") + @mock.patch("serial_raw_uart_test.resolve_device") + def test_run_loopback_sweep_down(self, mock_resolve, mock_open): + mock_resolve.return_value = serial_raw_uart_test.UartDevice( + "/dev/ttyS0", "/dev/ttyS0", 4000000, False + ) + mock_open.return_value = True + + with mock.patch("sys.stdout", StringIO()): + results = serial_raw_uart_test.run_loopback( + "/dev/ttyS0", 115200, "quick", sweep_down=True + ) + + self.assertEqual( + [result.baud_rate for result in results], + [115200, 57600, 38400, 19200, 9600], + ) + @mock.patch("serial_raw_uart_test.run_loopback") def test_quick_command_exits_nonzero_on_failure(self, mock_run_loopback): mock_run_loopback.return_value = [ @@ -249,6 +281,61 @@ def test_stress_command_uses_count_and_size(self, mock_run_loopback): "stress", stress_count=50, stress_size=512, + sweep_down=False, + ) + + @mock.patch("serial_raw_uart_test.run_loopback") + def test_quick_command_uses_sweep_down(self, mock_run_loopback): + mock_run_loopback.return_value = [ + serial_raw_uart_test.LoopbackResult(115200, True, "PASSED") + ] + + serial_raw_uart_test.main( + [ + "quick", + "--target", + "/dev/ttyS0", + "--baud", + "115200", + "--sweep-down", + ] + ) + + mock_run_loopback.assert_called_once_with( + "/dev/ttyS0", + 115200, + "quick", + sweep_down=True, + ) + + @mock.patch("serial_raw_uart_test.run_loopback") + def test_stress_command_uses_sweep_down(self, mock_run_loopback): + mock_run_loopback.return_value = [ + serial_raw_uart_test.LoopbackResult(115200, True, "PASSED") + ] + + serial_raw_uart_test.main( + [ + "stress", + "--target", + "/dev/ttyS0", + "--baud", + "115200", + "--count", + "50", + "--size", + "512", + "--sweep-down", + ] + ) + + mock_run_loopback.assert_called_once_with( + "/dev/ttyS0", + 115200, + "stress", + stress_count=50, + stress_size=512, + sweep_down=True, ) def test_serial_exception_is_available_for_mocks(self): diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/README.md b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/README.md index 5551dd64c4..45be487055 100644 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/README.md +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/README.md @@ -1,8 +1,11 @@ -# Serial raw UART loopback tests +# Serial raw UART self-loopback tests -The serial raw UART loopback tests check that one or more raw UART ports can +The serial raw UART self-loopback tests check that one or more raw UART ports can transmit and receive data when each port has its TX and RX pins connected -together. +together on the same physical UART port. + +These tests only validate self-loopback wiring. They do not validate +communication between two different UART ports or two different devices. ## Test plan @@ -10,8 +13,8 @@ The generated jobs are included by `ce-oem-serial-automated` through these template IDs: ```text -ce-oem-serial/raw-uart-loopback-quick-tests -ce-oem-serial/raw-uart-loopback-stress-tests +ce-oem-serial/raw-uart-self-loopback-quick-tests +ce-oem-serial/raw-uart-self-loopback-stress-tests ``` The jobs also use `flags: also-after-suspend`, so Checkbox can generate @@ -22,7 +25,7 @@ before-suspend and after-suspend variants for suspend flows. Enable the manifest gate before running the generated jobs: ```text -has_serial_raw_uart_loopback: True +has_serial_raw_uart_self_loopback: True ``` If this manifest value is not `True`, the generated raw UART jobs are not @@ -53,6 +56,23 @@ RAW_UART_PORTS="uart0:0x2260000:max uart1:/dev/ttyS1:115200" RAW_UART_PORTS="uart0:0x2260000:sweep uart1:/dev/ttyS1:autoscan-max" ``` +Quick and stress jobs sweep downward from an explicit integer baud rate to 9600 +by default. This default is used when `RAW_UART_SWEEP_DOWN` is not set: + +```text +RAW_UART_SWEEP_DOWN=true +``` + +`RAW_UART_SWEEP_DOWN` requires `BAUD` to be an integer, such as `115200`. It is +not valid with `max`, `sweep`, or `autoscan-max`. + +Disable sweep-down explicitly when you only want to test the configured integer +baud rate: + +```text +RAW_UART_SWEEP_DOWN=false +``` + Stress jobs also support optional environment overrides. The PXU command uses these defaults when the variables are not set: @@ -68,6 +88,23 @@ RAW_UART_STRESS_COUNT=50 RAW_UART_STRESS_SIZE=512 ``` +## Hardware connection + +Connect the same UART port's TX and RX pins together before running the test: + +```text +UART TX -----+ + | (Jumper short) +UART RX -----+ +``` + +The TX and RX pins must belong to the same UART controller and the same +physical port. This is why the test is named self-loopback. Do not connect TX +from one UART port to RX on a different UART port for this test case. + +USB serial adapters are blocked by default because they are not raw UART +self-loopback targets for this CE OEM test. + ## Resource input and output Input: @@ -96,7 +133,7 @@ baud: 115200 ## Test input and output -Quick test input: +Quick self-loopback test input: ```bash serial_raw_uart_test.py quick --target 0x2260000 --baud max @@ -114,12 +151,24 @@ Example failure output: FAIL: /dev/ttyS0 115200 baud FAILED ``` -Stress test input: +Quick self-loopback sweep-down input: + +```bash +serial_raw_uart_test.py quick --target /dev/ttyS1 --baud 115200 --sweep-down +``` + +Stress self-loopback test input: ```bash serial_raw_uart_test.py stress --target /dev/ttyS1 --baud 115200 --count 100 --size 1024 ``` +Stress self-loopback sweep-down input: + +```bash +serial_raw_uart_test.py stress --target /dev/ttyS1 --baud 115200 --count 100 --size 1024 --sweep-down +``` + Example success output: ```text @@ -130,7 +179,7 @@ PASS: /dev/ttyS1 115200 baud PASSED The helper is conservative for certification runs: -* USB serial adapters are blocked for raw UART loopback. +* USB serial adapters are blocked for raw UART self-loopback. * Active system console UARTs are blocked. * Hardware-address targets are resolved from Linux UART driver data. * Device-node targets are used directly after the same safety checks. diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/manifest.pxu b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/manifest.pxu index 1eca23035e..469a7e8177 100644 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/manifest.pxu +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/manifest.pxu @@ -9,6 +9,6 @@ _name: Does serial console port loopback to itself? value-type: bool unit: manifest entry -id: has_serial_raw_uart_loopback -_name: Has raw UART ports connected for loopback? +id: has_serial_raw_uart_self_loopback +_name: Has raw UART ports connected for self-loopback? value-type: bool diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/serial_raw_uart.pxu b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/serial_raw_uart.pxu index 49bee2adc8..8f88d0fee5 100644 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/serial_raw_uart.pxu +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/serial_raw_uart.pxu @@ -1,6 +1,6 @@ id: ce-oem-serial/raw-uart-list _summary: - Generates raw UART loopback resources from user configuration + Generates raw UART self-loopback resources from user configuration _description: Generates one resource for each raw UART listed in RAW_UART_PORTS. NAME:TARGET:BAUD @@ -18,46 +18,57 @@ unit: template template-resource: ce-oem-serial/raw-uart-list template-unit: job template-engine: jinja2 -template-id: ce-oem-serial/raw-uart-loopback-quick-tests -id: ce-oem-serial/raw-uart-loopback-quick-{{ name_id }}-{{ target_id }} +template-id: ce-oem-serial/raw-uart-self-loopback-quick-tests +id: ce-oem-serial/raw-uart-self-loopback-quick-{{ name_id }}-{{ target_id }} imports: from com.canonical.plainbox import manifest requires: - manifest.has_serial_raw_uart_loopback == 'True' + manifest.has_serial_raw_uart_self_loopback == 'True' _template-summary: - Quick raw UART loopback tests -_summary: Quick raw UART loopback test for {{ name }} + Quick raw UART self-loopback tests +_summary: Quick raw UART self-loopback test for {{ name }} _purpose: - To check that raw UART {{ name }} can send and receive loopback data. + To check that raw UART {{ name }} can send and receive self-loopback data. _description: - This test opens {{ target }} as a raw UART loopback target and verifies a - short transmit/receive ping at baud setting {{ baud }}. + This test opens {{ target }} as a raw UART self-loopback target and verifies + a short transmit/receive ping at baud setting {{ baud }}. plugin: shell user: root category_id: com.canonical.certification::serial estimated_duration: 10 flags: also-after-suspend +environ: + RAW_UART_SWEEP_DOWN command: + case "${RAW_UART_SWEEP_DOWN:-true}" in + 0|false|False|FALSE|no|No|NO) + SWEEP_DOWN_OPTION="" + ;; + *) + SWEEP_DOWN_OPTION="--sweep-down" + ;; + esac serial_raw_uart_test.py quick \ --target "{{ target }}" \ - --baud "{{ baud }}" + --baud "{{ baud }}" \ + $SWEEP_DOWN_OPTION unit: template template-resource: ce-oem-serial/raw-uart-list template-unit: job template-engine: jinja2 -template-id: ce-oem-serial/raw-uart-loopback-stress-tests -id: ce-oem-serial/raw-uart-loopback-stress-{{ name_id }}-{{ target_id }} +template-id: ce-oem-serial/raw-uart-self-loopback-stress-tests +id: ce-oem-serial/raw-uart-self-loopback-stress-{{ name_id }}-{{ target_id }} imports: from com.canonical.plainbox import manifest requires: - manifest.has_serial_raw_uart_loopback == 'True' + manifest.has_serial_raw_uart_self_loopback == 'True' _template-summary: - Stress raw UART loopback tests -_summary: Stress raw UART loopback test for {{ name }} + Stress raw UART self-loopback tests +_summary: Stress raw UART self-loopback test for {{ name }} _purpose: - To check that raw UART {{ name }} can send and receive repeated loopback + To check that raw UART {{ name }} can send and receive repeated self-loopback payloads. _description: - This test opens {{ target }} as a raw UART loopback target and verifies + This test opens {{ target }} as a raw UART self-loopback target and verifies repeated transmit/receive payloads at baud setting {{ baud }}. plugin: shell user: root @@ -67,9 +78,19 @@ flags: also-after-suspend environ: RAW_UART_STRESS_COUNT RAW_UART_STRESS_SIZE + RAW_UART_SWEEP_DOWN command: + case "${RAW_UART_SWEEP_DOWN:-true}" in + 0|false|False|FALSE|no|No|NO) + SWEEP_DOWN_OPTION="" + ;; + *) + SWEEP_DOWN_OPTION="--sweep-down" + ;; + esac serial_raw_uart_test.py stress \ --target "{{ target }}" \ --baud "{{ baud }}" \ --count "${RAW_UART_STRESS_COUNT:-100}" \ - --size "${RAW_UART_STRESS_SIZE:-1024}" + --size "${RAW_UART_STRESS_SIZE:-1024}" \ + $SWEEP_DOWN_OPTION diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/test-plan.pxu b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/test-plan.pxu index 9730f432b8..bc263779a1 100644 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/test-plan.pxu +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/test-plan.pxu @@ -23,8 +23,8 @@ bootstrap_include: include: ce-oem-serial/serial-transmit-data-tests ce-oem-serial/serial-console-tests - ce-oem-serial/raw-uart-loopback-quick-tests - ce-oem-serial/raw-uart-loopback-stress-tests + ce-oem-serial/raw-uart-self-loopback-quick-tests + ce-oem-serial/raw-uart-self-loopback-stress-tests id: before-suspend-ce-oem-serial-automated unit: test plan @@ -38,8 +38,8 @@ bootstrap_include: include: ce-oem-serial/serial-transmit-data-.* ce-oem-serial/serial-console-(?!list\b).*$ - ce-oem-serial/raw-uart-loopback-quick-tests - ce-oem-serial/raw-uart-loopback-stress-tests + ce-oem-serial/raw-uart-self-loopback-quick-tests + ce-oem-serial/raw-uart-self-loopback-stress-tests id: after-suspend-ce-oem-serial-automated unit: test plan @@ -53,8 +53,8 @@ bootstrap_include: include: after-suspend-ce-oem-serial/serial-transmit-data-.* after-suspend-ce-oem-serial/serial-console-.* - after-suspend-ce-oem-serial/raw-uart-loopback-quick-tests - after-suspend-ce-oem-serial/raw-uart-loopback-stress-tests + after-suspend-ce-oem-serial/raw-uart-self-loopback-quick-tests + after-suspend-ce-oem-serial/raw-uart-self-loopback-stress-tests id: ce-oem-serial-stress unit: test plan From b0a30553eee343f32910347ab4b784deaee5c8fd Mon Sep 17 00:00:00 2001 From: Isaac Yang Date: Wed, 8 Jul 2026 10:06:02 +0800 Subject: [PATCH 03/14] Refine raw UART sweep-down behavior Allow max baud to work with sweep-down and clarify the self-loopback hardware connection documentation. --- .../bin/serial_raw_uart_test.py | 19 ++++++++++++------- .../tests/test_serial_raw_uart_test.py | 12 ++++++++---- .../units/serial/README.md | 5 +++-- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/serial_raw_uart_test.py b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/serial_raw_uart_test.py index ce23a717be..c01c9e3aec 100755 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/serial_raw_uart_test.py +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/serial_raw_uart_test.py @@ -292,14 +292,19 @@ def get_rates_to_test(baud_base, baud): raise ValueError("unsupported baud mode: {}".format(baud)) -def get_sweep_down_rates(baud): +def get_sweep_down_rates(baud_base, baud): """Return descending standard rates from the configured baud to 9600.""" - if not isinstance(baud, int): - raise ValueError("--sweep-down requires an integer baud rate") - rates = [rate for rate in STANDARD_RATES if 9600 <= rate <= baud] - if baud not in rates: - rates.append(baud) + if baud == "max": + start_baud = baud_base if baud_base > 0 else 115200 + elif isinstance(baud, int): + start_baud = baud + else: + raise ValueError("--sweep-down requires an integer baud rate or max") + + rates = [rate for rate in STANDARD_RATES if 9600 <= rate <= start_baud] + if start_baud not in rates: + rates.append(start_baud) return sorted(set(rates), reverse=True) @@ -356,7 +361,7 @@ def run_loopback( device = resolve_device(target) rates = ( - get_sweep_down_rates(baud) + get_sweep_down_rates(device.baud_base, baud) if sweep_down else get_rates_to_test(device.baud_base, baud) ) diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/tests/test_serial_raw_uart_test.py b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/tests/test_serial_raw_uart_test.py index c550f9c403..506b5f8815 100644 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/tests/test_serial_raw_uart_test.py +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/tests/test_serial_raw_uart_test.py @@ -106,17 +106,21 @@ def test_get_rates_to_test(self): def test_get_sweep_down_rates(self): self.assertEqual( - serial_raw_uart_test.get_sweep_down_rates(115200), + serial_raw_uart_test.get_sweep_down_rates(0, 115200), [115200, 57600, 38400, 19200, 9600], ) self.assertEqual( - serial_raw_uart_test.get_sweep_down_rates(123456), + serial_raw_uart_test.get_sweep_down_rates(0, 123456), [123456, 115200, 57600, 38400, 19200, 9600], ) + self.assertEqual( + serial_raw_uart_test.get_sweep_down_rates(115200, "max"), + [115200, 57600, 38400, 19200, 9600], + ) def test_sweep_down_requires_integer_baud(self): - with self.assertRaisesRegex(ValueError, "requires an integer baud"): - serial_raw_uart_test.get_sweep_down_rates("max") + with self.assertRaisesRegex(ValueError, "integer baud rate or max"): + serial_raw_uart_test.get_sweep_down_rates(115200, "sweep") @mock.patch( "serial_raw_uart_test.get_driver_baud_base", return_value=3000000 diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/README.md b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/README.md index 45be487055..d9feec7d67 100644 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/README.md +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/README.md @@ -63,8 +63,9 @@ by default. This default is used when `RAW_UART_SWEEP_DOWN` is not set: RAW_UART_SWEEP_DOWN=true ``` -`RAW_UART_SWEEP_DOWN` requires `BAUD` to be an integer, such as `115200`. It is -not valid with `max`, `sweep`, or `autoscan-max`. +`RAW_UART_SWEEP_DOWN` supports integer baud values, such as `115200`, and +`max`, which starts from the driver-reported baud base. It is not valid with +`sweep` or `autoscan-max`. Disable sweep-down explicitly when you only want to test the configured integer baud rate: From 92a5d4f56d52bdfe8d14bf526bde9972f8be697d Mon Sep 17 00:00:00 2001 From: Isaac Yang <47034756+seankingyang@users.noreply.github.com> Date: Tue, 11 Aug 2026 14:35:01 +0800 Subject: [PATCH 04/14] Update contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic20/snap/snapcraft.yaml --- .../checkbox-ce-oem-snap/series_classic20/snap/snapcraft.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic20/snap/snapcraft.yaml b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic20/snap/snapcraft.yaml index 3e72231d76..97c72933f7 100644 --- a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic20/snap/snapcraft.yaml +++ b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic20/snap/snapcraft.yaml @@ -44,7 +44,6 @@ parts: - device-tree-compiler - linuxptp - snmp - - python3-serial - gpsd override-prime: | snapcraftctl prime From 58b386c9ccf0b4732deee81d17ab9428dc92349c Mon Sep 17 00:00:00 2001 From: Isaac Yang <47034756+seankingyang@users.noreply.github.com> Date: Tue, 11 Aug 2026 14:35:13 +0800 Subject: [PATCH 05/14] Update contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic22/snap/snapcraft.yaml --- .../checkbox-ce-oem-snap/series_classic22/snap/snapcraft.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic22/snap/snapcraft.yaml b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic22/snap/snapcraft.yaml index 4a12b06b6f..90c6bdd40c 100644 --- a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic22/snap/snapcraft.yaml +++ b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic22/snap/snapcraft.yaml @@ -45,7 +45,6 @@ parts: - linuxptp - snmp - python3-rpyc - - python3-serial - gpsd override-prime: | snapcraftctl prime From 269a7c3960af7e7d88134562dd10a88defcb4912 Mon Sep 17 00:00:00 2001 From: Isaac Yang <47034756+seankingyang@users.noreply.github.com> Date: Tue, 11 Aug 2026 14:35:20 +0800 Subject: [PATCH 06/14] Update contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc22/snap/snapcraft.yaml --- .../checkbox-ce-oem-snap/series_uc22/snap/snapcraft.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc22/snap/snapcraft.yaml b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc22/snap/snapcraft.yaml index 259449800f..991f08d970 100644 --- a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc22/snap/snapcraft.yaml +++ b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc22/snap/snapcraft.yaml @@ -83,7 +83,6 @@ parts: - linuxptp - snmp - python3-rpyc - - python3-serial - gpsd override-prime: | snapcraftctl prime From 594979c54e307338e9498fc72ccc5429354c356d Mon Sep 17 00:00:00 2001 From: Isaac Yang <47034756+seankingyang@users.noreply.github.com> Date: Tue, 11 Aug 2026 14:35:30 +0800 Subject: [PATCH 07/14] Update contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc24/snap/snapcraft.yaml --- .../checkbox-ce-oem-snap/series_uc24/snap/snapcraft.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc24/snap/snapcraft.yaml b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc24/snap/snapcraft.yaml index 3b0cb9de62..6c54df729d 100644 --- a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc24/snap/snapcraft.yaml +++ b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc24/snap/snapcraft.yaml @@ -83,7 +83,6 @@ parts: - linuxptp - snmp - python3-rpyc - - python3-serial - gpsd override-prime: | craftctl default From 1dd288cfcc6a7abbe94db232d76005337ae6a944 Mon Sep 17 00:00:00 2001 From: Isaac Yang <47034756+seankingyang@users.noreply.github.com> Date: Tue, 11 Aug 2026 14:35:38 +0800 Subject: [PATCH 08/14] Update contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc26/snap/snapcraft.yaml --- .../checkbox-ce-oem-snap/series_uc26/snap/snapcraft.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc26/snap/snapcraft.yaml b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc26/snap/snapcraft.yaml index 5592e4abbf..9cf77e4978 100644 --- a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc26/snap/snapcraft.yaml +++ b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc26/snap/snapcraft.yaml @@ -83,7 +83,6 @@ parts: - linuxptp - snmp - python3-rpyc - - python3-serial - gpsd override-build: | export PYTHONPATH=/snap/checkbox26/current/lib/python3.14/site-packages/:/usr/lib/python3/dist-packages From 09b2610d54b679215dc88928eabecb03883366ea Mon Sep 17 00:00:00 2001 From: Isaac Yang <47034756+seankingyang@users.noreply.github.com> Date: Tue, 11 Aug 2026 14:35:46 +0800 Subject: [PATCH 09/14] Update contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc20/snap/snapcraft.yaml --- .../checkbox-ce-oem-snap/series_uc20/snap/snapcraft.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc20/snap/snapcraft.yaml b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc20/snap/snapcraft.yaml index 0ac59493fc..8f1359ee07 100644 --- a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc20/snap/snapcraft.yaml +++ b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_uc20/snap/snapcraft.yaml @@ -82,7 +82,6 @@ parts: - device-tree-compiler - linuxptp - snmp - - python3-serial - gpsd override-prime: | snapcraftctl prime From 8d410cd4df08eead67a3ba42fa5ba16702378cae Mon Sep 17 00:00:00 2001 From: Isaac Yang <47034756+seankingyang@users.noreply.github.com> Date: Tue, 11 Aug 2026 14:35:53 +0800 Subject: [PATCH 10/14] Update contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic26/snap/snapcraft.yaml --- .../checkbox-ce-oem-snap/series_classic26/snap/snapcraft.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic26/snap/snapcraft.yaml b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic26/snap/snapcraft.yaml index 5777a177b7..a866c20bc0 100644 --- a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic26/snap/snapcraft.yaml +++ b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic26/snap/snapcraft.yaml @@ -45,7 +45,6 @@ parts: - linuxptp - snmp - python3-rpyc - - python3-serial - gpsd override-prime: | craftctl default From 5d2d3eb686f971ba0c02ceb42030aeb3e8fda928 Mon Sep 17 00:00:00 2001 From: Isaac Yang <47034756+seankingyang@users.noreply.github.com> Date: Tue, 11 Aug 2026 14:36:00 +0800 Subject: [PATCH 11/14] Update contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic24/snap/snapcraft.yaml --- .../checkbox-ce-oem-snap/series_classic24/snap/snapcraft.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic24/snap/snapcraft.yaml b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic24/snap/snapcraft.yaml index dc69132aac..99197191da 100644 --- a/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic24/snap/snapcraft.yaml +++ b/contrib/checkbox-ce-oem/checkbox-ce-oem-snap/series_classic24/snap/snapcraft.yaml @@ -45,7 +45,6 @@ parts: - linuxptp - snmp - python3-rpyc - - python3-serial - gpsd override-prime: | craftctl default From 25ced6ec0f82a7df1ed7972dfdb9c425713c02c6 Mon Sep 17 00:00:00 2001 From: Isaac Yang <47034756+seankingyang@users.noreply.github.com> Date: Wed, 12 Aug 2026 16:53:09 +0800 Subject: [PATCH 12/14] Update contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/serial_raw_uart.pxu Co-authored-by: Pei Yao-Chang --- .../checkbox-provider-ce-oem/units/serial/serial_raw_uart.pxu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/serial_raw_uart.pxu b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/serial_raw_uart.pxu index 8f88d0fee5..d974696884 100644 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/serial_raw_uart.pxu +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/serial_raw_uart.pxu @@ -1,7 +1,7 @@ id: ce-oem-serial/raw-uart-list _summary: Generates raw UART self-loopback resources from user configuration -_description: +_purpose: Generates one resource for each raw UART listed in RAW_UART_PORTS. NAME:TARGET:BAUD plugin: resource From 46a09da36dbcb398f2cb95a9c4de221aec7ebf5b Mon Sep 17 00:00:00 2001 From: Isaac Yang <47034756+seankingyang@users.noreply.github.com> Date: Wed, 12 Aug 2026 16:53:18 +0800 Subject: [PATCH 13/14] Update contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/serial_raw_uart.pxu Co-authored-by: Pei Yao-Chang --- .../checkbox-provider-ce-oem/units/serial/serial_raw_uart.pxu | 1 - 1 file changed, 1 deletion(-) diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/serial_raw_uart.pxu b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/serial_raw_uart.pxu index d974696884..8d1331ef4e 100644 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/serial_raw_uart.pxu +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/serial_raw_uart.pxu @@ -67,7 +67,6 @@ _summary: Stress raw UART self-loopback test for {{ name }} _purpose: To check that raw UART {{ name }} can send and receive repeated self-loopback payloads. -_description: This test opens {{ target }} as a raw UART self-loopback target and verifies repeated transmit/receive payloads at baud setting {{ baud }}. plugin: shell From 2a733e54ed0abe51651ed66b8749b8ebe95a6ce0 Mon Sep 17 00:00:00 2001 From: Isaac Yang <47034756+seankingyang@users.noreply.github.com> Date: Wed, 12 Aug 2026 16:53:26 +0800 Subject: [PATCH 14/14] Update contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/serial_raw_uart.pxu Co-authored-by: Pei Yao-Chang --- .../checkbox-provider-ce-oem/units/serial/serial_raw_uart.pxu | 1 - 1 file changed, 1 deletion(-) diff --git a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/serial_raw_uart.pxu b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/serial_raw_uart.pxu index 8d1331ef4e..547a519401 100644 --- a/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/serial_raw_uart.pxu +++ b/contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/serial/serial_raw_uart.pxu @@ -28,7 +28,6 @@ _template-summary: _summary: Quick raw UART self-loopback test for {{ name }} _purpose: To check that raw UART {{ name }} can send and receive self-loopback data. -_description: This test opens {{ target }} as a raw UART self-loopback target and verifies a short transmit/receive ping at baud setting {{ baud }}. plugin: shell