2.7 KiB
name, description
| name | description |
|---|---|
| sigterm-kill-test-stub | Pin "code kills its child process" behavior with a bash stub player: make the stub SIGTERM-reactive via sleep&wait, log KILLED via trap; diagnose deferred-trap failures. |
Use when a test must prove production code sends SIGTERM to a spawned child (player, worker, server stub) and the test asserts the kill happened — or when such a test mysteriously fails with KILLED missing and wall time equal to the stub's sleep duration.
The trap-during-sleep gotcha
A bash stub that sleeps in the foreground does NOT run its TERM trap when signalled: bash defers traps until the current foreground command completes. The stub dies (or logs) only after the sleep finishes — long after the test read the evidence file.
Symptoms:
- Assertion on a
KILLEDmarker line fails although production code demonstrably callschild.kill("SIGTERM"). - Test wall time equals the stub's full sleep (the kill "had no effect").
Fix: reactive stub skeleton
#!/usr/bin/env bash
printf '%s\n' "$*" >> "$STUB_LOG" # call record
trap 'printf "KILLED\n" >> "$STUB_LOG"; exit 143' TERM # BEFORE the sleep
for arg in "$@"; do case "$arg" in *.wav) cp "$arg" "$COPY" ;; esac; done
sleep "${STUB_HOLD:-0.1}" & # background sleep…
wait $! # …wait is interruptible
printf 'DONE\n' >> "$STUB_LOG"
sleep N & wait $!is the idiom: a signal makes builtinwaitreturn immediately, the trap runs at once,exitinside the trap prevents fallthrough toDONE.- Tune duration per test via the env knob (
STUB_HOLD=5) so the child is provably still alive when the kill is triggered. - Put the trap BEFORE any long command; a trap registered after the sleep never guards it.
Harness wiring (black-box driver pattern)
- Scenario: start the long work → settle (>spawn latency, <hold) → trigger the killer (shutdown event, second invocation,
/stop) → settle → read log. - Expose the stub log lines on the driver's stdout (
LOG <line>per line) so tests grep output, not files: black-box seam stays output-only. - Assert
KILLEDpresent; for interrupt-semantics also count (kills -eq 1) and total call lines (PLAYS 3= call, KILLED, call).
Orphan hygiene
Second child still sleeping when the driver exits: spawn with stdio: "ignore" so the orphan holds no capture pipe — it SIGPIPEs/quiets at teardown; do not lengthen tests waiting for it.
Verify before trusting
Run the pair separately: kill-tests green while a byte-equality test (cp of the payload before sleep) stays green — the reactive rewrite must not drop the copy step (it sits between trap and sleep).