"""Guards that the package stays coherent as the streams land. The second half of this file is a privacy guard rather than a coherence one. This package was written against a real aircraft parked at a real address, so the things that identify it -- the serial, the SoC CPU id, the Wi-Fi BSSID, the first GPS fix -- all passed through this working tree at some point. PyPI is immutable per version and sdists are mirrored within minutes, so the audit that catches a stray identifier has to run before every publish, not after one. """ import re import tomllib from pathlib import Path import mcbebop from mcbebop.server import build_server ROOT = Path(__file__).parent.parent # Every pattern here is a shape that identifies the owner's aircraft or # network. The comment on each says what it is, so a future hit is diagnosable # rather than mysterious. _FORBIDDEN = ( # A Bebop 2 serial's high half. The full serial also appears in the # aircraft's SSID, which makes it a locator and not just a label. (re.compile(r"PI04"), "a real Bebop 2 serial prefix"), # The P7 SoC CPU id: 'P7' then a long uppercase run. 'P7ID', the command # name in ardrone3.xml, is too short to match and is meant to be here. (re.compile(r"P7[0-9A-Z]{8,}"), "a P7 SoC CPU id"), # Any MAC address at all, not just a Parrot OUI. (re.compile(r"\b[0-9A-Fa-f]{2}(?::[0-9A-Fa-f]{2}){5}\b"), "a MAC address"), # A plausible residential coordinate. The simulator reports ARSDK's 500.0 # no-fix sentinel instead, which has no decimal places to match. (re.compile(r"\b[0-9]{1,3}\.[0-9]{6,}"), "a high-precision coordinate"), # An absolute build path leaks a username and the local layout. (re.compile(r"/home/[a-z]"), "an absolute home-directory path"), ) # 192.168.42.0/24 is the drone's own fixed network and is in Parrot's public # documentation, so it is expected. Any other private address is somebody's # real LAN; use RFC 5737 documentation space in examples instead. _PRIVATE_IP = re.compile( r"\b(?:10\.\d{1,3}\.\d{1,3}\.\d{1,3}" r"|172\.(?:1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3}" r"|192\.168\.\d{1,3}\.\d{1,3})\b" ) _ALLOWED_IP = re.compile(r"\b192\.168\.42\.\d{1,3}\b") def _shipped_files() -> list[Path]: """Every text file that reaches an artifact, which is src/ plus the docs.""" files = [ROOT / "README.md", ROOT / "pyproject.toml"] files += [p for p in (ROOT / "src").rglob("*") if p.is_file() and "__pycache__" not in p.parts] return files def _pyproject() -> dict: return tomllib.loads((ROOT / "pyproject.toml").read_text()) def test_version_matches_pyproject(): # importlib normalises 2026.10.02 to 2026.10.2; compare as release tuples. declared = _pyproject()["project"]["version"] assert tuple(int(p) for p in declared.split(".")) == tuple(int(p) for p in mcbebop.__version__.split(".")) def test_entry_point_is_importable(): module, _, attr = _pyproject()["project"]["scripts"]["mcbebop"].partition(":") mod = __import__(module, fromlist=[attr]) assert callable(getattr(mod, attr)) def test_server_builds(): assert build_server() is not None def test_vendored_xml_present_and_parses(): import xml.etree.ElementTree as ET root = Path(__file__).parent.parent / "src" / "mcbebop" / "arsdk-xml" total = 0 for name in ("common.xml", "ardrone3.xml"): proj = ET.parse(root / name).getroot() # Parrot's element is ; pyparrot renamed it and we reverted that. assert proj.find("class") is not None, f"{name} has no elements" total += sum(len(c.findall("cmd")) for c in proj.iter("class")) assert total == 264, f"expected 264 commands, found {total}" # -- privacy guards ------------------------------------------------------ def test_no_shipped_file_carries_an_aircraft_identifier(): hits = [] for path in _shipped_files(): text = path.read_text(encoding="utf-8", errors="replace") for lineno, line in enumerate(text.splitlines(), 1): for pattern, what in _FORBIDDEN: if pattern.search(line): hits.append(f"{path.relative_to(ROOT)}:{lineno} looks like {what}: {line.strip()[:90]}") assert not hits, "identifying data in a file that ships:\n" + "\n".join(hits) def test_no_shipped_file_names_a_private_network_but_the_drones_own(): hits = [] for path in _shipped_files(): text = path.read_text(encoding="utf-8", errors="replace") for lineno, line in enumerate(text.splitlines(), 1): for found in _PRIVATE_IP.finditer(line): if not _ALLOWED_IP.fullmatch(found.group()): hits.append(f"{path.relative_to(ROOT)}:{lineno} names {found.group()}") assert not hits, ( "a private address other than the drone's own 192.168.42.0/24; " "use RFC 5737 documentation space:\n" + "\n".join(hits) ) def test_sdist_excludes_captures_and_dev_trees(): """The sdist exclusions are a separate mechanism from .gitignore. A file can be gitignored and still swept into an sdist, which is how captures reach PyPI. Assert the directories that hold real data off the aircraft are named in both places. """ excluded = set(_pyproject()["tool"]["uv"]["build-backend"]["source-exclude"]) ignored = (ROOT / ".gitignore").read_text().split() for name in ("captures", "tests"): assert name in excluded or f"{name}/**" in excluded, f"{name} is not excluded from the sdist" for name in ("captures",): assert f"{name}/" in ignored or name in ignored, f"{name} is not gitignored" def test_declared_licence_files_exist(): """A licence named in metadata but absent from the tree ships a lie.""" declared = _pyproject()["project"]["license-files"] assert declared, "license-files must name the licence texts so they ship" for name in declared: assert (ROOT / name).is_file(), f"{name} is declared in license-files but missing" # The vendored XML is Parrot's and the expression has to say so. assert "BSD-3-Clause" in _pyproject()["project"]["license"]