| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2,7 +2,8 @@ | |||
| 2 | 2 | ||
| 3 | 3 | from __future__ import annotations | |
| 4 | 4 | ||
| 5 | - import sys | ||
| 5 | + import importlib.util | ||
| 6 | + import os | ||
| 6 | 7 | import threading | |
| 7 | 8 | from collections.abc import AsyncGenerator, Generator, Iterator | |
| 8 | 9 | from unittest.mock import patch | |
@@ -16,10 +17,19 @@ | |||
| 16 | 17 | from zeroconf._services import info as service_info | |
| 17 | 18 | from zeroconf.asyncio import AsyncZeroconf | |
| 18 | 19 | ||
| 19 | - # hypothesis is not installed on PyPy (no wheels for its native module) | ||
| 20 | + # The dev dependency group only installs hypothesis on CPython (the CI | ||
| 21 | + # matrix pins pypy-3.10 and hypothesis ships no pp310 wheels), and a | ||
| 22 | + # non-dev install has no hypothesis at all. | ||
| 20 | 23 | collect_ignore: list[str] = [] | |
| 21 | - if sys.implementation.name != "cpython": | ||
| 24 | + if importlib.util.find_spec("hypothesis") is None: | ||
| 22 | 25 | collect_ignore.append("test_fuzz_incoming.py") | |
| 26 | + else: | ||
| 27 | + from hypothesis import settings as _hypothesis_settings | ||
| 28 | + | ||
| 29 | + # Deterministic in CI; set HYPOTHESIS_PROFILE=long for a deep local run. | ||
| 30 | + _hypothesis_settings.register_profile("ci", derandomize=True, max_examples=200, deadline=None) | ||
| 31 | + _hypothesis_settings.register_profile("long", max_examples=50000, deadline=None) | ||
| 32 | + _hypothesis_settings.load_profile(os.environ.get("HYPOTHESIS_PROFILE", "ci")) | ||
| 23 | 33 | ||
| 24 | 34 | try: | |
| 25 | 35 | from blockbuster import BlockBuster, blockbuster_ctx | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -6,22 +6,18 @@ | |||
| 6 | 6 | ||
| 7 | 7 | from __future__ import annotations | |
| 8 | 8 | ||
| 9 | - import os | ||
| 10 | 9 | import socket | |
| 11 | 10 | ||
| 12 | - from hypothesis import given, settings | ||
| 11 | + from hypothesis import given | ||
| 13 | 12 | from hypothesis import strategies as st | |
| 14 | 13 | ||
| 15 | 14 | from zeroconf import DNSIncoming, DNSNsec, DNSOutgoing, const | |
| 15 | + from zeroconf._dns import DNSRecord | ||
| 16 | 16 | ||
| 17 | 17 | from .benchmarks.test_incoming import generate_packets | |
| 18 | 18 | ||
| 19 | - # Deterministic in CI; set HYPOTHESIS_PROFILE=long for a deep local run. | ||
| 20 | - settings.register_profile("ci", derandomize=True, max_examples=200, deadline=None) | ||
| 21 | - settings.register_profile("long", max_examples=50000, deadline=None) | ||
| 22 | - settings.load_profile(os.environ.get("HYPOTHESIS_PROFILE", "ci")) | ||
| 23 | - | ||
| 24 | - MAX_PACKET_SIZE = 9194 | ||
| 19 | + # Slack past the codec's cap so the fuzzer also probes oversized input | ||
| 20 | + MAX_PACKET_SIZE = const._MAX_MSG_ABSOLUTE + 256 | ||
| 25 | 21 | ||
| 26 | 22 | ||
| 27 | 23 | def _nsec_packet() -> bytes: | |
@@ -43,7 +39,7 @@ def _nsec_packet() -> bytes: | |||
| 43 | 39 | CORPUS = [*generate_packets(), _nsec_packet()] | |
| 44 | 40 | ||
| 45 | 41 | ||
| 46 | - def _parse(data: bytes) -> list: | ||
| 42 | + def _parse(data: bytes) -> list[DNSRecord]: | ||
| 47 | 43 | """Parse and touch every public surface; any exception is a bug.""" | |
| 48 | 44 | incoming = DNSIncoming(data) | |
| 49 | 45 | assert isinstance(incoming.valid, bool) | |
@@ -53,9 +49,20 @@ def _parse(data: bytes) -> list: | |||
| 53 | 49 | assert incoming.questions is not None | |
| 54 | 50 | answers = incoming.answers() | |
| 55 | 51 | assert isinstance(answers, list) | |
| 52 | + for record in answers: | ||
| 53 | + repr(record) | ||
| 54 | + hash(record) | ||
| 56 | 55 | return answers | |
| 57 | 56 | ||
| 58 | 57 | ||
| 58 | + def test_corpus_parses_cleanly() -> None: | ||
| 59 | + """Positive control: the fuzz corpus itself must parse fully.""" | ||
| 60 | + for packet in CORPUS: | ||
| 61 | + incoming = DNSIncoming(packet) | ||
| 62 | + assert incoming.valid | ||
| 63 | + assert incoming.answers() | ||
| 64 | + | ||
| 65 | + | ||
| 59 | 66 | @given(data=st.binary(max_size=MAX_PACKET_SIZE)) | |
| 60 | 67 | def test_arbitrary_bytes_never_raise(data: bytes) -> None: | |
| 61 | 68 | """Random garbage must parse or fail quietly, and parsing is deterministic.""" | |
| Back | FazBrowse Home | New Git URL |
0 commit comments