async def test_callback_group_addresses(self): """Test telegram_received_callback after state of switch was changed.""" xknx = XKNX() async_telegram_received_cb_one = AsyncMock() async_telegram_received_cb_two = AsyncMock() callback_one = xknx.telegram_queue.register_telegram_received_cb( async_telegram_received_cb_one, address_filters=[], group_addresses=[GroupAddress("1/2/3")], ) callback_two = xknx.telegram_queue.register_telegram_received_cb( async_telegram_received_cb_two, address_filters=[], group_addresses=[]) telegram = Telegram( destination_address=GroupAddress("1/2/3"), direction=TelegramDirection.INCOMING, payload=GroupValueWrite(DPTBinary(1)), ) await xknx.telegram_queue.process_telegram_incoming(telegram) async_telegram_received_cb_one.assert_called_once_with(telegram) async_telegram_received_cb_two.assert_not_called() async_telegram_received_cb_one.reset_mock() # modify the filters - add/remove a GroupAddress callback_one.group_addresses.remove(GroupAddress("1/2/3")) callback_two.group_addresses.append(GroupAddress("1/2/3")) await xknx.telegram_queue.process_telegram_incoming(telegram) async_telegram_received_cb_one.assert_not_called() async_telegram_received_cb_two.assert_called_once_with(telegram)
async def test_array_sensor_loop(self, value_type, test_payload, test_value): """Test sensor and expose_sensor with different values.""" xknx = XKNX() xknx.knxip_interface = AsyncMock() xknx.rate_limit = False await xknx.telegram_queue.start() expose = ExposeSensor( xknx, "TestExpose", group_address="1/1/1", value_type=value_type, ) assert expose.resolve_state() is None # set a value from expose - HA sends strings for new values stringified_value = str(test_value) await expose.set(stringified_value) outgoing_telegram = Telegram( destination_address=GroupAddress("1/1/1"), direction=TelegramDirection.OUTGOING, payload=GroupValueWrite(test_payload), ) await xknx.telegrams.join() xknx.knxip_interface.send_telegram.assert_called_with(outgoing_telegram) assert expose.resolve_state() == test_value # init sensor after expose is set - with same group address sensor = Sensor( xknx, "TestSensor", group_address_state="1/1/1", value_type=value_type, ) assert sensor.resolve_state() is None # read sensor state (from expose as it has the same GA) # wait_for_result so we don't have to await self.xknx.telegrams.join() await sensor.sync(wait_for_result=True) read_telegram = Telegram( destination_address=GroupAddress("1/1/1"), direction=TelegramDirection.OUTGOING, payload=GroupValueRead(), ) response_telegram = Telegram( destination_address=GroupAddress("1/1/1"), direction=TelegramDirection.OUTGOING, payload=GroupValueResponse(test_payload), ) xknx.knxip_interface.send_telegram.assert_has_calls( [ call(read_telegram), call(response_telegram), ] ) # test if Sensor has successfully read from ExposeSensor assert sensor.resolve_state() == test_value assert expose.resolve_state() == sensor.resolve_state() await xknx.telegram_queue.stop()
def test_representation(self): """Test string representation of address.""" assert repr(GroupAddress("0", GroupAddressType.FREE)) == 'GroupAddress("0")' assert repr(GroupAddress( "0", GroupAddressType.SHORT)) == 'GroupAddress("0/0")' assert repr(GroupAddress( "0", GroupAddressType.LONG)) == 'GroupAddress("0/0/0")'
def test_equal(self): """Test if the equal operator works in all cases.""" assert IndividualAddress("1.0.0") == IndividualAddress(4096) assert IndividualAddress("1.0.0") != IndividualAddress("1.1.1") assert IndividualAddress("1.0.0") is not None assert IndividualAddress("1.0.0") != "example" assert IndividualAddress("1.1.1") != GroupAddress("1/1/1") assert IndividualAddress(250) != GroupAddress(250) assert IndividualAddress(250) != 250
def test_middle(self): """ Test if `GroupAddress.middle` works. Checks: * Middle group part of a strings returns the right value * Return `None` if not `GroupAddressType.LONG` """ assert GroupAddress("1/0/1", GroupAddressType.LONG).middle == 0 assert GroupAddress("1/7/1", GroupAddressType.LONG).middle == 7 assert GroupAddress("1/0", GroupAddressType.SHORT).middle is None assert GroupAddress("1/0", GroupAddressType.FREE).middle is None
def test_main(self): """ Test if `GroupAddress.main` works. Checks: * Main group part of a strings returns the right value * Return `None` on `GroupAddressType.FREE` """ assert GroupAddress("1/0").main == 1 assert GroupAddress("15/0").main == 15 assert GroupAddress("31/0/0").main == 31 assert GroupAddress("1/0", GroupAddressType.FREE).main is None
def test_internal_address_filter_wildcard(self): """Test AddressFilter with wildcards for InternalGroupAddress.""" af1 = AddressFilter("i-?") assert af1.match("i1") assert af1.match("i 2") assert af1.match("i-3") assert af1.match(InternalGroupAddress("i-4")) assert not af1.match("1") assert not af1.match(GroupAddress(1)) assert not af1.match("i11") assert not af1.match("i 11") assert not af1.match("i-11") assert not af1.match(InternalGroupAddress("i-11")) af2 = AddressFilter("i-t?st") assert af2.match("it1st") assert af2.match("i t2st") assert af2.match("i-test") assert af2.match(InternalGroupAddress("i-test")) assert not af2.match("1") assert not af2.match(GroupAddress(1)) assert not af2.match("i11") assert not af2.match("i tst") assert not af2.match("i-teest") assert not af2.match(InternalGroupAddress("i-tst")) af3 = AddressFilter("i-*") assert af3.match("i1") assert af3.match("i asdf") assert af3.match("i-3sdf") assert af3.match(InternalGroupAddress("i-4")) assert not af3.match("1") assert not af3.match(GroupAddress(1)) assert af3.match("i11") assert af3.match("i 11??") assert af3.match("i-11*") assert af3.match(InternalGroupAddress("i-11")) af4 = AddressFilter("i-t*t") assert af4.match("it1t") assert af4.match("i t22t") assert af4.match("i-testt") assert af4.match("i-tt") assert af4.match(InternalGroupAddress("i-t333t")) assert not af4.match("1") assert not af4.match(GroupAddress(1)) assert not af4.match("i testx") assert not af4.match("i-11test") assert not af4.match(InternalGroupAddress("i-11"))
def test_middle(self): """ Test if `GroupAddress.middle` works. Checks: * Middle group part of a strings returns the right value * Return `None` if not `GroupAddressType.LONG` """ GroupAddress.address_format = GroupAddressType.LONG assert GroupAddress("1/0/1").middle == 0 assert GroupAddress("1/7/1").middle == 7 GroupAddress.address_format = GroupAddressType.SHORT assert GroupAddress("1/0").middle is None GroupAddress.address_format = GroupAddressType.FREE assert GroupAddress("1/0").middle is None
def test_equal(self): """Test if the equal operator works in all cases.""" assert GroupAddress("1/0") == GroupAddress(2048) assert GroupAddress("1/1") != GroupAddress("1/1/0") assert GroupAddress("1/0") is not None assert GroupAddress("1/0") != "example" assert GroupAddress(1) != IndividualAddress(1) assert GroupAddress(1) != 1
async def test_process_to_device(self, devices_by_ga_mock): """Test process_telegram_incoming for forwarding telegram to a device.""" xknx = XKNX() test_device = AsyncMock() devices_by_ga_mock.return_value = [test_device] telegram = Telegram( destination_address=GroupAddress("1/2/3"), direction=TelegramDirection.INCOMING, payload=GroupValueWrite(DPTBinary(1)), ) await xknx.telegram_queue.process_telegram_incoming(telegram) devices_by_ga_mock.assert_called_once_with(GroupAddress("1/2/3")) test_device.process.assert_called_once_with(telegram)
async def test_process_exception(self, process_tg_in_mock, logging_error_mock): """Test process_telegram exception handling.""" xknx = XKNX() async def process_exception(): raise CouldNotParseTelegram( "Something went wrong when receiving the telegram.") process_tg_in_mock.return_value = asyncio.ensure_future( process_exception()) telegram = Telegram( destination_address=GroupAddress("1/2/3"), direction=TelegramDirection.INCOMING, payload=GroupValueWrite(DPTBinary(1)), ) xknx.telegrams.put_nowait(telegram) await xknx.telegram_queue._process_all_telegrams() logging_error_mock.assert_called_once_with( "Error while processing telegram %s", CouldNotParseTelegram( "Something went wrong when receiving the telegram."), )
async def test_process_raising(self, process_mock, logging_exception_mock): """Test unexpected exception handling in telegram queues.""" xknx = XKNX() telegram_in = Telegram( destination_address=GroupAddress("1/2/3"), direction=TelegramDirection.INCOMING, payload=GroupValueWrite(DPTBinary(1)), ) # InternalGroupAddress to avoid CommunicationError for missing knxip_interface telegram_out = Telegram( destination_address=InternalGroupAddress("i-outgoing"), direction=TelegramDirection.OUTGOING, payload=GroupValueWrite(DPTBinary(0)), ) xknx.telegrams.put_nowait(telegram_in) xknx.telegrams.put_nowait(telegram_out) await xknx.telegram_queue.start() await xknx.telegram_queue.stop() log_calls = [ call( "Unexpected error while processing incoming telegram %s", telegram_in, ), call( "Unexpected error while processing outgoing telegram %s", telegram_out, ), ] logging_exception_mock.assert_has_calls(log_calls)
async def test_callback_raising(self, logging_exception_mock): """Test telegram_received_callback raising an exception.""" xknx = XKNX() good_callback_1 = AsyncMock() bad_callback = AsyncMock(side_effect=Exception("Boom")) good_callback_2 = AsyncMock() xknx.telegram_queue.register_telegram_received_cb(good_callback_1) xknx.telegram_queue.register_telegram_received_cb(bad_callback) xknx.telegram_queue.register_telegram_received_cb(good_callback_2) telegram = Telegram( destination_address=GroupAddress("1/2/3"), direction=TelegramDirection.INCOMING, payload=GroupValueWrite(DPTBinary(1)), ) await xknx.telegram_queue.process_telegram_incoming(telegram) good_callback_1.assert_called_once_with(telegram) bad_callback.assert_called_once_with(telegram) good_callback_2.assert_called_once_with(telegram) logging_exception_mock.assert_called_once_with( "Unexpected error while processing telegram_received_cb for %s", telegram, )
async def test_start(self): """Test start, run and stop.""" xknx = XKNX() telegram_in = Telegram( destination_address=GroupAddress("1/2/3"), direction=TelegramDirection.INCOMING, payload=GroupValueWrite(DPTBinary(1)), ) await xknx.telegram_queue.start() assert not xknx.telegram_queue._consumer_task.done() # queue shall now consume telegrams from xknx.telegrams assert xknx.telegrams.qsize() == 0 xknx.telegrams.put_nowait(telegram_in) xknx.telegrams.put_nowait(telegram_in) assert xknx.telegrams.qsize() == 2 # wait until telegrams are consumed await xknx.telegrams.join() assert xknx.telegrams.qsize() == 0 await xknx.telegrams.join() assert xknx.telegrams.qsize() == 0 # stop run() task with stop() await xknx.telegram_queue.stop() assert xknx.telegram_queue._consumer_task.done()
def test_internal_address_filter_exact(self): """Test AddressFilter for InternalGroupAddress.""" af1 = AddressFilter("i-1") assert af1.match("i1") assert af1.match("i 1") assert af1.match("i-1") assert af1.match(InternalGroupAddress("i-1")) assert not af1.match("1") assert not af1.match(GroupAddress(1))
async def _receive_telegram(self, group_address: str, payload: APCI) -> None: """Inject incoming KNX telegram.""" self.xknx.telegrams.put_nowait( Telegram( destination_address=GroupAddress(group_address), direction=TelegramDirection.INCOMING, payload=payload, source_address=IndividualAddress(self.INDIVIDUAL_ADDRESS), ) ) await self.hass.async_block_till_done()
def test_equal(self): """Test if the equal operator works in all cases.""" assert InternalGroupAddress("i 123") == InternalGroupAddress("i 123") assert InternalGroupAddress("i-asdf") == InternalGroupAddress("i asdf") assert InternalGroupAddress("i-asdf") == InternalGroupAddress("Iasdf") assert InternalGroupAddress("i-1") != InternalGroupAddress("i-2") assert InternalGroupAddress("i-1") is not None assert InternalGroupAddress("i-example") != "example" assert InternalGroupAddress("i-0") != GroupAddress(0) assert InternalGroupAddress("i-1") != IndividualAddress(1) assert InternalGroupAddress("i-1") != 1
def test_with_valid(self, address_test, address_raw): """ Test if the class constructor generates valid raw values. This test checks: * all allowed input variants (strings, tuples, integers) * for conversation errors * for upper/lower limits still working, to avoid off-by-one errors """ assert GroupAddress(address_test).raw == address_raw
def test_sub(self): """ Test if `GroupAddress.sub` works. Checks: * Sub group part of a strings returns the right value * Return never `None` """ assert GroupAddress("1/0", GroupAddressType.SHORT).sub == 0 assert GroupAddress("31/0", GroupAddressType.SHORT).sub == 0 assert GroupAddress("1/2047", GroupAddressType.SHORT).sub == 2047 assert GroupAddress("31/2047", GroupAddressType.SHORT).sub == 2047 assert GroupAddress("1/0/0", GroupAddressType.LONG).sub == 0 assert GroupAddress("1/0/255", GroupAddressType.LONG).sub == 255 assert GroupAddress("0/0", GroupAddressType.FREE).sub == 0 assert GroupAddress("1/0", GroupAddressType.FREE).sub == 2048 assert GroupAddress("31/2047", GroupAddressType.FREE).sub == 65535
def test_with_invalid(self, address_test): """ Test if constructor raises an exception for all known invalid cases. Checks: * addresses or parts of it too high/low * invalid input variants (lists instead of tuples) * invalid strings """ with pytest.raises(CouldNotParseAddress): GroupAddress(address_test)
def test_representation(self, initializer, string_free, string_short, string_long): """Test string representation of address.""" group_address = GroupAddress(initializer) GroupAddress.address_format = GroupAddressType.FREE assert str(group_address) == string_free assert repr(group_address) == f'GroupAddress("{string_free}")' GroupAddress.address_format = GroupAddressType.SHORT assert str(group_address) == string_short assert repr(group_address) == f'GroupAddress("{string_short}")' GroupAddress.address_format = GroupAddressType.LONG assert str(group_address) == string_long assert repr(group_address) == f'GroupAddress("{string_long}")'
async def test_register(self): """Test telegram_received_callback after state of switch was changed.""" xknx = XKNX() async_telegram_received_cb = AsyncMock() xknx.telegram_queue.register_telegram_received_cb(async_telegram_received_cb) telegram = Telegram( destination_address=GroupAddress("1/2/3"), direction=TelegramDirection.INCOMING, payload=GroupValueWrite(DPTBinary(1)), ) await xknx.telegram_queue.process_telegram_incoming(telegram) async_telegram_received_cb.assert_called_once_with(telegram)
async def test_process_all_telegrams(self, process_telegram_incoming_mock, process_telegram_outgoing_mock): """Test _process_all_telegrams for clearing the queue.""" xknx = XKNX() telegram_in = Telegram( destination_address=GroupAddress("1/2/3"), direction=TelegramDirection.INCOMING, payload=GroupValueWrite(DPTBinary(1)), ) telegram_out = Telegram( destination_address=GroupAddress("1/2/3"), direction=TelegramDirection.OUTGOING, payload=GroupValueWrite(DPTBinary(1)), ) xknx.telegrams.put_nowait(telegram_in) xknx.telegrams.put_nowait(telegram_out) res = await xknx.telegram_queue._process_all_telegrams() assert res is None process_telegram_incoming_mock.assert_called_once() process_telegram_outgoing_mock.assert_called_once()
async def test_register_with_outgoing_telegrams_does_not_trigger(self): """Test telegram_received_callback with outgoing telegrams.""" xknx = XKNX() xknx.knxip_interface = AsyncMock() async_telegram_received_cb = AsyncMock() xknx.telegram_queue.register_telegram_received_cb(async_telegram_received_cb) telegram = Telegram( destination_address=GroupAddress("1/2/3"), direction=TelegramDirection.OUTGOING, payload=GroupValueWrite(DPTBinary(1)), ) await xknx.telegram_queue.process_telegram_outgoing(telegram) async_telegram_received_cb.assert_not_called()
async def test_callback_no_filters(self): """Test telegram_received_callback after state of switch was changed.""" xknx = XKNX() async_telegram_received_callback = AsyncMock() xknx.telegram_queue.register_telegram_received_cb( async_telegram_received_callback) telegram = Telegram( destination_address=GroupAddress("1/2/3"), direction=TelegramDirection.INCOMING, payload=GroupValueWrite(DPTBinary(1)), ) xknx.telegrams.put_nowait(telegram) await xknx.telegram_queue._process_all_telegrams() async_telegram_received_callback.assert_called_with(telegram)
async def test_process_to_callback(self, devices_process): """Test process_telegram_incoming with callback.""" xknx = XKNX() async_telegram_received_callback = AsyncMock() xknx.telegram_queue.register_telegram_received_cb( async_telegram_received_callback) telegram = Telegram( destination_address=GroupAddress("1/2/3"), direction=TelegramDirection.INCOMING, payload=GroupValueWrite(DPTBinary(1)), ) await xknx.telegram_queue.process_telegram_incoming(telegram) async_telegram_received_callback.assert_called_once_with(telegram) devices_process.assert_called_once_with(telegram)
async def test_outgoing(self): """Test outgoing telegrams in telegram queue.""" xknx = XKNX() telegram = Telegram( destination_address=GroupAddress("1/2/3"), direction=TelegramDirection.OUTGOING, payload=GroupValueWrite(DPTBinary(1)), ) # log a warning if there is no KNXIP interface instanciated with pytest.raises(CommunicationError): await xknx.telegram_queue.process_telegram_outgoing(telegram) # if we have an interface send the telegram (doesn't raise) xknx.knxip_interface.send_telegram = AsyncMock() await xknx.telegram_queue.process_telegram_outgoing(telegram) xknx.knxip_interface.send_telegram.assert_called_once_with(telegram)
"0/0/11": 11, "0/0/111": 111, "0/0/255": 255, "0/1/11": 267, "0/1/111": 367, "0/7/255": 2047, "1/0": 2048, "1/0/0": 2048, "1/1/111": 2415, "1/7/255": 4095, "31/7/255": 65535, "1": 1, 0: 0, 65535: 65535, (0xFF, 0xFF): 65535, GroupAddress("1/1/111"): 2415, None: 0, } group_addresses_invalid = [ "0/2049", "0/8/0", "0/0/256", "32/0", "0/0a", "a0/0", "abc", "1.1.1", "0.0", IndividualAddress("11.11.111"), 65536,
def test_to_knx(self): """Test if `GroupAddress.to_knx()` generates valid byte tuples.""" assert GroupAddress("0/0").to_knx() == (0x0, 0x0) assert GroupAddress("31/2047").to_knx() == (0xFF, 0xFF)