C6-Reticulum-ASM/tests/conftest.py
DeFiDude 5416fb8f87 HwTarget: hardware test path against the Adafruit ESP32-C6 Feather
Closes the milestone-1 §harness deliverable's third target (the abstract
spec listed Emu/Hw/Oracle; only the Oracle and Emu targets had
implementations until now).

- HwTarget.start/stop/write/read against pyserial + esptool. Class-level
  flash cache keyed on (port, image-bin path, mtime) so back-to-back
  tests share the ~2 s reflash; per-test cost is just the DTR/RTS reset
  pulse and a boot-drain to the `boot\tready` marker.
- BuildArtifacts grew an `image_bin` field; build('c6') now also runs
  `make image` so the .image.bin and .elf stay in lockstep when a test
  forces a rebuild.
- TargetConfig grew image_bin / flash_chip / auto_flash / boot_ready_timeout
  fields; defaults match the Adafruit Feather (chip=esp32c6, port comes
  from --hardware-port / RATSPEAK_HW_PORT, default /dev/cu.usbmodem4101).
- conftest.py adds --hardware (and RATSPEAK_HW=1 env) opt-in; tests
  marked @pytest.mark.hardware skip cleanly without the flag.
- tests/hardware/test_basic_demo.py — three round-trip tests on real
  silicon: HEADER_1 packet → packet.parsed; short payload → packet.rejected;
  'S'+'abc' → SHA-256 KAT bit-for-bit equal to FIPS 180-4 §B.1.
- 442 pytest pass without --hardware (3 hardware skipped); 445 pass
  with --hardware in ~28 s.
2026-05-02 22:40:14 -06:00

68 lines
2.2 KiB
Python

"""pytest configuration: put `tests/` and `tools/` on sys.path so test modules
can import `harness` and the tool modules directly.
We deliberately avoid making `tests/` a Python package — the directory only
contains test modules and the harness, and the simpler import path keeps
fixture wiring obvious.
Hardware-target opt-in. Tests in `tests/hardware/` exercise a physical
Adafruit ESP32-C6 Feather connected over USB-C (per ADR-0010 the
USB-Serial/JTAG endpoint enumerates as /dev/cu.usbmodemNNNN). They are
slow and require the chip to be on bench, so we mark them with the
`hardware` pytest marker and skip them unless `--hardware` is passed
(or `RATSPEAK_HW=1` is set in the environment for CI parity).
"""
from __future__ import annotations
import os
import sys
from pathlib import Path
import pytest
REPO_ROOT = Path(__file__).resolve().parent.parent
TESTS_DIR = REPO_ROOT / "tests"
TOOLS_DIR = REPO_ROOT / "tools"
for entry in (TESTS_DIR, TOOLS_DIR):
s = str(entry)
if s not in sys.path:
sys.path.insert(0, s)
def pytest_addoption(parser: pytest.Parser) -> None:
parser.addoption(
"--hardware",
action="store_true",
default=False,
help="Run tests marked @pytest.mark.hardware against the physical "
"Adafruit ESP32-C6 Feather (default: skipped).",
)
parser.addoption(
"--hardware-port",
action="store",
default=os.environ.get("RATSPEAK_HW_PORT", "/dev/cu.usbmodem4101"),
help="Serial port for the C6 (default: /dev/cu.usbmodem4101 or "
"$RATSPEAK_HW_PORT). Only consulted when --hardware is set.",
)
def pytest_configure(config: pytest.Config) -> None:
config.addinivalue_line(
"markers",
"hardware: requires the Adafruit ESP32-C6 Feather connected over "
"USB-C (run with --hardware or set RATSPEAK_HW=1)",
)
def pytest_collection_modifyitems(
config: pytest.Config, items: list[pytest.Item]
) -> None:
enabled = config.getoption("--hardware") or os.environ.get("RATSPEAK_HW") == "1"
if enabled:
return
skip_hw = pytest.mark.skip(reason="needs --hardware (physical C6 on bench)")
for item in items:
if "hardware" in item.keywords:
item.add_marker(skip_hw)