use crate::tests::helpers;
use crate::*;
use std::time::{Duration, Instant};
const DEFAULT_INSTRUCTION_BUDGET: u64 = 3_000_000;
const DEFAULT_MIN_IPS: f64 = 150_000.0;
#[test]
fn sc32win_donut_emulation_throughput_regression_guard() {
helpers::setup();
if cfg!(debug_assertions) {
eprintln!(
"sc32win_donut_emulation_throughput_regression_guard: skipped in debug builds \
(run `cargo test -p libmwemu sc32win_donut_emulation_throughput --release`)"
);
return;
}
let budget: u64 = std::env::var("MWEMU_PERF_INSTRUCTION_BUDGET")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(DEFAULT_INSTRUCTION_BUDGET);
let min_ips: f64 = std::env::var("MWEMU_PERF_MIN_IPS")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(DEFAULT_MIN_IPS);
let mut emu = emu32();
emu.cfg.maps_folder = helpers::win32_maps_folder();
let sample = helpers::test_data_path("sc32win_donut.bin");
emu.load_code(&sample);
emu.set_verbose(2);
let t0 = Instant::now();
emu.run_to(budget).expect("emulation should complete");
let elapsed = t0.elapsed().as_secs_f64();
assert!(
elapsed > 0.0,
"elapsed time underflow; budget={} pos={}",
budget,
emu.pos
);
let ips = budget as f64 / elapsed;
assert!(
ips >= min_ips,
"emulation throughput regression: {:.0} ins/s < {:.0} ins/s (budget={} in {:.3}s). \
Raise MWEMU_PERF_MIN_IPS if CI is legitimately slower, or fix the performance regression. \
Calibrate with: cargo test -p libmwemu sc32win_donut_emulation_throughput --release -- --nocapture",
ips,
min_ips,
budget,
elapsed
);
eprintln!(
"perf: sc32win_donut budget={} elapsed={:.3}s => {:.0} ins/s (min {:.0})",
budget, elapsed, ips, min_ips
);
}
#[test]
fn benchmark32win_donut() {
helpers::setup();
if cfg!(debug_assertions) {
eprintln!("benchmark: skipped in debug builds (run with --release)");
return;
}
let mut emu = emu32();
emu.cfg.maps_folder = helpers::win32_maps_folder();
emu.cfg.console = false;
emu.cfg.console_enabled = false;
emu.disable_ctrlc();
let sample = helpers::test_data_path("sc32win_donut.bin");
assert!(
std::path::Path::new(&sample).is_file(),
"missing {}",
sample
);
emu.load_code(&sample);
emu.set_verbose(2);
let target_pos: u64 = std::env::var("MWEMU_BENCH_DONUT_TARGET_POS")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(20_000_001);
let budget = Duration::from_secs(25);
let t0 = Instant::now();
emu.run_to(target_pos).unwrap_or_else(|e| {
panic!(
"benchmark run_to({}) failed: {} (pos={} rip=0x{:x})",
target_pos,
e,
emu.pos,
emu.regs().rip
);
});
let elapsed = t0.elapsed();
assert!(
elapsed <= budget,
"benchmark regression: run_to({}) took {:?} (> {:?}) (pos={} rip=0x{:x})",
target_pos,
elapsed,
budget,
emu.pos,
emu.regs().rip
);
}
#[test]
fn benchmark64with_enigma() {
helpers::setup();
let mut emu = emu64();
emu.cfg.maps_folder = helpers::win64_maps_folder();
emu.cfg.console = false;
emu.cfg.console_enabled = false;
emu.disable_ctrlc();
let sample = helpers::test_data_path("exe64win_enigma.bin");
assert!(
std::path::Path::new(&sample).is_file(),
"missing {}",
sample
);
emu.load_code(&sample);
emu.set_verbose(2);
let (target_pos, budget) = if cfg!(debug_assertions) {
let target_pos: u64 = std::env::var("MWEMU_BENCH_ENIGMA_TARGET_POS_DEBUG")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(1_000_000);
(target_pos, Duration::from_secs(20))
} else {
let target_pos: u64 = std::env::var("MWEMU_BENCH_ENIGMA_TARGET_POS")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(12_000_000);
let max_secs: u64 = std::env::var("MWEMU_BENCH_ENIGMA_MAX_SECS_RELEASE")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(if cfg!(windows) { 13 } else { 7 });
(target_pos, Duration::from_secs(max_secs))
};
let t0 = Instant::now();
emu.run_to(target_pos).unwrap_or_else(|e| {
panic!(
"benchmark64with_enigma run_to({}) failed: {} (pos={} rip=0x{:x})",
target_pos,
e,
emu.pos,
emu.regs().rip
);
});
let elapsed = t0.elapsed();
assert!(
elapsed <= budget,
"benchmark64with_enigma regression: run_to({}) took {:?} (> {:?}) (pos={} rip=0x{:x})",
target_pos,
elapsed,
budget,
emu.pos,
emu.regs().rip
);
}