Skip to content

ci: run the test suite on pull requests and pushes to main - #190

Open
nelsonduarte wants to merge 2 commits into
mainfrom
ci/run-test-suite-on-pull-requests
Open

nelsonduarte wants to merge 2 commits into
mainfrom
ci/run-test-suite-on-pull-requests

Conversation

@nelsonduarte

Copy link
Copy Markdown
Owner

Summary

Adds .github/workflows/tests.yml, the first workflow that runs the pytest suite in CI. Until now the suite only ran on developer machines, all of them Windows, so there was no evidence of how it behaves on a clean Linux machine.

What the workflow does

  • Runs the full suite on ubuntu-latest, Python 3.14, with QT_QPA_PLATFORM=offscreen (mandatory: the suite instantiates real QWidgets, and the default xcb plugin aborts on a headless runner).
  • Triggers on every pull request against main, every push to main, and workflow_dispatch.
  • permissions: contents: read, actions pinned by SHA, 20-minute timeout, concurrency group per ref with cancel-in-progress.
  • Installs requirements.txt and then requirements-dev.txt, and runs python -m pytest -q -rs.

What it does NOT do

  • It is not a required check and does not block merges. main has no required status checks (confirmed through the branch protection API), and this PR does not add any.
  • It is Ubuntu only. There is no Windows or macOS leg in this first pass.

This workflow is red on purpose

The first runs are expected to fail. The value of this first pass is to find out what is broken on a clean Linux runner, not to show a green badge. Two failures are known, both pre-existing, and both are deliberately left in place. Please do not "fix" them by weakening or skipping the assertions; each needs its own change.

1. tests/test_updater_msix.py:198

In test_check_for_update_returns_release_for_nsis_when_newer, the final assertion

assert updater._find_asset(result)["name"] == "PDFAppsSetup.exe"

sits outside the with patch.object(updater.sys, "platform", "win32") block. On Windows that goes unnoticed. On Linux, _find_asset() looks up PDFApps-Linux.tar.gz, gets None, and the subscript raises TypeError. The defect is in the test; the production code is correct.

2. tests/test_round8_fixes.py::test_pdfapps_version_flag_runs

PyMuPDF 1.28.2 prints a deprecation notice about the legacy fitz API on stdout (not stderr), so the output of python pdfapps.py --version no longer starts with PDFApps . requirements.txt declares pymupdf>=1.28.0 with no upper bound, so a fresh install picks that release up.

Measured by QA before opening this PR, on Linux (Ubuntu, Python 3.14): 2 failed, 746 passed, 3 skipped. The first real CI run is the check that matters.

Why the test dependencies are in requirements-dev.txt

pytest, pytest-qt and coverage go into requirements-dev.txt, not requirements.txt:

  • They do not trigger security-deps.yml. That workflow's path filters are requirements.txt, flatpak/requirements-pinned.txt and, on PRs only, the workflow file itself.
  • They do not force a test runner to be pinned in the Flatpak. Every root entry in requirements.txt must also be pinned in flatpak/requirements-pinned.txt or listed in FLATPAK_OMITTED, as enforced by tests/test_flatpak_dependency_pins.py::test_no_root_dependency_is_silently_dropped_from_the_flatpak.
  • requirements.txt is the shipped runtime surface. It feeds the PyInstaller builds in build.yml. A test runner has no business in the app bundle.

The header of requirements-dev.txt also records why ruff, PyYAML and pytest-qt are mandatory for the suite and not optional: without them some tests fail or error instead of skipping.

.gitignore gains .coverage, .coverage.* and htmlcov/, since coverage is now a declared dev dependency.

Why libxcb-cursor0 is not installed

The workflow installs only libegl1 and libxkbcommon0. Diverging from build.yml:101, which also installs libxcb-cursor0, is intentional: that leg runs the real GUI app, while this one never leaves the offscreen platform. libxcb-cursor.so.0 is linked only by libqxcb.so, which QT_QPA_PLATFORM=offscreen never loads.

This was verified, not assumed: with the eight non-offscreen platform plugins physically deleted, the suite gave the same result (2 failed, 746 passed, 3 skipped). The reasoning is also recorded in a comment in the workflow step.

Follow-up work (outside this PR)

  • _DEJAVU is hardcoded to C:/Windows/Fonts/DejaVuSans.ttf in tests/test_editor_text_fidelity.py:36, so 2 tests are skipped on Linux even though the font is available there.
  • pymupdf has no upper bound in requirements.txt, which is how the 1.28.2 deprecation notice reached the --version test.
  • The two known failures above.

Scope

One commit, three files: .github/workflows/tests.yml (new), requirements-dev.txt, .gitignore. No application code, no tests, no requirements.txt, no version change. Reviewer and QA both approved.

