lib/gitea/search.sx: the forge builds document corpora SX-side — code files from the default branch head (path + blob text), issues (title + body + comments), PRs (title + body + reviews) — embeds them as one haskell-on-sx program and asks searchRankTfIdf for ranked doc ids (terms, AND/OR/NOT, phrases). Cost model honored: one evaluation parses the Haskell layers (~20s CPU), extra queries are nearly free — so the core primitive is gitea/search-multi (any number of corpora and queries in a single evaluation; each corpus an idxN binding) and only the six layers searchRankTfIdf needs are compiled, not the full search/src. The test suite runs its thirteen SX-level queries over five corpora as ONE evaluation. Global search spans exactly the repos the caller can read. Web: /:owner/:name/search page (kind filter), repo + global JSON search. Suite timeout raised to 900s for the haskell-backed suites. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
183 lines
6.5 KiB
Bash
183 lines
6.5 KiB
Bash
#!/usr/bin/env bash
|
|
# lib/gitea/conformance.sh — run sx-gitea test suites, emit scoreboard.json + scoreboard.md.
|
|
#
|
|
# Usage:
|
|
# bash lib/gitea/conformance.sh # run all suites
|
|
# bash lib/gitea/conformance.sh -v # also print failure details
|
|
|
|
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:-}"
|
|
|
|
# suite name | pass counter | fail counter | failures list | extra modules (;-sep)
|
|
ISSUES_EXTRAS="lib/relations/schema.sx;lib/relations/engine.sx;lib/relations/api.sx;lib/smalltalk/tokenizer.sx;lib/smalltalk/parser.sx;lib/guest/reflective/class-chain.sx;lib/smalltalk/runtime.sx;lib/guest/reflective/env.sx;lib/smalltalk/eval.sx;lib/content/block.sx;lib/content/doc.sx;lib/content/render.sx;lib/content/api.sx;lib/content/meta.sx;lib/content/text.sx;lib/content/section.sx;lib/content/table.sx;lib/content/markdown.sx;lib/content/md-import.sx;lib/gitea/issues.sx"
|
|
|
|
PR_EXTRAS="$ISSUES_EXTRAS;lib/guest/lex.sx;lib/guest/reflective/quoting.sx;lib/scheme/parser.sx;lib/scheme/eval.sx;lib/scheme/runtime.sx;lib/flow/spec.sx;lib/flow/store.sx;lib/flow/remote.sx;lib/flow/host.sx;lib/flow/api.sx;lib/gitea/pr.sx"
|
|
SEARCH_EXTRAS_TAIL="lib/haskell/tokenizer.sx;lib/haskell/layout.sx;lib/haskell/parser.sx;lib/haskell/desugar.sx;lib/haskell/runtime.sx;lib/haskell/match.sx;lib/haskell/eval.sx;lib/haskell/map.sx;lib/haskell/set.sx;lib/haskell/testlib.sx;lib/search/tokenize.sx;lib/search/index.sx;lib/search/query.sx;lib/search/parse.sx;lib/search/rank.sx;lib/search/rankq.sx;lib/search/testlib.sx;lib/gitea/search.sx"
|
|
ACT_EXTRAS="$PR_EXTRAS;lib/apl/runtime.sx;lib/feed/normalize.sx;lib/feed/stream.sx;lib/feed/api.sx;lib/feed/fanout.sx;lib/feed/dedupe.sx;lib/feed/aggregate.sx;lib/feed/rank.sx;lib/feed/acl.sx;lib/feed/mute.sx;lib/feed/page.sx;lib/feed/notify.sx;lib/feed/home.sx;lib/feed/fed.sx;lib/events/notify.sx;lib/gitea/activity.sx"
|
|
|
|
SUITES=(
|
|
"repo|gitea-repo-pass|gitea-repo-fail|gitea-repo-fails|"
|
|
"access|gitea-access-pass|gitea-access-fail|gitea-access-fails|"
|
|
"wire|gitea-wire-pass|gitea-wire-fail|gitea-wire-fails|"
|
|
"issues|gitea-issues-pass|gitea-issues-fail|gitea-issues-fails|$ISSUES_EXTRAS"
|
|
"pr|gitea-pr-pass|gitea-pr-fail|gitea-pr-fails|$PR_EXTRAS"
|
|
"activity|gitea-act-pass|gitea-act-fail|gitea-act-fails|$ACT_EXTRAS"
|
|
"search|gitea-search-pass|gitea-search-fail|gitea-search-fails|$PR_EXTRAS;$SEARCH_EXTRAS_TAIL"
|
|
"fed|gitea-fed-pass|gitea-fed-fail|gitea-fed-fails|$ACT_EXTRAS;lib/gitea/fed.sx"
|
|
)
|
|
|
|
OUT_JSON="lib/gitea/scoreboard.json"
|
|
OUT_MD="lib/gitea/scoreboard.md"
|
|
|
|
# Library load order: kernel stdlib, persist, artdag canon, sx-git, dream
|
|
# (types/router/middleware/error/html/json/api), then the gitea modules.
|
|
MODULES=(
|
|
"spec/stdlib.sx"
|
|
"spec/parser.sx"
|
|
"lib/r7rs.sx"
|
|
"lib/persist/event.sx"
|
|
"lib/persist/backend.sx"
|
|
"lib/persist/log.sx"
|
|
"lib/persist/kv.sx"
|
|
"lib/artdag/dag.sx"
|
|
"lib/git/object.sx"
|
|
"lib/git/ref.sx"
|
|
"lib/git/dag.sx"
|
|
"lib/git/worktree.sx"
|
|
"lib/git/diff.sx"
|
|
"lib/git/merge.sx"
|
|
"lib/git/porcelain.sx"
|
|
"lib/datalog/tokenizer.sx"
|
|
"lib/datalog/parser.sx"
|
|
"lib/datalog/unify.sx"
|
|
"lib/datalog/db.sx"
|
|
"lib/datalog/builtins.sx"
|
|
"lib/datalog/aggregates.sx"
|
|
"lib/datalog/strata.sx"
|
|
"lib/datalog/eval.sx"
|
|
"lib/datalog/api.sx"
|
|
"lib/datalog/magic.sx"
|
|
"lib/acl/schema.sx"
|
|
"lib/acl/facts.sx"
|
|
"lib/acl/engine.sx"
|
|
"lib/dream/types.sx"
|
|
"lib/dream/router.sx"
|
|
"lib/dream/middleware.sx"
|
|
"lib/dream/error.sx"
|
|
"lib/dream/html.sx"
|
|
"lib/dream/json.sx"
|
|
"lib/dream/auth.sx"
|
|
"lib/dream/api.sx"
|
|
"lib/gitea/repo.sx"
|
|
"lib/gitea/access.sx"
|
|
"lib/gitea/web.sx"
|
|
"lib/gitea/wire.sx"
|
|
)
|
|
|
|
run_suite() {
|
|
local suite=$1 passvar=$2 failvar=$3 failsvar=$4 extras=$5
|
|
local file="lib/gitea/tests/${suite}.sx"
|
|
local TMP
|
|
TMP=$(mktemp)
|
|
{
|
|
echo "(epoch 1)"
|
|
for M in "${MODULES[@]}"; do echo "(load \"$M\")"; done
|
|
if [ -n "$extras" ]; then
|
|
for M in ${extras//;/ }; do echo "(load \"$M\")"; done
|
|
fi
|
|
echo "(epoch 2)"
|
|
echo "(load \"${file}\")"
|
|
echo "(epoch 3)"
|
|
echo "(eval \"(list ${passvar} ${failvar})\")"
|
|
echo "(epoch 4)"
|
|
echo "(eval \"(inspect ${failsvar})\")"
|
|
} > "$TMP"
|
|
|
|
local OUTPUT
|
|
OUTPUT=$(timeout 900 "$SX_SERVER" < "$TMP" 2>/dev/null)
|
|
rm -f "$TMP"
|
|
|
|
local LINE
|
|
LINE=$(echo "$OUTPUT" | awk '/^\(ok-len 3 / {getline; print; exit}')
|
|
if [ -z "$LINE" ]; then
|
|
LINE=$(echo "$OUTPUT" | grep -E '^\(ok 3 \([0-9]+ [0-9]+\)\)' | tail -1 \
|
|
| sed -E 's/^\(ok 3 //; s/\)$//')
|
|
fi
|
|
|
|
local P F
|
|
P=$(echo "$LINE" | sed -E 's/^\(([0-9]+) ([0-9]+)\).*/\1/')
|
|
F=$(echo "$LINE" | sed -E 's/^\(([0-9]+) ([0-9]+)\).*/\2/')
|
|
P=${P:-0}
|
|
F=${F:-0}
|
|
|
|
if [ -n "$VERBOSE" ] && [ "$F" != "0" ]; then
|
|
echo " --- ${suite} failures ---" >&2
|
|
echo "$OUTPUT" | awk '/^\(ok(-len)? 4 /,0' | head -40 >&2
|
|
fi
|
|
|
|
echo "${P} ${F}"
|
|
}
|
|
|
|
declare -A SUITE_PASS
|
|
declare -A SUITE_FAIL
|
|
TOTAL_PASS=0
|
|
TOTAL_FAIL=0
|
|
|
|
echo "Running sx-gitea conformance suite..." >&2
|
|
for entry in "${SUITES[@]}"; do
|
|
IFS='|' read -r s passvar failvar failsvar extras <<< "$entry"
|
|
read -r p f < <(run_suite "$s" "$passvar" "$failvar" "$failsvar" "$extras")
|
|
SUITE_PASS[$s]=$p
|
|
SUITE_FAIL[$s]=$f
|
|
TOTAL_PASS=$((TOTAL_PASS + p))
|
|
TOTAL_FAIL=$((TOTAL_FAIL + f))
|
|
printf " %-12s %d/%d\n" "$s" "$p" "$((p+f))" >&2
|
|
done
|
|
|
|
{
|
|
printf '{\n'
|
|
printf ' "suites": {\n'
|
|
first=1
|
|
for entry in "${SUITES[@]}"; do
|
|
IFS='|' read -r s _ _ _ _ <<< "$entry"
|
|
if [ $first -eq 0 ]; then printf ',\n'; fi
|
|
printf ' "%s": {"pass": %d, "fail": %d}' "$s" "${SUITE_PASS[$s]}" "${SUITE_FAIL[$s]}"
|
|
first=0
|
|
done
|
|
printf '\n },\n'
|
|
printf ' "total_pass": %d,\n' "$TOTAL_PASS"
|
|
printf ' "total_fail": %d,\n' "$TOTAL_FAIL"
|
|
printf ' "total": %d\n' "$((TOTAL_PASS + TOTAL_FAIL))"
|
|
printf '}\n'
|
|
} > "$OUT_JSON"
|
|
|
|
{
|
|
printf '# sx-gitea Conformance Scoreboard\n\n'
|
|
printf '_Generated by `lib/gitea/conformance.sh`_\n\n'
|
|
printf '| Suite | Pass | Fail | Total |\n'
|
|
printf '|-------|-----:|-----:|------:|\n'
|
|
for entry in "${SUITES[@]}"; do
|
|
IFS='|' read -r s _ _ _ _ <<< "$entry"
|
|
p=${SUITE_PASS[$s]}
|
|
f=${SUITE_FAIL[$s]}
|
|
printf '| %s | %d | %d | %d |\n' "$s" "$p" "$f" "$((p+f))"
|
|
done
|
|
printf '| **Total** | **%d** | **%d** | **%d** |\n' "$TOTAL_PASS" "$TOTAL_FAIL" "$((TOTAL_PASS + TOTAL_FAIL))"
|
|
} > "$OUT_MD"
|
|
|
|
echo "Wrote $OUT_JSON and $OUT_MD" >&2
|
|
echo "Total: $TOTAL_PASS pass, $TOTAL_FAIL fail" >&2
|
|
|
|
[ "$TOTAL_FAIL" -eq 0 ]
|