ixa/profiling/
reporting.rs1use std::path::Path;
2
3use super::file::write_profiling_data_to_file;
4use crate::{error, Context, ContextReportExt};
5
6pub trait ProfilingContextExt: ContextReportExt {
8 fn print_execution_statistics(&mut self, include_profiling_data: bool) {
13 let stats = self.get_execution_statistics();
14 crate::execution_stats::print_execution_statistics(&stats);
15
16 if include_profiling_data {
17 super::print_profiling_data();
18 }
19 }
20
21 fn write_profiling_data(&mut self) {
24 let (mut prefix, directory, overwrite) = {
25 let report_options = self.report_options();
26 (
27 report_options.file_prefix.clone(),
28 report_options.output_dir.clone(),
29 report_options.overwrite,
30 )
31 };
32
33 let execution_statistics = self.get_execution_statistics();
34 prefix.push_str("profiling.json");
37 let profiling_data_path = directory.join(prefix);
38 let profiling_data_path = Path::new(&profiling_data_path);
39
40 if !overwrite && profiling_data_path.exists() {
41 error!(
42 "profiling output file already exists: {}",
43 profiling_data_path.display()
44 );
45 return;
46 }
47
48 write_profiling_data_to_file(profiling_data_path, execution_statistics)
49 .expect("could not write profiling data to file");
50 }
51}
52impl ProfilingContextExt for Context {}
53
54#[cfg(test)]
55mod tests {
56 use std::fs;
57 use std::time::Duration;
58
59 use tempfile::tempdir;
60
61 use super::ProfilingContextExt;
62 use crate::context::Context;
63 use crate::profiling::{add_computed_statistic, increment_named_count, open_span};
64 use crate::report::ContextReportExt as _; #[test]
67 fn print_execution_statistics_without_profiling_data() {
68 let mut context = Context::new();
69 context.print_execution_statistics(false);
70 }
71
72 #[test]
73 fn print_execution_statistics_with_profiling_data() {
74 increment_named_count("reporting_print_event");
76 increment_named_count("reporting_print_event");
77 {
78 let _span = open_span("reporting_print_span");
79 std::thread::sleep(Duration::from_millis(5));
80 }
81 add_computed_statistic::<usize>(
82 "reporting_print_stat",
83 "Count of reporting_print_event",
84 Box::new(|data| data.get_named_count("reporting_print_event")),
85 Box::new(|_v| {}),
86 );
87
88 let mut context = Context::new();
89 context.print_execution_statistics(true);
90 }
91
92 #[test]
93 fn write_profiling_data_creates_json() {
94 let temp_dir = tempdir().unwrap();
95 let out_dir = temp_dir.path().to_path_buf();
96
97 increment_named_count("reporting_write_event");
99 {
100 let _span = open_span("reporting_write_span");
101 std::thread::sleep(Duration::from_millis(3));
102 }
103
104 let mut context = Context::new();
105 let config = context.report_options();
106 config
107 .file_prefix("test_")
108 .directory(out_dir.clone())
109 .overwrite(true);
110
111 context.write_profiling_data();
112
113 let file_path = out_dir.join("test_profiling.json");
114 assert!(file_path.exists(), "JSON file should be created");
115
116 let content = fs::read_to_string(&file_path).expect("Failed to read JSON");
117 let json: serde_json::Value = serde_json::from_str(&content).expect("Invalid JSON");
118 assert!(json["execution_statistics"].is_object());
119 assert!(json["named_counts"].is_array());
120 assert!(json["named_spans"].is_array());
121 assert!(json["computed_statistics"].is_object());
122 }
123
124 #[test]
125 fn write_profiling_data_respects_overwrite_false() {
126 let temp_dir = tempdir().unwrap();
127 let out_dir = temp_dir.path().to_path_buf();
128
129 let file_path = out_dir.join("prefix_profiling.json");
131 fs::write(&file_path, "PREEXISTING").unwrap();
132
133 let mut context = Context::new();
134 let config = context.report_options();
135 config
136 .file_prefix("prefix_")
137 .directory(out_dir.clone())
138 .overwrite(false);
139
140 context.write_profiling_data();
142
143 let after = fs::read_to_string(&file_path).unwrap();
144 assert_eq!(
145 after, "PREEXISTING",
146 "File should remain unchanged when overwrite=false"
147 );
148 }
149
150 #[test]
151 fn write_profiling_data_overwrites_when_true() {
152 let temp_dir = tempdir().unwrap();
153 let out_dir = temp_dir.path().to_path_buf();
154
155 let file_path = out_dir
156 .join("ow_")
157 .join("..") .canonicalize()
159 .unwrap_or(out_dir.clone())
160 .join("ow_profiling.json");
161
162 let _ = fs::create_dir_all(file_path.parent().unwrap());
164 fs::write(&file_path, "OLD").unwrap();
165
166 let mut context = Context::new();
167 let config = context.report_options();
168 config
169 .file_prefix("ow_")
170 .directory(file_path.parent().unwrap().to_path_buf())
171 .overwrite(true);
172
173 context.write_profiling_data();
174
175 let content = fs::read_to_string(&file_path).unwrap();
176 assert!(
177 content.starts_with("{"),
178 "File should contain JSON after overwrite"
179 );
180 assert_ne!(
181 content, "OLD",
182 "File content should be updated when overwrite=true"
183 );
184 }
185}