myxce/.kiro/specs/xce-test-rc-file/tasks.md
Rongsong Shen d258aa3d91
test(xce-test-rc-file): task 3.1 end-to-end rc-file discovery/loading/--norc
Adds test_rcfile_integration.sh, mirroring clean_failover_preservation.sh's
shell-driven black-box pattern against the real built xce_test binary:

- a valid rc file's set masters override takes effect in both a piped run
  and a genuine pty-driven interactive run, before the normal session begins
- --norc leaves the default masters value untouched with the same rc file
  present

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-26 16:37:04 +00:00

7 KiB

Implementation Plan

  • 1. Foundation: rc-file path resolution and line-loading capability

  • 1.1 Implement rc-file path resolution following XDG precedence, with unit tests

    • Resolve the rc-file location using the XDG config variable when it is set and non-empty
    • Fall back to the home-directory XDG default when that variable is unset or empty, and to "no resolvable path" when neither variable is usable
    • Wire the new module and its test file into both existing build targets that need it, so both compile and the new unit tests run under the existing unit test suite
    • Save each of $HOME/$XDG_CONFIG_HOME's prior state (unset vs. empty vs. a value) before any test case mutates it, and restore that exact prior state afterward, since controller_unit_test runs 80+ test files sequentially in one process and a leaked env var would silently affect unrelated tests
    • Observable: a unit test run demonstrates the correct location is chosen for each of the three environment combinations above, and a subsequent unrelated test's own $HOME/$XDG_CONFIG_HOME reads are unaffected
    • Requirements: 1.1
  • 1.2 Implement rc-file line-loading with a pluggable command runner, with unit tests

    • Read the rc file line by line using getline() (no fixed-size buffer), invoking a caller-supplied runner once per line with the trailing newline removed
    • Treat a missing rc file as a no-op success (no error), and a present-but-unreadable rc file as a non-fatal warning that still allows startup to proceed
    • Stop reading immediately the first time the runner signals a stop, and propagate that signal to the caller without processing any remaining lines
    • Observable: a unit test run demonstrates every line is processed when the runner keeps succeeding, remaining lines are skipped the first time the runner signals a stop, the runner is never invoked when the file is absent, and a line well beyond a typical small fixed-buffer size (e.g. >4KB) reaches the runner intact and unsplit
    • Requirements: 1.2, 1.3
  • 2. Core: xce_test startup integration

  • 2.1 Add a flag that skips rc-file loading for a single invocation

    • Register the flag with xce_test's existing command-line option parsing so it is documented in xce_test's own help output
    • When the flag is given, record that rc-file loading should be skipped for this run
    • Observable: xce_test's help output lists the new flag with a description, and invoking xce_test with the flag parses successfully and records the skip state (verified directly, independent of task 2.2's wiring)
    • Requirements: 2.1, 2.2
    • Boundary: test.c::options[]/parse_opt
  • 2.2 Wire rc-file loading into xce_test startup for both interactive and scripted sessions

    • After xce_test determines whether the session is interactive or scripted and before it reads the first line of its normal session, resolve and load the rc file unless the skip flag was given, using the session's existing command-execution path as the per-line runner
    • When rc-file loading signals a stop, exit using the session's existing pass/fail exit-code convention without entering the normal session loop
    • Observable: a valid rc file's settings/context are visible once the normal session begins, in both a piped run and an interactive run; the same is confirmed absent when the skip flag is given
    • Requirements: 1.4, 1.5, 2.3
    • Depends: 1.1, 1.2
    • Boundary: test.c::main
  • 3. Validation: end-to-end behavior against the built binary

  • 3.1 Verify rc-file discovery, loading, and skip-flag behavior end-to-end

    • Scenario: a valid rc file's settings/context take effect in both a piped run and an interactive run
    • Scenario: the skip flag leaves default settings untouched even with a valid rc file present
    • Observable: an integration test run demonstrates both scenarios pass against the real built binary
    • Requirements: 1.1, 1.2, 1.4, 1.5, 2.1, 2.3
    • Depends: 2.2
  • 3.2 Verify rc-file failure handling matches the session's existing abort/continue behavior end-to-end

    • Scenario: a failing rc-file command in scripted/piped mode aborts before any of the piped script's own commands run, and the process exits with a failure status
    • Scenario: the same failing rc-file command in an interactive session still reaches the normal prompt afterward
    • Observable: an integration test run demonstrates both scenarios pass against the real built binary
    • Requirements: 3.1, 3.2, 3.3, 3.4
    • Depends: 2.2
  • 3.3 Confirm existing xce_test automation is unaffected when no rc file is present

    • Run the existing HA soak and resource-verification scripts (the known live callers of xce_test today) in an environment with no rc file at the resolved location
    • Compare their pass/fail result against their pre-feature baseline behavior
    • Observable: the existing scripts' outcomes are unchanged, demonstrating this feature introduces no regression for callers that know nothing about it
    • Requirements: 1.3
    • Depends: 2.2

Implementation Notes

  • Task 1.1: controller_unit_test can silently die with exit code 1/2 before reaching later-registered test groups if /tmp fills up — a pre-existing, already-tracked leak (bug-924) where test fixtures under /tmp/xce_* aren't cleaned up across many prior sessions' runs can exhaust disk and make mkdtemp() in unrelated tests (e.g. test_repl_apply.c) exit(1) on ENOSPC. If a later task's controller_unit_test run dies early or shows an unexplained failure in an unrelated file, check df -h /tmp before assuming a regression in this task's own code.
  • Task 1.1: the 2 pre-existing test_config_repl.c failures (test_conf_trim_keeps_only_n_versions, test_conf_get_saved_tag_no_config) are the tracked bug-1561 overlayfs-artifact regression, unrelated to this spec — expect them to keep appearing in every subsequent task's controller_unit_test run; do not treat them as a new regression.
  • Task 1.2: fopen() succeeds on a directory path on this platform's glibc — the EISDIR failure only surfaces on the first getline() call (errno=EISDIR, ferror(f) set). A naive if (!f) open-failure check misses this; a post-loop ferror(f) check is required to catch it (see rcfile_load(), bug-1716).
  • Task 1.2: an ASAN heap-use-after-free was observed ONCE in an ad-hoc standalone controller_unit_test run (commit_resolve_pending/fsync_worker/tap_teardown in test_apply_pump.c) — did not reproduce on a second run or in the canonical xce-build.sh run. Unrelated to this spec's files; logged as bug-1717 (unreproduced flake) for tracking. If it recurs on a later task's run, capture the full ASAN report before assuming it's this spec's fault.
  • Task 3.1: test_rcfile_integration.sh's per-test scratch $HOME dirs are cleaned via explicit rm -rf in each test function's normal flow, but there's no script-level trap ... EXIT INT TERM (unlike clean_failover_preservation.sh's global trap) — an interrupted run could leak a small scratch dir. Verified low-impact (small footprint, no leak in normal runs) but worth adding a trap if this script is extended later.