From 7db37c0e625bb92c4dafd1c3912a08ac29b6f5af Mon Sep 17 00:00:00 2001 From: Andy Wingo Date: Fri, 15 Aug 2025 11:38:03 +0200 Subject: [PATCH] Build C test cases via Python script to target-specific dir We move all the C sources from tests/c/testsuite to tests/c/src, somewhat like the Rust tests, then treat tests/c/testsuite as a built directory. The build script is rewritten to Python, as it is getting a bit more complicated. Tests are compiled into target-specific dirs, currently only tests/c/testsuite/wasm32-wasip1. The build script writes the version-specific manifest.json, so as to include the target in the test suite name. Workflows updated appropriately. --- .github/workflows/compile-tests.yml | 2 +- .../workflows/daily-runtime-validation.yml | 2 +- .gitignore | 3 +- README.md | 6 +- tests/c/build.py | 104 ++++++++++++++++++ tests/c/build.sh | 13 --- .../clock_getres-monotonic.c | 0 .../clock_getres-realtime.c | 0 .../clock_gettime-monotonic.c | 0 .../clock_gettime-realtime.c | 0 .../fdopendir-with-access.c | 0 .../fdopendir-with-access.json | 0 .../c/{testsuite => src}/fopen-with-access.c | 0 .../{testsuite => src}/fopen-with-access.json | 0 .../{testsuite => src}/fopen-with-no-access.c | 0 tests/c/{testsuite => src}/fs-tests.dir/file | 0 .../fs-tests.dir/fopendir.dir/file-0 | 0 .../fs-tests.dir/fopendir.dir/file-1 | 0 .../{testsuite => src}/fs-tests.dir/lseek.txt | 0 .../{testsuite => src}/fs-tests.dir/pread.txt | 0 .../fs-tests.dir/writeable/.gitignore | 0 tests/c/{testsuite => src}/lseek.c | 0 tests/c/{testsuite => src}/lseek.json | 0 .../c/{testsuite => src}/pread-with-access.c | 0 .../{testsuite => src}/pread-with-access.json | 0 .../c/{testsuite => src}/pwrite-with-access.c | 0 .../pwrite-with-access.json | 0 .../c/{testsuite => src}/pwrite-with-append.c | 0 .../pwrite-with-append.json | 0 .../sock_shutdown-invalid_fd.c | 0 .../sock_shutdown-not_sock.c | 0 tests/c/{testsuite => src}/stat-dev-ino.c | 0 tests/c/{testsuite => src}/stat-dev-ino.json | 0 tests/c/testsuite/manifest.json | 3 - 34 files changed, 111 insertions(+), 22 deletions(-) create mode 100755 tests/c/build.py delete mode 100755 tests/c/build.sh rename tests/c/{testsuite => src}/clock_getres-monotonic.c (100%) rename tests/c/{testsuite => src}/clock_getres-realtime.c (100%) rename tests/c/{testsuite => src}/clock_gettime-monotonic.c (100%) rename tests/c/{testsuite => src}/clock_gettime-realtime.c (100%) rename tests/c/{testsuite => src}/fdopendir-with-access.c (100%) rename tests/c/{testsuite => src}/fdopendir-with-access.json (100%) rename tests/c/{testsuite => src}/fopen-with-access.c (100%) rename tests/c/{testsuite => src}/fopen-with-access.json (100%) rename tests/c/{testsuite => src}/fopen-with-no-access.c (100%) rename tests/c/{testsuite => src}/fs-tests.dir/file (100%) rename tests/c/{testsuite => src}/fs-tests.dir/fopendir.dir/file-0 (100%) rename tests/c/{testsuite => src}/fs-tests.dir/fopendir.dir/file-1 (100%) rename tests/c/{testsuite => src}/fs-tests.dir/lseek.txt (100%) rename tests/c/{testsuite => src}/fs-tests.dir/pread.txt (100%) rename tests/c/{testsuite => src}/fs-tests.dir/writeable/.gitignore (100%) rename tests/c/{testsuite => src}/lseek.c (100%) rename tests/c/{testsuite => src}/lseek.json (100%) rename tests/c/{testsuite => src}/pread-with-access.c (100%) rename tests/c/{testsuite => src}/pread-with-access.json (100%) rename tests/c/{testsuite => src}/pwrite-with-access.c (100%) rename tests/c/{testsuite => src}/pwrite-with-access.json (100%) rename tests/c/{testsuite => src}/pwrite-with-append.c (100%) rename tests/c/{testsuite => src}/pwrite-with-append.json (100%) rename tests/c/{testsuite => src}/sock_shutdown-invalid_fd.c (100%) rename tests/c/{testsuite => src}/sock_shutdown-not_sock.c (100%) rename tests/c/{testsuite => src}/stat-dev-ino.c (100%) rename tests/c/{testsuite => src}/stat-dev-ino.json (100%) delete mode 100644 tests/c/testsuite/manifest.json diff --git a/.github/workflows/compile-tests.yml b/.github/workflows/compile-tests.yml index 574d752d2..c9266b981 100644 --- a/.github/workflows/compile-tests.yml +++ b/.github/workflows/compile-tests.yml @@ -121,7 +121,7 @@ jobs: - name: Build tests shell: bash working-directory: tests/c - run: CC="./wasi-sdk-${WASI_VERSION}.0-${SYSTEM_NAME}/bin/clang" ./build.sh + run: CC="./wasi-sdk-${WASI_VERSION}.0-${SYSTEM_NAME}/bin/clang" ./build.py - name: Upload precompiled tests if: matrix.os == 'ubuntu-latest' diff --git a/.github/workflows/daily-runtime-validation.yml b/.github/workflows/daily-runtime-validation.yml index f7e7973a2..39425a2c7 100644 --- a/.github/workflows/daily-runtime-validation.yml +++ b/.github/workflows/daily-runtime-validation.yml @@ -78,7 +78,7 @@ jobs: --json-output-location results.json \ -t tests/assemblyscript/testsuite \ tests/rust/testsuite \ - tests/c/testsuite + tests/c/testsuite/wasm32-wasip1 - name: Configure git uses: ./.github/actions/git-config diff --git a/.gitignore b/.gitignore index 9a9556977..a91f5c0f3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.wasm -.DS_Store \ No newline at end of file +.DS_Store +/tests/c/testsuite/ diff --git a/README.md b/README.md index 6649e8c4c..2dc74377a 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ executor is quite simple; see the [specification] document for the details and t ```bash python3 test-runner/wasi_test_runner.py \ -t ./tests/assemblyscript/testsuite/ `# path to folders containing .wasm test files` \ - ./tests/c/testsuite/ \ + ./tests/c/testsuite/wasm32-wasip1 \ ./tests/rust/testsuite/ \ -r adapters/wasmtime.py # path to a runtime adapter ``` @@ -56,7 +56,7 @@ Optionally you can specify test cases to skip with the `--exclude-filter` option ```bash python3 test-runner/wasi_test_runner.py \ -t ./tests/assemblyscript/testsuite/ `# path to folders containing .wasm test files` \ - ./tests/c/testsuite/ \ + ./tests/c/testsuite/wasm32-wasip1 \ ./tests/rust/testsuite/ \ --exclude-filter examples/skip.json \ -r adapters/wasmtime.py # path to a runtime adapter @@ -95,7 +95,7 @@ Here is some additional information for developers who are willing to contribute ### Cleaning up temporary resources -Some of the tests (e.g. [pwrite-with-access](./tests/c/testsuite/pwrite-with-access.c)) generate +Some of the tests (e.g. [pwrite-with-access](./tests/c/src/pwrite-with-access.c)) generate output artifacts and their existence can affect consecutive test executions. Tests should clean up the artifacts they generate, but there might be cases where the test fails early. The test runner will automatically delete all the files and directories in the test suite directory with the diff --git a/tests/c/build.py b/tests/c/build.py new file mode 100755 index 000000000..77f6fa292 --- /dev/null +++ b/tests/c/build.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 + +import argparse +import json +import os +import shlex +import shutil +import subprocess +import sys +from pathlib import Path +from math import inf + +# shlex.split() splits according to shell quoting rules +CC = shlex.split(os.getenv("CC", "clang")) + +parser = argparse.ArgumentParser() +parser.add_argument("--dry-run", action="store_true") +parser.add_argument("--verbose", action="store_true") + +args = parser.parse_args() + +SYSTEMS = ['wasm32'] +VERSIONS = ['wasip1'] # + ['wasip2', 'wasip3'] + +def compute_target(system, version): + return f"{system}-{version}" + +def compute_cc_target(system, version): + if version == 'wasip3': + # wasm32-wasip3 triple not yet supported. + return compute_target(system, 'wasip2') + return compute_target(system, version) + +BASE_DIR = Path(__file__).parent + +def maybe_stat(path, default): + try: + return path.stat().st_mtime + except FileNotFoundError: + return default + +def needs_rebuild(dst, src): + if maybe_stat(dst, 0) < src.stat().st_mtime: + return True + return (maybe_stat(dst.with_suffix(".json"), -1) + < maybe_stat(src.with_suffix(".json"), -inf)) + +def run(argv): + if args.verbose: + print(shlex.join([str(x) for x in argv])) + if not args.dry_run: + r = subprocess.run(argv) + if r.returncode != 0: + sys.exit(r.returncode) + +def cp(src, dst): + if args.verbose: + print(f"cp {src} {dst}") + if not args.dry_run: + shutil.copy(src, dst) + +def cp_R(src, dst): + if args.verbose: + print(f"cp -R {src} {dst}") + if not args.dry_run: + shutil.copytree(src, dst, dirs_exist_ok=True) + +def write_manifest(path, manifest): + if args.verbose: + print(f"writing {path}") + if not args.dry_run: + path.write_text(json.dumps(manifest)) + +def mkdir_p(path): + if args.verbose: + print(f"mkdir -p {path}") + if not args.dry_run: + path.mkdir(parents=True, exist_ok=True) + +for system in SYSTEMS: + for version in VERSIONS: + target = compute_target(system, version) + generic_sources = list((BASE_DIR / "src").glob("*.c")) + target_sources = list((BASE_DIR / "src" / target).glob("*.c")) + + target_dir = BASE_DIR / "testsuite" / target + mkdir_p(target_dir) + target_args = [f"--target={compute_cc_target(system, version)}"] + + write_manifest(target_dir / "manifest.json", + {'name': f"WASI C tests [{target}]"}) + + for src in generic_sources + target_sources: + dst = (target_dir / src.name).with_suffix(".wasm") + if needs_rebuild(dst, src): + print(f"building testsuite/{target}/{dst.name}") + src_json = src.with_suffix(".json") + if src_json.exists(): + dst_json = dst.with_suffix(".json") + with src_json.open() as f: + for d in json.load(f).get('dirs', []): + cp_R(src.parent / d, dst.parent / d) + cp(src_json, dst_json) + run(CC + target_args + [src] + ['-o'] + [dst]) diff --git a/tests/c/build.sh b/tests/c/build.sh deleted file mode 100755 index 3c74e022f..000000000 --- a/tests/c/build.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash -set -ueo pipefail - -CC=${CC:=clang} - -for input in testsuite/*.c; do - output="testsuite/$(basename $input .c).wasm" - - if [ "$input" -nt "$output" ]; then - echo "Compiling $input" - $CC --target=wasm32-wasip1 "$input" -o "$output" - fi -done diff --git a/tests/c/testsuite/clock_getres-monotonic.c b/tests/c/src/clock_getres-monotonic.c similarity index 100% rename from tests/c/testsuite/clock_getres-monotonic.c rename to tests/c/src/clock_getres-monotonic.c diff --git a/tests/c/testsuite/clock_getres-realtime.c b/tests/c/src/clock_getres-realtime.c similarity index 100% rename from tests/c/testsuite/clock_getres-realtime.c rename to tests/c/src/clock_getres-realtime.c diff --git a/tests/c/testsuite/clock_gettime-monotonic.c b/tests/c/src/clock_gettime-monotonic.c similarity index 100% rename from tests/c/testsuite/clock_gettime-monotonic.c rename to tests/c/src/clock_gettime-monotonic.c diff --git a/tests/c/testsuite/clock_gettime-realtime.c b/tests/c/src/clock_gettime-realtime.c similarity index 100% rename from tests/c/testsuite/clock_gettime-realtime.c rename to tests/c/src/clock_gettime-realtime.c diff --git a/tests/c/testsuite/fdopendir-with-access.c b/tests/c/src/fdopendir-with-access.c similarity index 100% rename from tests/c/testsuite/fdopendir-with-access.c rename to tests/c/src/fdopendir-with-access.c diff --git a/tests/c/testsuite/fdopendir-with-access.json b/tests/c/src/fdopendir-with-access.json similarity index 100% rename from tests/c/testsuite/fdopendir-with-access.json rename to tests/c/src/fdopendir-with-access.json diff --git a/tests/c/testsuite/fopen-with-access.c b/tests/c/src/fopen-with-access.c similarity index 100% rename from tests/c/testsuite/fopen-with-access.c rename to tests/c/src/fopen-with-access.c diff --git a/tests/c/testsuite/fopen-with-access.json b/tests/c/src/fopen-with-access.json similarity index 100% rename from tests/c/testsuite/fopen-with-access.json rename to tests/c/src/fopen-with-access.json diff --git a/tests/c/testsuite/fopen-with-no-access.c b/tests/c/src/fopen-with-no-access.c similarity index 100% rename from tests/c/testsuite/fopen-with-no-access.c rename to tests/c/src/fopen-with-no-access.c diff --git a/tests/c/testsuite/fs-tests.dir/file b/tests/c/src/fs-tests.dir/file similarity index 100% rename from tests/c/testsuite/fs-tests.dir/file rename to tests/c/src/fs-tests.dir/file diff --git a/tests/c/testsuite/fs-tests.dir/fopendir.dir/file-0 b/tests/c/src/fs-tests.dir/fopendir.dir/file-0 similarity index 100% rename from tests/c/testsuite/fs-tests.dir/fopendir.dir/file-0 rename to tests/c/src/fs-tests.dir/fopendir.dir/file-0 diff --git a/tests/c/testsuite/fs-tests.dir/fopendir.dir/file-1 b/tests/c/src/fs-tests.dir/fopendir.dir/file-1 similarity index 100% rename from tests/c/testsuite/fs-tests.dir/fopendir.dir/file-1 rename to tests/c/src/fs-tests.dir/fopendir.dir/file-1 diff --git a/tests/c/testsuite/fs-tests.dir/lseek.txt b/tests/c/src/fs-tests.dir/lseek.txt similarity index 100% rename from tests/c/testsuite/fs-tests.dir/lseek.txt rename to tests/c/src/fs-tests.dir/lseek.txt diff --git a/tests/c/testsuite/fs-tests.dir/pread.txt b/tests/c/src/fs-tests.dir/pread.txt similarity index 100% rename from tests/c/testsuite/fs-tests.dir/pread.txt rename to tests/c/src/fs-tests.dir/pread.txt diff --git a/tests/c/testsuite/fs-tests.dir/writeable/.gitignore b/tests/c/src/fs-tests.dir/writeable/.gitignore similarity index 100% rename from tests/c/testsuite/fs-tests.dir/writeable/.gitignore rename to tests/c/src/fs-tests.dir/writeable/.gitignore diff --git a/tests/c/testsuite/lseek.c b/tests/c/src/lseek.c similarity index 100% rename from tests/c/testsuite/lseek.c rename to tests/c/src/lseek.c diff --git a/tests/c/testsuite/lseek.json b/tests/c/src/lseek.json similarity index 100% rename from tests/c/testsuite/lseek.json rename to tests/c/src/lseek.json diff --git a/tests/c/testsuite/pread-with-access.c b/tests/c/src/pread-with-access.c similarity index 100% rename from tests/c/testsuite/pread-with-access.c rename to tests/c/src/pread-with-access.c diff --git a/tests/c/testsuite/pread-with-access.json b/tests/c/src/pread-with-access.json similarity index 100% rename from tests/c/testsuite/pread-with-access.json rename to tests/c/src/pread-with-access.json diff --git a/tests/c/testsuite/pwrite-with-access.c b/tests/c/src/pwrite-with-access.c similarity index 100% rename from tests/c/testsuite/pwrite-with-access.c rename to tests/c/src/pwrite-with-access.c diff --git a/tests/c/testsuite/pwrite-with-access.json b/tests/c/src/pwrite-with-access.json similarity index 100% rename from tests/c/testsuite/pwrite-with-access.json rename to tests/c/src/pwrite-with-access.json diff --git a/tests/c/testsuite/pwrite-with-append.c b/tests/c/src/pwrite-with-append.c similarity index 100% rename from tests/c/testsuite/pwrite-with-append.c rename to tests/c/src/pwrite-with-append.c diff --git a/tests/c/testsuite/pwrite-with-append.json b/tests/c/src/pwrite-with-append.json similarity index 100% rename from tests/c/testsuite/pwrite-with-append.json rename to tests/c/src/pwrite-with-append.json diff --git a/tests/c/testsuite/sock_shutdown-invalid_fd.c b/tests/c/src/sock_shutdown-invalid_fd.c similarity index 100% rename from tests/c/testsuite/sock_shutdown-invalid_fd.c rename to tests/c/src/sock_shutdown-invalid_fd.c diff --git a/tests/c/testsuite/sock_shutdown-not_sock.c b/tests/c/src/sock_shutdown-not_sock.c similarity index 100% rename from tests/c/testsuite/sock_shutdown-not_sock.c rename to tests/c/src/sock_shutdown-not_sock.c diff --git a/tests/c/testsuite/stat-dev-ino.c b/tests/c/src/stat-dev-ino.c similarity index 100% rename from tests/c/testsuite/stat-dev-ino.c rename to tests/c/src/stat-dev-ino.c diff --git a/tests/c/testsuite/stat-dev-ino.json b/tests/c/src/stat-dev-ino.json similarity index 100% rename from tests/c/testsuite/stat-dev-ino.json rename to tests/c/src/stat-dev-ino.json diff --git a/tests/c/testsuite/manifest.json b/tests/c/testsuite/manifest.json deleted file mode 100644 index bed87e376..000000000 --- a/tests/c/testsuite/manifest.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "WASI C tests" -}