execution_order_tests.rs
1 use std::{fs::File, io::Read, path::PathBuf}; 2 3 use tempfile::{tempdir, TempDir}; 4 5 mod common; 6 use common::hyperfine; 7 8 struct ExecutionOrderTest { 9 cmd: assert_cmd::Command, 10 expected_content: String, 11 logfile_path: PathBuf, 12 #[allow(dead_code)] 13 tempdir: TempDir, 14 } 15 16 impl ExecutionOrderTest { 17 fn new() -> Self { 18 let tempdir = tempdir().unwrap(); 19 let logfile_path = tempdir.path().join("output.log"); 20 21 ExecutionOrderTest { 22 cmd: hyperfine(), 23 expected_content: String::new(), 24 logfile_path, 25 tempdir, 26 } 27 } 28 29 fn arg<S: AsRef<str>>(&mut self, arg: S) -> &mut Self { 30 self.cmd.arg(arg.as_ref()); 31 self 32 } 33 34 fn get_command(&self, output: &str) -> String { 35 format!( 36 "echo {output} >> {path}", 37 output = output, 38 path = self.logfile_path.to_string_lossy() 39 ) 40 } 41 42 fn command(&mut self, output: &str) -> &mut Self { 43 self.arg(self.get_command(output)); 44 self 45 } 46 47 fn setup(&mut self, output: &str) -> &mut Self { 48 self.arg("--setup"); 49 self.command(output) 50 } 51 52 fn prepare(&mut self, output: &str) -> &mut Self { 53 self.arg("--prepare"); 54 self.command(output) 55 } 56 57 fn reference(&mut self, output: &str) -> &mut Self { 58 self.arg("--reference"); 59 self.command(output) 60 } 61 62 fn conclude(&mut self, output: &str) -> &mut Self { 63 self.arg("--conclude"); 64 self.command(output) 65 } 66 67 fn cleanup(&mut self, output: &str) -> &mut Self { 68 self.arg("--cleanup"); 69 self.command(output) 70 } 71 72 fn expect_output(&mut self, output: &str) -> &mut Self { 73 self.expected_content.push_str(output); 74 75 #[cfg(windows)] 76 { 77 self.expected_content.push_str(" \r"); 78 } 79 80 self.expected_content.push('\n'); 81 self 82 } 83 84 fn run(&mut self) { 85 self.cmd.assert().success(); 86 87 let mut f = File::open(&self.logfile_path).unwrap(); 88 let mut content = String::new(); 89 f.read_to_string(&mut content).unwrap(); 90 91 assert_eq!(content, self.expected_content); 92 } 93 } 94 95 impl Default for ExecutionOrderTest { 96 fn default() -> Self { 97 Self::new() 98 } 99 } 100 101 #[test] 102 fn benchmarks_are_executed_sequentially_one() { 103 ExecutionOrderTest::new() 104 .arg("--runs=1") 105 .command("command 1") 106 .command("command 2") 107 .expect_output("command 1") 108 .expect_output("command 2") 109 .run(); 110 } 111 112 #[test] 113 fn benchmarks_are_executed_sequentially() { 114 ExecutionOrderTest::new() 115 .arg("--runs=2") 116 .command("command 1") 117 .command("command 2") 118 .expect_output("command 1") 119 .expect_output("command 1") 120 .expect_output("command 2") 121 .expect_output("command 2") 122 .run(); 123 } 124 125 #[test] 126 fn warmup_runs_are_executed_before_benchmarking_runs() { 127 ExecutionOrderTest::new() 128 .arg("--runs=2") 129 .arg("--warmup=3") 130 .command("command 1") 131 .expect_output("command 1") 132 .expect_output("command 1") 133 .expect_output("command 1") 134 .expect_output("command 1") 135 .expect_output("command 1") 136 .run(); 137 } 138 139 #[test] 140 fn setup_commands_are_executed_before_each_series_of_timing_runs() { 141 ExecutionOrderTest::new() 142 .arg("--runs=2") 143 .setup("setup") 144 .command("command 1") 145 .command("command 2") 146 .expect_output("setup") 147 .expect_output("command 1") 148 .expect_output("command 1") 149 .expect_output("setup") 150 .expect_output("command 2") 151 .expect_output("command 2") 152 .run(); 153 } 154 155 #[test] 156 fn prepare_commands_are_executed_before_each_timing_run() { 157 ExecutionOrderTest::new() 158 .arg("--runs=2") 159 .prepare("prepare") 160 .command("command 1") 161 .command("command 2") 162 .expect_output("prepare") 163 .expect_output("command 1") 164 .expect_output("prepare") 165 .expect_output("command 1") 166 .expect_output("prepare") 167 .expect_output("command 2") 168 .expect_output("prepare") 169 .expect_output("command 2") 170 .run(); 171 } 172 173 #[test] 174 fn conclude_commands_are_executed_after_each_timing_run() { 175 ExecutionOrderTest::new() 176 .arg("--runs=2") 177 .conclude("conclude") 178 .command("command 1") 179 .command("command 2") 180 .expect_output("command 1") 181 .expect_output("conclude") 182 .expect_output("command 1") 183 .expect_output("conclude") 184 .expect_output("command 2") 185 .expect_output("conclude") 186 .expect_output("command 2") 187 .expect_output("conclude") 188 .run(); 189 } 190 191 #[test] 192 fn prepare_commands_are_executed_before_each_warmup() { 193 ExecutionOrderTest::new() 194 .arg("--warmup=2") 195 .arg("--runs=1") 196 .prepare("prepare") 197 .command("command 1") 198 .command("command 2") 199 // warmup 1 200 .expect_output("prepare") 201 .expect_output("command 1") 202 .expect_output("prepare") 203 .expect_output("command 1") 204 // benchmark 1 205 .expect_output("prepare") 206 .expect_output("command 1") 207 // warmup 2 208 .expect_output("prepare") 209 .expect_output("command 2") 210 .expect_output("prepare") 211 .expect_output("command 2") 212 // benchmark 2 213 .expect_output("prepare") 214 .expect_output("command 2") 215 .run(); 216 } 217 218 #[test] 219 fn conclude_commands_are_executed_after_each_warmup() { 220 ExecutionOrderTest::new() 221 .arg("--warmup=2") 222 .arg("--runs=1") 223 .conclude("conclude") 224 .command("command 1") 225 .command("command 2") 226 // warmup 1 227 .expect_output("command 1") 228 .expect_output("conclude") 229 .expect_output("command 1") 230 .expect_output("conclude") 231 // benchmark 1 232 .expect_output("command 1") 233 .expect_output("conclude") 234 // warmup 2 235 .expect_output("command 2") 236 .expect_output("conclude") 237 .expect_output("command 2") 238 .expect_output("conclude") 239 // benchmark 2 240 .expect_output("command 2") 241 .expect_output("conclude") 242 .run(); 243 } 244 245 #[test] 246 fn cleanup_commands_are_executed_once_after_each_benchmark() { 247 ExecutionOrderTest::new() 248 .arg("--runs=2") 249 .cleanup("cleanup") 250 .command("command 1") 251 .command("command 2") 252 .expect_output("command 1") 253 .expect_output("command 1") 254 .expect_output("cleanup") 255 .expect_output("command 2") 256 .expect_output("command 2") 257 .expect_output("cleanup") 258 .run(); 259 } 260 261 #[test] 262 fn setup_prepare_cleanup_combined() { 263 ExecutionOrderTest::new() 264 .arg("--warmup=1") 265 .arg("--runs=2") 266 .setup("setup") 267 .prepare("prepare") 268 .command("command1") 269 .command("command2") 270 .cleanup("cleanup") 271 // 1 272 .expect_output("setup") 273 .expect_output("prepare") 274 .expect_output("command1") 275 .expect_output("prepare") 276 .expect_output("command1") 277 .expect_output("prepare") 278 .expect_output("command1") 279 .expect_output("cleanup") 280 // 2 281 .expect_output("setup") 282 .expect_output("prepare") 283 .expect_output("command2") 284 .expect_output("prepare") 285 .expect_output("command2") 286 .expect_output("prepare") 287 .expect_output("command2") 288 .expect_output("cleanup") 289 .run(); 290 } 291 292 #[test] 293 fn setup_prepare_conclude_cleanup_combined() { 294 ExecutionOrderTest::new() 295 .arg("--warmup=1") 296 .arg("--runs=2") 297 .setup("setup") 298 .prepare("prepare") 299 .command("command1") 300 .command("command2") 301 .conclude("conclude") 302 .cleanup("cleanup") 303 // 1 304 .expect_output("setup") 305 .expect_output("prepare") 306 .expect_output("command1") 307 .expect_output("conclude") 308 .expect_output("prepare") 309 .expect_output("command1") 310 .expect_output("conclude") 311 .expect_output("prepare") 312 .expect_output("command1") 313 .expect_output("conclude") 314 .expect_output("cleanup") 315 // 2 316 .expect_output("setup") 317 .expect_output("prepare") 318 .expect_output("command2") 319 .expect_output("conclude") 320 .expect_output("prepare") 321 .expect_output("command2") 322 .expect_output("conclude") 323 .expect_output("prepare") 324 .expect_output("command2") 325 .expect_output("conclude") 326 .expect_output("cleanup") 327 .run(); 328 } 329 330 #[test] 331 fn single_parameter_value() { 332 ExecutionOrderTest::new() 333 .arg("--runs=2") 334 .arg("--parameter-list") 335 .arg("number") 336 .arg("1,2,3") 337 .command("command {number}") 338 .expect_output("command 1") 339 .expect_output("command 1") 340 .expect_output("command 2") 341 .expect_output("command 2") 342 .expect_output("command 3") 343 .expect_output("command 3") 344 .run(); 345 } 346 347 #[test] 348 fn multiple_parameter_values() { 349 ExecutionOrderTest::new() 350 .arg("--runs=2") 351 .arg("--parameter-list") 352 .arg("number") 353 .arg("1,2,3") 354 .arg("--parameter-list") 355 .arg("letter") 356 .arg("a,b") 357 .command("command {number} {letter}") 358 .expect_output("command 1 a") 359 .expect_output("command 1 a") 360 .expect_output("command 2 a") 361 .expect_output("command 2 a") 362 .expect_output("command 3 a") 363 .expect_output("command 3 a") 364 .expect_output("command 1 b") 365 .expect_output("command 1 b") 366 .expect_output("command 2 b") 367 .expect_output("command 2 b") 368 .expect_output("command 3 b") 369 .expect_output("command 3 b") 370 .run(); 371 } 372 373 #[test] 374 fn reference_is_executed_first() { 375 ExecutionOrderTest::new() 376 .arg("--runs=1") 377 .reference("reference") 378 .command("command 1") 379 .command("command 2") 380 .expect_output("reference") 381 .expect_output("command 1") 382 .expect_output("command 2") 383 .run(); 384 } 385 386 #[test] 387 fn reference_is_executed_first_parameter_value() { 388 ExecutionOrderTest::new() 389 .arg("--runs=2") 390 .reference("reference") 391 .arg("--parameter-list") 392 .arg("number") 393 .arg("1,2,3") 394 .command("command {number}") 395 .expect_output("reference") 396 .expect_output("reference") 397 .expect_output("command 1") 398 .expect_output("command 1") 399 .expect_output("command 2") 400 .expect_output("command 2") 401 .expect_output("command 3") 402 .expect_output("command 3") 403 .run(); 404 } 405 406 #[test] 407 fn reference_is_executed_separately_from_commands() { 408 ExecutionOrderTest::new() 409 .arg("--runs=1") 410 .reference("command 1") 411 .command("command 1") 412 .command("command 2") 413 .expect_output("command 1") 414 .expect_output("command 1") 415 .expect_output("command 2") 416 .run(); 417 } 418 419 #[test] 420 fn setup_prepare_reference_conclude_cleanup_combined() { 421 ExecutionOrderTest::new() 422 .arg("--warmup=1") 423 .arg("--runs=2") 424 .setup("setup") 425 .prepare("prepare") 426 .reference("reference") 427 .command("command1") 428 .command("command2") 429 .conclude("conclude") 430 .cleanup("cleanup") 431 // reference 432 .expect_output("setup") 433 .expect_output("prepare") 434 .expect_output("reference") 435 .expect_output("conclude") 436 .expect_output("prepare") 437 .expect_output("reference") 438 .expect_output("conclude") 439 .expect_output("prepare") 440 .expect_output("reference") 441 .expect_output("conclude") 442 .expect_output("cleanup") 443 // 1 444 .expect_output("setup") 445 .expect_output("prepare") 446 .expect_output("command1") 447 .expect_output("conclude") 448 .expect_output("prepare") 449 .expect_output("command1") 450 .expect_output("conclude") 451 .expect_output("prepare") 452 .expect_output("command1") 453 .expect_output("conclude") 454 .expect_output("cleanup") 455 // 2 456 .expect_output("setup") 457 .expect_output("prepare") 458 .expect_output("command2") 459 .expect_output("conclude") 460 .expect_output("prepare") 461 .expect_output("command2") 462 .expect_output("conclude") 463 .expect_output("prepare") 464 .expect_output("command2") 465 .expect_output("conclude") 466 .expect_output("cleanup") 467 .run(); 468 } 469 470 #[test] 471 fn setup_separate_prepare_separate_conclude_cleanup_combined() { 472 ExecutionOrderTest::new() 473 .arg("--warmup=1") 474 .arg("--runs=2") 475 .setup("setup") 476 .cleanup("cleanup") 477 .prepare("prepare1") 478 .command("command1") 479 .conclude("conclude1") 480 .prepare("prepare2") 481 .command("command2") 482 .conclude("conclude2") 483 // 1 484 .expect_output("setup") 485 .expect_output("prepare1") 486 .expect_output("command1") 487 .expect_output("conclude1") 488 .expect_output("prepare1") 489 .expect_output("command1") 490 .expect_output("conclude1") 491 .expect_output("prepare1") 492 .expect_output("command1") 493 .expect_output("conclude1") 494 .expect_output("cleanup") 495 // 2 496 .expect_output("setup") 497 .expect_output("prepare2") 498 .expect_output("command2") 499 .expect_output("conclude2") 500 .expect_output("prepare2") 501 .expect_output("command2") 502 .expect_output("conclude2") 503 .expect_output("prepare2") 504 .expect_output("command2") 505 .expect_output("conclude2") 506 .expect_output("cleanup") 507 .run(); 508 } 509 510 #[test] 511 fn setup_separate_prepare_reference_separate_conclude_cleanup_combined() { 512 ExecutionOrderTest::new() 513 .arg("--warmup=1") 514 .arg("--runs=2") 515 .setup("setup") 516 .cleanup("cleanup") 517 .prepare("prepareref") 518 .reference("reference") 519 .conclude("concluderef") 520 .prepare("prepare1") 521 .command("command1") 522 .conclude("conclude1") 523 .prepare("prepare2") 524 .command("command2") 525 .conclude("conclude2") 526 // reference 527 .expect_output("setup") 528 .expect_output("prepareref") 529 .expect_output("reference") 530 .expect_output("concluderef") 531 .expect_output("prepareref") 532 .expect_output("reference") 533 .expect_output("concluderef") 534 .expect_output("prepareref") 535 .expect_output("reference") 536 .expect_output("concluderef") 537 .expect_output("cleanup") 538 // 1 539 .expect_output("setup") 540 .expect_output("prepare1") 541 .expect_output("command1") 542 .expect_output("conclude1") 543 .expect_output("prepare1") 544 .expect_output("command1") 545 .expect_output("conclude1") 546 .expect_output("prepare1") 547 .expect_output("command1") 548 .expect_output("conclude1") 549 .expect_output("cleanup") 550 // 2 551 .expect_output("setup") 552 .expect_output("prepare2") 553 .expect_output("command2") 554 .expect_output("conclude2") 555 .expect_output("prepare2") 556 .expect_output("command2") 557 .expect_output("conclude2") 558 .expect_output("prepare2") 559 .expect_output("command2") 560 .expect_output("conclude2") 561 .expect_output("cleanup") 562 .run(); 563 }