Skip to content

test: add CLI end-to-end suite and a real external-metadata provider - #17

Merged
skipbit merged 3 commits into
mainfrom
feature/cli-e2e-tests
Jul 16, 2026
Merged

test: add CLI end-to-end suite and a real external-metadata provider#17
skipbit merged 3 commits into
mainfrom
feature/cli-e2e-tests

Conversation

@skipbit

@skipbit skipbit commented Jul 16, 2026

Copy link
Copy Markdown
Owner

Summary

Adds an end-to-end test suite that drives the built scrap binary as a real
subprocess, and replaces the placeholder external-metadata provider with a real
one that probes scrap-* executables.

Changes

  • MetadataProtocolProvider — a concrete ExternalMetadataProvider that
    runs <cmd> --scrap-metadata (falling back to <cmd> --help) via posix_spawn
    with a bounded timeout and extracts a plain first-line description. It is wired
    into the composition root in place of the previous no-op provider, and the
    now-unused NullMetadataProvider is removed. Structured JSON/options metadata
    is not part of the protocol yet.
  • scrap_e2e — a black-box GoogleTest target that spawns the built binary
    (fork/execve) with a controlled environment and asserts on
    argv → exit code / stdout / stderr across the built-in, external, and project
    command paths (labelled e2e, so ctest -L e2e selects them).
  • Direct unit tests for the provider (protocol, --help fallback, timeout,
    capture-cap and closed-stdout hang bounding, early-stdout-close, missing/empty).

Notes

  • The metadata probe reaps its child with a deadline-bounded loop so a slow or
    misbehaving scrap-* can never hang the CLI, while a provider that closes its
    stdout before exiting still has its captured output preserved.
  • environ is reached via _NSGetEnviron() on macOS.
  • Verified on GCC 13 and Clang 18 (libc++): build + 111 ctest green, clang-tidy
    and clang-format clean.

skipbit added 3 commits July 16, 2026 18:18
Add a black-box end-to-end test target (scrap_e2e) that spawns the built
scrap binary as a subprocess and asserts on argv -> exit code / stdout /
stderr across the built-in, external, and project command paths.

Implement MetadataProtocolProvider, a concrete ExternalMetadataProvider
that probes `<cmd> --scrap-metadata` (falling back to `<cmd> --help`) via
posix_spawn with a subprocess timeout, extracting a plain first-line
description. Wire it into the composition root in place of the previous
no-op provider and drop the now-unused NullMetadataProvider.

Add direct unit tests for the provider (protocol, help fallback, timeout,
missing/empty output).
Guarantee the metadata probe's timeout by unconditionally SIGKILLing the
child's process group before the blocking waitpid, instead of only on the
drain-timeout path. Capture-cap, EOF-while-still-running (e.g. a child that
runs `exec 1>&-; sleep`), and drain errors previously fell through to an
unbounded waitpid, letting a single misbehaving scrap-* freeze `scrap --help`.
Killing an already-exited child is a harmless no-op on its zombie, so the
happy-path exit status is preserved and WIFEXITED alone distinguishes a
normal exit from a forced kill.

Declare environ via <crt_externs.h>/_NSGetEnviron() on Apple, where
<unistd.h> does not provide it, so the file builds on macOS.

Wrap the pipe descriptors in a small RAII UniqueFd so every exit path closes
them exactly once. Add regression tests for the capture-cap and closed-stdout
hang variants (both must return within the injected timeout). Isolate the
end-to-end metadata test to the --scrap-metadata path with a disjoint marker,
create the e2e fixture dir atomically with mkdtemp, and label the e2e tests.
The unconditional SIGKILL before waitpid could discard a valid child's exit
status: EOF on the pipe only proves stdout is closed, not that the child
exited, so a probe that writes its line, closes stdout, then does brief work
before exiting 0 would be killed mid-flight and reported as WIFSIGNALED,
dropping the already-captured output.

Reap with a bounded loop instead (extracted into reapBounded): a non-blocking
waitpid reaps an already-exited child at once with its real status; a child
still running is polled until the deadline so one that closed stdout early but
exits shortly after is still reaped normally; only a child still alive at the
deadline is SIGKILLed. Add a regression test for the early-stdout-close case.
@skipbit
skipbit merged commit 89bd0af into main Jul 16, 2026
6 checks passed
@skipbit
skipbit deleted the feature/cli-e2e-tests branch July 16, 2026 10:46
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.

1 participant