Added more tests
Extracted common 400x behavior like timing and latching into common module Added maxed MCS-4
This commit is contained in:
parent
dd93e33087
commit
c039e9069a
25 changed files with 5694 additions and 494 deletions
392
tests/mock_based_tests.rs
Normal file
392
tests/mock_based_tests.rs
Normal file
|
|
@ -0,0 +1,392 @@
|
|||
//! 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 crate::mocks::*;
|
||||
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
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue