Files
smarm/tests/stack.rs
Claude 978678a46e feat: full runtime redesign (v0.6)
Complete rewrite with improved architecture & correctness:
- src/runtime.rs: Simplified task scheduling with proper state transitions
- src/scheduler.rs: Decoupled from runtime, pure task queue logic
- src/io.rs, src/mutex.rs: Refactored for clarity & performance
- New actor model framework (src/actor.rs, src/context.rs)
- Channel primitives (src/channel.rs) & process IDs (src/pid.rs)
- Preemption framework (src/preempt.rs) for fair timeslicing
- Expanded benchmarks & tests (multi_scheduler, primes, runtime)
2026-05-23 16:09:35 +00:00

124 lines
3.3 KiB
Rust

//! Stack allocator tests.
//!
//! Covers allocation, alignment, read/write across the usable region, and
//! (via subprocess) that the guard page actually SIGSEGVs.
use smarm::stack::Stack;
#[test]
fn top_is_16_byte_aligned() {
let s = Stack::new(64 * 1024).unwrap();
assert_eq!(s.top() as usize % 16, 0);
}
#[test]
fn top_is_within_allocation() {
let s = Stack::new(64 * 1024).unwrap();
let top = s.top() as usize;
let base = s.usable_base() as usize;
assert!(top > base);
assert!(top <= base + s.stack_size());
}
#[test]
fn write_and_read_top_of_stack() {
let s = Stack::new(64 * 1024).unwrap();
let sentinel: u64 = 0xDEAD_BEEF_CAFE_1234;
unsafe {
let ptr = s.top().sub(8) as *mut u64;
ptr.write_volatile(sentinel);
assert_eq!(ptr.read_volatile(), sentinel);
}
}
#[test]
fn write_and_read_bottom_of_usable_region() {
let s = Stack::new(64 * 1024).unwrap();
let sentinel: u64 = 0x0102_0304_0506_0708;
unsafe {
let ptr = s.usable_base() as *mut u64;
ptr.write_volatile(sentinel);
assert_eq!(ptr.read_volatile(), sentinel);
}
}
#[test]
fn small_stack_allocates() {
assert!(Stack::new(4096).is_ok());
}
#[test]
fn large_stack_allocates() {
assert!(Stack::new(8 * 1024 * 1024).is_ok());
}
#[test]
fn stack_size_at_least_requested() {
let s = Stack::new(64 * 1024).unwrap();
assert!(s.stack_size() >= 64 * 1024);
}
// ---------------------------------------------------------------------------
// Guard page SIGSEGV tests — subprocess-based.
// ---------------------------------------------------------------------------
use std::env;
use std::process::Command;
fn run_as_child_if_requested() {
match env::var("SMARM_SUBTEST").as_deref() {
Ok("guard_page_direct") => {
let s = Stack::new(64 * 1024).unwrap();
unsafe {
let guard_ptr = s.usable_base().sub(1);
guard_ptr.write_volatile(0xAB);
}
std::process::exit(0);
}
Ok("stack_overflow") => {
let s = Stack::new(64 * 1024).unwrap();
unsafe {
let mut ptr = s.top().sub(1);
let stop = s.usable_base().sub(1);
while ptr >= stop {
ptr.write_volatile(0xFF);
ptr = ptr.sub(1);
}
}
std::process::exit(0);
}
_ => {}
}
}
fn spawn_subtest(name: &str) -> std::process::ExitStatus {
let exe = env::current_exe().unwrap();
Command::new(exe)
.env("SMARM_SUBTEST", name)
.args(["--test-threads=1", "--quiet"])
.status()
.expect("failed to spawn subprocess")
}
#[test]
fn guard_page_causes_sigsegv() {
run_as_child_if_requested();
let status = spawn_subtest("guard_page_direct");
#[cfg(unix)]
{
use std::os::unix::process::ExitStatusExt;
assert_eq!(status.signal(), Some(11), "expected SIGSEGV, got: {:?}", status);
}
}
#[test]
fn stack_overflow_causes_sigsegv() {
run_as_child_if_requested();
let status = spawn_subtest("stack_overflow");
#[cfg(unix)]
{
use std::os::unix::process::ExitStatusExt;
assert_eq!(status.signal(), Some(11), "expected SIGSEGV, got: {:?}", status);
}
}