156 lines
5.1 KiB
Bash
Executable File
156 lines
5.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# run.sh — build and execute the time/ acceptance corpus.
|
|
#
|
|
# Each examples/<case>.el is a self-contained El program with a fn main()
|
|
# that prints a single deterministic result line. The runner compiles each
|
|
# via the canonical native elc, links it against the shared C runtime, runs
|
|
# it, and asserts the output matches the expected value.
|
|
#
|
|
# The typeerror case is special: the C source is generated successfully (a
|
|
# `#error` directive sits at the bottom) but cc must FAIL. Pass = cc fails
|
|
# with the expected diagnostic; Fail = cc succeeds (the type rule didn't
|
|
# fire) or fails with an unexpected diagnostic.
|
|
|
|
set -uo pipefail
|
|
cd "$(dirname "$0")"
|
|
|
|
EL_HOME="${EL_HOME:-$(cd ../.. && pwd)}"
|
|
ELC="${EL_HOME}/dist/platform/elc"
|
|
RUNTIME_DIR="${EL_HOME}/el-compiler/runtime"
|
|
|
|
if [ ! -x "${ELC}" ]; then
|
|
echo "elc not found at ${ELC}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
PASS=0
|
|
FAIL=0
|
|
FAILED_NAMES=()
|
|
|
|
# run_runtime_case <name> <source> <expected> [<extra>]
|
|
# Compiles the source via elc, links against the runtime, runs the binary,
|
|
# and compares stdout (whitespace-trimmed) against <expected>. If <extra> is
|
|
# the literal string "either", any one of the comma-separated values in
|
|
# <expected> counts as a pass — used by time-arithmetic where the second
|
|
# now() reading is microseconds after the first, which makes the >=
|
|
# comparison platform-dependent.
|
|
run_runtime_case() {
|
|
local name="$1"
|
|
local src="$2"
|
|
local expected="$3"
|
|
local mode="${4:-exact}"
|
|
|
|
local out_c
|
|
local out_bin
|
|
out_c="$(mktemp -t time_test.XXXXXX).c"
|
|
out_bin="$(mktemp -t time_test.XXXXXX)"
|
|
|
|
if ! "${ELC}" "${src}" > "${out_c}" 2>/tmp/time_test.elc.err; then
|
|
echo "FAIL ${name} — elc emit failed:"
|
|
cat /tmp/time_test.elc.err | sed 's/^/ /'
|
|
FAIL=$((FAIL+1))
|
|
FAILED_NAMES+=("${name}")
|
|
rm -f "${out_c}" "${out_bin}"
|
|
return
|
|
fi
|
|
|
|
if ! cc -O2 -I "${RUNTIME_DIR}" "${out_c}" "${RUNTIME_DIR}/el_runtime.c" \
|
|
-lcurl -lpthread -o "${out_bin}" 2>/tmp/time_test.cc.err; then
|
|
echo "FAIL ${name} — cc failed:"
|
|
cat /tmp/time_test.cc.err | sed 's/^/ /'
|
|
FAIL=$((FAIL+1))
|
|
FAILED_NAMES+=("${name}")
|
|
rm -f "${out_c}" "${out_bin}"
|
|
return
|
|
fi
|
|
|
|
local got
|
|
got="$("${out_bin}" 2>&1 | tr -d '[:space:]')"
|
|
|
|
if [ "${mode}" = "either" ]; then
|
|
local ok=0
|
|
local IFS=','
|
|
for choice in ${expected}; do
|
|
if [ "${got}" = "${choice}" ]; then ok=1; break; fi
|
|
done
|
|
if [ "${ok}" = "1" ]; then
|
|
echo "PASS ${name} (got: ${got})"
|
|
PASS=$((PASS+1))
|
|
else
|
|
echo "FAIL ${name} expected one of {${expected}}, got: ${got}"
|
|
FAIL=$((FAIL+1))
|
|
FAILED_NAMES+=("${name}")
|
|
fi
|
|
else
|
|
if [ "${got}" = "${expected}" ]; then
|
|
echo "PASS ${name}"
|
|
PASS=$((PASS+1))
|
|
else
|
|
echo "FAIL ${name} expected: ${expected}, got: ${got}"
|
|
FAIL=$((FAIL+1))
|
|
FAILED_NAMES+=("${name}")
|
|
fi
|
|
fi
|
|
|
|
rm -f "${out_c}" "${out_bin}"
|
|
}
|
|
|
|
# run_typeerror_case <name> <source>
|
|
# Compiles to C (which must succeed; the violation is recorded but codegen
|
|
# still completes), then asserts cc FAILS with the temporal-type-error
|
|
# directive in its output.
|
|
run_typeerror_case() {
|
|
local name="$1"
|
|
local src="$2"
|
|
|
|
local out_c
|
|
out_c="$(mktemp -t time_test.XXXXXX).c"
|
|
|
|
if ! "${ELC}" "${src}" > "${out_c}" 2>/tmp/time_test.elc.err; then
|
|
echo "FAIL ${name} — elc emit failed (expected emit-then-cc-fail):"
|
|
cat /tmp/time_test.elc.err | sed 's/^/ /'
|
|
FAIL=$((FAIL+1))
|
|
FAILED_NAMES+=("${name}")
|
|
rm -f "${out_c}"
|
|
return
|
|
fi
|
|
|
|
if cc -O2 -I "${RUNTIME_DIR}" "${out_c}" "${RUNTIME_DIR}/el_runtime.c" \
|
|
-lcurl -lpthread -o /tmp/time_test_should_not_link 2>/tmp/time_test.cc.err; then
|
|
echo "FAIL ${name} — cc unexpectedly succeeded; type rule did not fire"
|
|
FAIL=$((FAIL+1))
|
|
FAILED_NAMES+=("${name}")
|
|
rm -f "${out_c}" /tmp/time_test_should_not_link
|
|
return
|
|
fi
|
|
|
|
if grep -q "temporal type error" /tmp/time_test.cc.err; then
|
|
echo "PASS ${name} (cc rejected with: $(grep "temporal type error" /tmp/time_test.cc.err | head -1 | tr -d '\n'))"
|
|
PASS=$((PASS+1))
|
|
else
|
|
echo "FAIL ${name} — cc failed but without the expected temporal-type-error message:"
|
|
cat /tmp/time_test.cc.err | sed 's/^/ /'
|
|
FAIL=$((FAIL+1))
|
|
FAILED_NAMES+=("${name}")
|
|
fi
|
|
rm -f "${out_c}"
|
|
}
|
|
|
|
echo "==> Running time-types acceptance corpus"
|
|
echo
|
|
|
|
run_runtime_case "time-literals" examples/time-literals.el "30"
|
|
run_runtime_case "time-arithmetic" examples/time-arithmetic.el "0,1" either
|
|
run_runtime_case "time-comparison" examples/time-comparison.el "1"
|
|
run_typeerror_case "time-typeerror" examples/time-typeerror.el
|
|
run_runtime_case "ttl-cache" examples/ttl-cache.el "hello"
|
|
run_runtime_case "sleep-typed" examples/sleep-typed.el "1"
|
|
|
|
echo
|
|
echo "${PASS} passed, ${FAIL} failed"
|
|
if [ "${FAIL}" -gt 0 ]; then
|
|
echo "failed: ${FAILED_NAMES[*]}"
|
|
exit 1
|
|
fi
|
|
exit 0
|