Some checks failed
PDF engine / engine (push) Has been cancelled
PDF engine / makepad-integration (push) Has been cancelled
PDF engine / fuzz (push) Has been cancelled
repo hygiene / hygiene (push) Has been cancelled
Payment domain, storage, platform and UI / isolated-payment-tests (push) Has been cancelled
Payment domain, storage, platform and UI / payment-ui-tests (push) Has been cancelled
290 lines
12 KiB
Bash
Executable file
290 lines
12 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Isolated, self-cleaning Rust test runner for Nigig's pure domain crate.
|
|
#
|
|
# This deliberately tests a copied crate outside the workspace: the top-level
|
|
# workspace currently has sibling path dependencies (Makepad/Robius) that are
|
|
# not included in a standalone checkout. It installs Rust, Cargo caches, the
|
|
# crate copy and all build artifacts in one temporary directory, then deletes
|
|
# that directory on success, failure, Ctrl-C or termination.
|
|
#
|
|
# Usage:
|
|
# ./tools/test-rust-clean.sh
|
|
# RUST_TOOLCHAIN=1.85.0 ./tools/test-rust-clean.sh
|
|
# TEST_TARGET=platform ./tools/test-rust-clean.sh
|
|
# TEST_TARGET=pdf ./tools/test-rust-clean.sh
|
|
# TEST_TARGET=pdf-ui ./tools/test-rust-clean.sh # needs GUI system libs
|
|
# TEST_TARGET=valhalla ./tools/test-rust-clean.sh
|
|
# KEEP_TEST_ENV=1 ./tools/test-rust-clean.sh # debugging only; prints path
|
|
#
|
|
# It does not modify source files, Cargo.lock, ~/.cargo, ~/.rustup or the
|
|
# workspace target directory.
|
|
|
|
set -Eeuo pipefail
|
|
IFS=$'\n\t'
|
|
|
|
REPO_ROOT="$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
DOMAIN_DIR="$REPO_ROOT/crates/nigig-pay-domain"
|
|
STORAGE_DIR="$REPO_ROOT/crates/nigig-pay-storage"
|
|
PLATFORM_DIR="$REPO_ROOT/crates/nigig-pay-platform"
|
|
# The platform crate's Android adapter is an optional feature, so its USSD
|
|
# path is exercised separately from the default (portable) test run.
|
|
USSD_DIR="$REPO_ROOT/crates/robius-ussd"
|
|
VALHALLA_DIR="$REPO_ROOT/crates/apps/valhalla"
|
|
VALHALLA_CRATES=(valhalla-core valhalla-cost valhalla-path valhalla-search valhalla-match valhalla-narrative valhalla-elevation valhalla-service valhalla-makepad valhalla-builder valhalla-diff)
|
|
TEST_TARGET="${TEST_TARGET:-domain}"
|
|
SPREADSHEET_DIR="$REPO_ROOT/crates/apps/spreadsheet/spreadsheet-engine"
|
|
SPREADSHEET_UI_DIR="$REPO_ROOT/crates/apps/spreadsheet/spreadsheet-ui"
|
|
PDF_DIR="$REPO_ROOT/crates/apps/pdf"
|
|
# The PDF crates form a layered stack (cos <- document <- graphics) wired by
|
|
# relative path dependencies, so all three are copied together and each is
|
|
# tested in dependency order. pdf-makepad is excluded on purpose: it needs
|
|
# makepad_draw, which is not available in a standalone checkout.
|
|
PDF_CRATES=(pdf-cos pdf-document pdf-graphics)
|
|
# pdf-makepad additionally needs Makepad and system GUI libraries, so it is
|
|
# a separate target rather than part of the default pdf run.
|
|
PDF_UI_CRATES=(pdf-cos pdf-document pdf-graphics pdf-makepad)
|
|
# Read the toolchain from rust-toolchain.toml so the runner can never drift
|
|
# from the repository's declared version. Override only deliberately.
|
|
declared_toolchain() {
|
|
sed -n 's/^[[:space:]]*channel[[:space:]]*=[[:space:]]*"\([^"]*\)".*/\1/p' \
|
|
"$REPO_ROOT/rust-toolchain.toml" | head -n 1
|
|
}
|
|
TOOLCHAIN="${RUST_TOOLCHAIN:-$(declared_toolchain)}"
|
|
if [[ -z "$TOOLCHAIN" ]]; then
|
|
echo "error: could not read channel from rust-toolchain.toml" >&2
|
|
exit 2
|
|
fi
|
|
KEEP_TEST_ENV="${KEEP_TEST_ENV:-0}"
|
|
CHECK_FORMAT="${CHECK_FORMAT:-1}"
|
|
|
|
if [[ ! -f "$DOMAIN_DIR/Cargo.toml" ]]; then
|
|
echo "error: expected pure domain crate at $DOMAIN_DIR" >&2
|
|
exit 2
|
|
fi
|
|
if [[ "$TEST_TARGET" != "domain" && "$TEST_TARGET" != "storage" \
|
|
&& "$TEST_TARGET" != "platform" \
|
|
&& "$TEST_TARGET" != "spreadsheet" && "$TEST_TARGET" != "spreadsheet-ui" \
|
|
&& "$TEST_TARGET" != "spreadsheet-ui-check" \
|
|
&& "$TEST_TARGET" != "spreadsheet-ui-unit" \
|
|
&& "$TEST_TARGET" != "pdf" && "$TEST_TARGET" != "pdf-ui" \
|
|
&& "$TEST_TARGET" != "valhalla" ]]; then
|
|
echo "error: TEST_TARGET must be domain, storage, platform, spreadsheet," >&2
|
|
echo " spreadsheet-ui," >&2
|
|
echo " pdf, pdf-ui, or valhalla" >&2
|
|
exit 2
|
|
fi
|
|
if [[ "$TEST_TARGET" == "pdf" ]]; then
|
|
for crate in "${PDF_CRATES[@]}"; do
|
|
if [[ ! -f "$PDF_DIR/$crate/Cargo.toml" ]]; then
|
|
echo "error: expected PDF crate at $PDF_DIR/$crate" >&2
|
|
exit 2
|
|
fi
|
|
done
|
|
fi
|
|
if [[ "$TEST_TARGET" == "valhalla" ]]; then
|
|
for crate in "${VALHALLA_CRATES[@]}"; do
|
|
if [[ ! -f "$VALHALLA_DIR/$crate/Cargo.toml" ]]; then
|
|
echo "error: expected Valhalla crate at $VALHALLA_DIR/$crate" >&2
|
|
exit 2
|
|
fi
|
|
done
|
|
fi
|
|
if [[ "$TEST_TARGET" == "spreadsheet" && ! -f "$SPREADSHEET_DIR/Cargo.toml" ]]; then
|
|
echo "error: expected spreadsheet engine crate at $SPREADSHEET_DIR" >&2
|
|
exit 2
|
|
fi
|
|
if [[ ("$TEST_TARGET" == "spreadsheet-ui" || "$TEST_TARGET" == "spreadsheet-ui-check" || "$TEST_TARGET" == "spreadsheet-ui-unit") && ! -f "$SPREADSHEET_UI_DIR/Cargo.toml" ]]; then
|
|
echo "error: expected spreadsheet UI crate at $SPREADSHEET_UI_DIR" >&2
|
|
exit 2
|
|
fi
|
|
if [[ "$TEST_TARGET" == "storage" && ! -f "$STORAGE_DIR/Cargo.toml" ]]; then
|
|
echo "error: expected storage crate at $STORAGE_DIR" >&2
|
|
exit 2
|
|
fi
|
|
if [[ "$TEST_TARGET" == "platform" && ! -f "$PLATFORM_DIR/Cargo.toml" ]]; then
|
|
echo "error: expected platform crate at $PLATFORM_DIR" >&2
|
|
exit 2
|
|
fi
|
|
|
|
mkdir -p "$HOME/tmp"
|
|
WORK_DIR="$(mktemp -d "${TMPDIR:-$HOME/tmp}/nigig-rust-test.XXXXXXXX")"
|
|
cleanup() {
|
|
local status=$?
|
|
if [[ "$KEEP_TEST_ENV" == "1" ]]; then
|
|
echo "test environment retained for debugging: $WORK_DIR" >&2
|
|
else
|
|
rm -rf -- "$WORK_DIR"
|
|
echo "cleaned isolated Rust test environment" >&2
|
|
fi
|
|
exit "$status"
|
|
}
|
|
trap cleanup EXIT HUP INT TERM
|
|
|
|
export RUSTUP_HOME="$WORK_DIR/rustup"
|
|
export CARGO_HOME="$WORK_DIR/cargo"
|
|
export CARGO_TARGET_DIR="$WORK_DIR/target"
|
|
export PATH="$CARGO_HOME/bin:$PATH"
|
|
export CARGO_NET_GIT_FETCH_WITH_CLI=true
|
|
export CARGO_INCREMENTAL=0
|
|
export RUSTFLAGS="-C strip=debuginfo"
|
|
|
|
RUSTUP_INIT="$WORK_DIR/rustup-init"
|
|
echo "creating isolated Rust toolchain ($TOOLCHAIN) in $WORK_DIR" >&2
|
|
curl --fail --location --proto '=https' --tlsv1.2 \
|
|
https://sh.rustup.rs -o "$RUSTUP_INIT"
|
|
chmod 700 "$RUSTUP_INIT"
|
|
"$RUSTUP_INIT" -y --profile minimal --default-toolchain "$TOOLCHAIN" \
|
|
--component rustfmt --component clippy --no-modify-path
|
|
|
|
# Avoid workspace resolution: copy only the selected pure crate(s) and test
|
|
# their manifest. Storage needs the sibling domain crate for its relative path.
|
|
TEST_CRATE="$WORK_DIR/nigig-pay-domain"
|
|
cp -a "$DOMAIN_DIR" "$TEST_CRATE"
|
|
if [[ "$TEST_TARGET" == "storage" ]]; then
|
|
TEST_CRATE="$WORK_DIR/nigig-pay-storage"
|
|
cp -a "$STORAGE_DIR" "$TEST_CRATE"
|
|
elif [[ "$TEST_TARGET" == "platform" ]]; then
|
|
# robius-ussd is an *optional* dependency, but Cargo still resolves its
|
|
# path at manifest-read time, so it has to travel with the crate.
|
|
TEST_CRATE="$WORK_DIR/nigig-pay-platform"
|
|
cp -a "$PLATFORM_DIR" "$TEST_CRATE"
|
|
cp -a "$USSD_DIR" "$WORK_DIR/robius-ussd"
|
|
elif [[ "$TEST_TARGET" == "spreadsheet" ]]; then
|
|
TEST_CRATE="$WORK_DIR/spreadsheet-engine"
|
|
cp -a "$SPREADSHEET_DIR" "$TEST_CRATE"
|
|
elif [[ "$TEST_TARGET" == "spreadsheet-ui" || "$TEST_TARGET" == "spreadsheet-ui-check" || "$TEST_TARGET" == "spreadsheet-ui-unit" ]]; then
|
|
# Preserve the UI crate's sibling engine path dependency.
|
|
mkdir -p "$WORK_DIR/spreadsheet"
|
|
cp -a "$SPREADSHEET_DIR" "$WORK_DIR/spreadsheet/spreadsheet-engine"
|
|
cp -a "$SPREADSHEET_UI_DIR" "$WORK_DIR/spreadsheet/spreadsheet-ui"
|
|
TEST_CRATE="$WORK_DIR/spreadsheet/spreadsheet-ui"
|
|
elif [[ "$TEST_TARGET" == "pdf" ]]; then
|
|
for crate in "${PDF_CRATES[@]}"; do
|
|
cp -a "$PDF_DIR/$crate" "$WORK_DIR/$crate"
|
|
done
|
|
# The regression corpus sits beside the crates and is referenced by a
|
|
# relative path, so it has to come along or the corpus tests cannot
|
|
# find their fixtures.
|
|
mkdir -p "$WORK_DIR/tests"
|
|
cp -a "$PDF_DIR/tests/corpus" "$WORK_DIR/tests/corpus"
|
|
elif [[ "$TEST_TARGET" == "pdf-ui" ]]; then
|
|
for crate in "${PDF_UI_CRATES[@]}"; do
|
|
cp -a "$PDF_DIR/$crate" "$WORK_DIR/$crate"
|
|
done
|
|
mkdir -p "$WORK_DIR/tests"
|
|
cp -a "$PDF_DIR/tests/corpus" "$WORK_DIR/tests/corpus"
|
|
elif [[ "$TEST_TARGET" == "valhalla" ]]; then
|
|
for crate in "${VALHALLA_CRATES[@]}"; do
|
|
cp -a "$VALHALLA_DIR/$crate" "$WORK_DIR/$crate"
|
|
done
|
|
TEST_CRATE="$WORK_DIR/valhalla-makepad"
|
|
fi
|
|
|
|
cargo --version
|
|
rustc --version
|
|
if [[ "$TEST_TARGET" == "spreadsheet-ui" || "$TEST_TARGET" == "spreadsheet-ui-check" || "$TEST_TARGET" == "spreadsheet-ui-unit" ]]; then
|
|
# Makepad's Linux backend uses native Wayland/X11 libraries. Keep this
|
|
# preflight separate from Rust/Cargo caches; the runner only cleans its
|
|
# isolated temporary environment and never removes host packages.
|
|
"$REPO_ROOT/tools/makepad-native-libs.sh" --check
|
|
fi
|
|
|
|
# Run tests
|
|
if [[ "$TEST_TARGET" == "domain" || "$TEST_TARGET" == "storage" \
|
|
|| "$TEST_TARGET" == "platform" ]]; then
|
|
if [[ ! -f "$TEST_CRATE/Cargo.lock" ]]; then
|
|
echo "error: $TEST_TARGET crate has no checked-in Cargo.lock" >&2
|
|
exit 2
|
|
fi
|
|
cargo test --manifest-path "$TEST_CRATE/Cargo.toml" --all-targets --locked
|
|
elif [[ "$TEST_TARGET" == "pdf" ]]; then
|
|
for crate in "${PDF_CRATES[@]}"; do
|
|
echo "--- testing $crate" >&2
|
|
cargo test --manifest-path "$WORK_DIR/$crate/Cargo.toml" --all-targets
|
|
done
|
|
elif [[ "$TEST_TARGET" == "pdf-ui" ]]; then
|
|
for crate in "${PDF_UI_CRATES[@]}"; do
|
|
echo "--- testing $crate" >&2
|
|
cargo test --manifest-path "$WORK_DIR/$crate/Cargo.toml" --all-targets
|
|
done
|
|
elif [[ "$TEST_TARGET" == "spreadsheet-ui-check" ]]; then
|
|
cargo check --manifest-path "$TEST_CRATE/Cargo.toml" --lib --bins
|
|
elif [[ "$TEST_TARGET" == "spreadsheet-ui-unit" ]]; then
|
|
cargo test --manifest-path "$TEST_CRATE/Cargo.toml" --lib
|
|
elif [[ "$TEST_TARGET" == "valhalla" ]]; then
|
|
for crate in "${VALHALLA_CRATES[@]}"; do
|
|
echo "--- testing $crate" >&2
|
|
cargo test --manifest-path "$WORK_DIR/$crate/Cargo.toml" --all-targets
|
|
done
|
|
else
|
|
cargo test --manifest-path "$TEST_CRATE/Cargo.toml" --all-targets
|
|
fi
|
|
|
|
# Formatting is a source-quality check, not a formatter: never rewrite files.
|
|
if [[ "$CHECK_FORMAT" == "1" && "$TEST_TARGET" == "pdf-ui" ]]; then
|
|
for crate in "${PDF_UI_CRATES[@]}"; do
|
|
cargo fmt --manifest-path "$WORK_DIR/$crate/Cargo.toml" -- --check
|
|
done
|
|
elif [[ "$CHECK_FORMAT" == "1" && "$TEST_TARGET" == "pdf" ]]; then
|
|
for crate in "${PDF_CRATES[@]}"; do
|
|
cargo fmt --manifest-path "$WORK_DIR/$crate/Cargo.toml" -- --check
|
|
done
|
|
elif [[ "$CHECK_FORMAT" == "1" && "$TEST_TARGET" == "valhalla" ]]; then
|
|
for crate in "${VALHALLA_CRATES[@]}"; do
|
|
cargo fmt --manifest-path "$WORK_DIR/$crate/Cargo.toml" -- --check
|
|
done
|
|
elif [[ "$CHECK_FORMAT" == "1" ]]; then
|
|
cargo fmt --manifest-path "$TEST_CRATE/Cargo.toml" -- --check
|
|
else
|
|
echo "format check skipped (CHECK_FORMAT=$CHECK_FORMAT)" >&2
|
|
fi
|
|
|
|
if [[ "$TEST_TARGET" == "domain" || "$TEST_TARGET" == "storage" \
|
|
|| "$TEST_TARGET" == "platform" ]]; then
|
|
cargo clippy --manifest-path "$TEST_CRATE/Cargo.toml" --all-targets --locked -- -D warnings
|
|
fi
|
|
|
|
# The PDF stack parses untrusted input, so a warning there is a real defect
|
|
# class (silent truncation, unwrap on malformed bytes). Gate it on warnings.
|
|
if [[ "$TEST_TARGET" == "pdf" || "$TEST_TARGET" == "pdf-ui" ]]; then
|
|
if [[ "$TEST_TARGET" == "pdf-ui" ]]; then
|
|
clippy_crates=("${PDF_UI_CRATES[@]}")
|
|
else
|
|
clippy_crates=("${PDF_CRATES[@]}")
|
|
fi
|
|
for crate in "${clippy_crates[@]}"; do
|
|
cargo clippy --manifest-path "$WORK_DIR/$crate/Cargo.toml" \
|
|
--all-targets -- -D warnings
|
|
done
|
|
fi
|
|
|
|
if [[ "$TEST_TARGET" == "platform" ]]; then
|
|
# The Android adapter compiles for the host even though it only *runs* on
|
|
# a device, so a mismapped provider error is caught here rather than on a
|
|
# phone.
|
|
cargo test --manifest-path "$TEST_CRATE/Cargo.toml" --locked --features ussd
|
|
cargo clippy --manifest-path "$TEST_CRATE/Cargo.toml" --all-targets --locked \
|
|
--features ussd -- -D warnings
|
|
|
|
# Review item 5.4: MockGateway fabricates confirmations. A release build
|
|
# that enables it must fail to compile. This asserts the guard is real
|
|
# rather than trusting the comment above it.
|
|
if cargo build --manifest-path "$TEST_CRATE/Cargo.toml" --locked \
|
|
--release --features mock >/dev/null 2>&1; then
|
|
echo "error: the mock gateway compiled into a release build" >&2
|
|
exit 1
|
|
fi
|
|
echo "mock gateway is correctly refused in release builds" >&2
|
|
fi
|
|
|
|
if [[ "$TEST_TARGET" == "storage" ]]; then
|
|
cargo test --manifest-path "$TEST_CRATE/Cargo.toml" --locked --features sqlcipher
|
|
cargo clippy --manifest-path "$TEST_CRATE/Cargo.toml" --all-targets --locked \
|
|
--features sqlcipher -- -D warnings
|
|
fi
|
|
|
|
if [[ "$TEST_TARGET" == "domain" ]]; then
|
|
cargo bench --manifest-path "$TEST_CRATE/Cargo.toml" --locked
|
|
fi
|
|
|
|
echo "isolated $TEST_TARGET tests passed" >&2
|