508 lines
20 KiB
Rust
508 lines
20 KiB
Rust
//! Integration tests for Intel 400x common functionality
|
|
//!
|
|
//! These tests verify that concrete implementations correctly use
|
|
//! the intel_400x traits and that the common functionality works
|
|
//! as expected in real usage scenarios.
|
|
|
|
use rusty_emu::component::Component;
|
|
use rusty_emu::components::common::intel_400x::*;
|
|
use rusty_emu::components::memory::intel_4001::Intel4001;
|
|
use rusty_emu::pin::PinValue;
|
|
use std::time::Duration;
|
|
|
|
#[cfg(test)]
|
|
mod intel_4001_integration_tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_intel4001_uses_common_traits() {
|
|
let rom = Intel4001::new("ROM_4001".to_string());
|
|
|
|
// Verify that Intel4001 implements all the common traits
|
|
// These should compile without errors if the trait implementations are correct
|
|
|
|
// Test Intel400xClockHandling trait
|
|
let _: &dyn Intel400xClockHandling = &rom;
|
|
|
|
// Test Intel400xDataBus trait
|
|
let _: &dyn Intel400xDataBus = &rom;
|
|
|
|
// Test Intel400xAddressHandling trait
|
|
let _: &dyn Intel400xAddressHandling = &rom;
|
|
|
|
// Test Intel400xControlPins trait
|
|
let _: &dyn Intel400xControlPins = &rom;
|
|
|
|
// Test Intel400xResetHandling trait
|
|
let _: &dyn Intel400xResetHandling = &rom;
|
|
|
|
// Test Intel400xTimingState trait
|
|
let _: &dyn Intel400xTimingState = &rom;
|
|
|
|
// Verify the component has the expected name
|
|
assert_eq!(rom.name(), "ROM_4001");
|
|
}
|
|
|
|
#[test]
|
|
fn test_intel4001_timing_state_integration() {
|
|
let mut rom = Intel4001::new_with_access_time("ROM_4001".to_string(), 1);
|
|
|
|
// Test that timing state works correctly with the trait
|
|
let initial_state: TimingState = rom.get_timing_state();
|
|
assert_eq!(initial_state, TimingState::Idle);
|
|
|
|
// Test state changes through the trait
|
|
rom.set_timing_state(TimingState::AddressPhase);
|
|
let address_state: TimingState = rom.get_timing_state();
|
|
assert_eq!(address_state, TimingState::AddressPhase);
|
|
|
|
rom.set_timing_state(TimingState::WaitLatency);
|
|
let wait_state: TimingState = rom.get_timing_state();
|
|
assert_eq!(wait_state, TimingState::WaitLatency);
|
|
|
|
rom.set_timing_state(TimingState::DriveData);
|
|
let drive_state: TimingState = rom.get_timing_state();
|
|
assert_eq!(drive_state, TimingState::DriveData);
|
|
}
|
|
|
|
#[test]
|
|
fn test_intel4001_address_handling_integration() {
|
|
let mut rom = Intel4001::new_with_access_time("ROM_4001".to_string(), 1);
|
|
|
|
// Test address handling through the trait
|
|
rom.set_address_high_nibble(Some(0x12));
|
|
rom.set_address_low_nibble(Some(0x34));
|
|
rom.set_full_address_ready(true);
|
|
|
|
// Verify the values are accessible through the trait
|
|
assert_eq!(rom.get_address_high_nibble(), Some(0x12));
|
|
assert_eq!(rom.get_address_low_nibble(), Some(0x34));
|
|
assert_eq!(rom.get_full_address_ready(), true);
|
|
|
|
// Test access time configuration
|
|
assert_eq!(rom.get_access_time(), 1);
|
|
|
|
// Test address latch time handling
|
|
let test_time = std::time::Instant::now();
|
|
rom.set_address_latch_time(Some(test_time));
|
|
assert_eq!(rom.get_address_latch_time(), Some(test_time));
|
|
}
|
|
|
|
#[test]
|
|
fn test_intel4001_reset_integration() {
|
|
let mut rom = Intel4001::new_with_access_time("ROM_4001".to_string(), 1);
|
|
|
|
// Set up some state
|
|
rom.set_timing_state(TimingState::DriveData);
|
|
rom.set_address_high_nibble(Some(0x12));
|
|
rom.set_address_low_nibble(Some(0x34));
|
|
rom.set_full_address_ready(true);
|
|
|
|
// Verify state is set
|
|
assert_eq!(rom.get_timing_state(), TimingState::DriveData);
|
|
assert_eq!(rom.get_address_high_nibble(), Some(0x12));
|
|
assert_eq!(rom.get_address_low_nibble(), Some(0x34));
|
|
assert_eq!(rom.get_full_address_ready(), true);
|
|
|
|
// Perform reset through the trait
|
|
rom.perform_reset();
|
|
|
|
// Verify reset behavior
|
|
assert_eq!(rom.get_timing_state(), TimingState::Idle);
|
|
assert_eq!(rom.get_address_high_nibble(), None);
|
|
assert_eq!(rom.get_address_low_nibble(), None);
|
|
assert_eq!(rom.get_full_address_ready(), false);
|
|
}
|
|
|
|
#[test]
|
|
fn test_intel4001_memory_state_conversions() {
|
|
let rom = Intel4001::new_with_access_time("ROM_4001".to_string(), 1);
|
|
|
|
// Test that the ROM's internal MemoryState converts correctly to TimingState
|
|
let internal_state = MemoryState::Idle;
|
|
let timing_state: TimingState = internal_state.into();
|
|
assert_eq!(timing_state, TimingState::Idle);
|
|
|
|
let internal_state = MemoryState::AddressPhase;
|
|
let timing_state: TimingState = internal_state.into();
|
|
assert_eq!(timing_state, TimingState::AddressPhase);
|
|
|
|
let internal_state = MemoryState::WaitLatency;
|
|
let timing_state: TimingState = internal_state.into();
|
|
assert_eq!(timing_state, TimingState::WaitLatency);
|
|
|
|
let internal_state = MemoryState::DriveData;
|
|
let timing_state: TimingState = internal_state.into();
|
|
assert_eq!(timing_state, TimingState::DriveData);
|
|
}
|
|
|
|
#[test]
|
|
fn test_intel4001_with_mock_scenario() {
|
|
// Note: MockScenario would be used here for more complex testing
|
|
// For this integration test, we focus on the real component behavior
|
|
let mut rom = Intel4001::new_with_access_time("ROM_4001".to_string(), 1);
|
|
|
|
// Load test data
|
|
let test_data = vec![0x12, 0x34];
|
|
rom.load_rom_data(test_data, 0).unwrap();
|
|
|
|
// Test that the real component works correctly with the common traits
|
|
// This demonstrates the integration between the common functionality and concrete implementations
|
|
|
|
// Set up memory read operation
|
|
let sync_pin = rom.get_pin("SYNC").unwrap();
|
|
let cm_pin = rom.get_pin("CM").unwrap();
|
|
let ci_pin = rom.get_pin("CI").unwrap();
|
|
|
|
{
|
|
let mut sync_guard = sync_pin.lock().unwrap();
|
|
let mut cm_guard = cm_pin.lock().unwrap();
|
|
let mut ci_guard = ci_pin.lock().unwrap();
|
|
sync_guard.set_driver(Some("TEST".to_string()), PinValue::High);
|
|
cm_guard.set_driver(Some("TEST".to_string()), PinValue::High);
|
|
ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
|
|
}
|
|
|
|
// Verify the ROM responds to the pin states
|
|
rom.update();
|
|
assert_eq!(rom.get_timing_state(), TimingState::AddressPhase);
|
|
}
|
|
|
|
#[test]
|
|
fn test_common_functionality_with_real_component() {
|
|
let mut rom = Intel4001::new_with_access_time("ROM_4001".to_string(), 1);
|
|
|
|
// Test that common functionality works with the real component
|
|
let test_data = vec![0xAB, 0xCD];
|
|
rom.load_rom_data(test_data, 0).unwrap();
|
|
|
|
// Test direct memory access (bypassing the pin interface)
|
|
assert_eq!(rom.read_rom(0x00).unwrap(), 0xAB);
|
|
assert_eq!(rom.read_rom(0x01).unwrap(), 0xCD);
|
|
|
|
// Test that the component maintains its state correctly
|
|
assert_eq!(rom.get_rom_size(), 256);
|
|
assert_eq!(rom.get_access_time(), 1);
|
|
}
|
|
|
|
#[test]
|
|
fn test_timing_constants_usage() {
|
|
// Test that timing constants are used appropriately
|
|
let fast_rom = Intel4001::new_with_access_time(
|
|
"FAST_ROM".to_string(),
|
|
TimingConstants::FAST_ACCESS_TIME.as_nanos() as u64,
|
|
);
|
|
let default_rom = Intel4001::new_with_access_time(
|
|
"DEFAULT_ROM".to_string(),
|
|
TimingConstants::DEFAULT_ACCESS_TIME.as_nanos() as u64,
|
|
);
|
|
|
|
assert_eq!(
|
|
fast_rom.get_access_time(),
|
|
TimingConstants::FAST_ACCESS_TIME.as_nanos() as u64
|
|
);
|
|
assert_eq!(
|
|
default_rom.get_access_time(),
|
|
TimingConstants::DEFAULT_ACCESS_TIME.as_nanos() as u64
|
|
);
|
|
|
|
assert!(TimingConstants::FAST_ACCESS_TIME < TimingConstants::DEFAULT_ACCESS_TIME);
|
|
assert!(TimingConstants::ADDRESS_SETUP > Duration::from_nanos(0));
|
|
assert!(TimingConstants::DATA_VALID > Duration::from_nanos(0));
|
|
}
|
|
|
|
#[test]
|
|
fn test_state_machine_integration() {
|
|
let mut rom = Intel4001::new_with_access_time("ROM_4001".to_string(), 1);
|
|
|
|
// Test state machine progression through the trait interface
|
|
assert_eq!(rom.get_timing_state(), TimingState::Idle);
|
|
|
|
// Progress through states
|
|
rom.set_timing_state(TimingState::AddressPhase);
|
|
assert_eq!(rom.get_timing_state(), TimingState::AddressPhase);
|
|
|
|
rom.set_timing_state(TimingState::WaitLatency);
|
|
assert_eq!(rom.get_timing_state(), TimingState::WaitLatency);
|
|
|
|
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);
|
|
}
|
|
|
|
#[test]
|
|
fn test_address_latching_integration() {
|
|
let mut rom = Intel4001::new_with_access_time("ROM_4001".to_string(), 1);
|
|
|
|
// Test address latching through the trait
|
|
rom.set_address_high_nibble(Some(0x0F));
|
|
rom.set_address_low_nibble(Some(0x23));
|
|
rom.set_full_address_ready(true);
|
|
|
|
// Verify the address was assembled correctly
|
|
assert_eq!(rom.get_address_high_nibble(), Some(0x0F));
|
|
assert_eq!(rom.get_address_low_nibble(), Some(0x23));
|
|
assert_eq!(rom.get_full_address_ready(), true);
|
|
|
|
// Test clearing the address
|
|
rom.set_address_high_nibble(None);
|
|
rom.set_address_low_nibble(None);
|
|
rom.set_full_address_ready(false);
|
|
|
|
assert_eq!(rom.get_address_high_nibble(), None);
|
|
assert_eq!(rom.get_address_low_nibble(), None);
|
|
assert_eq!(rom.get_full_address_ready(), false);
|
|
}
|
|
|
|
#[test]
|
|
fn test_multiple_component_interaction() {
|
|
// Test how multiple components using the same traits interact
|
|
let rom1 = Intel4001::new_with_access_time("ROM1".to_string(), 1);
|
|
let rom2 = Intel4001::new_with_access_time("ROM2".to_string(), 2);
|
|
|
|
// Both should implement the same traits
|
|
let _: &dyn Intel400xClockHandling = &rom1;
|
|
let _: &dyn Intel400xClockHandling = &rom2;
|
|
|
|
let _: &dyn Intel400xDataBus = &rom1;
|
|
let _: &dyn Intel400xDataBus = &rom2;
|
|
|
|
// They should have different access times
|
|
assert_eq!(rom1.get_access_time(), 1);
|
|
assert_eq!(rom2.get_access_time(), 2);
|
|
|
|
// Both should start in the same initial state
|
|
assert_eq!(rom1.get_timing_state(), TimingState::Idle);
|
|
assert_eq!(rom2.get_timing_state(), TimingState::Idle);
|
|
}
|
|
|
|
#[test]
|
|
fn test_error_handling_integration() {
|
|
let mut rom = Intel4001::new("ROM_4001".to_string());
|
|
|
|
// Test reading from valid ROM address
|
|
assert_eq!(rom.read_rom(0xFF), Some(0x00)); // Should return default value
|
|
|
|
// Test loading data with invalid offset
|
|
let test_data = vec![0x12, 0x34];
|
|
assert!(rom.load_rom_data(test_data, 255).is_ok()); // Should work
|
|
assert!(rom.load_rom_data(vec![0x12], 256).is_err()); // Out of bounds
|
|
}
|
|
|
|
#[test]
|
|
fn test_performance_characteristics() {
|
|
// Test that the common functionality maintains expected performance characteristics
|
|
let mut rom = Intel4001::new_with_access_time("ROM_4001".to_string(), 1);
|
|
|
|
// Test that access time is correctly configured
|
|
assert_eq!(rom.get_access_time(), 1);
|
|
|
|
// Test that the component can handle rapid state changes
|
|
for i in 0..100 {
|
|
rom.set_timing_state(TimingState::Idle);
|
|
rom.set_timing_state(TimingState::AddressPhase);
|
|
rom.set_timing_state(TimingState::WaitLatency);
|
|
rom.set_timing_state(TimingState::DriveData);
|
|
}
|
|
|
|
// Should end up in the expected final state
|
|
assert_eq!(rom.get_timing_state(), TimingState::DriveData);
|
|
}
|
|
|
|
#[test]
|
|
fn test_memory_operation_simulation() {
|
|
let mut rom = Intel4001::new_with_access_time("ROM_4001".to_string(), 1);
|
|
|
|
// Load test data
|
|
let test_data = vec![0x12, 0x34, 0x56, 0x78];
|
|
rom.load_rom_data(test_data.clone(), 0).unwrap();
|
|
|
|
// Test that the ROM correctly stores and retrieves data
|
|
for (i, &expected) in test_data.iter().enumerate() {
|
|
assert_eq!(rom.read_rom(i as u8).unwrap(), expected);
|
|
}
|
|
|
|
// Test that the component maintains correct metadata
|
|
assert_eq!(rom.get_rom_size(), 256);
|
|
assert_eq!(rom.get_access_time(), 1);
|
|
assert_eq!(rom.name(), "ROM_4001");
|
|
}
|
|
|
|
#[test]
|
|
fn test_trait_consistency_across_implementations() {
|
|
// Test that different components implement the traits consistently
|
|
let rom = Intel4001::new_with_access_time("ROM_4001".to_string(), 1);
|
|
|
|
// Test that all trait methods are available and work consistently
|
|
let base_component: &dyn Component = &rom;
|
|
assert_eq!(base_component.name(), "ROM_4001");
|
|
|
|
// Test that timing state trait works
|
|
let timing_state: &dyn Intel400xTimingState = &rom;
|
|
assert_eq!(timing_state.get_timing_state(), TimingState::Idle);
|
|
assert_eq!(timing_state.get_access_time(), Duration::from_nanos(1));
|
|
|
|
// Test that clock handling trait works
|
|
let clock_handler: &dyn Intel400xClockHandling = &rom;
|
|
let base_for_clock = clock_handler.get_base();
|
|
assert_eq!(base_for_clock.get_name(), "ROM_4001");
|
|
|
|
// Test that data bus trait works
|
|
let data_bus: &dyn Intel400xDataBus = &rom;
|
|
let base_for_data = data_bus.get_base();
|
|
assert_eq!(base_for_data.get_name(), "ROM_4001");
|
|
|
|
// Test that address handling trait works
|
|
let address_handler: &dyn Intel400xAddressHandling = &rom;
|
|
let base_for_address = address_handler.get_base();
|
|
assert_eq!(base_for_address.get_name(), "ROM_4001");
|
|
|
|
// Test that control pins trait works
|
|
let control_pins: &dyn Intel400xControlPins = &rom;
|
|
let base_for_control = control_pins.get_base();
|
|
assert_eq!(base_for_control.get_name(), "ROM_4001");
|
|
|
|
// Test that reset handling trait works
|
|
let reset_handler: &dyn Intel400xResetHandling = &rom;
|
|
let base_for_reset = reset_handler.get_base();
|
|
assert_eq!(base_for_reset.get_name(), "ROM_4001");
|
|
}
|
|
|
|
#[test]
|
|
fn test_comprehensive_state_management() {
|
|
let mut rom = Intel4001::new_with_access_time("ROM_4001".to_string(), 1);
|
|
|
|
// Test comprehensive state management through traits
|
|
let initial_state = rom.get_timing_state();
|
|
assert_eq!(initial_state, TimingState::Idle);
|
|
|
|
// Set up a complete memory operation state
|
|
rom.set_timing_state(TimingState::AddressPhase);
|
|
rom.set_address_high_nibble(Some(0x0F));
|
|
rom.set_address_low_nibble(Some(0x23));
|
|
rom.set_full_address_ready(true);
|
|
rom.set_address_latch_time(Some(std::time::Instant::now()));
|
|
|
|
// Verify all state is consistent
|
|
assert_eq!(rom.get_timing_state(), TimingState::AddressPhase);
|
|
assert_eq!(rom.get_address_high_nibble(), Some(0x0F));
|
|
assert_eq!(rom.get_address_low_nibble(), Some(0x23));
|
|
assert_eq!(rom.get_full_address_ready(), true);
|
|
assert!(rom.get_address_latch_time().is_some());
|
|
|
|
// Test state reset
|
|
rom.perform_reset();
|
|
|
|
// Verify all state is cleared
|
|
assert_eq!(rom.get_timing_state(), TimingState::Idle);
|
|
assert_eq!(rom.get_address_high_nibble(), None);
|
|
assert_eq!(rom.get_address_low_nibble(), None);
|
|
assert_eq!(rom.get_full_address_ready(), false);
|
|
assert_eq!(rom.get_address_latch_time(), None);
|
|
}
|
|
|
|
#[test]
|
|
fn test_cross_component_compatibility() {
|
|
// Test that components using the same traits are compatible
|
|
let rom1 = Intel4001::new_with_access_time("ROM1".to_string(), 1);
|
|
let rom2 = Intel4001::new_with_access_time("ROM2".to_string(), 2);
|
|
|
|
// Both should implement the same interface
|
|
fn use_intel400x_component(component: &dyn Intel400xTimingState) {
|
|
let _state = component.get_timing_state();
|
|
let _access_time = component.get_access_time();
|
|
}
|
|
|
|
use_intel400x_component(&rom1);
|
|
use_intel400x_component(&rom2);
|
|
|
|
// Both should have the same initial state
|
|
assert_eq!(rom1.get_timing_state(), rom2.get_timing_state());
|
|
assert_eq!(rom1.get_timing_state(), TimingState::Idle);
|
|
|
|
// But different access times
|
|
assert_ne!(rom1.get_access_time(), rom2.get_access_time());
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod cross_component_integration_tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_multiple_intel4001_components() {
|
|
// Test that multiple Intel4001 components work together
|
|
let mut rom1 = Intel4001::new_with_access_time("ROM1".to_string(), 1);
|
|
let mut rom2 = Intel4001::new_with_access_time("ROM2".to_string(), 2);
|
|
|
|
// Load different data into each
|
|
rom1.load_rom_data(vec![0x11, 0x22], 0).unwrap();
|
|
rom2.load_rom_data(vec![0x33, 0x44], 0).unwrap();
|
|
|
|
// Both should implement the same traits
|
|
assert_eq!(rom1.read_rom(0x00).unwrap(), 0x11);
|
|
assert_eq!(rom2.read_rom(0x00).unwrap(), 0x33);
|
|
|
|
// Both should have the same interface
|
|
assert_eq!(rom1.get_timing_state(), TimingState::Idle);
|
|
assert_eq!(rom2.get_timing_state(), TimingState::Idle);
|
|
|
|
// Test state manipulation on both
|
|
rom1.set_timing_state(TimingState::AddressPhase);
|
|
rom2.set_timing_state(TimingState::AddressPhase);
|
|
|
|
assert_eq!(rom1.get_timing_state(), TimingState::AddressPhase);
|
|
assert_eq!(rom2.get_timing_state(), TimingState::AddressPhase);
|
|
}
|
|
|
|
#[test]
|
|
fn test_component_lifecycle_integration() {
|
|
let mut rom = Intel4001::new_with_access_time("ROM_4001".to_string(), 1);
|
|
|
|
// Test the complete lifecycle of a component
|
|
assert_eq!(rom.get_timing_state(), TimingState::Idle);
|
|
assert!(!rom.is_running());
|
|
|
|
// Set up some state
|
|
rom.set_timing_state(TimingState::AddressPhase);
|
|
rom.set_address_high_nibble(Some(0x12));
|
|
rom.set_full_address_ready(true);
|
|
|
|
// Verify state
|
|
assert_eq!(rom.get_timing_state(), TimingState::AddressPhase);
|
|
assert_eq!(rom.get_address_high_nibble(), Some(0x12));
|
|
assert_eq!(rom.get_full_address_ready(), true);
|
|
|
|
// Reset should clear everything
|
|
rom.perform_reset();
|
|
|
|
assert_eq!(rom.get_timing_state(), TimingState::Idle);
|
|
assert_eq!(rom.get_address_high_nibble(), None);
|
|
assert_eq!(rom.get_full_address_ready(), false);
|
|
}
|
|
|
|
#[test]
|
|
fn test_timing_consistency_across_components() {
|
|
// Test that timing behavior is consistent across different components
|
|
let mut rom1 = Intel4001::new_with_access_time("ROM1".to_string(), 100);
|
|
let mut rom2 = Intel4001::new_with_access_time("ROM2".to_string(), 200);
|
|
|
|
// Both should start with the same timing state
|
|
assert_eq!(rom1.get_timing_state(), rom2.get_timing_state());
|
|
assert_eq!(rom1.get_timing_state(), TimingState::Idle);
|
|
|
|
// But have different access times
|
|
assert_eq!(rom1.get_access_time(), 100);
|
|
assert_eq!(rom2.get_access_time(), 200);
|
|
|
|
// Both should support the same state transitions
|
|
rom1.set_timing_state(TimingState::AddressPhase);
|
|
rom2.set_timing_state(TimingState::AddressPhase);
|
|
|
|
assert_eq!(rom1.get_timing_state(), TimingState::AddressPhase);
|
|
assert_eq!(rom2.get_timing_state(), TimingState::AddressPhase);
|
|
}
|
|
}
|