Skip to content
This repository was archived by the owner on Oct 3, 2025. It is now read-only.

Commit 97fb3cc

Browse files
test(tinywasm): add AssertReturn to spec test harness
Signed-off-by: Henry Gressmann <mail@henrygressmann.de>
1 parent 1663131 commit 97fb3cc

File tree

8 files changed

+93
-23
lines changed

8 files changed

+93
-23
lines changed

.cargo/config.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
[alias]
22
dev="run -- -l debug run"
3+
spec-mvp="test --package tinywasm --test mvp -- test_mvp --exact --nocapture --ignored"

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/parser/src/error.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
use core::fmt::Debug;
1+
use core::fmt::{Debug, Display};
22

33
use alloc::string::{String, ToString};
44
use wasmparser::Encoding;
55

6+
#[derive(Debug)]
67
pub enum ParseError {
78
InvalidType,
89
UnsupportedSection(String),
@@ -16,7 +17,7 @@ pub enum ParseError {
1617
Other(String),
1718
}
1819

19-
impl Debug for ParseError {
20+
impl Display for ParseError {
2021
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2122
match self {
2223
Self::InvalidType => write!(f, "invalid type"),
@@ -37,6 +38,9 @@ impl Debug for ParseError {
3738
}
3839
}
3940

41+
#[cfg(any(feature = "std", all(not(feature = "std"), nightly)))]
42+
impl crate::std::error::Error for ParseError {}
43+
4044
impl From<wasmparser::BinaryReaderError> for ParseError {
4145
fn from(value: wasmparser::BinaryReaderError) -> Self {
4246
Self::ParseError {

crates/tinywasm/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ tinywasm-types={version="0.0.3", path="../types", default-features=false}
2121
wasm-testsuite={path="../wasm-testsuite"}
2222
wast={version="69.0"}
2323
owo-colors={version="3.5"}
24+
eyre={version="0.6"}
2425

2526
[features]
2627
default=["std", "parser", "logging"]

crates/tinywasm/src/module.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ pub struct Module {
1010
data: TinyWasmModule,
1111
}
1212

13+
impl From<&TinyWasmModule> for Module {
14+
fn from(data: &TinyWasmModule) -> Self {
15+
Self { data: data.clone() }
16+
}
17+
}
18+
1319
impl From<TinyWasmModule> for Module {
1420
fn from(data: TinyWasmModule) -> Self {
1521
Self { data }

crates/tinywasm/tests/mvp.rs

Lines changed: 73 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@ use std::{
33
fmt::{Debug, Formatter},
44
};
55

6-
use tinywasm::{Error, Result};
6+
use eyre::{eyre, Result};
7+
use log::debug;
78
use tinywasm_types::TinyWasmModule;
89
use wast::{
910
lexer::Lexer,
1011
parser::{self, ParseBuffer},
1112
QuoteWat, Wast,
1213
};
1314

14-
fn parse_module(mut module: wast::core::Module) -> Result<TinyWasmModule, Error> {
15+
fn parse_module(mut module: wast::core::Module) -> Result<TinyWasmModule> {
1516
let parser = tinywasm_parser::Parser::new();
1617
Ok(parser.parse_module_bytes(module.encode().expect("failed to encode module"))?)
1718
}
@@ -27,32 +28,34 @@ fn test_mvp() -> Result<()> {
2728
let wast = wasm_testsuite::get_test_wast(group).expect("failed to get test wast");
2829
let wast = std::str::from_utf8(&wast).expect("failed to convert wast to utf8");
2930

30-
let mut lexer = Lexer::new(&wast);
31+
let mut lexer = Lexer::new(wast);
3132
// we need to allow confusing unicode characters since they are technically valid wasm
3233
lexer.allow_confusing_unicode(true);
3334

3435
let buf = ParseBuffer::new_with_lexer(lexer).expect("failed to create parse buffer");
3536
let wast_data = parser::parse::<Wast>(&buf).expect("failed to parse wat");
3637

38+
let mut last_module: Option<TinyWasmModule> = None;
3739
for (i, directive) in wast_data.directives.into_iter().enumerate() {
3840
let span = directive.span();
39-
4041
use wast::WastDirective::*;
4142
let name = format!("{}-{}", group, i);
43+
4244
match directive {
4345
// TODO: needs to support more binary sections
4446
Wat(QuoteWat::Wat(wast::Wat::Module(module))) => {
45-
let module = std::panic::catch_unwind(|| parse_module(module));
46-
test_group.add_result(
47-
&format!("{}-parse", name),
48-
span,
49-
match module {
50-
Ok(Ok(_)) => Ok(()),
51-
Ok(Err(e)) => Err(e),
52-
Err(e) => Err(Error::Other(format!("failed to parse module: {:?}", e))),
53-
},
54-
);
47+
let result = std::panic::catch_unwind(|| parse_module(module))
48+
.map_err(|e| eyre!("failed to parse module: {:?}", e))
49+
.and_then(|res| res);
50+
51+
match &result {
52+
Err(_) => last_module = None,
53+
Ok(m) => last_module = Some(m.clone()),
54+
}
55+
56+
test_group.add_result(&format!("{}-parse", name), span, result.map(|_| ()));
5557
}
58+
5659
// these all pass already :)
5760
AssertMalformed {
5861
span,
@@ -64,11 +67,49 @@ fn test_mvp() -> Result<()> {
6467
&format!("{}-malformed", name),
6568
span,
6669
match res {
67-
Ok(Ok(_)) => Err(Error::Other("expected module to be malformed".to_string())),
70+
Ok(Ok(_)) => Err(eyre!("expected module to be malformed")),
6871
Err(_) | Ok(Err(_)) => Ok(()),
6972
},
7073
);
7174
}
75+
AssertReturn { span, exec, results: _ } => {
76+
let Some(module) = last_module.as_ref() else {
77+
// println!("no module found for assert_return: {:?}", exec);
78+
continue;
79+
};
80+
81+
let res: Result<Result<()>, _> = std::panic::catch_unwind(|| {
82+
let mut store = tinywasm::Store::new();
83+
let module = tinywasm::Module::from(module);
84+
let instance = module.instantiate(&mut store)?;
85+
86+
use wast::WastExecute::*;
87+
match exec {
88+
Wat(_) => return Result::Ok(()), // not used by the testsuite
89+
Get { module: _, global: _ } => return Result::Ok(()),
90+
Invoke(invoke) => {
91+
for arg in invoke.args {
92+
let arg = get_tinywasm_wasm_value(arg)?;
93+
let _res = instance.get_func(&store, invoke.name)?.call(&mut store, &[arg])?;
94+
// TODO: check the result
95+
}
96+
}
97+
}
98+
99+
Ok(())
100+
});
101+
102+
let res = match res {
103+
Err(e) => Err(eyre!("test panicked: {:?}", e)),
104+
Ok(Err(e)) => Err(e),
105+
Ok(Ok(())) => Ok(()),
106+
};
107+
108+
test_group.add_result(&format!("{}-return", name), span, res);
109+
}
110+
Invoke(m) => {
111+
debug!("invoke: {:?}", m);
112+
}
72113
// _ => test_group.add_result(
73114
// &format!("{}-unknown", name),
74115
// span,
@@ -82,13 +123,29 @@ fn test_mvp() -> Result<()> {
82123

83124
if test_suite.failed() {
84125
eprintln!("\n\nfailed one or more tests:\n{:#?}", test_suite);
85-
Err(Error::Other("failed one or more tests".to_string()))
126+
Err(eyre!("failed one or more tests"))
86127
} else {
87128
println!("\n\npassed all tests:\n{:#?}", test_suite);
88129
Ok(())
89130
}
90131
}
91132

133+
fn get_tinywasm_wasm_value(arg: wast::WastArg) -> Result<tinywasm_types::WasmValue> {
134+
let wast::WastArg::Core(arg) = arg else {
135+
return Err(eyre!("unsupported arg type"));
136+
};
137+
138+
use tinywasm_types::WasmValue;
139+
use wast::core::WastArgCore::*;
140+
Ok(match arg {
141+
F32(f) => WasmValue::F32(f32::from_bits(f.bits)),
142+
F64(f) => WasmValue::F64(f64::from_bits(f.bits)),
143+
I32(i) => WasmValue::I32(i),
144+
I64(i) => WasmValue::I64(i),
145+
_ => return Err(eyre!("unsupported arg type")),
146+
})
147+
}
148+
92149
struct TestSuite(BTreeMap<String, TestGroup>);
93150

94151
impl TestSuite {

crates/types/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ pub use instructions::*;
3636
/// This is the internal representation of a WebAssembly module in TinyWasm.
3737
/// TinyWasmModules are validated before being created, so they are guaranteed to be valid (as long as they were created by TinyWasm).
3838
/// This means you should not trust a TinyWasmModule created by a third party to be valid.
39-
#[derive(Debug)]
39+
#[derive(Debug, Clone)]
4040
pub struct TinyWasmModule {
4141
/// The version of the WebAssembly module.
4242
pub version: Option<u16>,
@@ -266,15 +266,15 @@ pub struct FuncType {
266266
}
267267

268268
/// A WebAssembly Function
269-
#[derive(Debug)]
269+
#[derive(Debug, Clone)]
270270
pub struct Function {
271271
pub ty: TypeAddr,
272272
pub locals: Box<[ValType]>,
273273
pub instructions: Box<[Instruction]>,
274274
}
275275

276276
/// A WebAssembly Module Export
277-
#[derive(Debug)]
277+
#[derive(Debug, Clone)]
278278
pub struct Export {
279279
/// The name of the export.
280280
pub name: Box<str>,

crates/wasm-testsuite/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ This crate embeds the latest version of the [WebAssembly Test Suite](https://git
77
```rust
88
use wasm_testsuite::{MVP_TESTS, get_test_wast};
99

10-
wasm_testsuite::MVP_TESTS.iter().for_each(|test| {
10+
MVP_TESTS.iter().for_each(|test| {
1111
let wast_bytes = get_test_wast(test).expect("Failed to get wast bytes");
12-
let wast = std::str::from_utf8(&wast).expect("failed to convert wast to utf8");
12+
let wast = std::str::from_utf8(&wast_bytes).expect("failed to convert wast to utf8");
1313

1414
// Do something with the wast (e.g. parse it using the `wast` crate)
1515
});

0 commit comments

Comments
 (0)