1#[cfg(test)]
7mod tests {
8 use super::*;
9
10 use std::collections::{HashMap, HashSet};
11
12 fn create_test_config() -> AnalysisConfig {
14 AnalysisConfig {
15 target_platforms: vec![
16 "wasmtime".to_string(),
17 "wasmer".to_string(),
18 "browser".to_string(),
19 ],
20 check_api_compatibility: true,
21 check_size_constraints: true,
22 check_performance: true,
23 check_security: true,
24 strict_mode: false,
25 }
26 }
27
28 fn create_test_component_info() -> ComponentInfo {
29 let mut features = HashSet::new();
30 features.insert("memory".to_string());
31 features.insert("tables".to_string());
32
33 ComponentInfo {
34 name: "test_component".to_string(),
35 version: "1.0.0".to_string(),
36 size: 1024 * 10, exports_count: 5,
38 imports_count: 3,
39 features,
40 }
41 }
42
43 fn create_test_portability_score() -> PortabilityScore {
44 let mut platform_scores = HashMap::new();
45 platform_scores.insert("wasmtime".to_string(), 0.95);
46 platform_scores.insert("wasmer".to_string(), 0.92);
47 platform_scores.insert("browser".to_string(), 0.88);
48
49 let mut feature_scores = HashMap::new();
50 feature_scores.insert("memory".to_string(), 0.9);
51 feature_scores.insert("threading".to_string(), 0.7);
52 feature_scores.insert("simd".to_string(), 0.6);
53
54 PortabilityScore {
55 overall_score: 0.85,
56 platform_scores,
57 feature_scores,
58 api_compatibility: 0.90,
59 size_efficiency: 0.88,
60 performance_portability: 0.82,
61 security_compliance: 0.95,
62 }
63 }
64
65 fn create_test_analyzer() -> PortabilityAnalyzer {
66 PortabilityAnalyzer::new()
67 }
68
69 fn create_test_analyzer_with_config() -> PortabilityAnalyzer {
70 let config = create_test_config();
71 PortabilityAnalyzer::new_with_config(config)
72 }
73
74 #[test]
77 fn test_analysis_config_creation() {
78 let config = create_test_config();
79 assert_eq!(config.target_platforms.len(), 3);
80 assert!(config.check_api_compatibility);
81 assert!(config.check_size_constraints);
82 assert!(config.check_performance);
83 assert!(config.check_security);
84 assert!(!config.strict_mode);
85 }
86
87 #[test]
88 fn test_analysis_config_default() {
89 let config = AnalysisConfig::default();
90 assert!(!config.target_platforms.is_empty());
91 assert!(config.check_api_compatibility);
92 assert!(!config.strict_mode);
93 }
94
95 #[test]
96 fn test_analysis_config_clone() {
97 let config1 = create_test_config();
98 let config2 = config1.clone();
99 assert_eq!(config1.target_platforms, config2.target_platforms);
100 assert_eq!(config1.strict_mode, config2.strict_mode);
101 }
102
103 #[test]
104 fn test_analysis_config_serialization() {
105 let config = create_test_config();
106 let json = serde_json::to_string(&config).unwrap();
107 let deserialized: AnalysisConfig = serde_json::from_str(&json).unwrap();
108 assert_eq!(config.target_platforms, deserialized.target_platforms);
109 }
110
111 #[test]
114 fn test_component_info_creation() {
115 let info = create_test_component_info();
116 assert_eq!(info.name, "test_component");
117 assert_eq!(info.version, "1.0.0");
118 assert_eq!(info.size, 10240);
119 assert_eq!(info.exports_count, 5);
120 assert_eq!(info.imports_count, 3);
121 assert_eq!(info.features.len(), 2);
122 }
123
124 #[test]
125 fn test_component_info_without_features() {
126 let info = ComponentInfo {
127 name: "minimal".to_string(),
128 version: "0.1.0".to_string(),
129 size: 1024,
130 exports_count: 0,
131 imports_count: 0,
132 features: HashSet::new(),
133 };
134 assert_eq!(info.exports_count, 0);
135 assert_eq!(info.imports_count, 0);
136 assert!(info.features.is_empty());
137 }
138
139 #[test]
140 fn test_component_info_serialization() {
141 let info = create_test_component_info();
142 let json = serde_json::to_string(&info).unwrap();
143 let deserialized: ComponentInfo = serde_json::from_str(&json).unwrap();
144 assert_eq!(info.name, deserialized.name);
145 assert_eq!(info.size, deserialized.size);
146 }
147
148 #[test]
151 fn test_portability_score_creation() {
152 let score = create_test_portability_score();
153 assert_eq!(score.overall_score, 0.85);
154 assert_eq!(score.platform_scores.len(), 3);
155 assert_eq!(score.feature_scores.len(), 3);
156 assert_eq!(score.api_compatibility, 0.90);
157 assert_eq!(score.security_compliance, 0.95);
158 }
159
160 #[test]
161 fn test_portability_score_platform_lookup() {
162 let score = create_test_portability_score();
163 assert_eq!(*score.platform_scores.get("wasmtime").unwrap(), 0.95);
164 assert_eq!(*score.platform_scores.get("wasmer").unwrap(), 0.92);
165 assert_eq!(*score.platform_scores.get("browser").unwrap(), 0.88);
166 }
167
168 #[test]
169 fn test_portability_score_feature_lookup() {
170 let score = create_test_portability_score();
171 assert_eq!(*score.feature_scores.get("memory").unwrap(), 0.9);
172 assert_eq!(*score.feature_scores.get("threading").unwrap(), 0.7);
173 assert_eq!(*score.feature_scores.get("simd").unwrap(), 0.6);
174 }
175
176 #[test]
177 fn test_portability_score_perfect() {
178 let mut score = create_test_portability_score();
179 score.overall_score = 1.0;
180 score.api_compatibility = 1.0;
181 score.size_efficiency = 1.0;
182 score.performance_portability = 1.0;
183 score.security_compliance = 1.0;
184
185 assert_eq!(score.overall_score, 1.0);
186 assert_eq!(score.api_compatibility, 1.0);
187 assert_eq!(score.size_efficiency, 1.0);
188 assert_eq!(score.performance_portability, 1.0);
189 assert_eq!(score.security_compliance, 1.0);
190 }
191
192 #[test]
193 fn test_portability_score_failing() {
194 let mut score = create_test_portability_score();
195 score.overall_score = 0.4;
196 assert!(score.overall_score < 0.5);
197 }
198
199 #[test]
202 fn test_compatibility_issue_creation() {
203 let issue = CompatibilityIssue {
204 severity: IssueSeverity::Warning,
205 category: IssueCategory::ApiIncompatibility,
206 affected_platforms: vec!["browser".to_string()],
207 description: "Missing API support".to_string(),
208 fix_suggestion: Some("Use polyfill or alternative API".to_string()),
209 };
210
211 assert_eq!(issue.severity, IssueSeverity::Warning);
212 assert_eq!(issue.category, IssueCategory::ApiIncompatibility);
213 assert_eq!(issue.affected_platforms.len(), 1);
214 assert!(issue.fix_suggestion.is_some());
215 }
216
217 #[test]
218 fn test_compatibility_issue_severity_levels() {
219 let severities = [IssueSeverity::Error,
220 IssueSeverity::Warning,
221 IssueSeverity::Info];
222
223 assert_eq!(severities.len(), 3);
224 assert_ne!(severities[0], severities[1]);
225 }
226
227 #[test]
228 fn test_compatibility_issue_categories() {
229 let categories = [IssueCategory::ApiIncompatibility,
230 IssueCategory::FeatureNotSupported,
231 IssueCategory::Performance,
232 IssueCategory::SizeConstraint,
233 IssueCategory::Security,
234 IssueCategory::Configuration];
235
236 assert_eq!(categories.len(), 6);
237 }
238
239 #[test]
242 fn test_recommendation_creation() {
243 let rec = Recommendation {
244 priority: RecommendationPriority::High,
245 title: "Optimize memory usage".to_string(),
246 description: "Reduce memory allocation".to_string(),
247 impact: 0.1,
248 platforms: vec!["wasmtime".to_string(), "wasmer".to_string()],
249 };
250
251 assert_eq!(rec.priority, RecommendationPriority::High);
252 assert_eq!(rec.title, "Optimize memory usage");
253 assert_eq!(rec.impact, 0.1);
254 assert_eq!(rec.platforms.len(), 2);
255 }
256
257 #[test]
258 fn test_recommendation_priorities() {
259 let priorities = [RecommendationPriority::Low,
260 RecommendationPriority::Medium,
261 RecommendationPriority::High];
262
263 assert_eq!(priorities.len(), 3);
264 }
265
266 #[test]
267 fn test_recommendation_serialization() {
268 let rec = Recommendation {
269 priority: RecommendationPriority::Medium,
270 title: "Test recommendation".to_string(),
271 description: "Test description".to_string(),
272 impact: 0.05,
273 platforms: vec!["browser".to_string()],
274 };
275
276 let json = serde_json::to_string(&rec).unwrap();
277 let deserialized: Recommendation = serde_json::from_str(&json).unwrap();
278 assert_eq!(rec.title, deserialized.title);
279 assert_eq!(rec.impact, deserialized.impact);
280 }
281
282 #[test]
285 fn test_platform_support_creation() {
286 let support = PlatformSupport {
287 platform: "wasmtime".to_string(),
288 support_level: SupportLevel::Full,
289 compatibility_score: 0.9,
290 required_modifications: vec![],
291 runtime_requirements: Some("1.0-2.0".to_string()),
292 };
293
294 assert_eq!(support.platform, "wasmtime");
295 assert_eq!(support.support_level, SupportLevel::Full);
296 assert!(support.required_modifications.is_empty());
297 assert_eq!(support.compatibility_score, 0.9);
298 }
299
300 #[test]
301 fn test_platform_support_partial() {
302 let support = PlatformSupport {
303 platform: "browser".to_string(),
304 support_level: SupportLevel::Partial,
305 compatibility_score: 0.7,
306 required_modifications: vec!["Remove filesystem access".to_string()],
307 runtime_requirements: None,
308 };
309
310 assert_eq!(support.support_level, SupportLevel::Partial);
311 assert_eq!(support.required_modifications.len(), 1);
312 assert_eq!(support.compatibility_score, 0.7);
313 assert!(support.runtime_requirements.is_none());
314 }
315
316 #[test]
319 fn test_feature_usage_creation() {
320 let mut core = HashSet::new();
321 core.insert("memory".to_string());
322 core.insert("tables".to_string());
323
324 let mut proposal = HashSet::new();
325 proposal.insert("simd".to_string());
326
327 let usage = FeatureUsage {
328 core_features: core,
329 proposal_features: proposal,
330 platform_specific: HashMap::new(),
331 compatibility: HashMap::new(),
332 };
333
334 assert_eq!(usage.core_features.len(), 2);
335 assert_eq!(usage.proposal_features.len(), 1);
336 assert!(usage.platform_specific.is_empty());
337 }
338
339 #[test]
340 fn test_feature_usage_with_proposals() {
341 let mut proposals = HashSet::new();
342 proposals.insert("threads".to_string());
343 proposals.insert("simd".to_string());
344
345 let usage = FeatureUsage {
346 core_features: HashSet::new(),
347 proposal_features: proposals,
348 platform_specific: HashMap::new(),
349 compatibility: HashMap::new(),
350 };
351
352 assert_eq!(usage.proposal_features.len(), 2);
353 assert!(usage.proposal_features.contains(&"threads".to_string()));
354 }
355
356 #[test]
359 fn test_size_analysis_creation() {
360 let analysis = SizeAnalysis {
361 total_size: 10240,
362 code_size: 6000,
363 data_size: 2000,
364 custom_sections_size: 1240,
365 section_sizes: HashMap::new(),
366 platform_limits: HashMap::new(),
367 };
368
369 assert_eq!(analysis.total_size, 10240);
370 assert_eq!(analysis.code_size, 6000);
371 assert_eq!(analysis.data_size, 2000);
372 assert_eq!(analysis.custom_sections_size, 1240);
373 }
374
375 #[test]
376 fn test_size_analysis_with_sections() {
377 let mut sections = HashMap::new();
378 sections.insert("code".to_string(), 6000);
379 sections.insert("data".to_string(), 2000);
380 sections.insert("debug".to_string(), 500);
381
382 let analysis = SizeAnalysis {
383 total_size: 10240,
384 code_size: 6000,
385 data_size: 2000,
386 custom_sections_size: 1500,
387 section_sizes: sections,
388 platform_limits: HashMap::new(),
389 };
390
391 assert_eq!(analysis.section_sizes.len(), 3);
392 assert_eq!(*analysis.section_sizes.get("debug").unwrap(), 500);
393 }
394
395 #[test]
398 fn test_analyzer_creation() {
399 let analyzer = create_test_analyzer();
400 assert_eq!(analyzer.config.target_platforms.len(), 6);
402 }
403
404 #[test]
405 fn test_analyzer_with_custom_config() {
406 let mut config = create_test_config();
407 config.strict_mode = true;
408 config.check_performance = false;
409
410 let analyzer = PortabilityAnalyzer::new_with_config(config);
411 assert!(analyzer.config.strict_mode);
412 assert!(!analyzer.config.check_performance);
413 }
414
415 #[test]
416 fn test_analyzer_default_config() {
417 let analyzer = create_test_analyzer();
418 assert!(analyzer.config.check_api_compatibility);
420 assert!(analyzer.config.check_size_constraints);
421 assert!(analyzer.config.check_performance);
422 assert!(analyzer.config.check_security);
423 }
424
425 #[test]
426 fn test_analyzer_custom_config() {
427 let analyzer = create_test_analyzer_with_config();
428 assert_eq!(analyzer.config.target_platforms.len(), 3);
430 assert!(analyzer.config.target_platforms.contains(&"wasmtime".to_string()));
431 assert!(analyzer.config.target_platforms.contains(&"wasmer".to_string()));
432 assert!(analyzer.config.target_platforms.contains(&"browser".to_string()));
433 }
434
435 #[test]
436 fn test_analyzer_strict_mode() {
437 let mut config = create_test_config();
438 config.strict_mode = true;
439 let analyzer = PortabilityAnalyzer::new_with_config(config);
440 assert!(analyzer.config.strict_mode);
441
442 let mut config2 = create_test_config();
444 config2.strict_mode = false;
445 let analyzer2 = PortabilityAnalyzer::new_with_config(config2);
446 assert!(!analyzer2.config.strict_mode);
447 }
448
449 #[test]
452 fn test_portability_report_creation() {
453 let report = PortabilityReport {
454 component_info: create_test_component_info(),
455 score: create_test_portability_score(),
456 issues: vec![],
457 recommendations: vec![],
458 platform_support: HashMap::new(),
459 feature_usage: FeatureUsage {
460 core_features: HashSet::new(),
461 proposal_features: HashSet::new(),
462 platform_specific: HashMap::new(),
463 compatibility: HashMap::new(),
464 },
465 size_analysis: SizeAnalysis {
466 total_size: 0,
467 code_size: 0,
468 data_size: 0,
469 custom_sections_size: 0,
470 section_sizes: HashMap::new(),
471 platform_limits: HashMap::new(),
472 },
473 };
474
475 assert_eq!(report.component_info.name, "test_component");
476 assert_eq!(report.score.overall_score, 0.85);
477 assert!(report.issues.is_empty());
478 assert!(report.recommendations.is_empty());
479 }
480
481 #[test]
482 fn test_portability_report_with_issues() {
483 let issue = CompatibilityIssue {
484 severity: IssueSeverity::Warning,
485 category: IssueCategory::FeatureNotSupported,
486 affected_platforms: vec!["browser".to_string()],
487 description: "Threading not supported".to_string(),
488 fix_suggestion: Some("Use web workers".to_string()),
489 };
490
491 let report = PortabilityReport {
492 component_info: create_test_component_info(),
493 score: create_test_portability_score(),
494 issues: vec![issue],
495 recommendations: vec![],
496 platform_support: HashMap::new(),
497 feature_usage: FeatureUsage {
498 core_features: HashSet::new(),
499 proposal_features: HashSet::new(),
500 platform_specific: HashMap::new(),
501 compatibility: HashMap::new(),
502 },
503 size_analysis: SizeAnalysis {
504 total_size: 0,
505 code_size: 0,
506 data_size: 0,
507 custom_sections_size: 0,
508 section_sizes: HashMap::new(),
509 platform_limits: HashMap::new(),
510 },
511 };
512
513 assert_eq!(report.issues.len(), 1);
514 assert_eq!(report.issues[0].severity, IssueSeverity::Warning);
515 }
516
517 #[test]
518 fn test_portability_report_serialization() {
519 let report = PortabilityReport {
520 component_info: create_test_component_info(),
521 score: create_test_portability_score(),
522 issues: vec![],
523 recommendations: vec![],
524 platform_support: HashMap::new(),
525 feature_usage: FeatureUsage {
526 core_features: HashSet::new(),
527 proposal_features: HashSet::new(),
528 platform_specific: HashMap::new(),
529 compatibility: HashMap::new(),
530 },
531 size_analysis: SizeAnalysis {
532 total_size: 0,
533 code_size: 0,
534 data_size: 0,
535 custom_sections_size: 0,
536 section_sizes: HashMap::new(),
537 platform_limits: HashMap::new(),
538 },
539 };
540
541 let json = serde_json::to_string(&report).unwrap();
542 let deserialized: PortabilityReport = serde_json::from_str(&json).unwrap();
543
544 assert_eq!(report.component_info.name, deserialized.component_info.name);
545 assert_eq!(report.score.overall_score, deserialized.score.overall_score);
546 }
547
548 #[test]
551 fn test_full_portability_analysis_workflow() {
552 let report = PortabilityReport {
554 component_info: create_test_component_info(),
555 score: create_test_portability_score(),
556 issues: vec![
557 CompatibilityIssue {
558 severity: IssueSeverity::Info,
559 category: IssueCategory::Performance,
560 affected_platforms: vec!["browser".to_string()],
561 description: "Performance may vary".to_string(),
562 fix_suggestion: None,
563 },
564 ],
565 recommendations: vec![
566 Recommendation {
567 priority: RecommendationPriority::Low,
568 title: "Consider optimization".to_string(),
569 description: "Optimize for browser platform".to_string(),
570 impact: 0.05,
571 platforms: vec!["browser".to_string()],
572 },
573 ],
574 platform_support: HashMap::new(),
575 feature_usage: FeatureUsage {
576 core_features: HashSet::new(),
577 proposal_features: HashSet::new(),
578 platform_specific: HashMap::new(),
579 compatibility: HashMap::new(),
580 },
581 size_analysis: SizeAnalysis {
582 total_size: 10240,
583 code_size: 6000,
584 data_size: 2000,
585 custom_sections_size: 1240,
586 section_sizes: HashMap::new(),
587 platform_limits: HashMap::new(),
588 },
589 };
590
591 assert!(!report.component_info.name.is_empty());
593 assert!(report.score.overall_score >= 0.0);
594 assert!(report.score.overall_score <= 1.0);
595 assert_eq!(report.issues.len(), 1);
596 assert_eq!(report.recommendations.len(), 1);
597 }
598
599 #[test]
600 fn test_enum_variants() {
601 let severities = vec![
603 IssueSeverity::Error,
604 IssueSeverity::Warning,
605 IssueSeverity::Info,
606 ];
607 for s in &severities {
608 assert!(matches!(s, IssueSeverity::Error | IssueSeverity::Warning | IssueSeverity::Info));
609 }
610
611 let categories = [IssueCategory::ApiIncompatibility,
613 IssueCategory::FeatureNotSupported,
614 IssueCategory::SizeConstraint,
615 IssueCategory::Performance,
616 IssueCategory::Security,
617 IssueCategory::Configuration];
618 assert_eq!(categories.len(), 6);
619
620 let priorities = [RecommendationPriority::Low,
622 RecommendationPriority::Medium,
623 RecommendationPriority::High];
624 assert_eq!(priorities.len(), 3);
625
626 let levels = [SupportLevel::Full,
628 SupportLevel::Partial,
629 SupportLevel::None];
630 assert_eq!(levels.len(), 3);
631 }
632
633 #[test]
634 fn test_complex_portability_score() {
635 let mut platform_scores = HashMap::new();
636 platform_scores.insert("wasmtime".to_string(), 1.0);
637 platform_scores.insert("wasmer".to_string(), 0.95);
638 platform_scores.insert("browser".to_string(), 0.75);
639 platform_scores.insert("node".to_string(), 0.85);
640
641 let mut feature_scores = HashMap::new();
642 feature_scores.insert("memory".to_string(), 1.0);
643 feature_scores.insert("tables".to_string(), 1.0);
644 feature_scores.insert("threading".to_string(), 0.5);
645 feature_scores.insert("simd".to_string(), 0.8);
646 feature_scores.insert("bulk-memory".to_string(), 0.9);
647
648 let score = PortabilityScore {
649 overall_score: 0.875,
650 platform_scores,
651 feature_scores,
652 api_compatibility: 0.92,
653 size_efficiency: 0.95,
654 performance_portability: 0.78,
655 security_compliance: 1.0,
656 };
657
658 assert_eq!(score.platform_scores.len(), 4);
659 assert_eq!(score.feature_scores.len(), 5);
660 assert_eq!(score.security_compliance, 1.0);
661 }
662
663 #[test]
664 fn test_edge_case_scores() {
665 let min_score = PortabilityScore {
667 overall_score: 0.0,
668 platform_scores: HashMap::new(),
669 feature_scores: HashMap::new(),
670 api_compatibility: 0.0,
671 size_efficiency: 0.0,
672 performance_portability: 0.0,
673 security_compliance: 0.0,
674 };
675
676 assert_eq!(min_score.overall_score, 0.0);
677 assert!(min_score.platform_scores.is_empty());
678
679 let mut perfect_platforms = HashMap::new();
681 perfect_platforms.insert("all".to_string(), 1.0);
682
683 let max_score = PortabilityScore {
684 overall_score: 1.0,
685 platform_scores: perfect_platforms,
686 feature_scores: HashMap::new(),
687 api_compatibility: 1.0,
688 size_efficiency: 1.0,
689 performance_portability: 1.0,
690 security_compliance: 1.0,
691 };
692
693 assert_eq!(max_score.overall_score, 1.0);
694 assert_eq!(max_score.api_compatibility, 1.0);
695 }
696
697 #[test]
698 fn test_component_info_edge_cases() {
699 let mut max_features = HashSet::new();
701 for i in 0..20 {
702 max_features.insert(format!("feature_{i}"));
703 }
704
705 let large_component = ComponentInfo {
706 name: "large_component".to_string(),
707 version: "99.99.99".to_string(),
708 size: usize::MAX,
709 exports_count: 1000,
710 imports_count: 500,
711 features: max_features,
712 };
713
714 assert_eq!(large_component.features.len(), 20);
715 assert_eq!(large_component.exports_count, 1000);
716 assert_eq!(large_component.size, usize::MAX);
717
718 let minimal_component = ComponentInfo {
720 name: String::new(),
721 version: String::new(),
722 size: 0,
723 exports_count: 0,
724 imports_count: 0,
725 features: HashSet::new(),
726 };
727
728 assert!(minimal_component.name.is_empty());
729 assert_eq!(minimal_component.size, 0);
730 }
731
732 #[test]
733 fn test_platform_support_variations() {
734 let support_variations = vec![
735 PlatformSupport {
736 platform: "wasmtime".to_string(),
737 support_level: SupportLevel::Full,
738 compatibility_score: 1.0,
739 required_modifications: vec![],
740 runtime_requirements: Some(">=1.0.0".to_string()),
741 },
742 PlatformSupport {
743 platform: "wasmer".to_string(),
744 support_level: SupportLevel::Partial,
745 compatibility_score: 0.8,
746 required_modifications: vec!["Remove SIMD".to_string()],
747 runtime_requirements: Some(">=2.0.0".to_string()),
748 },
749 PlatformSupport {
750 platform: "browser".to_string(),
751 support_level: SupportLevel::None,
752 compatibility_score: 0.0,
753 required_modifications: vec!["Complete rewrite".to_string()],
754 runtime_requirements: None,
755 },
756 ];
757
758 assert_eq!(support_variations.len(), 3);
759 assert_eq!(support_variations[0].support_level, SupportLevel::Full);
760 assert_eq!(support_variations[1].support_level, SupportLevel::Partial);
761 assert_eq!(support_variations[2].support_level, SupportLevel::None);
762 }
763
764 #[test]
765 fn test_feature_usage_complex() {
766 let mut core_features = HashSet::new();
767 core_features.insert("memory".to_string());
768 core_features.insert("tables".to_string());
769 core_features.insert("globals".to_string());
770
771 let mut proposal_features = HashSet::new();
772 proposal_features.insert("threads".to_string());
773 proposal_features.insert("simd".to_string());
774 proposal_features.insert("reference-types".to_string());
775
776 let mut platform_specific = HashMap::new();
777 let mut browser_features = HashSet::new();
778 browser_features.insert("webgl".to_string());
779 platform_specific.insert("browser".to_string(), browser_features);
780
781 let usage = FeatureUsage {
782 core_features: core_features.clone(),
783 proposal_features: proposal_features.clone(),
784 platform_specific,
785 compatibility: HashMap::new(),
786 };
787
788 assert_eq!(usage.core_features.len(), 3);
789 assert_eq!(usage.proposal_features.len(), 3);
790 assert_eq!(usage.platform_specific.len(), 1);
791 assert!(usage.core_features.contains("memory"));
792 assert!(usage.proposal_features.contains("threads"));
793 }
794
795 #[test]
796 fn test_size_analysis_comprehensive() {
797 let mut section_sizes = HashMap::new();
798 section_sizes.insert("type".to_string(), 1024);
799 section_sizes.insert("import".to_string(), 512);
800 section_sizes.insert("function".to_string(), 256);
801 section_sizes.insert("table".to_string(), 128);
802 section_sizes.insert("memory".to_string(), 64);
803 section_sizes.insert("global".to_string(), 32);
804 section_sizes.insert("export".to_string(), 256);
805 section_sizes.insert("start".to_string(), 8);
806 section_sizes.insert("element".to_string(), 512);
807 section_sizes.insert("code".to_string(), 8192);
808 section_sizes.insert("data".to_string(), 4096);
809
810 let mut platform_limits = HashMap::new();
811 platform_limits.insert("browser".to_string(), 1024 * 1024 * 4); platform_limits.insert("wasmtime".to_string(), 1024 * 1024 * 1024); let analysis = SizeAnalysis {
815 total_size: 15080,
816 code_size: 8192,
817 data_size: 4096,
818 custom_sections_size: 0,
819 section_sizes,
820 platform_limits,
821 };
822
823 assert_eq!(analysis.section_sizes.len(), 11);
824 assert_eq!(analysis.platform_limits.len(), 2);
825 assert_eq!(*analysis.section_sizes.get("code").unwrap(), 8192);
826 assert_eq!(*analysis.platform_limits.get("browser").unwrap(), 4_194_304);
827 }
828}
829
830use anyhow::Result;
831use serde::{Deserialize, Serialize};
832use std::collections::{HashMap, HashSet};
833use std::fmt;
834use super::component::WasmComponent;
835
836pub struct PortabilityAnalyzer {
838 config: AnalysisConfig,
840
841 compatibility_matrix: CompatibilityMatrix,
843
844 platform_requirements: HashMap<String, PlatformRequirements>,
846}
847
848#[derive(Debug, Clone, Serialize, Deserialize)]
850pub struct PortabilityScore {
851 pub overall_score: f64,
853
854 pub platform_scores: HashMap<String, f64>,
856
857 pub feature_scores: HashMap<String, f64>,
859
860 pub api_compatibility: f64,
862
863 pub size_efficiency: f64,
865
866 pub performance_portability: f64,
868
869 pub security_compliance: f64,
871}
872
873#[derive(Debug, Clone, Serialize, Deserialize)]
875pub struct PortabilityReport {
876 pub component_info: ComponentInfo,
878
879 pub score: PortabilityScore,
881
882 pub issues: Vec<CompatibilityIssue>,
884
885 pub recommendations: Vec<Recommendation>,
887
888 pub platform_support: HashMap<String, PlatformSupport>,
890
891 pub feature_usage: FeatureUsage,
893
894 pub size_analysis: SizeAnalysis,
896}
897
898#[derive(Debug, Clone, Serialize, Deserialize)]
900pub struct AnalysisConfig {
901 pub target_platforms: Vec<String>,
903
904 pub check_api_compatibility: bool,
906
907 pub check_size_constraints: bool,
909
910 pub check_performance: bool,
912
913 pub check_security: bool,
915
916 pub strict_mode: bool,
918}
919
920#[derive(Debug, Clone, Serialize, Deserialize)]
922pub struct ComponentInfo {
923 pub name: String,
925
926 pub version: String,
928
929 pub size: usize,
931
932 pub exports_count: usize,
934
935 pub imports_count: usize,
937
938 pub features: HashSet<String>,
940}
941
942#[derive(Debug, Clone, Serialize, Deserialize)]
944pub struct CompatibilityIssue {
945 pub severity: IssueSeverity,
947
948 pub category: IssueCategory,
950
951 pub affected_platforms: Vec<String>,
953
954 pub description: String,
956
957 pub fix_suggestion: Option<String>,
959}
960
961#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
963pub enum IssueSeverity {
964 Info,
966 Warning,
968 Error,
970 Critical,
972}
973
974#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
976pub enum IssueCategory {
977 ApiIncompatibility,
979 FeatureNotSupported,
981 SizeConstraint,
983 Performance,
985 Security,
987 Configuration,
989}
990
991#[derive(Debug, Clone, Serialize, Deserialize)]
993pub struct Recommendation {
994 pub priority: RecommendationPriority,
996
997 pub title: String,
999
1000 pub description: String,
1002
1003 pub impact: f64,
1005
1006 pub platforms: Vec<String>,
1008}
1009
1010impl fmt::Display for Recommendation {
1011 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1012 write!(f, "{}", self.title)
1013 }
1014}
1015
1016#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
1018pub enum RecommendationPriority {
1019 Low,
1021 Medium,
1023 High,
1025 Critical,
1027}
1028
1029#[derive(Debug, Clone, Serialize, Deserialize)]
1031pub struct PlatformSupport {
1032 pub platform: String,
1034
1035 pub support_level: SupportLevel,
1037
1038 pub compatibility_score: f64,
1040
1041 pub required_modifications: Vec<String>,
1043
1044 pub runtime_requirements: Option<String>,
1046}
1047
1048#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1050pub enum SupportLevel {
1051 Full,
1053 Partial,
1055 Limited,
1057 None,
1059}
1060
1061#[derive(Debug, Clone, Serialize, Deserialize)]
1063pub struct FeatureUsage {
1064 pub core_features: HashSet<String>,
1066
1067 pub proposal_features: HashSet<String>,
1069
1070 pub platform_specific: HashMap<String, HashSet<String>>,
1072
1073 pub compatibility: HashMap<String, Vec<String>>,
1075}
1076
1077#[derive(Debug, Clone, Serialize, Deserialize)]
1079pub struct SizeAnalysis {
1080 pub total_size: usize,
1082
1083 pub code_size: usize,
1085
1086 pub data_size: usize,
1088
1089 pub custom_sections_size: usize,
1091
1092 pub section_sizes: HashMap<String, usize>,
1094
1095 pub platform_limits: HashMap<String, usize>,
1097}
1098
1099#[derive(Debug, Clone)]
1101struct PlatformRequirements {
1102 required_features: HashSet<String>,
1104
1105 optional_features: HashSet<String>,
1107
1108 incompatible_features: HashSet<String>,
1110
1111 size_limit: Option<usize>,
1113
1114 _api_requirements: HashSet<String>,
1116}
1117
1118struct CompatibilityMatrix {
1120 support: HashMap<String, HashMap<String, bool>>,
1122}
1123
1124impl Default for AnalysisConfig {
1125 fn default() -> Self {
1126 Self {
1127 target_platforms: vec![
1128 "cloudflare-workers".to_string(),
1129 "fastly-compute".to_string(),
1130 "aws-lambda".to_string(),
1131 "browser".to_string(),
1132 "nodejs".to_string(),
1133 "wasmtime".to_string(),
1134 ],
1135 check_api_compatibility: true,
1136 check_size_constraints: true,
1137 check_performance: true,
1138 check_security: true,
1139 strict_mode: false,
1140 }
1141 }
1142}
1143
1144impl Default for PortabilityAnalyzer {
1145 fn default() -> Self {
1146 Self::new()
1147 }
1148}
1149
1150impl PortabilityAnalyzer {
1151 pub fn new() -> Self {
1153 Self {
1154 config: AnalysisConfig::default(),
1155 compatibility_matrix: Self::build_compatibility_matrix(),
1156 platform_requirements: Self::build_platform_requirements(),
1157 }
1158 }
1159
1160 pub fn new_with_config(config: AnalysisConfig) -> Self {
1162 Self {
1163 config,
1164 compatibility_matrix: Self::build_compatibility_matrix(),
1165 platform_requirements: Self::build_platform_requirements(),
1166 }
1167 }
1168
1169 pub fn analyze(&self, component: &WasmComponent) -> Result<PortabilityReport> {
1171 let component_info = self.extract_component_info(component)?;
1173
1174 let score = self.calculate_scores(&component_info)?;
1176
1177 let issues = self.find_issues(&component_info)?;
1179
1180 let recommendations = self.generate_recommendations(&component_info, &issues)?;
1182
1183 let platform_support = self.analyze_platform_support(&component_info)?;
1185
1186 let feature_usage = self.analyze_feature_usage(&component_info)?;
1188
1189 let size_analysis = self.analyze_size(component)?;
1191
1192 Ok(PortabilityReport {
1193 component_info,
1194 score,
1195 issues,
1196 recommendations,
1197 platform_support,
1198 feature_usage,
1199 size_analysis,
1200 })
1201 }
1202
1203 fn extract_component_info(&self, component: &WasmComponent) -> Result<ComponentInfo> {
1204 let mut features = HashSet::new();
1205
1206 if component.bytecode.len() > 1024 * 100 {
1209 features.insert("large-module".to_string());
1210 }
1211
1212 Ok(ComponentInfo {
1213 name: component.name.clone(),
1214 version: component.version.clone(),
1215 size: component.bytecode.len(),
1216 exports_count: component.exports.len(),
1217 imports_count: component.imports.len(),
1218 features,
1219 })
1220 }
1221
1222 fn calculate_scores(&self, info: &ComponentInfo) -> Result<PortabilityScore> {
1223 let mut platform_scores = HashMap::new();
1224
1225 for platform in &self.config.target_platforms {
1227 let score = self.calculate_platform_score(info, platform)?;
1228 platform_scores.insert(platform.clone(), score);
1229 }
1230
1231 let feature_scores = self.calculate_feature_scores(info)?;
1233
1234 let api_compatibility = self.calculate_api_compatibility(info)?;
1236 let size_efficiency = self.calculate_size_efficiency(info)?;
1237 let performance_portability = self.calculate_performance_portability(info)?;
1238 let security_compliance = self.calculate_security_compliance(info)?;
1239
1240 let overall_score = Self::calculate_overall_score(
1242 &platform_scores,
1243 &feature_scores,
1244 api_compatibility,
1245 size_efficiency,
1246 performance_portability,
1247 security_compliance,
1248 );
1249
1250 Ok(PortabilityScore {
1251 overall_score,
1252 platform_scores,
1253 feature_scores,
1254 api_compatibility,
1255 size_efficiency,
1256 performance_portability,
1257 security_compliance,
1258 })
1259 }
1260
1261 fn calculate_platform_score(&self, info: &ComponentInfo, platform: &str) -> Result<f64> {
1262 let requirements = self.platform_requirements.get(platform);
1263
1264 if let Some(reqs) = requirements {
1265 let mut score = 1.0;
1266
1267 if let Some(limit) = reqs.size_limit {
1269 if info.size > limit {
1270 score *= 0.5; }
1272 }
1273
1274 for feature in &info.features {
1276 if reqs.incompatible_features.contains(feature) {
1277 score *= 0.0; } else if !reqs.required_features.contains(feature) && !reqs.optional_features.contains(feature) {
1279 score *= 0.8; }
1281 }
1282
1283 Ok(score)
1284 } else {
1285 Ok(0.5) }
1287 }
1288
1289 fn calculate_feature_scores(&self, info: &ComponentInfo) -> Result<HashMap<String, f64>> {
1290 let mut scores = HashMap::new();
1291
1292 for feature in &info.features {
1294 let mut support_count = 0;
1295 let total_platforms = self.config.target_platforms.len();
1296
1297 for platform in &self.config.target_platforms {
1298 if let Some(platform_features) = self.compatibility_matrix.support.get(platform) {
1299 if platform_features.get(feature).copied().unwrap_or(false) {
1300 support_count += 1;
1301 }
1302 }
1303 }
1304
1305 let score = f64::from(support_count) / total_platforms as f64;
1306 scores.insert(feature.clone(), score);
1307 }
1308
1309 Ok(scores)
1310 }
1311
1312 fn calculate_api_compatibility(&self, _info: &ComponentInfo) -> Result<f64> {
1313 Ok(0.9)
1316 }
1317
1318 fn calculate_size_efficiency(&self, info: &ComponentInfo) -> Result<f64> {
1319 let size_kb = info.size as f64 / 1024.0;
1321
1322 if size_kb < 50.0 {
1323 Ok(1.0)
1324 } else if size_kb < 100.0 {
1325 Ok(0.9)
1326 } else if size_kb < 500.0 {
1327 Ok(0.7)
1328 } else if size_kb < 1000.0 {
1329 Ok(0.5)
1330 } else {
1331 Ok(0.3)
1332 }
1333 }
1334
1335 fn calculate_performance_portability(&self, _info: &ComponentInfo) -> Result<f64> {
1336 Ok(0.85)
1339 }
1340
1341 fn calculate_security_compliance(&self, _info: &ComponentInfo) -> Result<f64> {
1342 Ok(0.95)
1345 }
1346
1347 fn calculate_overall_score(
1348 platform_scores: &HashMap<String, f64>,
1349 feature_scores: &HashMap<String, f64>,
1350 api_compatibility: f64,
1351 size_efficiency: f64,
1352 performance_portability: f64,
1353 security_compliance: f64,
1354 ) -> f64 {
1355 let platform_avg = if platform_scores.is_empty() {
1356 0.0
1357 } else {
1358 platform_scores.values().sum::<f64>() / platform_scores.len() as f64
1359 };
1360
1361 let feature_avg = if feature_scores.is_empty() {
1362 1.0
1363 } else {
1364 feature_scores.values().sum::<f64>() / feature_scores.len() as f64
1365 };
1366
1367 platform_avg * 0.3 +
1369 feature_avg * 0.2 +
1370 api_compatibility * 0.2 +
1371 size_efficiency * 0.1 +
1372 performance_portability * 0.1 +
1373 security_compliance * 0.1
1374 }
1375
1376 fn find_issues(&self, info: &ComponentInfo) -> Result<Vec<CompatibilityIssue>> {
1377 let mut issues = Vec::new();
1378
1379 for platform in &self.config.target_platforms {
1381 if let Some(reqs) = self.platform_requirements.get(platform) {
1382 if let Some(limit) = reqs.size_limit {
1383 if info.size > limit {
1384 issues.push(CompatibilityIssue {
1385 severity: IssueSeverity::Warning,
1386 category: IssueCategory::SizeConstraint,
1387 affected_platforms: vec![platform.clone()],
1388 description: format!(
1389 "Component size ({} KB) exceeds {} platform limit ({} KB)",
1390 info.size / 1024,
1391 platform,
1392 limit / 1024
1393 ),
1394 fix_suggestion: Some("Consider optimizing component size or splitting functionality".to_string()),
1395 });
1396 }
1397 }
1398 }
1399 }
1400
1401 Ok(issues)
1402 }
1403
1404 fn generate_recommendations(&self, info: &ComponentInfo, issues: &[CompatibilityIssue]) -> Result<Vec<Recommendation>> {
1405 let mut recommendations = Vec::new();
1406
1407 if info.size > 100 * 1024 {
1409 recommendations.push(Recommendation {
1410 priority: RecommendationPriority::High,
1411 title: "Optimize component size".to_string(),
1412 description: "Component size can be reduced through optimization techniques".to_string(),
1413 impact: 0.2,
1414 platforms: self.config.target_platforms.clone(),
1415 });
1416 }
1417
1418 for issue in issues {
1420 if issue.category == IssueCategory::FeatureNotSupported {
1421 recommendations.push(Recommendation {
1422 priority: RecommendationPriority::Critical,
1423 title: "Remove incompatible features".to_string(),
1424 description: issue.description.clone(),
1425 impact: 0.3,
1426 platforms: issue.affected_platforms.clone(),
1427 });
1428 }
1429 }
1430
1431 Ok(recommendations)
1432 }
1433
1434 fn analyze_platform_support(&self, info: &ComponentInfo) -> Result<HashMap<String, PlatformSupport>> {
1435 let mut support = HashMap::new();
1436
1437 for platform in &self.config.target_platforms {
1438 let score = self.calculate_platform_score(info, platform)?;
1439
1440 let support_level = if score >= 0.9 {
1441 SupportLevel::Full
1442 } else if score >= 0.7 {
1443 SupportLevel::Partial
1444 } else if score >= 0.3 {
1445 SupportLevel::Limited
1446 } else {
1447 SupportLevel::None
1448 };
1449
1450 support.insert(platform.clone(), PlatformSupport {
1451 platform: platform.clone(),
1452 support_level,
1453 compatibility_score: score,
1454 required_modifications: Vec::new(),
1455 runtime_requirements: None,
1456 });
1457 }
1458
1459 Ok(support)
1460 }
1461
1462 fn analyze_feature_usage(&self, info: &ComponentInfo) -> Result<FeatureUsage> {
1463 Ok(FeatureUsage {
1464 core_features: info.features.clone(),
1465 proposal_features: HashSet::new(),
1466 platform_specific: HashMap::new(),
1467 compatibility: HashMap::new(),
1468 })
1469 }
1470
1471 fn analyze_size(&self, component: &WasmComponent) -> Result<SizeAnalysis> {
1472 let mut section_sizes = HashMap::new();
1473
1474 for (name, data) in &component.custom_sections {
1476 section_sizes.insert(name.clone(), data.len());
1477 }
1478
1479 let custom_sections_size: usize = component.custom_sections.values().map(std::vec::Vec::len).sum();
1480
1481 Ok(SizeAnalysis {
1482 total_size: component.bytecode.len(),
1483 code_size: component.bytecode.len() - custom_sections_size,
1484 data_size: 0,
1485 custom_sections_size,
1486 section_sizes,
1487 platform_limits: self.get_platform_limits(),
1488 })
1489 }
1490
1491 fn get_platform_limits(&self) -> HashMap<String, usize> {
1492 let mut limits = HashMap::new();
1493 limits.insert("cloudflare-workers".to_string(), 10 * 1024 * 1024); limits.insert("fastly-compute".to_string(), 50 * 1024 * 1024); limits.insert("aws-lambda".to_string(), 250 * 1024 * 1024); limits.insert("browser".to_string(), 100 * 1024 * 1024); limits
1498 }
1499
1500 fn build_compatibility_matrix() -> CompatibilityMatrix {
1501 let mut support = HashMap::new();
1502
1503 let mut cloudflare = HashMap::new();
1505 cloudflare.insert("simd".to_string(), false);
1506 cloudflare.insert("threads".to_string(), false);
1507 cloudflare.insert("bulk-memory".to_string(), true);
1508 cloudflare.insert("reference-types".to_string(), true);
1509 support.insert("cloudflare-workers".to_string(), cloudflare);
1510
1511 let mut browser = HashMap::new();
1513 browser.insert("simd".to_string(), true);
1514 browser.insert("threads".to_string(), true);
1515 browser.insert("bulk-memory".to_string(), true);
1516 browser.insert("reference-types".to_string(), true);
1517 support.insert("browser".to_string(), browser);
1518
1519 CompatibilityMatrix { support }
1520 }
1521
1522 fn build_platform_requirements() -> HashMap<String, PlatformRequirements> {
1523 let mut requirements = HashMap::new();
1524
1525 requirements.insert("cloudflare-workers".to_string(), PlatformRequirements {
1527 required_features: HashSet::new(),
1528 optional_features: HashSet::from(["bulk-memory".to_string(), "reference-types".to_string()]),
1529 incompatible_features: HashSet::from(["threads".to_string()]),
1530 size_limit: Some(10 * 1024 * 1024),
1531 _api_requirements: HashSet::new(),
1532 });
1533
1534 requirements.insert("browser".to_string(), PlatformRequirements {
1536 required_features: HashSet::new(),
1537 optional_features: HashSet::from(["simd".to_string(), "threads".to_string()]),
1538 incompatible_features: HashSet::new(),
1539 size_limit: Some(100 * 1024 * 1024),
1540 _api_requirements: HashSet::new(),
1541 });
1542
1543 requirements
1544 }
1545}