Skip to content

Conversation

@nbaertsch
Copy link
Contributor

@nbaertsch nbaertsch commented Dec 31, 2025

Added WASI testsuite to CI and cleaned up some WASI fd_* functions.

Related: #15

Noah Baertsch and others added 17 commits December 29, 2025 17:18
- ArrayHashMap.put() now requires allocator as first parameter
- ArrayList.append() now requires allocator as first parameter

These changes are required for Zig 0.15.2 which changed the API for
managed data structures to take the allocator explicitly.
- fd_tell: Get current file offset using cross-platform posix.lseek_CUR_get
- fd_readdir: Read directory entries with WASI dirent format

Platform support for fd_readdir:
- Linux: Implemented using getdents64 syscall (no libc required)
- macOS/iOS/tvOS/watchOS/visionOS: Implemented using getdirentries via libc
- FreeBSD/OpenBSD/NetBSD/DragonFly: Implemented using getdents via libc
- Windows: Compile-time error (TODO: implement with NtQueryDirectoryFile)
- Other platforms: Compile-time error

These are required for running CPython 3.13 WASM builds.
Changes:
- Use @sizeof(wasi.dirent_t) instead of hardcoded 24 for WASI dirent header size
- Add toWasiFiletype() helper to convert native DT_* values to wasi.filetype_t enum
- Add writeWasiDirent() helper for spec-compliant WASI dirent structure writes
- Implement Windows fd_readdir using NtQueryDirectoryFile API:
  - Uses FILE_BOTH_DIR_INFORMATION for directory enumeration
  - Converts UTF-16 filenames to UTF-8
  - Maps FILE_ATTRIBUTE_* to wasi.filetype_t
  - Uses FileIndex as pseudo-inode (Windows has no true inodes)
- Update doc comments to reference std.os.wasi type definitions

Platform support:
- Linux: getdents64 syscall (no libc)
- macOS/iOS/tvOS/watchOS/visionOS: getdirentries via std.c
- FreeBSD/OpenBSD/NetBSD/DragonFly: getdents via std.c
- Windows: NtQueryDirectoryFile via std.os.windows
- Other platforms: @CompileError
Add error mappings for:
- Unseekable -> ESPIPE (illegal seek on pipes/sockets)
- NotOpenForReading -> EBADF (bad file descriptor)
- InvalidArgument -> EINVAL
- PermissionDenied -> EPERM

