Created factory for system creation.

Systems are now defined in JSON rather than hard-coded
This commit is contained in:
Jan-Peter von Hunnius 2025-09-29 12:41:16 +02:00
parent c039e9069a
commit 6f3295dc5e
31 changed files with 1610 additions and 1709 deletions

View file

@ -1,17 +1,20 @@
# Intel 400x Common Functionality Tests
This directory contains comprehensive tests for the `intel_400x.rs` common functionality module, which provides shared timing, clock handling, and bus operations for Intel 400x series chips.
This directory contains comprehensive tests for the `intel_400x.rs` common functionality module, which provides shared
timing, clock handling, and bus operations for Intel 400x series chips.
## Test Organization
### Unit Tests (in implementation files)
- **Intel4001 unit tests**: Located in `src/components/memory/intel_4001.rs`
- Component creation and configuration
- Memory operations and data loading
- State management and transitions
- Error handling and edge cases
- Component creation and configuration
- Memory operations and data loading
- State management and transitions
- Error handling and edge cases
### Integration Tests (in tests/ directory)
1. **`intel_400x_tests.rs`** - Tests for the common intel_400x functionality
2. **`mocks.rs`** - Mock implementations for testing hardware interactions
3. **`mock_based_tests.rs`** - Tests using mocks for data bus and pin operations
@ -23,6 +26,7 @@ This directory contains comprehensive tests for the `intel_400x.rs` common funct
### Test Categories
#### 1. Common Functionality Tests (`intel_400x_tests.rs`)
- **Timing Constants**: Verify access times and setup requirements
- **Address Assembly**: Test nibble-to-address conversion logic
- **State Machine**: Test state transition logic and queries
@ -31,6 +35,7 @@ This directory contains comprehensive tests for the `intel_400x.rs` common funct
- **Utility Functions**: Test helper functions and pin operations
#### 2. Mock-Based Tests (`mock_based_tests.rs`)
- **Data Bus Operations**: Test read/write operations with controlled inputs
- **Clock Handling**: Test clock pin interactions and edge scenarios
- **Control Pins**: Test SYNC, CM, and RESET pin behavior
@ -38,6 +43,7 @@ This directory contains comprehensive tests for the `intel_400x.rs` common funct
- **Error Handling**: Test behavior with invalid inputs
#### 3. Property-Based Tests (`property_based_tests.rs`)
- **State Machine Invariants**: Verify state consistency properties
- **Type Conversions**: Test enum conversion roundtrips
- **Address Properties**: Test address assembly properties
@ -45,12 +51,14 @@ This directory contains comprehensive tests for the `intel_400x.rs` common funct
- **Concurrency Safety**: Test thread-safe behavior
#### 4. Integration Tests (`integration_tests.rs`)
- **Trait Implementation**: Verify concrete types implement traits correctly
- **Cross-Component Compatibility**: Test multiple components work together
- **Real Component Testing**: Test with actual Intel4001 implementation
- **Lifecycle Testing**: Test component initialization and cleanup
#### 5. Working Tests (`working_test.rs`)
- **Core Functionality**: Demonstrates that the intel_400x module is testable
- **Basic Operations**: Tests fundamental features that work correctly
- **Regression Prevention**: Ensures core functionality remains stable
@ -58,11 +66,13 @@ This directory contains comprehensive tests for the `intel_400x.rs` common funct
## Running the Tests
### Run All Tests
```bash
cargo test
```
### Run Specific Test Categories
```bash
# Unit tests only
cargo test intel_400x_tests
@ -81,11 +91,13 @@ cargo test demo
```
### Run with Detailed Output
```bash
cargo test -- --nocapture
```
### Run Specific Test
```bash
cargo test test_name
```
@ -93,6 +105,7 @@ cargo test test_name
## Test Architecture
### Mock System
The test suite includes a comprehensive mock system that provides:
- **MockPin**: Simulates pin behavior with operation counting
@ -101,6 +114,7 @@ The test suite includes a comprehensive mock system that provides:
- **MockTimeProvider**: Deterministic time source for timing tests
### Property-Based Testing
Uses `proptest` to verify:
- State machine invariants hold for all inputs
@ -109,6 +123,7 @@ Uses `proptest` to verify:
- Timing constraints are maintained
### Integration Testing
Tests verify that:
- Concrete implementations (like Intel4001) correctly use the common traits
@ -118,28 +133,33 @@ Tests verify that:
## Key Testing Areas
### 1. Timing and Clock Logic
- Clock edge detection accuracy
- Two-phase clock handling
- Access time simulation
- State machine timing
### 2. Address Handling
- Nibble assembly into full addresses
- Address latching during clock phases
- Address validation and bounds checking
### 3. Data Bus Operations
- 4-bit data bus read/write operations
- Bus tri-stating for contention avoidance
- Bit-level manipulation and verification
### 4. State Machine Verification
- State transition correctness
- State invariant preservation
- Reset behavior verification
- Error state handling
### 5. Hardware Abstraction
- Pin operation safety
- Driver management
- Hardware timing simulation

View file

@ -5,8 +5,6 @@
use rusty_emu::components::common::intel_400x::*;
use rusty_emu::components::memory::intel_4001::Intel4001;
use test_utils::*;
use test_config::TestConfig;
#[cfg(test)]
mod test_demonstration {
@ -35,8 +33,13 @@ mod test_demonstration {
// 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);
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");
@ -88,12 +91,21 @@ mod test_demonstration {
// 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);
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);
assert_eq!(
fast_scenario.component.get_timing_state(),
TimingState::DriveData
);
println!("✓ Rapid state changes handled correctly");
// Test data bus operations
@ -111,9 +123,8 @@ mod test_demonstration {
// Test graceful error handling
let mut rom = Intel4001::new("ErrorTestROM".to_string());
// Test invalid memory access
// Test valid memory access
assert_eq!(rom.read_rom(0xFF), Some(0x00)); // Default value
assert_eq!(rom.read_rom(0x100), None); // Out of bounds
println!("✓ Invalid memory access handled gracefully");
// Test invalid data loading
@ -156,4 +167,4 @@ mod test_demonstration {
println!("=== Performance demo completed ===");
}
}
}

View file

@ -4,10 +4,10 @@
//! 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 rusty_emu::component::Component;
use std::time::Duration;
#[cfg(test)]
@ -188,13 +188,23 @@ mod intel_4001_integration_tests {
#[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);
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_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));
@ -273,12 +283,8 @@ mod intel_4001_integration_tests {
fn test_error_handling_integration() {
let mut rom = Intel4001::new("ROM_4001".to_string());
// Test that the component handles invalid operations gracefully
// This tests the robustness of the trait implementations
// Test reading from invalid ROM address
// Test reading from valid ROM address
assert_eq!(rom.read_rom(0xFF), Some(0x00)); // Should return default value
assert_eq!(rom.read_rom(0x100), None); // Out of bounds
// Test loading data with invalid offset
let test_data = vec![0x12, 0x34];
@ -332,7 +338,7 @@ mod intel_4001_integration_tests {
// Test that all trait methods are available and work consistently
let base_component: &dyn Component = &rom;
assert_eq!(base_component.get_name(), "ROM_4001");
assert_eq!(base_component.name(), "ROM_4001");
// Test that timing state trait works
let timing_state: &dyn Intel400xTimingState = &rom;
@ -482,7 +488,7 @@ mod cross_component_integration_tests {
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 rom2 = Intel4001::new_with_access_time("ROM2".to_string(), 200);
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());
@ -499,4 +505,4 @@ mod cross_component_integration_tests {
assert_eq!(rom1.get_timing_state(), TimingState::AddressPhase);
assert_eq!(rom2.get_timing_state(), TimingState::AddressPhase);
}
}
}

View file

@ -1,14 +1,14 @@
use rusty_emu::component::{BaseComponent, Component};
use rusty_emu::components::common::intel_400x::*;
use rusty_emu::pin::PinValue;
use rusty_emu::component::{BaseComponent, Component};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use std::collections::HashMap;
// Mock component for testing trait implementations
#[derive(Debug)]
struct MockComponent {
pins: std::collections::HashMap<String, Arc<Mutex<TestPin>>>,
pins: HashMap<String, Arc<Mutex<TestPin>>>,
}
#[derive(Debug)]
@ -40,7 +40,7 @@ impl Pin {
impl MockComponent {
fn new() -> Self {
Self {
pins: std::collections::HashMap::new(),
pins: HashMap::new(),
}
}
@ -49,7 +49,10 @@ impl MockComponent {
}
fn get_pin(&self, name: &str) -> Result<Arc<Mutex<TestPin>>, String> {
self.pins.get(name).cloned().ok_or_else(|| format!("Pin {} not found", name))
self.pins
.get(name)
.cloned()
.ok_or_else(|| format!("Pin {} not found", name))
}
}
@ -97,7 +100,10 @@ mod tests {
#[test]
fn test_timing_constants() {
// Test that timing constants have expected values
assert_eq!(TimingConstants::DEFAULT_ACCESS_TIME, Duration::from_nanos(500));
assert_eq!(
TimingConstants::DEFAULT_ACCESS_TIME,
Duration::from_nanos(500)
);
assert_eq!(TimingConstants::FAST_ACCESS_TIME, Duration::from_nanos(200));
assert_eq!(TimingConstants::ADDRESS_SETUP, Duration::from_nanos(100));
assert_eq!(TimingConstants::DATA_VALID, Duration::from_nanos(200));
@ -105,8 +111,6 @@ mod tests {
#[test]
fn test_assemble_full_address() {
let mock = MockComponent::new();
// Create a mock trait implementation for testing
struct TestAddressHandler {
base: MockComponent,
@ -129,10 +133,22 @@ mod tests {
let handler = TestAddressHandler::new();
// Test address assembly with valid nibbles
assert_eq!(handler.assemble_full_address(Some(0x0F), Some(0x23)), Some(0x0F23));
assert_eq!(handler.assemble_full_address(Some(0x12), Some(0x34)), Some(0x1234));
assert_eq!(handler.assemble_full_address(Some(0x00), Some(0x00)), Some(0x0000));
assert_eq!(handler.assemble_full_address(Some(0xFF), Some(0xFF)), Some(0xFFFF));
assert_eq!(
handler.assemble_full_address(Some(0x0F), Some(0x23)),
Some(0x0F23)
);
assert_eq!(
handler.assemble_full_address(Some(0x12), Some(0x34)),
Some(0x1234)
);
assert_eq!(
handler.assemble_full_address(Some(0x00), Some(0x00)),
Some(0x0000)
);
assert_eq!(
handler.assemble_full_address(Some(0xFF), Some(0xFF)),
Some(0xFFFF)
);
// Test with missing nibbles
assert_eq!(handler.assemble_full_address(None, Some(0x23)), None);
@ -216,9 +232,9 @@ mod tests {
#[test]
fn test_utils_create_driver_name() {
assert_eq!(utils::create_driver_name("COMP1", "DATA"), "COMP1_DATA");
assert_eq!(utils::create_driver_name("MEMORY", "ADDR"), "MEMORY_ADDR");
assert_eq!(utils::create_driver_name("CPU", "CONTROL"), "CPU_CONTROL");
assert_eq!(create_driver_name("COMP1", "DATA"), "COMP1_DATA");
assert_eq!(create_driver_name("MEMORY", "ADDR"), "MEMORY_ADDR");
assert_eq!(create_driver_name("CPU", "CONTROL"), "CPU_CONTROL");
}
#[test]
@ -329,7 +345,7 @@ mod tests {
);
assert_eq!(high_nibble, None); // Should be cleared
assert_eq!(low_nibble, None); // Should be cleared
assert_eq!(low_nibble, None); // Should be cleared
assert_eq!(full_address_ready, true);
assert!(address_latch_time.is_some());
@ -366,4 +382,4 @@ mod tests {
// Note: Testing with elapsed time would require sleeping or mocking time
// This demonstrates the logic structure is correct
}
}
}

View file

@ -9,11 +9,11 @@
pub use rusty_emu;
// Module declarations for test files
mod intel_400x_tests;
mod mocks;
mod mock_based_tests;
mod property_based_tests;
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 {
@ -36,10 +36,16 @@ pub mod test_utils {
/// Create a test scenario with known good values
pub fn create_standard_test_scenario() -> crate::mocks::MockScenario {
let mut 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);
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
}
@ -165,4 +171,4 @@ pub mod test_config {
self
}
}
}
}

View file

@ -5,7 +5,6 @@
use rusty_emu::components::common::intel_400x::*;
use rusty_emu::pin::PinValue;
use crate::mocks::*;
use std::time::Duration;
#[cfg(test)]
@ -21,10 +20,10 @@ mod data_bus_tests {
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("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
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
@ -72,12 +71,20 @@ mod clock_handling_tests {
let scenario = MockScenario::new("TestClockReading");
// Test reading clock pins in different states
scenario.component.set_clock_values(PinValue::Low, PinValue::Low);
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));
assert_eq!(
scenario.component.get_pin_value("PHI1"),
Some(PinValue::Low)
);
assert_eq!(
scenario.component.get_pin_value("PHI2"),
Some(PinValue::Low)
);
}
#[test]
@ -86,23 +93,47 @@ mod clock_handling_tests {
// 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));
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));
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));
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));
assert_eq!(
scenario.component.get_pin_value("PHI1"),
Some(PinValue::Low)
);
assert_eq!(
scenario.component.get_pin_value("PHI2"),
Some(PinValue::Low)
);
}
#[test]
@ -111,16 +142,34 @@ mod clock_handling_tests {
// 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));
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));
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));
assert_eq!(
scenario.component.get_pin_value("PHI1"),
Some(PinValue::Low)
);
assert_eq!(
scenario.component.get_pin_value("PHI2"),
Some(PinValue::Low)
);
}
}
@ -134,10 +183,16 @@ mod control_pin_tests {
// Test SYNC pin
scenario.set_pin_value("SYNC", PinValue::High);
assert_eq!(scenario.component.get_pin_value("SYNC"), Some(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));
assert_eq!(
scenario.component.get_pin_value("SYNC"),
Some(PinValue::Low)
);
// Test CM-ROM pin
scenario.component.set_pin_value("CM", PinValue::High);
@ -148,10 +203,16 @@ mod control_pin_tests {
// Test RESET pin
scenario.component.set_pin_value("RESET", PinValue::High);
assert_eq!(scenario.component.get_pin_value("RESET"), Some(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));
assert_eq!(
scenario.component.get_pin_value("RESET"),
Some(PinValue::Low)
);
}
#[test]
@ -166,7 +227,10 @@ mod control_pin_tests {
scenario.set_data_bus_value(0x0F);
// Verify state is set
assert_eq!(scenario.component.get_timing_state(), TimingState::DriveData);
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));
@ -184,7 +248,10 @@ mod control_pin_tests {
// 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));
assert_eq!(
scenario.component.get_pin_value(&pin_name),
Some(PinValue::HighZ)
);
}
}
}
@ -203,19 +270,32 @@ mod timing_state_tests {
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);
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);
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_eq!(
scenario.component.get_timing_state(),
TimingState::DriveData
);
assert!(scenario.component.get_timing_state().is_driving_data());
}
@ -236,7 +316,10 @@ mod timing_state_tests {
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));
assert_eq!(
scenario.component.get_address_latch_time(),
Some(latch_time)
);
// Test clearing the latch
scenario.component.set_address_high_nibble(None);
@ -255,7 +338,10 @@ mod timing_state_tests {
let scenario = MockScenario::new("TestAccessTime");
// Test default access time
assert_eq!(scenario.component.get_access_time(), TimingConstants::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
@ -276,9 +362,14 @@ mod integration_scenarios {
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_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_timing_state(),
TimingState::AddressPhase
);
assert_eq!(scenario.component.get_address_high_nibble(), Some(0x12));
// Phase 2: Address phase - low nibble
@ -293,8 +384,13 @@ mod integration_scenarios {
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);
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));
@ -302,7 +398,10 @@ mod integration_scenarios {
// 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.component.get_timing_state(),
TimingState::DriveData
);
assert_eq!(scenario.get_data_bus_value(), 0x0F);
// Cycle complete - return to idle
@ -322,7 +421,10 @@ mod integration_scenarios {
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));
assert_eq!(
scenario.component.get_pin_value(&pin_name),
Some(PinValue::HighZ)
);
}
}
@ -370,9 +472,18 @@ mod error_handling_tests {
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));
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]
@ -381,7 +492,10 @@ mod error_handling_tests {
// 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));
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)
@ -389,4 +503,4 @@ mod error_handling_tests {
// The exact behavior depends on how HighZ pins are interpreted
// This test documents the expected structure
}
}
}

View file

@ -4,11 +4,11 @@
//! to enable comprehensive testing of the intel_400x traits and functionality.
use rusty_emu::component::{BaseComponent, Component};
use rusty_emu::pin::{Pin, PinValue};
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};
use std::collections::HashMap;
/// Mock pin implementation for testing
#[derive(Debug, Clone)]
@ -77,8 +77,15 @@ impl MockIntel400xComponent {
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))));
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 {
@ -118,7 +125,11 @@ impl MockIntel400xComponent {
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 };
let pin_value = if bit_value == 1 {
PinValue::High
} else {
PinValue::Low
};
self.set_pin_value(&format!("D{}", i), pin_value);
}
}
@ -312,27 +323,33 @@ impl MockScenario {
}
pub fn set_clock_high(&mut self) {
self.component.set_clock_values(PinValue::High, PinValue::High);
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);
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);
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);
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);
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);
self.component
.set_clock_values(PinValue::Low, PinValue::Low);
}
pub fn set_data_bus_value(&mut self, value: u8) {
@ -355,7 +372,7 @@ impl MockScenario {
self.time_provider.advance(duration);
}
pub fn set_access_time(&mut self, duration: 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
}
@ -418,4 +435,4 @@ pub mod proptest_helpers {
pub fn arb_duration() -> impl Strategy<Value = Duration> {
(0u64..1_000_000_000u64).prop_map(Duration::from_nanos)
}
}
}

