Gate the main test suite on test-build; add build-results; require ON_ERROR_STOP in test/install - #109
Conversation
…_ERROR_STOP in test/install test-build had no real dependency edge on installcheck, so the main suite (test/install + test/sql) actually ran before test-build's own sanity check in the default config, and raced with it under `make -j`. Add an explicit `installcheck: test-build` edge (guarded to avoid recursing into test-build's own nested installcheck invocation) so a broken build blocks the main suite entirely -- its results would be meaningless otherwise. Add `make build-results` to refresh test/build/expected/*.out from the last test-build run, mirroring `make results` for the main suite. Refuses to bless any file whose actual output contains an ERROR: line. test/install/*.sql files never get a real diff -- their actual output overwrites their expected output in place, so a content difference can never fail the build. Add check-test-install-error-stop, enforcing that every test/install/*.sql file sets ON_ERROR_STOP (directly or via test/pgxntool/psql.sql), since that's the only thing that still turns a hard SQL error into a build failure. test/install/*.out is now gitignored since it's rewritten by every run and never meaningfully compared. Fixes Postgres-Extensions#108. Fixes Postgres-Extensions#97. Related changes in pgxntool-test: - Template's test/install file now sets ON_ERROR_STOP; its .out is no longer committed - Test coverage for the new ordering edge, build-results, and check-test-install-error-stop Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
pg_regress hardcodes its psql invocation with no flag passthrough, and test/install shares that single invocation with test/sql -- so even a --launcher hack to inject the flag would break test/sql files that intentionally trigger and recover from an error.
| # changed column, whatever) will NEVER fail this way, no matter how it | ||
| # changes -- see issue #97. | ||
| # | ||
| # The only thing that still fails the build is psql's own exit code: a |
There was a problem hiding this comment.
s/still fails/forces the build to fail/
| .PHONY: build-results | ||
| build-results: install | ||
| @$(PGXNTOOL_DIR)/run-test-build.sh $(TESTDIR) | ||
| $(MAKE) -C . _PGXNTOOL_TEST_BUILD_ACTIVE=yes REGRESS="$(TEST_BUILD_REGRESS)" REGRESS_OPTS="--inputdir=$(TESTDIR)/build --outputdir=$(TESTDIR)/build" installcheck |
There was a problem hiding this comment.
Why's this needed? Isn't this essentially what run-test-build.sh does already?
There was a problem hiding this comment.
Good question — added a comment explaining it in e444b08. build-results can't just depend on test-build: test-build's own recipe exits 1 on any regression.diffs, which is exactly the diff build-results exists to accept as the new baseline. So it re-runs run-test-build.sh + the installcheck invocation directly, without that check.
There was a problem hiding this comment.
Uh, so what if it flags the failure? As long as we know that's why it errored we can ignore it; it already created the results/* files that we're expecting.
There was a problem hiding this comment.
Simplified build-results in 184e8c1: it now runs -$(MAKE) -C . test-build (the leading - ignores test-build's own exit status) instead of duplicating run-test-build.sh + the installcheck invocation. By the time test-build's regression.diffs check fires, the actual output is already on disk, so there's nothing left to re-run. Also dropped the now-redundant install prerequisite on build-results, since test-build already carries that dependency.
Verified both paths in pgxntool-test's make-test.bats against this branch (33/33 pass): the stale-expected-output refresh (test-build fails internally, build-results still blesses and exits 0) and the ERROR-skip case (build-results still refuses to bless the errored file and exits 1, while still refreshing the clean one).
There was a problem hiding this comment.
Needs a comment on what - is doing as well as why
There was a problem hiding this comment.
Claude here: this is now stale — commit 6e5f9f3 (pushed as part of 43d8dac) rewrote build-results to no longer use a bare - prefix. It now does $(MAKE) -C . test-build || test -s $(TESTDIR)/build/regression.diffs || { ...; exit 1; } instead, with its own comment block explaining the whole approach (clearing regression.diffs first, treating only a non-empty diff as "safe to bless"). Nothing to change here.
Cut internal pg_regress plumbing detail from the ON_ERROR_STOP comment block down to the actual design reason (forcing it globally would also apply to test/sql, breaking deliberate mid-file error tests) and drop the address-the-reader aside. Trim a similar over-explanation from the HISTORY.asc test-build entry. Note why build-results can't just depend on test-build (it aborts on the very diff build-results exists to accept).
…TOP (#79) Related pgxntool PR: Postgres-Extensions/pgxntool#109 Template's `test/install` file now sets `ON_ERROR_STOP` and stops committing its self-overwriting `.out` file, per pgxntool's new `check-test-install-error-stop` check. Test coverage added: - `installcheck:test-build` ordering edge, via `make -n` dry-run (issue #108) - `make build-results`: refreshes clean files, skips and reports files whose actual output contains `ERROR:`, and still exits non-zero on a partial skip - `check-test-install-error-stop`: script-level decision logic (direct `ON_ERROR_STOP`, via `psql.sql`, missing) and Makefile wiring (recipe args, disable via `PGXNTOOL_ENABLE_TEST_INSTALL_ERROR_STOP_CHECK`, exit-status propagation) Covers pgxntool issues #108 and #97 (fixed by the paired pgxntool PR above, not by this one). Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
| @@ -1,3 +1,31 @@ | |||
| STABLE | |||
| ------ | |||
| == `test-build` now gates the main test suite | |||
There was a problem hiding this comment.
Minor fix, not worth mentioning
There was a problem hiding this comment.
Not sure which specific wording/detail this is flagging — I compared this header against the file's own conventions (underline length, backtick usage, blank-line spacing between entries) and against the PR body's parallel section and didn't find a concrete defect to fix. Could you point at the specific text? Flagging back to the coordinator in the meantime rather than guessing at an edit.
There was a problem hiding this comment.
Literally that test-build gates the main suite!
There was a problem hiding this comment.
Claude here: also stale — this entry was removed entirely in 9b61026, per your later explicit instruction on this same thread ("that's one of the main points of the PR, but it's essentially user-invisible... doesn't warrant a mention in HISTORY"). Current HISTORY.asc's STABLE section no longer has this line at all.
| .PHONY: build-results | ||
| build-results: install | ||
| @$(PGXNTOOL_DIR)/run-test-build.sh $(TESTDIR) | ||
| $(MAKE) -C . _PGXNTOOL_TEST_BUILD_ACTIVE=yes REGRESS="$(TEST_BUILD_REGRESS)" REGRESS_OPTS="--inputdir=$(TESTDIR)/build --outputdir=$(TESTDIR)/build" installcheck |
There was a problem hiding this comment.
Uh, so what if it flags the failure? As long as we know that's why it errored we can ignore it; it already created the results/* files that we're expecting.
| $(MAKE) -C . _PGXNTOOL_TEST_BUILD_ACTIVE=yes REGRESS="$(TEST_BUILD_REGRESS)" REGRESS_OPTS="--inputdir=$(TESTDIR)/build --outputdir=$(TESTDIR)/build" installcheck | ||
| @mkdir -p $(TESTDIR)/build/expected | ||
| @skipped=0; \ | ||
| for f in $(TESTDIR)/build/results/*.out; do \ |
There was a problem hiding this comment.
This will leave behind files that no longer exist, no? Add a test where we rename the extension .sql file to make sure we handle that correctly.
There was a problem hiding this comment.
Checked this: it doesn't leave orphans unnoticed. test/bin/check-stale-expected.sh already covers test/build/expected the same way it covers the main suite's test/expected (check_pair "$testdir/build" "$testdir/build/expected"), and check-stale-expected runs as part of installcheck's dependency chain, which this PR now makes depend on test-build. So a renamed/removed test/build/*.sql file's orphaned .out gets flagged on the next make test/installcheck — same as make results already leaves (and check-stale-expected already catches) for the main suite's own test/expected. build-results itself isn't meant to be the thing that catches this, any more than make results is.
The rename-based regression test you're asking for is a good idea, but it belongs in pgxntool-test (this repo has no test infra), and it's really a check-stale-expected test, not a build-results one — flagging back to the coordinator to open that separately rather than pulling a cross-repo PR into this one.
test-build already writes the actual output build-results needs before its own regression.diffs check exits 1 -- ignoring that exit status (leading `-`) avoids re-running run-test-build.sh + installcheck a second time.
| for f in "$install_dir"/*.sql; do | ||
| [ -f "$f" ] || continue | ||
|
|
||
| if grep -q 'ON_ERROR_STOP' "$f"; then | ||
| continue | ||
| fi | ||
| if grep -qE '\\ir? +.*psql\.sql' "$f"; then | ||
| continue | ||
| fi | ||
|
|
||
| missing+=("$f") | ||
| done |
There was a problem hiding this comment.
The ON_ERROR_STOP detection here is too loose to reliably provide the protection this script exists for.
- Line 36,
grep -q 'ON_ERROR_STOP', is a bare substring match anywhere in the file — no anchor, no check of the\setvalue, no exclusion of\unset. A file containing\set ON_ERROR_STOP off,\unset ON_ERROR_STOP, or even just a comment like-- ON_ERROR_STOP not needed herepasses this check while providing none of the protection it's meant to guarantee. Given the PR's own framing — ON_ERROR_STOP is "the only thing that still turns a hard SQL error into a build failure" fortest/install— a check that a comment satisfies converts "unprotected" into "reported protected." - Line 39,
grep -qE '\\ir? +.*psql\.sql', has the same comment-blindness (-- see \i test/pgxntool/psql.sqlmatches), and.*psql\.sqlmatches any path merely ending inpsql.sql(e.g. a project's own unrelatedmypsql.sql). In the other direction, the literal-space separator misses a tab-separated\i<TAB>..., and\\ir? +doesn't match psql's long forms\include/\include_relative— nor does it match indirect inclusion (e.g.\i test/pgxntool/tap_setup.sql, which itself sourcespsql.sqland is explicitly suggested elsewhere in this PR's new README text as a way to add pgTap assertions to an install file).
Both directions are real: files that silently disable/never had the protection can pass, and some genuinely-protected files can be rejected. Worth tightening line 36 to require an actual enabling \set (rejecting off/0/\unset) and anchoring/making both greps comment-aware, since a silent false-pass here is worse than a false-fail (which at least fails loudly and has PGXNTOOL_ENABLE_TEST_INSTALL_ERROR_STOP_CHECK=no as an escape hatch).
There was a problem hiding this comment.
Claude here: this finding is valid — confirmed both false-pass cases (a comment or \unset/\set ON_ERROR_STOP off satisfying the bare substring match) and the comment-blind/path-suffix/indirect-inclusion gaps in the \i/\ir check still exist in the current script.
How far to fix this is a real scope question (tightening the direct-match cases vs. also tracing indirect inclusion chains like \i tap_setup.sql that itself sources psql.sql), so I'm flagging it to the coordinator for a decision rather than picking a scope myself. Will follow up here once that's settled.
There was a problem hiding this comment.
We don't want to boil the ocean, but there's absolutely room for improvement here. I don't think it's worth worrying about comments, esp. if you make the tests more specific on what they're looking for. Also, if the script explicitly enables ON_ERROR_STOP (vs just including psql.sql) it's OK if it then turns it back off at some point; presumably the user knows what they're doing in that scenario.
There was a problem hiding this comment.
Claude here: thanks, that settles the scope question I flagged. Checked the current script against this: it already does a bare grep -q 'ON_ERROR_STOP' substring match with no comment-awareness and no requirement that it stay on through EOF — so no change needed to check-test-install-error-stop.sh itself. If you want more specific test coverage of the edge cases (comment-only match, on-then-off), that's pgxntool-test territory (test/standard/check-test-install-error-stop-script.bats) rather than this script — flagging to the coordinator as a possible follow-up there, not blocking this PR.
| # a diff", which is exactly what build-results exists to bless. | ||
| .PHONY: build-results | ||
| build-results: | ||
| -$(MAKE) -C . test-build | ||
| @mkdir -p $(TESTDIR)/build/expected | ||
| @skipped=0; \ | ||
| for f in $(TESTDIR)/build/results/*.out; do \ | ||
| [ -f "$$f" ] || continue; \ | ||
| if grep -q 'ERROR:' "$$f"; then \ | ||
| echo "build-results: skipping $$f (actual output contains ERROR:)" >&2; \ | ||
| echo " If this is intentional, bless it by hand:" >&2; \ | ||
| echo " cp $$f $(TESTDIR)/build/expected/$$(basename "$$f")" >&2; \ | ||
| skipped=1; continue; \ | ||
| fi; \ | ||
| cp "$$f" $(TESTDIR)/build/expected/$$(basename "$$f"); \ | ||
| done; \ | ||
| [ "$$skipped" = 0 ] || exit 1 | ||
| endif |
There was a problem hiding this comment.
build-results can silently bless a stale/previous baseline instead of failing.
The leading - on -$(MAKE) -C . test-build (base.mk#L620) ignores every failure mode of the sub-make, not just "there was a diff to bless" (the only case the surrounding comment justifies). If install fails to compile, run-test-build.sh errors out, or pg_regress never runs, no new test/build/results/*.out is written this run — but the copy loop still runs and copies whatever .out files a previous run left on disk into test/build/expected/, then exits 0 (since skipped only tracks the ERROR: content scan, not whether test-build actually produced fresh output). That directly contradicts this target's own documented contract in README.asc ("...exits non-zero so the skip can't go unnoticed"): here the skip does go unnoticed.
Suggested direction: distinguish "test-build failed only because of a diff to bless" from every other failure mode — e.g. check for test/build/regression.diffs (which pg_regress recreates each run and is what test-build's own recipe already checks for) before treating the sub-make's non-zero exit as benign, rather than blanket-ignoring it with -.
There was a problem hiding this comment.
Claude here: same finding as #3963148463 (this is the duplicate posted against the same line) — fixed in 6e5f9f3 (pushed as part of 43d8dac). See my reply there for the details, including a scratch-extension repro that turned up an extra wrinkle: pg_regress can leave a stale but empty regression.diffs behind on a non-diff failure, so the fix checks for non-empty rather than mere existence.
test-build's new dependency edge on installcheck changes internal build ordering only; it has no user-visible effect worth a changelog entry.
…dering # Conflicts: # HISTORY.asc # README.html
Every failure path called core.setFailed() without ever setting run_tests, so test's if: needs.check-test-pr.outputs.run-tests == 'true' evaluated false and the job showed skipping -- zero test signal on exactly the PRs where a maintainer most needs one to decide whether to override the pairing requirement. Enforcement itself (the setFailed calls) is unchanged; each path now also sets run_tests/pgxntool_test_owner/ pgxntool_test_ref before failing, using the paired PR's own branch when one is known (ambiguous match, label contradiction, poll timeout) or canonical master otherwise. The already-passed and already-failed paired cases are untouched: their own CI result is the real signal already. test's with: block now consumes these outputs instead of a hardcoded Postgres-Extensions/master, so the reusable workflow actually runs against whichever ref check-test-pr decided on.
GitHub Actions implicitly ANDs a needs-based if: with success() unless the condition itself calls a status function, so test stayed skipped even after check-test-pr started setting run_tests=true on its failure paths -- confirmed live on this branch's own CI run. !cancelled() overrides that without also running on an actual workflow cancellation, which bare always() would.
Describe the no-pairing output-setting in terms of what it does, not what changed relative to before. Blank line separates the pgxntool: and pgxntool-test: comment blocks in test's with:, since they're logically distinct sections.
| test: | ||
| needs: check-test-pr | ||
| if: needs.check-test-pr.outputs.run-tests == 'true' | ||
| # !cancelled() (not bare always()): check-test-pr's own failure paths are |
There was a problem hiding this comment.
This comment is really hard to follow. Try to clarify it (without turning it into a novel). One possibly related issue is that maybe there should be a comment for the whole block before this code (maybe what's at 351?)
There was a problem hiding this comment.
Claude here: tightened in 2996d78 to just the two facts that matter (what triggers the run, why !cancelled() over always()), cut the rest. Left it as an inline comment on the if: line rather than folding into the CROSS-REPO REUSABLE WORKFLOW banner at 351 — that banner is specifically about the pinned uses: ref, a different concern from the if: condition's semantics, so merging them felt like it would muddy both rather than clarify either. Happy to move it if you still want it there.
Cut it to the two facts that matter: what triggers the run, and why !cancelled() rather than always().
The leading `-` on the test-build sub-make ignored every failure mode, not just a diff to bless. If install broke, run-test-build.sh errored, or pg_regress never ran, the copy loop still ran and blessed whatever stale test/build/results/*.out a prior run had left on disk, silently violating this target's own documented contract in README.asc. Clear regression.diffs before running test-build and only proceed past a failure when it comes back non-empty. Mere existence isn't enough: pg_regress can leave a stale *empty* diffs file behind from a run that bailed before comparing anything (observed when the target Postgres instance was unreachable), which would otherwise be mistaken for a genuine diff.
Two spots mixed a literal *.out glob with *emphasis* markup in the same sentence; asciidoctor parsed the glob's `*` as an emphasis opener and mangled the rendered HTML, in one case wrapping ~45 words of unrelated prose in a stray <code> span. Wrap the globs in passthrough monospace (`+...+`) so no substitution runs inside them. The test/install WARNING separately had `test/install`'s -- a backtick-monospace span immediately followed by a possessive apostrophe, which asciidoctor's constrained-quote parser can't close (it just keeps scanning for the next backtick, which was the ON_ERROR_STOP span). Reworded to avoid the adjacency instead. Regenerated README.html.
…dering # Conflicts: # HISTORY.asc # README.asc # README.html
| for f in "$install_dir"/*.sql; do | ||
| [ -f "$f" ] || continue | ||
|
|
||
| if grep -q 'ON_ERROR_STOP' "$f"; then | ||
| continue | ||
| fi | ||
| if grep -qE '\\ir? +.*psql\.sql' "$f"; then | ||
| continue | ||
| fi | ||
|
|
||
| missing+=("$f") | ||
| done |
There was a problem hiding this comment.
We don't want to boil the ocean, but there's absolutely room for improvement here. I don't think it's worth worrying about comments, esp. if you make the tests more specific on what they're looking for. Also, if the script explicitly enables ON_ERROR_STOP (vs just including psql.sql) it's OK if it then turns it back off at some point; presumably the user knows what they're doing in that scenario.
| name. | ||
|
|
||
| Issues fixed in this release: #87, #21, #55, #90 | ||
| == Add `make build-results` |
There was a problem hiding this comment.
Name is confusing; sounds a lot like what make results does. Use make results-build.
There was a problem hiding this comment.
Claude here: agreed on the name, but flagging before I rename — pgxntool-test's master already merged PR #79's tests (per the earlier Fixes #108/#97 pairing), which call make build-results by name in test/standard/make-test.bats. Renaming to results-build here without a paired pgxntool-test rename would break those already-merged tests the moment this PR's test job runs against pgxntool-test/master (which it now does for real, per the check-test-pr fix earlier in this PR). Flagging to the coordinator to sequence a paired pgxntool-test PR alongside this rename, same pattern as the earlier _PGXNTOOL_ variable rename (pgxntool-test #72).
Summary
make testnow catches two build-safety gaps that previously slipped through silently:test-buildgenuinely gatesinstallcheckinstead of racing it under parallelmake(#108), andtest/install/*.sqlfiles withoutON_ERROR_STOPnow fail the build instead of silently swallowing a hard SQL error (#97).A broken extension build can no longer slip through
make test:test-buildnow genuinely gatestest/install/test/sql(fixing a race undermake -j), andtest/install/*.sqlfiles that omitON_ERROR_STOP— the only thing standing between a hard SQL error and a silent "pass" there — now fail the build instead of being silently swallowed.test-build now gates the main suite (Fixes #108)
test-buildhad no real dependency edge oninstallcheck, so in the default (serial) config the main suite happened to run after it only becausecheck-stale-expected's own edge pulled it in first — undermake -jthe two raced. Added an explicitinstallcheck: test-buildedge (guarded against recursing into test-build's own nestedinstallcheckinvocation) so a broken build blocks the main suite entirely; there's no point validating install/query behavior against a build that doesn't come up cleanly.Tradeoff: this also means a stale or wrong
test/build/expected/*.outblocks the whole suite, not just a genuinely broken build — hencebuild-resultsbelow.make build-results(supports the above)Refreshes
test/build/expected/*.outfrom the lasttest-buildrun, mirroringmake resultsfor the main suite. Refuses to bless any file whose actual output contains anERROR:line — accepting an errored build as the new baseline would defeat the point of test-build — and reports exactly which file to bless by hand if the error is intentional.test/installnow requiresON_ERROR_STOP(Fixes #97)test/install/*.sqlfiles never get a real diff: pg_regress writes their actual output to the exact same file as their expected output, so a content difference can never fail the build.ON_ERROR_STOP(set directly, or via\i test/pgxntool/psql.sql) is the only thing that still turns a hard SQL error into a build failure. Addedcheck-test-install-error-stop, enforcing that everytest/install/*.sqlfile sets it (PGXNTOOL_ENABLE_TEST_INSTALL_ERROR_STOP_CHECKto disable).test/install/*.outis now gitignored, since it's rewritten by every run and never meaningfully compared.All of the above is documented in
base.mk,README.asc, andCLAUDE.mdfor both humans and AI agents reading the code directly, since none of it is obvious from the code alone — including whyON_ERROR_STOPcan't just be forced onto pg_regress's psql invocation instead of checking each file (pg_regress's command line is hardcoded, and forcing it would also changetest/sqlsemantics).Fixes #108.
Fixes #97.
Related pgxntool-test PR (merged): Postgres-Extensions/pgxntool-test#79
🤖 Generated with Claude Code