Note: The Windows fd_readdir implementation from the previous commit
is untested - zware itself lacks Windows support in other areas
(fd_t type handling, stat.inode types, posix.O flags, etc.)
WASI spec does not mandate filtering these entries, and major runtimes
like wasmtime and wasmer include them. Removing the filter ensures
compatibility with applications that may depend on seeing these entries.
- Remove verbose doc comments (code is self-documenting)
- Remove Windows implementation (to be added in separate branch)
- Simplify variable names and reduce code duplication
- Keep Linux, macOS, and BSD implementations
Integrates the official WebAssembly WASI testsuite
(https://github.com/WebAssembly/wasi-testsuite) to comprehensively test
zware's WASI implementation across multiple languages and platforms.

Changes:

- Enhanced zware-run with WASI support:
  * Added --env flag for environment variables (--env KEY=VALUE)
  * Added --dir flag for directory mappings/preopens (--dir GUEST::HOST)
  * Auto-detect _start entry point when no function specified
  * Created 24 WASI wrapper functions to bridge signature mismatch
    between WASI functions (no context param) and Store host functions
    (requires usize context param)
  * Implemented setupWasiImports() to expose all WASI preview 1 functions
    via wasi_snapshot_preview1 module
  * Added command-line argument parsing with Config struct
  * Setup WASI arguments, environment variables, and preopens in runtime

- Created wasi-testsuite-adapter/zware.py:
  * Python adapter implementing testsuite protocol
  * Provides get_name(), get_version(), get_wasi_versions(), compute_argv()
  * Translates test requirements into zware-run command-line arguments
  * Supports wasm32-wasip1 (WASI preview 1)

- Added build system integration (build.zig):
  * New 'wasi-testsuite' build step
  * Runs all three test suites: C (14 tests), Rust (46 tests),
    AssemblyScript (12 tests) = 72 total tests
  * Sets ZWARE_RUN environment variable for adapter
  * Depends on zware-run being built first

- Added dependency (build.zig.zon):
  * wasi-testsuite from prod/testsuite-base branch

- Updated CI/CD (.github/workflows/test.yaml):
  * New wasi-testsuite job running on ubuntu/macos/windows
  * Added Python 3.x setup for test runner
  * Runs 'zig build wasi-testsuite' in CI

Current test results: 17/72 passing (24% pass rate)
- C: 5/14 passing (36%)
- Rust: 4/46 passing (9%)
- AssemblyScript: 8/12 passing (67%)

Failing tests indicate areas for future WASI implementation work
(missing functions: sock_shutdown, fd_advise, fd_fdstat_set_rights,
fd_filestat_set; preopen directory access issues).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Splits the WASI testsuite into 72 individual build steps and CI jobs,
allowing each test to be tracked separately in GitHub Actions. This
provides at-a-glance visibility into which specific tests pass/fail
rather than a single aggregated result.

Changes:

- Added individual build steps in build.zig:
  * 14 wasi-c-* steps for C tests
  * 46 wasi-rust-* steps for Rust tests
  * 12 wasi-as-* steps for AssemblyScript tests
  * Each step runs a single test in isolation
  * Kept aggregated 'wasi-testsuite' step for convenience

- Created tools/run-single-wasi-test.py:
  * Wrapper script to run individual tests
  * Creates temp directory with test files and dependencies
  * Copies test .wasm, .json config, and required directories
  * Runs official test runner on isolated test

- Updated GitHub Actions workflow:
  * Matrix strategy with all 72 tests
  * Each test runs as separate job with unique name
  * fail-fast: false to run all tests even if some fail
  * Provides granular visibility in CI results

- Fixed zware-run argument parsing:
  * Added -f/--function flag to explicitly specify function name
  * All positional args after wasm file are now program arguments
  * Previous behavior incorrectly treated first arg as function name
  * Updated usage documentation and examples

This enables viewing individual test results in CI (e.g., "wasi-c-lseek:
PASS", "wasi-rust-fd_readdir: FAIL") rather than a single pass/fail for
all 72 tests. The aggregated step is still available for local testing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Removes all hardcoded test arrays and replaces them with runtime/build-time
discovery of WASI tests. This makes the system maintainable and automatically
adapts when tests are added/removed from the testsuite.

Changes:

- Created tools/discover-wasi-tests.py:
  * Scans testsuite directories for .wasm files
  * Outputs in multiple formats: json, zig-array, github-matrix
  * Single source of truth for available tests
  * No manual updates needed when tests change

- Updated build.zig for dynamic discovery:
  * Removed 72 hardcoded test names
  * Added filesystem scanning at build time
  * Discovers tests from wasi-testsuite dependency
  * Creates individual steps dynamically (wasi-c-*, wasi-rust-*, wasi-as-*)
  * Sorted output for deterministic build steps

- Updated GitHub Actions for dynamic discovery:
  * Added wasi-testsuite-discover job
  * Fetches testsuite and runs discovery script
  * Outputs matrix as JSON for dependent job
  * wasi-testsuite job uses fromJson() to consume matrix
  * Removed 164 lines of hardcoded test definitions
  * Automatically tracks new/removed tests in testsuite

Benefits:
- Zero maintenance when testsuite changes
- No risk of hardcoded lists getting out of sync
- Cleaner, more maintainable code
- Single discovery script shared by build system and CI
- Still provides granular per-test visibility in CI

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
WASI P1 testsuite added to CI + WIP WASI P1 implementation
@nbaertsch
Copy link
Contributor Author

Plenty of WASI P1 host functions unimplemented yet + existing ones have some stubbed or missing behavior. Might be better in a dedicated WASI P1 branch?

Noah Baertsch and others added 5 commits December 31, 2025 18:33
Handle error.NotLink in toWasiError() to return EINVAL instead of panicking
when path_readlink is called on a regular file (non-symlink).

This fixes CPython 3.13.1 and other WASI programs that use path_readlink
for symlink detection during import resolution.

Per POSIX/WASI spec, readlink should return EINVAL when the target is not
a symbolic link.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Implements WASI poll_oneoff syscall to enable:
- Non-blocking socket I/O
- File descriptor polling (similar to select/poll)
- Support for Python's select module and socket timeouts

Implementation supports FD_READ and FD_WRITE events with 100ms timeout.
Uses fixed-size buffer (64 fds) to avoid dynamic allocation complexity.

This enables requests/urllib3 to work with Python's standard socket library.
Add mapping for error.WouldBlock to WASI errno AGAIN.
This fixes panics when non-blocking socket I/O operations
return WouldBlock, which is expected behavior for sockets.
fix: Handle WouldBlock error in toWasiError
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant