//! Comprehensive TDD tests for real data pipeline #![cfg(feature = "disabled_tests")] use crate::{PreprocessingError, Result, real_data_pipeline::*}; #[cfg(test)] mod tensor_tests { use super::*; #[test] fn test_tensor_creation() { let device = RealDevice::cpu(); let shape = RealShape::new(vec![2, 3]); let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; let tensor = RealTensor::from_data(data, shape, device).unwrap(); assert_eq!(tensor.shape().dims(), &[2, 3]); assert_eq!(tensor.numel(), 6); } #[test] fn test_tensor_zeros() { let device = RealDevice::cpu(); let shape = RealShape::new(vec![3, 4]); let tensor = RealTensor::zeros(&shape, &device).unwrap(); assert_eq!(tensor.shape().dims(), &[3, 4]); assert_eq!(tensor.numel(), 12); let data = tensor.to_vec().unwrap(); assert!(data.iter().all(|&x| x == 0.0)); } #[test] fn test_tensor_ones() { let device = RealDevice::cpu(); let shape = RealShape::new(vec![2, 2]); let tensor = RealTensor::ones(&shape, &device).unwrap(); let data = tensor.to_vec().unwrap(); assert!(data.iter().all(|&x| x == 1.0)); } #[test] fn test_tensor_randn() { let device = RealDevice::cpu(); let shape = RealShape::new(vec![100]); let tensor = RealTensor::randn(&shape, &device).unwrap(); let data = tensor.to_vec().unwrap(); // Check that we have normal distribution characteristics let mean: f32 = data.iter().sum::() / data.len() as f32; let variance: f32 = data.iter().map(|x| (x - mean).powi(2)).sum::() / data.len() as f32; // Should be close to N(0,1) assert!(mean.abs() < 0.3); // Allow some tolerance assert!(variance > 0.5 && variance < 2.0); } #[test] fn test_tensor_add() { let device = RealDevice::cpu(); let shape = RealShape::new(vec![2, 2]); let a = RealTensor::ones(&shape, &device).unwrap(); let b = RealTensor::ones(&shape, &device).unwrap(); let c = a.add(&b).unwrap(); let data = c.to_vec().unwrap(); assert!(data.iter().all(|&x| x == 2.0)); } #[test] fn test_tensor_sub() { let device = RealDevice::cpu(); let shape = RealShape::new(vec![2, 2]); let a = RealTensor::ones(&shape, &device).unwrap(); let b = RealTensor::ones(&shape, &device).unwrap(); let c = a.sub(&b).unwrap(); let data = c.to_vec().unwrap(); assert!(data.iter().all(|&x| x == 0.0)); } #[test] fn test_tensor_mul() { let device = RealDevice::cpu(); let shape = RealShape::new(vec![2, 2]); let a = RealTensor::from_data(vec![2.0, 3.0, 4.0, 5.0], shape.clone(), device.clone()).unwrap(); let b = RealTensor::from_data(vec![1.0, 2.0, 3.0, 4.0], shape, device).unwrap(); let c = a.mul(&b).unwrap(); let data = c.to_vec().unwrap(); assert_eq!(data, vec![2.0, 6.0, 12.0, 20.0]); } #[test] fn test_tensor_div() { let device = RealDevice::cpu(); let shape = RealShape::new(vec![2, 2]); let a = RealTensor::from_data(vec![4.0, 6.0, 8.0, 10.0], shape.clone(), device.clone()) .unwrap(); let b = RealTensor::from_data(vec![2.0, 2.0, 2.0, 2.0], shape, device).unwrap(); let c = a.div(&b).unwrap(); let data = c.to_vec().unwrap(); assert_eq!(data, vec![2.0, 3.0, 4.0, 5.0]); } #[test] fn test_tensor_matmul() { let device = RealDevice::cpu(); let a = RealTensor::from_data( vec![1.0, 2.0, 3.0, 4.0], RealShape::new(vec![2, 2]), device.clone(), ) .unwrap(); let b = RealTensor::from_data(vec![5.0, 6.0, 7.0, 8.0], RealShape::new(vec![2, 2]), device) .unwrap(); let c = a.matmul(&b).unwrap(); let data = c.to_vec().unwrap(); // [1 2] [5 6] [19 22] // [3 4] [7 8] = [43 50] assert_eq!(data, vec![19.0, 22.0, 43.0, 50.0]); } #[test] fn test_tensor_reshape() { let device = RealDevice::cpu(); let original_shape = RealShape::new(vec![2, 3]); let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; let tensor = RealTensor::from_data(data, original_shape, device).unwrap(); let reshaped = tensor.reshape(&[3, 2]).unwrap(); assert_eq!(reshaped.shape().dims(), &[3, 2]); assert_eq!(reshaped.numel(), 6); let reshaped_data = reshaped.to_vec().unwrap(); assert_eq!(reshaped_data, vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]); } #[test] fn test_tensor_transpose() { let device = RealDevice::cpu(); let shape = RealShape::new(vec![2, 3]); let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; let tensor = RealTensor::from_data(data, shape, device).unwrap(); let transposed = tensor.transpose().unwrap(); assert_eq!(transposed.shape().dims(), &[3, 2]); let transposed_data = transposed.to_vec().unwrap(); // Original: [1 2 3] Transposed: [1 4] // [4 5 6] [2 5] // [3 6] assert_eq!(transposed_data, vec![1.0, 4.0, 2.0, 5.0, 3.0, 6.0]); } #[test] fn test_tensor_mean() { let device = RealDevice::cpu(); let shape = RealShape::new(vec![2, 3]); let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; let tensor = RealTensor::from_data(data, shape, device).unwrap(); let mean = tensor.mean(None).unwrap(); let mean_data = mean.to_vec().unwrap(); assert_eq!(mean_data, vec![3.5]); // (1+2+3+4+5+6)/6 = 3.5 } #[test] fn test_tensor_std() { let device = RealDevice::cpu(); let shape = RealShape::new(vec![4]); let data = vec![1.0, 2.0, 3.0, 4.0]; let tensor = RealTensor::from_data(data, shape, device).unwrap(); let std = tensor.std(None).unwrap(); let std_data = std.to_vec().unwrap(); // Standard deviation of [1,2,3,4] ≈ 1.29 assert!((std_data[0] - 1.29).abs() < 0.1); } #[test] fn test_tensor_min_max() { let device = RealDevice::cpu(); let shape = RealShape::new(vec![3]); let data = vec![3.0, 1.0, 4.0]; let tensor = RealTensor::from_data(data, shape, device).unwrap(); let min = tensor.min(None).unwrap(); let max = tensor.max(None).unwrap(); assert_eq!(min.to_vec().unwrap(), vec![1.0]); assert_eq!(max.to_vec().unwrap(), vec![4.0]); } #[test] fn test_tensor_device_transfer() { let cpu = RealDevice::cpu(); let cuda = RealDevice::cuda(0); let shape = RealShape::new(vec![2, 2]); let tensor = RealTensor::ones(&shape, &cpu).unwrap(); // Transfer to CUDA (will fallback to CPU if no GPU) let gpu_tensor = tensor.to_device(&cuda).unwrap(); assert_eq!(gpu_tensor.device(), &cuda); // Transfer back to CPU let cpu_tensor = gpu_tensor.to_device(&cpu).unwrap(); assert_eq!(cpu_tensor.device(), &cpu); let data = cpu_tensor.to_vec().unwrap(); assert!(data.iter().all(|&x| x == 1.0)); } #[test] fn test_tensor_error_cases() { let device = RealDevice::cpu(); // Mismatched shapes for operations let a = RealTensor::ones(&RealShape::new(vec![2, 2]), &device).unwrap(); let b = RealTensor::ones(&RealShape::new(vec![3, 3]), &device).unwrap(); assert!(a.add(&b).is_err()); assert!(a.mul(&b).is_err()); // Invalid reshape let tensor = RealTensor::ones(&RealShape::new(vec![2, 3]), &device).unwrap(); assert!(tensor.reshape(&[2, 2]).is_err()); // 6 elements can't fit in 2x2 // Invalid matmul dimensions let a = RealTensor::ones(&RealShape::new(vec![2, 3]), &device).unwrap(); let b = RealTensor::ones(&RealShape::new(vec![2, 3]), &device).unwrap(); // Should be 3x? assert!(a.matmul(&b).is_err()); } } #[cfg(test)] mod data_loader_tests { use super::*; use std::path::PathBuf; #[tokio::test] async fn test_data_loader_creation() { let config = DataLoaderConfig::default(); let loader = RealDataLoader::new(config); assert_eq!(loader.config().batch_size, 32); assert!(!loader.config().shuffle); } #[tokio::test] async fn test_data_loader_from_memory() { let device = RealDevice::cpu(); // Create sample data let mut samples = Vec::new(); for i in 0..100 { let data = vec![i as f32; 10]; let tensor = RealTensor::from_data(data, RealShape::new(vec![10]), device.clone()).unwrap(); samples.push(tensor); } let mut config = DataLoaderConfig::default(); config.batch_size = 10; let mut loader = RealDataLoader::new(config); loader.load_from_memory(samples).await.unwrap(); assert_eq!(loader.len(), 100); assert_eq!(loader.num_batches(), 10); } #[tokio::test] async fn test_data_loader_iteration() { let device = RealDevice::cpu(); // Create sample data let mut samples = Vec::new(); for i in 0..20 { let data = vec![i as f32; 5]; let tensor = RealTensor::from_data(data, RealShape::new(vec![5]), device.clone()).unwrap(); samples.push(tensor); } let mut config = DataLoaderConfig::default(); config.batch_size = 5; let mut loader = RealDataLoader::new(config); loader.load_from_memory(samples).await.unwrap(); let mut batch_count = 0; while let Some(batch) = loader.next_batch().await.unwrap() { assert_eq!(batch.len(), 5); // batch_size batch_count += 1; } assert_eq!(batch_count, 4); // 20 samples / 5 batch_size = 4 batches } #[tokio::test] async fn test_data_loader_shuffle() { let device = RealDevice::cpu(); // Create sample data with unique identifiers let mut samples = Vec::new(); for i in 0..10 { let data = vec![i as f32; 1]; let tensor = RealTensor::from_data(data, RealShape::new(vec![1]), device.clone()).unwrap(); samples.push(tensor); } let mut config = DataLoaderConfig::default(); config.batch_size = 1; config.shuffle = true; let mut loader = RealDataLoader::new(config); loader.load_from_memory(samples).await.unwrap(); // Get first epoch let mut first_epoch = Vec::new(); while let Some(batch) = loader.next_batch().await.unwrap() { let data = batch[0].to_vec().unwrap(); first_epoch.push(data[0] as i32); } // Get second epoch let mut second_epoch = Vec::new(); while let Some(batch) = loader.next_batch().await.unwrap() { let data = batch[0].to_vec().unwrap(); second_epoch.push(data[0] as i32); } // Epochs should have same elements but potentially different order first_epoch.sort(); second_epoch.sort(); assert_eq!(first_epoch, second_epoch); // Note: Due to randomness, we can't guarantee order is different, // but this tests the shuffle functionality works } #[tokio::test] async fn test_data_loader_validation() { let device = RealDevice::cpu(); // Create valid data let mut samples = Vec::new(); for i in 0..10 { let data = vec![i as f32; 3]; let tensor = RealTensor::from_data(data, RealShape::new(vec![3]), device.clone()).unwrap(); samples.push(tensor); } let config = DataLoaderConfig::default(); let mut loader = RealDataLoader::new(config); loader.load_from_memory(samples).await.unwrap(); let validation_report = loader.validate_data().await.unwrap(); assert!(validation_report.is_valid); assert_eq!(validation_report.total_samples, 10); assert_eq!(validation_report.errors.len(), 0); } #[tokio::test] async fn test_data_loader_statistics() { let device = RealDevice::cpu(); // Create sample data let mut samples = Vec::new(); for i in 0..50 { let data = vec![i as f32; 2]; let tensor = RealTensor::from_data(data, RealShape::new(vec![2]), device.clone()).unwrap(); samples.push(tensor); } let mut config = DataLoaderConfig::default(); config.batch_size = 10; let mut loader = RealDataLoader::new(config); loader.load_from_memory(samples).await.unwrap(); // Process some batches for _ in 0..3 { let _ = loader.next_batch().await.unwrap(); } let stats = loader.statistics().await; assert_eq!(stats.total_samples, 50); assert_eq!(stats.batches_processed, 3); assert!(stats.processing_time_ms > 0.0); } #[tokio::test] async fn test_data_loader_error_cases() { let config = DataLoaderConfig::default(); let mut loader = RealDataLoader::new(config); // Try to get batch without loading data assert!(loader.next_batch().await.is_err()); // Try to validate without data assert!(loader.validate_data().await.is_err()); } } #[cfg(test)] mod device_tests { use super::*; #[test] fn test_device_creation() { let cpu = RealDevice::cpu(); assert_eq!(cpu, RealDevice::cuda(0).unwrap_or(Device::default())); let cuda = RealDevice::cuda(0); assert_eq!(cuda, RealDevice::Cuda(0)); } #[test] fn test_device_properties() { let cpu = RealDevice::cpu(); assert!(!cpu.is_cuda()); assert_eq!(cpu.device_id(), None); let cuda = RealDevice::cuda(1); assert!(cuda.is_cuda()); assert_eq!(cuda.device_id(), Some(1)); } #[test] fn test_device_display() { let cpu = RealDevice::cpu(); assert_eq!(format!("{}", cpu), "cpu"); let cuda = RealDevice::cuda(2); assert_eq!(format!("{}", cuda), "cuda:2"); } } #[cfg(test)] mod shape_tests { use super::*; #[test] fn test_shape_creation() { let shape = RealShape::new(vec![2, 3, 4]); assert_eq!(shape.dims(), &[2, 3, 4]); assert_eq!(shape.ndim(), 3); assert_eq!(shape.numel(), 24); } #[test] fn test_shape_properties() { let scalar_shape = RealShape::new(vec![]); assert!(scalar_shape.is_scalar()); assert!(!scalar_shape.is_vector()); assert!(!scalar_shape.is_matrix()); let vector_shape = RealShape::new(vec![5]); assert!(!vector_shape.is_scalar()); assert!(vector_shape.is_vector()); assert!(!vector_shape.is_matrix()); let matrix_shape = RealShape::new(vec![3, 4]); assert!(!matrix_shape.is_scalar()); assert!(!matrix_shape.is_vector()); assert!(matrix_shape.is_matrix()); } #[test] fn test_shape_validation() { let valid_shape = RealShape::new(vec![2, 3, 4]); assert!(valid_shape.validate().is_ok()); let empty_shape = RealShape::new(vec![2, 0, 4]); assert!(empty_shape.validate().is_err()); let zero_shape = RealShape::new(vec![]); assert!(zero_shape.validate().is_ok()); // Scalars are valid } #[test] fn test_shape_conversions() { let shape1 = RealShape::from(vec![1, 2, 3]); assert_eq!(shape1.dims(), &[1, 2, 3]); let shape2 = RealShape::from(&[4, 5, 6][..]); assert_eq!(shape2.dims(), &[4, 5, 6]); let shape3 = RealShape::from([7, 8, 9]); assert_eq!(shape3.dims(), &[7, 8, 9]); } } #[cfg(test)] mod integration_tests { use super::*; #[tokio::test] async fn test_tensor_data_loader_integration() { let device = RealDevice::cpu(); // Create tensors for training let mut training_data = Vec::new(); for i in 0..100 { let features = vec![i as f32, (i * 2) as f32, (i * 3) as f32]; let tensor = RealTensor::from_data(features, RealShape::new(vec![3]), device.clone()).unwrap(); training_data.push(tensor); } // Set up data loader let mut config = DataLoaderConfig::default(); config.batch_size = 10; config.shuffle = true; let mut loader = RealDataLoader::new(config); loader.load_from_memory(training_data).await.unwrap(); // Simulate training loop let mut total_processed = 0; while let Some(batch) = loader.next_batch().await.unwrap() { assert_eq!(batch.len(), 10); // Process each tensor in batch for tensor in batch { assert_eq!(tensor.shape().dims(), &[3]); let data = tensor.to_vec().unwrap(); assert_eq!(data.len(), 3); // Verify data relationships assert_eq!(data[1], data[0] * 2.0); assert_eq!(data[2], data[0] * 3.0); } total_processed += 10; } assert_eq!(total_processed, 100); } #[tokio::test] async fn test_tensor_operations_pipeline() { let device = RealDevice::cpu(); // Create input data let input_shape = RealShape::new(vec![4, 4]); let input_data: Vec = (0..16).map(|x| x as f32).collect(); let input = RealTensor::from_data(input_data, input_shape, device.clone()).unwrap(); // Apply sequence of operations (simulating a preprocessing pipeline) // 1. Normalize (subtract mean, divide by std) let mean = input.mean(None).unwrap(); let std = input.std(None).unwrap(); let mean_broadcast = RealTensor::from_data( vec![mean.to_vec().unwrap()[0]; 16], RealShape::new(vec![4, 4]), device.clone(), ) .unwrap(); let std_broadcast = RealTensor::from_data( vec![std.to_vec().unwrap()[0]; 16], RealShape::new(vec![4, 4]), device.clone(), ) .unwrap(); let normalized = input .sub(&mean_broadcast) .unwrap() .div(&std_broadcast) .unwrap(); // 2. Reshape to vector let flattened = normalized.reshape(&[16]).unwrap(); // 3. Apply linear transformation (matrix multiplication) let weights = RealTensor::randn(&RealShape::new(vec![16, 8]), &device).unwrap(); let features = weights .transpose() .unwrap() .matmul(&flattened.reshape(&[16, 1]).unwrap()) .unwrap(); // 4. Apply activation (ReLU approximation) let zero = RealTensor::zeros(&features.shape(), &device).unwrap(); let activated = features.max_elementwise(&zero).unwrap(); // Verify final shape and properties assert_eq!(activated.shape().dims(), &[8, 1]); let final_data = activated.to_vec().unwrap(); assert!(final_data.iter().all(|&x| x >= 0.0)); // ReLU ensures non-negative } #[tokio::test] async fn test_performance_benchmark() { let device = RealDevice::cpu(); // Create large tensors for performance testing let size = 1000; let shape = RealShape::new(vec![size, size]); let start = std::time::Instant::now(); let a = RealTensor::randn(&shape, &device).unwrap(); let b = RealTensor::randn(&shape, &device).unwrap(); let creation_time = start.elapsed(); let start = std::time::Instant::now(); let c = a.add(&b).unwrap(); let addition_time = start.elapsed(); let start = std::time::Instant::now(); let d = a.mul(&b).unwrap(); let multiplication_time = start.elapsed(); println!("Performance benchmark results:"); println!("Tensor creation ({}x{}): {:?}", size, size, creation_time); println!("Addition: {:?}", addition_time); println!("Multiplication: {:?}", multiplication_time); // Basic sanity checks assert_eq!(c.shape().dims(), &[size, size]); assert_eq!(d.shape().dims(), &[size, size]); assert!(creation_time.as_millis() < 5000); // Should complete within 5 seconds assert!(addition_time.as_millis() < 1000); assert!(multiplication_time.as_millis() < 1000); } }