sweet_cli/bench/
bench_assert.rs1use anyhow::Result;
2use clap::Parser;
3use std::fs;
4use std::process::Command;
5
6#[derive(Debug, Parser)]
40pub struct BenchAssert {
41 #[arg(long, default_value_t = 1000)] iterations: usize,
43 #[arg(long)]
44 expect_only: bool,
45 #[arg(long)]
46 assert_only: bool,
47 #[arg(long)]
49 release: bool,
50 #[arg(long)]
52 run: bool,
53}
54
55const BENCH_DIR: &str = "./tests";
56
57impl BenchAssert {
58 pub fn run(self) -> Result<()> {
59 fs::create_dir_all(BENCH_DIR)?;
60
61 if self.expect_only {
62 self.run_expect()?;
63 } else if self.assert_only {
64 self.run_assert()?;
65 } else {
66 self.run_expect()?;
67 self.run_assert()?;
68 }
69
70 Ok(())
71 }
72
73
74 fn run_assert(&self) -> Result<()> {
75 self.create_iter_file(ASSERT_FILE_PATH, ASSERT_TEMPLATE, |i| {
76 format!("\tassert_eq!({},{});\n", i, i)
77 })?;
78 self.bench_compile("assert")?;
79 if self.run {
80 self.bench_run("assert")?;
81 }
82 Ok(())
83 }
84 fn run_expect(&self) -> Result<()> {
85 self.create_iter_file(EXPECT_FILE_PATH, EXPECT_TEMPLATE, |i| {
86 format!("\texpect({},{});\n", i, i)
87 })?;
88 self.bench_compile("expect")?;
89 if self.run {
90 self.bench_run("expect")?;
91 }
92 Ok(())
93 }
94
95 fn create_iter_file(
96 &self,
97 file_path: &str,
98 file_template: &str,
99 mk_str: impl Fn(usize) -> String,
100 ) -> Result<()> {
101 let mut iterations = String::new();
102 for i in 0..self.iterations {
103 iterations.push_str(&mk_str(i));
104 }
105
106 let output =
107 String::from(file_template).replace("__iterations__", &iterations);
108
109 fs::write(file_path, output)?;
110 Ok(())
111 }
112
113
114 fn bench_compile(&self, test_name: &str) -> Result<()> {
115 let mut command = Command::new("cargo");
117 command.arg("build").arg("--test").arg(test_name);
118 if self.release {
119 command.arg("--release");
120 }
121 let output = command.output()?;
122
123 let stderr = String::from_utf8_lossy(&output.stderr);
124
125 let duration = stderr
126 .lines()
127 .find(|line| line.contains("Finished"))
128 .expect("line not found")
129 .split(" ")
130 .last()
131 .unwrap()
132 .replace("s", "")
133 .parse::<f64>()
134 .unwrap();
135
136 let time_per_iter = (duration / self.iterations as f64) * 1000.;
137
138 println!(
139 "{} lines of '{}' comilied in {:.2}s, each line added {:.2}ms",
140 self.iterations, test_name, duration, time_per_iter
141 );
142 Ok(())
143 }
144
145 fn bench_run(&self, test_name: &str) -> Result<()> {
146 let output = Command::new("cargo")
147 .arg("test")
148 .arg("--test")
149 .arg(test_name)
150 .arg("--")
151 .arg("--nocapture")
152 .output()?;
153 let output = String::from_utf8_lossy(&output.stdout);
154 println!("{}", output);
155
156 let duration = output
157 .lines()
158 .find(|line| line.contains("__"))
159 .and_then(|line| line.split("__").nth(1))
160 .and_then(|num| num.parse::<f64>().ok())
161 .expect("Failed to find and parse number");
162
163 let time_per_iter = (duration / self.iterations as f64) * 1000.;
164
165 println!(
166 "{} lines of '{}' ran in {:.2}s, each line added {:.2}ms",
167 self.iterations, test_name, duration, time_per_iter
168 );
169
170 Ok(())
171 }
172}
173
174const ASSERT_FILE_PATH: &str = "./tests/assert.rs";
175const ASSERT_TEMPLATE: &str = r#"
176 use std::time::Instant;
177 #[test]
178 fn main(){
179 let start = Instant::now();
180__iterations__
181 println!("__{:.2}__", start.elapsed().as_secs_f32());
182}
183"#;
184const EXPECT_FILE_PATH: &str = "./tests/expect.rs";
185const EXPECT_TEMPLATE: &str = r#"
186 use std::time::Instant;
187 #[test]
188 fn main(){
189 let start = Instant::now();
190__iterations__
191 println!("__{:.2}__", start.elapsed().as_secs_f32());
192 }
193
194 fn expect(a: i32, b: i32) {
195 if a != b {
196 panic!("Expected {} but got {}", a, b);
197 }
198 }
199"#;