#!/bin/sh # Self-test for the gate scripts. It builds small fake trees in a temporary # directory and checks that each script passes the good tree and fails the bad ones. # Do not edit: this file defines the required behaviour of the scripts. set -eu here=$(cd "$(dirname "$0")" && pwd) tmp=$(mktemp -d) trap 'rm -rf "$tmp"' EXIT fails=0 expect() { # expect pass|fail NAME SCRIPT ROOT want="$1"; name="$2"; script="$3"; root="$4" if sh "$here/$script" "$root" >/dev/null 2>&1; then got=pass; else got=fail; fi if [ "$got" != "$want" ]; then echo "test-gate-scripts: $name: expected $want, got $got" >&2 fails=$((fails + 1)) fi } manifest() { # manifest DIR NAME [DEPENDENCY-LINES...] dir="$1"; name="$2"; shift 2 mkdir -p "$dir/src" { printf '[package]\nname = "%s"\nversion = "0.1.0"\n\n[dependencies]\n' "$name" for line in "$@"; do printf '%s\n' "$line"; done } > "$dir/Cargo.toml" } tree() { # tree ROOT: a good workspace with proto, loopd and brokerd root="$1" mkdir -p "$root/docs" printf '[workspace]\nmembers = ["crates/*"]\n\n[workspace.dependencies]\nproto = { path = "crates/proto" }\nserde = { version = "1", features = ["derive"] }\n' > "$root/Cargo.toml" printf '# Dependencies\n\n| Crate | Why |\n|---|---|\n| `serde` | types |\n' > "$root/docs/dependencies.md" manifest "$root/crates/proto" proto 'serde.workspace = true' manifest "$root/crates/loopd" loopd 'proto.workspace = true' manifest "$root/crates/brokerd" brokerd 'proto = { workspace = true }' 'serde.workspace = true' } lines() { # lines N FILE mkdir -p "$(dirname "$2")" i=0; : > "$2" while [ "$i" -lt "$1" ]; do echo "// line" >> "$2"; i=$((i + 1)); done } # check-lines.sh tree "$tmp/l-ok"; lines 500 "$tmp/l-ok/crates/loopd/src/lib.rs" expect pass "500 lines is allowed" check-lines.sh "$tmp/l-ok" tree "$tmp/l-bad"; lines 501 "$tmp/l-bad/crates/loopd/src/deep/mod.rs" expect fail "501 lines in a nested file" check-lines.sh "$tmp/l-bad" tree "$tmp/l-test"; lines 501 "$tmp/l-test/crates/proto/tests/big.rs" expect fail "501 lines in a test file" check-lines.sh "$tmp/l-test" tree "$tmp/l-tgt"; lines 501 "$tmp/l-tgt/crates/proto/target/debug/gen.rs" expect pass "files under target/ are ignored" check-lines.sh "$tmp/l-tgt" # check-crate-deps.sh tree "$tmp/c-ok" expect pass "roles depend only on proto" check-crate-deps.sh "$tmp/c-ok" tree "$tmp/c-role"; manifest "$tmp/c-role/crates/loopd" loopd 'proto.workspace = true' 'brokerd.workspace = true' expect fail "role depends on another role" check-crate-deps.sh "$tmp/c-role" tree "$tmp/c-tbl"; manifest "$tmp/c-tbl/crates/loopd" loopd 'brokerd = { path = "../brokerd" }' expect fail "role depends on another role by path" check-crate-deps.sh "$tmp/c-tbl" tree "$tmp/c-dev"; printf '\n[dev-dependencies]\nbrokerd.workspace = true\n' >> "$tmp/c-dev/crates/loopd/Cargo.toml" expect fail "role dev-depends on another role" check-crate-deps.sh "$tmp/c-dev" tree "$tmp/c-proto"; manifest "$tmp/c-proto/crates/proto" proto 'loopd.workspace = true' expect fail "proto depends on a role" check-crate-deps.sh "$tmp/c-proto" # A dependency can also be written as its own table: [dependencies.NAME] tree "$tmp/c-sect"; printf '\n[dependencies.brokerd]\npath = "../brokerd"\n' >> "$tmp/c-sect/crates/loopd/Cargo.toml" expect fail "role depends on another role, written as a table" check-crate-deps.sh "$tmp/c-sect" tree "$tmp/c-sectw"; printf '\n[dev-dependencies.brokerd]\nworkspace = true\n' >> "$tmp/c-sectw/crates/loopd/Cargo.toml" expect fail "role dev-depends on another role, written as a table" check-crate-deps.sh "$tmp/c-sectw" tree "$tmp/c-dot"; manifest "$tmp/c-dot/crates/loopd" loopd 'proto.workspace = true' 'brokerd.path = "../brokerd"' expect fail "role depends on another role, written with a dotted key" check-crate-deps.sh "$tmp/c-dot" tree "$tmp/c-sectok"; manifest "$tmp/c-sectok/crates/loopd" loopd; printf '\n[dependencies.proto]\nworkspace = true\n' >> "$tmp/c-sectok/crates/loopd/Cargo.toml" expect pass "role depends on proto, written as a table" check-crate-deps.sh "$tmp/c-sectok" # check-dep-docs.sh tree "$tmp/d-ok" expect pass "every dependency is documented" check-dep-docs.sh "$tmp/d-ok" tree "$tmp/d-miss"; printf 'rand = "0.9"\n' >> "$tmp/d-miss/Cargo.toml" expect fail "workspace dependency without a docs row" check-dep-docs.sh "$tmp/d-miss" tree "$tmp/d-prose"; printf 'rand = "0.9"\n' >> "$tmp/d-prose/Cargo.toml"; printf '\nWe do not use rand.\n' >> "$tmp/d-prose/docs/dependencies.md" expect fail "a mention in prose is not a table row" check-dep-docs.sh "$tmp/d-prose" tree "$tmp/d-loose"; manifest "$tmp/d-loose/crates/loopd" loopd 'proto.workspace = true' 'rand = "0.9"' expect fail "crate declares a dependency outside the workspace table" check-dep-docs.sh "$tmp/d-loose" tree "$tmp/d-sect"; printf '\n[dependencies.rand]\nversion = "0.9"\n' >> "$tmp/d-sect/crates/loopd/Cargo.toml" expect fail "table-form dependency without workspace = true" check-dep-docs.sh "$tmp/d-sect" tree "$tmp/d-sectok"; printf '\n[dependencies.serde]\nworkspace = true\nfeatures = ["derive"]\n' >> "$tmp/d-sectok/crates/loopd/Cargo.toml" expect pass "table-form dependency with workspace = true" check-dep-docs.sh "$tmp/d-sectok" # check-runbook.sh book() { # book ROOT [HEADING-LINES...]: a tree whose runbook has these lines root="$1"; shift tree "$root" { printf '# Runbook\n\nProse that mentions grants-invalid is not an entry.\n\n' for line in "$@"; do printf '%s\n\nText.\n\n' "$line"; done } > "$root/docs/runbook.md" } src() { # src FILE TEXT: a source file holding TEXT mkdir -p "$(dirname "$1")" printf '%s\n' "$2" > "$1" } says() { # says NAME SCRIPT ROOT WORD...: the script's output must contain every WORD name="$1"; script="$2"; root="$3"; shift 3 output=$(sh "$here/$script" "$root" 2>&1) || true for word in "$@"; do case "$output" in *"$word"*) ;; *) echo "test-gate-scripts: $name: output lacks $word" >&2; fails=$((fails + 1)) ;; esac done } book "$tmp/r-ok" '## grants-invalid' '## audit-unavailable' src "$tmp/r-ok/crates/brokerd/src/a.rs" 'eprintln!("see docs/runbook.md#grants-invalid");' src "$tmp/r-ok/crates/brokerd/src/b.rs" '// see docs/runbook.md#audit-unavailable.' expect pass "every pointer has an entry" check-runbook.sh "$tmp/r-ok" book "$tmp/r-miss" '## audit-unavailable' src "$tmp/r-miss/crates/brokerd/src/a.rs" 'eprintln!("see docs/runbook.md#grants-invalid");' expect fail "a pointer without an entry; a mention in prose is not one" check-runbook.sh "$tmp/r-miss" book "$tmp/r-h3" '### grants-invalid' '## grants-invalid and more' ' ## grants-invalid' src "$tmp/r-h3/crates/brokerd/src/a.rs" 'eprintln!("see docs/runbook.md#grants-invalid");' expect fail "the entry is the whole line, at level two" check-runbook.sh "$tmp/r-h3" book "$tmp/r-test" '## grants-invalid' src "$tmp/r-test/crates/brokerd/src/a.rs" 'eprintln!("see docs/runbook.md#grants-invalid");' src "$tmp/r-test/crates/brokerd/tests/t.rs" 'assert!(m.ends_with("see docs/runbook.md#no-such-entry"));' expect fail "a pointer in a test file counts" check-runbook.sh "$tmp/r-test" book "$tmp/r-line" '## grants-invalid' src "$tmp/r-line/crates/brokerd/src/a.rs" 'f("docs/runbook.md#grants-invalid", "docs/runbook.md#second-on-the-line");' expect fail "the second pointer on a line counts" check-runbook.sh "$tmp/r-line" book "$tmp/r-tgt" '## grants-invalid' src "$tmp/r-tgt/crates/brokerd/src/a.rs" 'eprintln!("see docs/runbook.md#grants-invalid");' src "$tmp/r-tgt/crates/brokerd/target/debug/gen.rs" 'eprintln!("see docs/runbook.md#no-such-entry");' expect pass "files under target/ are ignored" check-runbook.sh "$tmp/r-tgt" book "$tmp/r-case" '## grants-invalid' src "$tmp/r-case/crates/brokerd/src/a.rs" 'eprintln!("see docs/runbook.md#Grants-Invalid");' expect fail "anchors are compared exactly" check-runbook.sh "$tmp/r-case" book "$tmp/r-fmt" '## grants-invalid' src "$tmp/r-fmt/crates/brokerd/src/a.rs" 'eprintln!("see docs/runbook.md#grants-invalid");' src "$tmp/r-fmt/crates/brokerd/src/b.rs" 'eprintln!("see docs/runbook.md#{anchor}");' expect fail "a pointer whose anchor is not written out" check-runbook.sh "$tmp/r-fmt" # Report every problem, not only the first. book "$tmp/r-all" '## grants-invalid' src "$tmp/r-all/crates/brokerd/src/a.rs" 'eprintln!("see docs/runbook.md#first-missing");' src "$tmp/r-all/crates/loopd/src/b.rs" 'eprintln!("see docs/runbook.md#second-missing");' expect fail "two pointers without entries" check-runbook.sh "$tmp/r-all" says "both missing entries and their files are reported" check-runbook.sh "$tmp/r-all" \ first-missing second-missing crates/brokerd/src/a.rs crates/loopd/src/b.rs # A check that cannot find what it checks must fail, not pass. mkdir -p "$tmp/empty" expect fail "check-lines without a crates directory" check-lines.sh "$tmp/empty" expect fail "check-crate-deps without a crates directory" check-crate-deps.sh "$tmp/empty" expect fail "check-dep-docs without a crates directory" check-dep-docs.sh "$tmp/empty" tree "$tmp/d-nodoc"; rm "$tmp/d-nodoc/docs/dependencies.md" expect fail "check-dep-docs without docs/dependencies.md" check-dep-docs.sh "$tmp/d-nodoc" expect fail "check-runbook without a crates directory" check-runbook.sh "$tmp/empty" book "$tmp/r-nobook" '## grants-invalid'; rm "$tmp/r-nobook/docs/runbook.md" src "$tmp/r-nobook/crates/brokerd/src/a.rs" 'eprintln!("see docs/runbook.md#grants-invalid");' expect fail "check-runbook without docs/runbook.md" check-runbook.sh "$tmp/r-nobook" book "$tmp/r-none" '## grants-invalid' expect fail "check-runbook when no source file has a pointer" check-runbook.sh "$tmp/r-none" if [ "$fails" -ne 0 ]; then echo "test-gate-scripts: $fails failure(s)" >&2 exit 1 fi echo "test-gate-scripts: ok"