harn_cli/test_runner/
reporting.rs1use serde::Serialize;
4
5use crate::test_timing::DurationSummary;
6
7#[derive(Clone, Debug, Serialize)]
8pub struct TestResult {
9 pub name: String,
10 pub file: String,
11 pub passed: bool,
12 pub error: Option<String>,
13 #[serde(skip_serializing_if = "Option::is_none")]
18 pub captured_output: Option<String>,
19 pub timeout: Option<TestTimeout>,
22 pub duration_ms: u64,
23 #[serde(skip_serializing_if = "Option::is_none")]
26 pub phases: Option<PhaseTimings>,
27}
28
29#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
30pub struct TestTimeout {
31 pub phase: TestPhase,
32 pub limit_ms: u64,
33}
34
35#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
36#[serde(rename_all = "snake_case")]
37pub enum TestPhase {
38 Execute,
39}
40
41#[derive(Clone, Debug, Serialize)]
42pub struct TestSummary {
43 pub results: Vec<TestResult>,
44 pub passed: usize,
45 pub failed: usize,
46 pub total: usize,
47 pub duration_ms: u64,
48 pub timing: DurationSummary,
50 pub aggregate: AggregateTimings,
52}
53
54#[derive(Debug, Default, Clone, Copy, Serialize)]
60pub struct PhaseTimings {
61 pub setup_ms: u64,
64 pub compile_ms: u64,
66 pub execute_ms: u64,
68 pub teardown_ms: u64,
70 pub modules: harn_vm::ModulePhaseStats,
73}
74
75#[derive(Debug, Default, Clone, Copy, Serialize)]
79pub struct AggregateTimings {
80 pub collection_ms: u64,
81 pub setup_ms: u64,
82 pub compile_ms: u64,
83 pub execute_ms: u64,
84 pub teardown_ms: u64,
85 pub modules: harn_vm::ModulePhaseStats,
88}
89
90#[derive(Clone, Copy, Debug, Default)]
91pub(super) struct SuiteModulePreparation {
92 pub duration_ms: u64,
93 pub modules: harn_vm::ModulePhaseStats,
94}
95
96impl AggregateTimings {
97 pub(super) fn from_results(
98 collection_ms: u64,
99 module_preparation: SuiteModulePreparation,
100 results: &[TestResult],
101 ) -> Self {
102 results.iter().filter_map(|result| result.phases).fold(
103 Self {
104 collection_ms,
105 compile_ms: module_preparation.duration_ms,
106 modules: module_preparation.modules,
107 ..Self::default()
108 },
109 |acc, phases| Self {
110 collection_ms: acc.collection_ms,
111 setup_ms: acc.setup_ms.saturating_add(phases.setup_ms),
112 compile_ms: acc.compile_ms.saturating_add(phases.compile_ms),
113 execute_ms: acc.execute_ms.saturating_add(phases.execute_ms),
114 teardown_ms: acc.teardown_ms.saturating_add(phases.teardown_ms),
115 modules: acc.modules.saturating_add(phases.modules),
116 },
117 )
118 }
119}
120
121impl TestResult {
122 pub(super) fn emit_diagnose(&self) {
126 let outcome = if self.passed { "ok" } else { "FAIL" };
127 let phases = self
128 .phases
129 .expect("diagnostics are emitted only for executed cases");
130 eprintln!(
131 "[harn test diag] {} {} setup={}ms compile={}ms execute={}ms teardown={}ms module_compile={}ms module_load={}ms modules_compiled={} modules_loaded={} total={}ms",
132 outcome,
133 self.name,
134 phases.setup_ms,
135 phases.compile_ms,
136 phases.execute_ms,
137 phases.teardown_ms,
138 phases.modules.module_compile_ms,
139 phases.modules.module_load_ms,
140 phases.modules.modules_compiled,
141 phases.modules.modules_loaded,
142 self.duration_ms,
143 );
144 }
145}