rusty_emu/tests/gui_integration_tests.rs

589 lines
20 KiB
Rust

//! # GUI Integration Tests
//!
//! Comprehensive integration tests for GUI functionality including
//! system integration, thread safety, state synchronization, and
//! error handling scenarios.
#![cfg(test)]
use parking_lot::Mutex;
use rusty_emu::gui::GuiApp;
use rusty_emu::system_config::{ConfigurableSystem, SystemFactory};
use std::sync::Arc;
use std::thread;
use std::time::Duration;
/// Test utilities for GUI integration testing
mod gui_integration_test_utils {
use super::*;
/// Create a test system for integration testing
pub fn create_test_system() -> Arc<Mutex<ConfigurableSystem>> {
println!("Starting create_test_system");
let factory = SystemFactory::new();
Arc::new(Mutex::new(
factory.create_from_json("configs/mcs4_basic.json").unwrap(),
))
}
/// Create a mock system for testing error conditions
pub fn create_mock_system() -> Arc<Mutex<ConfigurableSystem>> {
// For now, return a basic system
// In a real implementation, this might create a mock or stub
create_test_system()
}
/// Simulate GUI state updates over time
/// Note: This function acquires the lock internally. Do NOT call it while holding the lock.
pub fn simulate_gui_updates(system: &Arc<Mutex<ConfigurableSystem>>) {
// Simulate the kind of updates that would happen in the GUI thread
let system_guard = system.lock();
{
// Simulate some system activity
let _ = system_guard.is_running(); // Access system state
}
}
/// Test helper to verify thread-safe access patterns
pub fn test_thread_safe_access(system: &Arc<Mutex<ConfigurableSystem>>) -> bool {
let system_clone = Arc::clone(system);
// Spawn a thread that accesses the system
let handle = thread::spawn(move || {
// Try to acquire the lock - return true if successful (parking_lot::lock always succeeds)
let _guard = system_clone.lock();
true
});
// Main thread also accesses the system - but release lock before joining to avoid deadlock
{
let _guard = system.lock();
// Access the system while holding the lock
}
let main_thread_result = true;
// Wait for spawned thread (lock is released, so no deadlock)
let spawned_thread_result = handle.join().unwrap_or(false);
// Both threads should be able to acquire the lock without deadlock
main_thread_result && spawned_thread_result
}
}
#[cfg(test)]
mod gui_system_integration_tests {
use super::*;
use gui_integration_test_utils::*;
#[test]
fn test_gui_system_integration() {
let system = create_test_system();
let system_clone = Arc::clone(&system);
// Test that GUI can be created with a system
let mut app = GuiApp::new_for_test();
// Set the system
app.set_system(system);
// Verify system is properly set
assert!(app.system.is_some());
assert!(app.gui_state.system_loaded);
// Test that system access works
let retrieved_system = app.get_system();
assert!(retrieved_system.is_some());
// Verify the retrieved system is the same as the original
assert!(Arc::ptr_eq(&retrieved_system.unwrap(), &system_clone));
}
#[test]
fn test_gui_state_synchronization() {
let system = create_test_system();
let mut app = GuiApp::new_for_test();
app.set_system(Arc::clone(&system));
// Test state update mechanism
// In a real implementation, this would test the actual update_from_system method
// For now, we verify the state structure is properly initialized
assert!(app.gui_state.system_loaded);
assert!(!app.gui_state.system_running); // Should start as not running
// Test that GUI state has proper structure for synchronization
assert_eq!(app.gui_state.memory_state.ram_contents.len(), 4);
assert_eq!(app.gui_state.register_state.index_registers.len(), 16); // Intel 4004 has 16 index registers (R0-R15)
}
#[test]
fn test_gui_thread_safety() {
let system = create_test_system();
// Test that multiple threads can safely access the system
let thread_safety_ok = test_thread_safe_access(&system);
assert!(thread_safety_ok, "System should support thread-safe access");
}
#[test]
fn test_gui_invalid_system_handling() {
// Test GUI behavior with invalid or corrupted system state
let system = create_mock_system();
let mut app = GuiApp::new_for_test();
// Test setting system that might be invalid
app.set_system(system);
// GUI should handle this gracefully
assert!(app.gui_state.system_loaded);
// Test error state handling
app.gui_state.set_error("System access error".to_string());
assert_eq!(app.gui_state.get_error(), Some("System access error"));
}
#[test]
fn test_gui_concurrent_state_access() {
let system = create_test_system();
// Test concurrent access patterns that would occur in GUI
let handles: Vec<_> = (0..5)
.map(|_| {
let system_clone = Arc::clone(&system);
thread::spawn(move || {
// Simulate GUI update pattern
for _ in 0..10 {
let _system_guard = system_clone.lock();
{
// Simulate some processing time
thread::sleep(Duration::from_micros(10));
}
}
})
})
.collect();
// Wait for all threads to complete
for handle in handles {
handle.join().expect("Thread should complete successfully");
}
// If we get here without deadlock, the test passes
assert!(true);
}
#[test]
fn test_gui_error_handling() {
let mut state = rusty_emu::gui::state::GuiState::new();
// Test error state management
let test_error = "Test error for GUI".to_string();
state.set_error(test_error.clone());
assert_eq!(state.get_error(), Some(test_error.as_str()));
// Test error clearing
state.clear_error();
assert!(state.get_error().is_none());
// Test multiple error scenarios
state.set_error("First error".to_string());
state.set_error("Second error".to_string());
assert_eq!(state.get_error(), Some("Second error"));
}
#[test]
fn test_gui_system_state_polling() {
let system = create_test_system();
// Test the polling mechanism that GUI would use
for i in 0..10 {
{
let system_guard = system.lock();
let is_running = system_guard.is_running();
// Simulate state updates that would happen in GUI
// In a real implementation, this would update GUI state
if i == 5 {
// Simulate state change at midpoint
assert!(is_running || !is_running); // Just verify we can read state
}
} // system_guard is dropped here
// Small delay to simulate GUI update interval
thread::sleep(Duration::from_micros(100));
}
}
#[test]
fn test_gui_memory_state_integration() {
let system = create_test_system();
// Test memory state structure for GUI integration
// In a real implementation, this would test actual memory access
{
let system_guard = system.lock();
// Test that system provides memory information
let _ = system_guard.get_system_info(); // Access system info
} // system_guard is dropped here
// Verify GUI state can hold memory data
let gui_state = rusty_emu::gui::state::GuiState::new();
assert_eq!(gui_state.memory_state.ram_contents.len(), 4);
for bank in &gui_state.memory_state.ram_contents {
assert_eq!(bank.len(), 4);
}
}
#[test]
fn test_gui_register_state_integration() {
let system = create_test_system();
// Test register state structure for GUI integration
{
let _system_guard = system.lock();
// In a real implementation, this would access actual CPU registers
// For now, we test the GUI state structure
}
let gui_state = rusty_emu::gui::state::GuiState::new();
// Verify register state structure
assert_eq!(gui_state.register_state.accumulator, 0);
assert!(!gui_state.register_state.carry_flag);
assert_eq!(gui_state.register_state.program_counter, 0);
assert_eq!(gui_state.register_state.index_registers.len(), 16); // Intel 4004 has 16 index registers (R0-R15)
assert_eq!(gui_state.register_state.stack_pointer, 0);
}
#[test]
fn test_gui_component_state_integration() {
let system = create_test_system();
// Test component state monitoring for GUI
{
let system_guard = system.lock();
let components = system_guard.get_components();
// Test that components are accessible (GUI would monitor these)
assert!(!components.is_empty());
} // system_guard is dropped here
// Test GUI state structure for component monitoring
let gui_state = rusty_emu::gui::state::GuiState::new();
// Verify component state structure
assert!(!gui_state.component_states.cpu_running);
assert!(!gui_state.component_states.ram_running);
assert!(!gui_state.component_states.rom_running);
assert!(!gui_state.component_states.clock_running);
}
#[test]
fn test_gui_system_compatibility() {
// Test that GUI works with different system configurations
let system = create_test_system();
// Test GUI creation and system integration
let mut app = GuiApp::new_for_test();
app.set_system(system);
// Verify compatibility
assert!(app.gui_state.system_loaded);
assert!(app.get_system().is_some());
// Test that GUI state is properly initialized for the system
assert_eq!(app.gui_state.memory_state.ram_contents.len(), 4);
assert_eq!(app.gui_state.register_state.index_registers.len(), 16); // Intel 4004 has 16 index registers (R0-R15)
}
#[test]
fn test_gui_performance_characteristics() {
println!("Starting test_gui_performance_characteristics");
let system = create_test_system();
// Test GUI update performance characteristics with timeout protection
let start_time = std::time::Instant::now();
let timeout = Duration::from_secs(5); // 5 second timeout
for _ in 0..100 {
// Check for timeout to prevent test from hanging indefinitely
if start_time.elapsed() > timeout {
panic!("Test timed out after {:?} - possible deadlock", timeout);
}
// simulate_gui_updates acquires its own lock - do not hold lock while calling
simulate_gui_updates(&system);
}
let elapsed = start_time.elapsed();
// GUI updates should be reasonably fast (less than 1ms per update on average)
assert!(
elapsed.as_millis() < 100,
"GUI updates should be performant"
);
}
#[test]
fn test_gui_error_recovery() {
let mut state = rusty_emu::gui::state::GuiState::new();
// Test error state and recovery
state.set_error("Simulated error".to_string());
assert_eq!(state.get_error(), Some("Simulated error"));
// Test recovery by clearing error
state.clear_error();
assert!(state.get_error().is_none());
// Test that system can continue after error recovery
state.system_running = true;
assert!(state.system_running);
}
#[test]
fn test_gui_state_consistency() {
let system = create_test_system();
// Test that GUI state remains consistent during updates
let mut app = GuiApp::new_for_test();
app.set_system(Arc::clone(&system));
// Simulate multiple state updates
for i in 0..10 {
if let Some(_sys) = app.get_system() {
// In a real implementation, this would call update_from_system
// For now, we verify the state structure remains consistent
assert_eq!(app.gui_state.memory_state.ram_contents.len(), 4);
assert_eq!(app.gui_state.register_state.index_registers.len(), 16); // Intel 4004 has 16 index registers (R0-R15)
// Simulate state change
app.gui_state.cycle_count = i;
}
}
// Verify final state is consistent
assert_eq!(app.gui_state.cycle_count, 9);
assert!(app.gui_state.system_loaded);
}
}
#[cfg(test)]
mod gui_error_handling_tests {
use super::*;
use gui_integration_test_utils::*;
#[test]
fn test_gui_invalid_system_handling() {
// Test GUI behavior with invalid or corrupted system state
let system = create_mock_system();
let mut app = GuiApp::new_for_test();
// Test setting system that might be invalid
app.set_system(system);
// GUI should handle this gracefully
assert!(app.gui_state.system_loaded);
// Test error state handling
app.gui_state.set_error("System access error".to_string());
assert_eq!(app.gui_state.get_error(), Some("System access error"));
}
#[test]
fn test_gui_concurrent_error_handling() {
let mut state = rusty_emu::gui::state::GuiState::new();
// Test error handling under concurrent access
let handles: Vec<_> = (0..3)
.map(|i| {
let mut state_clone = state.clone();
thread::spawn(move || {
state_clone.set_error(format!("Error from thread {}", i));
thread::sleep(Duration::from_micros(10));
state_clone.get_error().map(|s| s.to_string())
})
})
.collect();
// Main thread also sets errors
state.set_error("Main thread error".to_string());
// Wait for threads
let results: Vec<_> = handles.into_iter().map(|h| h.join().unwrap()).collect();
// All threads should complete without panic
assert_eq!(results.len(), 3);
for result in results {
assert!(result.is_some());
}
}
#[test]
fn test_gui_system_lock_timeout() {
let system = create_test_system();
// Test behavior when system lock might timeout or fail
// This simulates scenarios where the GUI can't immediately access the system
// Hold the lock for a short time to simulate contention
let system_clone = Arc::clone(&system);
let lock_handle = thread::spawn(move || {
let _system_guard = system_clone.lock();
{
thread::sleep(Duration::from_millis(10));
}
});
// Try to access from main thread (should not deadlock)
let access_result = system.try_lock().is_some();
lock_handle.join().unwrap();
// Should be able to access after the lock is released
assert!(access_result || system.try_lock().is_some());
}
#[test]
fn test_gui_state_corruption_recovery() {
let mut state = rusty_emu::gui::state::GuiState::new();
// Simulate state corruption and recovery
state.memory_state.ram_contents[0][0] = 255; // Invalid value
state.register_state.accumulator = 255; // Invalid value
// Test recovery by resetting state
state.memory_state.ram_contents = vec![[0; 4]; 4];
state.register_state.accumulator = 0;
// Verify recovery
assert_eq!(state.memory_state.ram_contents[0][0], 0);
assert_eq!(state.register_state.accumulator, 0);
}
}
#[cfg(test)]
mod gui_real_time_tests {
use super::*;
use gui_integration_test_utils::*;
#[test]
fn test_gui_real_time_state_updates() {
let system = create_test_system();
// Test real-time state update patterns
let mut update_count = 0;
let mut last_cycle_count = 0;
for i in 0..20 {
{
let _system_guard = system.lock();
// Simulate GUI state polling
update_count += 1;
// Simulate cycle count updates (would come from system in real implementation)
if i > 0 {
// In real implementation, this would be: gui_state.cycle_count = system.get_cycle_count()
last_cycle_count = i; // Simulate increasing cycle count
}
} // _system_guard is dropped here
thread::sleep(Duration::from_micros(50)); // Simulate GUI update interval
}
assert_eq!(update_count, 20);
assert_eq!(last_cycle_count, 19);
}
#[test]
fn test_gui_state_update_frequency() {
let system = create_test_system();
// Test that GUI can handle high-frequency state updates with timeout protection
let start_time = std::time::Instant::now();
let timeout = Duration::from_secs(5); // 5 second timeout
for _ in 0..50 {
// Check for timeout to prevent test from hanging indefinitely
if start_time.elapsed() > timeout {
panic!("Test timed out after {:?} - possible deadlock", timeout);
}
// simulate_gui_updates acquires its own lock - do not hold lock while calling
simulate_gui_updates(&system);
}
let elapsed = start_time.elapsed();
// Should complete reasonably quickly (indicating good performance)
assert!(
elapsed.as_millis() < 500,
"High-frequency updates should be performant"
);
}
#[test]
fn test_gui_memory_update_synchronization() {
// Test memory state synchronization between system and GUI
let system = create_test_system();
// In a real implementation, this would test actual memory synchronization
// For now, we test the data structures are compatible
{
let _system_guard = system.lock();
// Test memory data structure compatibility
}
let mut gui_state = rusty_emu::gui::state::GuiState::new();
// Verify memory layout matches expected Intel 4002 structure
assert_eq!(gui_state.memory_state.ram_contents.len(), 4); // 4 banks
for bank in &gui_state.memory_state.ram_contents {
assert_eq!(bank.len(), 4); // 4 bytes per bank
}
// Test bank selection bounds
gui_state.memory_state.selected_bank = 0;
gui_state.memory_state.selected_bank = 3; // Should be valid
}
#[test]
fn test_gui_register_update_synchronization() {
// Test register state synchronization between system and GUI
let system = create_test_system();
{
let _system_guard = system.lock();
}
let mut gui_state = rusty_emu::gui::state::GuiState::new();
// Verify register layout matches Intel 4004 structure
assert_eq!(gui_state.register_state.index_registers.len(), 16); // Intel 4004 has 16 index registers (R0-R15)
// Test register value bounds
gui_state.register_state.accumulator = 0x0F; // 4-bit value (0-15)
gui_state.register_state.carry_flag = true;
// Test program counter bounds (12-bit address)
gui_state.register_state.program_counter = 0xFFF; // Maximum 12-bit value
// Test stack pointer bounds (4-bit value)
gui_state.register_state.stack_pointer = 0x0F; // Maximum 4-bit value
}
}