55# VGI worker, using a prebuilt standalone `haybarn-unittest` and the signed
66# community `vgi` extension — no C++ build from source. See ci/README.md.
77#
8+ # Parameterized by TRANSPORT (default: subprocess), exercising the SAME suite
9+ # over each transport the vgi extension supports — the only thing that changes
10+ # is what LOCATION (VGI_MASK_WORKER) the .test files ATTACH:
11+ #
12+ # subprocess VGI_MASK_WORKER = the stdio worker command (DuckDB spawns it).
13+ # http start `mask-worker --http` (auto port; advertises `PORT:<n>`
14+ # on stdout), VGI_MASK_WORKER = http://127.0.0.1:<port>.
15+ # unix start `mask-worker --unix <sock>` (advertises `UNIX:<sock>`
16+ # on stdout), VGI_MASK_WORKER = unix://<sock>.
17+ #
818# Required environment:
919# HAYBARN_UNITTEST path to the haybarn-unittest binary
10- # VGI_MASK_WORKER worker LOCATION the .test files attach (a stdio command
11- # such as the compiled mask-worker binary, or an http:// URL)
20+ # WORKER_BIN path to the compiled mask-worker binary (used to launch
21+ # the http/unix servers, and the stdio LOCATION). Falls back
22+ # to VGI_MASK_WORKER when that is a bare command (subprocess).
1223# Optional:
24+ # TRANSPORT subprocess | http | unix (default: subprocess)
1325# STAGE scratch dir for the preprocessed test tree (default: mktemp)
1426set -euo pipefail
1527
28+ TRANSPORT=" ${TRANSPORT:- subprocess} "
29+
1630: " ${HAYBARN_UNITTEST:? path to the haybarn-unittest binary} "
17- : " ${VGI_MASK_WORKER:? worker LOCATION (stdio command or http:// URL)} "
1831
1932HERE=" $( cd " $( dirname " $0 " ) " && pwd) "
2033REPO=" $( cd " $HERE /.." && pwd) "
2134STAGE=" ${STAGE:- $(mktemp -d)} "
2235
36+ # For http/unix we must launch the worker binary ourselves; for subprocess the
37+ # binary IS the LOCATION. WORKER_BIN names the compiled binary; default to the
38+ # release build in this repo.
39+ WORKER_BIN=" ${WORKER_BIN:- $REPO / target/ release/ mask-worker} "
40+
41+ SERVER_PID=" "
42+ SOCK_PATH=" "
43+ cleanup () {
44+ local rc=$?
45+ if [[ -n " $SERVER_PID " ]]; then
46+ kill " $SERVER_PID " 2> /dev/null || true
47+ wait " $SERVER_PID " 2> /dev/null || true
48+ fi
49+ [[ -n " $SOCK_PATH " ]] && rm -f " $SOCK_PATH " 2> /dev/null || true
50+ return " $rc "
51+ }
52+ trap cleanup EXIT
53+
54+ # Bring up the out-of-band server for http/unix and resolve VGI_MASK_WORKER.
55+ # Both transports announce their endpoint on stdout (`PORT:<n>` / `UNIX:<path>`),
56+ # which we poll for in the log before running the suite (readiness gate).
57+ start_server_and_set_location () {
58+ local kind=" $1 "
59+ : " ${WORKER_BIN:? path to the mask-worker binary (WORKER_BIN)} "
60+ [[ -x " $WORKER_BIN " ]] || { echo " ERROR: worker binary not executable: $WORKER_BIN " >&2 ; exit 1; }
61+
62+ local log=" $STAGE /.worker-$kind .log"
63+ case " $kind " in
64+ http)
65+ " $WORKER_BIN " --http > " $log " 2>&1 &
66+ SERVER_PID=$!
67+ local port=" "
68+ for _ in $( seq 1 60) ; do
69+ if ! kill -0 " $SERVER_PID " 2> /dev/null; then
70+ echo " ERROR: worker (--http) exited during startup. Log:" >&2 ; cat " $log " >&2 ; exit 1
71+ fi
72+ port=$( sed -n ' s/.*PORT:\([0-9][0-9]*\).*/\1/p' " $log " 2> /dev/null | head -1)
73+ [[ -n " $port " ]] && break
74+ sleep 0.5
75+ done
76+ [[ -n " $port " ]] || { echo " ERROR: timed out waiting for PORT:<n>. Log:" >&2 ; cat " $log " >&2 ; exit 1; }
77+ export VGI_MASK_WORKER=" http://127.0.0.1:$port "
78+ echo " HTTP worker ready on 127.0.0.1:$port (pid $SERVER_PID )"
79+ ;;
80+ unix)
81+ SOCK_PATH=" ${VGI_MASK_SOCK:-/ tmp/ mask.$$ .sock} "
82+ rm -f " $SOCK_PATH " 2> /dev/null || true
83+ " $WORKER_BIN " --unix " $SOCK_PATH " > " $log " 2>&1 &
84+ SERVER_PID=$!
85+ local ready=" "
86+ for _ in $( seq 1 60) ; do
87+ if ! kill -0 " $SERVER_PID " 2> /dev/null; then
88+ echo " ERROR: worker (--unix) exited during startup. Log:" >&2 ; cat " $log " >&2 ; exit 1
89+ fi
90+ if grep -q " UNIX:$SOCK_PATH " " $log " 2> /dev/null && [[ -S " $SOCK_PATH " ]]; then
91+ ready=1; break
92+ fi
93+ sleep 0.5
94+ done
95+ [[ -n " $ready " ]] || { echo " ERROR: timed out waiting for UNIX socket. Log:" >&2 ; cat " $log " >&2 ; exit 1; }
96+ export VGI_MASK_WORKER=" unix://$SOCK_PATH "
97+ echo " Unix worker ready on $SOCK_PATH (pid $SERVER_PID )"
98+ ;;
99+ esac
100+ }
101+
102+ case " $TRANSPORT " in
103+ subprocess)
104+ # The binary itself is the stdio LOCATION DuckDB spawns. Honor an explicit
105+ # VGI_MASK_WORKER (e.g. a bare command) if the caller set one.
106+ export VGI_MASK_WORKER=" ${VGI_MASK_WORKER:- $WORKER_BIN } "
107+ ;;
108+ http) start_server_and_set_location http ;;
109+ unix) start_server_and_set_location unix ;;
110+ * ) echo " ERROR: unknown TRANSPORT '$TRANSPORT ' (want subprocess|http|unix)" >&2 ; exit 1 ;;
111+ esac
112+
113+ : " ${VGI_MASK_WORKER:? worker LOCATION (stdio command, http:// URL, or unix:// socket)} "
114+
23115echo " Staging preprocessed tests into $STAGE ..."
24116mkdir -p " $STAGE /test/sql"
117+ # Pass the transport to the preprocessor: the http leg additionally needs DuckDB's
118+ # `httpfs` extension loaded (the vgi extension's HTTP client is built on it), so
119+ # the awk injects a signed INSTALL/LOAD httpfs after each `LOAD vgi;`. Without it
120+ # the http ATTACH fails with a "HTTP"-containing error that the sqllogictest
121+ # runner *silently auto-skips* (default ignore_error_messages), masking the gap.
25122for f in " $REPO " /test/sql/* .test; do
26- awk -f " $HERE /preprocess-require.awk" " $f " > " $STAGE /test/sql/$( basename " $f " ) "
123+ awk -v transport= " $TRANSPORT " - f " $HERE /preprocess-require.awk" " $f " > " $STAGE /test/sql/$( basename " $f " ) "
27124done
28125
29126cd " $STAGE "
@@ -45,5 +142,22 @@ rm -f "$STAGE/test/_warm.test"
45142
46143# Run the whole suite in one invocation, streaming the runner's native
47144# sqllogictest report. Any failed assertion exits non-zero and fails the job.
48- echo " Running suite (worker: $VGI_MASK_WORKER ) ..."
49- " $HAYBARN_UNITTEST " " test/sql/*"
145+ #
146+ # Guard against the silent-skip trap: DuckDB's sqllogictest runner auto-skips
147+ # any test whose error message contains "HTTP" (default ignore_error_messages),
148+ # so a broken http leg can report "All tests were skipped" with exit 0 and look
149+ # green. Tee the report and fail if NOTHING actually ran. (For subprocess/unix
150+ # there is no skip path, so this only ever bites a genuinely broken http leg.)
151+ echo " Running suite (transport: $TRANSPORT , worker: $VGI_MASK_WORKER ) ..."
152+ REPORT=" $STAGE /.report.txt"
153+ set +e
154+ " $HAYBARN_UNITTEST " " test/sql/*" 2>&1 | tee " $REPORT "
155+ status=" ${PIPESTATUS[0]} "
156+ set -e
157+ if grep -qiE " All tests were skipped|total skipped [1-9]" " $REPORT " ; then
158+ echo " ERROR: tests were SKIPPED — almost certainly an ATTACH/transport error whose" >&2
159+ echo " message matched the runner's default ignore list (e.g. \" HTTP\" ). A skip" >&2
160+ echo " is NOT a pass. Transport=$TRANSPORT worker=$VGI_MASK_WORKER ." >&2
161+ exit 1
162+ fi
163+ exit " $status "
0 commit comments