#![allow( clippy::tests_outside_test_module, clippy::unwrap_used, clippy::expect_used, clippy::indexing_slicing, clippy::str_to_string, clippy::string_to_string, clippy::assertions_on_result_states, clippy::assertions_on_constants, clippy::let_underscore_must_use, clippy::use_debug, clippy::doc_markdown, clippy::shadow_unrelated, clippy::shadow_reuse, clippy::similar_names, clippy::clone_on_copy, clippy::get_unwrap, clippy::modulo_arithmetic, clippy::integer_division, clippy::non_ascii_literal, clippy::useless_vec, clippy::useless_format, clippy::wildcard_enum_match_arm, clippy::manual_range_contains, clippy::const_is_empty, clippy::needless_range_loop, clippy::field_reassign_with_default, clippy::items_after_test_module, clippy::missing_const_for_fn, unused_imports, unused_variables, unused_mut, unused_assignments, unused_comparisons, unused_must_use, dead_code, )] //! Comprehensive tests for SIMD fallback paths and lock-free data structures //! //! This test suite ensures proper fallback behavior when CPU features are unavailable //! and tests edge cases in concurrent data structures. // use trading_engine::types::simd_optimizations::{CacheAlignedPriceArray, SIMDFinancialOps}; // use common::{Price, Quantity}; // Unused in current tests use std::sync::Arc; use std::thread; use trading_engine::lockfree::mpsc_queue::{AtomicCounter, MPSCQueue}; // ============================================================================ // SIMD Fallback Path Tests // ============================================================================ // Note: SIMD tests are disabled as simd_optimizations module is not implemented yet /* #[test] fn test_simd_portfolio_value_empty_arrays() { let positions: Vec = vec![]; let prices: Vec = vec![]; let result = SIMDFinancialOps::portfolio_value_simd(&positions, &prices); assert_eq!(result, Price::ZERO); } #[test] fn test_simd_portfolio_value_single_element() { let positions = vec![Quantity(100)]; let prices = vec![Price::from_f64(50000.0).unwrap()]; let result = SIMDFinancialOps::portfolio_value_simd(&positions, &prices); let expected = Price::from_f64(5_000_000.0).unwrap(); // Use approximate comparison for floating point assert!((result.to_f64() - expected.to_f64()).abs() < 0.01); } #[test] fn test_simd_portfolio_value_misaligned_length() { // Test with length that's not a multiple of 8 let positions = vec![ Quantity(100), Quantity(200), Quantity(150), Quantity(75), Quantity(300), ]; let prices = vec![ Price::from_f64(50000.0).unwrap(), Price::from_f64(3000.0).unwrap(), Price::from_f64(100.0).unwrap(), Price::from_f64(1.0).unwrap(), Price::from_f64(200.0).unwrap(), ]; let result = SIMDFinancialOps::portfolio_value_simd(&positions, &prices); // Manual calculation: 100*50000 + 200*3000 + 150*100 + 75*1 + 300*200 // = 5,000,000 + 600,000 + 15,000 + 75 + 60,000 = 5,675,075 let expected = Price::from_f64(5_675_075.0).unwrap(); assert!((result.to_f64() - expected.to_f64()).abs() < 0.01); } #[test] fn test_simd_portfolio_value_exactly_8_elements() { // Test with exactly 8 elements (one SIMD register worth) let positions = vec![ Quantity(100), Quantity(200), Quantity(150), Quantity(75), Quantity(300), Quantity(50), Quantity(125), Quantity(175), ]; let prices: Vec = vec![ Price::from_f64(1000.0).unwrap(), Price::from_f64(2000.0).unwrap(), Price::from_f64(3000.0).unwrap(), Price::from_f64(4000.0).unwrap(), Price::from_f64(5000.0).unwrap(), Price::from_f64(6000.0).unwrap(), Price::from_f64(7000.0).unwrap(), Price::from_f64(8000.0).unwrap(), ]; let result = SIMDFinancialOps::portfolio_value_simd(&positions, &prices); // Manual: 100*1000 + 200*2000 + 150*3000 + 75*4000 + 300*5000 + 50*6000 + 125*7000 + 175*8000 // = 100000 + 400000 + 450000 + 300000 + 1500000 + 300000 + 875000 + 1400000 = 5,325,000 let expected = Price::from_f64(5_325_000.0).unwrap(); assert!((result.to_f64() - expected.to_f64()).abs() < 0.01); } #[test] fn test_simd_portfolio_value_large_array() { // Test with a large array (multiple SIMD registers) let mut positions = Vec::new(); let mut prices = Vec::new(); for i in 0..1000 { positions.push(Quantity(i + 1)); prices.push(Price::from_f64((i + 1) as f64 * 100.0).unwrap()); } let result = SIMDFinancialOps::portfolio_value_simd(&positions, &prices); // Verify result is reasonable (should be sum of i * i * 100 for i=1 to 1000) // Sum formula: sum(i^2 * 100) = 100 * (n * (n+1) * (2n+1) / 6) // For n=1000: 100 * (1000 * 1001 * 2001 / 6) = 333,833,500 let expected = Price::from_f64(333_833_500.0).unwrap(); assert!((result.to_f64() - expected.to_f64()).abs() < 100.0); // Allow some FP error } #[test] fn test_simd_portfolio_value_zero_prices() { let positions = vec![Quantity(100), Quantity(200), Quantity(150)]; let prices = vec![ Price::from_f64(0.0).unwrap(), Price::from_f64(0.0).unwrap(), Price::from_f64(0.0).unwrap(), ]; let result = SIMDFinancialOps::portfolio_value_simd(&positions, &prices); assert_eq!(result, Price::ZERO); } #[test] fn test_simd_portfolio_value_negative_positions() { // Short positions (negative quantities) let positions = vec![Quantity(-100), Quantity(-200)]; let prices = vec![ Price::from_f64(50000.0).unwrap(), Price::from_f64(3000.0).unwrap(), ]; let result = SIMDFinancialOps::portfolio_value_simd(&positions, &prices); // Result should be negative: -100*50000 + -200*3000 = -5,600,000 let expected = Price::from_f64(-5_600_000.0).unwrap(); assert!((result.to_f64() - expected.to_f64()).abs() < 0.01); } #[test] fn test_cache_aligned_price_array() { let prices = [ Price::from_f64(1.0).unwrap(), Price::from_f64(2.0).unwrap(), Price::from_f64(3.0).unwrap(), Price::from_f64(4.0).unwrap(), Price::from_f64(5.0).unwrap(), Price::from_f64(6.0).unwrap(), Price::from_f64(7.0).unwrap(), Price::from_f64(8.0).unwrap(), ]; let aligned = CacheAlignedPriceArray::new(prices); let slice = aligned.as_slice(); assert_eq!(slice.len(), 8); assert_eq!(slice[0], prices[0]); assert_eq!(slice[7], prices[7]); } #[test] fn test_cache_aligned_price_array_mutation() { let prices = [Price::ZERO; 8]; let mut aligned = CacheAlignedPriceArray::new(prices); let mut_slice = aligned.as_mut_slice(); mut_slice[0] = Price::from_f64(100.0).unwrap(); mut_slice[7] = Price::from_f64(200.0).unwrap(); let slice = aligned.as_slice(); assert_eq!(slice[0], Price::from_f64(100.0).unwrap()); assert_eq!(slice[7], Price::from_f64(200.0).unwrap()); } */ // ============================================================================ // Lock-free MPSC Queue Edge Cases // ============================================================================ #[test] fn test_mpsc_queue_concurrent_push_pop() { let queue = Arc::new(MPSCQueue::::new()); let num_producers = 8; let items_per_producer = 10000; // Spawn multiple producers let mut producer_handles = Vec::new(); for producer_id in 0..num_producers { let queue_clone = Arc::clone(&queue); let handle = thread::spawn(move || { for i in 0..items_per_producer { let value = producer_id * items_per_producer + i; queue_clone.push(value); } }); producer_handles.push(handle); } // Consumer thread let queue_consumer = Arc::clone(&queue); let consumer_handle = thread::spawn(move || { let mut received = Vec::new(); let expected_total = (num_producers * items_per_producer) as usize; while received.len() < expected_total { if let Some(item) = queue_consumer.try_pop() { received.push(item); } else { thread::yield_now(); } } received }); // Wait for producers for handle in producer_handles { handle.join().expect("Producer thread failed"); } // Wait for consumer let received = consumer_handle.join().expect("Consumer thread failed"); // Verify all items received assert_eq!( received.len(), (num_producers * items_per_producer) as usize ); // Verify queue is empty assert!(queue.is_empty()); } #[test] fn test_mpsc_queue_pop_from_empty() { let queue = MPSCQueue::::new(); // Multiple pops from empty queue should return None assert_eq!(queue.try_pop(), None); assert_eq!(queue.try_pop(), None); assert_eq!(queue.try_pop(), None); assert!(queue.is_empty()); } #[test] fn test_mpsc_queue_push_pop_interleaved() { let queue = MPSCQueue::::new(); // Interleave push and pop operations queue.push(1); assert_eq!(queue.try_pop(), Some(1)); queue.push(2); queue.push(3); assert_eq!(queue.try_pop(), Some(2)); queue.push(4); assert_eq!(queue.try_pop(), Some(3)); assert_eq!(queue.try_pop(), Some(4)); assert!(queue.is_empty()); } #[test] fn test_mpsc_queue_rapid_push() { let queue = Arc::new(MPSCQueue::::new()); let queue_clone = Arc::clone(&queue); // Rapidly push 100,000 items let handle = thread::spawn(move || { for i in 0..100_000 { queue_clone.push(i); } }); handle.join().expect("Producer thread failed"); // Verify count assert_eq!(queue.len(), 100_000); // Pop all items and verify order let mut prev = None; let mut count = 0; while let Some(item) = queue.try_pop() { if let Some(p) = prev { assert_eq!(item, p + 1, "Items should be in order"); } prev = Some(item); count += 1; } assert_eq!(count, 100_000); } #[test] fn test_mpsc_queue_size_tracking() { let queue = MPSCQueue::::new(); assert_eq!(queue.len(), 0); assert!(queue.is_empty()); queue.push(1); assert_eq!(queue.len(), 1); assert!(!queue.is_empty()); queue.push(2); queue.push(3); assert_eq!(queue.len(), 3); queue.try_pop(); assert_eq!(queue.len(), 2); queue.try_pop(); queue.try_pop(); assert_eq!(queue.len(), 0); assert!(queue.is_empty()); } // ============================================================================ // Atomic Counter Edge Cases // ============================================================================ #[test] fn test_atomic_counter_custom_increment() { let counter = AtomicCounter::new_with(100, 5); assert_eq!(counter.get(), 100); assert_eq!(counter.next(), 100); assert_eq!(counter.next(), 105); assert_eq!(counter.next(), 110); assert_eq!(counter.get(), 115); } #[test] fn test_atomic_counter_reset() { let counter = AtomicCounter::new(); counter.next(); counter.next(); counter.next(); assert_eq!(counter.get(), 3); counter.reset(0); assert_eq!(counter.get(), 0); assert_eq!(counter.next(), 0); } #[test] fn test_atomic_counter_add() { let counter = AtomicCounter::new(); assert_eq!(counter.add(10), 0); // Returns old value assert_eq!(counter.get(), 10); assert_eq!(counter.add(5), 10); // Returns old value assert_eq!(counter.get(), 15); } #[test] fn test_atomic_counter_concurrent_increments() { let counter = Arc::new(AtomicCounter::new()); let num_threads = 16; let increments_per_thread = 10000; let mut handles = Vec::new(); for _ in 0..num_threads { let counter_clone: Arc = Arc::clone(&counter); let handle = thread::spawn(move || { for _ in 0..increments_per_thread { counter_clone.next(); } }); handles.push(handle); } for handle in handles { handle.join().expect("Thread failed"); } // Final value should be num_threads * increments_per_thread assert_eq!(counter.get(), (num_threads * increments_per_thread) as u64); } #[test] fn test_atomic_counter_wraparound() { let counter = AtomicCounter::new_with(u64::MAX - 5, 1); assert_eq!(counter.next(), u64::MAX - 5); assert_eq!(counter.next(), u64::MAX - 4); assert_eq!(counter.next(), u64::MAX - 3); assert_eq!(counter.next(), u64::MAX - 2); assert_eq!(counter.next(), u64::MAX - 1); assert_eq!(counter.next(), u64::MAX); // Next increment will wrap around to 0 assert_eq!(counter.next(), 0); } #[test] fn test_mpsc_queue_debug_output() { let queue = MPSCQueue::::new(); queue.push(1); queue.push(2); let debug_str = format!("{:?}", queue); assert!(debug_str.contains("MPSCQueue")); assert!(debug_str.contains("size")); } #[test] fn test_atomic_counter_debug_output() { let counter = AtomicCounter::new_with(42, 5); let debug_str = format!("{:?}", counter); assert!(debug_str.contains("AtomicCounter")); assert!(debug_str.contains("42")); } #[test] fn test_mpsc_queue_with_large_items() { let queue = MPSCQueue::>::new(); // Push large vectors for i in 0..100 { let vec = vec![i; 1000]; // 1000 elements each queue.push(vec); } // Pop and verify let mut count = 0; while let Some(vec) = queue.try_pop() { assert_eq!(vec.len(), 1000); assert_eq!(vec[0], count); count += 1; } assert_eq!(count, 100); }