mirror of
https://github.com/zap-proto/go.git
synced 2026-10-30 15:05:00 +00:00
Generating C++ is a claim. This runs it. Both probes build the same cases from the same inputs — every type the dialect has, plus the request and response envelopes the generated client and dispatch produce — and print the hex; the two outputs are the same file. Then each probe reads both probes' bytes with its own generated accessors, four crossings, all agreeing. Then both read a corpus of real P-chain transactions produced by the Go chain implementation, 134 vectors of which 44 are truncated or malformed on purpose, and answer the same fields in the same places. Neither probe decides anything: every value printed comes from a generated accessor or a generated builder, so a disagreement between the probes is a disagreement between the backends and nowhere else. It earned its keep immediately. Five corpus vectors separated a Go [N]byte accessor answering zeros from a C++ span answering empty, and the round trip found the zero Object panicking in the Go runtime where C++ answered zero. Both are fixed; neither was visible by reading. list<T> carries a pointer and a count and nothing about elements — the dialect cannot state a stride — so both languages agree on the count, which is all either can say. That gap is in the dialect, in both backends at once.
120 lines
5.5 KiB
Bash
Executable File
120 lines
5.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# SPDX-License-Identifier: BSD-3-Clause-Eco
|
|
#
|
|
# The two-backend proof, end to end.
|
|
#
|
|
# 1. regenerate Go AND C++ from the same schemas with the same zapgen
|
|
# 2. build both probes
|
|
# 3. compare what each BUILT, byte for byte
|
|
# 4. read each side's bytes back with BOTH readers and compare the fields
|
|
# 5. compare what each READ from a corpus of real chain bytes
|
|
#
|
|
# A pass means the C++ backend writes the bytes the Go backend writes and
|
|
# reads the same fields out of bytes neither of them wrote.
|
|
#
|
|
# The C++ runtime comes from github.com/zap-proto/cpp, pinned below. Set
|
|
# ZAP_CPP to a checkout to use one you already have.
|
|
#
|
|
# The corpus is a vectors.tsv in node2's format. Pass a path, or set
|
|
# ZAP_CORPUS. Without one, only the build comparison runs, and the script
|
|
# says so rather than reporting a pass it did not earn.
|
|
set -euo pipefail
|
|
|
|
ZAP_CPP_REV=075e43b14878af486a98974759b57a5881df157a
|
|
here=$(cd "$(dirname "$0")" && pwd)
|
|
repo=$(cd "$here/.." && pwd)
|
|
work=${TMPDIR:-/tmp}/zapgen-conformance
|
|
corpus=${1:-${ZAP_CORPUS:-}}
|
|
|
|
mkdir -p "$work"
|
|
|
|
# ── the C++ runtime ────────────────────────────────────────────────────────
|
|
if [ -z "${ZAP_CPP:-}" ]; then
|
|
ZAP_CPP=$work/zap-cpp
|
|
if [ ! -d "$ZAP_CPP/.git" ]; then
|
|
git clone -q https://github.com/zap-proto/cpp "$ZAP_CPP"
|
|
fi
|
|
git -C "$ZAP_CPP" fetch -q origin
|
|
git -C "$ZAP_CPP" checkout -q $ZAP_CPP_REV
|
|
fi
|
|
echo "runtime: $ZAP_CPP @ $(git -C "$ZAP_CPP" rev-parse --short HEAD)"
|
|
|
|
# ── regenerate ─────────────────────────────────────────────────────────────
|
|
cd "$repo"
|
|
go build -o "$work/zapgen" ./cmd/zapgen
|
|
|
|
gen() { # gen SCHEMA GO_PKG_DIR
|
|
"$work/zapgen" -single -out "conformance/gen/$2" "$1"
|
|
"$work/zapgen" -lang cpp -single -out conformance/gen/hpp "$1"
|
|
}
|
|
gen conformance/schema/probe.zap probe
|
|
gen conformance/schema/pchain.zap pchain
|
|
gen cmd/zapgen/testdata/echo.zap echo
|
|
gen cmd/zapgen/testdata/basetx.zap xvm
|
|
|
|
# ── the per-struct headers compile too ─────────────────────────────────────
|
|
# -single is what the probes use; the default is one header per struct, with
|
|
# a sibling include per nested struct. Both have to build.
|
|
per=$work/per-struct
|
|
rm -rf "$per" && mkdir -p "$per"
|
|
"$work/zapgen" -lang cpp -out "$per" conformance/schema/probe.zap
|
|
"$work/zapgen" -lang cpp -out "$per" conformance/schema/pchain.zap
|
|
"$work/zapgen" -lang cpp -out "$per" cmd/zapgen/testdata/echo.zap
|
|
"$work/zapgen" -lang cpp -out "$per" cmd/zapgen/testdata/basetx.zap
|
|
{
|
|
echo "// Generated by conformance/run.sh: include every per-struct header."
|
|
for h in "$per"/*.hpp; do echo "#include \"$(basename "$h")\""; done
|
|
echo "int main() { return 0; }"
|
|
} > "$per/all.cpp"
|
|
${CXX:-g++} -std=c++23 -O1 -Wall -Wextra -I"$ZAP_CPP/include" -I"$per" \
|
|
-o "$work/per-struct-all" "$per/all.cpp" "$ZAP_CPP/src/rpc.cpp"
|
|
echo "per-struct headers: $(ls "$per"/*.hpp | wc -l) compiled together"
|
|
|
|
# ── build both probes ──────────────────────────────────────────────────────
|
|
go build -o "$work/probe-go" ./conformance/go
|
|
${CXX:-g++} -std=c++23 -O1 -Wall -Wextra \
|
|
-I"$ZAP_CPP/include" -Iconformance/gen/hpp \
|
|
-o "$work/probe-cpp" conformance/cpp/probe.cpp "$ZAP_CPP/src/rpc.cpp"
|
|
|
|
# ── what each BUILT ────────────────────────────────────────────────────────
|
|
"$work/probe-go" write > "$work/go.write"
|
|
"$work/probe-cpp" write > "$work/cpp.write"
|
|
if diff -u "$work/go.write" "$work/cpp.write"; then
|
|
echo "write parity: $(wc -l < "$work/go.write") cases identical"
|
|
else
|
|
echo "write parity: FAILED"
|
|
exit 1
|
|
fi
|
|
|
|
# ── each reader over the other language's bytes ────────────────────────────
|
|
# Four runs, so every combination is covered: each reader over its own bytes
|
|
# and over the other's. All four must print the same fields.
|
|
"$work/probe-go" back "$work/go.write" > "$work/go-reads-go"
|
|
"$work/probe-cpp" back "$work/go.write" > "$work/cpp-reads-go"
|
|
"$work/probe-go" back "$work/cpp.write" > "$work/go-reads-cpp"
|
|
"$work/probe-cpp" back "$work/cpp.write" > "$work/cpp-reads-cpp"
|
|
if diff -u "$work/go-reads-go" "$work/cpp-reads-go" &&
|
|
diff -u "$work/go-reads-go" "$work/go-reads-cpp" &&
|
|
diff -u "$work/go-reads-go" "$work/cpp-reads-cpp"; then
|
|
echo "read-back parity: $(wc -l < "$work/go-reads-go") cases, both readers, both writers"
|
|
else
|
|
echo "read-back parity: FAILED"
|
|
exit 1
|
|
fi
|
|
|
|
# ── what each READ ─────────────────────────────────────────────────────────
|
|
if [ -z "$corpus" ]; then
|
|
echo "read parity: SKIPPED (no corpus; pass a vectors.tsv path or set ZAP_CORPUS)"
|
|
exit 0
|
|
fi
|
|
"$work/probe-go" read "$corpus" > "$work/go.read"
|
|
"$work/probe-cpp" read "$corpus" > "$work/cpp.read"
|
|
if diff -u "$work/go.read" "$work/cpp.read"; then
|
|
n=$(wc -l < "$work/go.read")
|
|
ok=$(grep -c 'parse=ok' "$work/go.read" || true)
|
|
echo "read parity: $n corpus vectors identical ($ok parsed, $((n - ok)) refused by both)"
|
|
else
|
|
echo "read parity: FAILED"
|
|
exit 1
|
|
fi
|