From 04d965d67a7264a1c7c211494aebda1953df7603 Mon Sep 17 00:00:00 2001 From: "info@mode42.com" Date: Fri, 7 Aug 2026 18:25:13 +0000 Subject: Initial push --- stacks/daemon/test_ax25_codec.py | 97 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 stacks/daemon/test_ax25_codec.py (limited to 'stacks/daemon/test_ax25_codec.py') diff --git a/stacks/daemon/test_ax25_codec.py b/stacks/daemon/test_ax25_codec.py new file mode 100644 index 0000000..b12e43d --- /dev/null +++ b/stacks/daemon/test_ax25_codec.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 +"""Unit tests for ax25_codec (RFC 1171 FCS + libax25 address rules).""" +from __future__ import annotations + +import sys +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +from ax25_codec import ( # noqa: E402 + ax25_build_ui, + ax25_crc, + ax25_crc_valid, + ax25_encode_address, + ax25_parse_ui, + format_callsign, + parse_callsign, + validate_callsign, +) + + +def test_rfc1171_crc_vector() -> None: + body = bytes(range(256)) + crc = ax25_crc(body) + frame = body + bytes((crc & 0xFF, crc >> 8)) + assert ax25_crc_valid(frame) + assert crc == 0x303C + + +def test_address_roundtrip() -> None: + raw = ax25_encode_address("DG1ABC", 7, last=True) + frame = ax25_build_ui("DG1ABC-7", "CQ", b"73") + parsed = ax25_parse_ui(frame) + assert parsed == ("DG1ABC-7", "CQ", b"73") + + +def test_format_callsign_omit_zero_ssid() -> None: + assert format_callsign("DG1ABC", 0) == "DG1ABC" + assert format_callsign("CB", 0) == "CB" + + +def test_parse_callsign_legacy() -> None: + assert parse_callsign("DK0WC-7") == ("DK0WC", 7) + assert parse_callsign("bad-ssid") == ("BAD", 0) + + +def test_validate_callsign_strict() -> None: + assert validate_callsign("CB-0") == ("CB", 0) + assert validate_callsign("N0CALL-15") == ("N0CALL", 15) + try: + validate_callsign("CB_0") + raise AssertionError("expected ValueError") + except ValueError: + pass + + +def test_ui_frame_without_fcs() -> None: + frame = ax25_build_ui("DG1ABC", "QST", b"hi") + body = frame[:-2] + parsed = ax25_parse_ui(body) + assert parsed == ("DG1ABC", "QST", b"hi") + + +def test_invalid_callsign_build() -> None: + try: + ax25_build_ui("BAD_CALL", "CQ", b"x") + raise AssertionError("expected ValueError") + except ValueError: + pass + + +def test_non_ui_rejected() -> None: + frame = ax25_build_ui("A", "B", b"x") + frame = bytearray(frame) + frame[14] = 0x00 # not UI control + assert ax25_parse_ui(bytes(frame)) is None + + +def main() -> int: + tests = [ + test_rfc1171_crc_vector, + test_address_roundtrip, + test_format_callsign_omit_zero_ssid, + test_parse_callsign_legacy, + test_validate_callsign_strict, + test_ui_frame_without_fcs, + test_invalid_callsign_build, + test_non_ui_rejected, + ] + for fn in tests: + fn() + print("OK: ax25_codec unit tests") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) -- cgit v1.3.1