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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 87 additions & 8 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,73 @@ jobs:
--ignore-glob='*_runtime.py'
--ignore-glob='*_regression.py'

integration:
name: Integration Python ${{ matrix.python-version }}
if: ${{ github.event_name != 'workflow_call' }}
runs-on: ubuntu-latest
continue-on-error: ${{ matrix.experimental == true }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
python-version:
- "3.10"
- "3.11"
- "3.12"
- "3.13"
- "3.14"
experimental:
- false
include:
- python-version: "3.15"
experimental: true

steps:
- uses: actions/checkout@v7

- uses: actions/setup-python@v7
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: ${{ matrix.experimental }}
cache: pip

# The Scapy extra is there for examples/samples/pcap.py and legacy.py,
# which build their captures with scapy; examples/samples/pcapng.py needs
# nothing beyond the standard library and pcapkit itself, which it uses to
# parse each fixture back.
- name: Install package, test and generator dependencies
run: |
python -m pip install -U pip setuptools wheel
python -m pip install -e '.[test,Scapy]'

# `shell: bash` rather than the default, because it runs with -o pipefail:
# without it the `tee` below would swallow the generator's exit status.
- name: Regenerate sample captures
shell: bash
run: |
# Keep a failure here legible as what it is -- the fixtures could not
# be built, so no test has run yet and nothing is wrong with the code
# under test.
if ! python examples/samples/make_samples.py 2>&1 | tee "$RUNNER_TEMP/make-samples.log"; then
echo "::error title=Sample fixture generation failed::examples/samples/make_samples.py could not rebuild examples/sample/. This is a fixture-generation failure, not a test failure -- the test suite has not run."
exit 1
fi

# examples/samples/pcapng.py fetches two captures from the Wireshark
# repository and synthesises stand-ins when the download fails. The
# stand-ins parse, and tests/protocols/test_pcapng_regression.py only
# asserts that extraction succeeds, so a degraded run goes green and
# is otherwise indistinguishable from a clean one. Say which happened.
if grep -q '(download unavailable)' "$RUNNER_TEMP/make-samples.log"; then
echo "::warning title=Sample fixtures degraded::The upstream Wireshark captures were unreachable, so synthesised stand-ins were used. The PCAP-NG tier ran, but not against the upstream bytes."
grep '(download unavailable)' "$RUNNER_TEMP/make-samples.log"
else
echo "Upstream Wireshark captures were used, matching their pinned SHA-256 digests."
fi

- name: Run full test suite
run: python -m pytest -q

gate:
name: Write workflow gate
if: ${{ github.event_name == 'workflow_call' }}
Expand All @@ -67,14 +134,26 @@ jobs:
python-version: "3.14"
cache: pip

- name: Install package and test dependencies
- name: Install package, test and generator dependencies
run: |
python -m pip install -U pip setuptools wheel
python -m pip install -e '.[test]'
python -m pip install -e '.[test,Scapy]'

- name: Run unit tests
run: >-
python -m pytest -q
--ignore=tests/integration
--ignore-glob='*_runtime.py'
--ignore-glob='*_regression.py'
# See the integration job above for why this step is shaped the way it is.
- name: Regenerate sample captures
shell: bash
run: |
if ! python examples/samples/make_samples.py 2>&1 | tee "$RUNNER_TEMP/make-samples.log"; then
echo "::error title=Sample fixture generation failed::examples/samples/make_samples.py could not rebuild examples/sample/. This is a fixture-generation failure, not a test failure -- the test suite has not run."
exit 1
fi

if grep -q '(download unavailable)' "$RUNNER_TEMP/make-samples.log"; then
echo "::warning title=Sample fixtures degraded::The upstream Wireshark captures were unreachable, so synthesised stand-ins were used. The PCAP-NG tier ran, but not against the upstream bytes."
grep '(download unavailable)' "$RUNNER_TEMP/make-samples.log"
else
echo "Upstream Wireshark captures were used, matching their pinned SHA-256 digests."
fi

- name: Run full test suite
run: python -m pytest -q
16 changes: 8 additions & 8 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@
pcapkit-*-tempdir/
pcapkit-temp.html

sample/*
!sample/out.json
!sample/out.plist
!sample/out.txt
!sample/in.pcap
!sample/dhcp.pcapng
!sample/pcapng.txt
examples/sample/*
!examples/sample/out.json
!examples/sample/out.plist
!examples/sample/out.txt
!examples/sample/in.pcap
!examples/sample/dhcp.pcapng
!examples/sample/pcapng.txt

src
!requirements.txt

sample/test
examples/sample/test
test/pcapkit
test/dictdumper
deprecated/
Expand Down
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ prune .eggs
prune .github
prune .venv
prune docs
prune sample
prune examples/sample
prune temp
prune test

Expand Down
40 changes: 35 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
.PHONY: bootstrap setup dist release docs
.PHONY: bootstrap setup dist release docs samples test test-all coverage

export PIPENV_VENV_IN_PROJECT=1
export PIPENV_CACHE_DIR ?= $(CURDIR)/.pipenv-cache
export PIP_CACHE_DIR ?= $(CURDIR)/.pip-cache
export all_proxy=

SHELL := /opt/homebrew/bin/bash
# Recipes below use bash features (brace expansion), so bash is required; take it
# from PATH rather than a fixed prefix, as Homebrew, Linuxbrew and system installs
# all put it somewhere different.
SHELL := $(shell command -v bash 2>/dev/null || echo /bin/bash)
VERSION = $(shell cat pcapkit/__init__.py | grep "^__version__" | sed "s/__version__ = '\(.*\)'/\1/")

# ``lxml`` needs libxml2/libxslt; on a brewed system they live under the brew
# prefix, which differs between macOS (/opt/homebrew, /usr/local) and Linuxbrew.
BREW ?= $(shell command -v brew 2>/dev/null)
ifneq ($(BREW),)
BREW_PREFIX ?= $(shell $(BREW) --prefix)/opt
else
BREW_PREFIX ?= /opt/homebrew/opt
endif

LIBXML2_PREFIX := $(BREW_PREFIX)/libxml2
LIBXSLT_PREFIX := $(BREW_PREFIX)/libxslt
Expand Down Expand Up @@ -58,6 +69,24 @@ pipenv:
vendor:
pipenv run pcapkit-vendor

# Sample captures under examples/sample/ are not tracked (see .gitignore);
# regenerate the ones the runtime, regression and integration tests read.
samples:
pipenv run python examples/samples/make_samples.py

# Mirrors the selection run by .github/workflows/unit-tests.yml, i.e. the tests
# that need no sample captures beyond the committed ones.
test:
pipenv run python -m pytest -q --ignore=tests/integration --ignore-glob='*_runtime.py' --ignore-glob='*_regression.py'

# Everything, including the fixture-dependent runtime/regression/integration tests.
test-all: samples
pipenv run python -m pytest -q

coverage: samples
pipenv run coverage run -m pytest -q
pipenv run coverage report

docs:
PCAPKIT_SPHINX=1 pipenv run $(MAKE) -C docs html

Expand All @@ -68,13 +97,14 @@ docs-autobuild:
PCAPKIT_SPHINX=1 SPHINXOPTS="--watch ../pcapkit" pipenv run $(MAKE) -C docs livehtml

isort:
pipenv run isort -l100 -ppcapkit --skip-glob '**/__init__.py' pcapkit temp/sort.py
pipenv run isort -l100 -ppcapkit --skip-glob '**/__init__.py' pcapkit $(wildcard temp/sort.py)
pipenv run isort -l100 -ppcapkit pcapkit/{const,vendor}/*/*.py
pipenv run isort -l100 -ppcapkit util/*.py
pipenv run isort -l100 -ppcapkit util/*.py examples/samples/*.py

vermin:
mkdir -p temp
pipenv run vermin pcapkit --backport argparse --backport enum --backport importlib --backport ipaddress --backport typing --backport typing_extensions --no-parse-comments --eval-annotations -vv pcapkit > temp/vermin.txt
code temp/vermin.txt
command -v code >/dev/null && code temp/vermin.txt || cat temp/vermin.txt

pylint:
pipenv run pylint --load-plugins=pylint.extensions.check_elif,pylint.extensions.docstyle,pylint.extensions.emptystring,pylint.extensions.overlapping_exceptions --disable=all --enable=F,E,W,R,basic,classes,format,imports,refactoring,else_if_used,docstyle,compare-to-empty-string,overlapping-except --disable=blacklisted-name,invalid-name,missing-class-docstring,missing-function-docstring,missing-module-docstring,design,too-many-lines,eq-without-hash,old-division,no-absolute-import,input-builtin,too-many-nested-blocks,broad-except,singleton-comparison,ungrouped-imports --max-line-length=120 --init-import=yes pcapkit
Expand Down
28 changes: 28 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,34 @@ For CLI usage, you will need to install the optional packages:
# or explicitly...
pip install pypcapkit emoji

-------
Testing
-------

The unit tests need nothing beyond the package itself and the sample captures
tracked in the repository:

.. code-block:: shell

make test

The runtime, regression and integration tests additionally read sample captures
that are **not** tracked (see ``.gitignore``);
``examples/samples/make_samples.py`` reconstructs them into ``examples/sample/``,
and ``make test-all`` regenerates them before running the whole suite:

.. code-block:: shell

make samples # write examples/sample/*.pcap and *.pcapng
make test-all # regenerate the fixtures, then run every test

The same fixtures back the demonstration scripts in
``examples/legacy_smoke/``, which read them as ``../sample/…``.

Continuous integration runs the ``make test`` selection, since the fixtures are
not in the repository. ``tshark`` is only required to exercise the PyShark
engine, and is not needed by the test suite.

.. _PCAP: https://en.wikipedia.org/wiki/Pcap
.. _Scapy: https://scapy.net
.. _DPKT: https://dpkt.readthedocs.io
Expand Down
36 changes: 28 additions & 8 deletions docs/source/pep.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,35 @@ engines include:
- `pypcap <https://github.com/pynetwork/pypcap>`__
- `pycapfile <https://github.com/kisom/pypcapfile>`__

Implementation for support of new engines would include adding corresponding
handler methods and code blocks into :class:`pcapkit.foundation.extraction.Extractor`
(see support for Scapy, DPKT, and/or PyShark), as well as, the unified auxiliary
tools located in :mod:`pcapkit.toolkit`.
.. note::

The engine interface has since been refactored, so this no longer means adding
handler methods to :class:`~pcapkit.foundation.extraction.Extractor`. A new
engine subclasses :class:`pcapkit.foundation.engines.engine.Engine` and
implements just two methods, :meth:`~pcapkit.foundation.engines.engine.Engine.run`
and :meth:`~pcapkit.foundation.engines.engine.Engine.read_frame`; subclassing
registers it automatically. See :doc:`ext` for a worked example. What does
still apply is the unified auxiliary tools in :mod:`pcapkit.toolkit`, where
each engine has a matching module.

Test Cases
----------

PyPCAPKit still does not have a systematic testing suite to be bundled with it.
The only test cases I have worked out are those in the ``/tests`` folder - mostly
functional tests. As PyPCAPKit is growing bigger and bigger, a comprehensive test
suite is coming much more of demand for a more reliable development process.
.. note::

Largely **done**. There is now a systematic unit test suite under ``tests/``
(84 modules), bundled with the distribution, and it runs in CI against Python
3.10 through 3.14 (see ``.github/workflows/unit-tests.yml``). The sample
captures the runtime, regression and integration tiers read are not tracked in
git, so ``examples/samples/make_samples.py`` (``make samples``) rebuilds them
from source.

What remains wanted is coverage rather than infrastructure: the protocols and
the registered-but-unhandled type codes listed above have no tests because
they have no implementation yet.

Originally: PyPCAPKit still does not have a systematic testing suite to be
bundled with it. The only test cases I have worked out are those in the
``/tests`` folder - mostly functional tests. As PyPCAPKit is growing bigger and
bigger, a comprehensive test suite is coming much more of demand for a more
reliable development process.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading