rusty_emu/tests/lib.rs
Jan-Peter von Hunnius 08f53d68fe feat: implement virtual clock timing architecture (Phases 5-8)
Introduces a VirtualClock-based timing system replacing wall-clock
Instant-based timing for hardware-accurate simulation.

New modules:
- virtual_clock.rs: Shared virtual time source with condvar sync
- real_time_governor.rs: Thread that advances virtual time
- component_timing.rs: Per-component timing constraints

Key changes:
- Component trait: added update_at_time(), timing(), set_virtual_clock_optional()
- All Intel 400x components migrated to virtual-time-aware operation
- ConfigurableSystem: start() returns Result, spawns governor thread
- GUI: timing controls (speed slider, mode selector, step button)
- Console: speed/mode/step/time commands
- Backward compatible: legacy fallback for components without VirtualClock

Reviewed-by: code review found 2 warnings (clock resume() dead code,
reset() safety docs) — both fixed.
2026-05-14 21:00:22 +02:00

206 lines
7.2 KiB
Rust

//! Test library for Intel 400x common functionality
//!
//! This module provides a centralized entry point for all tests
//! and exports common testing utilities.
#![cfg(test)]
// Re-export the rusty_emu crate for use in tests
pub use rusty_emu;
// Module declarations for test files
mod gui_integration_tests;
mod gui_tests;
mod integration_tests;
mod intel_4001_state_machine_test;
mod intel_400x_tests;
// Common test utilities and helpers
pub mod test_utils {
use std::sync::Arc;
use std::time::{Duration, Instant};
/// Create a deterministic time source for testing
pub fn create_test_time() -> Instant {
Instant::now()
}
/// Create a test duration for timing-sensitive tests
pub fn create_test_duration(nanos: u64) -> Duration {
Duration::from_nanos(nanos)
}
/// Helper to advance time in tests
pub fn advance_test_time(duration: Duration) {
std::thread::sleep(duration);
}
/// Create a VirtualClock in MaxSpeed mode for fast deterministic tests.
///
/// The clock starts in stopped state. Call `clock.start()` after spawning
/// the governor thread.
///
/// # Parameters
/// - `tick_size_ns`: Virtual time advance per tick (default: 333ns for 750 kHz)
pub fn create_maxspeed_clock(tick_size_ns: u64) -> Arc<rusty_emu::virtual_clock::VirtualClock> {
let clock = Arc::new(rusty_emu::virtual_clock::VirtualClock::new(
Duration::from_nanos(tick_size_ns),
));
clock.set_mode(rusty_emu::virtual_clock::SimulationMode::MaxSpeed);
clock
}
/// Create a VirtualClock in SingleStep mode for precise state verification.
///
/// Each call to `clock.trigger_step()` advances virtual time by one tick.
pub fn create_singlestep_clock(tick_size_ns: u64) -> Arc<rusty_emu::virtual_clock::VirtualClock> {
let clock = Arc::new(rusty_emu::virtual_clock::VirtualClock::new(
Duration::from_nanos(tick_size_ns),
));
clock.set_mode(rusty_emu::virtual_clock::SimulationMode::SingleStep);
clock
}
/// Spawn a RealTimeGovernor thread for the given clock.
///
/// Returns a JoinHandle that can be used to wait for the governor to exit.
/// The governor will exit when `clock.stop()` is called.
pub fn spawn_governor(
clock: Arc<rusty_emu::virtual_clock::VirtualClock>,
) -> std::thread::JoinHandle<()> {
std::thread::spawn(move || {
let governor = rusty_emu::real_time_governor::RealTimeGovernor::with_defaults(clock);
governor.run();
})
}
/// Stop the virtual clock and wait for the governor thread to finish.
///
/// This ensures clean shutdown of the governor thread before the test ends.
pub fn stop_governor(
clock: &Arc<rusty_emu::virtual_clock::VirtualClock>,
handle: std::thread::JoinHandle<()>,
) {
clock.stop();
// Give the governor thread a moment to exit cleanly
let _ = handle.join();
}
/// Verify that a component implements all required traits
pub fn verify_intel400x_traits<C>(component: &C)
where
C: rusty_emu::components::common::intel_400x::Intel400xClockHandling,
C: rusty_emu::components::common::intel_400x::Intel400xDataBus,
C: rusty_emu::components::common::intel_400x::Intel400xAddressHandling,
C: rusty_emu::components::common::intel_400x::Intel400xControlPins,
C: rusty_emu::components::common::intel_400x::Intel400xResetHandling,
C: rusty_emu::components::common::intel_400x::Intel400xTimingState,
{
// Test Intel400xClockHandling
let _: &dyn rusty_emu::components::common::intel_400x::Intel400xClockHandling = component;
// Test Intel400xDataBus
let _: &dyn rusty_emu::components::common::intel_400x::Intel400xDataBus = component;
// Test Intel400xAddressHandling
let _: &dyn rusty_emu::components::common::intel_400x::Intel400xAddressHandling = component;
// Test Intel400xControlPins
let _: &dyn rusty_emu::components::common::intel_400x::Intel400xControlPins = component;
// Test Intel400xResetHandling
let _: &dyn rusty_emu::components::common::intel_400x::Intel400xResetHandling = component;
// Test Intel400xTimingState
let _: &dyn rusty_emu::components::common::intel_400x::Intel400xTimingState = component;
}
/// Test that timing constants are reasonable
#[allow(dead_code)]
pub fn verify_timing_constants() {
use rusty_emu::components::common::intel_400x::TimingConstants;
// Fast access should be faster than default
assert!(TimingConstants::FAST_ACCESS_TIME < TimingConstants::DEFAULT_ACCESS_TIME);
}
/// Test that state machine properties hold
#[allow(dead_code)]
pub fn verify_state_machine_properties() {
use rusty_emu::components::common::intel_400x::TimingState;
let states = vec![
TimingState::Idle,
TimingState::AddressPhase,
TimingState::WaitLatency,
TimingState::DriveData,
];
for state in states {
// Each state should have consistent query results
match state {
TimingState::Idle => {
assert!(state.is_idle());
assert!(!state.is_address_phase());
assert!(!state.is_waiting_latency());
assert!(!state.is_driving_data());
}
TimingState::AddressPhase => {
assert!(!state.is_idle());
assert!(state.is_address_phase());
assert!(!state.is_waiting_latency());
assert!(!state.is_driving_data());
}
TimingState::WaitLatency => {
assert!(!state.is_idle());
assert!(!state.is_address_phase());
assert!(state.is_waiting_latency());
assert!(!state.is_driving_data());
}
TimingState::DriveData => {
assert!(!state.is_idle());
assert!(!state.is_address_phase());
assert!(!state.is_waiting_latency());
assert!(state.is_driving_data());
}
}
}
}
}
// Test configuration and setup
pub mod test_config {
/// Test configuration for running the test suite
pub struct TestConfig {
pub enable_detailed_output: bool,
pub enable_timing_tests: bool,
pub enable_property_tests: bool,
}
impl Default for TestConfig {
fn default() -> Self {
Self {
enable_detailed_output: false,
enable_timing_tests: true,
enable_property_tests: true,
}
}
}
impl TestConfig {
pub fn with_detailed_output(mut self) -> Self {
self.enable_detailed_output = true;
self
}
pub fn without_timing_tests(mut self) -> Self {
self.enable_timing_tests = false;
self
}
pub fn without_property_tests(mut self) -> Self {
self.enable_property_tests = false;
self
}
}
}