From ceaa8392d73012516b14fbcb910f91942d9d1514 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Wed, 10 Jun 2026 10:01:15 +0200 Subject: [PATCH] serialize real-pql tests to fix parallel db contention MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pql integration tests (test/pql/client_test.dart) and pql.* daemon handler tests (test/daemon/pql_commands_test.dart) each spawn a real `pql` process against the shared on-disk .pql/pql.db. In the parallel test pool, concurrent invocations contend for the SQLite lock and flake with PqlException(69) (db busy) — surfaced reliably by the pql 1.10 record_id migration. They pass one-at-a-time, so isolation is the fix. Tag both files @Tags(['serial']) and add a --concurrency=1 serial pass to ci/test_core.sh (pty + serial together), mirroring ci/test.sh's existing serial handling. The error-path companion uses a fake binary, so it stays parallel. Co-Authored-By: Claude Opus 4.8 (1M context) --- ci/test_core.sh | 23 ++++++++++++++--------- test/daemon/pql_commands_test.dart | 12 ++++++++++++ test/pql/client_test.dart | 6 ++++++ 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/ci/test_core.sh b/ci/test_core.sh index ddd457fa..f3aabe73 100755 --- a/ci/test_core.sh +++ b/ci/test_core.sh @@ -48,15 +48,20 @@ run_pass() { fi } -# PTY-tagged tests spawn real PTYs (posix_openpt + posix_spawn) and rely on a -# reader isolate; run in the default parallel pool they contend for fds + CPU -# with the other suites and the isolate is starved, flaking 'write sends -# keystrokes to child' (T-292). Serialize them in their own pass — matching -# ci/test.sh — then run everything else in parallel. -echo "test-core: dart test (pty-tagged; --concurrency=1) (timeout ${TIMEOUT_SECONDS}s)" -run_pass --concurrency=1 --tags pty $CORE_DIRS +# Some core tests must not share the parallel pool: +# - pty: spawn real PTYs (posix_openpt + posix_spawn) + a reader isolate; +# in the parallel pool they contend for fds + CPU and the isolate starves, +# flaking 'write sends keystrokes to child' (T-292). +# - serial: spawn real `pql` processes against the shared on-disk +# `.pql/pql.db`; concurrent invocations contend for the SQLite lock and +# flake with PqlException(69) (db busy). (T-193, surfaced by the pql 1.10 +# record_id migration.) +# Run both in one --concurrency=1 pass (matching ci/test.sh's serial handling), +# then everything else in parallel. +echo "test-core: dart test (pty + serial; --concurrency=1) (timeout ${TIMEOUT_SECONDS}s)" +run_pass --concurrency=1 --tags "pty || serial" $CORE_DIRS -echo "test-core: dart test (rest; parallel, excludes pty) (timeout ${TIMEOUT_SECONDS}s)" -run_pass --exclude-tags pty $CORE_DIRS +echo "test-core: dart test (rest; parallel, excludes pty + serial) (timeout ${TIMEOUT_SECONDS}s)" +run_pass --exclude-tags "pty || serial" $CORE_DIRS echo "test-core: ok" diff --git a/test/daemon/pql_commands_test.dart b/test/daemon/pql_commands_test.dart index d0858d9b..56105b3d 100644 --- a/test/daemon/pql_commands_test.dart +++ b/test/daemon/pql_commands_test.dart @@ -1,3 +1,15 @@ +/// Drives the pql.* daemon handlers against the real `pql` binary and the +/// working directory's vault. +/// +/// Tagged `serial`: each handler spawns a real `pql` process that opens the +/// shared on-disk `.pql/pql.db`. Run in the parallel pool these contend for +/// the SQLite lock and flake (`PqlException(69)`, db busy); one-at-a-time they +/// pass — isolation is the real fix (T-193). The error-path companion +/// (`pql_commands_errors_test.dart`) points at a fake binary, so it needs no +/// such tag. +@Tags(['serial']) +library; + import 'dart:io'; import 'package:clide/clide.dart'; diff --git a/test/pql/client_test.dart b/test/pql/client_test.dart index 7c6d5b15..25987ccb 100644 --- a/test/pql/client_test.dart +++ b/test/pql/client_test.dart @@ -1,6 +1,12 @@ /// Integration tests for `lib/src/pql/client.dart`. Drives the real /// `pql` binary against the working directory's vault for happy-path /// methods; uses a fake pql path for the error-handling tail. +/// +/// Tagged `serial`: each happy-path test spawns a real `pql` process that +/// opens the shared on-disk `.pql/pql.db`. Run in the parallel pool these +/// contend for the SQLite lock and flake with `PqlException(69)` (db busy). +/// They pass reliably one-at-a-time — isolation is the real fix here (T-193). +@Tags(['serial']) library; import 'dart:io';