mirror of
https://github.com/ratspeak/C6-Reticulum-ASM
synced 2026-08-12 18:07:18 -04:00
Lays the verifier-loop infrastructure that every subsequent function goes through. Every change is wired to make ci. Tooling (tools/): - parse_spec.py validates the ADR-0007 spec block on every .S file (required fields, @function ↔ basename, @module ↔ directory, @adrs ↔ Accepted ADRs, @verify/@tests path existence, unbounded-cycles ↔ not-required-ct). - check_registry.py cross-checks FUNCTIONS.md against src/ — every registered global has a source file (and vice versa), every depends-on resolves, status disagreements raise warnings. Test harness (tests/harness/): - log_parser.py parses the ADR-0004 log line format with selectors. - target.py: Target ABC + NullTarget loopback + Emu/Hw skeletons that detect tool availability and raise TargetUnavailable until wired (deferred until there's a binary to drive end-to-end). - oracle.py: pure-Python KISS encode/decode reference + RNS.Packet shim for differential testing. - verify.py + verify_cli.py + ./verify wrapper: the dispatcher. Runs spec-validate, pytest, and the @verify-pointed tool (kat-only, .saw, .tla, .py); reports pass/fail/skip/not-implemented. Build chain (toolchain/): - qemu-virt.ld: DRAM @ 0x80000000, __global_pointer$ anchored, 16 KiB stack reserved at top. C6 linker script lands with hardware bring-up. - versions.lock pinned to riscv64-elf-binutils 2.46 + qemu 11.0 + Python 3.12 + RNS 1.2 (per ADR-0008). Includes (src/include/): - psabi.S with the RISC-V ABI register aliases (ADR-0001). - config.S with capacity constants (stack, ring buffers, KISS frame). - regs.S with the qemu-virt NS16550A UART addresses; C6 stub with .error until populated. ADR-0008: bundles four decisions surfaced during bring-up: 1. Rename verify/ → proofs/ to free ./verify for the dispatcher. 2. Adopt vanilla riscv64-elf-binutils (homebrew) over the Espressif crosstool-NG fork; -march=rv32imac -mabi=ilp32. 3. Log timestamps are 8 hex digits emitted by log_hex(width=8). 4. Spec-block prefix is # (RISC-V GAS line comment), not ;;. ADR-0006/0007 status lines flagged as partially superseded; ADR-0004 flagged as refined. ADR index updated. parse_spec accepts both ;; and # prefixes during the transition. open-questions.md tracks OQ-1..OQ-3 as resolved by ADR-0008 and OQ-4 (check_stack.py) deferred. make ci: 51 tests, all green.
94 lines
2.7 KiB
Python
94 lines
2.7 KiB
Python
"""Tests for tests/harness/log_parser.py."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from harness import log_parser as lp
|
|
|
|
|
|
def test_parse_canonical_line() -> None:
|
|
line = "00012345\tkiss\trx_frame\tlen=64\tdest=a1b2c3d4\r\n"
|
|
ev = lp.parse_line(line)
|
|
assert ev is not None
|
|
assert ev.ts_raw == "00012345"
|
|
assert ev.ts_ms == 0x12345
|
|
assert ev.module == "kiss"
|
|
assert ev.event == "rx_frame"
|
|
assert ev.fields == {"len": "64", "dest": "a1b2c3d4"}
|
|
|
|
|
|
def test_no_fields_ok() -> None:
|
|
line = "00000001\tboot\tready\r\n"
|
|
ev = lp.parse_line(line)
|
|
assert ev is not None
|
|
assert ev.module == "boot"
|
|
assert ev.event == "ready"
|
|
assert ev.fields == {}
|
|
|
|
|
|
def test_garbage_line_returns_none() -> None:
|
|
assert lp.parse_line("hello world") is None
|
|
assert lp.parse_line("") is None
|
|
assert lp.parse_line("\r\n") is None
|
|
# malformed: ts not hex
|
|
assert lp.parse_line("xx\tkiss\tevent\r\n") is None
|
|
|
|
|
|
def test_value_with_internal_whitespace_rejected() -> None:
|
|
# Per ADR-0004 values must not contain whitespace.
|
|
line = "00000001\tkiss\tev\tx=a b\r\n"
|
|
assert lp.parse_line(line) is None
|
|
|
|
|
|
def test_unix_line_endings_ok() -> None:
|
|
ev = lp.parse_line("00000001\tboot\tready\n")
|
|
assert ev is not None and ev.event == "ready"
|
|
|
|
|
|
def test_parse_lines_drops_non_conforming() -> None:
|
|
text = (
|
|
"boot ROM stage 0\r\n"
|
|
"00000001\tboot\tready\r\n"
|
|
"garbage\r\n"
|
|
"00000002\tuart\trx\tbyte=ff\r\n"
|
|
)
|
|
events = lp.parse_lines(text.splitlines())
|
|
assert [e.event for e in events] == ["ready", "rx"]
|
|
|
|
|
|
def test_find_event_by_field() -> None:
|
|
events = lp.parse_lines(
|
|
[
|
|
"00000001\tkiss\trx_frame\tdest=aaaa\r\n",
|
|
"00000002\tkiss\trx_frame\tdest=bbbb\r\n",
|
|
]
|
|
)
|
|
found = lp.find_event(events, module="kiss", event="rx_frame", dest="bbbb")
|
|
assert found is not None and found.fields["dest"] == "bbbb"
|
|
|
|
|
|
def test_assert_event_raises_when_missing() -> None:
|
|
events = lp.parse_lines(["00000001\tboot\tready\r\n"])
|
|
with pytest.raises(AssertionError):
|
|
lp.assert_event(events, module="kiss", event="rx_frame")
|
|
|
|
|
|
def test_find_all_returns_every_match() -> None:
|
|
events = lp.parse_lines(
|
|
[
|
|
"00000001\tkiss\trx_frame\tlen=10\r\n",
|
|
"00000002\tkiss\trx_frame\tlen=20\r\n",
|
|
"00000003\tkiss\ttx_frame\tlen=30\r\n",
|
|
]
|
|
)
|
|
matches = lp.find_all(events, module="kiss", event="rx_frame")
|
|
assert [m.fields["len"] for m in matches] == ["10", "20"]
|
|
|
|
|
|
def test_event_indexing() -> None:
|
|
ev = lp.parse_line("00000001\tk\te\ta=1\tb=2\r\n")
|
|
assert ev is not None
|
|
assert ev["a"] == "1"
|
|
assert ev.get("c") is None
|
|
assert ev.get("c", "z") == "z"
|