#![allow( clippy::assertions_on_constants, clippy::assertions_on_result_states, clippy::clone_on_copy, clippy::decimal_literal_representation, clippy::doc_markdown, clippy::empty_line_after_doc_comments, clippy::field_reassign_with_default, clippy::get_unwrap, clippy::identity_op, clippy::inconsistent_digit_grouping, clippy::indexing_slicing, clippy::integer_division, clippy::len_zero, clippy::let_underscore_must_use, clippy::manual_div_ceil, clippy::manual_let_else, clippy::manual_range_contains, clippy::modulo_arithmetic, clippy::needless_range_loop, clippy::non_ascii_literal, clippy::redundant_clone, clippy::shadow_reuse, clippy::shadow_same, clippy::shadow_unrelated, clippy::single_match_else, clippy::str_to_string, clippy::string_slice, clippy::tests_outside_test_module, clippy::too_many_lines, clippy::unnecessary_wraps, clippy::unseparated_literal_suffix, clippy::use_debug, clippy::useless_vec, clippy::wildcard_enum_match_arm, clippy::else_if_without_else, clippy::expect_used, clippy::missing_const_for_fn, clippy::similar_names, clippy::type_complexity, clippy::collapsible_else_if, clippy::doc_lazy_continuation, clippy::items_after_test_module, clippy::map_clone, clippy::multiple_unsafe_ops_per_block, clippy::unwrap_or_default, clippy::assign_op_pattern, clippy::needless_borrow, clippy::println_empty_string, clippy::unnecessary_cast, clippy::used_underscore_binding, clippy::create_dir, clippy::implicit_saturating_sub, clippy::exit, clippy::expect_fun_call, clippy::too_many_arguments, clippy::unnecessary_map_or, clippy::unwrap_used, dead_code, unused_imports, unused_variables, clippy::cloned_ref_to_slice_refs, clippy::neg_multiply, clippy::while_let_loop, clippy::bool_assert_comparison, clippy::excessive_precision, clippy::trivially_copy_pass_by_ref, clippy::op_ref, clippy::redundant_closure, clippy::unnecessary_lazy_evaluations, clippy::if_then_some_else_none, clippy::unnecessary_to_owned, clippy::single_component_path_imports, )] //! Comprehensive ML model validation tests //! Target: 95%+ coverage for ML model validation and error handling #![allow(unused_crate_dependencies)] #[cfg(test)] mod model_validation_tests { use super::*; // ======================================================================== // Model Input Validation Tests // ======================================================================== #[test] fn test_validate_input_shape_correct() { let input = vec![vec![1.0, 2.0, 3.0], vec![4.0, 5.0, 6.0]]; let expected_shape = (2, 3); assert!(validate_input_shape(&input, expected_shape)); } #[test] fn test_validate_input_shape_incorrect_rows() { let input = vec![vec![1.0, 2.0, 3.0]]; // Only 1 row let expected_shape = (2, 3); // Expecting 2 rows assert!(!validate_input_shape(&input, expected_shape)); } #[test] fn test_validate_input_shape_incorrect_columns() { let input = vec![vec![1.0, 2.0], vec![3.0, 4.0]]; // Only 2 columns let expected_shape = (2, 3); // Expecting 3 columns assert!(!validate_input_shape(&input, expected_shape)); } #[test] fn test_validate_input_shape_empty_input() { let input: Vec> = vec![]; let expected_shape = (2, 3); assert!(!validate_input_shape(&input, expected_shape)); } #[test] fn test_validate_input_shape_jagged_array() { let input = vec![vec![1.0, 2.0, 3.0], vec![4.0, 5.0]]; // Inconsistent columns let expected_shape = (2, 3); assert!(!validate_input_shape(&input, expected_shape)); } // ======================================================================== // Model Output Validation Tests // ======================================================================== #[test] fn test_validate_output_range_within_bounds() { let output = vec![0.3, 0.5, 0.7]; let min_val = 0.0; let max_val = 1.0; assert!(validate_output_range(&output, min_val, max_val)); } #[test] fn test_validate_output_range_exceeds_max() { let output = vec![0.3, 1.5, 0.7]; // 1.5 exceeds max let min_val = 0.0; let max_val = 1.0; assert!(!validate_output_range(&output, min_val, max_val)); } #[test] fn test_validate_output_range_below_min() { let output = vec![0.3, -0.5, 0.7]; // -0.5 below min let min_val = 0.0; let max_val = 1.0; assert!(!validate_output_range(&output, min_val, max_val)); } #[test] fn test_validate_output_range_with_nan() { let output = vec![0.3, f64::NAN, 0.7]; let min_val = 0.0; let max_val = 1.0; assert!(!validate_output_range(&output, min_val, max_val)); } #[test] fn test_validate_output_range_with_infinity() { let output = vec![0.3, f64::INFINITY, 0.7]; let min_val = 0.0; let max_val = 1.0; assert!(!validate_output_range(&output, min_val, max_val)); } // ======================================================================== // Prediction Probability Validation Tests // ======================================================================== #[test] fn test_validate_probabilities_sum_to_one() { let probabilities = vec![0.2, 0.3, 0.5]; assert!(validate_probabilities(&probabilities)); } #[test] fn test_validate_probabilities_do_not_sum_to_one() { let probabilities = vec![0.2, 0.3, 0.3]; // Sum = 0.8 assert!(!validate_probabilities(&probabilities)); } #[test] fn test_validate_probabilities_negative_value() { let probabilities = vec![0.5, -0.2, 0.7]; assert!(!validate_probabilities(&probabilities)); } #[test] fn test_validate_probabilities_exceeds_one() { let probabilities = vec![0.5, 0.3, 0.5]; // Sum = 1.3 assert!(!validate_probabilities(&probabilities)); } #[test] fn test_validate_probabilities_empty() { let probabilities: Vec = vec![]; assert!(!validate_probabilities(&probabilities)); } #[test] fn test_validate_probabilities_single_value() { let probabilities = vec![1.0]; assert!(validate_probabilities(&probabilities)); } #[test] fn test_validate_probabilities_with_rounding_tolerance() { let probabilities = vec![0.3333, 0.3333, 0.3334]; // Sum = 1.0 with rounding assert!(validate_probabilities_with_tolerance(&probabilities, 0.001)); } // ======================================================================== // Feature Scaling Validation Tests // ======================================================================== #[test] fn test_validate_normalized_features() { let features = vec![0.5, -0.3, 0.8, -0.1]; assert!(validate_normalized_features(&features, -1.0, 1.0)); } #[test] fn test_validate_normalized_features_out_of_range() { let features = vec![0.5, -0.3, 1.5, -0.1]; // 1.5 out of range assert!(!validate_normalized_features(&features, -1.0, 1.0)); } #[test] fn test_validate_standardized_features() { let features = vec![0.5, -1.2, 2.1, -0.8]; // Most values should be within 3 standard deviations let mean = features.iter().sum::() / features.len() as f64; let std_dev = (features.iter().map(|x| (x - mean).powi(2)).sum::() / features.len() as f64) .sqrt(); for f in &features { assert!((*f - mean).abs() <= 3.0 * std_dev + 0.1); } } // ======================================================================== // Model Confidence Validation Tests // ======================================================================== #[test] fn test_validate_confidence_score_valid() { let confidence = 0.85; assert!(validate_confidence_score(confidence)); } #[test] fn test_validate_confidence_score_negative() { let confidence = -0.1; assert!(!validate_confidence_score(confidence)); } #[test] fn test_validate_confidence_score_exceeds_one() { let confidence = 1.5; assert!(!validate_confidence_score(confidence)); } #[test] fn test_validate_confidence_score_boundary_values() { assert!(validate_confidence_score(0.0)); assert!(validate_confidence_score(1.0)); } #[test] fn test_confidence_threshold_validation() { let confidence = 0.75; let threshold = 0.70; assert!(confidence >= threshold); let low_confidence = 0.65; assert!(low_confidence < threshold); } // ======================================================================== // Batch Processing Validation Tests // ======================================================================== #[test] fn test_validate_batch_size_valid() { let batch_size = 32; let min_batch = 1; let max_batch = 128; assert!(validate_batch_size(batch_size, min_batch, max_batch)); } #[test] fn test_validate_batch_size_too_small() { let batch_size = 0; let min_batch = 1; let max_batch = 128; assert!(!validate_batch_size(batch_size, min_batch, max_batch)); } #[test] fn test_validate_batch_size_too_large() { let batch_size = 256; let min_batch = 1; let max_batch = 128; assert!(!validate_batch_size(batch_size, min_batch, max_batch)); } // ======================================================================== // Temporal Consistency Validation Tests // ======================================================================== #[test] fn test_validate_temporal_sequence_valid() { let timestamps = vec![100, 101, 102, 103, 104]; assert!(validate_temporal_sequence(×tamps)); } #[test] fn test_validate_temporal_sequence_out_of_order() { let timestamps = vec![100, 102, 101, 103, 104]; // 102 before 101 assert!(!validate_temporal_sequence(×tamps)); } #[test] fn test_validate_temporal_sequence_duplicates() { let timestamps = vec![100, 101, 101, 102, 103]; // Duplicate 101 // Depending on requirements, duplicates might be allowed // This test assumes they are not assert!(!validate_temporal_sequence_strict(×tamps)); } #[test] fn test_validate_temporal_sequence_gaps() { let timestamps = vec![100, 101, 105, 106]; // Gap between 101 and 105 let max_gap = 2; assert!(!validate_temporal_gaps(×tamps, max_gap)); } // ======================================================================== // Feature Importance Validation Tests // ======================================================================== #[test] fn test_validate_feature_importance_sum() { let importance = vec![0.3, 0.25, 0.2, 0.15, 0.1]; assert!(validate_feature_importance(&importance)); } #[test] fn test_validate_feature_importance_invalid_sum() { let importance = vec![0.3, 0.25, 0.2]; // Sum = 0.75 assert!(!validate_feature_importance(&importance)); } #[test] fn test_validate_feature_importance_negative() { let importance = vec![0.5, -0.1, 0.6]; assert!(!validate_feature_importance(&importance)); } // ======================================================================== // Model Metadata Validation Tests // ======================================================================== #[test] fn test_validate_model_version_valid() { let version = "1.2.3"; assert!(validate_semantic_version(version)); } #[test] fn test_validate_model_version_invalid() { let version = "1.2"; assert!(!validate_semantic_version(version)); let version_invalid = "abc"; assert!(!validate_semantic_version(version_invalid)); } #[test] fn test_validate_model_name_valid() { let name = "mamba2_v1"; assert!(validate_model_name(name)); } #[test] fn test_validate_model_name_invalid() { let empty_name = ""; assert!(!validate_model_name(empty_name)); let special_chars = "model@#$"; assert!(!validate_model_name(special_chars)); } // ======================================================================== // Training Hyperparameter Validation Tests // ======================================================================== #[test] fn test_validate_learning_rate_valid() { let lr = 0.001; assert!(validate_learning_rate(lr)); } #[test] fn test_validate_learning_rate_too_high() { let lr = 1.5; // Too high assert!(!validate_learning_rate(lr)); } #[test] fn test_validate_learning_rate_negative() { let lr = -0.01; assert!(!validate_learning_rate(lr)); } #[test] fn test_validate_learning_rate_zero() { let lr = 0.0; assert!(!validate_learning_rate(lr)); } #[test] fn test_validate_epochs_valid() { let epochs = 100; assert!(validate_epochs(epochs)); } #[test] fn test_validate_epochs_zero() { let epochs = 0; assert!(!validate_epochs(epochs)); } #[test] fn test_validate_epochs_excessive() { let epochs = 100000; // Potentially too many let max_epochs = 10000; assert!(epochs > max_epochs); } // ======================================================================== // Edge Case Tests // ======================================================================== #[test] fn test_validate_with_extreme_values() { let extreme = vec![f64::MIN, f64::MAX]; // Should handle extreme but finite values assert!(extreme.iter().all(|x| x.is_finite())); } #[test] fn test_validate_with_denormalized_numbers() { let denorm = f64::MIN_POSITIVE / 2.0; // Denormalized number assert!(denorm > 0.0); assert!(denorm.is_finite()); } #[test] fn test_validate_empty_batch() { let batch: Vec> = vec![]; assert!(batch.is_empty()); // Should reject empty batches } #[test] fn test_validate_single_sample_batch() { let batch = vec![vec![1.0, 2.0, 3.0]]; assert_eq!(batch.len(), 1); // Should handle single sample batches } } // Helper validation functions fn validate_input_shape(input: &[Vec], expected: (usize, usize)) -> bool { let (expected_rows, expected_cols) = expected; if input.len() != expected_rows { return false; } input.iter().all(|row| row.len() == expected_cols) } fn validate_output_range(output: &[f64], min: f64, max: f64) -> bool { output .iter() .all(|&x| x.is_finite() && x >= min && x <= max) } fn validate_probabilities(probs: &[f64]) -> bool { if probs.is_empty() { return false; } let sum: f64 = probs.iter().sum(); let all_valid = probs.iter().all(|&p| p >= 0.0 && p <= 1.0 && p.is_finite()); all_valid && (sum - 1.0).abs() < 1e-6 } fn validate_probabilities_with_tolerance(probs: &[f64], tolerance: f64) -> bool { if probs.is_empty() { return false; } let sum: f64 = probs.iter().sum(); let all_valid = probs.iter().all(|&p| p >= 0.0 && p <= 1.0 && p.is_finite()); all_valid && (sum - 1.0).abs() < tolerance } fn validate_normalized_features(features: &[f64], min: f64, max: f64) -> bool { features .iter() .all(|&f| f >= min && f <= max && f.is_finite()) } fn validate_confidence_score(confidence: f64) -> bool { confidence >= 0.0 && confidence <= 1.0 && confidence.is_finite() } fn validate_batch_size(size: usize, min: usize, max: usize) -> bool { size >= min && size <= max } fn validate_temporal_sequence(timestamps: &[u64]) -> bool { timestamps.windows(2).all(|w| w[0] <= w[1]) } fn validate_temporal_sequence_strict(timestamps: &[u64]) -> bool { timestamps.windows(2).all(|w| w[0] < w[1]) } fn validate_temporal_gaps(timestamps: &[u64], max_gap: u64) -> bool { timestamps.windows(2).all(|w| w[1] - w[0] <= max_gap) } fn validate_feature_importance(importance: &[f64]) -> bool { let sum: f64 = importance.iter().sum(); let all_positive = importance.iter().all(|&x| x >= 0.0); all_positive && (sum - 1.0).abs() < 1e-6 } fn validate_semantic_version(version: &str) -> bool { let parts: Vec<&str> = version.split('.').collect(); if parts.len() != 3 { return false; } parts.iter().all(|p| p.parse::().is_ok()) } fn validate_model_name(name: &str) -> bool { !name.is_empty() && name.chars().all(|c| c.is_alphanumeric() || c == '_') } fn validate_learning_rate(lr: f64) -> bool { lr > 0.0 && lr < 1.0 && lr.is_finite() } fn validate_epochs(epochs: usize) -> bool { epochs > 0 }