harn_cli/test_runner/
session.rs1use std::collections::BTreeSet;
2use std::path::PathBuf;
3use std::sync::Mutex;
4
5use serde::Serialize;
6
7use super::reporting::SuiteModulePreparation;
8
9pub struct TestRunSession {
16 prepared_module_cache: harn_vm::PreparedModuleCache,
17 workers: Mutex<usize>,
18 clock: std::sync::Arc<dyn harn_vm::clock::Clock>,
19 stdio_available: bool,
20 callable_preparations: Mutex<(usize, usize)>,
21}
22
23impl Default for TestRunSession {
24 fn default() -> Self {
25 Self {
26 prepared_module_cache: harn_vm::PreparedModuleCache::default(),
27 workers: Mutex::new(0),
28 clock: harn_vm::clock::RealClock::arc(),
29 stdio_available: true,
30 callable_preparations: Mutex::new((0, 0)),
31 }
32 }
33}
34
35#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize)]
37#[non_exhaustive]
38pub struct TestRunSessionStats {
39 pub workers: usize,
40 pub hits: u64,
41 pub misses: u64,
42 pub insertions: u64,
43 pub evictions: u64,
44 pub entries: usize,
45 pub test_files_compiled: usize,
46 pub test_entries_compiled: usize,
47}
48
49impl TestRunSession {
50 pub fn without_stdio() -> Self {
52 Self {
53 stdio_available: false,
54 ..Self::default()
55 }
56 }
57
58 pub fn stats(&self) -> TestRunSessionStats {
59 let stats = self.prepared_module_cache.stats();
60 let callable = *self.callable_preparations.lock().unwrap();
61 TestRunSessionStats {
62 workers: *self.workers.lock().unwrap(),
63 hits: stats.hits,
64 misses: stats.misses,
65 insertions: stats.insertions,
66 evictions: stats.evictions,
67 entries: stats.entries,
68 test_files_compiled: callable.0,
69 test_entries_compiled: callable.1,
70 }
71 }
72
73 pub(super) fn prepared_module_cache(
74 &self,
75 worker_index: usize,
76 ) -> harn_vm::PreparedModuleCache {
77 let mut workers = self.workers.lock().unwrap();
78 *workers = (*workers).max(worker_index.saturating_add(1));
79 self.prepared_module_cache.clone()
80 }
81
82 pub(super) fn prepare_import_graphs(
83 &self,
84 roots: impl IntoIterator<Item = (PathBuf, bool)>,
85 ) -> SuiteModulePreparation {
86 let mut user_files = BTreeSet::new();
87 let mut trusted_files = BTreeSet::new();
88 for (path, trusted_host_dispatch) in roots {
89 if trusted_host_dispatch {
90 trusted_files.insert(path);
91 } else {
92 user_files.insert(path);
93 }
94 }
95 let user = self.prepare_import_graph(&user_files.into_iter().collect::<Vec<_>>(), false);
96 let trusted =
97 self.prepare_import_graph(&trusted_files.into_iter().collect::<Vec<_>>(), true);
98 SuiteModulePreparation {
99 duration_ms: user.duration_ms.saturating_add(trusted.duration_ms),
100 modules: user.modules.saturating_add(trusted.modules),
101 }
102 }
103
104 fn prepare_import_graph(
105 &self,
106 roots: &[std::path::PathBuf],
107 trusted_host_dispatch: bool,
108 ) -> SuiteModulePreparation {
109 let started_ms = self.clock.monotonic_ms();
110 let modules = if trusted_host_dispatch {
111 self.prepared_module_cache
112 .prepare_trusted_host_dispatch_import_graph(roots)
113 } else {
114 self.prepared_module_cache.prepare_import_graph(roots)
115 };
116 let duration_ms = self.clock.monotonic_ms().saturating_sub(started_ms).max(0) as u64;
117 SuiteModulePreparation {
118 duration_ms,
119 modules,
120 }
121 }
122
123 pub(super) fn record_callable_preparation(&self, files: usize, entries: usize) {
124 let mut totals = self.callable_preparations.lock().unwrap();
125 totals.0 = totals.0.saturating_add(files);
126 totals.1 = totals.1.saturating_add(entries);
127 }
128
129 pub(super) fn stdio_available(&self) -> bool {
130 self.stdio_available
131 }
132}