summaryrefslogtreecommitdiff
path: root/stacks/daemon/test_ax25_codec.py
diff options
context:
space:
mode:
authorinfo@mode42.com <info@mode42.com>2026-08-07 18:23:28 +0000
committerinfo@mode42.com <info@mode42.com>2026-08-07 18:23:28 +0000
commitfa05a5f8238e9e1417235711656e5062ccc843a7 (patch)
tree46fbbd18de54492b59307c16efcc7f3152723238 /stacks/daemon/test_ax25_codec.py
Initial push
Diffstat (limited to 'stacks/daemon/test_ax25_codec.py')
-rw-r--r--stacks/daemon/test_ax25_codec.py97
1 files changed, 97 insertions, 0 deletions
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())
git clone -b <branch> https://cgit.mode42.com/<repo>.git
git clone -b <branch> git://cgit.mode42.com/<repo>.git

info@mode42.com