#!/usr/bin/env bash # next/tests/http_server_start.sh — Step 8b-start structural test. # # `http_server:start/1,2` spawn an Erlang process that blocks in # `http:listen/2` forever. In this port's cooperative scheduler, # any in-process `erlang-eval-ast` that triggers that spawn hangs # the runtime — `er-sched-run-all!` waits for every spawned # process to leave the runnable queue before returning to the # caller, and the listener never does. So this test verifies the # code SHAPE without actually invoking start/1: # * Module loads. # * `start/1` and `start/2` are bound in the module env. # * The dict↔proplist marshaling bridge (the BIF-wrapper hook) # is bound in the runtime env. # The live TCP behaviour lands in `next/tests/http_server_tcp.sh` # (Step 9a-tcp) via a shell-side curl probe. set -uo pipefail cd "$(git rev-parse --show-toplevel)" SX_SERVER="${SX_SERVER:-hosts/ocaml/_build/default/bin/sx_server.exe}" if [ ! -x "$SX_SERVER" ]; then SX_SERVER="/root/rose-ash/hosts/ocaml/_build/default/bin/sx_server.exe" fi if [ ! -x "$SX_SERVER" ]; then echo "ERROR: sx_server.exe not found." >&2 exit 1 fi VERBOSE="${1:-}" PASS=0; FAIL=0; ERRORS="" TMPFILE=$(mktemp); trap "rm -f $TMPFILE" EXIT cat > "$TMPFILE" <<'EPOCHS' (epoch 1) (load "lib/erlang/tokenizer.sx") (load "lib/erlang/parser.sx") (load "lib/erlang/parser-core.sx") (load "lib/erlang/parser-expr.sx") (load "lib/erlang/parser-module.sx") (load "lib/erlang/transpile.sx") (load "lib/erlang/runtime.sx") (load "lib/erlang/vm/dispatcher.sx") (epoch 2) (eval "(get (erlang-load-module (file-read \"next/kernel/http_server.erl\")) :name)") ;; --- module is registered --- (epoch 10) (eval "(let ((m (get (er-modules-get) \"http_server\"))) (cond (= m nil) 'absent :else 'present))") ;; --- start/1 + start/2 are bound (multi-arity stored as a single binding) --- (epoch 11) (eval "(let ((env (get (get (er-modules-get) \"http_server\") \"current\"))) (cond (= (get env \"start\") nil) 'missing :else 'present))") ;; --- request->proplist marshaler exists in runtime env --- (epoch 12) (eval "(if (= (type-of er-request-dict-to-proplist) \"lambda\") 'present 'missing)") ;; --- proplist->dict marshaler exists in runtime env --- (epoch 13) (eval "(if (= (type-of er-proplist-to-dict) \"lambda\") 'present 'missing)") ;; --- http:listen BIF wrapper now routes through the marshalers --- ;; Probe by registration only (calling listen would block forever). (epoch 14) (eval "(not (= (er-lookup-bif \"http\" \"listen\" 2) nil))") EPOCHS OUTPUT=$(timeout 30 "$SX_SERVER" < "$TMPFILE" 2>/dev/null) check() { local epoch="$1" desc="$2" expected="$3" local actual actual=$(echo "$OUTPUT" | awk -v e="$epoch" ' $0 ~ "^\\(ok-len " e " " { getline; print; exit } $0 ~ "^\\(ok " e " " { print; exit } $0 ~ "^\\(error " e " " { print; exit } ') [ -z "$actual" ] && actual="" if echo "$actual" | grep -qF -- "$expected"; then PASS=$((PASS+1)) [ "$VERBOSE" = "-v" ] && echo " ok $desc" else FAIL=$((FAIL+1)) ERRORS+=" FAIL [$desc] (epoch $epoch) expected: $expected | actual: $actual " fi } check 2 "http_server module loaded" "http_server" check 10 "module registered" "present" check 11 "start bound in module env" "present" check 12 "request marshaler defined" "present" check 13 "response marshaler defined" "present" check 14 "http:listen BIF registered" "true" TOTAL=$((PASS+FAIL)) if [ $FAIL -eq 0 ]; then echo "ok $PASS/$TOTAL http_server_start tests passed" else echo "FAIL $PASS/$TOTAL passed, $FAIL failed:" echo "$ERRORS" fi [ $FAIL -eq 0 ]