summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-06-14 15:43:12 +0300
committerPaul Buetow <paul@buetow.org>2026-06-14 15:43:12 +0300
commitfe4e83cd5159809a70f24301fcabc08779b51706 (patch)
tree29b71066a22db0630ed37ced8e8710e8175b83f7 /tests
parent3eeef1e0f68d750e7817748180a2007095e99ef3 (diff)
6m0 clean up BASH_ENV context tempfile via traps on interrupt
source_template_file created its context tempfile with mktemp but only ran rm -f on the success and explicit-failure paths. When a render was interrupted by a signal (terminate_active_generation SIGTERMs the backgrounded render subtree on Ctrl-C/abort) the tempfile leaked. Register cleanup in exactly one place, inline in source_template_file's body: - A RETURN trap covers normal and error returns and clears all of these traps (including itself) so it cannot linger and fire on an enclosing function's return against the out-of-scope context_file local (set -u). The trap must be set in the function body, not a helper: without functrace a RETURN trap is not function-scoped and would fire when a helper returns, deleting the file before the render runs. - INT/TERM/HUP handlers remove the file, clear the traps and re-raise so the process still exits with the signal's default disposition. PIPE is not trapped (the internal context-build pipeline emits legitimate SIGPIPE); SIGKILL is untrappable, leaving only the OS-reaped KILL-escalation case. The redundant rm -f calls are removed. Adds test_template_interrupt_removes_context_file, which runs an in-flight render and SIGTERMs it, asserting the context file is gone. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to 'tests')
-rwxr-xr-xtests/cli.sh95
1 files changed, 95 insertions, 0 deletions
diff --git a/tests/cli.sh b/tests/cli.sh
index b8b56f6..9bd399b 100755
--- a/tests/cli.sh
+++ b/tests/cli.sh
@@ -4090,6 +4090,98 @@ BASH
test::teardown
}
+test_template_interrupt_removes_context_file() {
+ local context_file
+ local fake_bin
+ local started_file
+ local template_dir
+ local child_pid
+ local -i status=0
+
+ test::setup
+ fake_bin="$TEST_TMPDIR/bin"
+ template_dir="$TEST_TMPDIR/templates"
+ context_file="$TEST_TMPDIR/template-context"
+ started_file="$TEST_TMPDIR/template-started"
+ mkdir -p "$fake_bin" "$template_dir" "$TEST_TMPDIR/dist"
+ {
+ printf '#!/usr/bin/env bash\n'
+ # shellcheck disable=SC2016
+ printf 'printf %%s\\\\n \"$SHURIKEN_FAKE_CONTEXT_FILE\"\n'
+ } > "$fake_bin/mktemp"
+ chmod 0755 "$fake_bin/mktemp"
+ # The rendered template signals it started, then blocks so the render is
+ # genuinely in flight (context already built, env -i bash running) when we
+ # deliver the interrupt. This exercises the signal traps, not the RETURN
+ # trap: a plain RETURN trap does not fire when a signal kills the shell.
+ {
+ printf 'printf rendered > %q\n' "$started_file"
+ printf 'sleep 30\n'
+ } > "$template_dir/preview.tmpl"
+
+ # Run a full template render in a child shell we can signal mid-render. Going
+ # through the template entry point populates a valid render context so the
+ # context build succeeds and the render actually reaches the blocking sleep.
+ bash -euo pipefail -s \
+ "$TEST_SHURIKEN" \
+ "$fake_bin" \
+ "$template_dir" \
+ "$TEST_TMPDIR/dist" \
+ "$context_file" \
+ <<'BASH' &
+shuriken="$1"; shift
+fake_bin="$1"; shift
+template_dir="$1"; shift
+dist_dir="$1"; shift
+context_file="$1"; shift
+
+# shellcheck source=/dev/null
+source <(sed '$d' "$shuriken")
+
+PATH="$fake_bin:$PATH"
+SHURIKEN_FAKE_CONTEXT_FILE="$context_file"
+export SHURIKEN_FAKE_CONTEXT_FILE
+DIST_DIR="$dist_dir"
+TEMPLATE_DIR="$template_dir"
+TITLE='Template interrupt cleanup'
+HEIGHT=''
+THUMBHEIGHT=30
+MAXPREVIEWS=40
+ORIGINAL_BASEPATH=''
+TARBALL_INCLUDE=no
+SHURIKEN_OUTPUT_MODE=quiet
+apply_config_defaults
+
+template preview out.html \
+ animation_class '' \
+ backhref '#' \
+ html_dir . \
+ page_num 1 \
+ photo photo.jpg \
+ preview_num 1 \
+ thumbs_dir thumbs
+BASH
+ child_pid=$!
+
+ # Wait for the render to begin, then interrupt it with SIGTERM.
+ while [ ! -f "$started_file" ] && kill -0 "$child_pid" 2>/dev/null; do
+ sleep 0.05
+ done
+ kill -TERM "$child_pid" 2>/dev/null || true
+ set +e
+ wait "$child_pid"
+ status=$?
+ set -e
+
+ if (( status == 0 )); then
+ printf 'FAIL: expected interrupted render to exit non-zero\n' >&2
+ exit 1
+ fi
+
+ test::assert_path_absent "$context_file"
+ test::teardown
+}
+
test_generate_swap_failure_restores_dist() {
local config_file
local fake_bin
@@ -5034,6 +5126,9 @@ main() {
'template setup failure fails status-tested render' \
test_template_setup_failure_fails_status_tested_render
test::run_case \
+ 'template interrupt removes context file' \
+ test_template_interrupt_removes_context_file
+ test::run_case \
'--generate swap failure restores final dist' \
test_generate_swap_failure_restores_dist
test::run_case \