Skip to main content

harn_cli/test_runner/
session.rs

1use std::sync::Mutex;
2
3use serde::Serialize;
4
5use super::reporting::SuiteModulePreparation;
6
7/// Reusable runtime state for repeated user-test runs.
8///
9/// Logical workers share one bounded cache of immutable prepared bytecode while
10/// every test case still receives a fresh VM and fresh runtime state. Sharing
11/// lets the suite prepare its import graph once before per-test clocks start,
12/// instead of making one arbitrary test on every worker pay the cold compile.
13pub struct TestRunSession {
14    prepared_module_cache: harn_vm::PreparedModuleCache,
15    workers: Mutex<usize>,
16    clock: std::sync::Arc<dyn harn_vm::clock::Clock>,
17    stdio_available: bool,
18}
19
20impl Default for TestRunSession {
21    fn default() -> Self {
22        Self {
23            prepared_module_cache: harn_vm::PreparedModuleCache::default(),
24            workers: Mutex::new(0),
25            clock: harn_vm::clock::RealClock::arc(),
26            stdio_available: true,
27        }
28    }
29}
30
31/// Aggregate prepared-module cache counters for a [`TestRunSession`].
32#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize)]
33#[non_exhaustive]
34pub struct TestRunSessionStats {
35    pub workers: usize,
36    pub hits: u64,
37    pub misses: u64,
38    pub insertions: u64,
39    pub evictions: u64,
40    pub entries: usize,
41}
42
43impl TestRunSession {
44    /// Build a session for an embedder whose control protocol owns stdio.
45    pub fn without_stdio() -> Self {
46        Self {
47            stdio_available: false,
48            ..Self::default()
49        }
50    }
51
52    pub fn stats(&self) -> TestRunSessionStats {
53        let stats = self.prepared_module_cache.stats();
54        TestRunSessionStats {
55            workers: *self.workers.lock().unwrap(),
56            hits: stats.hits,
57            misses: stats.misses,
58            insertions: stats.insertions,
59            evictions: stats.evictions,
60            entries: stats.entries,
61        }
62    }
63
64    pub(super) fn prepared_module_cache(
65        &self,
66        worker_index: usize,
67    ) -> harn_vm::PreparedModuleCache {
68        let mut workers = self.workers.lock().unwrap();
69        *workers = (*workers).max(worker_index.saturating_add(1));
70        self.prepared_module_cache.clone()
71    }
72
73    pub(super) fn prepare_import_graph(
74        &self,
75        roots: &[std::path::PathBuf],
76    ) -> SuiteModulePreparation {
77        let started_ms = self.clock.monotonic_ms();
78        let modules = self.prepared_module_cache.prepare_import_graph(roots);
79        let duration_ms = self.clock.monotonic_ms().saturating_sub(started_ms).max(0) as u64;
80        SuiteModulePreparation {
81            duration_ms,
82            modules,
83        }
84    }
85
86    pub(super) fn stdio_available(&self) -> bool {
87        self.stdio_available
88    }
89}