1use aiken_lang::{
2 expr::UntypedExpr,
3 test_framework::{BenchmarkResult, PropertyTestResult, TestResult, UnitTestResult},
4};
5pub use json::{Json, json_schema};
6use std::{
7 collections::BTreeMap,
8 fmt::Display,
9 io::{self, IsTerminal},
10 path::PathBuf,
11};
12pub use terminal::Terminal;
13use uplc::machine::cost_model::ExBudget;
14
15mod json;
16mod terminal;
17
18pub trait EventListener {
19 fn handle_event(&self, _event: Event) {}
20}
21
22pub enum Event {
23 StartingCompilation {
24 name: String,
25 version: String,
26 root: PathBuf,
27 },
28 BuildingDocumentation {
29 name: String,
30 version: String,
31 root: PathBuf,
32 },
33 GeneratingDocFiles {
34 output_path: PathBuf,
35 },
36 GeneratingBlueprint {
37 path: PathBuf,
38 },
39 DumpingUPLC {
40 path: PathBuf,
41 },
42 GeneratingUPLCFor {
43 name: String,
44 path: PathBuf,
45 },
46 CollectingTests {
47 matching_module: Option<String>,
48 matching_names: Vec<String>,
49 },
50 RunningTests,
51 RunningBenchmarks,
52 FinishedTests {
53 seed: u32,
54 coverage_mode: CoverageMode,
55 tests: Vec<TestResult<UntypedExpr, UntypedExpr>>,
56 plain_numbers: bool,
57 },
58 FinishedBenchmarks {
59 seed: u32,
60 benchmarks: Vec<TestResult<UntypedExpr, UntypedExpr>>,
61 },
62 WaitingForBuildDirLock,
63 ResolvingPackages {
64 name: String,
65 },
66 PackageResolveFallback {
67 name: String,
68 },
69 PackagesDownloaded {
70 start: tokio::time::Instant,
71 count: usize,
72 source: DownloadSource,
73 },
74 ResolvingVersions,
75}
76
77#[derive(Debug, Clone, Copy, Default)]
78pub enum CoverageMode {
79 RelativeToTests,
80 #[default]
81 RelativeToLabels,
82}
83
84impl CoverageMode {
85 pub fn parse(raw_str: &str) -> Result<Self, String> {
86 if raw_str == "relative-to-tests" {
87 return Ok(CoverageMode::RelativeToTests);
88 }
89
90 if raw_str == "relative-to-labels" {
91 return Ok(CoverageMode::RelativeToLabels);
92 }
93
94 Err(
95 "unexpected coverage mode: neither 'relative-to-tests' nor 'relative-to-labels'"
96 .to_string(),
97 )
98 }
99}
100
101impl Display for CoverageMode {
102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 match self {
104 CoverageMode::RelativeToTests => write!(f, "relative-to-tests"),
105 CoverageMode::RelativeToLabels => write!(f, "relative-to-labels"),
106 }
107 }
108}
109
110#[derive(Clone)]
111pub enum EventTarget {
112 Json(Json),
113 Terminal(Terminal),
114}
115
116impl Default for EventTarget {
117 fn default() -> Self {
118 if io::stdout().is_terminal() {
119 EventTarget::Terminal(Terminal)
120 } else {
121 EventTarget::Json(Json)
122 }
123 }
124}
125
126impl EventListener for EventTarget {
127 fn handle_event(&self, event: Event) {
128 match self {
129 EventTarget::Terminal(term) => term.handle_event(event),
130 EventTarget::Json(json) => json.handle_event(event),
131 }
132 }
133}
134
135pub enum DownloadSource {
136 Network,
137 Cache,
138}
139
140impl Display for DownloadSource {
141 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142 match self {
143 DownloadSource::Network => write!(f, "network"),
144 DownloadSource::Cache => write!(f, "cache"),
145 }
146 }
147}
148
149pub(crate) fn group_by_module(
150 results: &[TestResult<UntypedExpr, UntypedExpr>],
151) -> BTreeMap<String, Vec<&TestResult<UntypedExpr, UntypedExpr>>> {
152 let mut modules = BTreeMap::new();
153 for r in results {
154 let xs: &mut Vec<&TestResult<_, _>> = modules.entry(r.module().to_string()).or_default();
155 xs.push(r);
156 }
157 modules
158}
159
160pub(crate) fn find_max_execution_units<T>(xs: &[TestResult<T, T>]) -> (usize, usize, usize) {
161 fn max_execution_units(max_mem: i64, max_cpu: i64, cost: &ExBudget) -> (i64, i64) {
162 if cost.mem >= max_mem && cost.cpu >= max_cpu {
163 (cost.mem, cost.cpu)
164 } else if cost.mem > max_mem {
165 (cost.mem, max_cpu)
166 } else if cost.cpu > max_cpu {
167 (max_mem, cost.cpu)
168 } else {
169 (max_mem, max_cpu)
170 }
171 }
172
173 let (max_mem, max_cpu, max_iter) =
174 xs.iter()
175 .fold((0, 0, 0), |(max_mem, max_cpu, max_iter), test| match test {
176 TestResult::PropertyTestResult(PropertyTestResult { iterations, .. }) => {
177 (max_mem, max_cpu, std::cmp::max(max_iter, *iterations))
178 }
179 TestResult::UnitTestResult(UnitTestResult { spent_budget, .. }) => {
180 let (max_mem, max_cpu) = max_execution_units(max_mem, max_cpu, spent_budget);
181 (max_mem, max_cpu, max_iter)
182 }
183 TestResult::BenchmarkResult(BenchmarkResult { measures, .. }) => {
184 let (mut max_mem, mut max_cpu) = (max_mem, max_cpu);
185 for (_, measure) in measures {
186 (max_mem, max_cpu) = max_execution_units(max_mem, max_cpu, measure);
187 }
188 (max_mem, max_cpu, max_iter)
189 }
190 });
191
192 (
193 max_mem.to_string().len(),
194 max_cpu.to_string().len(),
195 max_iter.to_string().len(),
196 )
197}