|
4 | 4 |
|
5 | 5 | """ Unit tests for zeroconf._handlers """ |
6 | 6 |
|
| 7 | +import asyncio |
7 | 8 | import logging |
8 | 9 | import pytest |
9 | 10 | import socket |
@@ -834,3 +835,83 @@ async def test_qu_response_only_sends_additionals_if_sends_answer(): |
834 | 835 | # unregister |
835 | 836 | zc.registry.remove(info) |
836 | 837 | await aiozc.async_close() |
| 838 | + |
| 839 | + |
| 840 | +# This test uses asyncio because it needs to access the cache directly |
| 841 | +# which is not threadsafe |
| 842 | +@pytest.mark.asyncio |
| 843 | +async def test_cache_flush_bit(): |
| 844 | + """Test that the cache flush bit sets the TTL to one for matching records.""" |
| 845 | + # instantiate a zeroconf instance |
| 846 | + aiozc = AsyncZeroconf(interfaces=['127.0.0.1']) |
| 847 | + zc = aiozc.zeroconf |
| 848 | + |
| 849 | + type_ = "_cacheflush._tcp.local." |
| 850 | + name = "knownname" |
| 851 | + registration_name = "%s.%s" % (name, type_) |
| 852 | + desc = {'path': '/~paulsm/'} |
| 853 | + server_name = "server-uu1.local." |
| 854 | + info = ServiceInfo( |
| 855 | + type_, registration_name, 80, 0, 0, desc, server_name, addresses=[socket.inet_aton("10.0.1.2")] |
| 856 | + ) |
| 857 | + a_record = info.dns_addresses()[0] |
| 858 | + zc.cache.async_add_records([info.dns_pointer(), a_record, info.dns_text(), info.dns_service()]) |
| 859 | + |
| 860 | + info.addresses = [socket.inet_aton("10.0.1.5"), socket.inet_aton("10.0.1.6")] |
| 861 | + new_records = info.dns_addresses() |
| 862 | + for new_record in new_records: |
| 863 | + assert new_record.unique is True |
| 864 | + |
| 865 | + original_a_record = zc.cache.get(a_record) |
| 866 | + # Do the run within 1s to verify the original record is not going to be expired |
| 867 | + out = r.DNSOutgoing(const._FLAGS_QR_RESPONSE | const._FLAGS_AA, multicast=True) |
| 868 | + for answer in new_records: |
| 869 | + out.add_answer_at_time(answer, 0) |
| 870 | + for packet in out.packets(): |
| 871 | + zc.record_manager.async_updates_from_response(r.DNSIncoming(packet)) |
| 872 | + assert zc.cache.get(a_record) is original_a_record |
| 873 | + assert original_a_record.ttl != 1 |
| 874 | + for record in new_records: |
| 875 | + assert zc.cache.get(record) is not None |
| 876 | + |
| 877 | + original_a_record.created = current_time_millis() - 1001 |
| 878 | + |
| 879 | + # Do the run within 1s to verify the original record is not going to be expired |
| 880 | + out = r.DNSOutgoing(const._FLAGS_QR_RESPONSE | const._FLAGS_AA, multicast=True) |
| 881 | + for answer in new_records: |
| 882 | + out.add_answer_at_time(answer, 0) |
| 883 | + for packet in out.packets(): |
| 884 | + zc.record_manager.async_updates_from_response(r.DNSIncoming(packet)) |
| 885 | + assert original_a_record.ttl == 1 |
| 886 | + for record in new_records: |
| 887 | + assert zc.cache.get(record) is not None |
| 888 | + |
| 889 | + cached_records = [zc.cache.get(record) for record in new_records] |
| 890 | + for record in cached_records: |
| 891 | + record.created = current_time_millis() - 1001 |
| 892 | + |
| 893 | + fresh_address = socket.inet_aton("4.4.4.4") |
| 894 | + info.addresses = [fresh_address] |
| 895 | + # Do the run within 1s to verify the two new records get marked as expired |
| 896 | + out = r.DNSOutgoing(const._FLAGS_QR_RESPONSE | const._FLAGS_AA, multicast=True) |
| 897 | + for answer in info.dns_addresses(): |
| 898 | + out.add_answer_at_time(answer, 0) |
| 899 | + for packet in out.packets(): |
| 900 | + zc.record_manager.async_updates_from_response(r.DNSIncoming(packet)) |
| 901 | + for record in cached_records: |
| 902 | + assert record.ttl == 1 |
| 903 | + |
| 904 | + for entry in zc.cache.get_all_by_details(server_name, const._TYPE_A, const._CLASS_IN): |
| 905 | + if entry.address == fresh_address: |
| 906 | + assert entry.ttl > 1 |
| 907 | + else: |
| 908 | + assert entry.ttl == 1 |
| 909 | + |
| 910 | + # Wait for the ttl 1 records to expire |
| 911 | + await asyncio.sleep(1.01) |
| 912 | + |
| 913 | + loaded_info = r.ServiceInfo(type_, registration_name) |
| 914 | + loaded_info.load_from_cache(zc) |
| 915 | + assert loaded_info.addresses == info.addresses |
| 916 | + |
| 917 | + await aiozc.async_close() |
0 commit comments