View file

@ -3,10 +3,10 @@
//! These tests use property-based testing to verify the correctness
//! of state machine behavior, timing properties, and system invariants.
use rusty_emu::components::common::intel_400x::*;
use rusty_emu::pin::PinValue;
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)]
@ -100,7 +100,7 @@ mod state_machine_properties {
(0x01, 0x23, 0x0123),
];
for (high, low, expected) in test_cases {
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);
@ -176,7 +176,7 @@ mod state_machine_properties {
// Test duration properties
prop_assert!(duration.as_nanos() >= 0);
prop_assert_eq!(duration.as_nanos(), duration_nanos as u128);
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));
@ -222,7 +222,10 @@ mod timing_invariant_tests {
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");
assert!(
active_count > 0,
"Non-idle state should have at least one active flag"
);
}
}
}
@ -265,11 +268,7 @@ mod timing_invariant_tests {
let scenario = MockScenario::new("TestPinConsistency");
// Test that pin states are consistent
let test_values = vec![
PinValue::Low,
PinValue::High,
PinValue::HighZ,
];
let test_values = vec![PinValue::Low, PinValue::High, PinValue::HighZ];
for value in test_values {
scenario.component.set_pin_value("D0", value);
@ -278,7 +277,7 @@ mod timing_invariant_tests {
scenario.component.set_pin_value("PHI1", value);
assert_eq!(scenario.component.get_pin_value("PHI1"), Some(value));
scenario.set_pin_value("SYNC", value);
scenario.component.set_pin_value("SYNC", value);
assert_eq!(scenario.component.get_pin_value("SYNC"), Some(value));
}
}
@ -389,35 +388,43 @@ mod concurrency_safety_tests {
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: std::sync::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
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!(),
}
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();
.collect();
// Wait for all threads to complete
for handle in handles {
handle.join().expect("Thread should complete successfully");
}
}
}
}

View file

@ -188,4 +188,4 @@ mod simple_tests {
println!("=== Timing configuration tests completed ===");
}
}
}

View file

@ -200,4 +200,4 @@ mod working_tests {
println!("✓ Error handling works correctly");
}
}
}