From 44737e436b17fadfeb779961e93a0828c8276c14 Mon Sep 17 00:00:00 2001 From: Yuki Kishimoto Date: Thu, 23 Jul 2026 12:52:35 +0200 Subject: [PATCH] Add WASM time provider tests Signed-off-by: Yuki Kishimoto --- .github/workflows/ci.yml | 10 ++++ justfile | 6 +++ tests/run_wasm_test.mjs | 72 +++++++++++++++++++++++++++++ tests/wasm_missing_time_provider.rs | 8 ++++ tests/wasm_time_provider.rs | 30 ++++++++++++ 5 files changed, 126 insertions(+) create mode 100644 tests/run_wasm_test.mjs create mode 100644 tests/wasm_missing_time_provider.rs create mode 100644 tests/wasm_time_provider.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2d90a71..59e97f0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,6 +32,7 @@ jobs: - --features std - --no-default-features - --target wasm32-unknown-unknown + - --target wasm32-unknown-unknown --features std steps: - name: Checkout uses: actions/checkout@v4 @@ -48,3 +49,12 @@ jobs: - name: Test if: "!contains(matrix.args, 'wasm32-')" run: cargo test ${{ matrix.args }} + + - name: Test WASM provider + if: "contains(matrix.args, 'wasm32-')" + run: | + set -o pipefail + cargo test ${{ matrix.args }} --test wasm_time_provider --no-run --message-format=json \ + | node tests/run_wasm_test.mjs wasm_time_provider success + cargo test ${{ matrix.args }} --test wasm_missing_time_provider --no-run --message-format=json \ + | node tests/run_wasm_test.mjs wasm_missing_time_provider failure diff --git a/justfile b/justfile index 3d1a58c..8670774 100755 --- a/justfile +++ b/justfile @@ -15,4 +15,10 @@ test: cargo test cargo test --features std +test-wasm: + bash -o pipefail -c 'cargo test --target wasm32-unknown-unknown --test wasm_time_provider --no-run --message-format=json | node tests/run_wasm_test.mjs wasm_time_provider success' + bash -o pipefail -c 'cargo test --target wasm32-unknown-unknown --test wasm_missing_time_provider --no-run --message-format=json | node tests/run_wasm_test.mjs wasm_missing_time_provider failure' + bash -o pipefail -c 'cargo test --target wasm32-unknown-unknown --features std --test wasm_time_provider --no-run --message-format=json | node tests/run_wasm_test.mjs wasm_time_provider success' + bash -o pipefail -c 'cargo test --target wasm32-unknown-unknown --features std --test wasm_missing_time_provider --no-run --message-format=json | node tests/run_wasm_test.mjs wasm_missing_time_provider failure' + precommit: fmt check clippy test diff --git a/tests/run_wasm_test.mjs b/tests/run_wasm_test.mjs new file mode 100644 index 0000000..4afd483 --- /dev/null +++ b/tests/run_wasm_test.mjs @@ -0,0 +1,72 @@ +import { readFile } from "node:fs/promises"; +import { createInterface } from "node:readline"; + +const [testName, expectedResult] = process.argv.slice(2); + +if (!testName || !["success", "failure"].includes(expectedResult)) { + throw new Error( + "usage: node tests/run_wasm_test.mjs ", + ); +} + +const messages = createInterface({ input: process.stdin }); +let testExecutable; + +for await (const line of messages) { + const message = JSON.parse(line); + + if ( + message.reason === "compiler-artifact" && + message.target.name === testName && + message.executable + ) { + testExecutable = message.executable; + } +} + +if (!testExecutable) { + throw new Error("WASM time provider test executable not found"); +} + +const wasm = await readFile(testExecutable); + +console.log(`Running WASM test: ${testName}`); + +let exitCode; +let runtimeError; + +try { + const { instance } = await WebAssembly.instantiate(wasm); + exitCode = instance.exports.main(); +} catch (error) { + runtimeError = error; +} + +if (expectedResult === "success") { + if (runtimeError) { + throw runtimeError; + } + + if (exitCode !== 0) { + throw new Error(`WASM test failed with exit code ${exitCode}`); + } + + console.log(`WASM test passed: ${testName}`); +} else { + const isWebAssemblyError = + runtimeError instanceof WebAssembly.RuntimeError || + runtimeError instanceof WebAssembly.LinkError; + + if (!isWebAssemblyError) { + if (runtimeError) { + throw runtimeError; + } + + throw new Error( + `WASM test unexpectedly returned exit code ${exitCode}: ${testName}`, + ); + } + + const failure = `${runtimeError.name}: ${runtimeError.message}`; + console.log(`WASM test failed as expected: ${testName} (${failure})`); +} diff --git a/tests/wasm_missing_time_provider.rs b/tests/wasm_missing_time_provider.rs new file mode 100644 index 0000000..e4440f4 --- /dev/null +++ b/tests/wasm_missing_time_provider.rs @@ -0,0 +1,8 @@ +#![cfg(all(target_family = "wasm", target_os = "unknown"))] + +use universal_time::SystemTime; + +#[test] +fn missing_provider_errors() { + let _ = SystemTime::now(); +} diff --git a/tests/wasm_time_provider.rs b/tests/wasm_time_provider.rs new file mode 100644 index 0000000..a99295e --- /dev/null +++ b/tests/wasm_time_provider.rs @@ -0,0 +1,30 @@ +#![cfg(all(target_family = "wasm", target_os = "unknown"))] + +use core::time::Duration; + +use universal_time::{define_time_provider, Instant, MonotonicClock, SystemTime, WallClock}; + +struct TestTimeProvider; + +impl WallClock for TestTimeProvider { + fn system_time(&self) -> SystemTime { + SystemTime::from_unix_duration(Duration::from_secs(42)) + } +} + +impl MonotonicClock for TestTimeProvider { + fn instant(&self) -> Instant { + Instant::from_ticks(Duration::from_secs(24)) + } +} + +define_time_provider!(TestTimeProvider); + +#[test] +fn defined_provider_links() { + assert_eq!( + SystemTime::now().as_unix_duration(), + Duration::from_secs(42) + ); + assert_eq!(Instant::now().to_ticks(), Duration::from_secs(24)); +}