Skip to content

Commit d614745

Browse files
authored
Merge pull request #34 from arnoox/mem-fill-sort-test
feat: add end-to-end tests for Rust → Wasm memory operations
2 parents 5348eab + d3cd2dc commit d614745

2 files changed

Lines changed: 188 additions & 0 deletions

File tree

crates/herkos-tests/data/rust/rust_e2e_memory_bench.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,15 @@ static mut BUF: [i32; 1024] = [0i32; 1024];
2525
pub extern "C" fn mem_fill_sort_sum(n: i32, seed: i32) -> i32 {
2626
unsafe { fill_sort_sum_impl(&mut BUF, n, seed) }
2727
}
28+
29+
/// Read one element from the work buffer by index.
30+
///
31+
/// Returns 0 for out-of-range indices. Intended for tests that need to
32+
/// inspect the buffer after a `mem_fill_sort_sum` call.
33+
#[no_mangle]
34+
pub extern "C" fn mem_read_element(idx: i32) -> i32 {
35+
if idx < 0 || idx as usize >= 1024 {
36+
return 0;
37+
}
38+
unsafe { BUF[idx as usize] }
39+
}
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
//! End-to-end tests: Rust → Wasm → Rust (fill / bubble-sort / sum over Wasm memory).
2+
//!
3+
//! The source module (`data/rust/rust_e2e_memory_bench.rs`) exports a single
4+
//! function:
5+
//!
6+
//! ```text
7+
//! mem_fill_sort_sum(n: i32, seed: i32) -> i32
8+
//! ```
9+
//!
10+
//! It fills the first `n` elements of a 1024-element static buffer with LCG
11+
//! pseudo-random values, bubble-sorts them in place, then returns a wrapping
12+
//! checksum. Tests verify the transpiled output against a native reference.
13+
14+
use herkos_tests::rust_e2e_memory_bench;
15+
16+
fn new_module() -> rust_e2e_memory_bench::WasmModule {
17+
rust_e2e_memory_bench::new().expect("module instantiation should succeed")
18+
}
19+
20+
// ── Reference implementation ──────────────────────────────────────────────────
21+
22+
include!("../data/rust/common/fill_sort_sum.rs");
23+
24+
fn fill_sort_sum_ref(n: i32, seed: i32) -> i32 {
25+
let mut buf = [0i32; 1024];
26+
fill_sort_sum_impl(&mut buf, n, seed)
27+
}
28+
29+
// ── Edge cases ────────────────────────────────────────────────────────────────
30+
31+
#[test]
32+
fn test_zero_elements_returns_zero() {
33+
let mut m = new_module();
34+
assert_eq!(m.mem_fill_sort_sum(0, 0).unwrap(), 0);
35+
assert_eq!(m.mem_fill_sort_sum(0, 42).unwrap(), 0);
36+
}
37+
38+
#[test]
39+
fn test_negative_n_returns_zero() {
40+
let mut m = new_module();
41+
assert_eq!(m.mem_fill_sort_sum(-1, 0).unwrap(), 0);
42+
assert_eq!(m.mem_fill_sort_sum(i32::MIN, 1).unwrap(), 0);
43+
}
44+
45+
#[test]
46+
fn test_single_element() {
47+
let mut m = new_module();
48+
// With n=1 there is nothing to sort; checksum is just the one LCG value.
49+
let seed: i32 = 1;
50+
let expected = seed.wrapping_mul(1103515245_i32).wrapping_add(12345);
51+
assert_eq!(m.mem_fill_sort_sum(1, seed).unwrap(), expected);
52+
}
53+
54+
// ── Cross-validation against reference ───────────────────────────────────────
55+
56+
#[test]
57+
fn test_matches_reference_small_n() {
58+
let mut m = new_module();
59+
for n in 1i32..=16 {
60+
assert_eq!(
61+
m.mem_fill_sort_sum(n, 0).unwrap(),
62+
fill_sort_sum_ref(n, 0),
63+
"n={n} seed=0"
64+
);
65+
}
66+
}
67+
68+
#[test]
69+
fn test_matches_reference_various_seeds() {
70+
let mut m = new_module();
71+
let cases: &[(i32, i32)] = &[
72+
(10, 0),
73+
(10, 1),
74+
(10, -1),
75+
(10, i32::MAX),
76+
(10, i32::MIN),
77+
(32, 42),
78+
(64, 12345),
79+
(128, -99999),
80+
];
81+
for &(n, seed) in cases {
82+
assert_eq!(
83+
m.mem_fill_sort_sum(n, seed).unwrap(),
84+
fill_sort_sum_ref(n, seed),
85+
"n={n} seed={seed}"
86+
);
87+
}
88+
}
89+
90+
#[test]
91+
fn test_matches_reference_full_buffer() {
92+
let mut m = new_module();
93+
// n=1024 exercises every element of the static buffer.
94+
assert_eq!(
95+
m.mem_fill_sort_sum(1024, 7).unwrap(),
96+
fill_sort_sum_ref(1024, 7)
97+
);
98+
}
99+
100+
#[test]
101+
fn test_n_capped_at_1024() {
102+
let mut m = new_module();
103+
// Values beyond 1024 must be clamped; result should equal n=1024.
104+
assert_eq!(
105+
m.mem_fill_sort_sum(2048, 7).unwrap(),
106+
fill_sort_sum_ref(1024, 7),
107+
"n>1024 must be clamped to 1024"
108+
);
109+
}
110+
111+
// ── Sorting invariant ─────────────────────────────────────────────────────────
112+
//
113+
// After each call we read back every element via mem_read_element and confirm
114+
// the buffer is non-decreasing. Note: a wrapping sum is commutative, so sum
115+
// equality alone is not sufficient to verify that sorting occurred.
116+
117+
fn assert_sorted(m: &mut rust_e2e_memory_bench::WasmModule, n: i32, label: &str) {
118+
for i in 0..(n - 1) {
119+
let a = m.mem_read_element(i).unwrap();
120+
let b = m.mem_read_element(i + 1).unwrap();
121+
assert!(
122+
a <= b,
123+
"{label}: buf[{i}]={a} > buf[{}]={b} — buffer is not sorted",
124+
i + 1
125+
);
126+
}
127+
}
128+
129+
#[test]
130+
fn test_buffer_is_sorted_after_call() {
131+
let mut m = new_module();
132+
let cases: &[(i32, i32)] = &[
133+
(1, 0),
134+
(2, 0),
135+
(10, 42),
136+
(32, -99),
137+
(64, 9999),
138+
(128, i32::MAX),
139+
(256, i32::MIN),
140+
(1024, 7),
141+
];
142+
for &(n, seed) in cases {
143+
m.mem_fill_sort_sum(n, seed).unwrap();
144+
assert_sorted(&mut m, n, &format!("n={n} seed={seed}"));
145+
}
146+
}
147+
148+
#[test]
149+
fn test_deterministic_across_calls() {
150+
let mut m = new_module();
151+
let first = m.mem_fill_sort_sum(64, 9999).unwrap();
152+
let second = m.mem_fill_sort_sum(64, 9999).unwrap();
153+
assert_eq!(
154+
first, second,
155+
"same (n, seed) must always yield the same checksum"
156+
);
157+
}
158+
159+
// ── Sequential calls with distinct inputs remain independent ──────────────────
160+
//
161+
// The module keeps a static buffer; verify that successive calls with different
162+
// seeds still match the reference (no stale state from a previous call leaks
163+
// into the next fill).
164+
165+
#[test]
166+
fn test_sequential_calls_independent() {
167+
let mut m = new_module();
168+
let seeds = [0i32, 1, -1, 100, 999_999, i32::MAX, i32::MIN];
169+
for seed in seeds {
170+
assert_eq!(
171+
m.mem_fill_sort_sum(32, seed).unwrap(),
172+
fill_sort_sum_ref(32, seed),
173+
"seed={seed}"
174+
);
175+
}
176+
}

0 commit comments

Comments
 (0)