chore: baseline existing CI-V suite before RepeaterBook integration
This commit is contained in:
+77
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Unit tests for the pure CI-V codec (civ.py). Stdlib only: `python3 -m unittest`."""
|
||||
import unittest
|
||||
|
||||
import civ
|
||||
|
||||
|
||||
class TestFreqCodec(unittest.TestCase):
|
||||
# Real capture from the radio: VFO was 147.300000 MHz.
|
||||
REAL_BCD = bytes.fromhex("0000304701")
|
||||
REAL_HZ = 147_300_000
|
||||
|
||||
def test_bcd_to_freq_decodes_real_capture(self):
|
||||
self.assertEqual(civ.bcd_to_freq(self.REAL_BCD), self.REAL_HZ)
|
||||
|
||||
def test_freq_to_bcd_matches_real_capture(self):
|
||||
self.assertEqual(civ.freq_to_bcd(self.REAL_HZ), self.REAL_BCD)
|
||||
|
||||
def test_freq_roundtrip(self):
|
||||
for hz in (146_520_000, 446_000_000, 147_300_000, 1_240_000_000):
|
||||
self.assertEqual(civ.bcd_to_freq(civ.freq_to_bcd(hz)), hz)
|
||||
|
||||
|
||||
class TestLevelCodec(unittest.TestCase):
|
||||
# ICOM levels are 0..255 sent as 2-byte BCD "0000".."0255".
|
||||
def test_level_to_bcd_known(self):
|
||||
self.assertEqual(civ.level_to_bcd(0), b"\x00\x00")
|
||||
self.assertEqual(civ.level_to_bcd(128), b"\x01\x28")
|
||||
self.assertEqual(civ.level_to_bcd(255), b"\x02\x55")
|
||||
|
||||
def test_bcd_to_level_known(self):
|
||||
self.assertEqual(civ.bcd_to_level(b"\x00\x00"), 0)
|
||||
self.assertEqual(civ.bcd_to_level(b"\x01\x28"), 128)
|
||||
self.assertEqual(civ.bcd_to_level(b"\x02\x55"), 255)
|
||||
|
||||
def test_level_rejects_out_of_range(self):
|
||||
with self.assertRaises(ValueError):
|
||||
civ.level_to_bcd(256)
|
||||
with self.assertRaises(ValueError):
|
||||
civ.level_to_bcd(-1)
|
||||
|
||||
|
||||
class TestBuildFrame(unittest.TestCase):
|
||||
def test_read_freq_frame(self):
|
||||
# FE FE <radio=8C> <ctrl=E0> 03 FD
|
||||
self.assertEqual(civ.build_frame(0x03), bytes.fromhex("fefe8ce003fd"))
|
||||
|
||||
def test_frame_with_data(self):
|
||||
# set freq 147.3 MHz: FE FE 8C E0 05 <5 BCD> FD
|
||||
frame = civ.build_frame(0x05, civ.freq_to_bcd(147_300_000))
|
||||
self.assertEqual(frame, bytes.fromhex("fefe8ce005" + "0000304701" + "fd"))
|
||||
|
||||
|
||||
class TestParseFrames(unittest.TestCase):
|
||||
# Exact bytes the radio returned during the probe (echo + reply).
|
||||
REAL_RX = bytes.fromhex("fefe8ce003fd" + "fefee08c030000304701fd")
|
||||
|
||||
def test_splits_echo_and_reply(self):
|
||||
frames = civ.parse_frames(self.REAL_RX)
|
||||
self.assertEqual(len(frames), 2)
|
||||
echo, reply = frames
|
||||
self.assertEqual((echo.to, echo.frm, echo.cn), (0x8C, 0xE0, 0x03))
|
||||
self.assertEqual(echo.data, b"")
|
||||
self.assertEqual((reply.to, reply.frm, reply.cn), (0xE0, 0x8C, 0x03))
|
||||
self.assertEqual(reply.data, bytes.fromhex("0000304701"))
|
||||
|
||||
def test_ignores_leading_noise_and_partial_tail(self):
|
||||
noisy = b"\x00\xff" + self.REAL_RX + b"\xfe\xfe\x99" # partial frame at end
|
||||
frames = civ.parse_frames(noisy)
|
||||
self.assertEqual(len(frames), 2)
|
||||
|
||||
def test_empty_buffer(self):
|
||||
self.assertEqual(civ.parse_frames(b""), [])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user