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

Commit f203c4a

Browse files
test(tinywasm): create wasm spec test harness
Signed-off-by: Henry Gressmann <mail@henrygressmann.de>
1 parent 4ba9393 commit f203c4a

File tree

5 files changed

+140
-5
lines changed

5 files changed

+140
-5
lines changed

Cargo.lock

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

crates/parser/src/conversion.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ pub(crate) fn convert_module_code(
4646
}
4747

4848
let body_reader = func.get_operators_reader()?;
49-
let body = process_operators(body_reader.into_iter(), validator)?;
49+
let body = process_operators(body_reader.original_position(), body_reader.into_iter(), validator)?;
5050

5151
Ok(CodeSection {
5252
locals: locals.into_boxed_slice(),
@@ -111,12 +111,13 @@ pub(crate) fn convert_memarg(memarg: wasmparser::MemArg) -> MemArg {
111111
}
112112

113113
pub fn process_operators<'a>(
114+
offset: usize,
114115
ops: impl Iterator<Item = Result<wasmparser::Operator<'a>, wasmparser::BinaryReaderError>>,
115116
mut validator: FuncValidator<ValidatorResources>,
116117
) -> Result<Box<[Instruction]>> {
117118
let mut instructions = Vec::new();
118119

119-
let mut offset = 0;
120+
let mut offset = offset.into();
120121
for op in ops {
121122
let op = op?;
122123
validator.op(offset, &op)?;

crates/tinywasm/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ log={version="0.4", optional=true}
1717
tinywasm-parser={version="0.0.3", path="../parser", default-features=false, optional=true}
1818
tinywasm-types={version="0.0.3", path="../types", default-features=false}
1919

20+
[dev-dependencies]
21+
wasm-testsuite={path="../wasm-testsuite"}
22+
wast={version="69.0"}
23+
owo-colors={version="3.5"}
24+
2025
[features]
2126
default=["std", "parser", "logging"]
2227
logging=["log", "tinywasm-types/logging", "tinywasm-parser?/logging"]

crates/tinywasm/tests/mvp.rs

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
use std::{
2+
collections::HashMap,
3+
fmt::{Debug, Formatter},
4+
};
5+
6+
use tinywasm::{Error, Result};
7+
use tinywasm_types::TinyWasmModule;
8+
use wast::{
9+
lexer::Lexer,
10+
parser::{self, ParseBuffer},
11+
QuoteWat, Wast,
12+
};
13+
14+
fn parse_module(mut module: wast::core::Module) -> Result<TinyWasmModule, Error> {
15+
let parser = tinywasm_parser::Parser::new();
16+
Ok(parser.parse_module_bytes(module.encode().expect("failed to encode module"))?)
17+
}
18+
19+
#[test]
20+
#[ignore]
21+
fn test_mvp() {
22+
let mut test_suite = TestSuite::new();
23+
24+
wasm_testsuite::MVP_TESTS.iter().for_each(|name| {
25+
println!("test: {}", name);
26+
27+
let test_group = test_suite.test_group("mvp");
28+
29+
let wast = wasm_testsuite::get_test_wast(name).expect("failed to get test wast");
30+
let wast = std::str::from_utf8(&wast).expect("failed to convert wast to utf8");
31+
32+
let mut lexer = Lexer::new(&wast);
33+
lexer.allow_confusing_unicode(true);
34+
35+
let buf = ParseBuffer::new_with_lexer(lexer).expect("failed to create parse buffer");
36+
let wast_data = parser::parse::<Wast>(&buf).expect("failed to parse wat");
37+
38+
for directive in wast_data.directives {
39+
let span = directive.span();
40+
41+
use wast::WastDirective::*;
42+
match directive {
43+
Wat(QuoteWat::Wat(wast::Wat::Module(module))) => {
44+
let module = parse_module(module).map(|_| ());
45+
test_group.module_compiles(name, span, module);
46+
}
47+
_ => {}
48+
}
49+
}
50+
});
51+
52+
if test_suite.failed() {
53+
panic!("failed one or more tests: {:#?}", test_suite);
54+
}
55+
}
56+
57+
struct TestSuite(HashMap<String, TestGroup>);
58+
59+
impl TestSuite {
60+
fn new() -> Self {
61+
Self(HashMap::new())
62+
}
63+
64+
fn failed(&self) -> bool {
65+
self.0.values().any(|group| group.failed())
66+
}
67+
68+
fn test_group(&mut self, name: &str) -> &mut TestGroup {
69+
self.0.entry(name.to_string()).or_insert_with(TestGroup::new)
70+
}
71+
}
72+
73+
impl Debug for TestSuite {
74+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
75+
use owo_colors::OwoColorize;
76+
let mut passed_count = 0;
77+
let mut failed_count = 0;
78+
79+
for (group_name, group) in &self.0 {
80+
writeln!(f, "{}", group_name.bold().underline())?;
81+
for (test_name, test) in &group.tests {
82+
writeln!(f, " {}", test_name.bold())?;
83+
match test.result {
84+
Ok(()) => {
85+
writeln!(f, " Result: {}", "Passed".green())?;
86+
passed_count += 1;
87+
}
88+
Err(_) => {
89+
writeln!(f, " Result: {}", "Failed".red())?;
90+
failed_count += 1;
91+
}
92+
}
93+
writeln!(f, " Span: {:?}", test.span)?;
94+
}
95+
}
96+
97+
writeln!(f, "\n{}", "Test Summary:".bold().underline())?;
98+
writeln!(f, " Total Tests: {}", (passed_count + failed_count))?;
99+
writeln!(f, " Passed: {}", passed_count.to_string().green())?;
100+
writeln!(f, " Failed: {}", failed_count.to_string().red())?;
101+
Ok(())
102+
}
103+
}
104+
105+
struct TestGroup {
106+
tests: HashMap<String, TestCase>,
107+
}
108+
109+
impl TestGroup {
110+
fn new() -> Self {
111+
Self { tests: HashMap::new() }
112+
}
113+
114+
fn failed(&self) -> bool {
115+
self.tests.values().any(|test| test.result.is_err())
116+
}
117+
118+
fn module_compiles(&mut self, name: &str, span: wast::token::Span, result: Result<()>) {
119+
self.tests.insert(name.to_string(), TestCase { result, span });
120+
}
121+
}
122+
123+
struct TestCase {
124+
result: Result<()>,
125+
span: wast::token::Span,
126+
}

crates/wasm-testsuite/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@ pub const PROPOSALS: &[&str] = &["annotations", "exception-handling", "memory64"
3030

3131
/// List of all tests that apply to the MVP (V1) spec.
3232
/// Note that the tests are still for the latest spec, so the latest version of Wast is used.
33-
#[rustfmt::skip]
34-
pub const MVP_TESTS: &[&str] = &["address.wast","address.wast","align.wast","align.wast","binary-leb128.wast","binary-leb128.wast","binary.wast","binary.wast","block.wast","block.wast","br.wast","br.wast","br_if.wast","br_if.wast","br_table.wast","br_table.wast","break-drop.wast","break-drop.wast","call.wast","call.wast","call_indirect.wast","call_indirect.wast","comments.wast","comments.wast","const.wast","const.wast","conversions.wast","conversions.wast","custom.wast","custom.wast","data.wast","data.wast","elem.wast","elem.wast","endianness.wast","endianness.wast","exports.wast","exports.wast","f32.wast","f32.wast","f32_bitwise.wast","f32_bitwise.wast","f32_cmp.wast","f32_cmp.wast","f64.wast","f64.wast","f64_bitwise.wast","f64_bitwise.wast","f64_cmp.wast","f64_cmp.wast","fac.wast","fac.wast","float_exprs.wast","float_exprs.wast","float_literals.wast","float_literals.wast","float_memory.wast","float_memory.wast","float_misc.wast","float_misc.wast","forward.wast","forward.wast","func.wast","func.wast","func_ptrs.wast","func_ptrs.wast","globals.wast","globals.wast","i32.wast","i32.wast","i64.wast","i64.wast","if.wast","if.wast","imports.wast","imports.wast","inline-module.wast","inline-module.wast","int_exprs.wast","int_exprs.wast","int_literals.wast","int_literals.wast","labels.wast","labels.wast","left-to-right.wast","left-to-right.wast","linking.wast","linking.wast","load.wast","load.wast","local_get.wast","local_get.wast","local_set.wast","local_set.wast","local_tee.wast","local_tee.wast","loop.wast","loop.wast","memory.wast","memory.wast","memory_grow.wast","memory_grow.wast","memory_redundancy.wast","memory_redundancy.wast","memory_size.wast","memory_size.wast","memory_trap.wast","memory_trap.wast","names.wast","names.wast","nop.wast","nop.wast","return.wast","return.wast","select.wast","select.wast","skip-stack-guard-page.wast","skip-stack-guard-page.wast","stack.wast","stack.wast","start.wast","start.wast","store.wast","store.wast","switch.wast","switch.wast","token.wast","token.wast","traps.wast","traps.wast","type.wast","type.wast","unreachable.wast","unreachable.wast","unreached-invalid.wast","unreached-invalid.wast","unwind.wast","unwind.wast","utf8-custom-section-id.wast","utf8-custom-section-id.wast","utf8-import-field.wast","utf8-import-field.wast","utf8-import-module.wast","utf8-import-module.wast","utf8-invalid-encoding.wast","utf8-invalid-encoding.wast"];
33+
#[rustfmt::skip] // removed: "break-drop.wast",
34+
pub const MVP_TESTS: &[&str] = &["address.wast","align.wast","binary-leb128.wast","binary.wast","block.wast","br.wast","br_if.wast","br_table.wast","call.wast","call_indirect.wast","comments.wast","const.wast","conversions.wast","custom.wast","data.wast","elem.wast","endianness.wast","exports.wast","f32.wast","f32_bitwise.wast","f32_cmp.wast","f64.wast","f64_bitwise.wast","f64_cmp.wast","fac.wast","float_exprs.wast","float_literals.wast","float_memory.wast","float_misc.wast","forward.wast","func.wast","func_ptrs.wast","global.wast","i32.wast","i64.wast","if.wast","imports.wast","inline-module.wast","int_exprs.wast","int_literals.wast","labels.wast","left-to-right.wast","linking.wast","load.wast","local_get.wast","local_set.wast","local_tee.wast","loop.wast","memory.wast","memory_grow.wast","memory_redundancy.wast","memory_size.wast","memory_trap.wast","names.wast","nop.wast","return.wast","select.wast","skip-stack-guard-page.wast","stack.wast","start.wast","store.wast","switch.wast","token.wast","traps.wast","type.wast","unreachable.wast","unreached-invalid.wast","unwind.wast","utf8-custom-section-id.wast","utf8-import-field.wast","utf8-import-module.wast","utf8-invalid-encoding.wast"];
3535

3636
/// List of all tests that apply to the V2 draft 1 spec.
3737
#[rustfmt::skip]
38-
pub const V2_DRAFT_1_TESTS: &[&str] = &["address.wast","address.wast","align.wast","align.wast","binary-leb128.wast","binary-leb128.wast","binary.wast","binary.wast","block.wast","block.wast","br.wast","br.wast","br_if.wast","br_if.wast","br_table.wast","br_table.wast","bulk.wast","bulk.wast","call.wast","call.wast","call_indirect.wast","call_indirect.wast","comments.wast","comments.wast","const.wast","const.wast","conversions.wast","conversions.wast","custom.wast","custom.wast","data.wast","data.wast","elem.wast","elem.wast","endianness.wast","endianness.wast","exports.wast","exports.wast","f32.wast","f32.wast","f32_bitwise.wast","f32_bitwise.wast","f32_cmp.wast","f32_cmp.wast","f64.wast","f64.wast","f64_bitwise.wast","f64_bitwise.wast","f64_cmp.wast","f64_cmp.wast","fac.wast","fac.wast","float_exprs.wast","float_exprs.wast","float_literals.wast","float_literals.wast","float_memory.wast","float_memory.wast","float_misc.wast","float_misc.wast","forward.wast","forward.wast","func.wast","func.wast","func_ptrs.wast","func_ptrs.wast","global.wast","global.wast","i32.wast","i32.wast","i64.wast","i64.wast","if.wast","if.wast","imports.wast","imports.wast","inline-module.wast","inline-module.wast","int_exprs.wast","int_exprs.wast","int_literals.wast","int_literals.wast","labels.wast","labels.wast","left-to-right.wast","left-to-right.wast","linking.wast","linking.wast","load.wast","load.wast","local_get.wast","local_get.wast","local_set.wast","local_set.wast","local_tee.wast","local_tee.wast","loop.wast","loop.wast","memory.wast","memory.wast","memory_copy.wast","memory_copy.wast","memory_fill.wast","memory_fill.wast","memory_grow.wast","memory_grow.wast","memory_init.wast","memory_init.wast","memory_redundancy.wast","memory_redundancy.wast","memory_size.wast","memory_size.wast","memory_trap.wast","memory_trap.wast","names.wast","names.wast","nop.wast","nop.wast","ref_func.wast","ref_func.wast","ref_is_null.wast","ref_is_null.wast","ref_null.wast","ref_null.wast","return.wast","return.wast","select.wast","select.wast","skip-stack-guard-page.wast","skip-stack-guard-page.wast","stack.wast","stack.wast","start.wast","start.wast","store.wast","store.wast","switch.wast","switch.wast","table-sub.wast","table-sub.wast","table.wast","table.wast","table_copy.wast","table_copy.wast","table_fill.wast","table_fill.wast","table_get.wast","table_get.wast","table_grow.wast","table_grow.wast","table_init.wast","table_init.wast","table_set.wast","table_set.wast","table_size.wast","table_size.wast","token.wast","token.wast","traps.wast","traps.wast","type.wast","type.wast","unreachable.wast","unreachable.wast","unreached-invalid.wast","unreached-invalid.wast","unreached-valid.wast","unreached-valid.wast","unwind.wast","unwind.wast","utf8-custom-section-id.wast","utf8-custom-section-id.wast","utf8-import-field.wast","utf8-import-field.wast","utf8-import-module.wast","utf8-import-module.wast","utf8-invalid-encoding.wast","utf8-invalid-encoding.wast"];
38+
pub const V2_DRAFT_1_TESTS: &[&str] = &["address.wast","align.wast","binary-leb128.wast","binary.wast","block.wast","br.wast","br_if.wast","br_table.wast","bulk.wast","call.wast","call_indirect.wast","comments.wast","const.wast","conversions.wast","custom.wast","data.wast","elem.wast","endianness.wast","exports.wast","f32.wast","f32_bitwise.wast","f32_cmp.wast","f64.wast","f64_bitwise.wast","f64_cmp.wast","fac.wast","float_exprs.wast","float_literals.wast","float_memory.wast","float_misc.wast","forward.wast","func.wast","func_ptrs.wast","global.wast","i32.wast","i64.wast","if.wast","imports.wast","inline-module.wast","int_exprs.wast","int_literals.wast","labels.wast","left-to-right.wast","linking.wast","load.wast","local_get.wast","local_set.wast","local_tee.wast","loop.wast","memory.wast","memory_copy.wast","memory_fill.wast","memory_grow.wast","memory_init.wast","memory_redundancy.wast","memory_size.wast","memory_trap.wast","names.wast","nop.wast","ref_func.wast","ref_is_null.wast","ref_null.wast","return.wast","select.wast","skip-stack-guard-page.wast","stack.wast","start.wast","store.wast","switch.wast","table-sub.wast","table.wast","table_copy.wast","table_fill.wast","table_get.wast","table_grow.wast","table_init.wast","table_set.wast","table_size.wast","token.wast","traps.wast","type.wast","unreachable.wast","unreached-invalid.wast","unreached-valid.wast","unwind.wast","utf8-custom-section-id.wast","utf8-import-field.wast","utf8-import-module.wast","utf8-invalid-encoding.wast"];
3939

4040
/// Get all test file names and their contents.
4141
pub fn get_tests_wast(include_proposals: &[String]) -> impl Iterator<Item = (String, Cow<'static, [u8]>)> {

0 commit comments

Comments
 (0)