//! Mock-based tests for Intel 400x data bus and pin operations //! //! These tests use the mock implementations to verify the behavior of //! the intel_400x traits when interacting with hardware components. use rusty_emu::components::common::intel_400x::*; use rusty_emu::pin::PinValue; use std::time::Duration; #[cfg(test)] mod data_bus_tests { use super::*; #[test] fn test_data_bus_read_write_operations() { let mut scenario = MockScenario::new("TestDataBus"); // Test writing data to bus scenario.set_data_bus_value(0x05); // 0101 in binary assert_eq!(scenario.get_data_bus_value(), 0x05); // Verify individual bits assert_eq!(scenario.component.get_pin_value("D0"), Some(PinValue::High)); // LSB assert_eq!(scenario.component.get_pin_value("D1"), Some(PinValue::Low)); assert_eq!(scenario.component.get_pin_value("D2"), Some(PinValue::High)); assert_eq!(scenario.component.get_pin_value("D3"), Some(PinValue::Low)); // MSB // Test writing different value scenario.set_data_bus_value(0x0A); // 1010 in binary assert_eq!(scenario.get_data_bus_value(), 0x0A); assert_eq!(scenario.component.get_pin_value("D0"), Some(PinValue::Low)); assert_eq!(scenario.component.get_pin_value("D1"), Some(PinValue::High)); assert_eq!(scenario.component.get_pin_value("D2"), Some(PinValue::Low)); assert_eq!(scenario.component.get_pin_value("D3"), Some(PinValue::High)); } #[test] fn test_data_bus_all_values() { let mut scenario = MockScenario::new("TestAllDataValues"); // Test all possible 4-bit values for value in 0u8..16 { scenario.set_data_bus_value(value); let read_value = scenario.get_data_bus_value(); assert_eq!(value, read_value, "Failed for value 0x{:02X}", value); } } #[test] fn test_pin_operation_counting() { let scenario = MockScenario::new("TestPinCounting"); // Set some pin values and check that operations are counted scenario.component.set_pin_value("D0", PinValue::High); scenario.component.set_pin_value("D1", PinValue::Low); // Note: Our current mock doesn't fully implement the counting // This test demonstrates the structure for when we enhance the mock assert_eq!(scenario.component.get_pin_value("D0"), Some(PinValue::High)); assert_eq!(scenario.component.get_pin_value("D1"), Some(PinValue::Low)); } } #[cfg(test)] mod clock_handling_tests { use super::*; #[test] fn test_clock_pin_reading() { let scenario = MockScenario::new("TestClockReading"); // Test reading clock pins in different states scenario .component .set_clock_values(PinValue::Low, PinValue::Low); // In a real implementation, this would test the actual trait methods // For now, we verify the mock setup works correctly assert_eq!( scenario.component.get_pin_value("PHI1"), Some(PinValue::Low) ); assert_eq!( scenario.component.get_pin_value("PHI2"), Some(PinValue::Low) ); } #[test] fn test_clock_edge_scenarios() { let mut scenario = MockScenario::new("TestClockEdges"); // Test PHI1 rising edge scenario.set_phi1_rising_edge(); assert_eq!( scenario.component.get_pin_value("PHI1"), Some(PinValue::High) ); assert_eq!( scenario.component.get_pin_value("PHI2"), Some(PinValue::Low) ); // Test PHI1 falling edge scenario.set_phi1_falling_edge(); assert_eq!( scenario.component.get_pin_value("PHI1"), Some(PinValue::Low) ); assert_eq!( scenario.component.get_pin_value("PHI2"), Some(PinValue::Low) ); // Test PHI2 rising edge scenario.set_phi2_rising_edge(); assert_eq!( scenario.component.get_pin_value("PHI1"), Some(PinValue::Low) ); assert_eq!( scenario.component.get_pin_value("PHI2"), Some(PinValue::High) ); // Test PHI2 falling edge scenario.set_phi2_falling_edge(); assert_eq!( scenario.component.get_pin_value("PHI1"), Some(PinValue::Low) ); assert_eq!( scenario.component.get_pin_value("PHI2"), Some(PinValue::Low) ); } #[test] fn test_clock_state_transitions() { let mut scenario = MockScenario::new("TestClockTransitions"); // Simulate a clock cycle: Low -> High -> Low scenario.set_clock_low(); assert_eq!( scenario.component.get_pin_value("PHI1"), Some(PinValue::Low) ); assert_eq!( scenario.component.get_pin_value("PHI2"), Some(PinValue::Low) ); scenario.set_clock_high(); assert_eq!( scenario.component.get_pin_value("PHI1"), Some(PinValue::High) ); assert_eq!( scenario.component.get_pin_value("PHI2"), Some(PinValue::High) ); scenario.set_clock_low(); assert_eq!( scenario.component.get_pin_value("PHI1"), Some(PinValue::Low) ); assert_eq!( scenario.component.get_pin_value("PHI2"), Some(PinValue::Low) ); } } #[cfg(test)] mod control_pin_tests { use super::*; #[test] fn test_control_pin_reading() { let scenario = MockScenario::new("TestControlPins"); // Test SYNC pin scenario.set_pin_value("SYNC", PinValue::High); assert_eq!( scenario.component.get_pin_value("SYNC"), Some(PinValue::High) ); scenario.set_pin_value("SYNC", PinValue::Low); assert_eq!( scenario.component.get_pin_value("SYNC"), Some(PinValue::Low) ); // Test CM-ROM pin scenario.component.set_pin_value("CM", PinValue::High); assert_eq!(scenario.component.get_pin_value("CM"), Some(PinValue::High)); scenario.component.set_pin_value("CM", PinValue::Low); assert_eq!(scenario.component.get_pin_value("CM"), Some(PinValue::Low)); // Test RESET pin scenario.component.set_pin_value("RESET", PinValue::High); assert_eq!( scenario.component.get_pin_value("RESET"), Some(PinValue::High) ); scenario.component.set_pin_value("RESET", PinValue::Low); assert_eq!( scenario.component.get_pin_value("RESET"), Some(PinValue::Low) ); } #[test] fn test_reset_functionality() { let mut scenario = MockScenario::new("TestReset"); // Set up some state scenario.component.set_timing_state(TimingState::DriveData); scenario.component.set_full_address_ready(true); scenario.component.set_address_high_nibble(Some(0x12)); scenario.component.set_address_low_nibble(Some(0x34)); scenario.set_data_bus_value(0x0F); // Verify state is set assert_eq!( scenario.component.get_timing_state(), TimingState::DriveData ); assert_eq!(scenario.component.get_full_address_ready(), true); assert_eq!(scenario.component.get_address_high_nibble(), Some(0x12)); assert_eq!(scenario.component.get_address_low_nibble(), Some(0x34)); assert_eq!(scenario.get_data_bus_value(), 0x0F); // Perform reset scenario.component.perform_reset(); // Verify reset behavior assert_eq!(scenario.component.get_timing_state(), TimingState::Idle); assert_eq!(scenario.component.get_full_address_ready(), false); assert_eq!(scenario.component.get_address_high_nibble(), None); assert_eq!(scenario.component.get_address_low_nibble(), None); // Data bus should be tri-stated (HighZ) for i in 0..4 { let pin_name = format!("D{}", i); assert_eq!( scenario.component.get_pin_value(&pin_name), Some(PinValue::HighZ) ); } } } #[cfg(test)] mod timing_state_tests { use super::*; #[test] fn test_timing_state_machine_transitions() { let mut scenario = MockScenario::new("TestTimingStateMachine"); // Start in idle state assert_eq!(scenario.component.get_timing_state(), TimingState::Idle); assert!(scenario.component.get_timing_state().is_idle()); assert!(!scenario.component.get_timing_state().is_address_phase()); // Transition to address phase scenario .component .set_timing_state(TimingState::AddressPhase); assert_eq!( scenario.component.get_timing_state(), TimingState::AddressPhase ); assert!(!scenario.component.get_timing_state().is_idle()); assert!(scenario.component.get_timing_state().is_address_phase()); // Transition to wait latency scenario .component .set_timing_state(TimingState::WaitLatency); assert_eq!( scenario.component.get_timing_state(), TimingState::WaitLatency ); assert!(scenario.component.get_timing_state().is_waiting_latency()); // Transition to drive data scenario.component.set_timing_state(TimingState::DriveData); assert_eq!( scenario.component.get_timing_state(), TimingState::DriveData ); assert!(scenario.component.get_timing_state().is_driving_data()); } #[test] fn test_address_latch_timing() { let mut scenario = MockScenario::new("TestAddressLatchTiming"); // Set up address latching scenario.component.set_address_high_nibble(Some(0x12)); scenario.component.set_address_low_nibble(Some(0x34)); scenario.component.set_full_address_ready(true); // Record latch time let latch_time = scenario.time_provider.now(); scenario.component.set_address_latch_time(Some(latch_time)); // Verify state assert_eq!(scenario.component.get_address_high_nibble(), Some(0x12)); assert_eq!(scenario.component.get_address_low_nibble(), Some(0x34)); assert_eq!(scenario.component.get_full_address_ready(), true); assert_eq!( scenario.component.get_address_latch_time(), Some(latch_time) ); // Test clearing the latch scenario.component.set_address_high_nibble(None); scenario.component.set_address_low_nibble(None); scenario.component.set_full_address_ready(false); scenario.component.set_address_latch_time(None); assert_eq!(scenario.component.get_address_high_nibble(), None); assert_eq!(scenario.component.get_address_low_nibble(), None); assert_eq!(scenario.component.get_full_address_ready(), false); assert_eq!(scenario.component.get_address_latch_time(), None); } #[test] fn test_access_time_configuration() { let scenario = MockScenario::new("TestAccessTime"); // Test default access time assert_eq!( scenario.component.get_access_time(), TimingConstants::DEFAULT_ACCESS_TIME ); // In a real implementation, we would test changing the access time // For now, we verify the getter works let access_time = scenario.component.get_access_time(); assert!(access_time > Duration::from_nanos(0)); } } #[cfg(test)] mod integration_scenarios { use super::*; #[test] fn test_complete_memory_operation_cycle() { let mut scenario = MockScenario::new("TestMemoryCycle"); // Initial state assert_eq!(scenario.component.get_timing_state(), TimingState::Idle); // Phase 1: Address phase - high nibble scenario .component .set_timing_state(TimingState::AddressPhase); scenario.component.set_address_high_nibble(Some(0x12)); assert_eq!( scenario.component.get_timing_state(), TimingState::AddressPhase ); assert_eq!(scenario.component.get_address_high_nibble(), Some(0x12)); // Phase 2: Address phase - low nibble scenario.component.set_address_low_nibble(Some(0x34)); scenario.component.set_full_address_ready(true); let latch_time = scenario.time_provider.now(); scenario.component.set_address_latch_time(Some(latch_time)); // Verify address assembly assert_eq!(scenario.component.get_address_high_nibble(), Some(0x12)); assert_eq!(scenario.component.get_address_low_nibble(), Some(0x34)); assert_eq!(scenario.component.get_full_address_ready(), true); // Phase 3: Wait for latency scenario .component .set_timing_state(TimingState::WaitLatency); assert_eq!( scenario.component.get_timing_state(), TimingState::WaitLatency ); // Advance time past access time scenario.advance_time(scenario.component.get_access_time() + Duration::from_nanos(10)); // Phase 4: Drive data scenario.component.set_timing_state(TimingState::DriveData); scenario.set_data_bus_value(0x0F); // Drive some data assert_eq!( scenario.component.get_timing_state(), TimingState::DriveData ); assert_eq!(scenario.get_data_bus_value(), 0x0F); // Cycle complete - return to idle scenario.component.set_timing_state(TimingState::Idle); assert_eq!(scenario.component.get_timing_state(), TimingState::Idle); } #[test] fn test_bus_contention_avoidance() { let mut scenario = MockScenario::new("TestBusContention"); // Set up a scenario where bus contention could occur scenario.set_data_bus_value(0x0F); // In a real implementation, we would test tri-stating the bus // to avoid contention with other devices for i in 0..4 { let pin_name = format!("D{}", i); scenario.component.set_pin_value(&pin_name, PinValue::HighZ); assert_eq!( scenario.component.get_pin_value(&pin_name), Some(PinValue::HighZ) ); } } #[test] fn test_multiple_address_cycles() { let mut scenario = MockScenario::new("TestMultipleAddresses"); // First address cycle scenario.component.set_address_high_nibble(Some(0x12)); scenario.component.set_address_low_nibble(Some(0x34)); scenario.component.set_full_address_ready(true); assert_eq!(scenario.component.get_address_high_nibble(), Some(0x12)); assert_eq!(scenario.component.get_address_low_nibble(), Some(0x34)); // Clear for next cycle scenario.component.set_address_high_nibble(None); scenario.component.set_address_low_nibble(None); scenario.component.set_full_address_ready(false); // Second address cycle scenario.component.set_address_high_nibble(Some(0x56)); scenario.component.set_address_low_nibble(Some(0x78)); scenario.component.set_full_address_ready(true); assert_eq!(scenario.component.get_address_high_nibble(), Some(0x56)); assert_eq!(scenario.component.get_address_low_nibble(), Some(0x78)); assert_eq!(scenario.component.get_full_address_ready(), true); // First address should be cleared assert_eq!(scenario.component.get_address_high_nibble(), Some(0x56)); assert_eq!(scenario.component.get_address_low_nibble(), Some(0x78)); } } #[cfg(test)] mod error_handling_tests { use super::*; #[test] fn test_missing_pin_handling() { let scenario = MockScenario::new("TestMissingPins"); // Test accessing non-existent pins assert_eq!(scenario.component.get_pin_value("NONEXISTENT"), None); // Standard pins should exist assert_eq!( scenario.component.get_pin_value("D0"), Some(PinValue::HighZ) ); assert_eq!( scenario.component.get_pin_value("PHI1"), Some(PinValue::Low) ); assert_eq!( scenario.component.get_pin_value("SYNC"), Some(PinValue::Low) ); } #[test] fn test_invalid_pin_operations() { let scenario = MockScenario::new("TestInvalidOperations"); // Test setting pin to HighZ (tri-state) scenario.component.set_pin_value("D0", PinValue::HighZ); assert_eq!( scenario.component.get_pin_value("D0"), Some(PinValue::HighZ) ); // Test that HighZ is handled correctly in bus operations // (This would be more relevant in actual hardware interface tests) let bus_value = scenario.get_data_bus_value(); // The exact behavior depends on how HighZ pins are interpreted // This test documents the expected structure } }