Added console for displaying the system state

Corrected behaviour and tests
This commit is contained in:
Jan-Peter von Hunnius 2025-09-29 19:02:00 +02:00
parent 8193bcda46
commit 0278a96a8d
24 changed files with 1074 additions and 2918 deletions

View file

@ -1,170 +0,0 @@
//! Demonstration of running Intel 400x tests
//!
//! This module demonstrates how to run the comprehensive test suite
//! for the intel_400x common functionality.
use rusty_emu::components::common::intel_400x::*;
use rusty_emu::components::memory::intel_4001::Intel4001;
#[cfg(test)]
mod test_demonstration {
use super::*;
#[test]
fn demo_basic_functionality() {
println!("=== Intel 400x Common Functionality Test Demo ===");
// Test 1: Verify timing constants
println!("\n1. Testing timing constants...");
assert!(TimingConstants::DEFAULT_ACCESS_TIME > std::time::Duration::from_nanos(0));
assert!(TimingConstants::FAST_ACCESS_TIME > std::time::Duration::from_nanos(0));
println!("✓ Timing constants are valid");
// Test 2: Test address assembly
println!("\n2. Testing address assembly...");
let mut scenario = create_standard_test_scenario();
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));
println!("✓ Address assembly works correctly");
// Test 3: Test state machine
println!("\n3. Testing state machine...");
scenario
.component
.set_timing_state(TimingState::AddressPhase);
assert_eq!(
scenario.component.get_timing_state(),
TimingState::AddressPhase
);
assert!(scenario.component.get_timing_state().is_address_phase());
println!("✓ State machine transitions work correctly");
// Test 4: Test real component integration
println!("\n4. Testing real component integration...");
let mut rom = Intel4001::new_with_access_time("DemoROM".to_string(), 1);
rom.load_rom_data(vec![0xAB, 0xCD], 0).unwrap();
assert_eq!(rom.read_rom(0x00).unwrap(), 0xAB);
assert_eq!(rom.read_rom(0x01).unwrap(), 0xCD);
println!("✓ Real component integration works");
// Test 5: Test trait consistency
println!("\n5. Testing trait consistency...");
verify_intel400x_traits(&rom);
println!("✓ All required traits are implemented correctly");
println!("\n=== All tests passed! ===");
println!("The intel_400x common functionality is working correctly.");
}
#[test]
fn demo_comprehensive_testing() {
println!("=== Comprehensive Intel 400x Testing Demo ===");
// Create test configuration
let config = TestConfig::default();
if config.enable_timing_tests {
println!("\n1. Running timing tests...");
verify_timing_constants();
println!("✓ Timing tests completed");
}
if config.enable_property_tests {
println!("\n2. Running property tests...");
verify_state_machine_properties();
println!("✓ Property tests completed");
}
// Test with different scenarios
println!("\n3. Testing different scenarios...");
let mut fast_scenario = crate::mocks::MockScenario::new("FastTest");
fast_scenario.set_pin_value("SYNC", rusty_emu::pin::PinValue::High);
fast_scenario.set_pin_value("CM", rusty_emu::pin::PinValue::High);
fast_scenario.set_pin_value("CI", rusty_emu::pin::PinValue::Low);
// Test rapid state changes
for i in 0..10 {
fast_scenario.component.set_timing_state(TimingState::Idle);
fast_scenario
.component
.set_timing_state(TimingState::AddressPhase);
fast_scenario
.component
.set_timing_state(TimingState::WaitLatency);
fast_scenario
.component
.set_timing_state(TimingState::DriveData);
}
assert_eq!(
fast_scenario.component.get_timing_state(),
TimingState::DriveData
);
println!("✓ Rapid state changes handled correctly");
// Test data bus operations
fast_scenario.set_data_bus_value(0x0F);
assert_eq!(fast_scenario.get_data_bus_value(), 0x0F);
println!("✓ Data bus operations work correctly");
println!("\n=== Comprehensive testing completed successfully! ===");
}
#[test]
fn demo_error_handling() {
println!("=== Error Handling Demo ===");
// Test graceful error handling
let mut rom = Intel4001::new("ErrorTestROM".to_string());
// Test valid memory access
assert_eq!(rom.read_rom(0xFF), Some(0x00)); // Default value
println!("✓ Invalid memory access handled gracefully");
// Test invalid data loading
assert!(rom.load_rom_data(vec![0x12], 255).is_ok()); // Valid
assert!(rom.load_rom_data(vec![0x12], 256).is_err()); // Invalid
println!("✓ Invalid data loading handled gracefully");
println!("=== Error handling demo completed ===");
}
#[test]
fn demo_performance_characteristics() {
println!("=== Performance Characteristics Demo ===");
// Test that components can handle rapid operations
let mut rom = Intel4001::new_with_access_time("PerfTestROM".to_string(), 1);
let start_time = std::time::Instant::now();
// Perform many state transitions
for _ in 0..1000 {
rom.set_timing_state(TimingState::Idle);
rom.set_timing_state(TimingState::AddressPhase);
rom.set_timing_state(TimingState::WaitLatency);
rom.set_timing_state(TimingState::DriveData);
}
let elapsed = start_time.elapsed();
println!("✓ 4000 state transitions completed in {:?}", elapsed);
// Test address operations
for i in 0..256 {
rom.set_address_high_nibble(Some((i >> 4) as u8));
rom.set_address_low_nibble(Some((i & 0x0F) as u8));
rom.set_full_address_ready(i % 2 == 0);
}
let elapsed = start_time.elapsed();
println!("✓ 256 address operations completed in {:?}", elapsed);
println!("=== Performance demo completed ===");
}
}

View file

@ -88,85 +88,8 @@ mod intel_4001_integration_tests {
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() {
@ -279,18 +202,6 @@ mod intel_4001_integration_tests {
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() {
@ -301,7 +212,7 @@ mod intel_4001_integration_tests {
assert_eq!(rom.get_access_time(), 1);
// Test that the component can handle rapid state changes
for i in 0..100 {
for _ in 0..100 {
rom.set_timing_state(TimingState::Idle);
rom.set_timing_state(TimingState::AddressPhase);
rom.set_timing_state(TimingState::WaitLatency);
@ -371,38 +282,6 @@ mod intel_4001_integration_tests {
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() {
@ -458,31 +337,6 @@ mod cross_component_integration_tests {
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() {

View file

@ -20,17 +20,6 @@ struct TestPin {
type Pin = TestPin;
impl Pin {
fn new() -> Self {
Self {
value: PinValue::HighZ,
driver: None,
}
}
fn read(&self) -> PinValue {
self.value
}
fn set_driver(&mut self, driver: Option<String>, value: PinValue) {
self.driver = driver.map(|name| (name, value));
self.value = value;
@ -134,20 +123,20 @@ mod tests {
// Test address assembly with valid nibbles
assert_eq!(
handler.assemble_full_address(Some(0x0F), Some(0x23)),
Some(0x0F23)
handler.assemble_full_address(Some(0x0F), Some(0x03)),
Some(0xF3)
);
assert_eq!(
handler.assemble_full_address(Some(0x12), Some(0x34)),
Some(0x1234)
handler.assemble_full_address(Some(0x02), Some(0x04)),
Some(0x24)
);
assert_eq!(
handler.assemble_full_address(Some(0x00), Some(0x00)),
Some(0x0000)
Some(0x00)
);
assert_eq!(
handler.assemble_full_address(Some(0xFF), Some(0xFF)),
Some(0xFFFF)
handler.assemble_full_address(Some(0x0F), Some(0x0F)),
Some(0xFF)
);
// Test with missing nibbles
@ -237,26 +226,6 @@ mod tests {
assert_eq!(create_driver_name("CPU", "CONTROL"), "CPU_CONTROL");
}
#[test]
fn test_clock_edge_detection_logic() {
// Test edge detection logic without hardware dependencies
// PHI1: Low -> High (rising edge)
assert!(PinValue::High == PinValue::High && PinValue::Low == PinValue::Low);
// PHI1: High -> Low (falling edge)
assert!(PinValue::Low == PinValue::Low && PinValue::High == PinValue::High);
// PHI2: Low -> High (rising edge)
assert!(PinValue::High == PinValue::High && PinValue::Low == PinValue::Low);
// PHI2: High -> Low (falling edge)
assert!(PinValue::Low == PinValue::Low && PinValue::High == PinValue::High);
// No edge cases
assert!(PinValue::High == PinValue::High && PinValue::High == PinValue::Low);
assert!(PinValue::Low == PinValue::Low && PinValue::Low == PinValue::High);
}
#[test]
fn test_data_bus_bit_operations() {
// Test bit manipulation for data bus operations
@ -350,8 +319,8 @@ mod tests {
assert!(address_latch_time.is_some());
// Verify the assembled address
let assembled = handler.assemble_full_address(Some(0x12), Some(0x34));
assert_eq!(assembled, Some(0x1234));
let assembled = handler.assemble_full_address(Some(0x02), Some(0x04));
assert_eq!(assembled, Some(0x24));
}
#[test]
@ -376,7 +345,7 @@ mod tests {
&recent_time,
access_time
),
false
true
);
// Note: Testing with elapsed time would require sleeping or mocking time

View file

@ -0,0 +1,170 @@
//! JSON-Based Integration Tests
//!
//! These tests verify that the JSON configuration system works correctly
//! and that complete MCS-4 systems function as expected.
use rusty_emu::system_config::SystemFactory;
use std::fs;
#[cfg(test)]
mod json_system_tests {
use super::*;
#[test]
fn test_system_factory_creation() {
let factory = SystemFactory::new();
// Factory should be created successfully
assert!(format!("{:?}", factory).len() > 0);
}
#[test]
fn test_basic_system_loading() {
let factory = SystemFactory::new();
// Test loading basic system configuration
let result = factory.create_from_json("configs/mcs4_basic.json");
assert!(result.is_ok(), "Failed to create basic system: {:?}", result.err());
let mut system = result.unwrap();
let info = system.get_system_info();
// Verify system properties
assert_eq!(info.name, "IntelMcs4");
assert_eq!(info.component_count, 5); // CPU, Clock, 2 ROMs, 1 RAM
assert_eq!(info.cpu_speed, 750000.0);
}
#[test]
fn test_system_component_access() {
let factory = SystemFactory::new();
let system = factory.create_from_json("configs/mcs4_basic.json").unwrap();
// Test that we can access system components
let info = system.get_system_info();
assert_eq!(info.component_count, 5);
// Test that system is initially not running
assert!(!system.is_running());
}
#[test]
fn test_invalid_config_file() {
let factory = SystemFactory::new();
// Test with non-existent file
let result = factory.create_from_json("configs/non_existent.json");
assert!(result.is_err());
// Test with invalid JSON
let invalid_json = "{\"invalid\": json}";
fs::write("test_invalid.json", invalid_json).unwrap();
let result = factory.create_from_json("test_invalid.json");
assert!(result.is_err());
// Clean up
let _ = fs::remove_file("test_invalid.json");
}
#[test]
fn test_system_configuration_metadata() {
let factory = SystemFactory::new();
let system = factory.create_from_json("configs/mcs4_basic.json").unwrap();
let info = system.get_system_info();
// Verify metadata is loaded correctly
assert_eq!(info.name, "IntelMcs4");
assert_eq!(info.description, "Basic MCS-4 System with CPU, Clock, 2 ROMs, and 1 RAM");
assert_eq!(info.component_count, 5);
assert_eq!(info.cpu_speed, 750000.0);
assert_eq!(info.rom_size, 256);
assert_eq!(info.ram_size, 40);
}
}
#[cfg(test)]
mod system_execution_tests {
use super::*;
#[test]
fn test_system_execution_lifecycle() {
let factory = SystemFactory::new();
let system = factory.create_from_json("configs/mcs4_basic.json").unwrap();
// System should start not running
assert!(!system.is_running());
// Test that we can start and stop the system
// Note: In a real test, we might want to run for a very short time
// or mock the execution to avoid infinite loops
// For now, just test that the system object exists and has correct properties
let info = system.get_system_info();
assert_eq!(info.name, "IntelMcs4");
}
}
#[cfg(test)]
mod fibonacci_program_tests {
use super::*;
#[test]
fn test_fibonacci_program_loading() {
let factory = SystemFactory::new();
let system = factory.create_from_json("configs/mcs4_basic.json").unwrap();
// Test that the system can be created with fibonacci program
// The actual program loading is handled by the main application
let info = system.get_system_info();
assert_eq!(info.name, "IntelMcs4");
}
#[test]
fn test_program_file_existence() {
// Test that fibonacci program files exist
assert!(std::path::Path::new("programs/fibonacci.bin").exists());
assert!(std::path::Path::new("programs/fibonacci_output.bin").exists());
// Test that config files exist
assert!(std::path::Path::new("configs/mcs4_basic.json").exists());
assert!(std::path::Path::new("configs/mcs4_max.json").exists());
}
#[test]
fn test_program_file_sizes() {
// Test that program files have reasonable sizes
let fibonacci_size = fs::metadata("programs/fibonacci.bin")
.unwrap()
.len();
let fibonacci_output_size = fs::metadata("programs/fibonacci_output.bin")
.unwrap()
.len();
// Both should be non-zero and reasonable sizes
assert!(fibonacci_size > 0);
assert!(fibonacci_output_size > 0);
assert!(fibonacci_size < 1000); // Reasonable upper bound
assert!(fibonacci_output_size < 1000);
}
}
#[cfg(test)]
mod configuration_validation_tests {
use super::*;
#[test]
fn test_component_naming_consistency() {
let factory = SystemFactory::new();
let system = factory.create_from_json("configs/mcs4_basic.json").unwrap();
// Test that component names match expected patterns
let info = system.get_system_info();
assert_eq!(info.name, "IntelMcs4");
assert_eq!(info.component_count, 5);
}
}

View file

@ -11,9 +11,6 @@ pub use rusty_emu;
// Module declarations for test files
mod integration_tests;
mod intel_400x_tests;
mod mock_based_tests;
mod mocks;
mod property_based_tests;
// Common test utilities and helpers
pub mod test_utils {
@ -34,21 +31,6 @@ pub mod test_utils {
std::thread::sleep(duration);
}
/// Create a test scenario with known good values
pub fn create_standard_test_scenario() -> crate::mocks::MockScenario {
let scenario = crate::mocks::MockScenario::new("StandardTest");
scenario
.component
.set_pin_value("SYNC", rusty_emu::pin::PinValue::High);
scenario
.component
.set_pin_value("CM", rusty_emu::pin::PinValue::High);
scenario
.component
.set_pin_value("CI", rusty_emu::pin::PinValue::Low);
scenario
}
/// Verify that a component implements all required traits
pub fn verify_intel400x_traits<C>(component: &C)
where
@ -83,11 +65,6 @@ pub mod test_utils {
pub fn verify_timing_constants() {
use rusty_emu::components::common::intel_400x::TimingConstants;
assert!(TimingConstants::DEFAULT_ACCESS_TIME > Duration::from_nanos(0));
assert!(TimingConstants::FAST_ACCESS_TIME > Duration::from_nanos(0));
assert!(TimingConstants::ADDRESS_SETUP > Duration::from_nanos(0));
assert!(TimingConstants::DATA_VALID > Duration::from_nanos(0));
// Fast access should be faster than default
assert!(TimingConstants::FAST_ACCESS_TIME < TimingConstants::DEFAULT_ACCESS_TIME);
}

View file

@ -1,506 +0,0 @@
//! 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
}
}

View file

@ -1,438 +0,0 @@
//! Mock implementations for testing Intel 400x components
//!
//! This module provides mock implementations of pins, components, and timing
//! to enable comprehensive testing of the intel_400x traits and functionality.
use rusty_emu::component::{BaseComponent, Component};
use rusty_emu::components::common::intel_400x::*;
use rusty_emu::pin::{Pin, PinValue};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
/// Mock pin implementation for testing
#[derive(Debug, Clone)]
pub struct MockPin {
pub name: String,
pub value: PinValue,
pub driver: Option<(String, PinValue)>,
pub read_count: usize,
pub write_count: usize,
}
impl MockPin {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
value: PinValue::HighZ,
driver: None,
read_count: 0,
write_count: 0,
}
}
pub fn read(&mut self) -> PinValue {
self.read_count += 1;
self.value
}
pub fn set_driver(&mut self, driver: Option<String>, value: PinValue) {
self.write_count += 1;
self.driver = driver.map(|name| (name, value));
self.value = value;
}
pub fn set_value(&mut self, value: PinValue) {
self.value = value;
}
pub fn get_read_count(&self) -> usize {
self.read_count
}
pub fn get_write_count(&self) -> usize {
self.write_count
}
}
/// Mock component for testing trait implementations
#[derive(Debug)]
pub struct MockIntel400xComponent {
pub name: String,
pub pins: HashMap<String, Arc<Mutex<MockPin>>>,
pub timing_state: TimingState,
pub address_latch_time: Option<Instant>,
pub full_address_ready: bool,
pub address_high_nibble: Option<u8>,
pub address_low_nibble: Option<u8>,
pub access_time: Duration,
}
impl MockIntel400xComponent {
pub fn new(name: &str) -> Self {
let mut pins = HashMap::new();
// Add standard Intel 400x pins
let clock_pins = ["PHI1", "PHI2"];
let data_pins = ["D0", "D1", "D2", "D3"];
let control_pins = ["SYNC", "CM", "RESET"];
for pin_name in clock_pins
.iter()
.chain(data_pins.iter())
.chain(control_pins.iter())
{
pins.insert(
pin_name.to_string(),
Arc::new(Mutex::new(MockPin::new(pin_name))),
);
}
Self {
name: name.to_string(),
pins,
timing_state: TimingState::Idle,
address_latch_time: None,
full_address_ready: false,
address_high_nibble: None,
address_low_nibble: None,
access_time: TimingConstants::DEFAULT_ACCESS_TIME,
}
}
pub fn set_pin_value(&self, pin_name: &str, value: PinValue) {
if let Some(pin) = self.pins.get(pin_name) {
if let Ok(mut pin_guard) = pin.lock() {
pin_guard.set_value(value);
}
}
}
pub fn get_pin_value(&self, pin_name: &str) -> Option<PinValue> {
if let Some(pin) = self.pins.get(pin_name) {
if let Ok(pin_guard) = pin.lock() {
return Some(pin_guard.value);
}
}
None
}
pub fn set_clock_values(&self, phi1: PinValue, phi2: PinValue) {
self.set_pin_value("PHI1", phi1);
self.set_pin_value("PHI2", phi2);
}
pub fn set_data_bus(&self, value: u8) {
for i in 0..4 {
let bit_value = (value >> i) & 1;
let pin_value = if bit_value == 1 {
PinValue::High
} else {
PinValue::Low
};
self.set_pin_value(&format!("D{}", i), pin_value);
}
}
pub fn get_pin_read_count(&self, pin_name: &str) -> Option<usize> {
if let Some(pin) = self.pins.get(pin_name) {
if let Ok(pin_guard) = pin.lock() {
return Some(pin_guard.get_read_count());
}
}
None
}
pub fn get_pin_write_count(&self, pin_name: &str) -> Option<usize> {
if let Some(pin) = self.pins.get(pin_name) {
if let Ok(pin_guard) = pin.lock() {
return Some(pin_guard.get_write_count());
}
}
None
}
}
impl Component for MockIntel400xComponent {
fn name(&self) -> String {
self.name.clone()
}
fn pins(&self) -> HashMap<String, Arc<Mutex<Pin>>> {
// Return empty map for testing - in real implementation would convert MockPins
HashMap::new()
}
fn get_pin(&self, name: &str) -> Result<Arc<Mutex<Pin>>, String> {
// Convert our MockPin to the expected Pin type
if let Some(_mock_pin) = self.pins.get(name) {
// This is a simplified conversion - in a real implementation,
// you'd need to create a proper adapter
Err(format!("Mock pin conversion not implemented for {}", name))
} else {
Err(format!("Pin {} not found", name))
}
}
fn update(&mut self) {
// No-op for mock
}
fn run(&mut self) {
// No-op for mock
}
fn stop(&mut self) {
// No-op for mock
}
fn is_running(&self) -> bool {
false
}
}
impl Intel400xClockHandling for MockIntel400xComponent {
fn get_base(&self) -> &BaseComponent {
// For testing, we need to create a minimal BaseComponent
// This is a limitation of the current test setup
// In a real implementation, this would return a reference to an embedded BaseComponent
unimplemented!("MockIntel400xComponent doesn't contain BaseComponent")
}
}
impl Intel400xDataBus for MockIntel400xComponent {
fn get_base(&self) -> &BaseComponent {
unimplemented!("MockIntel400xComponent doesn't contain BaseComponent")
}
}
impl Intel400xAddressHandling for MockIntel400xComponent {
fn get_base(&self) -> &BaseComponent {
unimplemented!("MockIntel400xComponent doesn't contain BaseComponent")
}
}
impl Intel400xControlPins for MockIntel400xComponent {
fn get_base(&self) -> &BaseComponent {
unimplemented!("MockIntel400xComponent doesn't contain BaseComponent")
}
}
impl Intel400xResetHandling for MockIntel400xComponent {
fn get_base(&self) -> &BaseComponent {
unimplemented!("MockIntel400xComponent doesn't contain BaseComponent")
}
fn perform_reset(&self) {
// Reset implementation for testing - Note: This is a limitation
// In a real implementation with proper BaseComponent, this would work
// For testing purposes, we document the expected behavior
}
}
impl Intel400xTimingState for MockIntel400xComponent {
fn get_timing_state(&self) -> TimingState {
self.timing_state
}
fn set_timing_state(&mut self, state: TimingState) {
self.timing_state = state;
}
fn get_address_latch_time(&self) -> Option<Instant> {
self.address_latch_time
}
fn set_address_latch_time(&mut self, time: Option<Instant>) {
self.address_latch_time = time;
}
fn get_full_address_ready(&self) -> bool {
self.full_address_ready
}
fn set_full_address_ready(&mut self, ready: bool) {
self.full_address_ready = ready;
}
fn get_address_high_nibble(&self) -> Option<u8> {
self.address_high_nibble
}
fn set_address_high_nibble(&mut self, nibble: Option<u8>) {
self.address_high_nibble = nibble;
}
fn get_address_low_nibble(&self) -> Option<u8> {
self.address_low_nibble
}
fn set_address_low_nibble(&mut self, nibble: Option<u8>) {
self.address_low_nibble = nibble;
}
fn get_access_time(&self) -> Duration {
self.access_time
}
}
/// Mock time provider for deterministic testing
#[derive(Debug, Clone)]
pub struct MockTimeProvider {
pub current_time: Instant,
pub time_offset: Duration,
}
impl MockTimeProvider {
pub fn new() -> Self {
Self {
current_time: Instant::now(),
time_offset: Duration::from_nanos(0),
}
}
pub fn advance(&mut self, duration: Duration) {
self.time_offset += duration;
}
pub fn set_time(&mut self, time: Instant) {
self.current_time = time;
}
pub fn now(&self) -> Instant {
self.current_time + self.time_offset
}
pub fn elapsed(&self, since: Instant) -> Duration {
self.now() - since
}
}
/// Test helper for creating mock scenarios
pub struct MockScenario {
pub component: MockIntel400xComponent,
pub time_provider: MockTimeProvider,
}
impl MockScenario {
pub fn new(name: &str) -> Self {
Self {
component: MockIntel400xComponent::new(name),
time_provider: MockTimeProvider::new(),
}
}
pub fn set_clock_high(&mut self) {
self.component
.set_clock_values(PinValue::High, PinValue::High);
}
pub fn set_clock_low(&mut self) {
self.component
.set_clock_values(PinValue::Low, PinValue::Low);
}
pub fn set_phi1_rising_edge(&mut self) {
self.component
.set_clock_values(PinValue::High, PinValue::Low);
}
pub fn set_phi1_falling_edge(&mut self) {
self.component
.set_clock_values(PinValue::Low, PinValue::Low);
}
pub fn set_phi2_rising_edge(&mut self) {
self.component
.set_clock_values(PinValue::Low, PinValue::High);
}
pub fn set_phi2_falling_edge(&mut self) {
self.component
.set_clock_values(PinValue::Low, PinValue::Low);
}
pub fn set_data_bus_value(&mut self, value: u8) {
self.component.set_data_bus(value);
}
pub fn get_data_bus_value(&self) -> u8 {
let mut value = 0;
for i in 0..4 {
if let Some(pin_value) = self.component.get_pin_value(&format!("D{}", i)) {
if pin_value == PinValue::High {
value |= 1 << i;
}
}
}
value
}
pub fn advance_time(&mut self, duration: Duration) {
self.time_provider.advance(duration);
}
pub fn set_access_time(&mut self, _duration: Duration) {
// This would need to be implemented in the actual component
// For now, it's a placeholder
}
}
/// Property-based testing helpers
pub mod proptest_helpers {
use super::*;
use proptest::prelude::*;
pub fn arb_pin_value() -> impl Strategy<Value = PinValue> {
prop_oneof![
Just(PinValue::Low),
Just(PinValue::High),
Just(PinValue::HighZ),
]
}
pub fn arb_timing_state() -> impl Strategy<Value = TimingState> {
prop_oneof![
Just(TimingState::Idle),
Just(TimingState::AddressPhase),
Just(TimingState::WaitLatency),
Just(TimingState::DriveData),
]
}
pub fn arb_memory_state() -> impl Strategy<Value = MemoryState> {
prop_oneof![
Just(MemoryState::Idle),
Just(MemoryState::AddressPhase),
Just(MemoryState::WaitLatency),
Just(MemoryState::DriveData),
]
}
pub fn arb_ram_state() -> impl Strategy<Value = RamState> {
prop_oneof![
Just(RamState::Idle),
Just(RamState::AddressPhase),
Just(RamState::WaitLatency),
Just(RamState::ReadData),
Just(RamState::WriteData),
Just(RamState::OutputPort),
]
}
pub fn arb_address_nibble() -> impl Strategy<Value = u8> {
0u8..16u8
}
pub fn arb_data_value() -> impl Strategy<Value = u8> {
0u8..16u8 // 4-bit values for Intel 4004
}
pub fn arb_duration_nanos() -> impl Strategy<Value = u64> {
0u64..1_000_000_000u64 // Up to 1 second in nanoseconds
}
pub fn arb_duration() -> impl Strategy<Value = Duration> {
(0u64..1_000_000_000u64).prop_map(Duration::from_nanos)
}
}

View file

@ -1,430 +0,0 @@
//! Property-based tests for Intel 400x state machine verification
//!
//! These tests use property-based testing to verify the correctness
//! of state machine behavior, timing properties, and system invariants.
use crate::mocks::*;
use proptest::prelude::*;
use rusty_emu::components::common::intel_400x::*;
use rusty_emu::pin::PinValue;
use std::time::{Duration, Instant};
#[cfg(test)]
mod state_machine_properties {
use super::*;
proptest! {
#[test]
fn test_timing_state_invariants(
state in proptest_helpers::arb_timing_state()
) {
// Test that state queries are consistent
match state {
TimingState::Idle => {
prop_assert!(state.is_idle());
prop_assert!(!state.is_address_phase());
prop_assert!(!state.is_waiting_latency());
prop_assert!(!state.is_driving_data());
}
TimingState::AddressPhase => {
prop_assert!(!state.is_idle());
prop_assert!(state.is_address_phase());
prop_assert!(!state.is_waiting_latency());
prop_assert!(!state.is_driving_data());
}
TimingState::WaitLatency => {
prop_assert!(!state.is_idle());
prop_assert!(!state.is_address_phase());
prop_assert!(state.is_waiting_latency());
prop_assert!(!state.is_driving_data());
}
TimingState::DriveData => {
prop_assert!(!state.is_idle());
prop_assert!(!state.is_address_phase());
prop_assert!(!state.is_waiting_latency());
prop_assert!(state.is_driving_data());
}
}
}
#[test]
fn test_memory_state_conversions_roundtrip(
mem_state in proptest_helpers::arb_memory_state()
) {
// Test that conversions to TimingState and back preserve the state
let timing_state: TimingState = mem_state.into();
let back_to_mem: MemoryState = timing_state.into();
// They should be equivalent (though not necessarily identical due to enum differences)
match (&mem_state, &back_to_mem) {
(MemoryState::Idle, MemoryState::Idle) => prop_assert!(true),
(MemoryState::AddressPhase, MemoryState::AddressPhase) => prop_assert!(true),
(MemoryState::WaitLatency, MemoryState::WaitLatency) => prop_assert!(true),
(MemoryState::DriveData, MemoryState::DriveData) => prop_assert!(true),
_ => prop_assert!(false, "State conversion mismatch: {:?} -> {:?}", mem_state, back_to_mem),
}
}
#[test]
fn test_ram_state_conversions_roundtrip(
ram_state in proptest_helpers::arb_ram_state()
) {
// Test that conversions to TimingState and back work correctly
let timing_state: TimingState = ram_state.into();
let back_to_ram: RamState = timing_state.into();
// Verify the conversion maintains the essential state information
match (&ram_state, &back_to_ram) {
(RamState::Idle, RamState::Idle) => prop_assert!(true),
(RamState::AddressPhase, RamState::AddressPhase) => prop_assert!(true),
(RamState::WaitLatency, RamState::WaitLatency) => prop_assert!(true),
// ReadData, WriteData, and OutputPort all map to DriveData in TimingState
(RamState::ReadData, RamState::ReadData) => prop_assert!(true),
(RamState::WriteData, RamState::ReadData) => prop_assert!(true), // This is expected
(RamState::OutputPort, RamState::ReadData) => prop_assert!(true), // This is expected
_ => prop_assert!(false, "Unexpected state conversion: {:?} -> {:?}", ram_state, back_to_ram),
}
}
}
#[test]
fn test_address_assembly_properties() {
let mut scenario = MockScenario::new("TestAddressAssembly");
// Test that address assembly is deterministic and correct
let test_cases = vec![
(0x00, 0x00, 0x0000),
(0x12, 0x34, 0x1234),
(0xFF, 0xFF, 0xFFFF),
(0x0A, 0x0B, 0x0A0B),
(0x01, 0x23, 0x0123),
];
for (high, low, _expected) in test_cases {
scenario.component.set_address_high_nibble(Some(high));
scenario.component.set_address_low_nibble(Some(low));
scenario.component.set_full_address_ready(true);
// In a real implementation, this would test the actual trait method
// For now, we verify our mock state is consistent
assert_eq!(scenario.component.get_address_high_nibble(), Some(high));
assert_eq!(scenario.component.get_address_low_nibble(), Some(low));
assert_eq!(scenario.component.get_full_address_ready(), true);
// Clear for next test
scenario.component.set_address_high_nibble(None);
scenario.component.set_address_low_nibble(None);
scenario.component.set_full_address_ready(false);
}
}
proptest! {
#[test]
fn test_address_nibble_assembly(
high_nibble in proptest_helpers::arb_address_nibble(),
low_nibble in proptest_helpers::arb_address_nibble()
) {
// Test that address assembly works for all valid nibble combinations
let expected_address = ((high_nibble as u16) << 4) | (low_nibble as u16);
let mut scenario = MockScenario::new("TestNibbleAssembly");
scenario.component.set_address_high_nibble(Some(high_nibble));
scenario.component.set_address_low_nibble(Some(low_nibble));
// Verify the nibbles are stored correctly
prop_assert_eq!(scenario.component.get_address_high_nibble(), Some(high_nibble));
prop_assert_eq!(scenario.component.get_address_low_nibble(), Some(low_nibble));
// The expected address should be correctly calculated
prop_assert_eq!(expected_address, ((high_nibble as u16) << 4) | (low_nibble as u16));
}
#[test]
fn test_data_bus_bit_pattern_consistency(
data_value in proptest_helpers::arb_data_value()
) {
let mut scenario = MockScenario::new("TestDataBusConsistency");
// Set data bus value
scenario.set_data_bus_value(data_value);
// Read it back
let read_value = scenario.get_data_bus_value();
// Should be identical (roundtrip consistency)
prop_assert_eq!(data_value, read_value);
// Verify individual bits
for i in 0..4 {
let expected_bit = (data_value >> i) & 1;
let pin_name = format!("D{}", i);
let actual_pin_value = scenario.component.get_pin_value(&pin_name);
match (expected_bit, actual_pin_value) {
(0, Some(PinValue::Low)) => prop_assert!(true),
(1, Some(PinValue::High)) => prop_assert!(true),
_ => prop_assert!(false, "Bit {} mismatch for value 0x{:02X}", i, data_value),
}
}
}
#[test]
fn test_timing_duration_properties(
duration_nanos in proptest_helpers::arb_duration()
) {
let duration = Duration::from_nanos(duration_nanos);
// Test duration properties
prop_assert!(duration.as_nanos() >= 0);
prop_assert_eq!(duration.as_nanos(), duration_nanos);
// Test that our timing constants are reasonable
prop_assert!(TimingConstants::DEFAULT_ACCESS_TIME > Duration::from_nanos(0));
prop_assert!(TimingConstants::FAST_ACCESS_TIME > Duration::from_nanos(0));
prop_assert!(TimingConstants::ADDRESS_SETUP > Duration::from_nanos(0));
prop_assert!(TimingConstants::DATA_VALID > Duration::from_nanos(0));
}
}
}
#[cfg(test)]
mod timing_invariant_tests {
use super::*;
#[test]
fn test_state_machine_never_invalid() {
// Test that our state machine never enters invalid states
let mut scenario = MockScenario::new("TestStateMachineValidity");
let valid_states = vec![
TimingState::Idle,
TimingState::AddressPhase,
TimingState::WaitLatency,
TimingState::DriveData,
];
for state in valid_states {
scenario.component.set_timing_state(state);
// State should be queryable and consistent
assert_eq!(scenario.component.get_timing_state(), state);
// State queries should be mutually exclusive where appropriate
let idle = state.is_idle();
let address = state.is_address_phase();
let waiting = state.is_waiting_latency();
let driving = state.is_driving_data();
// Only one "active" state should be true at a time
let active_states = vec![address, waiting, driving];
let active_count = active_states.iter().filter(|&&x| x).count();
if idle {
assert_eq!(active_count, 0, "Idle state should not have active flags");
} else {
assert!(
active_count > 0,
"Non-idle state should have at least one active flag"
);
}
}
}
#[test]
fn test_address_latch_invariants() {
let mut scenario = MockScenario::new("TestAddressLatchInvariants");
// Test address latch state consistency
scenario.component.set_address_high_nibble(Some(0x12));
scenario.component.set_address_low_nibble(Some(0x34));
scenario.component.set_full_address_ready(true);
// All three should be consistent
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);
// Test partial state
scenario.component.set_address_high_nibble(Some(0x56));
scenario.component.set_address_low_nibble(None);
scenario.component.set_full_address_ready(false);
assert_eq!(scenario.component.get_address_high_nibble(), Some(0x56));
assert_eq!(scenario.component.get_address_low_nibble(), None);
assert_eq!(scenario.component.get_full_address_ready(), false);
// Test empty state
scenario.component.set_address_high_nibble(None);
scenario.component.set_address_low_nibble(None);
scenario.component.set_full_address_ready(false);
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);
}
#[test]
fn test_pin_state_consistency() {
let scenario = MockScenario::new("TestPinConsistency");
// Test that pin states are consistent
let test_values = vec![PinValue::Low, PinValue::High, PinValue::HighZ];
for value in test_values {
scenario.component.set_pin_value("D0", value);
assert_eq!(scenario.component.get_pin_value("D0"), Some(value));
scenario.component.set_pin_value("PHI1", value);
assert_eq!(scenario.component.get_pin_value("PHI1"), Some(value));
scenario.component.set_pin_value("SYNC", value);
assert_eq!(scenario.component.get_pin_value("SYNC"), Some(value));
}
}
proptest! {
#[test]
fn test_bus_operation_invariants(
initial_value in proptest_helpers::arb_data_value(),
new_value in proptest_helpers::arb_data_value()
) {
let mut scenario = MockScenario::new("TestBusInvariants");
// Set initial value
scenario.set_data_bus_value(initial_value);
prop_assert_eq!(scenario.get_data_bus_value(), initial_value);
// Change to new value
scenario.set_data_bus_value(new_value);
prop_assert_eq!(scenario.get_data_bus_value(), new_value);
// Values should be different (unless they happen to be the same)
if initial_value != new_value {
// This is just documenting expected behavior
prop_assert!(true);
}
}
}
}
#[cfg(test)]
mod edge_case_tests {
use super::*;
#[test]
fn test_boundary_timing_values() {
// Test boundary conditions for timing
let zero_duration = Duration::from_nanos(0);
let small_duration = Duration::from_nanos(1);
let large_duration = Duration::from_nanos(1_000_000_000); // 1 second
// Test that our timing system can handle these values
let mut scenario = MockScenario::new("TestBoundaryTiming");
scenario.time_provider.advance(zero_duration);
scenario.time_provider.advance(small_duration);
scenario.time_provider.advance(large_duration);
// Verify time advancement works
assert!(scenario.time_provider.now() > Instant::now() - large_duration * 3);
}
#[test]
fn test_maximum_address_values() {
let mut scenario = MockScenario::new("TestMaxAddress");
// Test maximum address values
scenario.component.set_address_high_nibble(Some(0xFF));
scenario.component.set_address_low_nibble(Some(0xFF));
scenario.component.set_full_address_ready(true);
assert_eq!(scenario.component.get_address_high_nibble(), Some(0xFF));
assert_eq!(scenario.component.get_address_low_nibble(), Some(0xFF));
assert_eq!(scenario.component.get_full_address_ready(), true);
}
#[test]
fn test_minimum_address_values() {
let mut scenario = MockScenario::new("TestMinAddress");
// Test minimum address values
scenario.component.set_address_high_nibble(Some(0x00));
scenario.component.set_address_low_nibble(Some(0x00));
scenario.component.set_full_address_ready(true);
assert_eq!(scenario.component.get_address_high_nibble(), Some(0x00));
assert_eq!(scenario.component.get_address_low_nibble(), Some(0x00));
assert_eq!(scenario.component.get_full_address_ready(), true);
}
#[test]
fn test_rapid_state_transitions() {
let mut scenario = MockScenario::new("TestRapidTransitions");
// Test rapid state changes
let states = vec![
TimingState::Idle,
TimingState::AddressPhase,
TimingState::WaitLatency,
TimingState::DriveData,
TimingState::Idle,
];
for state in states {
scenario.component.set_timing_state(state);
assert_eq!(scenario.component.get_timing_state(), state);
}
}
}
#[cfg(test)]
mod concurrency_safety_tests {
use super::*;
use std::sync::Arc;
use std::thread;
#[test]
fn test_mock_component_thread_safety() {
let scenario = Arc::new(MockScenario::new("TestThreadSafety"));
// Test that our mock can handle concurrent access patterns
let handles: Vec<_> = (0..4)
.map(|i| {
let scenario_clone: Arc<MockScenario> = Arc::clone(&scenario);
thread::spawn(move || {
// Each thread tests different aspects
match i {
0 => {
// Note: This would need proper Arc<Mutex<>> handling for thread safety
// For now, we'll skip the mutable operation in this test
// Note: Thread safety testing would require proper Arc<Mutex<>> handling
}
1 => {
scenario_clone.component.set_pin_value("D0", PinValue::High);
assert_eq!(
scenario_clone.component.get_pin_value("D0"),
Some(PinValue::High)
);
}
2 => {
scenario_clone.component.set_address_high_nibble(Some(0x12));
assert_eq!(
scenario_clone.component.get_address_high_nibble(),
Some(0x12)
);
}
3 => {
scenario_clone.advance_time(Duration::from_nanos(100));
}
_ => unreachable!(),
}
})
})
.collect();
// Wait for all threads to complete
for handle in handles {
handle.join().expect("Thread should complete successfully");
}
}
}

View file

@ -40,9 +40,9 @@ mod simple_tests {
// Test address assembly with different values
let test_cases = vec![
(0x00, 0x00, 0x0000),
(0x12, 0x34, 0x1234),
(0xFF, 0xFF, 0xFFFF),
(0x0A, 0x0B, 0x0A0B),
(0x02, 0x04, 0x24),
(0x0F, 0x0F, 0xFF),
(0x0A, 0x0B, 0xAB),
];
for (high, low, expected) in test_cases {

View file

@ -1,203 +0,0 @@
//! Working test that demonstrates intel_400x functionality is testable
//!
//! This test focuses on the core functionality that actually works
//! and demonstrates that the intel_400x module is indeed testable.
use rusty_emu::components::common::intel_400x::*;
use rusty_emu::components::memory::intel_4001::Intel4001;
#[cfg(test)]
mod working_tests {
use super::*;
#[test]
fn test_intel400x_core_functionality() {
println!("=== Testing Intel 400x Core Functionality ===");
// Test 1: Verify timing constants work
println!("\n1. Testing timing constants...");
assert!(TimingConstants::DEFAULT_ACCESS_TIME > std::time::Duration::from_nanos(0));
assert!(TimingConstants::FAST_ACCESS_TIME > std::time::Duration::from_nanos(0));
assert!(TimingConstants::FAST_ACCESS_TIME < TimingConstants::DEFAULT_ACCESS_TIME);
println!("✓ Timing constants are valid and reasonable");
// Test 2: Test state machine functionality
println!("\n2. Testing state machine...");
let idle_state = TimingState::Idle;
let address_state = TimingState::AddressPhase;
let wait_state = TimingState::WaitLatency;
let drive_state = TimingState::DriveData;
// Test state queries
assert!(idle_state.is_idle());
assert!(address_state.is_address_phase());
assert!(wait_state.is_waiting_latency());
assert!(drive_state.is_driving_data());
// Test state transitions
assert!(!idle_state.is_address_phase());
assert!(!address_state.is_idle());
println!("✓ State machine works correctly");
// Test 3: Test address assembly logic
println!("\n3. Testing address assembly...");
// Test address assembly with various values
let test_cases = vec![
(0x00, 0x00, 0x0000),
(0x12, 0x34, 0x1234),
(0xFF, 0xFF, 0xFFFF),
(0x0A, 0x0B, 0x0A0B),
(0x01, 0x23, 0x0123),
];
for (high, low, expected) in test_cases {
let assembled = ((high as u16) << 4) | (low as u16);
assert_eq!(assembled, expected);
}
println!("✓ Address assembly works correctly for all test cases");
// Test 4: Test real component creation and basic functionality
println!("\n4. Testing real component integration...");
let mut rom = Intel4001::new_with_access_time("TestROM".to_string(), 1);
// Test that the component has the expected properties
assert_eq!(rom.get_rom_size(), 256);
assert_eq!(rom.get_access_time(), 1);
// Test data loading and reading
let test_data = vec![0x12, 0x34, 0x56, 0x78];
rom.load_rom_data(test_data, 0).unwrap();
assert_eq!(rom.read_rom(0x00).unwrap(), 0x12);
assert_eq!(rom.read_rom(0x01).unwrap(), 0x34);
assert_eq!(rom.read_rom(0x02).unwrap(), 0x56);
assert_eq!(rom.read_rom(0x03).unwrap(), 0x78);
println!("✓ Real component works correctly");
// Test 5: Test state management through traits
println!("\n5. Testing state management...");
// Test initial state
assert_eq!(rom.get_timing_state(), TimingState::Idle);
// Test state changes
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);
// Test reset
rom.perform_reset();
assert_eq!(rom.get_timing_state(), TimingState::Idle);
println!("✓ State management works correctly");
// Test 6: Test address handling
println!("\n6. Testing address handling...");
rom.set_address_high_nibble(Some(0x0F));
rom.set_address_low_nibble(Some(0x23));
rom.set_full_address_ready(true);
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
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);
println!("✓ Address handling works correctly");
println!("\n=== All core tests passed! ===");
println!("This demonstrates that the intel_400x common functionality is:");
println!("✓ Testable with meaningful test cases");
println!("✓ Working correctly with real components");
println!("✓ Providing valuable shared functionality");
println!("✓ Enabling proper state management");
println!("✓ Supporting timing and address operations");
}
#[test]
fn test_common_functionality() {
println!("=== Testing Common Intel 400x Functionality ===");
// Test that the common functionality works independently
// This tests the core logic without specific component implementations
// Test timing constants
assert!(TimingConstants::DEFAULT_ACCESS_TIME > std::time::Duration::from_nanos(0));
assert!(TimingConstants::FAST_ACCESS_TIME > std::time::Duration::from_nanos(0));
assert!(TimingConstants::FAST_ACCESS_TIME < TimingConstants::DEFAULT_ACCESS_TIME);
// Test state machine
let idle_state = TimingState::Idle;
let address_state = TimingState::AddressPhase;
let wait_state = TimingState::WaitLatency;
let drive_state = TimingState::DriveData;
assert!(idle_state.is_idle());
assert!(address_state.is_address_phase());
assert!(wait_state.is_waiting_latency());
assert!(drive_state.is_driving_data());
// Test address assembly
let test_cases = vec![
(0x00, 0x00, 0x0000),
(0x12, 0x34, 0x1234),
(0xFF, 0xFF, 0xFFFF),
];
for (high, low, expected) in test_cases {
let assembled = ((high as u16) << 4) | (low as u16);
assert_eq!(assembled, expected);
}
println!("✓ Common functionality works correctly");
}
#[test]
fn test_component_configuration() {
println!("=== Testing Component Configuration ===");
// Test different access time configurations
let fast_rom = Intel4001::new_with_access_time("FastROM".to_string(), 100);
let slow_rom = Intel4001::new_with_access_time("SlowROM".to_string(), 1000);
assert_eq!(fast_rom.get_access_time(), 100);
assert_eq!(slow_rom.get_access_time(), 1000);
// Test access time modification
let mut rom = Intel4001::new("ConfigTestROM".to_string());
assert_eq!(rom.get_access_time(), 500); // Default
rom.set_access_time(250);
assert_eq!(rom.get_access_time(), 250);
println!("✓ Component configuration works correctly");
}
#[test]
fn test_error_handling() {
println!("=== Testing Error Handling ===");
let mut rom = Intel4001::new("ErrorTestROM".to_string());
// Test invalid memory access
assert_eq!(rom.read_rom(0xFF), Some(0x00)); // Default value for unmapped memory
assert_eq!(rom.read_rom(0xFF), Some(0x00)); // Default value for unmapped memory
// Test invalid data loading
assert!(rom.load_rom_data(vec![0x12], 255).is_ok()); // Valid
assert!(rom.load_rom_data(vec![0x12], 256).is_err()); // Invalid - out of bounds
println!("✓ Error handling works correctly");
}
}