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.
206 lines
7.2 KiB
Rust
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
|
|
}
|
|
}
|
|
}
|