Skip to content

Dev/test setup: sample capture generators, portable Makefile - #340

Merged
JarryShaw merged 4 commits into
mainfrom
chore/dev-test-setup
Sep 14, 2026
Merged

JarryShaw merged 4 commits into
mainfrom
chore/dev-test-setup

Conversation

@JarryShaw

Copy link
Copy Markdown
Owner

Draft, accumulating — further commits will land on this branch as each subtask finishes.

Why

A fresh clone cannot run the whole test suite. The runtime, regression and integration suites read eleven captures out of sample/, and .gitignore keeps them out of the repository, so those 28 tests fail locally and CI has to --ignore them. This branch reconstructs the fixtures from source instead, so pytest with no ignore flags passes on any machine.

What changed

util/make_samples.py (plus util/samples_pcap.py, util/samples_pcapng.py) — regenerates every fixture; also wired as make samples.

  • .pcap fixtures (arp, ipv4, ipv6, tcp, stream, http) are built with scapy from the traffic each test pins. Checksums are computed by scapy from the finished packets, sequence/acknowledgement continuity holds across all 114 connections, and regeneration is byte-identical.
  • .pcapng fixtures are either downloaded from Wireshark's test/captures with the source URL and SHA-256 pinned in the script (dhcp_big_endian.pcapng, many_interfaces.pcapng), or synthesised block by block (dhcp_little_endian.pcapng, test.pcapng, profile.pcapng). A digest mismatch is a hard error; losing the network degrades to a synthesised stand-in and says so. Nothing downloaded is redistributed — sample/ stays gitignored.

Makefile — could not run outside a Homebrew macOS box: SHELL was hardcoded to /opt/homebrew/bin/bash, so every target died with Command not found on Linux. bash now comes from PATH and the libxml2/libxslt prefixes from brew --prefix, leaving macOS behaviour unchanged. Added samples, test (the CI-equivalent selection), test-all and coverage; fixed isort (referenced an untracked temp/sort.py) and vermin (wrote into a missing temp/, and hard-required VS Code).

README.rst — a Testing section covering the three commands and why CI runs the narrower selection.

Test result

pytest -q → 334 passed, 70 subtests passed (was 28 failed, 309 passed). make test (CI selection) → 284 passed.

Defects found on the way, not fixed here

Building the pcapng fixtures surfaced parser bugs, all documented in util/samples_pcapng.py and left for their own change:

  • pcapkit/protocols/schema/misc/pcapng.py:1591 — Custom Block padding is (4 - pkt['data'] % 4) % 4 on bytes; every custom block raises TypeError.
  • Same file :1335 — ISB options sized length - 20, but the fixed prefix is 24 bytes. Fires on Wireshark's own many_interfaces.pcapng.
  • :1102 / :1177 — NRB IPv6 records sized as if the address were 4 bytes, and NRB options over-run when ns_* options are present.
  • :1657/:1659 — obsolete Packet Block declares interface_id/drop_count as 32-bit; the spec makes both 16-bit, and the options arithmetic assumes the 16-bit layout, so neither parses.
  • pcapkit/corekit/fields/ipaddress.py — if_IPv6addr parses the prefix-length octet as an ASCII decimal string; /64 raises, /56 silently decodes as /8.
  • pcapkit/foundation/engines/pcapng.py:218 — rejects a Simple Packet Block in any section with more than one interface. Per spec §4.4 that is legal; the check should be for zero interfaces.
  • pcapkit/protocols/internet/ipv6.py:342 — the extension-header loop breaks before advancing past the last header, so a fragmented datagram hands the fragment header to the next layer as if it were the UDP header. tests/protocols/internet/test_ipv6_extension_runtime.py currently pins this behaviour.

The runtime, regression and integration tests read eleven captures out of
sample/, which .gitignore excludes, so a fresh clone fails 28 tests and CI
has to skip those suites entirely. util/make_samples.py rebuilds the whole
set: the .pcap fixtures are constructed with scapy from the traffic each
test pins, and the .pcapng ones are either fetched from Wireshark's
test/captures with a pinned SHA-256 or synthesised block by block, with an
offline fallback so generation never depends on the network.

The Makefile could not run anywhere but a Homebrew macOS box, since SHELL
was hardcoded to /opt/homebrew/bin/bash; bash now comes from PATH and the
libxml2/libxslt prefixes from brew --prefix. Added samples, test, test-all
and coverage targets, and fixed the isort and vermin targets, which
referenced an untracked scratch file and a missing temp/ directory.

Full suite: 334 passed, 70 subtests passed.
The captures and the scripts that build them now live under examples/, next
to the legacy_smoke demonstrations that read them as ../sample/ and were
therefore pointing at nothing. Generators moved from util/ to
examples/samples/, fixtures to examples/sample/.

Tests no longer spell that directory out: tests/_support.sample_path()
resolves a capture name against the repository root, so the suite no longer
depends on pytest being invoked from the top of the tree, and a fresh clone
gets an error naming the command that rebuilds the fixture.

Added examples/samples/legacy.py for the two captures only the smoke
scripts read - test.pcap, whose out-of-order and retransmitted segments give
TCP reassembly something to reassemble, and http6.cap, HTTP/1.1 over IPv6
including a 304 with no body.

setup.py read README.rst by bare relative name, which failed whenever
setup.py was loaded from elsewhere; it now resolves against its own
directory.

Full suite: 334 passed, 70 subtests passed, from the repository root and
from an unrelated working directory.
The runtime, regression and integration tiers were ignored in CI because
their captures are not in the repository. They can be rebuilt now, so both
the pull-request matrix and the reusable gate generate the fixtures and then
run the suite with no --ignore flags. Python 3.10 through 3.14 block, 3.15
stays experimental, matching the unit job.

Generation is a step of its own, and a failure there annotates the run as a
fixture-generation failure so it cannot be misread as a test failure. When
the upstream Wireshark captures are unreachable the generators fall back to
synthesised stand-ins, which parse and pass; the step now says which of the
two happened, since a silent fallback would halve that tier's coverage
without turning the build red.

Installs the Scapy extra, which the .pcap generators need.
The Test Cases proposal predates the unit suite: there are now 84 test
modules under tests/, bundled with the distribution and running in CI across
Python 3.10 through 3.14, so the section records what remains wanted -
coverage of the protocols that are still unimplemented - rather than asking
for a suite that exists.

The New Engines proposal still describes adding handler methods to Extractor.
That stopped being true when engines became Engine subclasses with two
abstract methods and automatic registration; the note points at that and at
the worked example in ext.rst. The candidate engines themselves are still
open.

Dropped the PCAP-NG todo from pcapkit.foundation.engines, whose docstring
asked for support that engines/pcapng.py already provides.
@JarryShaw
JarryShaw marked this pull request as ready for review September 14, 2026 02:32
@JarryShaw
JarryShaw merged commit 72f950d into main Sep 14, 2026
49 checks passed
@JarryShaw
JarryShaw deleted the chore/dev-test-setup branch September 14, 2026 02:33
@JarryShaw JarryShaw added test Pull requests that add or correct tests (test: subject prefix) chore Maintenance work: tooling, repo hygiene, no library behaviour change labels Sep 22, 2026
@JarryShaw JarryShaw added this to the 1.5 milestone Oct 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

chore Maintenance work: tooling, repo hygiene, no library behaviour change test Pull requests that add or correct tests (test: subject prefix)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant