Files
RedBear-OS/local/recipes/system/redbear-threadtest/source/tests/layers.rs
T

200 lines
6.1 KiB
Rust

//! Host-runnable validation tests for the threading stress suite layers.
//!
//! These tests validate the suite's own LOGIC — counting, metrics collection,
//! timeout detection, and the correctness of test-layer behavior on a known-good
//! host threading substrate. They prove the suite is correct BEFORE it is used
//! for Redox-target attribution in QEMU/bare metal.
use redbear_threadtest::{
host_validation_spec, layers, ConsoleProgress, Layer, LayerOutcome, LayerTest, Progress,
SuiteSpec,
};
use redbear_threadtest::layers::l3_rwlock::RwLockTest;
use redbear_threadtest::layers::l4_scope::ScopeTest;
// ---------------------------------------------------------------------------
// Suite validation: host_validation_spec uses small numbers.
// ---------------------------------------------------------------------------
#[test]
fn l3_rwlock_host_validation_writer_makes_progress() {
let spec = host_validation_spec();
let test = RwLockTest::new(&spec);
let mut progress = ConsoleProgress::new();
let outcome = test.run(&mut progress);
assert!(
outcome.passed,
"L3 RwLock writer MUST make progress on host: detail={}",
outcome.detail,
);
assert!(
outcome.iterations > 0,
"expected >0 write acquisitions on host, got {}",
outcome.iterations,
);
assert!(
outcome.hang_attribution.is_none(),
"no attribution expected on host",
);
}
#[test]
fn l4_scope_host_validation_completes_all_iterations() {
let spec = host_validation_spec();
let test = ScopeTest::new(&spec);
let mut progress = ConsoleProgress::new();
let outcome = test.run(&mut progress);
assert!(
outcome.passed,
"L4 scope MUST complete all iterations on host: detail={}",
outcome.detail,
);
// host_validation_spec: 100 iterations * 4 workers = 400 increments.
assert_eq!(
outcome.iterations, 100,
"expected 100 scope iterations",
);
}
#[test]
fn l2_pthread_host_validation_condvar_broadcast() {
let spec = host_validation_spec();
let test = layers::l2_pthread::PthreadTest::new(&spec);
let mut progress = ConsoleProgress::new();
let outcome = test.run(&mut progress);
assert!(
outcome.passed,
"L2 pthread condvar broadcast MUST work on host: detail={}",
outcome.detail,
);
}
#[test]
fn l1_futex_host_mock_validates_counting() {
let spec = host_validation_spec();
let test = layers::l1_futex::FutexTest::new(&spec);
let mut progress = ConsoleProgress::new();
let outcome = test.run(&mut progress);
// Host mock always passes (it uses park/unpark, not real futex).
assert!(outcome.passed);
assert_eq!(outcome.iterations, 50, "expected spec.contention_rounds rounds");
assert!(outcome.wall_time.as_secs_f64() < 30.0, "should complete quickly");
}
#[test]
fn suite_spec_default_is_full_stress() {
let spec = SuiteSpec::default();
assert_eq!(spec.rwlock_readers, 24);
assert_eq!(spec.rwlock_soak.as_secs(), 60);
assert_eq!(spec.scope_iterations, 10_000);
assert_eq!(spec.futex_contention_rounds, 1_000);
}
#[test]
fn host_validation_spec_is_smaller() {
let spec = host_validation_spec();
assert_eq!(spec.rwlock_readers, 4);
assert_eq!(spec.rwlock_soak.as_secs(), 2);
assert_eq!(spec.scope_iterations, 100);
}
#[test]
fn layer_parse_from_str() {
assert_eq!(Layer::from_str("l1").unwrap(), Layer::Futex);
assert_eq!(Layer::from_str("futex").unwrap(), Layer::Futex);
assert_eq!(Layer::from_str("l2").unwrap(), Layer::Pthread);
assert_eq!(Layer::from_str("pthread").unwrap(), Layer::Pthread);
assert_eq!(Layer::from_str("l3").unwrap(), Layer::RwLock);
assert_eq!(Layer::from_str("rwlock").unwrap(), Layer::RwLock);
assert_eq!(Layer::from_str("l4").unwrap(), Layer::Scope);
assert_eq!(Layer::from_str("scope").unwrap(), Layer::Scope);
assert!(Layer::from_str("invalid").is_err());
}
#[test]
fn layer_label_is_stable() {
assert_eq!(Layer::Futex.label(), "L1-futex");
assert_eq!(Layer::Pthread.label(), "L2-pthread");
assert_eq!(Layer::RwLock.label(), "L3-rwlock");
assert_eq!(Layer::Scope.label(), "L4-scope");
}
#[test]
fn layer_all_returns_four() {
assert_eq!(Layer::all().len(), 4);
}
#[test]
fn outcome_hang_attribution_operator_escalation_contains_detail() {
use redbear_threadtest::HangAttribution;
let attr = HangAttribution::OperatorEscalationStdPal {
detail: "test detail".into(),
};
assert!(attr.detail().contains("test detail"));
assert!(
match &attr {
HangAttribution::OperatorEscalationStdPal { .. } => true,
_ => false,
}
);
}
#[test]
fn outcome_passed_has_no_attribution() {
let outcome = LayerOutcome {
layer: Layer::RwLock,
passed: true,
iterations: 10,
wall_time: std::time::Duration::from_secs(1),
detail: "ok".into(),
hang_attribution: None,
};
assert!(outcome.passed);
assert!(outcome.hang_attribution.is_none());
}
#[test]
fn rwlock_stress_zero_writes_is_fail() {
// Directly test the pass/fail logic: zero writes = fail.
let outcome = LayerOutcome {
layer: Layer::RwLock,
passed: false,
iterations: 0,
wall_time: std::time::Duration::from_secs(60),
detail: "0 writes".into(),
hang_attribution: Some(redbear_threadtest::HangAttribution::Relibc {
detail: "starvation".into(),
}),
};
assert!(!outcome.passed);
assert_eq!(outcome.iterations, 0);
}
#[test]
fn console_progress_does_not_panic() {
let mut progress = ConsoleProgress::new();
// Calling heartbeat rapidly should not panic (throttle kicks in).
for i in 0..5 {
progress.heartbeat(i, Some(100), "test");
}
}
#[test]
fn run_all_suites_on_host_validation_spec_completes() {
let spec = host_validation_spec();
let outcomes = layers::run_all_suites(&spec);
assert_eq!(outcomes.len(), 4);
for out in &outcomes {
assert!(
out.passed,
"layer {} must pass on host validation spec: {}",
out.layer.label(),
out.detail,
);
}
}