FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

chore: adopt the zc fixture across the remaining simple tests by bdraco · Pull Request #1876 · python-zeroconf/python-zeroconf · GitHub

Repository navigation

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .py  (5) All 1 file type selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
7 changes: 5 additions & 2 deletions tests/conftest.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,15 @@ def verify_threads_ended():


@pytest.fixture
def zc() -> Generator[Zeroconf]:
def zc(verify_threads_ended: None) -> Generator[Zeroconf]:
"""Yield a loopback `Zeroconf` and close it on teardown.

Replaces the inline `zc = Zeroconf(interfaces=["127.0.0.1"])` +
explicit `zc.close()` pattern duplicated across the suite. Calling
`zc.close()` inside a test is still safe — `close()` is idempotent.

Depends on `verify_threads_ended` so the instance is torn down before
the thread check runs.
"""
zc = Zeroconf(interfaces=["127.0.0.1"])
try:
Expand All @@ -120,7 +123,7 @@ def zc() -> Generator[Zeroconf]:


@pytest_asyncio.fixture
async def aiozc() -> AsyncGenerator[AsyncZeroconf]:
async def aiozc(verify_threads_ended: None) -> AsyncGenerator[AsyncZeroconf]:
"""Yield a loopback `AsyncZeroconf` and close it on teardown.

Replaces the inline `aiozc = AsyncZeroconf(interfaces=["127.0.0.1"])`
Expand Down
44 changes: 9 additions & 35 deletions tests/services/test_browser.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,10 @@ def teardown_module():
log.setLevel(original_logging_level)


def test_service_browser_cancel_multiple_times():
def test_service_browser_cancel_multiple_times(zc: Zeroconf) -> None:
"""Test we can cancel a ServiceBrowser multiple times before close."""

# instantiate a zeroconf instance
zc = Zeroconf(interfaces=["127.0.0.1"])
# start a browser
type_ = "_hap._tcp.local."

Expand All @@ -73,14 +72,11 @@ class MyServiceListener(r.ServiceListener):
browser.cancel()
browser.cancel()

zc.close()


def test_service_browser_cancel_context_manager():
def test_service_browser_cancel_context_manager(zc: Zeroconf) -> None:
"""Test we can cancel a ServiceBrowser with it being used as a context manager."""

# instantiate a zeroconf instance
zc = Zeroconf(interfaces=["127.0.0.1"])
# start a browser
type_ = "_hap._tcp.local."

Expand All @@ -102,8 +98,6 @@ class MyServiceListener(r.ServiceListener):

assert cast(bool, browser.done) is True

zc.close()


def test_service_browser_cancel_multiple_times_after_close():
"""Test we can cancel a ServiceBrowser multiple times after close."""
Expand Down Expand Up @@ -834,12 +828,10 @@ def on_service_state_change(zeroconf, service_type, state_change, name):
await aiozc.async_close()


def test_legacy_record_update_listener(quick_timing: None) -> None:
def test_legacy_record_update_listener(zc: Zeroconf, quick_timing: None) -> None:
"""Test a RecordUpdateListener that does not implement update_records."""

# instantiate a zeroconf instance
zc = Zeroconf(interfaces=["127.0.0.1"])

with pytest.raises(RuntimeError):
r.RecordUpdateListener().update_record(
zc,
Expand Down Expand Up @@ -892,14 +884,11 @@ def on_service_state_change(zeroconf, service_type, state_change, name):
# Removing a second time should not throw
zc.remove_listener(listener)

zc.close()


def test_service_browser_is_aware_of_port_changes():
def test_service_browser_is_aware_of_port_changes(zc: Zeroconf) -> None:
"""Test that the ServiceBrowser is aware of port changes."""

# instantiate a zeroconf instance
zc = Zeroconf(interfaces=["127.0.0.1"])
# start a browser
type_ = "_hap._tcp.local."
registration_name = f"xxxyyy.{type_}"
Expand Down Expand Up @@ -955,14 +944,11 @@ def on_service_state_change(zeroconf, service_type, state_change, name):
assert service_info.port == 400
browser.cancel()

zc.close()


def test_service_browser_listeners_update_service():
def test_service_browser_listeners_update_service(zc: Zeroconf) -> None:
"""Test that the ServiceBrowser ServiceListener that implements update_service."""

# instantiate a zeroconf instance
zc = Zeroconf(interfaces=["127.0.0.1"])
# start a browser
type_ = "_hap._tcp.local."
registration_name = f"xxxyyy.{type_}"
Expand Down Expand Up @@ -1017,14 +1003,11 @@ def update_service(self, zc, type_, name) -> None: # type: ignore[no-untyped-de
]
browser.cancel()

zc.close()


def test_service_browser_listeners_no_update_service():
def test_service_browser_listeners_no_update_service(zc: Zeroconf) -> None:
"""A listener that ignores update events records only add/remove callbacks."""

# instantiate a zeroconf instance
zc = Zeroconf(interfaces=["127.0.0.1"])
# start a browser
type_ = "_hap._tcp.local."
registration_name = f"xxxyyy.{type_}"
Expand Down Expand Up @@ -1077,12 +1060,9 @@ def update_service(self, zc, type_, name) -> None: # type: ignore[no-untyped-de
]
browser.cancel()

zc.close()


def test_service_browser_nsec_record_does_not_trigger_update():
def test_service_browser_nsec_record_does_not_trigger_update(zc: Zeroconf) -> None:
"""NSEC records assert non-existence and must not fire ServiceStateChange.Updated."""
zc = Zeroconf(interfaces=["127.0.0.1"])
type_ = "_hap._tcp.local."
registration_name = f"xxxyyy.{type_}"
callbacks: list[tuple[str, str, str]] = []
Expand Down Expand Up @@ -1144,24 +1124,21 @@ def update_service(self, zc, type_, name) -> None: # type: ignore[no-untyped-de
assert callbacks == [("add", type_, registration_name)]
finally:
browser.cancel()
zc.close()


def test_service_browser_uses_non_strict_names():
def test_service_browser_uses_non_strict_names(zc: Zeroconf) -> None:
"""Verify we can look for technically invalid names as we cannot change what others do."""

# dummy service callback
def on_service_state_change(zeroconf, service_type, state_change, name):
pass

zc = r.Zeroconf(interfaces=["127.0.0.1"])
browser = ServiceBrowser(zc, ["_tivo-videostream._tcp.local."], [on_service_state_change])
browser.cancel()

# Still fail on completely invalid
with pytest.raises(r.BadTypeInNameException):
browser = ServiceBrowser(zc, ["tivo-videostream._tcp.local."], [on_service_state_change])
zc.close()


def test_group_ptr_queries_with_known_answers():
Expand Down Expand Up @@ -1408,11 +1385,10 @@ def send(out, addr=const._MDNS_ADDR, port=const._MDNS_PORT, v6_flow_scope=()):
await aiozc.async_close()


def test_service_browser_matching():
def test_service_browser_matching(zc: Zeroconf) -> None:
"""Test that the ServiceBrowser matching does not match partial names."""

# instantiate a zeroconf instance
zc = Zeroconf(interfaces=["127.0.0.1"])
# start a browser
type_ = "_http._tcp.local."
registration_name = f"xxxyyy.{type_}"
Expand Down Expand Up @@ -1495,8 +1471,6 @@ def update_service(self, zc, type_, name) -> None: # type: ignore[no-untyped-de
]
browser.cancel()

zc.close()


@patch.object(_engine, "_CACHE_CLEANUP_INTERVAL", 0.01)
def test_service_browser_expire_callbacks():
Expand Down
60 changes: 13 additions & 47 deletions tests/test_core.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -181,13 +181,9 @@ def test_use_asyncio_true_requires_running_loop():
r.Zeroconf(interfaces=["127.0.0.1"], use_asyncio=True)


def test_use_asyncio_default_starts_thread_without_loop():
def test_use_asyncio_default_starts_thread_without_loop(zc: Zeroconf) -> None:
"""use_asyncio=None (default) keeps the historic auto-detect behavior."""
zc = r.Zeroconf(interfaces=["127.0.0.1"])
try:
assert zc._loop_thread is not None
finally:
zc.close()
assert zc._loop_thread is not None


def test_async_updates_from_response():
Expand Down Expand Up @@ -396,9 +392,8 @@ def test_invalid_packets_ignored_and_does_not_cause_loop_exception():
assert zc.cache.get(entry) is not None


def test_goodbye_all_services():
def test_goodbye_all_services(zc: Zeroconf) -> None:
"""Verify generating the goodbye query does not change with time."""
zc = Zeroconf(interfaces=["127.0.0.1"])
out = zc.generate_unregister_all_services()
assert out is None
type_ = "_http._tcp.local."
Expand Down Expand Up @@ -429,15 +424,11 @@ def test_goodbye_all_services():
assert out3 is None
assert zc.registry.async_get_service_infos() == []

zc.close()


def test_register_service_with_custom_ttl(quick_timing: None) -> None:
def test_register_service_with_custom_ttl(zc: Zeroconf, quick_timing: None) -> None:
"""Test a registering a service with a custom ttl."""

# instantiate a zeroconf instance
zc = Zeroconf(interfaces=["127.0.0.1"])

# start a browser
type_ = "_homeassistant._tcp.local."
name = "MyTestHome"
Expand All @@ -456,15 +447,12 @@ def test_register_service_with_custom_ttl(quick_timing: None) -> None:
record = zc.cache.get(info_service.dns_pointer())
assert record is not None
assert record.ttl == 3000
zc.close()


def test_logging_packets(caplog: pytest.LogCaptureFixture, quick_timing: None) -> None:
def test_logging_packets(zc: Zeroconf, caplog: pytest.LogCaptureFixture, quick_timing: None) -> None:
"""Test packets are only logged with debug logging."""

# instantiate a zeroconf instance
zc = Zeroconf(interfaces=["127.0.0.1"])

# start a browser
type_ = "_logging._tcp.local."
name = "TLD"
Expand Down Expand Up @@ -492,23 +480,18 @@ def test_logging_packets(caplog: pytest.LogCaptureFixture, quick_timing: None) -
assert "Sending to" not in caplog.text
logging.getLogger("zeroconf").setLevel(logging.DEBUG)

zc.close()


def test_get_service_info_failure_path():
def test_get_service_info_failure_path(zc: Zeroconf) -> None:
"""Verify get_service_info return None when the underlying call returns False."""
zc = Zeroconf(interfaces=["127.0.0.1"])
assert zc.get_service_info("_neverused._tcp.local.", "xneverused._neverused._tcp.local.", 10) is None
zc.close()


@pytest.mark.skipif(
sys.platform == "win32",
reason="multicast loopback on the 127.0.0.1-only socket is unreliable on GH Actions Windows runners",
)
def test_sending_unicast():
def test_sending_unicast(zc: Zeroconf) -> None:
"""Test sending unicast response."""
zc = Zeroconf(interfaces=["127.0.0.1"])
generated = r.DNSOutgoing(const._FLAGS_QR_RESPONSE)
entry = r.DNSText(
"didnotcrashincoming._crash._tcp.local.",
Expand All @@ -534,11 +517,8 @@ def test_sending_unicast():

assert zc.cache.get(entry) is not None

zc.close()


def test_tc_bit_defers():
zc = Zeroconf(interfaces=["127.0.0.1"])
def test_tc_bit_defers(zc: Zeroconf) -> None:
_wait_for_start(zc)
type_ = "_tcbitdefer._tcp.local."
name = "knownname"
Expand Down Expand Up @@ -634,11 +614,9 @@ def test_tc_bit_defers():

# unregister
zc.unregister_service(info)
zc.close()


def test_tc_bit_defers_last_response_missing():
zc = Zeroconf(interfaces=["127.0.0.1"])
def test_tc_bit_defers_last_response_missing(zc: Zeroconf) -> None:
_wait_for_start(zc)
type_ = "_knowndefer._tcp.local."
name = "knownname"
Expand Down Expand Up @@ -751,12 +729,10 @@ def test_tc_bit_defers_last_response_missing():

# unregister
zc.registry.async_remove(info)
zc.close()


def test_tc_bit_defer_window_is_bounded():
def test_tc_bit_defer_window_is_bounded(zc: Zeroconf) -> None:
"""TC-deferral assembly window must not slide past first_arrival + max delay."""
zc = Zeroconf(interfaces=["127.0.0.1"])
_wait_for_start(zc)
type_ = "_boundeddefer._tcp.local."
registration_name = f"knownname.{type_}"
Expand Down Expand Up @@ -797,7 +773,6 @@ def test_tc_bit_defer_window_is_bounded():
assert protocol._timers[source_ip].when() <= first_when

zc.registry.async_remove(info)
zc.close()


def _make_distinct_tc_packets(count: int, name_prefix: str = "q") -> list[bytes]:
Expand All @@ -819,9 +794,8 @@ def _synthetic_source_ip(i: int) -> str:
return f"192.0.2.{i - 512}"


def test_tc_bit_per_addr_queue_is_bounded(quick_timing: None) -> None:
def test_tc_bit_per_addr_queue_is_bounded(zc: Zeroconf, quick_timing: None) -> None:
"""Per-addr deferred queue must not grow past ``_MAX_DEFERRED_PER_ADDR``."""
zc = Zeroconf(interfaces=["127.0.0.1"])
_wait_for_start(zc)
protocol = zc.engine.protocols[0]
source_ip = "203.0.113.21"
Expand All @@ -841,12 +815,9 @@ def test_tc_bit_per_addr_queue_is_bounded(quick_timing: None) -> None:
retained = [incoming.data for incoming in protocol._deferred[source_ip]]
assert retained == packets[: const._MAX_DEFERRED_PER_ADDR]

zc.close()


def test_tc_bit_total_addrs_is_bounded(quick_timing: None) -> None:
def test_tc_bit_total_addrs_is_bounded(zc: Zeroconf, quick_timing: None) -> None:
"""Distinct addrs with deferred state must not exceed ``_MAX_DEFERRED_ADDRS``."""
zc = Zeroconf(interfaces=["127.0.0.1"])
_wait_for_start(zc)
protocol = zc.engine.protocols[0]

Expand All @@ -865,12 +836,9 @@ def test_tc_bit_total_addrs_is_bounded(quick_timing: None) -> None:
assert len(protocol._deferred) == const._MAX_DEFERRED_ADDRS
assert len(protocol._timers) == const._MAX_DEFERRED_ADDRS

zc.close()


def test_tc_bit_eviction_drops_oldest_addr(quick_timing: None) -> None:
def test_tc_bit_eviction_drops_oldest_addr(zc: Zeroconf, quick_timing: None) -> None:
"""Adding a new addr at capacity drops the oldest insertion (FIFO)."""
zc = Zeroconf(interfaces=["127.0.0.1"])
_wait_for_start(zc)
protocol = zc.engine.protocols[0]

Expand All @@ -892,8 +860,6 @@ def test_tc_bit_eviction_drops_oldest_addr(quick_timing: None) -> None:
assert new_addr in protocol._deferred
assert len(protocol._deferred) == const._MAX_DEFERRED_ADDRS

zc.close()


@pytest.mark.asyncio
async def test_open_close_twice_from_async() -> None:
Expand Down
Loading
Loading

Back | FazBrowse Home | New Git URL