File: test_packet_callbacks.py

package info (click to toggle)
zigpy 0.90.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,208 kB
  • sloc: python: 37,692; sql: 2,109; makefile: 7
file content (116 lines) | stat: -rw-r--r-- 3,873 bytes parent folder | download
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import pytest

import zigpy.types as t

from .async_mock import MagicMock, call
from .conftest import make_ieee


@pytest.fixture
def base_packet():
    """Base ZigbeePacket to clone with .replace()."""
    return t.ZigbeePacket(
        src=t.AddrModeAddress(addr_mode=t.AddrMode.NWK, address=0x1234),
        src_ep=1,
        dst=t.AddrModeAddress(addr_mode=t.AddrMode.NWK, address=0x0000),
        dst_ep=1,
        tsn=123,
        profile_id=0x0104,
        cluster_id=0x0006,
        data=t.SerializableBytes(b"test"),
        lqi=255,
        rssi=-30,
    )


@pytest.mark.parametrize(
    "filter_address",
    [
        None,
        t.AddrModeAddress(addr_mode=t.AddrMode.NWK, address=0x1234),
        t.AddrModeAddress(addr_mode=t.AddrMode.IEEE, address=make_ieee()),
    ],
)
async def test_packet_callback_register_cancel(app, filter_address):
    """Register and cancel packet callback."""
    cb = MagicMock()
    cancel = app.register_packet_callback(filter_address, cb)
    assert cb in app._packet_callbacks[filter_address]
    cancel()
    assert cb not in app._packet_callbacks[filter_address]
    cancel()


@pytest.mark.parametrize(
    ("src_address", "should_trigger"),
    [
        (t.AddrModeAddress(addr_mode=t.AddrMode.NWK, address=0x1234), True),
        (t.AddrModeAddress(addr_mode=t.AddrMode.NWK, address=0x5678), False),
    ],
)
async def test_packet_callback_address_filter(
    app, base_packet, src_address, should_trigger
):
    """Source address match."""
    filt = t.AddrModeAddress(addr_mode=t.AddrMode.NWK, address=0x1234)
    cb = MagicMock()
    app.register_packet_callback(filt, cb)
    pkt = base_packet.replace(src=src_address)
    app.notify_packet_callbacks(pkt)
    if should_trigger:
        assert cb.mock_calls == [call(pkt)]
    else:
        assert cb.mock_calls == []


async def test_packet_callback_addr_mode_mismatch(app, base_packet):
    """Address mode mismatch."""
    ieee = make_ieee()
    filt = t.AddrModeAddress(addr_mode=t.AddrMode.IEEE, address=ieee)
    cb = MagicMock()
    app.register_packet_callback(filt, cb)
    app.notify_packet_callbacks(base_packet)
    assert cb.mock_calls == []


async def test_packet_callback_multiple_same_filter(app, base_packet):
    """Multiple callbacks, cancel one."""
    addr = t.AddrModeAddress(addr_mode=t.AddrMode.NWK, address=0x9ABC)
    cb1 = MagicMock()
    cb2 = MagicMock()
    cancel1 = app.register_packet_callback(addr, cb1)
    app.register_packet_callback(addr, cb2)
    pkt1 = base_packet.replace(src=addr, tsn=200)
    app.notify_packet_callbacks(pkt1)
    assert cb1.mock_calls == [call(pkt1)]
    assert cb2.mock_calls == [call(pkt1)]
    cancel1()
    pkt2 = base_packet.replace(src=addr, tsn=201)
    app.notify_packet_callbacks(pkt2)
    assert cb1.mock_calls == [call(pkt1)]
    assert cb2.mock_calls == [call(pkt1), call(pkt2)]


async def test_packet_callback_exception_global(app, base_packet, caplog):
    """Exception isolation for global callbacks."""
    failing = MagicMock(side_effect=ValueError("boom"))
    ok = MagicMock()
    app.register_packet_callback(None, failing)
    app.register_packet_callback(None, ok)
    app.notify_packet_callbacks(base_packet)
    assert ok.mock_calls == [call(base_packet)]
    assert any("global packet callback" in r.message.lower() for r in caplog.records)


async def test_packet_callback_exception_address(app, base_packet, caplog):
    """Exception isolation for address-specific callbacks."""
    addr = base_packet.src
    failing = MagicMock(side_effect=ValueError("boom"))
    ok = MagicMock()
    app.register_packet_callback(addr, failing)
    app.register_packet_callback(addr, ok)
    app.notify_packet_callbacks(base_packet)
    assert ok.mock_calls == [call(base_packet)]
    assert any(
        "packet callback for address" in r.message.lower() for r in caplog.records
    )