Test Strategy & Suites

Four suites at different layers. All are independent of the full cmake build — Makefile.transport-tests drives them on Linux and macOS; the CI workflow handles Windows. See also: story/06-testing.

The Makefile is platform-aware: detects Darwin and uses -bundle -undefined dynamic_lookup + curl-config instead of -shared -fPIC + pkg-config, and omits -ldl (in libSystem on macOS). CXX defaults to g++ (which is clang++ on macOS).

Suite 0 — GTest coretest (cmake-integrated)

File: src/test/TransportTest.cpp → compiled into coretest binary

Run: cd build && ctest -R Coretests --output-on-failure

Covers: pws_is_transport_url, extract_scheme, file: plugin end-to-end via PWScore.

Suite 1 — Standalone transport infrastructure

File: src/test/transport_standalone_test.cpp

Run: make -f Makefile.transport-tests suite1

No network required. Covers plugin loading, scheme detection, file: round-trips, cache path generation, FILE* map, identity pre-scan rejection, roundtrip byte identity.

Suite 2 — WebDAV plugin (live server)

File: src/test/transport_webdav_test.cpp — 11 sections, 59 assertions

Run: make -f Makefile.transport-tests suite2 (requires PWSAFE_WEBDAV_TEST_URL or WEBDAV_URL)

Skips (exit 77) if PWSAFE_WEBDAV_TEST_URL is unset. Run from build/: PWSAFE_WEBDAV_TEST_URL=https://webdav.critchley.biz/test /tmp/transport_webdav_test

Stale lock resilience: seed stores use store_clearing_stale_lock() which retries up to 4× with 10s sleep if EBUSY. Necessary because §C of suite 3 deliberately leaves a stale server lock; without retry the next run fails at the initial store.

Key sections:

• §7 — store while locked: the If: (<token>) header regression. Server returns 423 if missing.

• §11 — lock contention: EBUSY when another client holds the lock; failed lock must not populate token_out.

Suite 3 — Lock lifecycle (standalone + live)

File: src/test/transport_lock_lifecycle_test.cpp — 6 sections (A–F), 49 assertions (15 offline, 34 live)

Run: make -f Makefile.transport-tests suite3

Links transport.cpp and transport_lockd.cpp directly — tests the full daemon IPC. Not applicable to Windows (lockd is synchronous there; suite 3 is Linux/macOS only in CI).

Key sections:

• §A — registry unit tests: pws_lock_register/has_lock/unregister in isolation, no network

• §C — crash simulation: registry cleared without plugin unlock → server lock still held; subsequent lock() returns EBUSY. Note: this section intentionally leaves a stale lock — hence the retry logic in suite 2.

• §D — SafeUnlockFile simulation: the guard (IsLockedFile → UnlockFile) that was broken before the fix

• §E — daemon EBUSY path: lock contention propagated correctly through the pipe round-trip

• §F — store regression: t->store() in parent → EBUSY (bug); pws_lockd_store() → 0 (fix)

Running the suites

# Offline tests only (default):
make -f Makefile.transport-tests

# All suites with a local wsgidav server (no remote dependency):
make -f Makefile.transport-tests local-live

# All suites against the remote server:
make -f Makefile.transport-tests live

# CI target (build plugins + suites 1-3, no cmake):
make -f Makefile.transport-tests ci-live

.NOTPARALLEL ensures sequential execution — suites share /tmp paths and ~/.cache/pwsafe/, and suite 3 forks a lock daemon.

CI — GitHub Actions

Workflow: .github/workflows/transport-tests.yml — triggered on push to webdav branch and workflow_dispatch.

Jobs run sequentially: Linux → macOS → Windows. Sequential ordering prevents concurrent access to the same live WebDAV test files (lock conflicts).

• Linux: g++ + libcurl4-openssl-dev + apt; suites 1, 2, 3

• macOS: clang++ + brew curl; suites 1, 2, 3

• Windows: MSVC cl.exe + vcpkg curl (x64-windows); suite 2 only (no fork-based lock daemon)

Live server: https://webdav.critchley.biz/test — credentials in repository secrets WEBDAV_TEST_USER / WEBDAV_TEST_PASS. Server reachability is checked first; jobs skip gracefully if unreachable.

The local wsgidav server

Files: src/test/webdav_test_server.py + src/test/run_local_webdav_tests.sh

Uses wsgidav 4.3.3 + cheroot 11.1.2. DAV class 2 (locking), anonymous write access. Pre-seeds pwsafe_test.psafe3. Prints READY once the port accepts connections.

Install: pip install --user --break-system-packages wsgidav cheroot

version 3  ·  updated 2026-06-04