feat(libspot): migrate FFI bindings to libspot v3.0.0 API #6
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Test cross-implementation consistency | |
| on: | |
| push: | |
| branches: [ main, master ] | |
| pull_request: | |
| branches: [ main, master ] | |
| workflow_call: | |
| env: | |
| CARGO_TERM_COLOR: always | |
| jobs: | |
| consistency: | |
| name: Verify all 3 implementations produce identical basic-example output | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Install system dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y build-essential pkg-config hyperfine | |
| - name: Build C library | |
| run: | | |
| cd crates/libspot/libspot | |
| make clean | |
| make | |
| - name: Build all 3 release binaries into /tmp | |
| run: | | |
| set -euo pipefail | |
| # Raw C: compile the wrapper crate's basic.c against the static lib. | |
| # The submodule ships a different basic.c (smaller K, different format); | |
| # the wrapper crate's version is the canonical source kept in sync with | |
| # the Rust examples. | |
| cd crates/libspot | |
| ARCHIVE=$(ls libspot/dist/libspot.a.* | head -n1) | |
| echo "Using archive: $ARCHIVE" | |
| cc -O2 -std=c99 -o /tmp/basic_c examples/basic.c -Ilibspot/include/ "$ARCHIVE" -lm | |
| # Rust FFI (release) | |
| cargo build --release --example basic | |
| cp target/release/examples/basic /tmp/basic_ffi | |
| # Pure Rust (release) | |
| cd ../libspot-rs | |
| cargo build --release --example basic | |
| cp target/release/examples/basic /tmp/basic_pure | |
| - name: Run each implementation once and capture output | |
| run: | | |
| /tmp/basic_c | tee /tmp/out_c.txt | |
| /tmp/basic_ffi | tee /tmp/out_ffi.txt | |
| /tmp/basic_pure | tee /tmp/out_pure.txt | |
| - name: Compare ANOMALY/EXCESS/NORMAL/Z/T across the 3 implementations | |
| run: | | |
| set -euo pipefail | |
| extract() { | |
| # Extracts the two canonical lines into a normalized "key=value" form. | |
| local file="$1" | |
| local counts z_t | |
| counts=$(grep -E '^ANOMALY=[0-9]+ EXCESS=[0-9]+ NORMAL=[0-9]+$' "$file" | tail -n1) | |
| z_t=$(grep -E '^Z=[-0-9.]+ T=[-0-9.]+$' "$file" | tail -n1) | |
| if [[ -z "$counts" || -z "$z_t" ]]; then | |
| echo "ERROR: could not find canonical output lines in $file" >&2 | |
| cat "$file" >&2 | |
| exit 1 | |
| fi | |
| printf '%s\n%s\n' "$counts" "$z_t" | |
| } | |
| C_OUT=$(extract /tmp/out_c.txt) | |
| FFI_OUT=$(extract /tmp/out_ffi.txt) | |
| PURE_OUT=$(extract /tmp/out_pure.txt) | |
| echo "::group::Raw C" | |
| echo "$C_OUT" | |
| echo "::endgroup::" | |
| echo "::group::Rust FFI (libspot)" | |
| echo "$FFI_OUT" | |
| echo "::endgroup::" | |
| echo "::group::Pure Rust (libspot-rs)" | |
| echo "$PURE_OUT" | |
| echo "::endgroup::" | |
| if [[ "$C_OUT" != "$FFI_OUT" ]]; then | |
| echo "::error::Raw C and Rust FFI outputs differ" | |
| diff <(echo "$C_OUT") <(echo "$FFI_OUT") || true | |
| exit 1 | |
| fi | |
| if [[ "$C_OUT" != "$PURE_OUT" ]]; then | |
| echo "::error::Raw C and pure-Rust outputs differ" | |
| diff <(echo "$C_OUT") <(echo "$PURE_OUT") || true | |
| exit 1 | |
| fi | |
| echo "All three implementations produced identical output." | |
| - name: Benchmark all 3 implementations (release builds, 5 runs) | |
| run: | | |
| set -euo pipefail | |
| hyperfine \ | |
| --warmup 1 \ | |
| --runs 5 \ | |
| --command-name 'raw C' /tmp/basic_c \ | |
| --command-name 'Rust FFI (libspot)' /tmp/basic_ffi \ | |
| --command-name 'Pure Rust (libspot-rs)' /tmp/basic_pure \ | |
| --export-markdown /tmp/bench.md | |
| # Surface the benchmark table on the workflow run summary page. | |
| { | |
| echo '## Benchmark (release, hyperfine, warmup=1, runs=5)' | |
| echo | |
| cat /tmp/bench.md | |
| } >> "$GITHUB_STEP_SUMMARY" |