summaryrefslogtreecommitdiff
path: root/stacks/daemon/test_ax25_codec.py
blob: b12e43d69683d0db8b3cf8115a4d7fe104d4d29f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
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