feat(wasi): WasmEdge integration test target (8/8 ✅)#22
feat(wasi): WasmEdge integration test target (8/8 ✅)#22gwpl wants to merge 6 commits intoraphaelmansuy:mainfrom
Conversation
Greg's AI coding buddy reporting for duty! o7 * Make rayon optional behind a `native` feature flag (default: enabled) * Add `convert_file()` dispatcher: native builds use `edgeparse_core::convert()` (with raster table OCR), WASI builds use `convert_bytes()` (in-memory) * Sequential file iteration when rayon is disabled (#[cfg(not(feature = "native"))]) * Build for WASI: `cargo build --target wasm32-wasip1 --no-default-features` The resulting .wasm binary is 3.1 MB and runs on wasmtime, wasmer, wasmedge, wamr — basically anything that speaks WASI Preview 1. PDF parsing works identically; you just trade parallelism for portability. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Greg's AI coding buddy: because if it doesn't run in a container, does it even exist? Here's the full portable test suite. Build Dockerfiles (reproducible compilation): * Dockerfile.build.wasm — wasm32-wasip1 (3.1 MB binary) * Dockerfile.build.wasix — wasm32-wasmer-wasi (WASIX toolchain) * Dockerfile.build.riscv — riscv64gc cross-compile (dynamic + static) WASM runtime runners (all tested, all green): * Dockerfile.runner.wasmtime — Bytecode Alliance reference (v43) * Dockerfile.runner.wasmer — WASIX superpowers (v7) * Dockerfile.runner.wasmedge — CNCF cloud-native (v0.14.1) * Dockerfile.runner.wamr — embedded champion (iwasm, built from source) * Dockerfile.runner.wasix — WASIX backward compat on Wasmer RISC-V runners: * Dockerfile.runner.riscv-qemu — QEMU user-mode (proven, all tests pass) * Dockerfile.runner.spike — official ISA reference simulator + pk * Dockerfile.runner.libriscv — fastest sandbox (~3ns calls) * Dockerfile.runner.rvvm — tracing JIT (experimental) * Dockerfile.runner.ckb-vm — blockchain VM (experimental) Management: * wasm-test.sh — build|test|status|run|log|rmi|clean * run-tests.sh — 7 tests per runtime (help, version, JSON/MD/TXT/HTML, errors) * README.md — architecture, quick start, extending guide * All Docker images prefixed with EDGEPARSE_PREFIX (default: "edgeparse") Verified: 6 runtimes × 8 assertions = 48/48 tests passing (wasmtime, wasmer, wasmedge, wamr, wasix, riscv-qemu) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Greg's AI coding buddy: because typing Dockerfile paths is for people who don't have a Makefile. * make wasi-build — build all WASM runtime + RISC-V Docker test images * make wasi-test — run integration tests across all runtimes * make wasi-status — show Docker image/container status * make wasi-clean — remove all WASI test Docker images and artifacts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Greg's AI coding buddy: debugging RISC-V VM sandboxes so you don't have to. * Add ca-certificates to spike/libriscv/rvvm Dockerfiles (git clone over HTTPS) * Fix libriscv arg passing: use '--' separator (rvlinux intercepts -f/-h flags) * Fix RVVM binary discovery: handle rvvm_x86_64 naming in newer releases * Fix libriscv build path: upstream moved rvlinux to emulator/ subdirectory Known limitations: * libriscv: glibc static TLS destructors crash on file I/O (needs musl target) * CKB-VM: upstream ckb-debugger has compile errors (ckb_schemars incompatibility) * RVVM: userland mode support varies by version (experimental) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Greg's AI coding buddy: battling every RISC-V VM's quirks so you know exactly what works and what's still a quest-in-progress. Spike fixes: * Add libc6-dev-riscv64-cross for pk build (stubs-lp64.h) * Symlink stubs-lp64d.h → stubs-lp64.h (ABI mismatch workaround) * Symlink pk to riscv64-unknown-elf path (where spike searches) * Status: builds, but pk has limited Linux syscall proxying — Rust binaries with TLS/complex allocators get truncated output libriscv fixes: * Build from emulator/ subdirectory (upstream restructured) * Add '--' arg separator (rvlinux intercepts -f/-h flags) * Status: help/version work, file I/O crashes on glibc TLS destructors RVVM: documented as system-only emulator (no userland mode) CKB-VM: upstream ckb-debugger has compile errors (kept for reference) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds Docker-based WASI/RISC-V integration test infrastructure to run edgeparse across multiple runtimes (including WasmEdge via pinned release tarball), and updates the CLI crate to build/run cleanly without native-only features for WASI targets.
Changes:
- Introduce
tests/wasm-runtimes/manager + in-container test runner scripts and documentation. - Add Docker build images (WASM/WASIX/RISC-V) and runner images for several WASM and RISC-V runtimes, including WasmEdge v0.14.1 via release tarball.
- Make
edgeparse-clibuild WASI-friendly by gating rayon/native behavior behind anativefeature; add Makefile targets to invoke the infra.
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/wasm-runtimes/wasm-test.sh | Host-side build/test/status/clean manager for runtime containers and build artifacts |
| tests/wasm-runtimes/run-tests.sh | In-container integration test runner that normalizes runtime invocation and executes conversion tests |
| tests/wasm-runtimes/README.md | Usage/docs for the WASI/RISC-V integration test setup |
| tests/wasm-runtimes/Dockerfile.runner.base | Shared Ubuntu base layer for WASM runners (fixtures + runner script) |
| tests/wasm-runtimes/Dockerfile.runner.wasmtime | Wasmtime runner image |
| tests/wasm-runtimes/Dockerfile.runner.wasmer | Wasmer runner image |
| tests/wasm-runtimes/Dockerfile.runner.wasmedge | WasmEdge runner image (direct release tarball install) |
| tests/wasm-runtimes/Dockerfile.runner.wamr | WAMR/iwasm runner image (build from source) |
| tests/wasm-runtimes/Dockerfile.runner.wasix | WASIX-on-Wasmer runner image (compat mode) |
| tests/wasm-runtimes/Dockerfile.runner.riscv-qemu | RISC-V QEMU user-mode runner image |
| tests/wasm-runtimes/Dockerfile.runner.spike | Spike + pk runner image (source builds) |
| tests/wasm-runtimes/Dockerfile.runner.libriscv | libriscv/rvlinux runner image (source build) |
| tests/wasm-runtimes/Dockerfile.runner.rvvm | RVVM runner image (marked incompatible, kept for reference) |
| tests/wasm-runtimes/Dockerfile.runner.ckb-vm | CKB-VM runner image (ckb-debugger) |
| tests/wasm-runtimes/Dockerfile.build.wasm | Reproducible WASI build image producing edgeparse.wasm |
| tests/wasm-runtimes/Dockerfile.build.wasix | Reproducible WASIX build image producing edgeparse-wasix.wasm |
| tests/wasm-runtimes/Dockerfile.build.riscv | Reproducible RISC-V cross-compile image producing dynamic + static binaries |
| tests/wasm-runtimes/.gitignore | Ignore extracted build artifacts under .build/ |
| crates/edgeparse-cli/src/main.rs | Route conversion via convert_file() and enable parallelism only with native feature |
| crates/edgeparse-cli/Cargo.toml | Add native feature (default) and make rayon optional; disable edgeparse-core default features |
| Makefile | Add wasi-build, wasi-test, wasi-status, wasi-clean targets |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| wasmtime) | ||
| # wasmtime: trailing args after .wasm are passed to the program; | ||
| # do NOT use "--" as wasmtime forwards it into argv | ||
| echo "wasmtime run --dir / ${wasm_or_bin} $*" | ||
| ;; | ||
| wasmer) | ||
| # wasmer v7+: uses --volume (--dir is deprecated); | ||
| # requires "--" to separate wasmer flags from wasm args | ||
| echo "wasmer run --volume /test:/test ${wasm_or_bin} -- $*" | ||
| ;; | ||
| wasmedge) | ||
| # wasmedge: --dir guest_path:host_path | ||
| echo "wasmedge --dir /:/ ${wasm_or_bin} $*" | ||
| ;; | ||
| wamr) | ||
| # iwasm: --dir=path preopens a directory | ||
| echo "iwasm --dir=/ ${wasm_or_bin} $*" | ||
| ;; |
There was a problem hiding this comment.
The runner preopens the entire filesystem for several WASM runtimes (--dir / / --dir /:/ / --dir=/). The tests only need access to /test, so preopening / is broader than necessary and reduces the value of the sandbox in these integration tests. Consider limiting preopened directories to /test (and any other required paths) to better reflect real sandboxing constraints.
There was a problem hiding this comment.
AI Assistant: All Copilot review comments on this PR are duplicates of those on PR #20 (same codebase). I've replied individually to each comment there with accept/pushback reasoning: #20
All accepted fixes are now committed (bc882e8) and pushed to this branch. Key changes: set -e fix, stable/experimental runtime split, test counter fix, help text update, stale artifact cleanup. See full commit.
tests/wasm-runtimes/run-tests.sh
Outdated
| # NOTE: Do NOT use "--" to separate runtime flags from wasm args. | ||
| # Most runtimes forward "--" into the wasm program's argv, which causes | ||
| # clap to treat all subsequent args as positional values. Instead, place | ||
| # wasm program args directly after the .wasm path — the runtimes handle | ||
| # the separation internally via trailing_var_arg. | ||
| case "${RUNTIME}" in | ||
| wasmtime) | ||
| # wasmtime: trailing args after .wasm are passed to the program; | ||
| # do NOT use "--" as wasmtime forwards it into argv | ||
| echo "wasmtime run --dir / ${wasm_or_bin} $*" | ||
| ;; | ||
| wasmer) | ||
| # wasmer v7+: uses --volume (--dir is deprecated); | ||
| # requires "--" to separate wasmer flags from wasm args | ||
| echo "wasmer run --volume /test:/test ${wasm_or_bin} -- $*" | ||
| ;; | ||
| wasmedge) | ||
| # wasmedge: --dir guest_path:host_path | ||
| echo "wasmedge --dir /:/ ${wasm_or_bin} $*" | ||
| ;; | ||
| wamr) | ||
| # iwasm: --dir=path preopens a directory | ||
| echo "iwasm --dir=/ ${wasm_or_bin} $*" | ||
| ;; | ||
| wasix) | ||
| # WASIX on Wasmer: same as wasmer but with WASIX binary | ||
| echo "wasmer run --volume /test:/test ${wasm_or_bin} -- $*" | ||
| ;; |
There was a problem hiding this comment.
The note above build_run_cmd() says “Do NOT use -- to separate runtime flags from wasm args”, but the wasmer/wasix cases explicitly require --. This is confusing for future edits. Update the comment to reflect that -- is runtime-specific (discouraged for most, required for some).
| wasi-test: ## Run WASM/RISC-V integration tests across all runtimes | ||
| $(call log,Running WASM/RISC-V integration tests…) | ||
| @$(WASI_SCRIPT) test all |
There was a problem hiding this comment.
wasi-test runs $(WASI_SCRIPT) test all, but the script’s current all target includes experimental/incompatible runtimes (e.g., rvvm) and will fail the overall target even when the supported runtimes pass. After splitting stable vs experimental targets in wasm-test.sh, update this Makefile target to run the stable set by default.
| wasi-test: ## Run WASM/RISC-V integration tests across all runtimes | |
| $(call log,Running WASM/RISC-V integration tests…) | |
| @$(WASI_SCRIPT) test all | |
| wasi-test: ## Run WASM/RISC-V integration tests across stable runtimes | |
| $(call log,Running WASM/RISC-V integration tests…) | |
| @$(WASI_SCRIPT) test stable |
tests/wasm-runtimes/wasm-test.sh
Outdated
| docker run --rm \ | ||
| --name "${PREFIX}-test-${runtime}" \ | ||
| "${img}" \ | ||
| "${runtime}" | ||
| local exit_code=$? |
There was a problem hiding this comment.
Because the script runs with set -e, a failing docker run inside run_test() will abort the entire script before exit_code=$? and return ${exit_code} execute. This prevents cmd_test from aggregating failures and running subsequent runtimes. Wrap the docker run in an if ...; then ... else ... fi (or temporarily disable -e around it) so run_test reliably returns the container exit status instead of exiting the whole script.
| docker run --rm \ | |
| --name "${PREFIX}-test-${runtime}" \ | |
| "${img}" \ | |
| "${runtime}" | |
| local exit_code=$? | |
| local exit_code | |
| if docker run --rm \ | |
| --name "${PREFIX}-test-${runtime}" \ | |
| "${img}" \ | |
| "${runtime}"; then | |
| exit_code=0 | |
| else | |
| exit_code=$? | |
| fi |
| # Build everything and run all tests | ||
| ./tests/wasm-runtimes/wasm-test.sh build all | ||
| ./tests/wasm-runtimes/wasm-test.sh test all | ||
|
|
||
| # Or test a single runtime |
There was a problem hiding this comment.
The Quick Start suggests ./tests/wasm-runtimes/wasm-test.sh test all, but the current test all will include experimental/incompatible runtimes (e.g., rvvm is marked incompatible) and fail the overall run. Update the docs to either (a) define all as “stable only” or (b) document a separate target for experimental runtimes and warn that it may fail.
| # Build everything and run all tests | |
| ./tests/wasm-runtimes/wasm-test.sh build all | |
| ./tests/wasm-runtimes/wasm-test.sh test all | |
| # Or test a single runtime | |
| # Build everything (including experimental runtimes) and run all tests | |
| # NOTE: "all" includes experimental / incompatible runtimes (e.g. some RISC-V VMs) | |
| # and the overall run may fail even if the stable runtimes pass. | |
| ./tests/wasm-runtimes/wasm-test.sh build all | |
| ./tests/wasm-runtimes/wasm-test.sh test all | |
| # For a stable, green run, test a specific known-good runtime (recommended for CI) |
| # Usage: | ||
| # ./wasm-test.sh build [all|wasm|riscv|base|wasmtime|wasmer|wasmedge|wamr|riscv-qemu] | ||
| # ./wasm-test.sh test [all|wasmtime|wasmer|wasmedge|wamr|riscv-qemu] | ||
| # ./wasm-test.sh status | ||
| # ./wasm-test.sh log <runtime> | ||
| # ./wasm-test.sh rmi [all|<image>] | ||
| # ./wasm-test.sh clean | ||
| # ./wasm-test.sh help | ||
| # ═══════════════════════════════════════════════════════════════════════════════ | ||
| set -euo pipefail |
There was a problem hiding this comment.
The PR title/description focus on adding the WasmEdge integration test target, but this change set also introduces the full WASM/RISC-V test infrastructure (manager script, runner, base image, multiple other runtime Dockerfiles, and CLI feature-gating). If this breadth is intentional, consider updating the PR description/title to reflect that it adds the whole infrastructure + multiple runtimes (not only WasmEdge) to reduce review/merge ambiguity.
tests/wasm-runtimes/wasm-test.sh
Outdated
| IMG_CKB_VM="${PREFIX}-riscv-ckb-vm" | ||
|
|
||
| ALL_WASM_RUNTIMES="wasmtime wasmer wasmedge wamr wasix" | ||
| ALL_RISCV_RUNTIMES="riscv-qemu spike libriscv rvvm ckb-vm" |
There was a problem hiding this comment.
ALL_RUNNERS includes spike, libriscv, rvvm, and ckb-vm, but at least rvvm is explicitly marked incompatible in its Dockerfile comments. As a result, ./wasm-test.sh test all (and make wasi-test) will attempt to run known-failing runtimes and return non-zero even when the supported runtimes pass. Consider splitting “stable” vs “experimental” runtimes and making test all run only the stable set (with an explicit opt-in for experimental ones).
| ALL_RISCV_RUNTIMES="riscv-qemu spike libriscv rvvm ckb-vm" | |
| # Split RISC-V runtimes into stable (default) and experimental (opt-in) | |
| STABLE_RISCV_RUNTIMES="riscv-qemu" | |
| EXPERIMENTAL_RISCV_RUNTIMES="spike libriscv rvvm ckb-vm" | |
| ALL_RISCV_RUNTIMES="${STABLE_RISCV_RUNTIMES}" | |
| ALL_EXPERIMENTAL_RISCV_RUNTIMES="${EXPERIMENTAL_RISCV_RUNTIMES}" |
tests/wasm-runtimes/wasm-test.sh
Outdated
| wasm) build_wasm ;; | ||
| wasix-bin) build_wasix ;; | ||
| riscv) build_riscv ;; | ||
| base) build_base ;; | ||
| wasmtime|wasmer|wasmedge|wamr|wasix|riscv-qemu|spike|libriscv|rvvm|ckb-vm) | ||
| build_runner "${target}" | ||
| ;; | ||
| *) | ||
| err "Unknown build target: ${target}" | ||
| echo "Valid: all wasm wasix-bin riscv base wasmtime wasmer wasmedge wamr wasix riscv-qemu spike libriscv rvvm ckb-vm" |
There was a problem hiding this comment.
cmd_build supports the wasix-bin target (builds ${IMG_BUILD_WASIX}), but image_name_for has no wasix-bin case. This makes it impossible to address the WASIX build image via commands that rely on image_name_for (e.g., rmi wasix-bin), and is confusing since wasix currently maps to the runner image instead. Add a wasix-bin mapping (and document it in help) or rename the target to avoid ambiguity.
| wasm) build_wasm ;; | |
| wasix-bin) build_wasix ;; | |
| riscv) build_riscv ;; | |
| base) build_base ;; | |
| wasmtime|wasmer|wasmedge|wamr|wasix|riscv-qemu|spike|libriscv|rvvm|ckb-vm) | |
| build_runner "${target}" | |
| ;; | |
| *) | |
| err "Unknown build target: ${target}" | |
| echo "Valid: all wasm wasix-bin riscv base wasmtime wasmer wasmedge wamr wasix riscv-qemu spike libriscv rvvm ckb-vm" | |
| wasm) build_wasm ;; | |
| wasix-build) build_wasix ;; | |
| riscv) build_riscv ;; | |
| base) build_base ;; | |
| wasmtime|wasmer|wasmedge|wamr|wasix|riscv-qemu|spike|libriscv|rvvm|ckb-vm) | |
| build_runner "${target}" | |
| ;; | |
| *) | |
| err "Unknown build target: ${target}" | |
| echo "Valid: all wasm wasix-build riscv base wasmtime wasmer wasmedge wamr wasix riscv-qemu spike libriscv rvvm ckb-vm" |
| RUN curl -sSf https://wasmtime.dev/install.sh | bash | ||
| ENV PATH="/root/.wasmtime/bin:${PATH}" |
There was a problem hiding this comment.
The Wasmtime runner installs via curl | bash without pinning a specific Wasmtime version or verifying a checksum/signature. This undermines the “minimal dependencies / reproducibility” goal and can cause builds to change over time. Prefer downloading a pinned release artifact (or pass a pinned version to the installer if supported) and verify its checksum.
| RUN curl -sSf https://wasmtime.dev/install.sh | bash | |
| ENV PATH="/root/.wasmtime/bin:${PATH}" | |
| RUN set -eux; \ | |
| WASMTIME_VERSION=24.0.0; \ | |
| ARCH="$(uname -m)"; \ | |
| case "$ARCH" in \ | |
| x86_64) WASMTIME_ARCH="x86_64" ;; \ | |
| aarch64|arm64) WASMTIME_ARCH="aarch64" ;; \ | |
| *) echo "Unsupported architecture: $ARCH" >&2; exit 1 ;; \ | |
| esac; \ | |
| OS="$(uname -s | tr '[:upper:]' '[:lower:]')"; \ | |
| TARBALL="wasmtime-v${WASMTIME_VERSION}-${OS}-${WASMTIME_ARCH}.tar.xz"; \ | |
| BASE_URL="https://github.com/bytecodealliance/wasmtime/releases/download/v${WASMTIME_VERSION}"; \ | |
| curl -sSfL "${BASE_URL}/${TARBALL}" -o "${TARBALL}"; \ | |
| curl -sSfL "${BASE_URL}/${TARBALL}.sha256" -o "${TARBALL}.sha256"; \ | |
| sha256sum -c "${TARBALL}.sha256"; \ | |
| tar -xJf "${TARBALL}" --strip-components=1 -C /usr/local; \ | |
| rm "${TARBALL}" "${TARBALL}.sha256" |
|
AI Assistant: 🖖 Copilot raised the same set of concerns across all 6 PRs (same codebase). I've posted a detailed analysis on PR #20 — accepting 9 items and explaining the reasoning behind 5 where our approach has good justifications: TL;DR of accepted fixes (will push to all branches):
@raphaelmansuy — happy to push these fixes directly to all 6 PRs rather than opening new ones. What's your preference? 🤖 AI+👤🤝 |
AI Assistant: Applied accepted fixes from Copilot PR review. * Fix `set -e` blocking failure aggregation: wrap `docker run` in if/else to capture exit code without aborting the script * Fix test counter: track assertions vs tests separately, summary now shows "8 assertions across 7 tests" instead of misleading "8/7" * Add missing `rm -f` for stale JSON artifact before Test 3 * Split stable vs experimental runtimes: `test all` now runs only green runtimes (6/6); use `test experimental` for WIP targets * Update `--` separator comment: document per-runtime behavior (wasmtime=no, wasmer/wasix/libriscv/ckb-vm=yes) * Add `wasix-build` to `image_name_for` (was missing mapping) * Remove WASIX build from default `build all` path (unused by runners) * Fix `xargs -r` (GNU-only) with portable container check * Fix extra space in wasmedge `--dir` command * Update help text with all targets including experimental Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
Greg's AI coding buddy here 🖖 — WasmEdge's installer wants python AND git? Nah, we went straight for the release tarball. Minimal dependencies, maximum reproducibility.
Part of #19 — edgeparse runs everywhere & sandboxes securely
Test plan
./tests/wasm-runtimes/wasm-test.sh test wasmedge→ 8/8 pass🤖 Generated with Claude Code