//! Virtual Time Integration Tests //! //! Tests for the virtual time architecture (Phase 9): //! - MaxSpeed mode for fast deterministic execution //! - SingleStep mode for precise state verification //! - Overclocking tests verifying component timing violations //! - Virtual clock integration with ConfigurableSystem use std::sync::Arc; use std::time::Duration; use rusty_emu::component::Component; use rusty_emu::component_timing::ComponentTiming; use rusty_emu::components::common::intel_400x::*; use rusty_emu::components::memory::intel_4001::Intel4001; use rusty_emu::components::memory::intel_4002::Intel4002; use rusty_emu::components::memory::intel_4003::Intel4003; use rusty_emu::real_time_governor::RealTimeGovernor; use rusty_emu::system_config::SystemFactory; use rusty_emu::virtual_clock::{SimulationMode, VirtualClock}; // ============================================================================= // MaxSpeed Mode Tests // ============================================================================= #[cfg(test)] mod maxspeed_tests { use super::*; /// Verify that a VirtualClock in MaxSpeed mode advances time /// when driven by a RealTimeGovernor. #[test] fn test_maxspeed_clock_advances() { let clock = Arc::new(VirtualClock::new(Duration::from_nanos(333))); let governor = RealTimeGovernor::with_defaults(clock.clone()); let handle = std::thread::spawn(move || { governor.run(); }); // Let the governor run for a bit (it calls clock.start() internally) std::thread::sleep(Duration::from_millis(50)); // Virtual time should have advanced significantly let vt = clock.now_ns(); assert!(vt > 0, "Virtual time should have advanced past 0"); // Verify we can read the time assert!(clock.now() > Duration::ZERO, "Virtual time should be > 0"); clock.stop(); let _ = handle.join(); } /// Verify that multiple time targets are reached in MaxSpeed mode. #[test] fn test_maxspeed_multiple_targets() { let clock = Arc::new(VirtualClock::new(Duration::from_nanos(100))); let governor = RealTimeGovernor::with_defaults(clock.clone()); let handle = std::thread::spawn(move || { governor.run(); }); // Let the governor advance time std::thread::sleep(Duration::from_millis(50)); // Virtual time should have advanced well past multiple targets let vt = clock.now_ns(); assert!(vt >= 1000, "Should have passed 1000ns, got {}", vt); assert!(vt >= 5000, "Should have passed 5000ns, got {}", vt); assert!(vt >= 10000, "Should have passed 10000ns, got {}", vt); clock.stop(); let _ = handle.join(); } /// Verify that component update_at_time works with virtual time in MaxSpeed mode. /// Tests that a ROM component can be updated with specific virtual times. #[test] fn test_maxspeed_component_update_at_time() { let mut rom = Intel4001::new_with_access_time("TestROM".to_string(), 500); // Load test data rom.load_rom_data(vec![0xAB, 0xCD], 0).unwrap(); // Simulate address latching at virtual time T=0 rom.set_timing_state(TimingState::Idle); rom.set_address_high_nibble(Some(0x0)); rom.set_address_low_nibble(Some(0x0)); rom.set_full_address_ready(true); rom.set_address_latch_time(Some(Duration::ZERO)); // At T=0 (before access_time), data should not be ready let timing = ComponentTiming::intel_4001(); let elapsed = Duration::ZERO; assert!( timing.check_access_time(elapsed).is_err(), "Access time should not have elapsed at T=0" ); // At T=500ns (exactly access_time), data should be ready let elapsed_500 = Duration::from_nanos(500); assert!( timing.check_access_time(elapsed_500).is_ok(), "Access time should have elapsed at T=500ns" ); // At T=600ns (after access_time), data should definitely be ready let elapsed_600 = Duration::from_nanos(600); assert!( timing.check_access_time(elapsed_600).is_ok(), "Access time should have elapsed at T=600ns" ); } /// Verify that a ROM with 1ns access time works correctly through /// the full state machine in MaxSpeed mode. #[test] fn test_maxspeed_rom_fast_access() { // Create ROM with 1ns access time (fast for testing) let mut rom = Intel4001::new_with_access_time("FastROM".to_string(), 1); rom.load_rom_data(vec![0x42], 0).unwrap(); // Drive through state machine with virtual time // T=0: Idle state assert_eq!(rom.get_timing_state(), TimingState::Idle); // T=100ns: Set address (simulating PHI1 rising edge) let t = Duration::from_nanos(100); rom.set_address_high_nibble(Some(0x0)); rom.set_address_low_nibble(Some(0x0)); rom.set_full_address_ready(true); rom.set_address_latch_time(Some(t)); rom.set_timing_state(TimingState::AddressPhase); assert_eq!(rom.get_timing_state(), TimingState::AddressPhase); // T=200ns: Wait for access time (1ns already elapsed) rom.set_timing_state(TimingState::WaitLatency); // T=300ns: Drive data rom.set_timing_state(TimingState::DriveData); assert_eq!(rom.get_timing_state(), TimingState::DriveData); // Verify data is accessible assert_eq!(rom.read_rom(0x00).unwrap(), 0x42); } } // ============================================================================= // SingleStep Mode Tests // ============================================================================= #[cfg(test)] mod singlestep_tests { use super::*; /// Verify that each trigger_step() advances virtual time by exactly one tick. #[test] fn test_singlestep_advances_one_tick() { let clock = Arc::new(VirtualClock::new(Duration::from_nanos(333))); clock.set_mode(SimulationMode::SingleStep); let governor = RealTimeGovernor::with_defaults(clock.clone()); let handle = std::thread::spawn(move || { governor.run(); }); // Wait for governor to start std::thread::sleep(Duration::from_millis(50)); // Initial time should be 0 assert_eq!(clock.now_ns(), 0, "Initial time should be 0"); // Trigger one step clock.trigger_step(); std::thread::sleep(Duration::from_millis(50)); assert_eq!(clock.now_ns(), 333, "After 1 step, time should be 333ns"); // Trigger another step clock.trigger_step(); std::thread::sleep(Duration::from_millis(50)); assert_eq!(clock.now_ns(), 666, "After 2 steps, time should be 666ns"); // Trigger a third step clock.trigger_step(); std::thread::sleep(Duration::from_millis(50)); assert_eq!(clock.now_ns(), 999, "After 3 steps, time should be 999ns"); clock.stop(); let _ = handle.join(); } /// Verify that ROM state can be inspected at specific virtual times /// using SingleStep mode. #[test] fn test_singlestep_rom_state_at_specific_time() { let clock = Arc::new(VirtualClock::new(Duration::from_nanos(100))); clock.set_mode(SimulationMode::SingleStep); let governor = RealTimeGovernor::with_defaults(clock.clone()); let handle = std::thread::spawn(move || { governor.run(); }); // Wait for governor to start std::thread::sleep(Duration::from_millis(50)); // Create ROM with 500ns access time (standard Intel 4001) let mut rom = Intel4001::new_with_access_time("StepROM".to_string(), 500); rom.load_rom_data(vec![0xAB], 0).unwrap(); // Trigger 1 step → T=100ns — latch address clock.trigger_step(); std::thread::sleep(Duration::from_millis(20)); let t_latch = clock.now(); assert!(t_latch >= Duration::from_nanos(100)); rom.set_address_latch_time(Some(t_latch)); rom.set_address_high_nibble(Some(0x0)); rom.set_address_low_nibble(Some(0x0)); rom.set_full_address_ready(true); // At T=100ns, 0ns have elapsed since latch — access time NOT met let timing = ComponentTiming::intel_4001(); assert!( timing.check_access_time(Duration::ZERO).is_err(), "At T=100ns (0ns since latch), access time (500ns) should NOT be met" ); // Trigger 4 more steps → T=500ns (4 × 100ns = 400ns more) for _ in 0..4 { clock.trigger_step(); std::thread::sleep(Duration::from_millis(20)); } // At T=500ns, 400ns have elapsed since latch at T=100ns — still NOT enough let elapsed_400 = Duration::from_nanos(400); assert!( timing.check_access_time(elapsed_400).is_err(), "At T=500ns (400ns since latch at T=100ns), access time (500ns) should NOT be met" ); // Trigger 1 more step → T=600ns clock.trigger_step(); std::thread::sleep(Duration::from_millis(20)); // At T=600ns, 500ns have elapsed since latch at T=100ns — exactly access time let elapsed_500 = Duration::from_nanos(500); assert!( timing.check_access_time(elapsed_500).is_ok(), "At T=600ns (500ns since latch at T=100ns), access time (500ns) SHOULD be met" ); clock.stop(); let _ = handle.join(); } /// Verify that trigger_step returns false when not in SingleStep mode. #[test] fn test_trigger_step_only_works_in_singlestep() { let clock = Arc::new(VirtualClock::new(Duration::from_nanos(333))); // Default mode is RealTime — trigger_step should fail assert!( !clock.trigger_step(), "trigger_step should return false in RealTime mode" ); // Switch to MaxSpeed — trigger_step should still fail clock.set_mode(SimulationMode::MaxSpeed); assert!( !clock.trigger_step(), "trigger_step should return false in MaxSpeed mode" ); // Switch to SingleStep — now it should work clock.set_mode(SimulationMode::SingleStep); assert!( clock.trigger_step(), "trigger_step should return true in SingleStep mode" ); } /// Verify precise tick count tracking in SingleStep mode. #[test] fn test_singlestep_tick_count() { let clock = Arc::new(VirtualClock::new(Duration::from_nanos(100))); clock.set_mode(SimulationMode::SingleStep); let governor = RealTimeGovernor::with_defaults(clock.clone()); let handle = std::thread::spawn(move || { governor.run(); }); // Wait for governor to start std::thread::sleep(Duration::from_millis(50)); assert_eq!(clock.tick_count(), 0, "Initial tick count should be 0"); // Trigger 5 steps for _ in 0..5 { clock.trigger_step(); std::thread::sleep(Duration::from_millis(20)); } // After 5 ticks of 100ns each assert_eq!(clock.now_ns(), 500, "After 5 steps, time should be 500ns"); assert_eq!(clock.tick_count(), 5, "After 5 steps, tick_count should be 5"); clock.stop(); let _ = handle.join(); } } // ============================================================================= // Overclocking Tests // ============================================================================= #[cfg(test)] mod overclocking_tests { use super::*; /// Verify that ROM (500ns access time) fails timing check at 2 MHz. /// /// At 2 MHz, the clock phase is 250ns. The ROM needs 500ns from address /// valid to data ready, but only gets 250ns — a timing violation. #[test] fn test_overclocking_rom_fails_at_2mhz() { let rom_timing = ComponentTiming::intel_4001(); // 2 MHz clock → 500ns cycle → 250ns per phase let phase_time = Duration::from_nanos(250); // ROM has 500ns access time, but phase is only 250ns assert!( rom_timing.check_access_time(phase_time).is_err(), "ROM (500ns access) should FAIL at 2 MHz (250ns phase)" ); // Also verify: ROM does NOT support 2 MHz assert!( !rom_timing.supports_frequency(2_000_000.0), "ROM should NOT support 2 MHz (max is 750 kHz)" ); } /// Verify that RAM (500ns access time) fails timing check at 2 MHz. #[test] fn test_overclocking_ram_fails_at_2mhz() { let ram_timing = ComponentTiming::intel_4002(); // 2 MHz clock → 250ns per phase let phase_time = Duration::from_nanos(250); // RAM has 500ns access time, but phase is only 250ns assert!( ram_timing.check_access_time(phase_time).is_err(), "RAM (500ns access) should FAIL at 2 MHz (250ns phase)" ); assert!( !ram_timing.supports_frequency(2_000_000.0), "RAM should NOT support 2 MHz (max is 750 kHz)" ); } /// Verify that the shift register (200ns access time) STILL WORKS at 2 MHz. /// /// The Intel 4003 has a faster 200ns access time, so at 2 MHz (250ns phase), /// there's still enough time for data to become valid. This models real /// hardware behavior where faster chips continue working when slower ones fail. #[test] fn test_shift_register_works_at_higher_frequency() { let sr_timing = ComponentTiming::intel_4003(); // 2 MHz clock → 250ns per phase let phase_time = Duration::from_nanos(250); // Shift register has 200ns access time, phase is 250ns — PLENTY of time assert!( sr_timing.check_access_time(phase_time).is_ok(), "Shift register (200ns access) should WORK at 2 MHz (250ns phase)" ); // Shift register supports 1 MHz assert!( sr_timing.supports_frequency(1_000_000.0), "Shift register should support 1 MHz" ); // But NOT 2 MHz (max is 1 MHz) assert!( !sr_timing.supports_frequency(2_000_000.0), "Shift register should NOT support 2 MHz (max is 1 MHz)" ); // However, the access time check passes because 250ns > 200ns. // This is the key insight: access_time and max_clock_frequency are // independent constraints. The access time is met (250ns >= 200ns) // even though the frequency exceeds the spec. } /// Verify the overclocking scenario from the timing architecture plan: /// At 2 MHz, ROM and RAM fail while the shift register keeps working. #[test] fn test_overclocking_scenario_comprehensive() { let rom_timing = ComponentTiming::intel_4001(); let ram_timing = ComponentTiming::intel_4002(); let sr_timing = ComponentTiming::intel_4003(); let cpu_timing = ComponentTiming::intel_4004(); // 2 MHz clock: phase = 250ns let phase_time_2mhz = Duration::from_nanos(250); // ROM: 500ns access, 250ns available → FAIL assert!(rom_timing.check_access_time(phase_time_2mhz).is_err()); // RAM: 500ns access, 250ns available → FAIL assert!(ram_timing.check_access_time(phase_time_2mhz).is_err()); // Shift register: 200ns access, 250ns available → OK assert!(sr_timing.check_access_time(phase_time_2mhz).is_ok()); // CPU: 0ns access time → always OK (CPU doesn't have "access time") assert!(cpu_timing.check_access_time(phase_time_2mhz).is_ok()); // Now at standard 750 kHz: phase = 666ns let phase_time_750khz = Duration::from_nanos(666); // All components should work at 750 kHz assert!(rom_timing.check_access_time(phase_time_750khz).is_ok()); assert!(ram_timing.check_access_time(phase_time_750khz).is_ok()); assert!(sr_timing.check_access_time(phase_time_750khz).is_ok()); assert!(cpu_timing.check_access_time(phase_time_750khz).is_ok()); } /// Verify that ComponentTiming::min_clock_period() returns correct values. #[test] fn test_min_clock_period_per_component() { let rom_timing = ComponentTiming::intel_4001(); let ram_timing = ComponentTiming::intel_4002(); let sr_timing = ComponentTiming::intel_4003(); // ROM: max 750 kHz → min period ~1333ns let rom_period = rom_timing.min_clock_period(); assert!( rom_period > Duration::from_nanos(1300) && rom_period < Duration::from_nanos(1400), "ROM min clock period should be ~1333ns, got {:?}", rom_period ); // RAM: max 750 kHz → min period ~1333ns let ram_period = ram_timing.min_clock_period(); assert!( ram_period > Duration::from_nanos(1300) && ram_period < Duration::from_nanos(1400), "RAM min clock period should be ~1333ns, got {:?}", ram_period ); // Shift register: max 1 MHz → min period 1000ns let sr_period = sr_timing.min_clock_period(); assert!( sr_period > Duration::from_nanos(900) && sr_period < Duration::from_nanos(1100), "Shift register min clock period should be ~1000ns, got {:?}", sr_period ); } /// Verify setup time and hold time checks work correctly. #[test] fn test_setup_and_hold_time_checks() { let rom_timing = ComponentTiming::intel_4001(); // Setup time: 100ns assert!(rom_timing.check_setup_time(Duration::from_nanos(150)).is_ok()); assert!(rom_timing.check_setup_time(Duration::from_nanos(100)).is_ok()); assert!(rom_timing.check_setup_time(Duration::from_nanos(50)).is_err()); // Hold time: 50ns assert!(rom_timing.check_hold_time(Duration::from_nanos(100)).is_ok()); assert!(rom_timing.check_hold_time(Duration::from_nanos(50)).is_ok()); assert!(rom_timing.check_hold_time(Duration::from_nanos(25)).is_err()); } /// Verify that timing violations contain meaningful error information. #[test] fn test_timing_violation_error_details() { let rom_timing = ComponentTiming::intel_4001(); let err = rom_timing.check_access_time(Duration::from_nanos(100)); assert!(err.is_err()); let err_msg = format!("{:?}", err.unwrap_err()); assert!( err_msg.contains("Intel 4001 ROM") || err_msg.contains("4001"), "Violation should mention component name: {}", err_msg ); } } // ============================================================================= // Virtual Clock Integration Tests // ============================================================================= #[cfg(test)] mod virtual_clock_integration_tests { use super::*; /// Verify that ConfigurableSystem creates and exposes a VirtualClock. #[test] fn test_configurable_system_creates_virtual_clock() { let factory = SystemFactory::new(); let system = factory.create_from_json("configs/mcs4_basic.json").unwrap(); // The system should have a virtual clock let clock = system.get_virtual_clock(); assert!(Arc::strong_count(clock) >= 1, "VirtualClock Arc should exist"); // Clock should have correct tick size for 750 kHz (333ns per quarter-cycle) let tick_size = clock.tick_size(); assert!( tick_size > Duration::ZERO, "Tick size should be non-zero" ); // For 750 kHz, cycle time is ~1333ns, quarter-cycle is ~333ns assert_eq!( tick_size, Duration::from_nanos(333), "Tick size should be 333ns for 750 kHz" ); } /// Verify that ConfigurableSystem's VirtualClock can be put in MaxSpeed mode. #[test] fn test_configurable_system_maxspeed_mode() { let factory = SystemFactory::new(); let system = factory.create_from_json("configs/mcs4_basic.json").unwrap(); let clock = system.get_virtual_clock(); clock.set_mode(SimulationMode::MaxSpeed); assert_eq!(clock.mode(), SimulationMode::MaxSpeed); } /// Verify that clock.stop() causes the governor thread to exit cleanly. #[test] fn test_virtual_clock_stop_exits_governor() { let clock = Arc::new(VirtualClock::new(Duration::from_nanos(333))); let governor = RealTimeGovernor::with_defaults(clock.clone()); let handle = std::thread::spawn(move || { governor.run(); }); // Let the governor start and advance time std::thread::sleep(Duration::from_millis(50)); assert!(clock.now_ns() > 0, "Clock should have advanced"); // Stop the clock clock.stop(); // Governor thread should exit cleanly let join_result = handle.join(); assert!( join_result.is_ok(), "Governor thread should exit cleanly after stop" ); } /// Verify that clock.stop() causes wait_until to return None. #[test] fn test_stop_unblocks_wait_until() { let clock = Arc::new(VirtualClock::new(Duration::from_nanos(333))); let governor = RealTimeGovernor::with_defaults(clock.clone()); let clock_clone = Arc::clone(&clock); let handle = std::thread::spawn(move || { governor.run(); }); // Let the governor start std::thread::sleep(Duration::from_millis(50)); // Now stop the clock clock.stop(); // wait_until for a far-future time should return None immediately let result = clock_clone.wait_until(Duration::from_secs(100)); assert!( result.is_none(), "wait_until should return None after clock is stopped" ); let _ = handle.join(); } /// Verify pause/resume behavior: time stops advancing when paused. #[test] fn test_pause_resume_behavior() { let clock = Arc::new(VirtualClock::new(Duration::from_nanos(333))); let governor = RealTimeGovernor::with_defaults(clock.clone()); let handle = std::thread::spawn(move || { governor.run(); }); // Let it run std::thread::sleep(Duration::from_millis(50)); let time_before_pause = clock.now_ns(); assert!(time_before_pause > 0); // Pause the clock clock.pause(); assert!(clock.is_paused()); // Wait a short wall-clock time, then check virtual time hasn't advanced std::thread::sleep(Duration::from_millis(50)); let time_after_pause = clock.now_ns(); // Virtual time should not have advanced significantly while paused // (may have advanced by 1-2 ticks due to race, but not more) let diff = time_after_pause.saturating_sub(time_before_pause); assert!( diff < 1000, "Virtual time should not advance significantly while paused. \ Before: {}, After: {}, Diff: {}", time_before_pause, time_after_pause, diff ); // Resume the clock clock.resume(); assert!(!clock.is_paused()); // Wait for time to advance again std::thread::sleep(Duration::from_millis(50)); let time_after_resume = clock.now_ns(); assert!( time_after_resume > time_after_pause, "Virtual time should advance after resume" ); clock.stop(); let _ = handle.join(); } /// Verify that the VirtualClock reset works correctly. #[test] fn test_virtual_clock_reset() { let clock = Arc::new(VirtualClock::new(Duration::from_nanos(333))); let governor = RealTimeGovernor::with_defaults(clock.clone()); let handle = std::thread::spawn(move || { governor.run(); }); // Advance time std::thread::sleep(Duration::from_millis(50)); assert!(clock.now_ns() > 0); // Stop and reset clock.stop(); let _ = handle.join(); // Reset time to zero clock.reset(); // Verify time is back to zero assert_eq!(clock.now(), Duration::ZERO); assert_eq!(clock.tick_count(), 0); } /// Verify speed multiplier can be changed dynamically. #[test] fn test_speed_multiplier_change() { let clock = Arc::new(VirtualClock::new(Duration::from_nanos(333))); // Default speed is 1.0x assert!((clock.speed_multiplier() - 1.0).abs() < 0.001); // Set to 0.5x clock.set_speed_multiplier(0.5); assert!((clock.speed_multiplier() - 0.5).abs() < 0.001); // Set to 10.0x clock.set_speed_multiplier(10.0); assert!((clock.speed_multiplier() - 10.0).abs() < 0.01); // Set to 0.001x (very slow) clock.set_speed_multiplier(0.001); assert!((clock.speed_multiplier() - 0.001).abs() < 0.0001); } /// Verify that has_reached works correctly for non-blocking time checks. #[test] fn test_has_reached_nonblocking() { let clock = Arc::new(VirtualClock::new(Duration::from_nanos(100))); let governor = RealTimeGovernor::with_defaults(clock.clone()); let handle = std::thread::spawn(move || { governor.run(); }); // Initially, time is 0 assert!(clock.has_reached(Duration::ZERO)); assert!(!clock.has_reached(Duration::from_nanos(1))); // Wait for time to advance std::thread::sleep(Duration::from_millis(50)); // Now it should have reached various targets assert!(clock.has_reached(Duration::from_nanos(100))); assert!(clock.has_reached(Duration::from_nanos(1000))); assert!(clock.has_reached(Duration::from_micros(5))); clock.stop(); let _ = handle.join(); } /// Verify tick_size can be changed dynamically. #[test] fn test_tick_size_change() { let clock = Arc::new(VirtualClock::new(Duration::from_nanos(333))); assert_eq!(clock.tick_size(), Duration::from_nanos(333)); clock.set_tick_size(Duration::from_nanos(100)); assert_eq!(clock.tick_size(), Duration::from_nanos(100)); clock.set_tick_size(Duration::from_nanos(500)); assert_eq!(clock.tick_size(), Duration::from_nanos(500)); } } // ============================================================================= // Component Timing with Virtual Time Tests // ============================================================================= #[cfg(test)] mod component_virtual_time_tests { use super::*; /// Verify that Intel4001 implements the timing() method correctly. #[test] fn test_intel4001_timing_profile() { let rom = Intel4001::new("TestROM".to_string()); let timing = rom.timing(); assert_eq!(timing.name, "Intel 4001 ROM"); assert_eq!(timing.access_time, Duration::from_nanos(500)); assert_eq!(timing.propagation_delay, Duration::from_nanos(200)); assert_eq!(timing.setup_time, Duration::from_nanos(100)); assert_eq!(timing.hold_time, Duration::from_nanos(50)); assert!((timing.max_clock_frequency - 750_000.0).abs() < 1.0); } /// Verify that Intel4002 implements the timing() method correctly. #[test] fn test_intel4002_timing_profile() { let ram = Intel4002::new("TestRAM".to_string()); let timing = ram.timing(); assert_eq!(timing.name, "Intel 4002 RAM"); assert_eq!(timing.access_time, Duration::from_nanos(500)); assert_eq!(timing.propagation_delay, Duration::from_nanos(200)); } /// Verify that Intel4003 implements the timing() method correctly. #[test] fn test_intel4003_timing_profile() { let sr = Intel4003::new("TestSR".to_string()); let timing = sr.timing(); assert_eq!(timing.name, "Intel 4003 Shift Register"); assert_eq!(timing.access_time, Duration::from_nanos(200)); assert_eq!(timing.propagation_delay, Duration::from_nanos(100)); assert!((timing.max_clock_frequency - 1_000_000.0).abs() < 1.0); } /// Verify that update_at_time() works correctly for ROM component. /// This tests the virtual-time-aware update path. #[test] fn test_rom_update_at_time() { let mut rom = Intel4001::new_with_access_time("TestROM".to_string(), 1); rom.load_rom_data(vec![0x42], 0).unwrap(); rom.set_running(true); // Call update_at_time at various virtual times // This should work without panicking rom.update_at_time(Duration::from_nanos(0)); rom.update_at_time(Duration::from_nanos(333)); rom.update_at_time(Duration::from_nanos(666)); rom.update_at_time(Duration::from_nanos(999)); // Component should still function correctly assert_eq!(rom.read_rom(0x00).unwrap(), 0x42); } /// Verify that update_at_time() works correctly for RAM component. #[test] fn test_ram_update_at_time() { let mut ram = Intel4002::new("TestRAM".to_string()); ram.set_running(true); // Call update_at_time at various virtual times rom_update_at_time_helper(&mut ram); } fn rom_update_at_time_helper(ram: &mut Intel4002) { ram.update_at_time(Duration::from_nanos(0)); ram.update_at_time(Duration::from_nanos(333)); ram.update_at_time(Duration::from_nanos(666)); } /// Verify that update_at_time() works correctly for shift register component. #[test] fn test_sr_update_at_time() { let mut sr = Intel4003::new("TestSR".to_string()); sr.set_running(true); // Call update_at_time at various virtual times sr.update_at_time(Duration::from_nanos(0)); sr.update_at_time(Duration::from_nanos(333)); sr.update_at_time(Duration::from_nanos(666)); // Should not panic } /// Test that address latch time tracking works with virtual time. #[test] fn test_address_latch_time_virtual_time() { let mut rom = Intel4001::new_with_access_time("TestROM".to_string(), 500); // Set address latch time to a specific virtual time let latch_time = Duration::from_nanos(1000); rom.set_address_latch_time(Some(latch_time)); assert_eq!(rom.get_address_latch_time(), Some(latch_time)); // Clear it rom.set_address_latch_time(None); assert_eq!(rom.get_address_latch_time(), None); } /// Verify that the timing state machine works correctly with virtual time. #[test] fn test_timing_state_machine_with_virtual_time() { let mut rom = Intel4001::new_with_access_time("TestROM".to_string(), 500); // Start in Idle assert_eq!(rom.get_timing_state(), TimingState::Idle); // Simulate a complete memory access cycle with virtual time let t0 = Duration::ZERO; // Start let _t1 = Duration::from_nanos(333); // Address phase let _t2 = Duration::from_nanos(666); // Wait latency let t3 = Duration::from_nanos(999); // Drive data // T0: Idle rom.update_at_time(t0); assert_eq!(rom.get_timing_state(), TimingState::Idle); // T1: Address latched let t1 = Duration::from_nanos(333); rom.set_address_latch_time(Some(t1)); rom.set_timing_state(TimingState::AddressPhase); assert_eq!(rom.get_timing_state(), TimingState::AddressPhase); // T2: Waiting for access time rom.set_timing_state(TimingState::WaitLatency); assert_eq!(rom.get_timing_state(), TimingState::WaitLatency); // T3: Access time (666ns) > 500ns → data ready let elapsed = t3.saturating_sub(t1); let timing = ComponentTiming::intel_4001(); assert!( timing.check_access_time(elapsed).is_ok(), "After 666ns, access time (500ns) should be met" ); rom.set_timing_state(TimingState::DriveData); assert_eq!(rom.get_timing_state(), TimingState::DriveData); // Return to idle rom.set_timing_state(TimingState::Idle); assert_eq!(rom.get_timing_state(), TimingState::Idle); } }