Skip to content

test: coverage S1-S5 — 28 files past 80% lines, 4 bug fixes, +600 tests#178

Merged
singaraiona merged 13 commits intomasterfrom
tests/coverage-pass-7
May 4, 2026
Merged

test: coverage S1-S5 — 28 files past 80% lines, 4 bug fixes, +600 tests#178
singaraiona merged 13 commits intomasterfrom
tests/coverage-pass-7

Conversation

@ser-vasilich
Copy link
Copy Markdown
Collaborator

Summary

Five-session parallel-agent coverage campaign. TOTAL line coverage ~73% → 86.72%, function coverage ~91% → 97.06%. Files in red zone (<80% lines): 30 → 2 (both structural — see below).

Metric Before After
TOTAL lines ~73% 86.72%
TOTAL functions ~91% 97.06%
TOTAL regions ~78% 82.15%
Files <80% lines 30 2
Tests passing ~1240 2067

Bug fixes (4) — all surfaced by coverage agents

  1. ipc_read_creds buffer realloc — auth was BROKEN under poll-based server. ray_poll_rx_request resets offset=0 on grow, discarding the cred_len byte. Result: poll-mode auth always failed even with the correct password. Fix: grow the rx buffer in-place inside ipc_read_creds, preserving data[0]=cred_len.

  2. ipc_send_fn dead code removed — registered as reg.send_fn but the poll engine never invokes it (sync ray_sock_send is used directly).

  3. ipc_on_data dead code removed — registered as reg.data_fn but never reachable: ipc_read_payload always returns NULL, and the contract is "data_fn fires only when read_fn returns non-NULL".

  4. serde F32 atom round-tripray_f32 stores in obj->f64 (atom reuses F64 slot) but ray_ser_raw was reading &obj->i32. i32 and f64 share the union, but the lower 4 bytes of an 8-byte double are NOT the float bit pattern. Fix: ser narrows obj->f64 to float; de reads as float and returns ray_f32(v) (preserves type, was promoting to F64).

Coverage progression by phase

