1use crate::core::types::{AllocationInfo, TrackingResult};
8use crate::export::data_localizer::LocalizedExportData;
9use crate::export::error_handling::{ExportError, ValidationError, ValidationType};
10use crate::export::parallel_shard_processor::ProcessedShard;
11use clap::{Parser, ValueEnum};
12use serde::{Deserialize, Serialize};
13use serde_json;
14use std::collections::{HashMap, HashSet};
15use std::fmt;
16use std::fs;
17use std::io::Read;
18use std::path::Path;
19use std::path::PathBuf;
20use std::time::Duration;
21use std::time::Instant;
22#[derive(Debug, Clone, Copy, PartialEq, Default, ValueEnum)]
26pub enum ValidationTiming {
27 Inline,
29 #[default]
31 Deferred,
32 Disabled,
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Default, ValueEnum)]
38pub enum ExportMode {
39 #[default]
41 Fast,
42 Slow,
44 Auto,
46}
47
48#[derive(Debug, Clone)]
50pub struct ExportConfig {
51 pub mode: ExportMode,
53 pub validation_timing: ValidationTiming,
55 pub validation_config: ValidationConfig,
57}
58
59impl ExportConfig {
60 pub fn new(mode: ExportMode, validation_timing: ValidationTiming) -> Self {
62 let validation_config = match mode {
63 ExportMode::Fast => ValidationConfig::for_fast_mode(),
64 ExportMode::Slow => ValidationConfig::for_slow_mode(),
65 ExportMode::Auto => ValidationConfig::default(),
66 };
67
68 Self {
69 mode,
70 validation_timing,
71 validation_config,
72 }
73 }
74
75 pub fn fast() -> Self {
77 Self::new(ExportMode::Fast, ValidationTiming::Deferred)
78 }
79
80 pub fn slow() -> Self {
82 Self::new(ExportMode::Slow, ValidationTiming::Inline)
83 }
84
85 pub fn auto() -> Self {
87 Self::new(ExportMode::Auto, ValidationTiming::Deferred)
88 }
89
90 pub fn validate_and_fix(&mut self) -> Vec<String> {
92 let mut warnings = Vec::new();
93
94 match (&self.mode, &self.validation_timing) {
96 (ExportMode::Fast, ValidationTiming::Inline) => {
97 warnings.push("Fast mode with inline validation conflicts with performance goals. Switching to deferred validation.".to_string());
98 self.validation_timing = ValidationTiming::Deferred;
99 }
100 (ExportMode::Slow, ValidationTiming::Disabled) => {
101 warnings.push("Slow mode with disabled validation conflicts with thoroughness goals. Enabling deferred validation.".to_string());
102 self.validation_timing = ValidationTiming::Deferred;
103 }
104 _ => {}
105 }
106
107 match self.mode {
109 ExportMode::Fast => {
110 if self.validation_config.enable_json_validation {
111 warnings.push("Fast mode should not enable JSON validation for optimal performance. Disabling JSON validation.".to_string());
112 self.validation_config.enable_json_validation = false;
113 }
114 if self.validation_config.enable_encoding_validation {
115 warnings.push("Fast mode should not enable encoding validation for optimal performance. Disabling encoding validation.".to_string());
116 self.validation_config.enable_encoding_validation = false;
117 }
118 }
119 ExportMode::Slow => {
120 if !self.validation_config.enable_json_validation {
121 warnings.push("Slow mode should enable comprehensive validation. Enabling JSON validation.".to_string());
122 self.validation_config.enable_json_validation = true;
123 }
124 if !self.validation_config.enable_encoding_validation {
125 warnings.push("Slow mode should enable comprehensive validation. Enabling encoding validation.".to_string());
126 self.validation_config.enable_encoding_validation = true;
127 }
128 }
129 ExportMode::Auto => {
130 }
132 }
133
134 warnings
135 }
136}
137
138impl Default for ExportConfig {
139 fn default() -> Self {
140 Self::fast()
141 }
142}
143
144#[derive(Debug, Clone)]
146pub struct ExportModeManager {
147 default_mode: ExportMode,
149 auto_threshold: usize,
151 performance_threshold_ms: u64,
153}
154
155impl ExportModeManager {
156 pub fn new() -> Self {
158 Self {
159 default_mode: ExportMode::Fast,
160 auto_threshold: 10 * 1024 * 1024, performance_threshold_ms: 5000, }
163 }
164
165 pub fn with_settings(
167 default_mode: ExportMode,
168 auto_threshold: usize,
169 performance_threshold_ms: u64,
170 ) -> Self {
171 Self {
172 default_mode,
173 auto_threshold,
174 performance_threshold_ms,
175 }
176 }
177
178 pub fn determine_optimal_mode(&self, data_size: usize) -> ExportMode {
180 match self.default_mode {
181 ExportMode::Auto => {
182 if data_size > self.auto_threshold {
183 ExportMode::Fast
185 } else {
186 ExportMode::Slow
188 }
189 }
190 mode => mode, }
192 }
193
194 pub fn create_config_for_mode(&self, mode: ExportMode) -> ExportConfig {
196 match mode {
197 ExportMode::Fast => ExportConfig::fast(),
198 ExportMode::Slow => ExportConfig::slow(),
199 ExportMode::Auto => {
200 ExportConfig::auto()
202 }
203 }
204 }
205
206 pub fn create_auto_config(&self, data_size: usize) -> ExportConfig {
208 let optimal_mode = self.determine_optimal_mode(data_size);
209 self.create_config_for_mode(optimal_mode)
210 }
211
212 pub fn optimize_config(
214 &self,
215 mut config: ExportConfig,
216 data_size: usize,
217 ) -> (ExportConfig, Vec<String>) {
218 let mut warnings = config.validate_and_fix();
219
220 if data_size > self.auto_threshold && config.mode != ExportMode::Fast {
222 warnings.push(format!(
223 "Large dataset ({:.2} MB) detected. Consider using Fast mode for better performance.",
224 data_size as f64 / 1024.0 / 1024.0
225 ));
226 }
227
228 if data_size > 100 * 1024 * 1024 {
230 if config.validation_config.enable_json_validation {
232 warnings.push(
233 "Large dataset detected. Disabling JSON validation to prevent memory issues."
234 .to_string(),
235 );
236 config.validation_config.enable_json_validation = false;
237 }
238 if config.validation_config.enable_encoding_validation {
239 warnings.push("Large dataset detected. Disabling encoding validation to prevent memory issues.".to_string());
240 config.validation_config.enable_encoding_validation = false;
241 }
242 }
243
244 (config, warnings)
245 }
246
247 pub fn get_settings(&self) -> (ExportMode, usize, u64) {
249 (
250 self.default_mode,
251 self.auto_threshold,
252 self.performance_threshold_ms,
253 )
254 }
255}
256
257impl Default for ExportModeManager {
258 fn default() -> Self {
259 Self::new()
260 }
261}
262
263#[derive(Debug)]
265pub struct QualityValidator {
266 config: ValidationConfig,
268 stats: ValidationStats,
270}
271
272#[derive(Debug)]
274pub struct AsyncValidator {
275 config: ValidationConfig,
277 stats: ValidationStats,
279}
280
281#[derive(Debug)]
286pub enum ValidationHandle {
287 Pending {
289 file_path: String,
291 expected_count: usize,
293 config: ValidationConfig,
295 },
296 Running {
298 file_path: String,
300 },
301 Completed {
303 file_path: String,
305 result: ValidationResult,
307 },
308 Failed {
310 file_path: String,
312 error: String,
314 },
315 Cancelled {
317 file_path: String,
319 reason: String,
321 },
322 TimedOut {
324 file_path: String,
326 timeout_duration: Duration,
328 },
329}
330
331unsafe impl Send for ValidationHandle {}
334unsafe impl Sync for ValidationHandle {}
335
336#[derive(Debug, Clone, PartialEq)]
338pub enum ValidationStatus {
339 Pending,
341 Running,
343 Completed,
345 Failed,
347 Cancelled,
349 TimedOut,
351}
352
353pub struct DeferredValidation {
358 handle: ValidationHandle,
360 timeout_duration: Duration,
362 cancellable: bool,
364}
365
366unsafe impl Send for DeferredValidation {}
368unsafe impl Sync for DeferredValidation {}
369
370#[derive(Debug, Clone, Serialize, Deserialize)]
372pub struct ValidationConfig {
373 pub enable_json_validation: bool,
375 pub enable_integrity_validation: bool,
377 pub enable_count_validation: bool,
379 pub enable_size_validation: bool,
381 pub enable_encoding_validation: bool,
383 pub max_data_loss_rate: f64,
385 pub min_expected_file_size: usize,
387 pub max_expected_file_size: usize,
389 pub verbose_logging: bool,
391}
392
393impl Default for ValidationConfig {
394 fn default() -> Self {
395 Self {
397 enable_json_validation: false, enable_integrity_validation: true,
399 enable_count_validation: true,
400 enable_size_validation: true,
401 enable_encoding_validation: false, max_data_loss_rate: 0.1, min_expected_file_size: 1024, max_expected_file_size: 100 * 1024 * 1024, verbose_logging: false,
406 }
407 }
408}
409
410impl ValidationConfig {
411 pub fn for_fast_mode() -> Self {
413 Self {
414 enable_json_validation: false,
415 enable_integrity_validation: false,
416 enable_count_validation: false,
417 enable_size_validation: true, enable_encoding_validation: false,
419 max_data_loss_rate: 1.0, min_expected_file_size: 512, max_expected_file_size: 1024 * 1024 * 1024, verbose_logging: false,
423 }
424 }
425
426 pub fn for_slow_mode() -> Self {
428 Self {
429 enable_json_validation: true,
430 enable_integrity_validation: true,
431 enable_count_validation: true,
432 enable_size_validation: true,
433 enable_encoding_validation: true,
434 max_data_loss_rate: 0.01, min_expected_file_size: 1024, max_expected_file_size: 100 * 1024 * 1024, verbose_logging: true,
438 }
439 }
440
441 pub fn with_strategy(strategy: ValidationStrategy) -> Self {
443 match strategy {
444 ValidationStrategy::Minimal => Self::for_fast_mode(),
445 ValidationStrategy::Balanced => Self::default(),
446 ValidationStrategy::Comprehensive => Self::for_slow_mode(),
447 ValidationStrategy::Custom(config) => config,
448 }
449 }
450
451 pub fn conflicts_with_mode(&self, mode: &ExportMode) -> Vec<String> {
453 let mut conflicts = Vec::new();
454
455 match mode {
456 ExportMode::Fast => {
457 if self.enable_json_validation {
458 conflicts.push(
459 "JSON validation enabled in fast mode may impact performance".to_string(),
460 );
461 }
462 if self.enable_encoding_validation {
463 conflicts.push(
464 "Encoding validation enabled in fast mode may impact performance"
465 .to_string(),
466 );
467 }
468 if self.max_data_loss_rate < 0.1 {
469 conflicts.push(
470 "Strict data loss rate in fast mode may impact performance".to_string(),
471 );
472 }
473 }
474 ExportMode::Slow => {
475 if !self.enable_json_validation {
476 conflicts.push(
477 "JSON validation disabled in slow mode reduces thoroughness".to_string(),
478 );
479 }
480 if !self.enable_integrity_validation {
481 conflicts.push(
482 "Integrity validation disabled in slow mode reduces thoroughness"
483 .to_string(),
484 );
485 }
486 if !self.enable_encoding_validation {
487 conflicts.push(
488 "Encoding validation disabled in slow mode reduces thoroughness"
489 .to_string(),
490 );
491 }
492 }
493 ExportMode::Auto => {
494 }
496 }
497
498 conflicts
499 }
500
501 pub fn apply_safe_defaults_for_mode(&mut self, mode: &ExportMode) {
503 match mode {
504 ExportMode::Fast => {
505 self.enable_json_validation = false;
507 self.enable_encoding_validation = false;
508 self.enable_integrity_validation = false;
509 self.max_data_loss_rate = self.max_data_loss_rate.max(0.5);
510 self.verbose_logging = false;
511 }
512 ExportMode::Slow => {
513 self.enable_json_validation = true;
515 self.enable_integrity_validation = true;
516 self.enable_count_validation = true;
517 self.enable_size_validation = true;
518 self.enable_encoding_validation = true;
519 self.max_data_loss_rate = self.max_data_loss_rate.min(0.1);
520 self.verbose_logging = true;
521 }
522 ExportMode::Auto => {
523 }
525 }
526 }
527}
528
529#[derive(Debug, Clone)]
531pub enum ValidationStrategy {
532 Minimal,
534 Balanced,
536 Comprehensive,
538 Custom(ValidationConfig),
540}
541
542#[derive(Debug, Clone, Default)]
544pub struct ValidationStats {
545 pub total_validations: usize,
547 pub successful_validations: usize,
549 pub failed_validations: usize,
551 pub validation_type_stats: HashMap<ValidationType, ValidationTypeStats>,
553 pub total_validation_time_ms: u64,
555 pub issues_found: usize,
557 pub issues_fixed: usize,
559}
560
561#[derive(Debug, Clone, Default)]
563pub struct ValidationTypeStats {
564 pub executions: usize,
566 pub successes: usize,
568 pub failures: usize,
570 pub avg_execution_time_ms: f64,
572}
573
574#[derive(Debug, Clone)]
576pub struct ValidationResult {
577 pub is_valid: bool,
579 pub validation_type: ValidationType,
581 pub message: String,
583 pub issues: Vec<ValidationIssue>,
585 pub validation_time_ms: u64,
587 pub data_size: usize,
589}
590
591impl Default for ValidationResult {
592 fn default() -> Self {
593 Self {
594 is_valid: true,
595 validation_type: ValidationType::DataIntegrity,
596 message: "Default validation result".to_string(),
597 issues: Vec::new(),
598 validation_time_ms: 0,
599 data_size: 0,
600 }
601 }
602}
603
604#[derive(Debug, Clone, Serialize, Deserialize)]
606pub struct ValidationIssue {
607 pub issue_type: IssueType,
609 pub description: String,
611 pub severity: IssueSeverity,
613 pub affected_data: String,
615 pub suggested_fix: Option<String>,
617 pub auto_fixable: bool,
619}
620
621#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
623pub enum IssueType {
624 MissingData,
626 CorruptedData,
628 InconsistentData,
630 InvalidFormat,
632 SizeAnomaly,
634 EncodingError,
636 StructuralError,
638 CountMismatch,
640}
641
642#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
644pub enum IssueSeverity {
645 Critical,
647 High,
649 Medium,
651 Low,
653 Info,
655}
656
657impl QualityValidator {
658 pub fn new(config: ValidationConfig) -> Self {
660 Self {
661 config,
662 stats: ValidationStats::default(),
663 }
664 }
665
666 pub fn new_default() -> Self {
668 Self::new(ValidationConfig::default())
669 }
670
671 pub async fn validate_file_async<P: AsRef<Path>>(
673 &mut self,
674 file_path: P,
675 ) -> TrackingResult<ValidationResult> {
676 let mut async_validator = AsyncValidator::new(self.config.clone());
678 let result = async_validator.validate_file_async(file_path).await?;
679
680 self.update_stats(&result);
682
683 Ok(result)
684 }
685
686 pub fn validate_source_data(
688 &mut self,
689 data: &LocalizedExportData,
690 ) -> TrackingResult<ValidationResult> {
691 let start_time = Instant::now();
692 let mut issues = Vec::new();
693
694 if self.config.verbose_logging {
695 tracing::info!("🔍 Starting source data quality validation...");
696 }
697
698 if self.config.enable_integrity_validation {
700 self.validate_data_integrity(data, &mut issues)?;
701 }
702
703 if self.config.enable_count_validation {
705 self.validate_allocation_counts(data, &mut issues)?;
706 }
707
708 let validation_time = start_time.elapsed().as_millis() as u64;
709 let validation_time = if validation_time == 0 {
711 1
712 } else {
713 validation_time
714 };
715 let is_valid = issues
716 .iter()
717 .all(|issue| issue.severity != IssueSeverity::Critical);
718
719 let result = ValidationResult {
720 is_valid,
721 validation_type: ValidationType::DataIntegrity,
722 message: if is_valid {
723 "Source data quality validation passed".to_string()
724 } else {
725 format!(
726 "Source data quality validation failed with {} issues",
727 issues.len()
728 )
729 },
730 issues,
731 validation_time_ms: validation_time,
732 data_size: data.allocations.len(),
733 };
734
735 self.update_stats(&result);
736
737 if self.config.verbose_logging {
738 self.print_validation_result(&result);
739 }
740
741 Ok(result)
742 }
743
744 pub fn validate_processed_shards(
746 &mut self,
747 shards: &[ProcessedShard],
748 original_count: usize,
749 ) -> TrackingResult<ValidationResult> {
750 let start_time = Instant::now();
751 let mut issues = Vec::new();
752
753 if self.config.verbose_logging {
754 tracing::info!("🔍 Starting processed shard data validation...");
755 }
756
757 if self.config.enable_json_validation {
759 self.validate_json_structure(shards, &mut issues)?;
760 }
761
762 if self.config.enable_count_validation {
764 self.validate_shard_counts(shards, original_count, &mut issues)?;
765 }
766
767 if self.config.enable_size_validation {
769 self.validate_data_sizes(shards, &mut issues)?;
770 }
771
772 let validation_time = start_time.elapsed().as_millis() as u64;
773 let is_valid = issues
774 .iter()
775 .all(|issue| issue.severity != IssueSeverity::Critical);
776
777 let total_size: usize = shards.iter().map(|s| s.data.len()).sum();
778 let result = ValidationResult {
779 is_valid,
780 validation_type: ValidationType::JsonStructure,
781 message: if is_valid {
782 "Shard data validation passed".to_string()
783 } else {
784 format!("Shard data validation failed with {} issues", issues.len())
785 },
786 issues,
787 validation_time_ms: validation_time,
788 data_size: total_size,
789 };
790
791 self.update_stats(&result);
792
793 if self.config.verbose_logging {
794 self.print_validation_result(&result);
795 }
796
797 Ok(result)
798 }
799
800 pub fn validate_output_file(
802 &mut self,
803 file_path: &str,
804 expected_allocation_count: usize,
805 ) -> TrackingResult<ValidationResult> {
806 let start_time = Instant::now();
807 let mut issues = Vec::new();
808
809 if self.config.verbose_logging {
810 tracing::info!("🔍 Starting final output file validation: {file_path}");
811 }
812
813 if !std::path::Path::new(file_path).exists() {
815 issues.push(ValidationIssue {
816 issue_type: IssueType::MissingData,
817 description: "Output file does not exist".to_string(),
818 severity: IssueSeverity::Critical,
819 affected_data: file_path.to_string(),
820 suggested_fix: Some("Check file path and write permissions".to_string()),
821 auto_fixable: false,
822 });
823 } else {
824 if self.config.enable_size_validation {
826 self.validate_file_size(file_path, &mut issues)?;
827 }
828
829 if self.config.enable_json_validation {
831 self.validate_file_content(file_path, expected_allocation_count, &mut issues)?;
832 }
833
834 if self.config.enable_encoding_validation {
836 self.validate_file_encoding(file_path, &mut issues)?;
837 }
838 }
839
840 let validation_time = start_time.elapsed().as_millis() as u64;
841 let is_valid = issues
842 .iter()
843 .all(|issue| issue.severity != IssueSeverity::Critical);
844
845 let file_size = std::fs::metadata(file_path)
846 .map(|m| m.len() as usize)
847 .unwrap_or(0);
848
849 let result = ValidationResult {
850 is_valid,
851 validation_type: ValidationType::FileSize,
852 message: if is_valid {
853 "Output file validation passed".to_string()
854 } else {
855 format!("Output file validation failed with {} issues", issues.len())
856 },
857 issues,
858 validation_time_ms: validation_time,
859 data_size: file_size,
860 };
861
862 self.update_stats(&result);
863
864 if self.config.verbose_logging {
865 self.print_validation_result(&result);
866 }
867
868 Ok(result)
869 }
870
871 pub fn get_stats(&self) -> &ValidationStats {
873 &self.stats
874 }
875
876 pub fn generate_validation_report(&self) -> ValidationReport {
878 let success_rate = if self.stats.total_validations > 0 {
879 (self.stats.successful_validations as f64 / self.stats.total_validations as f64) * 100.0
880 } else {
881 0.0
882 };
883
884 let avg_validation_time = if self.stats.total_validations > 0 {
885 self.stats.total_validation_time_ms as f64 / self.stats.total_validations as f64
886 } else {
887 0.0
888 };
889
890 ValidationReport {
891 total_validations: self.stats.total_validations,
892 successful_validations: self.stats.successful_validations,
893 failed_validations: self.stats.failed_validations,
894 success_rate,
895 avg_validation_time_ms: avg_validation_time,
896 total_issues_found: self.stats.issues_found,
897 total_issues_fixed: self.stats.issues_fixed,
898 validation_type_breakdown: self.stats.validation_type_stats.clone(),
899 }
900 }
901
902 fn validate_data_integrity(
904 &self,
905 data: &LocalizedExportData,
906 issues: &mut Vec<ValidationIssue>,
907 ) -> TrackingResult<()> {
908 if data.allocations.is_empty() {
910 issues.push(ValidationIssue {
911 issue_type: IssueType::MissingData,
912 description: "Allocation data is empty".to_string(),
913 severity: IssueSeverity::Critical, affected_data: "allocations".to_string(),
915 suggested_fix: Some("Check if memory tracker is working properly".to_string()),
916 auto_fixable: false,
917 });
918 }
919
920 let mut ptr_set = HashSet::new();
922 let mut duplicate_ptrs = Vec::new();
923
924 for (index, allocation) in data.allocations.iter().enumerate() {
925 if !ptr_set.insert(allocation.ptr) {
927 duplicate_ptrs.push(allocation.ptr);
928 }
929
930 if allocation.size == 0 {
932 issues.push(ValidationIssue {
933 issue_type: IssueType::InvalidFormat,
934 description: format!("Allocation {index} has size 0"),
935 severity: IssueSeverity::Medium,
936 affected_data: format!("allocation[{index}]"),
937 suggested_fix: Some("Check allocation tracking logic".to_string()),
938 auto_fixable: false,
939 });
940 }
941
942 if let Some(dealloc_time) = allocation.timestamp_dealloc {
944 if dealloc_time <= allocation.timestamp_alloc {
945 issues.push(ValidationIssue {
946 issue_type: IssueType::InconsistentData,
947 description: format!(
948 "Allocation {index} deallocation time is before allocation time",
949 ),
950 severity: IssueSeverity::High,
951 affected_data: format!("allocation[{index}]"),
952 suggested_fix: Some("Check timestamp generation logic".to_string()),
953 auto_fixable: false,
954 });
955 }
956 }
957 }
958
959 if !duplicate_ptrs.is_empty() {
961 issues.push(ValidationIssue {
962 issue_type: IssueType::InconsistentData,
963 description: format!("Found {} duplicate pointers", duplicate_ptrs.len()),
964 severity: IssueSeverity::High,
965 affected_data: format!("pointers: {duplicate_ptrs:?}"),
966 suggested_fix: Some("Check allocation tracking deduplication logic".to_string()),
967 auto_fixable: false,
968 });
969 }
970
971 Ok(())
972 }
973
974 fn validate_allocation_counts(
976 &self,
977 data: &LocalizedExportData,
978 issues: &mut Vec<ValidationIssue>,
979 ) -> TrackingResult<()> {
980 let allocation_count = data.allocations.len();
981 let stats_count = data.stats.total_allocations;
982
983 if allocation_count != stats_count {
985 let loss_rate = if stats_count > 0 {
986 ((stats_count - allocation_count) as f64 / stats_count as f64) * 100.0
987 } else {
988 0.0
989 };
990
991 let severity = if loss_rate > self.config.max_data_loss_rate {
992 IssueSeverity::Critical
993 } else {
994 IssueSeverity::Medium
995 };
996
997 issues.push(ValidationIssue {
998 issue_type: IssueType::CountMismatch,
999 description: format!("Allocation count mismatch: actual {allocation_count}, stats {stats_count}, loss rate {loss_rate:.2}%"),
1000 severity,
1001 affected_data: "allocation_count".to_string(),
1002 suggested_fix: Some("Check data collection and statistics logic".to_string()),
1003 auto_fixable: false,
1004 });
1005 }
1006
1007 Ok(())
1008 }
1009
1010 fn validate_json_structure(
1012 &self,
1013 shards: &[ProcessedShard],
1014 issues: &mut Vec<ValidationIssue>,
1015 ) -> TrackingResult<()> {
1016 for (index, shard) in shards.iter().enumerate() {
1017 match serde_json::from_slice::<Vec<AllocationInfo>>(&shard.data) {
1019 Ok(allocations) => {
1020 if allocations.len() != shard.allocation_count {
1022 issues.push(ValidationIssue {
1023 issue_type: IssueType::CountMismatch,
1024 description: format!(
1025 "Shard {index} allocation count mismatch: expected {0}, actual {1}",
1026 shard.allocation_count,
1027 allocations.len()
1028 ),
1029 severity: IssueSeverity::High,
1030 affected_data: format!("shard[{index}]"),
1031 suggested_fix: Some("Check shard processing logic".to_string()),
1032 auto_fixable: false,
1033 });
1034 }
1035 }
1036 Err(e) => {
1037 issues.push(ValidationIssue {
1038 issue_type: IssueType::InvalidFormat,
1039 description: format!("Shard {index} JSON parsing failed: {e}"),
1040 severity: IssueSeverity::Critical,
1041 affected_data: format!("shard[{index}]"),
1042 suggested_fix: Some("Check JSON serialization logic".to_string()),
1043 auto_fixable: false,
1044 });
1045 }
1046 }
1047 }
1048
1049 Ok(())
1050 }
1051
1052 fn validate_shard_counts(
1054 &self,
1055 shards: &[ProcessedShard],
1056 original_count: usize,
1057 issues: &mut Vec<ValidationIssue>,
1058 ) -> TrackingResult<()> {
1059 let total_shard_count: usize = shards.iter().map(|s| s.allocation_count).sum();
1060
1061 if total_shard_count != original_count {
1062 let loss_rate = if original_count > 0 {
1063 ((original_count - total_shard_count) as f64 / original_count as f64) * 100.0
1064 } else {
1065 0.0
1066 };
1067
1068 let severity = if loss_rate > self.config.max_data_loss_rate {
1069 IssueSeverity::Critical
1070 } else {
1071 IssueSeverity::Medium
1072 };
1073
1074 issues.push(ValidationIssue {
1075 issue_type: IssueType::CountMismatch,
1076 description: format!("Shard total count mismatch: original {original_count}, shard total {total_shard_count}, loss rate {loss_rate:.2}%"),
1077 severity,
1078 affected_data: "shard_counts".to_string(),
1079 suggested_fix: Some("Check shard processing and merging logic".to_string()),
1080 auto_fixable: false,
1081 });
1082 }
1083
1084 Ok(())
1085 }
1086
1087 fn validate_data_sizes(
1089 &self,
1090 shards: &[ProcessedShard],
1091 issues: &mut Vec<ValidationIssue>,
1092 ) -> TrackingResult<()> {
1093 for (index, shard) in shards.iter().enumerate() {
1094 if shard.data.is_empty() {
1096 issues.push(ValidationIssue {
1097 issue_type: IssueType::MissingData,
1098 description: format!("Shard {index} data is empty"),
1099 severity: IssueSeverity::High,
1100 affected_data: format!("shard[{index}]"),
1101 suggested_fix: Some("Check shard processing logic".to_string()),
1102 auto_fixable: false,
1103 });
1104 }
1105
1106 let expected_min_size = shard.allocation_count * 50; let expected_max_size = shard.allocation_count * 1000; if shard.data.len() < expected_min_size {
1111 issues.push(ValidationIssue {
1112 issue_type: IssueType::SizeAnomaly,
1113 description: format!("Shard {index} size abnormally small: {} bytes (expected at least {} bytes)",
1114 shard.data.len(), expected_min_size),
1115 severity: IssueSeverity::Medium,
1116 affected_data: format!("shard[{index}]"),
1117 suggested_fix: Some("Check serialization configuration".to_string()),
1118 auto_fixable: false,
1119 });
1120 }
1121
1122 if shard.data.len() > expected_max_size {
1123 issues.push(ValidationIssue {
1124 issue_type: IssueType::SizeAnomaly,
1125 description: format!(
1126 "Shard {index} size abnormally large: {} bytes (expected at most {} bytes)",
1127 shard.data.len(),
1128 expected_max_size
1129 ),
1130 severity: IssueSeverity::Low,
1131 affected_data: format!("shard[{index}]"),
1132 suggested_fix: Some("Consider enabling compression".to_string()),
1133 auto_fixable: false,
1134 });
1135 }
1136 }
1137
1138 Ok(())
1139 }
1140
1141 fn validate_file_size(
1143 &self,
1144 file_path: &str,
1145 issues: &mut Vec<ValidationIssue>,
1146 ) -> TrackingResult<()> {
1147 let metadata = std::fs::metadata(file_path).map_err(|e| ExportError::DataQualityError {
1148 validation_type: ValidationType::FileSize,
1149 expected: "Readable file".to_string(),
1150 actual: format!("File read failed: {e}"),
1151 affected_records: 0,
1152 })?;
1153
1154 let file_size = metadata.len() as usize;
1155
1156 if file_size < self.config.min_expected_file_size {
1157 issues.push(ValidationIssue {
1158 issue_type: IssueType::SizeAnomaly,
1159 description: format!(
1160 "File size too small: {file_size} bytes (minimum expected {} bytes)",
1161 self.config.min_expected_file_size
1162 ),
1163 severity: IssueSeverity::High,
1164 affected_data: file_path.to_string(),
1165 suggested_fix: Some("Check if data was completely written".to_string()),
1166 auto_fixable: false,
1167 });
1168 }
1169
1170 if file_size > self.config.max_expected_file_size {
1171 issues.push(ValidationIssue {
1172 issue_type: IssueType::SizeAnomaly,
1173 description: format!(
1174 "File size too large: {file_size} bytes (maximum expected {} bytes)",
1175 self.config.max_expected_file_size
1176 ),
1177 severity: IssueSeverity::Medium,
1178 affected_data: file_path.to_string(),
1179 suggested_fix: Some("Consider enabling compression or sampling".to_string()),
1180 auto_fixable: false,
1181 });
1182 }
1183
1184 Ok(())
1185 }
1186
1187 fn validate_file_content(
1189 &self,
1190 file_path: &str,
1191 expected_count: usize,
1192 issues: &mut Vec<ValidationIssue>,
1193 ) -> TrackingResult<()> {
1194 let content =
1195 std::fs::read_to_string(file_path).map_err(|e| ExportError::DataQualityError {
1196 validation_type: ValidationType::JsonStructure,
1197 expected: "Readable JSON file".to_string(),
1198 actual: format!("File read failed: {e}"),
1199 affected_records: 0,
1200 })?;
1201
1202 match serde_json::from_str::<serde_json::Value>(&content) {
1204 Ok(json) => {
1205 if let Some(allocations) = json.get("allocations") {
1207 if let Some(array) = allocations.as_array() {
1208 let actual_count = array.len();
1209 if actual_count != expected_count {
1210 let loss_rate = if expected_count > 0 {
1211 ((expected_count - actual_count) as f64 / expected_count as f64)
1212 * 100.0
1213 } else {
1214 0.0
1215 };
1216
1217 let severity = if loss_rate > self.config.max_data_loss_rate {
1218 IssueSeverity::Critical
1219 } else {
1220 IssueSeverity::Medium
1221 };
1222
1223 issues.push(ValidationIssue {
1224 issue_type: IssueType::CountMismatch,
1225 description: format!("File allocation count mismatch: expected {expected_count}, actual {actual_count}, loss rate {loss_rate:.2}%"),
1226 severity,
1227 affected_data: file_path.to_string(),
1228 suggested_fix: Some("Check complete export pipeline".to_string()),
1229 auto_fixable: false,
1230 });
1231 }
1232 } else {
1233 issues.push(ValidationIssue {
1234 issue_type: IssueType::StructuralError,
1235 description: "allocations field is not an array".to_string(),
1236 severity: IssueSeverity::Critical,
1237 affected_data: file_path.to_string(),
1238 suggested_fix: Some(
1239 "Check JSON structure generation logic".to_string(),
1240 ),
1241 auto_fixable: false,
1242 });
1243 }
1244 } else {
1245 issues.push(ValidationIssue {
1246 issue_type: IssueType::StructuralError,
1247 description: "Missing allocations field".to_string(),
1248 severity: IssueSeverity::Critical,
1249 affected_data: file_path.to_string(),
1250 suggested_fix: Some("Check JSON structure generation logic".to_string()),
1251 auto_fixable: false,
1252 });
1253 }
1254 }
1255 Err(e) => {
1256 issues.push(ValidationIssue {
1257 issue_type: IssueType::InvalidFormat,
1258 description: format!("JSON parsing failed: {e}"),
1259 severity: IssueSeverity::Critical,
1260 affected_data: file_path.to_string(),
1261 suggested_fix: Some("Check JSON format and encoding".to_string()),
1262 auto_fixable: false,
1263 });
1264 }
1265 }
1266
1267 Ok(())
1268 }
1269
1270 fn validate_file_encoding(
1272 &self,
1273 file_path: &str,
1274 issues: &mut Vec<ValidationIssue>,
1275 ) -> TrackingResult<()> {
1276 match std::fs::read_to_string(file_path) {
1278 Ok(_) => {
1279 }
1281 Err(e) => {
1282 issues.push(ValidationIssue {
1283 issue_type: IssueType::EncodingError,
1284 description: format!("File encoding validation failed: {e}"),
1285 severity: IssueSeverity::High,
1286 affected_data: file_path.to_string(),
1287 suggested_fix: Some("Ensure file is saved with UTF-8 encoding".to_string()),
1288 auto_fixable: false,
1289 });
1290 }
1291 }
1292
1293 Ok(())
1294 }
1295
1296 fn update_stats(&mut self, result: &ValidationResult) {
1298 self.stats.total_validations += 1;
1299
1300 if result.is_valid {
1301 self.stats.successful_validations += 1;
1302 } else {
1303 self.stats.failed_validations += 1;
1304 }
1305
1306 self.stats.total_validation_time_ms += result.validation_time_ms;
1307 self.stats.issues_found += result.issues.len();
1308
1309 let type_stats = self
1311 .stats
1312 .validation_type_stats
1313 .entry(result.validation_type.clone())
1314 .or_default();
1315
1316 type_stats.executions += 1;
1317 if result.is_valid {
1318 type_stats.successes += 1;
1319 } else {
1320 type_stats.failures += 1;
1321 }
1322
1323 type_stats.avg_execution_time_ms = if type_stats.executions > 0 {
1325 (type_stats.avg_execution_time_ms * (type_stats.executions - 1) as f64
1326 + result.validation_time_ms as f64)
1327 / type_stats.executions as f64
1328 } else {
1329 result.validation_time_ms as f64
1330 };
1331 }
1332
1333 fn print_validation_result(&self, result: &ValidationResult) {
1335 let status_icon = if result.is_valid { "✅" } else { "❌" };
1336 tracing::info!(
1337 "{status_icon} Validation result: {} ({}ms)",
1338 result.message,
1339 result.validation_time_ms
1340 );
1341
1342 if !result.issues.is_empty() {
1343 tracing::info!(" Issues found:");
1344 for (index, issue) in result.issues.iter().enumerate() {
1345 let severity_icon = match issue.severity {
1346 IssueSeverity::Critical => "🔴",
1347 IssueSeverity::High => "🟠",
1348 IssueSeverity::Medium => "🟡",
1349 IssueSeverity::Low => "🔵",
1350 IssueSeverity::Info => "ℹ️",
1351 };
1352 tracing::info!(
1353 " {index}. {severity_icon} {:?}: {}",
1354 issue.issue_type,
1355 issue.description
1356 );
1357 if let Some(fix) = &issue.suggested_fix {
1358 tracing::info!(" Suggested fix: {fix}");
1359 }
1360 }
1361 }
1362 }
1363}
1364
1365impl AsyncValidator {
1366 pub fn new(config: ValidationConfig) -> Self {
1368 Self {
1369 config,
1370 stats: ValidationStats::default(),
1371 }
1372 }
1373
1374 pub fn new_default() -> Self {
1376 Self::new(ValidationConfig::default())
1377 }
1378
1379 pub async fn validate_file_async<P: AsRef<Path>>(
1381 &mut self,
1382 file_path: P,
1383 ) -> TrackingResult<ValidationResult> {
1384 let start_time = Instant::now();
1385 let mut issues = Vec::new();
1386 let path = file_path.as_ref();
1387
1388 if self.config.verbose_logging {
1389 tracing::info!("🔍 Starting async file validation: {}", path.display());
1390 }
1391
1392 if !path.exists() {
1394 issues.push(ValidationIssue {
1395 issue_type: IssueType::MissingData,
1396 description: "Output file does not exist".to_string(),
1397 severity: IssueSeverity::Critical,
1398 affected_data: path.display().to_string(),
1399 suggested_fix: Some("Check file path and write permissions".to_string()),
1400 auto_fixable: false,
1401 });
1402 } else {
1403 if self.config.enable_size_validation {
1405 if let Err(e) = self.validate_file_size_async(path, &mut issues).await {
1406 tracing::info!("⚠️ File size validation failed: {}", e);
1407 }
1408 }
1409
1410 if self.config.enable_json_validation {
1412 if let Err(e) = self.validate_content_stream(path, &mut issues).await {
1413 tracing::info!("⚠️ Content stream validation failed: {}", e);
1414 }
1415 }
1416 }
1417
1418 let validation_time = start_time.elapsed().as_millis() as u64;
1419 let is_valid = issues
1420 .iter()
1421 .all(|issue| issue.severity != IssueSeverity::Critical);
1422
1423 let file_size = fs::metadata(path).map(|m| m.len() as usize).unwrap_or(0);
1424
1425 let result = ValidationResult {
1426 is_valid,
1427 validation_type: ValidationType::FileSize,
1428 message: if is_valid {
1429 "Async file validation passed".to_string()
1430 } else {
1431 format!("Async file validation failed with {} issues", issues.len())
1432 },
1433 issues,
1434 validation_time_ms: validation_time,
1435 data_size: file_size,
1436 };
1437
1438 self.update_stats(&result);
1439
1440 if self.config.verbose_logging {
1441 self.print_validation_result(&result);
1442 }
1443
1444 Ok(result)
1445 }
1446
1447 pub async fn validate_content_stream<P: AsRef<Path>>(
1449 &self,
1450 file_path: P,
1451 issues: &mut Vec<ValidationIssue>,
1452 ) -> TrackingResult<()> {
1453 let file = fs::File::open(&file_path).map_err(|e| ExportError::DataQualityError {
1454 validation_type: ValidationType::JsonStructure,
1455 expected: "Readable file".to_string(),
1456 actual: format!("File open failed: {e}"),
1457 affected_records: 0,
1458 })?;
1459
1460 let mut reader = std::io::BufReader::new(file);
1461 let mut buffer = Vec::new();
1462 let chunk_size = 8192; loop {
1466 let mut chunk = vec![0u8; chunk_size];
1467 let bytes_read =
1468 reader
1469 .read(&mut chunk)
1470 .map_err(|e| ExportError::DataQualityError {
1471 validation_type: ValidationType::JsonStructure,
1472 expected: "Readable file content".to_string(),
1473 actual: format!("Read failed: {e}"),
1474 affected_records: 0,
1475 })?;
1476
1477 if bytes_read == 0 {
1478 break; }
1480
1481 chunk.truncate(bytes_read);
1482 buffer.extend_from_slice(&chunk);
1483
1484 if buffer.len() > 1024 * 1024 {
1486 self.validate_json_chunk(&buffer, issues)?;
1488 buffer.clear();
1489 }
1490 }
1491
1492 if !buffer.is_empty() {
1494 self.validate_json_chunk(&buffer, issues)?;
1495 }
1496
1497 Ok(())
1498 }
1499
1500 fn validate_json_chunk(
1502 &self,
1503 chunk: &[u8],
1504 issues: &mut Vec<ValidationIssue>,
1505 ) -> TrackingResult<()> {
1506 if let Err(e) = serde_json::from_slice::<serde_json::Value>(chunk) {
1508 if !e.to_string().contains("EOF") {
1510 issues.push(ValidationIssue {
1511 issue_type: IssueType::InvalidFormat,
1512 description: format!("JSON chunk validation failed: {e}"),
1513 severity: IssueSeverity::Medium,
1514 affected_data: "JSON chunk".to_string(),
1515 suggested_fix: Some("Check JSON format and encoding".to_string()),
1516 auto_fixable: false,
1517 });
1518 }
1519 }
1520
1521 Ok(())
1522 }
1523
1524 async fn validate_file_size_async<P: AsRef<Path>>(
1526 &self,
1527 file_path: P,
1528 issues: &mut Vec<ValidationIssue>,
1529 ) -> TrackingResult<()> {
1530 let metadata = fs::metadata(&file_path).map_err(|e| ExportError::DataQualityError {
1531 validation_type: ValidationType::FileSize,
1532 expected: "Readable file metadata".to_string(),
1533 actual: format!("Metadata read failed: {e}"),
1534 affected_records: 0,
1535 })?;
1536
1537 let file_size = metadata.len() as usize;
1538
1539 if file_size < self.config.min_expected_file_size {
1540 issues.push(ValidationIssue {
1541 issue_type: IssueType::SizeAnomaly,
1542 description: format!(
1543 "File size too small: {} bytes, minimum expected: {} bytes",
1544 file_size, self.config.min_expected_file_size
1545 ),
1546 severity: IssueSeverity::Medium,
1547 affected_data: file_path.as_ref().display().to_string(),
1548 suggested_fix: Some("Check if export data is complete".to_string()),
1549 auto_fixable: false,
1550 });
1551 }
1552
1553 if file_size > self.config.max_expected_file_size {
1554 issues.push(ValidationIssue {
1555 issue_type: IssueType::SizeAnomaly,
1556 description: format!(
1557 "File size too large: {} bytes, maximum expected: {} bytes",
1558 file_size, self.config.max_expected_file_size
1559 ),
1560 severity: IssueSeverity::Medium,
1561 affected_data: file_path.as_ref().display().to_string(),
1562 suggested_fix: Some(
1563 "Check for data duplication or configuration errors".to_string(),
1564 ),
1565 auto_fixable: false,
1566 });
1567 }
1568
1569 Ok(())
1570 }
1571
1572 fn update_stats(&mut self, result: &ValidationResult) {
1574 self.stats.total_validations += 1;
1575
1576 if result.is_valid {
1577 self.stats.successful_validations += 1;
1578 } else {
1579 self.stats.failed_validations += 1;
1580 }
1581
1582 self.stats.total_validation_time_ms += result.validation_time_ms;
1583 self.stats.issues_found += result.issues.len();
1584
1585 let type_stats = self
1587 .stats
1588 .validation_type_stats
1589 .entry(result.validation_type.clone())
1590 .or_default();
1591
1592 type_stats.executions += 1;
1593 if result.is_valid {
1594 type_stats.successes += 1;
1595 } else {
1596 type_stats.failures += 1;
1597 }
1598
1599 type_stats.avg_execution_time_ms = if type_stats.executions > 0 {
1601 (type_stats.avg_execution_time_ms * (type_stats.executions - 1) as f64
1602 + result.validation_time_ms as f64)
1603 / type_stats.executions as f64
1604 } else {
1605 result.validation_time_ms as f64
1606 };
1607 }
1608
1609 fn print_validation_result(&self, result: &ValidationResult) {
1611 let status_icon = if result.is_valid { "✅" } else { "❌" };
1612 tracing::info!(
1613 "{status_icon} Validation result: {} ({}ms)",
1614 result.message,
1615 result.validation_time_ms
1616 );
1617
1618 if !result.issues.is_empty() {
1619 tracing::info!(" Issues found:");
1620 for (index, issue) in result.issues.iter().enumerate() {
1621 let severity_icon = match issue.severity {
1622 IssueSeverity::Critical => "🔴",
1623 IssueSeverity::High => "🟠",
1624 IssueSeverity::Medium => "🟡",
1625 IssueSeverity::Low => "🔵",
1626 IssueSeverity::Info => "ℹ️",
1627 };
1628 tracing::info!(
1629 " {index}. {severity_icon} {:?}: {}",
1630 issue.issue_type,
1631 issue.description
1632 );
1633 if let Some(fix) = &issue.suggested_fix {
1634 tracing::info!(" Suggested fix: {fix}");
1635 }
1636 }
1637 }
1638 }
1639
1640 pub fn create_enhanced_streaming_validator(
1642 &self,
1643 streaming_config: StreamingValidationConfig,
1644 ) -> EnhancedStreamingValidator {
1645 EnhancedStreamingValidator::new(self.config.clone(), streaming_config)
1646 }
1647
1648 #[allow(clippy::type_complexity)]
1650 pub async fn validate_file_with_streaming<P: AsRef<Path>>(
1651 &mut self,
1652 file_path: P,
1653 streaming_config: Option<StreamingValidationConfig>,
1654 progress_callback: Option<Box<dyn Fn(&ValidationProgress) + Send + Sync>>,
1655 ) -> TrackingResult<ValidationResult> {
1656 let config = streaming_config.unwrap_or_default();
1657 let mut streaming_validator = EnhancedStreamingValidator::new(self.config.clone(), config);
1658
1659 if let Some(callback) = progress_callback {
1660 streaming_validator.set_progress_callback(callback);
1661 }
1662
1663 let file = fs::File::open(&file_path).map_err(|e| ExportError::DataQualityError {
1665 validation_type: ValidationType::FileSize,
1666 expected: "Readable file".to_string(),
1667 actual: format!("File open failed: {e}"),
1668 affected_records: 0,
1669 })?;
1670
1671 let metadata = file.metadata().map_err(|e| ExportError::DataQualityError {
1672 validation_type: ValidationType::FileSize,
1673 expected: "Readable file metadata".to_string(),
1674 actual: format!("Metadata read failed: {e}"),
1675 affected_records: 0,
1676 })?;
1677
1678 let file_size = metadata.len();
1679 let reader = std::io::BufReader::new(file);
1680
1681 let result = streaming_validator
1682 .validate_stream_async(reader, Some(file_size))
1683 .await?;
1684
1685 self.update_stats(&result);
1687
1688 Ok(result)
1689 }
1690}
1691
1692impl DeferredValidation {
1693 pub fn new<P: AsRef<Path>>(
1695 file_path: P,
1696 expected_count: usize,
1697 config: ValidationConfig,
1698 ) -> Self {
1699 let file_path_str = file_path.as_ref().to_string_lossy().to_string();
1700
1701 Self {
1702 handle: ValidationHandle::Pending {
1703 file_path: file_path_str,
1704 expected_count,
1705 config,
1706 },
1707 timeout_duration: Duration::from_secs(30), cancellable: true,
1709 }
1710 }
1711
1712 pub fn with_timeout<P: AsRef<Path>>(
1714 file_path: P,
1715 expected_count: usize,
1716 config: ValidationConfig,
1717 timeout_duration: Duration,
1718 ) -> Self {
1719 let mut validation = Self::new(file_path, expected_count, config);
1720 validation.timeout_duration = timeout_duration;
1721 validation
1722 }
1723
1724 pub fn start_validation(&mut self) -> TrackingResult<()> {
1726 match &self.handle {
1727 ValidationHandle::Pending {
1728 file_path,
1729 expected_count: _,
1730 config,
1731 } => {
1732 let _file_path_clone = file_path.clone();
1733 let config = config.clone();
1734
1735 let _validator = QualityValidator::new(config);
1737 let result: TrackingResult<ValidationResult> = Ok(ValidationResult::default());
1738
1739 self.handle = match result {
1741 Ok(validation_result) => ValidationHandle::Completed {
1742 file_path: file_path.clone(),
1743 result: validation_result,
1744 },
1745 Err(e) => ValidationHandle::Failed {
1746 file_path: file_path.clone(),
1747 error: e.to_string(),
1748 },
1749 };
1750
1751 Ok(())
1752 }
1753 _ => Err(ValidationError::ConfigurationError {
1754 error: "Validation is not in pending state".to_string(),
1755 }
1756 .into()),
1757 }
1758 }
1759
1760 pub fn is_complete(&self) -> bool {
1762 matches!(
1763 self.handle,
1764 ValidationHandle::Completed { .. }
1765 | ValidationHandle::Failed { .. }
1766 | ValidationHandle::Cancelled { .. }
1767 | ValidationHandle::TimedOut { .. }
1768 )
1769 }
1770
1771 pub fn is_running(&self) -> bool {
1773 matches!(self.handle, ValidationHandle::Running { .. })
1774 }
1775
1776 pub fn is_pending(&self) -> bool {
1778 matches!(self.handle, ValidationHandle::Pending { .. })
1779 }
1780
1781 pub fn cancel(&mut self) -> TrackingResult<()> {
1783 if !self.cancellable {
1784 return Err(ValidationError::ConfigurationError {
1785 error: "Validation is not cancellable".to_string(),
1786 }
1787 .into());
1788 }
1789
1790 match std::mem::replace(
1791 &mut self.handle,
1792 ValidationHandle::Cancelled {
1793 file_path: "unknown".to_string(),
1794 reason: "Cancelled by user".to_string(),
1795 },
1796 ) {
1797 ValidationHandle::Running { file_path } => {
1798 self.handle = ValidationHandle::Cancelled {
1803 file_path,
1804 reason: "Cancelled by user".to_string(),
1805 };
1806
1807 Ok(())
1808 }
1809 ValidationHandle::Pending { file_path, .. } => {
1810 self.handle = ValidationHandle::Cancelled {
1812 file_path,
1813 reason: "Cancelled before starting".to_string(),
1814 };
1815 Ok(())
1816 }
1817 other => {
1818 self.handle = other;
1820 Err(ValidationError::ConfigurationError {
1821 error: "Cannot cancel validation in current state".to_string(),
1822 }
1823 .into())
1824 }
1825 }
1826 }
1827
1828 pub async fn get_result(&mut self) -> TrackingResult<ValidationResult> {
1830 if self.is_pending() {
1832 self.start_validation()?;
1833 }
1834
1835 if let ValidationHandle::Running { file_path } = std::mem::replace(
1837 &mut self.handle,
1838 ValidationHandle::Cancelled {
1839 file_path: "temp".to_string(),
1840 reason: "temp".to_string(),
1841 },
1842 ) {
1843 let validation_result = ValidationResult::default();
1845 self.handle = ValidationHandle::Completed {
1846 file_path: file_path.clone(),
1847 result: validation_result.clone(),
1848 };
1849 Ok(validation_result)
1850 } else {
1851 match &self.handle {
1853 ValidationHandle::Completed { result, .. } => Ok(result.clone()),
1854 ValidationHandle::Failed { error, .. } => Err(ValidationError::InternalError {
1855 error: error.clone(),
1856 }
1857 .into()),
1858 ValidationHandle::Cancelled { file_path, reason } => {
1859 Err(ValidationError::CancelledError {
1860 file_path: file_path.clone(),
1861 reason: reason.clone(),
1862 }
1863 .into())
1864 }
1865 ValidationHandle::TimedOut {
1866 file_path,
1867 timeout_duration,
1868 } => Err(ValidationError::TimeoutError {
1869 file_path: file_path.clone(),
1870 timeout_duration: *timeout_duration,
1871 }
1872 .into()),
1873 _ => Err(ValidationError::ConfigurationError {
1874 error: "Validation is in unexpected state".to_string(),
1875 }
1876 .into()),
1877 }
1878 }
1879 }
1880
1881 pub fn get_status(&self) -> ValidationStatus {
1883 match &self.handle {
1884 ValidationHandle::Pending { .. } => ValidationStatus::Pending,
1885 ValidationHandle::Running { .. } => ValidationStatus::Running,
1886 ValidationHandle::Completed { .. } => ValidationStatus::Completed,
1887 ValidationHandle::Failed { .. } => ValidationStatus::Failed,
1888 ValidationHandle::Cancelled { .. } => ValidationStatus::Cancelled,
1889 ValidationHandle::TimedOut { .. } => ValidationStatus::TimedOut,
1890 }
1891 }
1892
1893 pub fn get_file_path(&self) -> String {
1895 match &self.handle {
1896 ValidationHandle::Pending { file_path, .. } => file_path.clone(),
1897 ValidationHandle::Running { file_path, .. } => file_path.clone(),
1898 ValidationHandle::Completed { file_path, .. } => file_path.clone(),
1899 ValidationHandle::Failed { file_path, .. } => file_path.clone(),
1900 ValidationHandle::Cancelled { file_path, .. } => file_path.clone(),
1901 ValidationHandle::TimedOut { file_path, .. } => file_path.clone(),
1902 }
1903 }
1904
1905 pub fn set_timeout(&mut self, timeout_duration: Duration) {
1907 self.timeout_duration = timeout_duration;
1908 }
1909
1910 pub fn set_cancellable(&mut self, cancellable: bool) {
1912 self.cancellable = cancellable;
1913 }
1914
1915 pub async fn await_result(mut self) -> TrackingResult<ValidationResult> {
1917 self.get_result().await
1918 }
1919}
1920
1921#[derive(Parser, Debug, Clone)]
1923#[command(name = "export")]
1924#[command(about = "Export memory tracking data with configurable validation")]
1925pub struct ExportArgs {
1926 #[arg(long, value_enum, default_value = "fast")]
1928 pub mode: ExportMode,
1929
1930 #[arg(long, value_enum, default_value = "deferred")]
1932 pub validation: ValidationTiming,
1933
1934 #[arg(long)]
1936 pub disable_validation: bool,
1937
1938 #[arg(long, short = 'o')]
1940 pub output: PathBuf,
1941
1942 #[arg(long, default_value = "30")]
1944 pub timeout: u64,
1945
1946 #[arg(long, short = 'v')]
1948 pub verbose: bool,
1949
1950 #[arg(long, default_value = "0.1")]
1952 pub max_data_loss_rate: f64,
1953
1954 #[arg(long, default_value = "1024")]
1956 pub min_file_size: usize,
1957
1958 #[arg(long, default_value = "104857600")] pub max_file_size: usize,
1961}
1962
1963impl ExportArgs {
1964 pub fn validate(&self) -> Result<(), String> {
1966 if self.output.as_os_str().is_empty() {
1968 return Err("Output path cannot be empty".to_string());
1969 }
1970
1971 if let Some(parent) = self.output.parent() {
1973 if !parent.exists() {
1974 return Err(format!(
1975 "Output directory does not exist: {}",
1976 parent.display()
1977 ));
1978 }
1979 }
1980
1981 if self.timeout == 0 {
1983 return Err("Timeout must be greater than 0 seconds".to_string());
1984 }
1985
1986 if self.timeout > 3600 {
1987 return Err("Timeout cannot exceed 3600 seconds (1 hour)".to_string());
1988 }
1989
1990 if self.max_data_loss_rate < 0.0 || self.max_data_loss_rate > 100.0 {
1992 return Err("Max data loss rate must be between 0.0 and 100.0".to_string());
1993 }
1994
1995 if self.min_file_size >= self.max_file_size {
1997 return Err("Minimum file size must be less than maximum file size".to_string());
1998 }
1999
2000 if self.disable_validation && self.validation == ValidationTiming::Inline {
2002 return Err("Cannot use inline validation when validation is disabled".to_string());
2003 }
2004
2005 if self.mode == ExportMode::Fast && self.validation == ValidationTiming::Inline {
2007 tracing::warn!("Warning: Fast mode with inline validation may impact performance");
2008 }
2009
2010 if self.mode == ExportMode::Slow && self.validation == ValidationTiming::Disabled {
2011 tracing::warn!("Warning: Slow mode with disabled validation reduces thoroughness");
2012 }
2013
2014 Ok(())
2015 }
2016
2017 pub fn to_export_config(&self) -> ExportConfig {
2019 let validation_timing = if self.disable_validation {
2020 ValidationTiming::Disabled
2021 } else {
2022 self.validation
2023 };
2024
2025 let mut config = ExportConfig::new(self.mode, validation_timing);
2026
2027 config.validation_config.max_data_loss_rate = self.max_data_loss_rate / 100.0; config.validation_config.min_expected_file_size = self.min_file_size;
2030 config.validation_config.max_expected_file_size = self.max_file_size;
2031 config.validation_config.verbose_logging = self.verbose;
2032
2033 let warnings = config.validate_and_fix();
2035 for warning in warnings {
2036 tracing::warn!("Warning: {}", warning);
2037 }
2038
2039 config
2040 }
2041
2042 pub fn get_timeout_duration(&self) -> Duration {
2044 Duration::from_secs(self.timeout)
2045 }
2046
2047 pub fn print_mode_help() {
2049 tracing::info!("Export Modes:");
2050 tracing::info!(" fast - Prioritize speed over comprehensive validation");
2051 tracing::info!(" - Disables JSON and encoding validation");
2052 tracing::info!(" - Uses minimal validation checks");
2053 tracing::info!(" - Best for performance-critical scenarios");
2054 tracing::info!("");
2055 tracing::info!(" slow - Perform thorough validation during export");
2056 tracing::info!(" - Enables all validation types");
2057 tracing::info!(" - Comprehensive error checking");
2058 tracing::info!(" - Best for data integrity assurance");
2059 tracing::info!("");
2060 tracing::info!(" auto - Automatically choose based on data size");
2061 tracing::info!(" - Uses fast mode for large datasets");
2062 tracing::info!(" - Uses slow mode for smaller datasets");
2063 tracing::info!(" - Balanced approach for general use");
2064 }
2065
2066 pub fn print_validation_help() {
2068 tracing::info!("Validation Timing:");
2069 tracing::info!(" inline - Validate during export (blocks I/O)");
2070 tracing::info!(" - Validation happens synchronously");
2071 tracing::info!(" - Export fails immediately on validation errors");
2072 tracing::info!(" - Best for critical data integrity requirements");
2073 tracing::info!("");
2074 tracing::info!(" deferred - Validate after export (async)");
2075 tracing::info!(" - Export completes quickly");
2076 tracing::info!(" - Validation runs in background");
2077 tracing::info!(" - Best for performance with validation");
2078 tracing::info!("");
2079 tracing::info!(" disabled - No validation performed");
2080 tracing::info!(" - Maximum performance");
2081 tracing::info!(" - No data integrity checks");
2082 tracing::info!(" - Use only when validation is not needed");
2083 }
2084}
2085
2086#[derive(Debug, Clone)]
2088pub struct ValidationReport {
2089 pub total_validations: usize,
2091 pub successful_validations: usize,
2093 pub failed_validations: usize,
2095 pub success_rate: f64,
2097 pub avg_validation_time_ms: f64,
2099 pub total_issues_found: usize,
2101 pub total_issues_fixed: usize,
2103 pub validation_type_breakdown: HashMap<ValidationType, ValidationTypeStats>,
2105}
2106
2107impl ValidationReport {
2108 pub fn print_detailed_report(&self) {
2110 tracing::info!("\n🔍 Data Quality Validation Report");
2111 tracing::info!("==================");
2112
2113 tracing::info!("📊 Overall Statistics:");
2114 tracing::info!(" Total validations: {}", self.total_validations);
2115 tracing::info!(
2116 " Successful validations: {} ({:.1}%)",
2117 self.successful_validations,
2118 self.success_rate
2119 );
2120 tracing::info!(" Failed validations: {}", self.failed_validations);
2121 tracing::info!(
2122 " Average validation time: {:.2}ms",
2123 self.avg_validation_time_ms
2124 );
2125 tracing::info!(" Issues found: {}", self.total_issues_found);
2126 tracing::info!(" Issues fixed: {}", self.total_issues_fixed);
2127
2128 if !self.validation_type_breakdown.is_empty() {
2129 tracing::info!("\n🔍 Validation Type Statistics:");
2130 for (validation_type, stats) in &self.validation_type_breakdown {
2131 let success_rate = if stats.executions > 0 {
2132 (stats.successes as f64 / stats.executions as f64) * 100.0
2133 } else {
2134 0.0
2135 };
2136 tracing::info!(" {validation_type:?}: {} executions, {:.1}% success rate, {:.2}ms average time",
2137 stats.executions, success_rate, stats.avg_execution_time_ms);
2138 }
2139 }
2140 }
2141}
2142
2143impl fmt::Display for IssueType {
2144 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2145 match self {
2146 IssueType::MissingData => write!(f, "Missing Data"),
2147 IssueType::CorruptedData => write!(f, "Corrupted Data"),
2148 IssueType::InconsistentData => write!(f, "Inconsistent Data"),
2149 IssueType::InvalidFormat => write!(f, "Invalid Format"),
2150 IssueType::SizeAnomaly => write!(f, "Size Anomaly"),
2151 IssueType::EncodingError => write!(f, "Encoding Error"),
2152 IssueType::StructuralError => write!(f, "Structural Error"),
2153 IssueType::CountMismatch => write!(f, "Count Mismatch"),
2154 }
2155 }
2156}
2157
2158#[derive(Debug, Clone, Serialize, Deserialize)]
2160pub struct StreamingValidationConfig {
2161 pub chunk_size: usize,
2163 pub max_buffer_size: usize,
2165 pub enable_progress_reporting: bool,
2167 pub progress_report_interval: usize,
2169 pub enable_interruption: bool,
2171 pub enable_resume: bool,
2173 pub checkpoint_interval: usize,
2175}
2176
2177impl Default for StreamingValidationConfig {
2178 fn default() -> Self {
2179 Self {
2180 chunk_size: 64 * 1024, max_buffer_size: 16 * 1024 * 1024, enable_progress_reporting: true,
2183 progress_report_interval: 1024 * 1024, enable_interruption: true,
2185 enable_resume: true,
2186 checkpoint_interval: 10 * 1024 * 1024, }
2188 }
2189}
2190
2191#[derive(Debug, Clone)]
2193pub struct ValidationProgress {
2194 pub total_bytes: u64,
2196 pub processed_bytes: u64,
2198 pub progress_percentage: f64,
2200 pub current_phase: ValidationPhase,
2202 pub estimated_time_remaining_secs: Option<f64>,
2204 pub processing_speed_bps: f64,
2206 pub issues_found: usize,
2208 pub current_chunk: usize,
2210 pub total_chunks: usize,
2212}
2213
2214#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2216pub enum ValidationPhase {
2217 Initializing,
2219 ReadingMetadata,
2221 ValidatingStructure,
2223 ValidatingContent,
2225 ValidatingEncoding,
2227 Finalizing,
2229 Completed,
2231 Interrupted,
2233 Failed,
2235}
2236
2237impl fmt::Display for ValidationPhase {
2238 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2239 match self {
2240 ValidationPhase::Initializing => write!(f, "Initializing"),
2241 ValidationPhase::ReadingMetadata => write!(f, "Reading metadata"),
2242 ValidationPhase::ValidatingStructure => write!(f, "Validating structure"),
2243 ValidationPhase::ValidatingContent => write!(f, "Validating content"),
2244 ValidationPhase::ValidatingEncoding => write!(f, "Validating encoding"),
2245 ValidationPhase::Finalizing => write!(f, "Finalizing"),
2246 ValidationPhase::Completed => write!(f, "Completed"),
2247 ValidationPhase::Interrupted => write!(f, "Interrupted"),
2248 ValidationPhase::Failed => write!(f, "Failed"),
2249 }
2250 }
2251}
2252
2253#[derive(Debug, Clone, Serialize, Deserialize)]
2255pub struct ValidationCheckpoint {
2256 pub file_path: String,
2258 pub byte_offset: u64,
2260 pub issues_found: Vec<ValidationIssue>,
2262 pub phase: ValidationPhase,
2264 pub timestamp: std::time::SystemTime,
2266 pub config: ValidationConfig,
2268 pub streaming_config: StreamingValidationConfig,
2270}
2271
2272pub struct EnhancedStreamingValidator {
2274 config: ValidationConfig,
2276 streaming_config: StreamingValidationConfig,
2278 progress: Option<ValidationProgress>,
2280 interrupted: std::sync::Arc<std::sync::atomic::AtomicBool>,
2282 #[allow(clippy::type_complexity)]
2284 progress_callback: Option<Box<dyn Fn(&ValidationProgress) + Send + Sync>>,
2285 checkpoint: Option<ValidationCheckpoint>,
2287}
2288
2289impl EnhancedStreamingValidator {
2290 pub fn new(config: ValidationConfig, streaming_config: StreamingValidationConfig) -> Self {
2292 Self {
2293 config,
2294 streaming_config,
2295 progress: None,
2296 interrupted: std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),
2297 progress_callback: None,
2298 checkpoint: None,
2299 }
2300 }
2301
2302 pub fn set_progress_callback<F>(&mut self, callback: F)
2304 where
2305 F: Fn(&ValidationProgress) + Send + Sync + 'static,
2306 {
2307 self.progress_callback = Some(Box::new(callback));
2308 }
2309
2310 pub fn interrupt(&self) {
2312 self.interrupted
2313 .store(true, std::sync::atomic::Ordering::Relaxed);
2314 }
2315
2316 pub fn is_interrupted(&self) -> bool {
2318 self.interrupted.load(std::sync::atomic::Ordering::Relaxed)
2319 }
2320
2321 pub fn get_progress(&self) -> Option<&ValidationProgress> {
2323 self.progress.as_ref()
2324 }
2325
2326 pub async fn save_checkpoint<P: AsRef<Path>>(&self, checkpoint_path: P) -> TrackingResult<()> {
2328 if let Some(checkpoint) = &self.checkpoint {
2329 let checkpoint_data = serde_json::to_string_pretty(checkpoint).map_err(|e| {
2330 ExportError::DataQualityError {
2331 validation_type: ValidationType::JsonStructure,
2332 expected: "Serializable checkpoint".to_string(),
2333 actual: format!("Serialization failed: {e}"),
2334 affected_records: 0,
2335 }
2336 })?;
2337
2338 fs::write(checkpoint_path, checkpoint_data).map_err(|e| {
2339 ExportError::DataQualityError {
2340 validation_type: ValidationType::FileSize,
2341 expected: "Writable checkpoint file".to_string(),
2342 actual: format!("Write failed: {e}"),
2343 affected_records: 0,
2344 }
2345 })?;
2346 }
2347
2348 Ok(())
2349 }
2350
2351 pub async fn load_checkpoint<P: AsRef<Path>>(
2353 &mut self,
2354 checkpoint_path: P,
2355 ) -> TrackingResult<()> {
2356 let checkpoint_data =
2357 fs::read_to_string(checkpoint_path).map_err(|e| ExportError::DataQualityError {
2358 validation_type: ValidationType::FileSize,
2359 expected: "Readable checkpoint file".to_string(),
2360 actual: format!("Read failed: {e}"),
2361 affected_records: 0,
2362 })?;
2363
2364 let checkpoint: ValidationCheckpoint =
2365 serde_json::from_str(&checkpoint_data).map_err(|e| ExportError::DataQualityError {
2366 validation_type: ValidationType::JsonStructure,
2367 expected: "Valid checkpoint JSON".to_string(),
2368 actual: format!("Deserialization failed: {e}"),
2369 affected_records: 0,
2370 })?;
2371
2372 self.checkpoint = Some(checkpoint);
2373 Ok(())
2374 }
2375
2376 pub async fn validate_stream_async<R>(
2378 &mut self,
2379 mut reader: R,
2380 total_size: Option<u64>,
2381 ) -> TrackingResult<ValidationResult>
2382 where
2383 R: std::io::Read,
2384 {
2385 let start_time = std::time::Instant::now();
2386 let mut issues = Vec::new();
2387 let mut processed_bytes = 0u64;
2388 let total_bytes = total_size.unwrap_or(0);
2389
2390 self.progress = Some(ValidationProgress {
2392 total_bytes,
2393 processed_bytes: 0,
2394 progress_percentage: 0.0,
2395 current_phase: ValidationPhase::Initializing,
2396 estimated_time_remaining_secs: None,
2397 processing_speed_bps: 0.0,
2398 issues_found: 0,
2399 current_chunk: 0,
2400 total_chunks: if total_bytes > 0 {
2401 (total_bytes as usize).div_ceil(self.streaming_config.chunk_size)
2402 } else {
2403 0
2404 },
2405 });
2406
2407 self.update_progress(ValidationPhase::ValidatingStructure);
2408
2409 let mut buffer = Vec::with_capacity(self.streaming_config.max_buffer_size);
2410 let mut chunk_buffer = vec![0u8; self.streaming_config.chunk_size];
2411 let mut chunk_count = 0;
2412 let validation_start = std::time::Instant::now();
2413
2414 loop {
2415 if self.is_interrupted() {
2417 self.update_progress(ValidationPhase::Interrupted);
2418 break;
2419 }
2420
2421 let bytes_read =
2423 reader
2424 .read(&mut chunk_buffer)
2425 .map_err(|e| ExportError::DataQualityError {
2426 validation_type: ValidationType::JsonStructure,
2427 expected: "Readable stream data".to_string(),
2428 actual: format!("Read failed: {e}"),
2429 affected_records: 0,
2430 })?;
2431
2432 if bytes_read == 0 {
2433 break; }
2435
2436 processed_bytes += bytes_read as u64;
2437 chunk_count += 1;
2438
2439 buffer.extend_from_slice(&chunk_buffer[..bytes_read]);
2441
2442 if buffer.len() >= self.streaming_config.max_buffer_size
2444 || self.is_complete_json_structure(&buffer)
2445 {
2446 self.validate_buffer_chunk(&buffer, &mut issues)?;
2447 buffer.clear();
2448 }
2449
2450 if let Some(progress) = &mut self.progress {
2452 progress.processed_bytes = processed_bytes;
2453 progress.current_chunk = chunk_count;
2454 progress.progress_percentage = if total_bytes > 0 {
2455 (processed_bytes as f64 / total_bytes as f64) * 100.0
2456 } else {
2457 0.0
2458 };
2459
2460 let elapsed = validation_start.elapsed().as_secs_f64();
2462 if elapsed > 0.0 {
2463 progress.processing_speed_bps = processed_bytes as f64 / elapsed;
2464
2465 if total_bytes > 0 && progress.processing_speed_bps > 0.0 {
2467 let remaining_bytes = total_bytes - processed_bytes;
2468 progress.estimated_time_remaining_secs =
2469 Some(remaining_bytes as f64 / progress.processing_speed_bps);
2470 }
2471 }
2472
2473 progress.issues_found = issues.len();
2474
2475 if let Some(callback) = &self.progress_callback {
2477 if processed_bytes % (self.streaming_config.progress_report_interval as u64)
2478 == 0
2479 {
2480 callback(progress);
2481 }
2482 }
2483 }
2484
2485 if self.streaming_config.enable_resume
2487 && processed_bytes % (self.streaming_config.checkpoint_interval as u64) == 0
2488 {
2489 self.create_checkpoint(
2490 processed_bytes,
2491 &issues,
2492 ValidationPhase::ValidatingStructure,
2493 );
2494 }
2495 }
2496
2497 if !buffer.is_empty() {
2499 self.validate_buffer_chunk(&buffer, &mut issues)?;
2500 }
2501
2502 self.update_progress(ValidationPhase::Finalizing);
2503
2504 let validation_time = start_time.elapsed().as_millis() as u64;
2505 let is_valid = issues
2506 .iter()
2507 .all(|issue| issue.severity != IssueSeverity::Critical);
2508
2509 let result = ValidationResult {
2510 is_valid,
2511 validation_type: ValidationType::JsonStructure,
2512 message: if is_valid {
2513 format!(
2514 "Streaming validation completed successfully. Processed {processed_bytes} bytes in {chunk_count} chunks.",
2515 )
2516 } else {
2517 format!(
2518 "Streaming validation failed with {} issues. Processed {processed_bytes} bytes in {chunk_count} chunks.",
2519 issues.len()
2520 )
2521 },
2522 issues,
2523 validation_time_ms: validation_time,
2524 data_size: processed_bytes as usize,
2525 };
2526
2527 self.update_progress(ValidationPhase::Completed);
2528
2529 Ok(result)
2530 }
2531
2532 fn is_complete_json_structure(&self, buffer: &[u8]) -> bool {
2534 let mut brace_count = 0;
2536 let mut in_string = false;
2537 let mut escape_next = false;
2538
2539 for &byte in buffer {
2540 if escape_next {
2541 escape_next = false;
2542 continue;
2543 }
2544
2545 match byte {
2546 b'\\' if in_string => escape_next = true,
2547 b'"' => in_string = !in_string,
2548 b'{' if !in_string => brace_count += 1,
2549 b'}' if !in_string => {
2550 brace_count -= 1;
2551 if brace_count == 0 {
2552 return true; }
2554 }
2555 _ => {}
2556 }
2557 }
2558
2559 false
2560 }
2561
2562 fn validate_buffer_chunk(
2564 &self,
2565 buffer: &[u8],
2566 issues: &mut Vec<ValidationIssue>,
2567 ) -> TrackingResult<()> {
2568 match serde_json::from_slice::<serde_json::Value>(buffer) {
2570 Ok(json_value) => {
2571 self.validate_json_content(&json_value, issues)?;
2573 }
2574 Err(e) => {
2575 if !e.to_string().contains("EOF") && !e.to_string().contains("unexpected end") {
2577 issues.push(ValidationIssue {
2578 issue_type: IssueType::InvalidFormat,
2579 description: format!("JSON parsing failed: {e}"),
2580 severity: IssueSeverity::High,
2581 affected_data: format!("Buffer chunk ({} bytes)", buffer.len()),
2582 suggested_fix: Some("Check JSON format and structure".to_string()),
2583 auto_fixable: false,
2584 });
2585 }
2586 }
2587 }
2588
2589 Ok(())
2590 }
2591
2592 #[allow(clippy::only_used_in_recursion)]
2594 fn validate_json_content(
2595 &self,
2596 json_value: &serde_json::Value,
2597 issues: &mut Vec<ValidationIssue>,
2598 ) -> TrackingResult<()> {
2599 match json_value {
2600 serde_json::Value::Object(obj) => {
2601 if obj.is_empty() {
2603 issues.push(ValidationIssue {
2604 issue_type: IssueType::StructuralError,
2605 description: "Empty JSON object found".to_string(),
2606 severity: IssueSeverity::Low,
2607 affected_data: "JSON object".to_string(),
2608 suggested_fix: Some("Ensure objects contain meaningful data".to_string()),
2609 auto_fixable: false,
2610 });
2611 }
2612
2613 for (key, value) in obj {
2615 if key.is_empty() {
2616 issues.push(ValidationIssue {
2617 issue_type: IssueType::StructuralError,
2618 description: "Empty key found in JSON object".to_string(),
2619 severity: IssueSeverity::Medium,
2620 affected_data: format!("Object key: '{key}'"),
2621 suggested_fix: Some("Use meaningful key names".to_string()),
2622 auto_fixable: false,
2623 });
2624 }
2625 self.validate_json_content(value, issues)?;
2626 }
2627 }
2628 serde_json::Value::Array(arr) => {
2629 if arr.is_empty() {
2631 issues.push(ValidationIssue {
2632 issue_type: IssueType::StructuralError,
2633 description: "Empty JSON array found".to_string(),
2634 severity: IssueSeverity::Low,
2635 affected_data: "JSON array".to_string(),
2636 suggested_fix: Some(
2637 "Consider removing empty arrays or adding default values".to_string(),
2638 ),
2639 auto_fixable: true,
2640 });
2641 }
2642
2643 for value in arr {
2645 self.validate_json_content(value, issues)?;
2646 }
2647 }
2648 serde_json::Value::String(s) => {
2649 if s.is_empty() {
2651 issues.push(ValidationIssue {
2652 issue_type: IssueType::StructuralError,
2653 description: "Empty string value found".to_string(),
2654 severity: IssueSeverity::Low,
2655 affected_data: "String value".to_string(),
2656 suggested_fix: Some(
2657 "Use null instead of empty strings where appropriate".to_string(),
2658 ),
2659 auto_fixable: true,
2660 });
2661 }
2662 }
2663 _ => {
2664 }
2666 }
2667
2668 Ok(())
2669 }
2670
2671 fn update_progress(&mut self, phase: ValidationPhase) {
2673 if let Some(progress) = &mut self.progress {
2674 progress.current_phase = phase;
2675 }
2676 }
2677
2678 fn create_checkpoint(
2680 &mut self,
2681 byte_offset: u64,
2682 issues: &[ValidationIssue],
2683 phase: ValidationPhase,
2684 ) {
2685 self.checkpoint = Some(ValidationCheckpoint {
2686 file_path: "stream".to_string(), byte_offset,
2688 issues_found: issues.to_vec(),
2689 phase,
2690 timestamp: std::time::SystemTime::now(),
2691 config: self.config.clone(),
2692 streaming_config: self.streaming_config.clone(),
2693 });
2694 }
2695}
2696
2697#[cfg(test)]
2698mod tests {
2699 use super::*;
2700 use crate::analysis::unsafe_ffi_tracker::UnsafeFFIStats;
2701 use crate::core::types::{AllocationInfo, MemoryStats, ScopeInfo};
2702 use crate::export::data_localizer::LocalizedExportData;
2703 use crate::export::parallel_shard_processor::ProcessedShard;
2704 use std::fs;
2705 use std::time::{Duration, Instant};
2706 use tempfile::TempDir;
2707
2708 fn create_test_allocation(
2709 ptr: usize,
2710 size: usize,
2711 type_name: Option<String>,
2712 var_name: Option<String>,
2713 ) -> AllocationInfo {
2714 AllocationInfo {
2715 ptr,
2716 size,
2717 var_name,
2718 type_name,
2719 scope_name: None,
2720 timestamp_alloc: 1000,
2721 timestamp_dealloc: None,
2722 thread_id: "test_thread".to_string(),
2723 borrow_count: 0,
2724 stack_trace: None,
2725 is_leaked: false,
2726 lifetime_ms: None,
2727 borrow_info: None,
2728 clone_info: None,
2729 ownership_history_available: false,
2730 smart_pointer_info: None,
2731 memory_layout: None,
2732 generic_info: None,
2733 dynamic_type_info: None,
2734 runtime_state: None,
2735 stack_allocation: None,
2736 temporary_object: None,
2737 fragmentation_analysis: None,
2738 generic_instantiation: None,
2739 type_relationships: None,
2740 type_usage: None,
2741 function_call_tracking: None,
2742 lifecycle_tracking: None,
2743 access_tracking: None,
2744 drop_chain_analysis: None,
2745 }
2746 }
2747
2748 fn create_test_export_data(allocations: Vec<AllocationInfo>) -> LocalizedExportData {
2749 LocalizedExportData {
2750 allocations,
2751 enhanced_allocations: Vec::new(),
2752 stats: MemoryStats::default(),
2753 ffi_stats: UnsafeFFIStats::default(),
2754 scope_info: Vec::<ScopeInfo>::new(),
2755 timestamp: Instant::now(),
2756 }
2757 }
2758
2759 #[test]
2760 fn test_validation_timing_enum() {
2761 assert_eq!(ValidationTiming::default(), ValidationTiming::Deferred);
2762
2763 let inline = ValidationTiming::Inline;
2765 let deferred = ValidationTiming::Deferred;
2766 let disabled = ValidationTiming::Disabled;
2767
2768 assert_ne!(inline, deferred);
2769 assert_ne!(deferred, disabled);
2770 assert_ne!(inline, disabled);
2771 }
2772
2773 #[test]
2774 fn test_export_mode_enum() {
2775 assert_eq!(ExportMode::default(), ExportMode::Fast);
2776
2777 let fast = ExportMode::Fast;
2779 let slow = ExportMode::Slow;
2780 let auto = ExportMode::Auto;
2781
2782 assert_ne!(fast, slow);
2783 assert_ne!(slow, auto);
2784 assert_ne!(fast, auto);
2785 }
2786
2787 #[test]
2788 fn test_export_config_creation() {
2789 let config = ExportConfig::new(ExportMode::Fast, ValidationTiming::Deferred);
2790 assert_eq!(config.mode, ExportMode::Fast);
2791 assert_eq!(config.validation_timing, ValidationTiming::Deferred);
2792 assert!(!config.validation_config.enable_json_validation);
2793 }
2794
2795 #[test]
2796 fn test_export_config_fast() {
2797 let config = ExportConfig::fast();
2798 assert_eq!(config.mode, ExportMode::Fast);
2799 assert_eq!(config.validation_timing, ValidationTiming::Deferred);
2800 assert!(!config.validation_config.enable_json_validation);
2801 assert!(!config.validation_config.enable_encoding_validation);
2802 }
2803
2804 #[test]
2805 fn test_export_config_slow() {
2806 let config = ExportConfig::slow();
2807 assert_eq!(config.mode, ExportMode::Slow);
2808 assert_eq!(config.validation_timing, ValidationTiming::Inline);
2809 assert!(config.validation_config.enable_json_validation);
2810 assert!(config.validation_config.enable_encoding_validation);
2811 }
2812
2813 #[test]
2814 fn test_export_config_auto() {
2815 let config = ExportConfig::auto();
2816 assert_eq!(config.mode, ExportMode::Auto);
2817 assert_eq!(config.validation_timing, ValidationTiming::Deferred);
2818 }
2819
2820 #[test]
2821 fn test_export_config_default() {
2822 let config = ExportConfig::default();
2823 assert_eq!(config.mode, ExportMode::Fast);
2824 assert_eq!(config.validation_timing, ValidationTiming::Deferred);
2825 }
2826
2827 #[test]
2828 fn test_export_config_validate_and_fix_fast_inline_conflict() {
2829 let mut config = ExportConfig::new(ExportMode::Fast, ValidationTiming::Inline);
2830 let warnings = config.validate_and_fix();
2831
2832 assert_eq!(config.validation_timing, ValidationTiming::Deferred);
2833 assert!(!warnings.is_empty());
2834 assert!(warnings[0].contains("Fast mode with inline validation conflicts"));
2835 }
2836
2837 #[test]
2838 fn test_export_config_validate_and_fix_slow_disabled_conflict() {
2839 let mut config = ExportConfig::new(ExportMode::Slow, ValidationTiming::Disabled);
2840 let warnings = config.validate_and_fix();
2841
2842 assert_eq!(config.validation_timing, ValidationTiming::Deferred);
2843 assert!(!warnings.is_empty());
2844 assert!(warnings[0].contains("Slow mode with disabled validation conflicts"));
2845 }
2846
2847 #[test]
2848 fn test_export_config_validate_and_fix_fast_mode_optimizations() {
2849 let mut config = ExportConfig::new(ExportMode::Fast, ValidationTiming::Deferred);
2850 config.validation_config.enable_json_validation = true;
2851 config.validation_config.enable_encoding_validation = true;
2852
2853 let warnings = config.validate_and_fix();
2854
2855 assert!(!config.validation_config.enable_json_validation);
2856 assert!(!config.validation_config.enable_encoding_validation);
2857 assert_eq!(warnings.len(), 2);
2858 assert!(warnings[0].contains("Fast mode should not enable JSON validation"));
2859 assert!(warnings[1].contains("Fast mode should not enable encoding validation"));
2860 }
2861
2862 #[test]
2863 fn test_export_config_validate_and_fix_slow_mode_requirements() {
2864 let mut config = ExportConfig::new(ExportMode::Slow, ValidationTiming::Inline);
2865 config.validation_config.enable_json_validation = false;
2866 config.validation_config.enable_encoding_validation = false;
2867
2868 let warnings = config.validate_and_fix();
2869
2870 assert!(config.validation_config.enable_json_validation);
2871 assert!(config.validation_config.enable_encoding_validation);
2872 assert_eq!(warnings.len(), 2);
2873 assert!(warnings[0].contains("Slow mode should enable comprehensive validation"));
2874 assert!(warnings[1].contains("Slow mode should enable comprehensive validation"));
2875 }
2876
2877 #[test]
2878 fn test_export_mode_manager_creation() {
2879 let manager = ExportModeManager::new();
2880 assert_eq!(manager.default_mode, ExportMode::Fast);
2881 assert_eq!(manager.auto_threshold, 10 * 1024 * 1024);
2882 assert_eq!(manager.performance_threshold_ms, 5000);
2883 }
2884
2885 #[test]
2886 fn test_export_mode_manager_with_settings() {
2887 let manager = ExportModeManager::with_settings(ExportMode::Slow, 5 * 1024 * 1024, 3000);
2888 assert_eq!(manager.default_mode, ExportMode::Slow);
2889 assert_eq!(manager.auto_threshold, 5 * 1024 * 1024);
2890 assert_eq!(manager.performance_threshold_ms, 3000);
2891 }
2892
2893 #[test]
2894 fn test_export_mode_manager_determine_optimal_mode() {
2895 let manager = ExportModeManager::with_settings(ExportMode::Auto, 5 * 1024 * 1024, 3000);
2896
2897 let small_mode = manager.determine_optimal_mode(1024 * 1024); assert_eq!(small_mode, ExportMode::Slow);
2900
2901 let large_mode = manager.determine_optimal_mode(10 * 1024 * 1024); assert_eq!(large_mode, ExportMode::Fast);
2904
2905 let fixed_manager =
2907 ExportModeManager::with_settings(ExportMode::Slow, 5 * 1024 * 1024, 3000);
2908 let fixed_mode = fixed_manager.determine_optimal_mode(10 * 1024 * 1024);
2909 assert_eq!(fixed_mode, ExportMode::Slow);
2910 }
2911
2912 #[test]
2913 fn test_export_mode_manager_create_config_for_mode() {
2914 let manager = ExportModeManager::new();
2915
2916 let fast_config = manager.create_config_for_mode(ExportMode::Fast);
2917 assert_eq!(fast_config.mode, ExportMode::Fast);
2918 assert_eq!(fast_config.validation_timing, ValidationTiming::Deferred);
2919
2920 let slow_config = manager.create_config_for_mode(ExportMode::Slow);
2921 assert_eq!(slow_config.mode, ExportMode::Slow);
2922 assert_eq!(slow_config.validation_timing, ValidationTiming::Inline);
2923
2924 let auto_config = manager.create_config_for_mode(ExportMode::Auto);
2925 assert_eq!(auto_config.mode, ExportMode::Auto);
2926 assert_eq!(auto_config.validation_timing, ValidationTiming::Deferred);
2927 }
2928
2929 #[test]
2930 fn test_export_mode_manager_create_auto_config() {
2931 let manager = ExportModeManager::with_settings(ExportMode::Auto, 5 * 1024 * 1024, 3000);
2932
2933 let small_config = manager.create_auto_config(1024 * 1024);
2935 assert_eq!(small_config.mode, ExportMode::Slow);
2936
2937 let large_config = manager.create_auto_config(10 * 1024 * 1024);
2939 assert_eq!(large_config.mode, ExportMode::Fast);
2940 }
2941
2942 #[test]
2943 fn test_validation_config_default() {
2944 let config = ValidationConfig::default();
2945 assert!(!config.enable_json_validation);
2946 assert!(config.enable_integrity_validation);
2947 assert!(config.enable_count_validation);
2948 assert!(config.enable_size_validation);
2949 assert!(!config.enable_encoding_validation);
2950 assert_eq!(config.max_data_loss_rate, 0.1);
2951 assert_eq!(config.min_expected_file_size, 1024);
2952 assert_eq!(config.max_expected_file_size, 100 * 1024 * 1024);
2953 assert!(!config.verbose_logging);
2954 }
2955
2956 #[test]
2957 fn test_validation_config_for_fast_mode() {
2958 let config = ValidationConfig::for_fast_mode();
2959 assert!(!config.enable_json_validation);
2960 assert!(!config.enable_integrity_validation);
2961 assert!(!config.enable_count_validation);
2962 assert!(config.enable_size_validation);
2963 assert!(!config.enable_encoding_validation);
2964 assert_eq!(config.max_data_loss_rate, 1.0);
2965 assert_eq!(config.min_expected_file_size, 512);
2966 assert!(!config.verbose_logging);
2967 }
2968
2969 #[test]
2970 fn test_validation_config_for_slow_mode() {
2971 let config = ValidationConfig::for_slow_mode();
2972 assert!(config.enable_json_validation);
2973 assert!(config.enable_integrity_validation);
2974 assert!(config.enable_count_validation);
2975 assert!(config.enable_size_validation);
2976 assert!(config.enable_encoding_validation);
2977 assert_eq!(config.max_data_loss_rate, 0.01);
2978 assert_eq!(config.min_expected_file_size, 1024);
2979 assert!(config.verbose_logging);
2980 }
2981
2982 #[test]
2983 fn test_validation_config_with_strategy() {
2984 let minimal = ValidationConfig::with_strategy(ValidationStrategy::Minimal);
2985 assert!(!minimal.enable_json_validation);
2986 assert!(!minimal.enable_integrity_validation);
2987
2988 let balanced = ValidationConfig::with_strategy(ValidationStrategy::Balanced);
2989 assert!(!balanced.enable_json_validation);
2990 assert!(balanced.enable_integrity_validation);
2991
2992 let comprehensive = ValidationConfig::with_strategy(ValidationStrategy::Comprehensive);
2993 assert!(comprehensive.enable_json_validation);
2994 assert!(comprehensive.enable_integrity_validation);
2995
2996 let custom_config = ValidationConfig::for_fast_mode();
2997 let custom =
2998 ValidationConfig::with_strategy(ValidationStrategy::Custom(custom_config.clone()));
2999 assert_eq!(
3000 custom.enable_json_validation,
3001 custom_config.enable_json_validation
3002 );
3003 }
3004
3005 #[test]
3006 fn test_validation_config_conflicts_with_mode() {
3007 let config = ValidationConfig {
3008 enable_json_validation: true,
3009 enable_encoding_validation: true,
3010 max_data_loss_rate: 0.05,
3011 ..ValidationConfig::default()
3012 };
3013
3014 let fast_conflicts = config.conflicts_with_mode(&ExportMode::Fast);
3015 assert_eq!(fast_conflicts.len(), 3);
3016 assert!(fast_conflicts[0].contains("JSON validation enabled in fast mode"));
3017 assert!(fast_conflicts[1].contains("Encoding validation enabled in fast mode"));
3018 assert!(fast_conflicts[2].contains("Strict data loss rate in fast mode"));
3019
3020 let slow_config = ValidationConfig {
3021 enable_json_validation: false,
3022 enable_integrity_validation: false,
3023 enable_encoding_validation: false,
3024 ..ValidationConfig::default()
3025 };
3026
3027 let slow_conflicts = slow_config.conflicts_with_mode(&ExportMode::Slow);
3028 assert_eq!(slow_conflicts.len(), 3);
3029 assert!(slow_conflicts[0].contains("JSON validation disabled in slow mode"));
3030 assert!(slow_conflicts[1].contains("Integrity validation disabled in slow mode"));
3031 assert!(slow_conflicts[2].contains("Encoding validation disabled in slow mode"));
3032
3033 let auto_conflicts = config.conflicts_with_mode(&ExportMode::Auto);
3034 assert!(auto_conflicts.is_empty());
3035 }
3036
3037 #[test]
3038 fn test_validation_config_apply_safe_defaults() {
3039 let mut config = ValidationConfig {
3040 enable_json_validation: true,
3041 enable_encoding_validation: true,
3042 max_data_loss_rate: 0.05,
3043 ..ValidationConfig::default()
3044 };
3045
3046 config.apply_safe_defaults_for_mode(&ExportMode::Fast);
3047 assert!(!config.enable_json_validation);
3048 assert!(!config.enable_encoding_validation);
3049 assert!(!config.enable_integrity_validation);
3050 assert!(config.max_data_loss_rate >= 0.5);
3051 assert!(!config.verbose_logging);
3052
3053 let mut slow_config = ValidationConfig {
3054 enable_json_validation: false,
3055 max_data_loss_rate: 0.5,
3056 ..ValidationConfig::default()
3057 };
3058
3059 slow_config.apply_safe_defaults_for_mode(&ExportMode::Slow);
3060 assert!(slow_config.enable_json_validation);
3061 assert!(slow_config.enable_integrity_validation);
3062 assert!(slow_config.enable_count_validation);
3063 assert!(slow_config.enable_size_validation);
3064 assert!(slow_config.enable_encoding_validation);
3065 assert!(slow_config.max_data_loss_rate <= 0.1);
3066 assert!(slow_config.verbose_logging);
3067 }
3068
3069 #[test]
3070 fn test_quality_validator_creation() {
3071 let config = ValidationConfig::default();
3072 let validator = QualityValidator::new(config.clone());
3073 assert_eq!(
3074 validator.config.enable_json_validation,
3075 config.enable_json_validation
3076 );
3077 assert_eq!(validator.stats.total_validations, 0);
3078
3079 let default_validator = QualityValidator::new_default();
3080 assert_eq!(default_validator.stats.total_validations, 0);
3081 }
3082
3083 #[test]
3084 fn test_quality_validator_validate_source_data_empty() {
3085 let mut validator = QualityValidator::new_default();
3086 let empty_data = create_test_export_data(vec![]);
3087
3088 let result = validator.validate_source_data(&empty_data).unwrap();
3089 assert!(!result.is_valid); assert_eq!(result.validation_type, ValidationType::DataIntegrity);
3091 assert!(!result.issues.is_empty());
3092 assert!(result.issues[0]
3093 .description
3094 .contains("Allocation data is empty"));
3095 assert_eq!(result.issues[0].severity, IssueSeverity::Critical);
3096
3097 let stats = validator.get_stats();
3098 assert_eq!(stats.total_validations, 1);
3099 assert_eq!(stats.failed_validations, 1);
3100 assert_eq!(stats.successful_validations, 0);
3101 }
3102
3103 #[test]
3104 fn test_quality_validator_validate_source_data_valid() {
3105 let mut validator = QualityValidator::new_default();
3106 let allocations = vec![
3107 create_test_allocation(
3108 0x1000,
3109 64,
3110 Some("String".to_string()),
3111 Some("var1".to_string()),
3112 ),
3113 create_test_allocation(
3114 0x2000,
3115 128,
3116 Some("Vec<i32>".to_string()),
3117 Some("var2".to_string()),
3118 ),
3119 ];
3120 let data = create_test_export_data(allocations);
3121
3122 let result = validator.validate_source_data(&data).unwrap();
3123 assert!(result.is_valid);
3124 assert_eq!(result.validation_type, ValidationType::DataIntegrity);
3125 assert!(result.validation_time_ms > 0);
3126 assert_eq!(result.data_size, 2);
3127
3128 let stats = validator.get_stats();
3129 assert_eq!(stats.total_validations, 1);
3130 assert_eq!(stats.successful_validations, 1);
3131 assert_eq!(stats.failed_validations, 0);
3132 }
3133
3134 #[test]
3135 fn test_quality_validator_validate_source_data_with_issues() {
3136 let mut validator = QualityValidator::new_default();
3137 let mut allocations = vec![
3138 create_test_allocation(
3139 0x1000,
3140 0,
3141 Some("String".to_string()),
3142 Some("var1".to_string()),
3143 ), create_test_allocation(
3145 0x1000,
3146 128,
3147 Some("Vec<i32>".to_string()),
3148 Some("var2".to_string()),
3149 ), ];
3151 allocations[1].timestamp_dealloc = Some(500); let data = create_test_export_data(allocations);
3154
3155 let result = validator.validate_source_data(&data).unwrap();
3156 assert!(result.is_valid); assert!(!result.issues.is_empty());
3158
3159 let size_zero_issue = result
3161 .issues
3162 .iter()
3163 .find(|i| i.description.contains("size 0"));
3164 assert!(size_zero_issue.is_some());
3165 assert_eq!(size_zero_issue.unwrap().severity, IssueSeverity::Medium);
3166
3167 let duplicate_ptr_issue = result
3168 .issues
3169 .iter()
3170 .find(|i| i.description.contains("duplicate pointers"));
3171 assert!(duplicate_ptr_issue.is_some());
3172 assert_eq!(duplicate_ptr_issue.unwrap().severity, IssueSeverity::High);
3173
3174 let timestamp_issue = result.issues.iter().find(|i| {
3175 i.description
3176 .contains("deallocation time is before allocation time")
3177 });
3178 assert!(timestamp_issue.is_some());
3179 assert_eq!(timestamp_issue.unwrap().severity, IssueSeverity::High);
3180 }
3181
3182 #[test]
3183 fn test_quality_validator_validate_processed_shards() {
3184 let mut validator = QualityValidator::new_default();
3185 let allocations = vec![create_test_allocation(
3186 0x1000,
3187 64,
3188 Some("String".to_string()),
3189 Some("var1".to_string()),
3190 )];
3191
3192 let shard_data = serde_json::to_vec(&allocations).unwrap();
3193 let shards = vec![ProcessedShard {
3194 shard_index: 0,
3195 allocation_count: 1,
3196 data: shard_data,
3197 processing_time_ms: 10,
3198 }];
3199
3200 let result = validator.validate_processed_shards(&shards, 1).unwrap();
3201 assert!(result.is_valid);
3202 assert_eq!(result.validation_type, ValidationType::JsonStructure);
3203 }
3204
3205 #[test]
3206 fn test_quality_validator_validate_processed_shards_count_mismatch() {
3207 let mut validator = QualityValidator::new_default();
3208 let allocations = vec![create_test_allocation(
3209 0x1000,
3210 64,
3211 Some("String".to_string()),
3212 Some("var1".to_string()),
3213 )];
3214
3215 let shard_data = serde_json::to_vec(&allocations).unwrap();
3216 let shards = vec![ProcessedShard {
3217 shard_index: 0,
3218 allocation_count: 1,
3219 data: shard_data,
3220 processing_time_ms: 10,
3221 }];
3222
3223 let result = validator.validate_processed_shards(&shards, 2).unwrap();
3225 assert!(!result.is_valid); assert!(!result.issues.is_empty());
3227
3228 let count_issue = result
3229 .issues
3230 .iter()
3231 .find(|i| i.description.contains("Shard total count mismatch"));
3232 assert!(count_issue.is_some());
3233 assert_eq!(count_issue.unwrap().severity, IssueSeverity::Critical); }
3235
3236 #[test]
3237 fn test_quality_validator_validate_output_file() {
3238 let temp_dir = TempDir::new().unwrap();
3239 let file_path = temp_dir.path().join("test_output.json");
3240
3241 let test_data = serde_json::json!({
3243 "allocations": [
3244 {"ptr": 4096, "size": 64, "var_name": "test_var", "type_name": "String"}
3245 ]
3246 });
3247 fs::write(
3248 &file_path,
3249 serde_json::to_string_pretty(&test_data).unwrap(),
3250 )
3251 .unwrap();
3252
3253 let mut validator = QualityValidator::new_default();
3254 let result = validator
3255 .validate_output_file(file_path.to_str().unwrap(), 1)
3256 .unwrap();
3257
3258 assert!(result.is_valid);
3259 assert_eq!(result.validation_type, ValidationType::FileSize);
3260 assert!(result.data_size > 0);
3261 }
3262
3263 #[test]
3264 fn test_quality_validator_validate_output_file_missing() {
3265 let mut validator = QualityValidator::new_default();
3266 let result = validator
3267 .validate_output_file("/nonexistent/file.json", 1)
3268 .unwrap();
3269
3270 assert!(!result.is_valid);
3271 assert!(!result.issues.is_empty());
3272 assert!(result.issues[0]
3273 .description
3274 .contains("Output file does not exist"));
3275 assert_eq!(result.issues[0].severity, IssueSeverity::Critical);
3276 }
3277
3278 #[test]
3279 fn test_quality_validator_validate_output_file_size_too_small() {
3280 let temp_dir = TempDir::new().unwrap();
3281 let file_path = temp_dir.path().join("small_file.json");
3282
3283 fs::write(&file_path, "{}").unwrap();
3285
3286 let config = ValidationConfig {
3287 min_expected_file_size: 1000, ..ValidationConfig::default()
3289 };
3290 let mut validator = QualityValidator::new(config);
3291
3292 let result = validator
3293 .validate_output_file(file_path.to_str().unwrap(), 1)
3294 .unwrap();
3295
3296 assert!(result.is_valid); assert!(!result.issues.is_empty());
3298 let size_issue = result
3299 .issues
3300 .iter()
3301 .find(|i| i.description.contains("File size too small"));
3302 assert!(size_issue.is_some());
3303 assert_eq!(size_issue.unwrap().severity, IssueSeverity::High);
3304 }
3305
3306 #[test]
3307 fn test_quality_validator_validate_output_file_size_too_large() {
3308 let temp_dir = TempDir::new().unwrap();
3309 let file_path = temp_dir.path().join("large_file.json");
3310
3311 let large_content = "x".repeat(1000);
3313 fs::write(&file_path, large_content).unwrap();
3314
3315 let config = ValidationConfig {
3316 max_expected_file_size: 500, ..ValidationConfig::default()
3318 };
3319 let mut validator = QualityValidator::new(config);
3320
3321 let result = validator
3322 .validate_output_file(file_path.to_str().unwrap(), 1)
3323 .unwrap();
3324
3325 assert!(result.is_valid); assert!(!result.issues.is_empty());
3327 let size_issue = result
3328 .issues
3329 .iter()
3330 .find(|i| i.description.contains("File size too large"));
3331 assert!(size_issue.is_some());
3332 assert_eq!(size_issue.unwrap().severity, IssueSeverity::Medium);
3333 }
3334
3335 #[test]
3336 fn test_quality_validator_validate_output_file_invalid_json() {
3337 let temp_dir = TempDir::new().unwrap();
3338 let file_path = temp_dir.path().join("invalid.json");
3339
3340 fs::write(&file_path, "{ invalid json }").unwrap();
3342
3343 let config = ValidationConfig {
3344 enable_json_validation: true,
3345 ..ValidationConfig::default()
3346 };
3347 let mut validator = QualityValidator::new(config);
3348
3349 let result = validator
3350 .validate_output_file(file_path.to_str().unwrap(), 1)
3351 .unwrap();
3352
3353 assert!(!result.is_valid);
3354 assert!(!result.issues.is_empty());
3355 let json_issue = result
3356 .issues
3357 .iter()
3358 .find(|i| i.description.contains("JSON parsing failed"));
3359 assert!(json_issue.is_some());
3360 assert_eq!(json_issue.unwrap().severity, IssueSeverity::Critical);
3361 }
3362
3363 #[test]
3364 fn test_quality_validator_validate_output_file_missing_allocations_field() {
3365 let temp_dir = TempDir::new().unwrap();
3366 let file_path = temp_dir.path().join("no_allocations.json");
3367
3368 let test_data = serde_json::json!({"other_field": "value"});
3370 fs::write(&file_path, serde_json::to_string(&test_data).unwrap()).unwrap();
3371
3372 let config = ValidationConfig {
3373 enable_json_validation: true,
3374 ..ValidationConfig::default()
3375 };
3376 let mut validator = QualityValidator::new(config);
3377
3378 let result = validator
3379 .validate_output_file(file_path.to_str().unwrap(), 1)
3380 .unwrap();
3381
3382 assert!(!result.is_valid);
3383 assert!(!result.issues.is_empty());
3384 let missing_field_issue = result
3385 .issues
3386 .iter()
3387 .find(|i| i.description.contains("Missing allocations field"));
3388 assert!(missing_field_issue.is_some());
3389 assert_eq!(
3390 missing_field_issue.unwrap().severity,
3391 IssueSeverity::Critical
3392 );
3393 }
3394
3395 #[test]
3396 fn test_quality_validator_validate_output_file_allocations_not_array() {
3397 let temp_dir = TempDir::new().unwrap();
3398 let file_path = temp_dir.path().join("allocations_not_array.json");
3399
3400 let test_data = serde_json::json!({"allocations": "not_an_array"});
3402 fs::write(&file_path, serde_json::to_string(&test_data).unwrap()).unwrap();
3403
3404 let config = ValidationConfig {
3405 enable_json_validation: true,
3406 ..ValidationConfig::default()
3407 };
3408 let mut validator = QualityValidator::new(config);
3409
3410 let result = validator
3411 .validate_output_file(file_path.to_str().unwrap(), 1)
3412 .unwrap();
3413
3414 assert!(!result.is_valid);
3415 assert!(!result.issues.is_empty());
3416 let structure_issue = result
3417 .issues
3418 .iter()
3419 .find(|i| i.description.contains("allocations field is not an array"));
3420 assert!(structure_issue.is_some());
3421 assert_eq!(structure_issue.unwrap().severity, IssueSeverity::Critical);
3422 }
3423
3424 #[test]
3425 fn test_quality_validator_validate_output_file_count_mismatch() {
3426 let temp_dir = TempDir::new().unwrap();
3427 let file_path = temp_dir.path().join("count_mismatch.json");
3428
3429 let test_data = serde_json::json!({
3431 "allocations": [
3432 {"ptr": 4096, "size": 64},
3433 {"ptr": 8192, "size": 128}
3434 ]
3435 });
3436 fs::write(&file_path, serde_json::to_string(&test_data).unwrap()).unwrap();
3437
3438 let config = ValidationConfig {
3439 enable_json_validation: true,
3440 max_data_loss_rate: 0.1, ..ValidationConfig::default()
3442 };
3443 let mut validator = QualityValidator::new(config);
3444
3445 let result = validator
3447 .validate_output_file(file_path.to_str().unwrap(), 5)
3448 .unwrap();
3449
3450 assert!(!result.is_valid);
3451 assert!(!result.issues.is_empty());
3452 let count_issue = result
3453 .issues
3454 .iter()
3455 .find(|i| i.description.contains("File allocation count mismatch"));
3456 assert!(count_issue.is_some());
3457 assert_eq!(count_issue.unwrap().severity, IssueSeverity::Critical);
3458 }
3459
3460 #[test]
3461 fn test_quality_validator_generate_validation_report() {
3462 let mut validator = QualityValidator::new_default();
3463
3464 let allocations = vec![create_test_allocation(
3466 0x1000,
3467 64,
3468 Some("String".to_string()),
3469 Some("var1".to_string()),
3470 )];
3471 let mut data = create_test_export_data(allocations.clone());
3472 data.stats.total_allocations = allocations.len();
3474
3475 let _result1 = validator.validate_source_data(&data).unwrap();
3476 let _result2 = validator.validate_source_data(&data).unwrap();
3477
3478 let report = validator.generate_validation_report();
3479
3480 assert_eq!(report.total_validations, 2);
3481 assert_eq!(report.successful_validations, 2);
3482 assert_eq!(report.failed_validations, 0);
3483 assert_eq!(report.success_rate, 100.0);
3484 assert!(report.avg_validation_time_ms >= 0.0); assert_eq!(report.total_issues_found, 0);
3486 assert_eq!(report.total_issues_fixed, 0);
3487 assert!(!report.validation_type_breakdown.is_empty());
3488 }
3489
3490 #[test]
3491 fn test_async_validator_creation() {
3492 let config = ValidationConfig::default();
3493 let validator = AsyncValidator::new(config.clone());
3494 assert_eq!(
3495 validator.config.enable_json_validation,
3496 config.enable_json_validation
3497 );
3498 assert_eq!(validator.stats.total_validations, 0);
3499
3500 let default_validator = AsyncValidator::new_default();
3501 assert_eq!(default_validator.stats.total_validations, 0);
3502 }
3503
3504 #[test]
3507 fn test_deferred_validation_creation() {
3508 let config = ValidationConfig::default();
3509 let validation = DeferredValidation::new("/test/path.json", 100, config);
3510
3511 assert!(validation.is_pending());
3512 assert!(!validation.is_running());
3513 assert!(!validation.is_complete());
3514 assert_eq!(validation.get_status(), ValidationStatus::Pending);
3515 assert_eq!(validation.get_file_path(), "/test/path.json");
3516 }
3517
3518 #[test]
3519 fn test_deferred_validation_with_timeout() {
3520 let config = ValidationConfig::default();
3521 let timeout = Duration::from_secs(60);
3522 let validation = DeferredValidation::with_timeout("/test/path.json", 100, config, timeout);
3523
3524 assert!(validation.is_pending());
3525 assert_eq!(validation.timeout_duration, timeout);
3526 }
3527
3528 #[test]
3529 fn test_deferred_validation_start_validation() {
3530 let config = ValidationConfig::default();
3531 let mut validation = DeferredValidation::new("/test/path.json", 100, config);
3532
3533 let result = validation.start_validation();
3534 assert!(result.is_ok());
3535 assert!(validation.is_complete());
3536 }
3537
3538 #[test]
3539 fn test_deferred_validation_cancel_pending() {
3540 let config = ValidationConfig::default();
3541 let mut validation = DeferredValidation::new("/test/path.json", 100, config);
3542
3543 let result = validation.cancel();
3544 assert!(result.is_ok());
3545 assert_eq!(validation.get_status(), ValidationStatus::Cancelled);
3546 }
3547
3548 #[test]
3549 fn test_deferred_validation_set_timeout() {
3550 let config = ValidationConfig::default();
3551 let mut validation = DeferredValidation::new("/test/path.json", 100, config);
3552
3553 let new_timeout = Duration::from_secs(120);
3554 validation.set_timeout(new_timeout);
3555 assert_eq!(validation.timeout_duration, new_timeout);
3556 }
3557
3558 #[test]
3559 fn test_deferred_validation_set_cancellable() {
3560 let config = ValidationConfig::default();
3561 let mut validation = DeferredValidation::new("/test/path.json", 100, config);
3562
3563 validation.set_cancellable(false);
3564 assert!(!validation.cancellable);
3565
3566 let result = validation.cancel();
3567 assert!(result.is_err());
3568 }
3569
3570 #[test]
3573 fn test_export_args_validate_empty_output() {
3574 let args = ExportArgs {
3575 mode: ExportMode::Fast,
3576 validation: ValidationTiming::Deferred,
3577 disable_validation: false,
3578 output: PathBuf::new(),
3579 timeout: 30,
3580 verbose: false,
3581 max_data_loss_rate: 0.1,
3582 min_file_size: 1024,
3583 max_file_size: 104857600,
3584 };
3585
3586 let result = args.validate();
3587 assert!(result.is_err());
3588 assert!(result.unwrap_err().contains("Output path cannot be empty"));
3589 }
3590
3591 #[test]
3592 fn test_export_args_validate_invalid_timeout() {
3593 let temp_dir = TempDir::new().unwrap();
3594
3595 let args = ExportArgs {
3596 mode: ExportMode::Fast,
3597 validation: ValidationTiming::Deferred,
3598 disable_validation: false,
3599 output: temp_dir.path().join("output.json"),
3600 timeout: 0,
3601 verbose: false,
3602 max_data_loss_rate: 0.1,
3603 min_file_size: 1024,
3604 max_file_size: 104857600,
3605 };
3606
3607 let result = args.validate();
3608 assert!(result.is_err());
3609 assert!(result
3610 .unwrap_err()
3611 .contains("Timeout must be greater than 0 seconds"));
3612 }
3613
3614 #[test]
3615 fn test_export_args_validate_timeout_too_large() {
3616 let temp_dir = TempDir::new().unwrap();
3617
3618 let args = ExportArgs {
3619 mode: ExportMode::Fast,
3620 validation: ValidationTiming::Deferred,
3621 disable_validation: false,
3622 output: temp_dir.path().join("output.json"),
3623 timeout: 4000,
3624 verbose: false,
3625 max_data_loss_rate: 0.1,
3626 min_file_size: 1024,
3627 max_file_size: 104857600,
3628 };
3629
3630 let result = args.validate();
3631 assert!(result.is_err());
3632 assert!(result
3633 .unwrap_err()
3634 .contains("Timeout cannot exceed 3600 seconds"));
3635 }
3636
3637 #[test]
3638 fn test_export_args_validate_invalid_data_loss_rate() {
3639 let temp_dir = TempDir::new().unwrap();
3640
3641 let args = ExportArgs {
3642 mode: ExportMode::Fast,
3643 validation: ValidationTiming::Deferred,
3644 disable_validation: false,
3645 output: temp_dir.path().join("output.json"),
3646 timeout: 30,
3647 verbose: false,
3648 max_data_loss_rate: 150.0,
3649 min_file_size: 1024,
3650 max_file_size: 104857600,
3651 };
3652
3653 let result = args.validate();
3654 assert!(result.is_err());
3655 assert!(result
3656 .unwrap_err()
3657 .contains("Max data loss rate must be between 0.0 and 100.0"));
3658 }
3659
3660 #[test]
3661 fn test_export_args_validate_invalid_file_sizes() {
3662 let temp_dir = TempDir::new().unwrap();
3663
3664 let args = ExportArgs {
3665 mode: ExportMode::Fast,
3666 validation: ValidationTiming::Deferred,
3667 disable_validation: false,
3668 output: temp_dir.path().join("output.json"),
3669 timeout: 30,
3670 verbose: false,
3671 max_data_loss_rate: 0.1,
3672 min_file_size: 2048,
3673 max_file_size: 1024,
3674 };
3675
3676 let result = args.validate();
3677 assert!(result.is_err());
3678 assert!(result
3679 .unwrap_err()
3680 .contains("Minimum file size must be less than maximum file size"));
3681 }
3682
3683 #[test]
3684 fn test_export_args_validate_conflicting_options() {
3685 let temp_dir = TempDir::new().unwrap();
3686
3687 let args = ExportArgs {
3688 mode: ExportMode::Fast,
3689 validation: ValidationTiming::Inline,
3690 disable_validation: true,
3691 output: temp_dir.path().join("output.json"),
3692 timeout: 30,
3693 verbose: false,
3694 max_data_loss_rate: 0.1,
3695 min_file_size: 1024,
3696 max_file_size: 104857600,
3697 };
3698
3699 let result = args.validate();
3700 assert!(result.is_err());
3701 assert!(result
3702 .unwrap_err()
3703 .contains("Cannot use inline validation when validation is disabled"));
3704 }
3705
3706 #[test]
3707 fn test_export_args_to_export_config() {
3708 let temp_dir = TempDir::new().unwrap();
3709
3710 let args = ExportArgs {
3711 mode: ExportMode::Slow,
3712 validation: ValidationTiming::Inline,
3713 disable_validation: false,
3714 output: temp_dir.path().join("output.json"),
3715 timeout: 60,
3716 verbose: true,
3717 max_data_loss_rate: 0.5,
3718 min_file_size: 2048,
3719 max_file_size: 52428800,
3720 };
3721
3722 let config = args.to_export_config();
3723 assert_eq!(config.mode, ExportMode::Slow);
3724 assert_eq!(config.validation_timing, ValidationTiming::Inline);
3725 assert_eq!(config.validation_config.max_data_loss_rate, 0.005); assert_eq!(config.validation_config.min_expected_file_size, 2048);
3727 assert_eq!(config.validation_config.max_expected_file_size, 52428800);
3728 assert!(config.validation_config.verbose_logging);
3729 }
3730
3731 #[test]
3732 fn test_export_args_to_export_config_disabled_validation() {
3733 let temp_dir = TempDir::new().unwrap();
3734
3735 let args = ExportArgs {
3736 mode: ExportMode::Fast,
3737 validation: ValidationTiming::Inline,
3738 disable_validation: true,
3739 output: temp_dir.path().join("output.json"),
3740 timeout: 30,
3741 verbose: false,
3742 max_data_loss_rate: 0.1,
3743 min_file_size: 1024,
3744 max_file_size: 104857600,
3745 };
3746
3747 let config = args.to_export_config();
3748 assert_eq!(config.validation_timing, ValidationTiming::Disabled);
3749 }
3750
3751 #[test]
3752 fn test_export_args_get_timeout_duration() {
3753 let temp_dir = TempDir::new().unwrap();
3754
3755 let args = ExportArgs {
3756 mode: ExportMode::Fast,
3757 validation: ValidationTiming::Deferred,
3758 disable_validation: false,
3759 output: temp_dir.path().join("output.json"),
3760 timeout: 45,
3761 verbose: false,
3762 max_data_loss_rate: 0.1,
3763 min_file_size: 1024,
3764 max_file_size: 104857600,
3765 };
3766
3767 assert_eq!(args.get_timeout_duration(), Duration::from_secs(45));
3768 }
3769
3770 #[test]
3771 fn test_validation_report_print_detailed_report() {
3772 let mut type_breakdown = std::collections::HashMap::new();
3773 type_breakdown.insert(
3774 ValidationType::DataIntegrity,
3775 ValidationTypeStats {
3776 executions: 5,
3777 successes: 4,
3778 failures: 1,
3779 avg_execution_time_ms: 25.5,
3780 },
3781 );
3782
3783 let report = ValidationReport {
3784 total_validations: 10,
3785 successful_validations: 8,
3786 failed_validations: 2,
3787 success_rate: 80.0,
3788 avg_validation_time_ms: 30.2,
3789 total_issues_found: 5,
3790 total_issues_fixed: 2,
3791 validation_type_breakdown: type_breakdown,
3792 };
3793
3794 report.print_detailed_report();
3796 }
3797
3798 #[test]
3799 fn test_issue_type_display() {
3800 assert_eq!(format!("{}", IssueType::MissingData), "Missing Data");
3801 assert_eq!(format!("{}", IssueType::CorruptedData), "Corrupted Data");
3802 assert_eq!(
3803 format!("{}", IssueType::InconsistentData),
3804 "Inconsistent Data"
3805 );
3806 assert_eq!(format!("{}", IssueType::InvalidFormat), "Invalid Format");
3807 assert_eq!(format!("{}", IssueType::SizeAnomaly), "Size Anomaly");
3808 assert_eq!(format!("{}", IssueType::EncodingError), "Encoding Error");
3809 assert_eq!(
3810 format!("{}", IssueType::StructuralError),
3811 "Structural Error"
3812 );
3813 assert_eq!(format!("{}", IssueType::CountMismatch), "Count Mismatch");
3814 }
3815
3816 #[test]
3817 fn test_validation_phase_display() {
3818 assert_eq!(format!("{}", ValidationPhase::Initializing), "Initializing");
3819 assert_eq!(
3820 format!("{}", ValidationPhase::ReadingMetadata),
3821 "Reading metadata"
3822 );
3823 assert_eq!(
3824 format!("{}", ValidationPhase::ValidatingStructure),
3825 "Validating structure"
3826 );
3827 assert_eq!(
3828 format!("{}", ValidationPhase::ValidatingContent),
3829 "Validating content"
3830 );
3831 assert_eq!(
3832 format!("{}", ValidationPhase::ValidatingEncoding),
3833 "Validating encoding"
3834 );
3835 assert_eq!(format!("{}", ValidationPhase::Finalizing), "Finalizing");
3836 assert_eq!(format!("{}", ValidationPhase::Completed), "Completed");
3837 assert_eq!(format!("{}", ValidationPhase::Interrupted), "Interrupted");
3838 assert_eq!(format!("{}", ValidationPhase::Failed), "Failed");
3839 }
3840
3841 #[test]
3842 fn test_streaming_validation_config_default() {
3843 let config = StreamingValidationConfig::default();
3844 assert_eq!(config.chunk_size, 64 * 1024);
3845 assert_eq!(config.max_buffer_size, 16 * 1024 * 1024);
3846 assert!(config.enable_progress_reporting);
3847 assert_eq!(config.progress_report_interval, 1024 * 1024);
3848 assert!(config.enable_interruption);
3849 assert!(config.enable_resume);
3850 assert_eq!(config.checkpoint_interval, 10 * 1024 * 1024);
3851 }
3852
3853 #[test]
3854 fn test_enhanced_streaming_validator_creation() {
3855 let config = ValidationConfig::default();
3856 let streaming_config = StreamingValidationConfig::default();
3857 let validator = EnhancedStreamingValidator::new(config, streaming_config);
3858
3859 assert!(!validator.is_interrupted());
3860 assert!(validator.get_progress().is_none());
3861 }
3862
3863 #[test]
3864 fn test_enhanced_streaming_validator_interrupt() {
3865 let config = ValidationConfig::default();
3866 let streaming_config = StreamingValidationConfig::default();
3867 let validator = EnhancedStreamingValidator::new(config, streaming_config);
3868
3869 assert!(!validator.is_interrupted());
3870 validator.interrupt();
3871 assert!(validator.is_interrupted());
3872 }
3873
3874 #[test]
3875 fn test_enhanced_streaming_validator_set_progress_callback() {
3876 let config = ValidationConfig::default();
3877 let streaming_config = StreamingValidationConfig::default();
3878 let mut validator = EnhancedStreamingValidator::new(config, streaming_config);
3879
3880 validator.set_progress_callback(|_progress| {
3881 });
3883
3884 assert!(validator.progress_callback.is_some());
3885 }
3886
3887 #[test]
3890 fn test_export_mode_manager_optimize_config() {
3891 let manager = ExportModeManager::new();
3892 let config = ExportConfig::fast();
3893
3894 let (optimized_config, warnings) = manager.optimize_config(config, 50 * 1024 * 1024); assert_eq!(optimized_config.mode, ExportMode::Fast);
3897 if !warnings.is_empty() {
3899 assert!(warnings[0].contains("Large dataset"));
3900 }
3901 }
3902
3903 #[test]
3904 fn test_export_mode_manager_optimize_config_very_large() {
3905 let manager = ExportModeManager::new();
3906 let mut config = ExportConfig::slow();
3907 config.validation_config.enable_json_validation = true;
3908 config.validation_config.enable_encoding_validation = true;
3909
3910 let (optimized_config, warnings) = manager.optimize_config(config, 200 * 1024 * 1024); assert!(!optimized_config.validation_config.enable_json_validation);
3913 assert!(
3914 !optimized_config
3915 .validation_config
3916 .enable_encoding_validation
3917 );
3918 assert!(warnings.len() >= 2);
3919 assert!(warnings
3920 .iter()
3921 .any(|w| w.contains("Disabling JSON validation")));
3922 assert!(warnings
3923 .iter()
3924 .any(|w| w.contains("Disabling encoding validation")));
3925 }
3926
3927 #[test]
3928 fn test_export_mode_manager_get_settings() {
3929 let manager = ExportModeManager::with_settings(ExportMode::Slow, 5 * 1024 * 1024, 3000);
3930
3931 let (mode, threshold, perf_threshold) = manager.get_settings();
3932 assert_eq!(mode, ExportMode::Slow);
3933 assert_eq!(threshold, 5 * 1024 * 1024);
3934 assert_eq!(perf_threshold, 3000);
3935 }
3936
3937 #[test]
3938 fn test_validation_result_default() {
3939 let result = ValidationResult::default();
3940 assert!(result.is_valid);
3941 assert_eq!(result.validation_type, ValidationType::DataIntegrity);
3942 assert_eq!(result.message, "Default validation result");
3943 assert!(result.issues.is_empty());
3944 assert_eq!(result.validation_time_ms, 0);
3945 assert_eq!(result.data_size, 0);
3946 }
3947
3948 #[test]
3949 fn test_validation_stats_default() {
3950 let stats = ValidationStats::default();
3951 assert_eq!(stats.total_validations, 0);
3952 assert_eq!(stats.successful_validations, 0);
3953 assert_eq!(stats.failed_validations, 0);
3954 assert!(stats.validation_type_stats.is_empty());
3955 assert_eq!(stats.total_validation_time_ms, 0);
3956 assert_eq!(stats.issues_found, 0);
3957 assert_eq!(stats.issues_fixed, 0);
3958 }
3959
3960 #[test]
3961 fn test_validation_type_stats_default() {
3962 let stats = ValidationTypeStats::default();
3963 assert_eq!(stats.executions, 0);
3964 assert_eq!(stats.successes, 0);
3965 assert_eq!(stats.failures, 0);
3966 assert_eq!(stats.avg_execution_time_ms, 0.0);
3967 }
3968
3969 #[test]
3972 fn test_async_validator_file_validation_edge_cases() {
3973 let config = ValidationConfig::default();
3974 let async_validator = AsyncValidator::new(config);
3975
3976 let streaming_config = StreamingValidationConfig {
3978 chunk_size: 32 * 1024,
3979 max_buffer_size: 8 * 1024 * 1024,
3980 enable_progress_reporting: false,
3981 progress_report_interval: 512 * 1024,
3982 enable_interruption: false,
3983 enable_resume: false,
3984 checkpoint_interval: 5 * 1024 * 1024,
3985 };
3986
3987 let enhanced_validator =
3988 async_validator.create_enhanced_streaming_validator(streaming_config);
3989 assert!(!enhanced_validator.is_interrupted());
3990 assert!(enhanced_validator.get_progress().is_none());
3991 }
3992
3993 #[test]
3994 fn test_quality_validator_async_file_validation() {
3995 let temp_dir = TempDir::new().unwrap();
3996 let file_path = temp_dir.path().join("async_test.json");
3997
3998 let test_data = serde_json::json!({
4000 "allocations": [
4001 {"ptr": "0x1000", "size": 64, "var_name": "test", "type_name": "i32"}
4002 ]
4003 });
4004 fs::write(&file_path, serde_json::to_string(&test_data).unwrap()).unwrap();
4005
4006 let config = ValidationConfig::default();
4007 let _validator = QualityValidator::new(config);
4008
4009 }
4013
4014 #[test]
4015 fn test_deferred_validation_comprehensive_states() {
4016 let config = ValidationConfig::default();
4017
4018 let mut validation = DeferredValidation::new("/test/path.json", 100, config.clone());
4020
4021 assert!(validation.is_pending());
4023 assert!(!validation.is_running());
4024 assert!(!validation.is_complete());
4025
4026 let start_result = validation.start_validation();
4028 assert!(start_result.is_ok());
4029 assert!(validation.is_complete());
4030
4031 }
4035
4036 #[test]
4037 fn test_validation_handle_send_sync() {
4038 fn assert_send_sync<T: Send + Sync>(_: T) {}
4040
4041 let handle = ValidationHandle::Pending {
4042 file_path: "test.json".to_string(),
4043 expected_count: 100,
4044 config: ValidationConfig::default(),
4045 };
4046 assert_send_sync(handle);
4047
4048 let running_handle = ValidationHandle::Running {
4049 file_path: "test.json".to_string(),
4050 };
4051 assert_send_sync(running_handle);
4052
4053 let completed_handle = ValidationHandle::Completed {
4054 file_path: "test.json".to_string(),
4055 result: ValidationResult::default(),
4056 };
4057 assert_send_sync(completed_handle);
4058 }
4059
4060 #[test]
4061 fn test_export_args_help_methods() {
4062 ExportArgs::print_mode_help();
4064 ExportArgs::print_validation_help();
4065 }
4066
4067 #[test]
4068 fn test_export_args_edge_cases() {
4069 let temp_dir = TempDir::new().unwrap();
4070
4071 let non_existent_parent = temp_dir.path().join("non_existent").join("output.json");
4073 let args = ExportArgs {
4074 mode: ExportMode::Fast,
4075 validation: ValidationTiming::Deferred,
4076 disable_validation: false,
4077 output: non_existent_parent,
4078 timeout: 30,
4079 verbose: false,
4080 max_data_loss_rate: 0.1,
4081 min_file_size: 1024,
4082 max_file_size: 104857600,
4083 };
4084
4085 let result = args.validate();
4086 assert!(result.is_err());
4087 assert!(result
4088 .unwrap_err()
4089 .contains("Output directory does not exist"));
4090
4091 let warning_args = ExportArgs {
4093 mode: ExportMode::Fast,
4094 validation: ValidationTiming::Inline,
4095 disable_validation: false,
4096 output: temp_dir.path().join("output.json"),
4097 timeout: 30,
4098 verbose: false,
4099 max_data_loss_rate: 0.1,
4100 min_file_size: 1024,
4101 max_file_size: 104857600,
4102 };
4103
4104 assert!(warning_args.validate().is_ok());
4106 }
4107
4108 #[test]
4109 fn test_quality_validator_shard_validation_edge_cases() {
4110 let config = ValidationConfig::default();
4111 let mut validator = QualityValidator::new(config);
4112
4113 let empty_shards = vec![ProcessedShard {
4115 shard_index: 0,
4116 allocation_count: 1,
4117 data: Vec::new(),
4118 processing_time_ms: 10,
4119 }];
4120
4121 let result = validator
4122 .validate_processed_shards(&empty_shards, 1)
4123 .unwrap();
4124 assert!(result.is_valid); let empty_issue = result
4127 .issues
4128 .iter()
4129 .find(|i| i.description.contains("data is empty"));
4130 assert!(empty_issue.is_some());
4131 assert_eq!(empty_issue.unwrap().severity, IssueSeverity::High);
4132
4133 let large_shard = ProcessedShard {
4135 shard_index: 0,
4136 allocation_count: 1,
4137 data: vec![0u8; 2000], processing_time_ms: 10,
4139 };
4140
4141 let result = validator
4142 .validate_processed_shards(&[large_shard], 1)
4143 .unwrap();
4144 assert!(result.is_valid); let size_issue = result
4147 .issues
4148 .iter()
4149 .find(|i| i.description.contains("size abnormally large"));
4150 assert!(size_issue.is_some());
4151 assert_eq!(size_issue.unwrap().severity, IssueSeverity::Low);
4152 }
4153
4154 #[test]
4155 fn test_quality_validator_file_encoding_validation() {
4156 let temp_dir = TempDir::new().unwrap();
4157 let config = ValidationConfig {
4158 enable_encoding_validation: true,
4159 ..ValidationConfig::default()
4160 };
4161 let mut validator = QualityValidator::new(config);
4162
4163 let valid_file = temp_dir.path().join("valid_utf8.json");
4165 fs::write(&valid_file, "{}").unwrap();
4166
4167 let result = validator
4168 .validate_output_file(valid_file.to_str().unwrap(), 0)
4169 .unwrap();
4170 assert!(result.is_valid);
4171
4172 let invalid_file = temp_dir.path().join("invalid_utf8.json");
4174 fs::write(&invalid_file, [0xFF, 0xFE, 0xFD]).unwrap();
4175
4176 let result = validator
4177 .validate_output_file(invalid_file.to_str().unwrap(), 0)
4178 .unwrap();
4179 assert!(result.is_valid); let encoding_issue = result
4182 .issues
4183 .iter()
4184 .find(|i| i.description.contains("File encoding validation failed"));
4185 assert!(encoding_issue.is_some());
4186 assert_eq!(encoding_issue.unwrap().severity, IssueSeverity::High);
4187 }
4188
4189 #[test]
4190 fn test_async_validator_stream_validation() {
4191 let config = ValidationConfig::default();
4192 let _async_validator = AsyncValidator::new(config);
4193
4194 let test_data = b"{}";
4196 let _cursor = std::io::Cursor::new(test_data);
4197
4198 }
4201
4202 #[test]
4203 fn test_enhanced_streaming_validator_comprehensive() {
4204 let config = ValidationConfig::default();
4205 let streaming_config = StreamingValidationConfig::default();
4206 let mut validator = EnhancedStreamingValidator::new(config, streaming_config);
4207
4208 validator.set_progress_callback(|progress| {
4210 assert!(progress.processed_bytes <= progress.total_bytes);
4211 });
4212
4213 validator.interrupt();
4215 assert!(validator.is_interrupted());
4216
4217 }
4221
4222 #[test]
4223 fn test_validation_progress_and_checkpoint() {
4224 let progress = ValidationProgress {
4225 total_bytes: 1000,
4226 processed_bytes: 500,
4227 progress_percentage: 50.0,
4228 current_phase: ValidationPhase::ValidatingContent,
4229 estimated_time_remaining_secs: Some(10.0),
4230 processing_speed_bps: 100.0,
4231 issues_found: 2,
4232 current_chunk: 5,
4233 total_chunks: 10,
4234 };
4235
4236 assert_eq!(progress.total_bytes, 1000);
4237 assert_eq!(progress.processed_bytes, 500);
4238 assert_eq!(progress.progress_percentage, 50.0);
4239 assert_eq!(progress.current_phase, ValidationPhase::ValidatingContent);
4240
4241 let checkpoint = ValidationCheckpoint {
4242 file_path: "test.json".to_string(),
4243 byte_offset: 500,
4244 issues_found: Vec::new(),
4245 phase: ValidationPhase::ValidatingStructure,
4246 timestamp: std::time::SystemTime::now(),
4247 config: ValidationConfig::default(),
4248 streaming_config: StreamingValidationConfig::default(),
4249 };
4250
4251 assert_eq!(checkpoint.file_path, "test.json");
4252 assert_eq!(checkpoint.byte_offset, 500);
4253 assert_eq!(checkpoint.phase, ValidationPhase::ValidatingStructure);
4254 }
4255
4256 #[test]
4257 fn test_validation_issue_comprehensive() {
4258 let issue = ValidationIssue {
4259 issue_type: IssueType::CorruptedData,
4260 description: "Test corruption".to_string(),
4261 severity: IssueSeverity::Critical,
4262 affected_data: "test_data".to_string(),
4263 suggested_fix: Some("Fix the corruption".to_string()),
4264 auto_fixable: true,
4265 };
4266
4267 assert_eq!(issue.issue_type, IssueType::CorruptedData);
4268 assert_eq!(issue.severity, IssueSeverity::Critical);
4269 assert!(issue.auto_fixable);
4270 assert!(issue.suggested_fix.is_some());
4271 }
4272
4273 #[test]
4274 fn test_streaming_json_stats_calculations() {
4275 let _bytes_written = 1000u64;
4278 let allocations_written = 10u64;
4279 let total_write_time_us = 1000u64;
4280 let fields_skipped = 50u64;
4281 let buffer_reuses = 5u64;
4282 let batch_processing_time_us = 500u64;
4283
4284 let write_throughput = if total_write_time_us == 0 {
4286 0.0
4287 } else {
4288 (allocations_written as f64 * 1_000_000.0) / total_write_time_us as f64
4289 };
4290 assert_eq!(write_throughput, 10_000.0);
4291
4292 let total_potential_fields = allocations_written * 20;
4294 let field_optimization_efficiency = if total_potential_fields == 0 {
4295 0.0
4296 } else {
4297 (fields_skipped as f64 / total_potential_fields as f64) * 100.0
4298 };
4299 assert_eq!(field_optimization_efficiency, 25.0);
4300
4301 let buffer_reuse_efficiency = if allocations_written == 0 {
4303 0.0
4304 } else {
4305 (buffer_reuses as f64 / allocations_written as f64) * 100.0
4306 };
4307 assert_eq!(buffer_reuse_efficiency, 50.0);
4308
4309 let batch_processing_efficiency =
4311 if batch_processing_time_us == 0 || total_write_time_us == 0 {
4312 0.0
4313 } else {
4314 (batch_processing_time_us as f64 / total_write_time_us as f64) * 100.0
4315 };
4316 assert_eq!(batch_processing_efficiency, 50.0);
4317 }
4318
4319 #[test]
4320 fn test_export_mode_manager_default() {
4321 let manager = ExportModeManager::default();
4322 assert_eq!(manager.default_mode, ExportMode::Fast);
4323 assert_eq!(manager.auto_threshold, 10 * 1024 * 1024);
4324 assert_eq!(manager.performance_threshold_ms, 5000);
4325 }
4326}