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).
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.
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.
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.
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)
# 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.
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.
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