Phase Files Tests Notable
S1 (squash-merged in #177) already in master
S1 (this PR) 6 +85 builtins.c, csr.c, csv.c, datalog.c, list.c, pool.c
S2 5 +56 query.c, sym.c, sym.h, temporal.c, window.c
S3 8 +330 morsel.c (100%), lftj.c (96.2%), splay.c, heap.c, format.c (97.1%), ipc.c (with fix), eval.c (90.1%), repl.c
S4 5 +150 filter.c (52.6→95.9%!), expr.c, group.c, journal.c, serde.c (with fix)
S5 5 +95 compile.c (100%), pivot.c, sort.c, hash.h, internal.h

Files still <80% lines (2, both structural)

  • src/core/block.c — 52.4%. The weak __attribute__((weak)) ray_alloc stub is dead code under every build configuration in tree (the strong buddy-allocator definition in src/mem/heap.c always wins at link time). Reverted earlier #if 0 — to be discussed with maintainer whether to delete the stub or keep it as aspirational portability.

  • src/ops/hash.h — 73.1%. 32 missed lines are MSVC, 32-bit (no __SIZEOF_INT128__), and big-endian byte-swap branches — compile-time-dead on the active platform but still recorded in the source coverage map. Tests verify all reachable branches (branches → 100%).

Process notes

  • 5 parallel-agent sessions (S1-S5), Sonnet model, isolated worktrees per agent, exclusive test-file allocation per agent to avoid merge conflicts.
  • 4 src/ bug fixes were not pre-planned — each was surfaced by an agent investigating uncovered code, then triaged and fixed manually.
  • Some agents identified structural ceilings (platform-specific branches, dead code blocks like if (0 && ...), OOM injection paths) — those are documented in commit messages, not "fixed" with #if 0 / coverage markers.
  • Pre-push portability check (.portability-check.sh) confirms no Linux-only headers used without macOS guard.

Commits

174934dc test: S5 coverage — 5 more files past 80% lines
47cfddb3 test: S4 coverage — 5 more files past 80% lines
12098df5 test: S3 coverage — 8 more files past 80% lines
144d6505 fix(ipc, serde): 4 bugs surfaced by coverage agents
1adc6827 revert: keep block.c weak ray_alloc stub uncommented
1715a73e test: S2 coverage — 5 more files past 80% lines via parallel agents
031d4fb2 test: S1 coverage — 6 files past 80% lines via parallel agents

Test plan

  • make test — 2067 of 2068 passed (1 pre-existing skip, 0 failed)
  • make coverage — TOTAL line coverage 86.72%, function coverage 97.06%
  • .portability-check.sh passed (no Linux-only headers without macOS guard)
  • CI verifies on macOS + Linux (pending after push)

🤖 Generated with Claude Code

ser-vasilich and others added 13 commits May 4, 2026 09:52
| File         | Before  | After    | Tests |
|--------------|---------|----------|-------|
| builtins.c   | 77.83%  | 83.36%   | +20   |
| csr.c        | 77.31%  | 81.23%   | +7    |
| csv.c        | 76.41%  | 81.00%   | +21   |
| datalog.c    | 78.13%  | 82.17%   | +10   |
| list.c       | 78.95%  | 89.47%   | +18   |
| pool.c       | 79.67%  | 80.67%   | +5    |

TOTAL lines 75.93% → 76.53%, functions 94.30% → 95.07%, regions
78.70% → 80.57%.  Tests 1335 → 1420 passing (1 pre-existing skip).

No src/ changes.  No static-expose.  No mocks.  All tests reach
production code paths through public API (rfl-eval or direct C calls
on declarations from src/lang/internal.h, src/store/csr.h, etc).

Highlights per file:

  builtins.c — 20 tests covering 0%-coverage functions (group_grow,
  group_ht_grow, ght_*_hash_gi, cast_par_fn) plus partial-coverage
  paths in nil_fn, where_fn, format_fn, raze_fn, within_fn, fdiv_fn,
  concat_fn, enlist_fn, resolve_fn.

  csr.c — 7 tests for ray_rel_neighbors, ray_rel_n_nodes,
  ray_rel_set_props, save/load error paths, ray_rel_free(NULL) guard,
  ray_rel_from_edges error branches.

  csv.c — 21 tests for type-inference variants (date/time/timestamp,
  bool, F64 specials, null sentinels, promotions), tab delimiter,
  no-header, CRLF, truncated rows, write-side branches (int widths,
  NaN/inf, null cells, sliced columns, header quoting), parallel
  parse path, sym narrowing.

  datalog.c — 10 tests for dl_rule_head_const, dl_rule_add_builtin,
  dl_rule_add_interval, dl_builtin_before/duration_since/abs.
  normalize_columns is `__attribute__((unused))` dead code, skipped.

  list.c — 18 tests covering insert_at error paths, insert_many
  parallel/broadcast/empty, COW copy-on-shared-rc, RAY_IS_ERR sub-
  expression branches.

  pool.c — 5 tests for ring-cap clamp at MAX_RING_CAP=65536, multi-
  iter ring growth (1024→2048→...), exact-cap boundary, double-
  destroy CAS-fail branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| File         | Before  | After    | Tests |
|--------------|---------|----------|-------|
| query.c      | 77.76%  | 80.78%   | rfl   |
| sym.c        | 78.09%  | 81.81%   | +16   |
| sym.h        | 76.47%  | 97.06%   | +3    |
| temporal.c   | 77.92%  | 90.73%   | +11   |
| window.c     | 79.41%  | 85.46%   | +7    |

TOTAL lines 76.53% → 76.92%, functions 95.07% → 95.42%, regions
80.57% → 81.13%.  Tests 1420 → 1456 passing (1 pre-existing skip).

No src/ changes.  No static-expose.  No mocks.

Highlights:

  query.c — Expanded test/rfl/ops/query_coverage.rfl with named-lambda
  variants (GUID + I64 + SYM group keys) and multi-column-ref forms to
  drive nonagg_eval_per_group / _core / _buf, collect_col_refs,
  bind_col_slice, typed_vec_to_list, groups_idx_feed, buf_idx_feed.
  Plus apply_sort_take vec-take and window-join F64 sorted-f branches.

  sym.c — 16 tests covering ray_sym_save/load null-path guards, prefix
  validation, stale prefix, id mismatch, file-exists-but-corrupt,
  ray_sym_str/is_dotted/segs invalid-id boundaries, hash table linear
  probing after grow, ray_sym_ensure_cap edge cases, dotted leading-dot
  reserved namespace.

  sym.h — 3 tests directly call ray_sym_dict_width (W32/W64 ranges),
  ray_sym_elem_size (non-SYM types), ray_read_sym/ray_write_sym (all
  four W8/W16/W32/W64 widths).  Only 1 line still uncovered: the
  defensive `return 0` after a fully-covered switch in ray_read_sym.

  temporal.c — 11 tests for ray_extract_ss/hh/minute/yyyy/mm/dd_fn
  (was 0% functions), ray_temporal_extract on RAY_TIME atoms+vectors,
  ray_timestamp_clock_fn (was 0%), is_global_arg, ray_epoch_offset
  (was 0%), ray_temporal_truncate on RAY_DATE/RAY_TIME, exec_date_trunc
  RAY_DATE/RAY_TIME column branches, SECOND case, doy leap-year branch.

  window.c — 7 tests for running_max i64 else-branch, leading-null
  win_set_null, win_keys_differ F64+I32/DATE arms via RANK with ties,
  single-key radix sort path (n=200), radix_sort_run sub-path
  (n>4096), running_avg cnt==0 leading-null branch.

Process notes:
  - Each agent ran in an isolated worktree (avoiding the build-
    contention that plagued S1 when 6 agents shared one tree).
  - Two agents overlapped on test/test_sym.c (sym.c + sym.h targets);
    surgical merge of both diff sets onto current HEAD was needed.
  - Worktrees were created off master, not the local pass-S1 commit,
    so 3-way merge against the parent's HEAD was required.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
S1 wrapped the weak-attribute ray_alloc fallback in #if 0 because every
build configuration in tree links the strong definition from
src/mem/heap.c (so the body never executes and dragged llvm-cov line
coverage down).  Reverting per request — Anton will decide whether to
keep the weak stub as aspirational portability code or remove it
outright.

The structural coverage gap on src/core/block.c (~52% lines) is a
known consequence of leaving this stub.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ipc_read_creds buffer realloc — auth was BROKEN under the poll-based
server.  ray_poll_rx_request resets offset=0 when it grows the rx
buffer, discarding the cred_len byte that ipc_read_creds had already
consumed.  Result: poll-mode auth always failed, even with the correct
password.  Fix: grow the rx buffer in-place inside ipc_read_creds,
preserving data[0]=cred_len.

ipc_send_fn / ipc_on_data dead code removed — both were registered on
the connection (reg.send_fn / reg.data_fn) but never called.  The
poll engine has no async-send queue draining (sync ray_sock_send is
used directly inside ipc_read_payload), and ipc_read_payload always
returns NULL so the data_fn callback is unreachable.  Net -8 lines.

serde F32 atom round-trip — ray_f32 stores its value in obj->f64 (the
F32 "atom" reuses the F64 union slot per the constructor doc), but
ray_ser_raw was reading &obj->i32 ("same 4-byte slot" comment, line 295).
i32 and f64 share storage in the union, but the lower 4 bytes of an
8-byte double are NOT the float bit pattern — they're the LSB half of
the double.  The ser side now narrows obj->f64 to float and writes the
4 bytes; the de side reads as float and returns ray_f32(v) (preserving
type, not promoting to F64 as before).  Round-trip now preserves both
value (within float precision) and type.

Tests: new test/test_ipc.c (27 cases including poll-auth happy path
that verifies the realloc fix), updates to test/test_store.c F32
round-trip case to assert -RAY_F32 type and exercise both ser+de
sides of the fix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| File                | Before  | After    | Tests |
|---------------------|---------|----------|-------|
| src/app/repl.c      | 75.6%   |  ~87%    | +20 PTY-driven |
| src/core/morsel.c   | 74.5%   |  100%    | +7   |
| src/core/ipc.c      | 74.5%   | 89.4%    | (in fix commit) |
| src/lang/eval.c     | 76.8%   | 90.1%    | +159 (eval-rerun's aggressive batch) |
| src/lang/format.c   | 76.0%   | 97.1%    | +89 (every atom/vec/list/dict/table type) |
| src/mem/heap.c      | 75.4%   | 81.8%    | +14  |
| src/ops/lftj.c      | 74.5%   | 96.2%    | +13  |
| src/store/splay.c   | 73.6%   | 88.8%    | +16 (new test_splay.c) |

Tests added across the 8 files: ~330.  No src/ changes for these
tests (the ipc.c bug fixes are in their own commit).  No static-expose,
no mocks.  Each new file registered in test/main.c.

Highlights per file:

  morsel.c — 7 tests for HAS_INDEX inline + ext null bitmaps,
    mmap_advise path, init_range, broaden previous S0 work to 100%.

  lftj.c — 13 tests covering grow_output (was 0%), build_plan rev/
    self-loop/oob/too-many-vars, default plans (n=2, 4-clique, chain
    fallback), enumerate root + depth=1 no-bindings, leapfrog k<=0 +
    single-iter.  Reaches 96.2%; remaining 6 lines are OOM injection.

  format.c — 89 tests covering every atom type (u8/i16/i32/f32/date/
    time/timestamp/sym/str/guid + typed nulls), vectors of every type,
    list (empty/het/mode 0/1), dict with all key+val type combos,
    tables (mode 0, empty, wide, tall, list_col), public API
    (ray_fmt_print/_set_precision/_set_width).

  heap.c — 14 tests for SLICE / NULLMAP_EXT / PARTED branches in
    detach_owned_refs, scratch_realloc table/dict/mapcommon, alloc_copy
    DICT, foreign-flush owner-gone path, slab-overflow merge, free
    mmod==1 atom, GC return-foreign-freelist, ceil_log2 exact-power.

  splay.c — 16 tests in NEW test_splay.c: save NULL guards, load
    error paths (missing schema, deleted col, bad sym path, oversized
    paths), mmap roundtrip, validate_sym_columns variants.  Registered
    splay_entries in test/main.c.

  repl.c — +20 PTY-driven REPL tests (forkpty, raw-mode keystroke
    matrix, banner, progress callback, run_file errors, no-poll
    interactive fallback).  Final ~87% with remaining lines being
    progress rendering paths only fired on tty stderr at high min-ms.

  format/temporal/eval inserted into test_lang.c — 159 new
    test_eval_* cases covering arith mixed types, comparisons,
    vector arithmetic, type errors, lambdas + closures + recursion,
    error frames with NULL nfo, callf rp overflow, DAG cast paths.

Tests 1456 → ~1900 passing (1 pre-existing skip).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| File                | Before  | After    | Tests |
|---------------------|---------|----------|-------|
| src/ops/filter.c    | 52.6%   | 95.9%    | +31  |
| src/ops/expr.c      | 69.6%   | 86.3%    | + 2  |
| src/ops/group.c     | 69.5%   |  ~85%    | +rfl + new test_group_extra.c |
| src/store/journal.c | 68.3%   | 85.0%    | +57 (new test_journal.c) |
| src/store/serde.c   | 67.8%   | 94.1%    | +24  |

Tests added across the 5 files: ~150 (mostly C-level; +rfl for group).
No src/ changes (serde F32 fix is in fix commit).  No static-expose,
no mocks.  New files: test_journal.c, test_group_extra.c.

Highlights per file:

  filter.c — 31 tests in test_partition_exec.c covering exec_filter
    parallel + sequential paths, exec_filter_parted_vec STR + non-STR,
    parted_gather_col cross-segment + null/esz mismatch, exec_filter_head
    parted variants (i64/str/sym + esz_skip), sel_compact basic + parted
    + per-column parallel + 17-col fan-out (>MGATHER_MAX_COLS).
    Reaches 95.9%; remaining 22 lines are OOM/pool-null injection.

  expr.c — 2 tests in test_exec.c: AND/OR with both-nullable I64 inputs
    (binary_range BOOL path), SYM W8 fused expression input
    (expr_load_i64 RAY_SYM case).  Covers most non-dead branches.

  group.c — 386 lines added to test/rfl/ops/group_coverage.rfl plus
    new test_group_extra.c (839 lines) covering wide-key types,
    LIST-STR / RAY_STR keys, NULL key handling, parted-table edge
    cases, variance/stddev/quantile aggregators.

  journal.c — 57 tests in NEW test_journal.c covering validate_*
    (clean log, empty, no-file, bad-tail variants, oversize, growing),
    replay_* (compressed frame, restricted flag, eval error),
    open_* (replays existing log, qdb-corrupt, log-is-directory),
    write/sync/roll/snapshot.  Registered journal_entries in main.c.

  serde.c — 24 tests in test_store.c for round-trip coverage of
    every atom type, every vector type, TABLE/DICT, function (UNARY/
    BINARY/VARY) types, RAY_LAMBDA, error roundtrip, large null vec
    (external nullmap), de error paths (truncated header, bad type),
    LIST with NULL sentinel, F32 atom (in fix commit).

Tests ~1900 → ~1970 passing (1 pre-existing skip).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| File                | Before  | After    | Tests |
|---------------------|---------|----------|-------|
| src/lang/compile.c  | 72.2%   |  100%    | +34 (new test_compile.c) |
| src/ops/pivot.c     | 71.2%   | 84.2%    | +14 rfl (new pivot_coverage.rfl) |
| src/ops/sort.c      | 71.9%   | 84.6%    | +rfl (new test_sort.c, sort_coverage2.rfl) |
| src/ops/hash.h      | 73.1%   | 73.1%    | +33 (structural ceiling — see below) |
| src/ops/internal.h  | 63.4%   | 83.3%    | +rfl (new internal_coverage.rfl) |

src/ops/hash.h stays at 73.1% — structural ceiling on Linux x86_64
with __SIZEOF_INT128__ and little-endian.  The remaining 32 missed
lines are MSVC + 32-bit fallback + big-endian byte-swap branches
that are compile-time-dead on the active platform but still recorded
in the source coverage map.  Tests verify all reachable branches
(branches go from 92.86% → 100%, regions stay 100%).

No src/ changes.  No static-expose, no mocks.  New files: test_compile.c,
test_sort.c, test_hash.c, plus 3 new rfl files.

Highlights per file:

  compile.c — 34 tests in NEW test_compile.c covering OP_CALLD set/
    self-recursion, if-no-else, do/let/cond/and/or/try, lambda inlining
    + arity errors, RAY_LAMBDA OP_CALLF, const-pool grow >16,
    OP_RESOLVE_W (sym idx>=256), code-buffer grow >256 bytes, default
    switch case, const dedup.  100% lines.

  pivot.c — 14 sections in NEW test/rfl/ops/pivot_coverage.rfl
    covering exec_if I32/BOOL/TIMESTAMP/I16 outputs, exec_pivot F64
    MIN/MAX/FIRST/LAST, snprintf path variants for pivot column
    names (I64/BOOL/F64/DATE/TIME/TIMESTAMP/U8), F64 index column
    (ray_hash_f64), pv_cap realloc (>64 distinct pivot values),
    ix_cap realloc (>256 distinct rows), HT collision linear probe.

  sort.c — radix paths for I32/I16/U8, comparison paths for STR/SYM/
    GUID, NULL-aware sort, multi-key composite, IEEE NaN F64 (line 968,
    8 lines previously unreachable).  Files: test_sort.c +
    test/rfl/sort/sort_coverage2.rfl.

  hash.h — 33 tests in NEW test_hash.c covering ray_hash_bytes len=0/
    1/2/3/4/8/16/17/32/47/48/96/100, ray_hash_i64 (zero/INT64_MIN/
    INT64_MAX), ray_hash_f64 (+0.0/-0.0 normalization), ray_hash_combine
    + cross-function sanity.  Branches 92.86% → 100%.  Lines stay at
    73.1% because of platform-dead branches noted above.

  internal.h — 2 new sections in NEW internal_coverage.rfl plus
    additions to test_exec.c covering 65-partition heap path
    (parted_gather_str_rows scratch_alloc), parallel STDDEV with F64
    keys + singleton groups (par_set_null inline→EXT promotion +
    par_finalize_nulls).  Note: the 65-partition section was dropped
    from the rfl (initial test had a `.db.parted.get` issue with
    65 segments specifically; the parallel-STDDEV section remains and
    delivers the bulk of the gap closure).

Tests ~1970 → 2066 passing (1 pre-existing skip).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
par_prepare_nullmap is supposed to pre-allocate the external nullmap
so parallel workers (par_set_null) can set bits race-free.  It probed
at idx=0, but ray_vec_set_null_checked(vec, 0, true) stays in the
inline-bitmap path (idx<128 fits the 16-byte inline nullmap) — so the
external nullmap was never actually allocated.

Subsequent parallel par_set_null calls at idx>=128 then tried to
lazy-allocate ext_nullmap concurrently, racing on the attrs check vs
ext_nullmap pointer write.  ASAN crashed with SEGV inside ray_data
during the rayforce/ops/internal_coverage rfl test (parallel STDDEV
with F64 keys + singleton groups, vec->len=200).

Fix: probe at vec->len-1 (always >=128 since the function returns
early for len<=128).  This forces the inline→ext promotion in
ray_vec_set_null, populating ext_nullmap before parallel work begins.

Bug surfaced by S5 internal.h coverage agent; now covered by section
9 of test/rfl/ops/internal_coverage.rfl which previously crashed CI.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
tcsetattr on a PTY slave blocks indefinitely on Darwin when the
master fd has unread bytes — the test's progress callback writes
ANSI sequences to stderr (redirected to the PTY slave), nobody
drains master_fd, and ray_term_destroy's terminal-attrs restore
on shutdown hangs.  Linux's tty layer doesn't show this — same
sequence completes immediately.  CI on macos-latest hung at this
test (#1568+1) for 30+ minutes before this change.

Skipping on __APPLE__ keeps Linux coverage intact (the test was
designed to exercise progress_term_cols's ws_col<=10 fallback).
The macOS-specific draining/non-blocking-master fix is a separate
follow-up if we need the coverage there too.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The test's purpose is to exercise the SIGINT-during-eval recovery
path in repl.c — not to assert a specific child exit code.  Linux
returns 0 cleanly; macOS under ASan can deliver SIGBUS to the child
during an interrupted syscall, producing rc=-7 or similar that the
prior `rc == 0 || -1 || -2` allowlist rejected.

The coverage targeted by this test (repl_read sz==-2 SIGINT branch +
ray_eval interrupt path) is recorded as soon as the child runs the
eval; what happens at child shutdown is environment-specific and
not the assertion's concern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the macOS-skip on progress_bar/in_parent with the real fix:
ray_term_destroy uses tcsetattr(TCSAFLUSH) which on macOS blocks
until the slave's output buffer drains to master.  The progress
callback writes ANSI escapes to stderr (= PTY slave); without
draining master, the kernel buffer never empties and TCSAFLUSH
hangs.  Linux's TTY layer is more lenient here.

Fix: set master_fd O_NONBLOCK and drain via read() loop right
before ray_repl_destroy.  Test now exercises the same code path on
both platforms.

Also restores the rc assertion on sigint_during_eval — when the
runner hits an unexpected child exit code we want CI to surface it
(rather than silently passing).  If CI shows a bad rc, that's a
real signal-handling regression worth investigating, not a test bug.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
(sum (til 50000000)) allocated 400MB under ASan on macOS GitHub
Actions runner (7GB Apple Silicon, ASan ~2x memory).  Mid-allocation
SIGINT interaction left the heap in a state ASan flagged as an
error and trapped via __builtin_trap (rc=-5 = SIGTRAP).  Linux
runners had more headroom and didn't trip ASan.

Switch to a deep recursive lambda that exercises the same SIGINT
recovery path without the huge allocation footprint.  The eval
errors out via RAY_EVAL_MAX_DEPTH well before SIGINT actually
arrives — but that matches the real behavior on Linux too (where
the test took 1.2ms, well under the parent's 400ms pre-SIGINT
wait): the test really exercises "SIGINT delivered to a healthy
REPL prompt", not interrupt during a live computation.  Either
way, the rc=0 assertion is restored and macOS is no longer ASan-
trapping.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces fixed sleep(400ms) with observable synchronisation: the
eval expression starts with (println "EVALSTART"), and the parent
reads master_fd until that marker appears before delivering SIGINT.

This makes the test machine-independent — it doesn't matter whether
the runner is fast or slow, has 8GB or 64GB of RAM.  The previous
test "tested" SIGINT-during-eval but on Linux CI eval finished in
1.2ms, leaving 398ms of pure waiting before SIGINT — so it actually
exercised SIGINT-at-idle-prompt.

Also drops the eval expression from (sum (til 50000000)) (400MB
allocation, ASan-trapped on macOS) to (sum (til 100000)) (800KB).
Even small allocations let the marker fire before the eval finishes,
because println is the FIRST thing in the do-form — the rest is
guaranteed to be in flight when SIGINT arrives.

Test design principle: portable tests should sync on observable
state (output bytes, atomic flags, file existence), not absolute
sleeps.  Sleeps embed assumptions about CPU speed and memory size
that don't generalise across machines.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@singaraiona singaraiona merged commit c1c61fb into master May 4, 2026
4 checks passed
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.

2 participants