1use super::runner::{
27 EmcComplianceReport, ExecutionMetrics, ExperimentResult, FalsificationSummary,
28 ReproducibilitySummary, VerificationSummary,
29};
30use std::fmt::Write;
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub enum ReportFormat {
35 Markdown,
37 Json,
39 Text,
41}
42
43pub struct ReportGenerator;
45
46impl ReportGenerator {
47 pub fn markdown(result: &ExperimentResult) -> Result<String, String> {
52 let mut report = String::new();
53
54 writeln!(report, "# Experiment Report: {}", result.name).map_err(|e| e.to_string())?;
56 writeln!(report).map_err(|e| e.to_string())?;
57
58 writeln!(report, "## Metadata").map_err(|e| e.to_string())?;
60 writeln!(report).map_err(|e| e.to_string())?;
61 writeln!(report, "| Field | Value |").map_err(|e| e.to_string())?;
62 writeln!(report, "|-------|-------|").map_err(|e| e.to_string())?;
63 writeln!(report, "| Experiment ID | {} |", result.experiment_id)
64 .map_err(|e| e.to_string())?;
65 writeln!(report, "| Seed | {} |", result.seed).map_err(|e| e.to_string())?;
66 writeln!(
67 report,
68 "| Status | {} |",
69 if result.passed { "PASSED" } else { "FAILED" }
70 )
71 .map_err(|e| e.to_string())?;
72 writeln!(report).map_err(|e| e.to_string())?;
73
74 writeln!(report, "## Verification Tests").map_err(|e| e.to_string())?;
76 writeln!(report).map_err(|e| e.to_string())?;
77 Self::write_verification_summary(&mut report, &result.verification)?;
78
79 writeln!(report, "## Falsification Criteria").map_err(|e| e.to_string())?;
81 writeln!(report).map_err(|e| e.to_string())?;
82 Self::write_falsification_summary(&mut report, &result.falsification)?;
83
84 if let Some(ref repro) = result.reproducibility {
86 writeln!(report, "## Reproducibility").map_err(|e| e.to_string())?;
87 writeln!(report).map_err(|e| e.to_string())?;
88 Self::write_reproducibility_summary(&mut report, repro)?;
89 }
90
91 writeln!(report, "## Execution Metrics").map_err(|e| e.to_string())?;
93 writeln!(report).map_err(|e| e.to_string())?;
94 Self::write_execution_metrics(&mut report, &result.execution)?;
95
96 if !result.warnings.is_empty() {
98 writeln!(report, "## Warnings").map_err(|e| e.to_string())?;
99 writeln!(report).map_err(|e| e.to_string())?;
100 for warning in &result.warnings {
101 writeln!(report, "- {warning}").map_err(|e| e.to_string())?;
102 }
103 writeln!(report).map_err(|e| e.to_string())?;
104 }
105
106 writeln!(report, "---").map_err(|e| e.to_string())?;
108 writeln!(
109 report,
110 "*Generated by simular v{}*",
111 env!("CARGO_PKG_VERSION")
112 )
113 .map_err(|e| e.to_string())?;
114
115 Ok(report)
116 }
117
118 fn write_verification_summary(
119 report: &mut String,
120 summary: &VerificationSummary,
121 ) -> Result<(), String> {
122 writeln!(
123 report,
124 "**Summary:** {} passed, {} failed out of {} total",
125 summary.passed, summary.failed, summary.total
126 )
127 .map_err(|e| e.to_string())?;
128 writeln!(report).map_err(|e| e.to_string())?;
129
130 if !summary.tests.is_empty() {
131 writeln!(
132 report,
133 "| ID | Name | Status | Expected | Actual | Tolerance |"
134 )
135 .map_err(|e| e.to_string())?;
136 writeln!(report, "|---|---|---|---|---|---|").map_err(|e| e.to_string())?;
137
138 for test in &summary.tests {
139 let status = if test.passed { "PASS" } else { "FAIL" };
140 let expected = test
141 .expected
142 .map_or_else(|| "-".to_string(), |v| format!("{v:.6}"));
143 let actual = test
144 .actual
145 .map_or_else(|| "-".to_string(), |v| format!("{v:.6}"));
146 let tolerance = test
147 .tolerance
148 .map_or_else(|| "-".to_string(), |v| format!("{v:.2e}"));
149
150 writeln!(
151 report,
152 "| {} | {} | {} | {} | {} | {} |",
153 test.id, test.name, status, expected, actual, tolerance
154 )
155 .map_err(|e| e.to_string())?;
156 }
157 writeln!(report).map_err(|e| e.to_string())?;
158 }
159
160 Ok(())
161 }
162
163 fn write_falsification_summary(
164 report: &mut String,
165 summary: &FalsificationSummary,
166 ) -> Result<(), String> {
167 writeln!(
168 report,
169 "**Summary:** {} passed, {} triggered out of {} total",
170 summary.passed, summary.triggered, summary.total
171 )
172 .map_err(|e| e.to_string())?;
173
174 if summary.jidoka_triggered {
175 writeln!(report, "\n**Jidoka:** TRIGGERED (stop-on-error)")
176 .map_err(|e| e.to_string())?;
177 }
178 writeln!(report).map_err(|e| e.to_string())?;
179
180 if !summary.criteria.is_empty() {
181 writeln!(report, "| ID | Name | Triggered | Severity | Condition |")
182 .map_err(|e| e.to_string())?;
183 writeln!(report, "|---|---|---|---|---|").map_err(|e| e.to_string())?;
184
185 for crit in &summary.criteria {
186 let triggered = if crit.triggered { "YES" } else { "NO" };
187 writeln!(
188 report,
189 "| {} | {} | {} | {} | {} |",
190 crit.id, crit.name, triggered, crit.severity, crit.condition
191 )
192 .map_err(|e| e.to_string())?;
193 }
194 writeln!(report).map_err(|e| e.to_string())?;
195 }
196
197 Ok(())
198 }
199
200 fn write_reproducibility_summary(
201 report: &mut String,
202 summary: &ReproducibilitySummary,
203 ) -> Result<(), String> {
204 writeln!(report, "| Property | Value |").map_err(|e| e.to_string())?;
205 writeln!(report, "|---|---|").map_err(|e| e.to_string())?;
206 writeln!(
207 report,
208 "| Passed | {} |",
209 if summary.passed { "YES" } else { "NO" }
210 )
211 .map_err(|e| e.to_string())?;
212 writeln!(report, "| Runs | {} |", summary.runs).map_err(|e| e.to_string())?;
213 writeln!(report, "| Identical | {} |", summary.identical).map_err(|e| e.to_string())?;
214 writeln!(report, "| Platform | {} |", summary.platform).map_err(|e| e.to_string())?;
215 writeln!(report, "| Reference Hash | `{}` |", summary.reference_hash)
216 .map_err(|e| e.to_string())?;
217 writeln!(report).map_err(|e| e.to_string())?;
218
219 Ok(())
220 }
221
222 fn write_execution_metrics(
223 report: &mut String,
224 metrics: &ExecutionMetrics,
225 ) -> Result<(), String> {
226 writeln!(report, "| Metric | Value |").map_err(|e| e.to_string())?;
227 writeln!(report, "|---|---|").map_err(|e| e.to_string())?;
228 writeln!(report, "| Duration | {} ms |", metrics.duration_ms).map_err(|e| e.to_string())?;
229 writeln!(report, "| Steps | {} |", metrics.steps).map_err(|e| e.to_string())?;
230 writeln!(report, "| Replications | {} |", metrics.replications)
231 .map_err(|e| e.to_string())?;
232
233 if let Some(mem) = metrics.peak_memory_bytes {
234 writeln!(report, "| Peak Memory | {mem} bytes |").map_err(|e| e.to_string())?;
235 }
236 writeln!(report).map_err(|e| e.to_string())?;
237
238 Ok(())
239 }
240
241 pub fn json(result: &ExperimentResult) -> Result<String, String> {
246 serde_json::to_string_pretty(result)
247 .map_err(|e| format!("Failed to serialize to JSON: {e}"))
248 }
249
250 pub fn text(result: &ExperimentResult) -> Result<String, String> {
255 let mut report = String::new();
256
257 let status = if result.passed { "PASSED" } else { "FAILED" };
258 writeln!(report, "EXPERIMENT REPORT: {}", result.name).map_err(|e| e.to_string())?;
259 writeln!(
260 report,
261 "================================================================================"
262 )
263 .map_err(|e| e.to_string())?;
264 writeln!(report).map_err(|e| e.to_string())?;
265 writeln!(report, "ID: {}", result.experiment_id).map_err(|e| e.to_string())?;
266 writeln!(report, "Seed: {}", result.seed).map_err(|e| e.to_string())?;
267 writeln!(report, "Status: {status}").map_err(|e| e.to_string())?;
268 writeln!(report).map_err(|e| e.to_string())?;
269
270 writeln!(report, "VERIFICATION TESTS").map_err(|e| e.to_string())?;
271 writeln!(
272 report,
273 "--------------------------------------------------------------------------------"
274 )
275 .map_err(|e| e.to_string())?;
276 writeln!(report, "Total: {}", result.verification.total).map_err(|e| e.to_string())?;
277 writeln!(report, "Passed: {}", result.verification.passed).map_err(|e| e.to_string())?;
278 writeln!(report, "Failed: {}", result.verification.failed).map_err(|e| e.to_string())?;
279 writeln!(report).map_err(|e| e.to_string())?;
280
281 writeln!(report, "FALSIFICATION CRITERIA").map_err(|e| e.to_string())?;
282 writeln!(
283 report,
284 "--------------------------------------------------------------------------------"
285 )
286 .map_err(|e| e.to_string())?;
287 writeln!(report, "Total: {}", result.falsification.total).map_err(|e| e.to_string())?;
288 writeln!(report, "Passed: {}", result.falsification.passed)
289 .map_err(|e| e.to_string())?;
290 writeln!(report, "Triggered: {}", result.falsification.triggered)
291 .map_err(|e| e.to_string())?;
292 writeln!(
293 report,
294 "Jidoka: {}",
295 if result.falsification.jidoka_triggered {
296 "TRIGGERED"
297 } else {
298 "OK"
299 }
300 )
301 .map_err(|e| e.to_string())?;
302 writeln!(report).map_err(|e| e.to_string())?;
303
304 writeln!(report, "EXECUTION").map_err(|e| e.to_string())?;
305 writeln!(
306 report,
307 "--------------------------------------------------------------------------------"
308 )
309 .map_err(|e| e.to_string())?;
310 writeln!(report, "Duration: {} ms", result.execution.duration_ms)
311 .map_err(|e| e.to_string())?;
312 writeln!(report, "Replications: {}", result.execution.replications)
313 .map_err(|e| e.to_string())?;
314 writeln!(report).map_err(|e| e.to_string())?;
315
316 writeln!(
317 report,
318 "================================================================================"
319 )
320 .map_err(|e| e.to_string())?;
321 writeln!(report, "RESULT: {status}").map_err(|e| e.to_string())?;
322
323 Ok(report)
324 }
325
326 pub fn emc_compliance_markdown(report: &EmcComplianceReport) -> Result<String, String> {
331 let mut output = String::new();
332
333 writeln!(
334 output,
335 "# EMC Compliance Report: {}",
336 report.experiment_name
337 )
338 .map_err(|e| e.to_string())?;
339 writeln!(output).map_err(|e| e.to_string())?;
340
341 let status = if report.passed {
342 "COMPLIANT"
343 } else {
344 "NON-COMPLIANT"
345 };
346 writeln!(output, "**Status:** {status}").map_err(|e| e.to_string())?;
347 writeln!(output).map_err(|e| e.to_string())?;
348
349 writeln!(output, "## EDD Compliance Checklist").map_err(|e| e.to_string())?;
351 writeln!(output).map_err(|e| e.to_string())?;
352 writeln!(output, "| Requirement | Status |").map_err(|e| e.to_string())?;
353 writeln!(output, "|---|---|").map_err(|e| e.to_string())?;
354
355 let check = |b: bool| if b { "PASS" } else { "FAIL" };
356 writeln!(
357 output,
358 "| EDD-01: EMC Reference | {} |",
359 check(report.edd_compliance.edd_01_emc_reference)
360 )
361 .map_err(|e| e.to_string())?;
362 writeln!(
363 output,
364 "| EDD-02: Verification Tests | {} |",
365 check(report.edd_compliance.edd_02_verification_tests)
366 )
367 .map_err(|e| e.to_string())?;
368 writeln!(
369 output,
370 "| EDD-03: Seed Specified | {} |",
371 check(report.edd_compliance.edd_03_seed_specified)
372 )
373 .map_err(|e| e.to_string())?;
374 writeln!(
375 output,
376 "| EDD-04: Falsification Criteria | {} |",
377 check(report.edd_compliance.edd_04_falsification_criteria)
378 )
379 .map_err(|e| e.to_string())?;
380 writeln!(
381 output,
382 "| EDD-05: Hypothesis (Optional) | {} |",
383 check(report.edd_compliance.edd_05_hypothesis)
384 )
385 .map_err(|e| e.to_string())?;
386 writeln!(output).map_err(|e| e.to_string())?;
387
388 if !report.schema_errors.is_empty() || !report.emc_errors.is_empty() {
390 writeln!(output, "## Errors").map_err(|e| e.to_string())?;
391 writeln!(output).map_err(|e| e.to_string())?;
392
393 for err in &report.schema_errors {
394 writeln!(output, "- Schema: {err}").map_err(|e| e.to_string())?;
395 }
396 for err in &report.emc_errors {
397 writeln!(output, "- EMC: {err}").map_err(|e| e.to_string())?;
398 }
399 writeln!(output).map_err(|e| e.to_string())?;
400 }
401
402 if !report.warnings.is_empty() {
404 writeln!(output, "## Warnings").map_err(|e| e.to_string())?;
405 writeln!(output).map_err(|e| e.to_string())?;
406 for warning in &report.warnings {
407 writeln!(output, "- {warning}").map_err(|e| e.to_string())?;
408 }
409 writeln!(output).map_err(|e| e.to_string())?;
410 }
411
412 writeln!(output, "---").map_err(|e| e.to_string())?;
414 writeln!(
415 output,
416 "*Generated by simular v{}*",
417 env!("CARGO_PKG_VERSION")
418 )
419 .map_err(|e| e.to_string())?;
420
421 Ok(output)
422 }
423}
424
425#[cfg(test)]
426mod tests {
427 use super::*;
428 use crate::edd::runner::{
429 EddComplianceChecklist, FalsificationCriterionResult, VerificationTestSummary,
430 };
431
432 fn sample_result() -> ExperimentResult {
433 ExperimentResult {
434 name: "Test Experiment".to_string(),
435 experiment_id: "EXP-001".to_string(),
436 seed: 42,
437 passed: true,
438 verification: VerificationSummary {
439 total: 2,
440 passed: 2,
441 failed: 0,
442 tests: vec![VerificationTestSummary {
443 id: "VT-001".to_string(),
444 name: "Basic test".to_string(),
445 passed: true,
446 expected: Some(10.0),
447 actual: Some(10.0),
448 tolerance: Some(0.001),
449 error: None,
450 }],
451 },
452 falsification: FalsificationSummary {
453 total: 1,
454 passed: 1,
455 triggered: 0,
456 jidoka_triggered: false,
457 criteria: vec![FalsificationCriterionResult {
458 id: "FC-001".to_string(),
459 name: "Error bound".to_string(),
460 triggered: false,
461 condition: "error < 0.01".to_string(),
462 severity: "critical".to_string(),
463 value: None,
464 threshold: Some(0.01),
465 }],
466 },
467 reproducibility: Some(ReproducibilitySummary {
468 passed: true,
469 runs: 3,
470 identical: true,
471 reference_hash: "abc123".to_string(),
472 run_hashes: vec!["abc123".to_string(); 3],
473 platform: "x86_64".to_string(),
474 }),
475 execution: ExecutionMetrics {
476 duration_ms: 100,
477 steps: 1000,
478 replications: 30,
479 peak_memory_bytes: Some(1024 * 1024),
480 },
481 artifacts: Vec::new(),
482 warnings: vec!["Test warning".to_string()],
483 }
484 }
485
486 #[test]
487 fn test_markdown_report() {
488 let result = sample_result();
489 let report = ReportGenerator::markdown(&result);
490 assert!(report.is_ok());
491 let report = report.expect("markdown generation should succeed");
492 assert!(report.contains("# Experiment Report: Test Experiment"));
493 assert!(report.contains("PASSED"));
494 assert!(report.contains("42"));
495 assert!(report.contains("VT-001"));
496 }
497
498 #[test]
499 fn test_json_report() {
500 let result = sample_result();
501 let report = ReportGenerator::json(&result);
502 assert!(report.is_ok());
503 let report = report.expect("json generation should succeed");
504 assert!(report.contains("\"name\": \"Test Experiment\""));
505 assert!(report.contains("\"seed\": 42"));
506 }
507
508 #[test]
509 fn test_text_report() {
510 let result = sample_result();
511 let report = ReportGenerator::text(&result);
512 assert!(report.is_ok());
513 let report = report.expect("text generation should succeed");
514 assert!(report.contains("EXPERIMENT REPORT: Test Experiment"));
515 assert!(report.contains("RESULT: PASSED"));
516 }
517
518 #[test]
519 fn test_emc_compliance_markdown() {
520 let report = EmcComplianceReport {
521 experiment_name: "Test".to_string(),
522 passed: true,
523 schema_errors: Vec::new(),
524 emc_errors: Vec::new(),
525 warnings: Vec::new(),
526 edd_compliance: EddComplianceChecklist {
527 edd_01_emc_reference: true,
528 edd_02_verification_tests: true,
529 edd_03_seed_specified: true,
530 edd_04_falsification_criteria: true,
531 edd_05_hypothesis: true,
532 },
533 };
534
535 let output = ReportGenerator::emc_compliance_markdown(&report);
536 assert!(output.is_ok());
537 let output = output.expect("compliance markdown should succeed");
538 assert!(output.contains("COMPLIANT"));
539 assert!(output.contains("EDD-01"));
540 }
541
542 #[test]
543 fn test_failed_report() {
544 let mut result = sample_result();
545 result.passed = false;
546 result.verification.failed = 1;
547 result.verification.passed = 1;
548
549 let report = ReportGenerator::markdown(&result);
550 assert!(report.is_ok());
551 let report = report.expect("markdown for failed result should succeed");
552 assert!(report.contains("FAILED"));
553 }
554
555 #[test]
556 fn test_report_format_enum() {
557 let markdown = ReportFormat::Markdown;
558 let json = ReportFormat::Json;
559 let text = ReportFormat::Text;
560
561 assert_eq!(markdown, ReportFormat::Markdown);
562 assert_ne!(json, ReportFormat::Markdown);
563 assert_ne!(text, ReportFormat::Json);
564 }
565
566 #[test]
567 fn test_markdown_report_no_reproducibility() {
568 let mut result = sample_result();
569 result.reproducibility = None;
570 result.warnings.clear();
571
572 let report = ReportGenerator::markdown(&result);
573 assert!(report.is_ok());
574 let report = report.ok().unwrap();
575 assert!(!report.contains("## Reproducibility"));
577 assert!(!report.contains("## Warnings"));
579 }
580
581 #[test]
582 fn test_markdown_report_with_memory() {
583 let result = sample_result();
584 let report = ReportGenerator::markdown(&result);
585 assert!(report.is_ok());
586 let report = report.ok().unwrap();
587 assert!(report.contains("1048576")); }
590
591 #[test]
592 fn test_text_report_with_jidoka() {
593 let mut result = sample_result();
594 result.falsification.jidoka_triggered = true;
595
596 let report = ReportGenerator::text(&result);
597 assert!(report.is_ok());
598 let report = report.ok().unwrap();
599 assert!(report.contains("TRIGGERED"));
600 }
601
602 #[test]
603 fn test_markdown_verification_with_no_expected() {
604 let mut result = sample_result();
605 result.verification.tests = vec![VerificationTestSummary {
606 id: "VT-002".to_string(),
607 name: "No expected".to_string(),
608 passed: true,
609 expected: None,
610 actual: None,
611 tolerance: None,
612 error: None,
613 }];
614
615 let report = ReportGenerator::markdown(&result);
616 assert!(report.is_ok());
617 }
618
619 #[test]
620 fn test_markdown_falsification_with_triggered() {
621 let mut result = sample_result();
622 result.falsification.criteria[0].triggered = true;
623 result.falsification.triggered = 1;
624 result.falsification.passed = 0;
625
626 let report = ReportGenerator::markdown(&result);
627 assert!(report.is_ok());
628 let report = report.ok().unwrap();
629 assert!(report.contains("YES"));
630 }
631
632 #[test]
633 fn test_markdown_jidoka_triggered() {
634 let mut result = sample_result();
635 result.falsification.jidoka_triggered = true;
636
637 let report = ReportGenerator::markdown(&result);
638 assert!(report.is_ok());
639 let report = report.ok().unwrap();
640 assert!(report.contains("Jidoka"));
641 assert!(report.contains("TRIGGERED"));
642 }
643
644 #[test]
645 fn test_emc_compliance_with_errors() {
646 let report = EmcComplianceReport {
647 experiment_name: "Error Test".to_string(),
648 passed: false,
649 schema_errors: vec!["Schema error 1".to_string()],
650 emc_errors: vec!["EMC error 1".to_string()],
651 warnings: vec!["Warning 1".to_string()],
652 edd_compliance: EddComplianceChecklist {
653 edd_01_emc_reference: false,
654 edd_02_verification_tests: false,
655 edd_03_seed_specified: true,
656 edd_04_falsification_criteria: false,
657 edd_05_hypothesis: false,
658 },
659 };
660
661 let output = ReportGenerator::emc_compliance_markdown(&report);
662 assert!(output.is_ok());
663 let output = output.ok().unwrap();
664 assert!(output.contains("NON-COMPLIANT"));
665 assert!(output.contains("Schema: Schema error 1"));
666 assert!(output.contains("EMC: EMC error 1"));
667 assert!(output.contains("Warning 1"));
668 assert!(output.contains("FAIL"));
669 }
670
671 #[test]
672 fn test_markdown_execution_no_memory() {
673 let mut result = sample_result();
674 result.execution.peak_memory_bytes = None;
675
676 let report = ReportGenerator::markdown(&result);
677 assert!(report.is_ok());
678 let report = report.ok().unwrap();
679 assert!(!report.contains("Peak Memory"));
681 }
682
683 #[test]
684 fn test_verification_empty_tests() {
685 let mut result = sample_result();
686 result.verification.tests.clear();
687 result.verification.total = 0;
688 result.verification.passed = 0;
689 result.verification.failed = 0;
690
691 let report = ReportGenerator::markdown(&result);
692 assert!(report.is_ok());
693 let report = report.ok().unwrap();
694 assert!(report.contains("0 passed, 0 failed out of 0 total"));
695 }
696
697 #[test]
698 fn test_falsification_empty_criteria() {
699 let mut result = sample_result();
700 result.falsification.criteria.clear();
701 result.falsification.total = 0;
702 result.falsification.passed = 0;
703 result.falsification.triggered = 0;
704
705 let report = ReportGenerator::markdown(&result);
706 assert!(report.is_ok());
707 let report = report.ok().unwrap();
708 assert!(report.contains("0 passed, 0 triggered out of 0 total"));
709 }
710
711 #[test]
712 fn test_compliance_no_errors_no_warnings() {
713 let report = EmcComplianceReport {
714 experiment_name: "Clean".to_string(),
715 passed: true,
716 schema_errors: Vec::new(),
717 emc_errors: Vec::new(),
718 warnings: Vec::new(),
719 edd_compliance: EddComplianceChecklist {
720 edd_01_emc_reference: true,
721 edd_02_verification_tests: true,
722 edd_03_seed_specified: true,
723 edd_04_falsification_criteria: true,
724 edd_05_hypothesis: true,
725 },
726 };
727
728 let output = ReportGenerator::emc_compliance_markdown(&report);
729 assert!(output.is_ok());
730 let output = output.ok().unwrap();
731 assert!(output.contains("COMPLIANT"));
732 assert!(!output.contains("## Errors"));
734 assert!(!output.contains("## Warnings"));
735 }
736
737 #[test]
738 fn test_report_format_debug() {
739 let format = ReportFormat::Markdown;
740 let debug_str = format!("{format:?}");
741 assert!(debug_str.contains("Markdown"));
742 }
743
744 #[test]
745 fn test_report_format_clone() {
746 let format = ReportFormat::Json;
747 let cloned = format;
748 assert_eq!(cloned, ReportFormat::Json);
749 }
750
751 #[test]
752 fn test_report_format_copy() {
753 let format = ReportFormat::Text;
754 let copied = format;
755 assert_eq!(format, copied);
756 }
757
758 #[test]
759 fn test_text_report_without_jidoka() {
760 let result = sample_result();
761 let report = ReportGenerator::text(&result);
762 assert!(report.is_ok());
763 let report = report.ok().unwrap();
764 assert!(report.contains("Jidoka: OK"));
765 }
766
767 #[test]
768 fn test_markdown_report_content_details() {
769 let result = sample_result();
770 let report = ReportGenerator::markdown(&result);
771 assert!(report.is_ok());
772 let report = report.ok().unwrap();
773
774 assert!(report.contains("| Field | Value |"));
776 assert!(report.contains("| Experiment ID |"));
777 assert!(report.contains("| Seed |"));
778 assert!(report.contains("| Status |"));
779
780 assert!(report.contains("## Verification Tests"));
782 assert!(report.contains("**Summary:**"));
783
784 assert!(report.contains("## Falsification Criteria"));
786
787 assert!(report.contains("## Reproducibility"));
789
790 assert!(report.contains("## Execution Metrics"));
792 assert!(report.contains("| Duration |"));
793 assert!(report.contains("| Steps |"));
794 assert!(report.contains("| Replications |"));
795 }
796
797 #[test]
798 fn test_json_report_deserialization() {
799 let result = sample_result();
800 let json = ReportGenerator::json(&result);
801 assert!(json.is_ok());
802 let json = json.ok().unwrap();
803
804 let parsed: Result<serde_json::Value, _> = serde_json::from_str(&json);
806 assert!(parsed.is_ok());
807 }
808
809 #[test]
810 fn test_text_report_full_content() {
811 let result = sample_result();
812 let report = ReportGenerator::text(&result);
813 assert!(report.is_ok());
814 let report = report.ok().unwrap();
815
816 assert!(report.contains("EXPERIMENT REPORT:"));
817 assert!(report.contains("ID:"));
818 assert!(report.contains("Seed:"));
819 assert!(report.contains("VERIFICATION TESTS"));
820 assert!(report.contains("FALSIFICATION CRITERIA"));
821 assert!(report.contains("EXECUTION"));
822 assert!(report.contains("Duration:"));
823 assert!(report.contains("Replications:"));
824 }
825
826 #[test]
827 fn test_emc_compliance_only_schema_errors() {
828 let report = EmcComplianceReport {
829 experiment_name: "Schema Error".to_string(),
830 passed: false,
831 schema_errors: vec!["Schema error only".to_string()],
832 emc_errors: Vec::new(),
833 warnings: Vec::new(),
834 edd_compliance: EddComplianceChecklist {
835 edd_01_emc_reference: true,
836 edd_02_verification_tests: true,
837 edd_03_seed_specified: true,
838 edd_04_falsification_criteria: true,
839 edd_05_hypothesis: true,
840 },
841 };
842
843 let output = ReportGenerator::emc_compliance_markdown(&report);
844 assert!(output.is_ok());
845 let output = output.ok().unwrap();
846 assert!(output.contains("Schema: Schema error only"));
847 }
848
849 #[test]
850 fn test_emc_compliance_only_emc_errors() {
851 let report = EmcComplianceReport {
852 experiment_name: "EMC Error".to_string(),
853 passed: false,
854 schema_errors: Vec::new(),
855 emc_errors: vec!["EMC error only".to_string()],
856 warnings: Vec::new(),
857 edd_compliance: EddComplianceChecklist {
858 edd_01_emc_reference: true,
859 edd_02_verification_tests: true,
860 edd_03_seed_specified: true,
861 edd_04_falsification_criteria: true,
862 edd_05_hypothesis: true,
863 },
864 };
865
866 let output = ReportGenerator::emc_compliance_markdown(&report);
867 assert!(output.is_ok());
868 let output = output.ok().unwrap();
869 assert!(output.contains("EMC: EMC error only"));
870 }
871
872 #[test]
873 fn test_verification_with_error_field() {
874 let mut result = sample_result();
875 result.verification.tests = vec![VerificationTestSummary {
876 id: "VT-ERR".to_string(),
877 name: "Test with error".to_string(),
878 passed: false,
879 expected: Some(10.0),
880 actual: Some(15.0),
881 tolerance: Some(0.001),
882 error: Some("Out of tolerance".to_string()),
883 }];
884 result.verification.passed = 0;
885 result.verification.failed = 1;
886
887 let report = ReportGenerator::markdown(&result);
888 assert!(report.is_ok());
889 let report = report.ok().unwrap();
890 assert!(report.contains("VT-ERR"));
891 assert!(report.contains("FAIL"));
892 }
893
894 #[test]
895 fn test_reproducibility_not_passed() {
896 let mut result = sample_result();
897 result.reproducibility = Some(ReproducibilitySummary {
898 passed: false,
899 runs: 3,
900 identical: false,
901 reference_hash: "abc123".to_string(),
902 run_hashes: vec![
903 "abc123".to_string(),
904 "def456".to_string(),
905 "ghi789".to_string(),
906 ],
907 platform: "x86_64".to_string(),
908 });
909
910 let report = ReportGenerator::markdown(&result);
911 assert!(report.is_ok());
912 let report = report.ok().unwrap();
913 assert!(report.contains("| Passed | NO |"));
914 assert!(report.contains("| Identical | false |"));
915 }
916
917 #[test]
918 fn test_multiple_verification_tests() {
919 let mut result = sample_result();
920 result.verification.tests = vec![
921 VerificationTestSummary {
922 id: "VT-001".to_string(),
923 name: "Test 1".to_string(),
924 passed: true,
925 expected: Some(1.0),
926 actual: Some(1.0),
927 tolerance: Some(0.01),
928 error: None,
929 },
930 VerificationTestSummary {
931 id: "VT-002".to_string(),
932 name: "Test 2".to_string(),
933 passed: true,
934 expected: Some(2.0),
935 actual: Some(2.0),
936 tolerance: Some(0.01),
937 error: None,
938 },
939 VerificationTestSummary {
940 id: "VT-003".to_string(),
941 name: "Test 3".to_string(),
942 passed: false,
943 expected: Some(3.0),
944 actual: Some(3.5),
945 tolerance: Some(0.01),
946 error: Some("Test 3 failed".to_string()),
947 },
948 ];
949
950 let report = ReportGenerator::markdown(&result);
951 assert!(report.is_ok());
952 let report = report.ok().unwrap();
953 assert!(report.contains("VT-001"));
954 assert!(report.contains("VT-002"));
955 assert!(report.contains("VT-003"));
956 }
957
958 #[test]
959 fn test_multiple_falsification_criteria() {
960 let mut result = sample_result();
961 result.falsification.criteria = vec![
962 FalsificationCriterionResult {
963 id: "FC-001".to_string(),
964 name: "Criterion 1".to_string(),
965 triggered: false,
966 condition: "x < 1".to_string(),
967 severity: "warning".to_string(),
968 value: Some(0.5),
969 threshold: Some(1.0),
970 },
971 FalsificationCriterionResult {
972 id: "FC-002".to_string(),
973 name: "Criterion 2".to_string(),
974 triggered: true,
975 condition: "y > 10".to_string(),
976 severity: "critical".to_string(),
977 value: Some(15.0),
978 threshold: Some(10.0),
979 },
980 ];
981
982 let report = ReportGenerator::markdown(&result);
983 assert!(report.is_ok());
984 let report = report.ok().unwrap();
985 assert!(report.contains("FC-001"));
986 assert!(report.contains("FC-002"));
987 }
988
989 #[test]
990 fn test_all_edd_compliance_fail() {
991 let report = EmcComplianceReport {
992 experiment_name: "All Fail".to_string(),
993 passed: false,
994 schema_errors: Vec::new(),
995 emc_errors: Vec::new(),
996 warnings: Vec::new(),
997 edd_compliance: EddComplianceChecklist {
998 edd_01_emc_reference: false,
999 edd_02_verification_tests: false,
1000 edd_03_seed_specified: false,
1001 edd_04_falsification_criteria: false,
1002 edd_05_hypothesis: false,
1003 },
1004 };
1005
1006 let output = ReportGenerator::emc_compliance_markdown(&report);
1007 assert!(output.is_ok());
1008 let output = output.ok().unwrap();
1009
1010 let fail_count = output.matches("| FAIL |").count();
1012 assert_eq!(fail_count, 5, "All 5 EDD checks should fail");
1013 }
1014
1015 #[test]
1016 fn test_emc_compliance_footer() {
1017 let report = EmcComplianceReport {
1018 experiment_name: "Footer Test".to_string(),
1019 passed: true,
1020 schema_errors: Vec::new(),
1021 emc_errors: Vec::new(),
1022 warnings: Vec::new(),
1023 edd_compliance: EddComplianceChecklist {
1024 edd_01_emc_reference: true,
1025 edd_02_verification_tests: true,
1026 edd_03_seed_specified: true,
1027 edd_04_falsification_criteria: true,
1028 edd_05_hypothesis: true,
1029 },
1030 };
1031
1032 let output = ReportGenerator::emc_compliance_markdown(&report);
1033 assert!(output.is_ok());
1034 let output = output.ok().unwrap();
1035 assert!(output.contains("---"));
1036 assert!(output.contains("*Generated by simular v"));
1037 }
1038}