🤖 Generated with Claude Code

Nothing ran pytest in CI. The suite only ever ran on developer machines,
all of them Windows, so there was no evidence of how it behaves on a
clean Linux machine. This adds .github/workflows/tests.yml, which runs
the full suite on ubuntu-latest with Python 3.14 and
QT_QPA_PLATFORM=offscreen, on every pull request against main, on every
push to main, and on demand.

The workflow is expected to be red on its first runs, on purpose. It is
Ubuntu only and it does not block merges: main has no required status
checks (confirmed through the branch protection API), and this change
does not add any. Its value in this first pass is to surface what is
broken on a clean Linux runner. Two failures are already known. Both are
pre-existing, both are left in place, and neither should be silenced by
weakening the assertion:

* tests/test_updater_msix.py:198, in
  test_check_for_update_returns_release_for_nsis_when_newer. The
  _find_asset() assertion sits outside the
  `with patch.object(updater.sys, "platform", "win32")` block, so on
  Linux it looks up the Linux asset name, gets None, and raises
  TypeError. The defect is in the test; the production code is correct.
* tests/test_round8_fixes.py::test_pdfapps_version_flag_runs. PyMuPDF
  1.28.2 prints a deprecation notice about the legacy `fitz` API on
  stdout rather than stderr, so the output no longer starts with
  "PDFApps ". requirements.txt declares pymupdf>=1.28.0 with no upper
  bound, so a fresh install picks that release up.

The test dependencies (pytest, pytest-qt, coverage) go into
requirements-dev.txt, not requirements.txt. requirements.txt is the
runtime surface that ships: it feeds the PyInstaller builds, it is the
file security-deps.yml watches, and every entry in it must also be
pinned for the Flatpak or explicitly omitted, as enforced by
test_no_root_dependency_is_silently_dropped_from_the_flatpak. A test
runner belongs in none of those. The header of requirements-dev.txt now
also records why ruff, PyYAML and pytest-qt are mandatory for the suite
rather than optional.

Only libegl1 and libxkbcommon0 are installed from apt. Unlike the Linux
leg of build.yml, libxcb-cursor0 is omitted on purpose: it is linked
only by the xcb platform plugin, which the offscreen platform never
loads. With the eight non-offscreen platform plugins physically deleted,
the suite gave the same result as before (2 failed, 746 passed,
3 skipped).

.gitignore gains .coverage, .coverage.* and htmlcov/, since coverage is
now a declared dev dependency and leaves those behind in the repo root.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The comment on "Install Qt system deps" in tests.yml pointed at
build.yml by line number, said `ldd libqoffscreen.so` needs exactly
libEGL, libxkbcommon and libxcb, and predicted a "could not load the
Qt platform plugin" failure. Rewrite it against PySide6 6.11.2:
libEGL.so.1 and libxkbcommon.so.0 are DT_NEEDED by libQt6Gui.so.6,
so a missing one makes `from PySide6 import QtGui` raise ImportError,
and libqoffscreen.so and libQt6Gui.so.6 also need libX11.so.6 and
libGL.so.1.

libxcb-cursor0 stays out because an offscreen run of the suite loads
none of the four objects that need libxcb-cursor.so.0. The comment
says "loaded", not "opened": Qt reads the metadata of the platform
plugins from disk and only dlopens the one it selects. Under strace -f
(openat, mmap, mprotect, munmap) and LD_DEBUG=files, a full offscreen
run of the suite with PySide6 6.11.2, in ubuntu:24.04 and ubuntu:26.04
containers (Python 3.12.3 and 3.14.4), opened libqxcb.so once and
mapped 19000 bytes of it PROT_READ, unmapped straight after; nothing
mapped it PROT_EXEC, and ld.so loaded only libqoffscreen.so from the
platforms directory.

The package observations are anchored to the runner image
(ubuntu-24.04 20260927.320.1) rather than to a run, whose log expires
after 90 days. Evidence: run 36692428596, where apt-get installed
libegl1 as a new package and found libxkbcommon0 already installed.

Of libX11.so.6, libGL.so.1 and libxcb.so.1, only libGL.so.1 relies
on the image alone: on clean Ubuntu 24.04 and 26.04 images, installing
libegl1 also pulls in libx11-6 and libxcb1 via libegl-mesa0, but not
libgl1. It is the one of the three to watch when ubuntu-latest moves
to 26.04.

Comment-only change: the parsed YAML is identical to the parent
commit.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Deploying pdfapps with  Cloudflare Pages  Cloudflare Pages

Latest commit: 4b20cc8
Status: ✅  Deploy successful!
Preview URL: https://76b06158.pdfapps.pages.dev
Branch Preview URL: https://ci-run-test-suite-on-pull-re.pdfapps.pages.dev

View logs

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant