"""命令层测试 — 用 FakeTransport 验证高层操作的帧序列与顺序(证据 docs/protocol.md)。""" import datetime import pytest from ppclock import commands as C class FakeTransport: def __init__(self, id_response="DEVICE12345678"): self.epd_writes = [] self.rxtx_writes = [] self.id_response = id_response self.delays = [] async def write_epd(self, data, response=True): self.epd_writes.append(data) async def write_rxtx(self, data, response=True): self.rxtx_writes.append(data) async def request_device_id(self, timeout=8.0): self.rxtx_writes.append(bytes([0xEF, 0xEF])) return self.id_response async def delay(self, seconds): self.delays.append(seconds) @pytest.fixture def t(): return FakeTransport() class TestTimeAndMode: @pytest.mark.asyncio async def test_set_time_sends_dd_then_commit(self, t): dt = datetime.datetime(2026, 7, 24, 15, 30, 0) await C.set_time(t, dt) assert t.rxtx_writes[0][0] == 0xDD assert t.rxtx_writes[-1] == bytes([0xE2]) @pytest.mark.asyncio async def test_set_mode_appends_commit(self, t): await C.set_mode(t, "clock1") assert t.rxtx_writes == [bytes.fromhex("e10201"), bytes([0xE2])] @pytest.mark.asyncio async def test_clear_uses_epd_channel(self, t): await C.clear(t) assert t.epd_writes == [bytes([0x00, 0x00]), bytes([0x01])] assert t.rxtx_writes == [] class TestUploadImage: @pytest.mark.asyncio async def test_full_image_sequence(self, t): """整图:bw块×63 → 红块×63 → 延时 → 01""" bw = bytes(15000) red = bytes([0xFF]) * 15000 await C.upload_image(t, bw, red, small=False) assert t.epd_writes[0][:4] == bytes([0x03, 0xFF, 0x00, 0x00]) assert t.epd_writes[63][:2] == bytes([0x03, 0x00]) # 红平面首块 assert t.epd_writes[-1] == bytes([0x01]) assert len(t.epd_writes) == 63 + 63 + 1 @pytest.mark.asyncio async def test_mono_image_no_red_blocks(self, t): await C.upload_image(t, bytes(15000), None, small=False) assert len(t.epd_writes) == 63 + 1 @pytest.mark.asyncio async def test_slot_select_before_upload(self, t): await C.upload_image(t, bytes(15000), None, small=False, slot=2) assert t.rxtx_writes[0] == bytes([0xEA, 0x02]) @pytest.mark.asyncio async def test_small_image_ends_with_AA(self, t): await C.upload_image(t, bytes(3500), None, small=True) assert t.epd_writes[-1] == bytes([0xAA]) assert t.epd_writes[0][0] == 0x04 class TestOtherCommands: @pytest.mark.asyncio async def test_countdown_set(self, t): d = datetime.date(2026, 12, 31) await C.countdown_set(t, d, "clock") assert t.rxtx_writes[0] == bytes([0xFF, 0x00, 0x20, 0x26, 0x12, 0x31]) assert t.rxtx_writes[-1] == bytes([0xE2]) @pytest.mark.asyncio async def test_countdown_off(self, t): await C.countdown_off(t) assert t.rxtx_writes == [bytes([0xFA]), bytes([0xE2])] @pytest.mark.asyncio async def test_parking(self, t): await C.parking(t, "13800138000") assert t.rxtx_writes[0] == bytes([0xEF]) + bytes( int(f"{d}{d}", 16) for d in "13800138000" ) @pytest.mark.asyncio async def test_sleep_on_off(self, t): await C.sleep(t, True, 23, 7) assert t.rxtx_writes[0] == bytes([0xFB, 0x01, 0x23, 0x07]) await C.sleep(t, False, 23, 7) assert t.rxtx_writes[1] == bytes([0xFB, 0x00, 0x23, 0x07]) @pytest.mark.asyncio async def test_rotation(self, t): await C.rotation(t, count=4) assert t.rxtx_writes[0] == bytes.fromhex("EA0003") await C.rotation(t, interval=5, unit="min") assert t.rxtx_writes[1] == bytes.fromhex("e9010501") @pytest.mark.asyncio async def test_activate(self, t): await C.activate(t, "A1B2" * 8) assert t.rxtx_writes[0] == bytes([0xEF]) + bytes.fromhex("A1B2" * 8) assert t.rxtx_writes[-1] == bytes([0xE2]) @pytest.mark.asyncio async def test_wifi(self, t): await C.wifi(t, "MyAP", "pw", "101010100") assert t.rxtx_writes[0] == b"WIFI_SSID:MyAP;WIFI_PASS:pw;CITY:101010100" @pytest.mark.asyncio async def test_lut(self, t): await C.lut(t, 0x05) assert t.rxtx_writes[0] == bytes([0xE6, 0x05]) @pytest.mark.asyncio async def test_get_device_id(self, t): dev_id = await C.get_device_id(t) assert t.rxtx_writes[0] == bytes([0xEF, 0xEF]) assert dev_id == "DEVICE12345678"