//! Smoke test against real RAW files. //! //! cargo run -p dr-decode --example smoke -- ... //! //! Reports, per file, what each entry point costs — which is the whole reason //! they are separate (ARCH §3.2). use std::path::{Path, PathBuf}; use std::time::Instant; fn main() { env_logger::init(); let args: Vec = std::env::args().skip(1).collect(); if args.is_empty() { eprintln!("usage: smoke ..."); std::process::exit(2); } let mut files = Vec::new(); for a in &args { let p = PathBuf::from(a); if p.is_dir() { collect(&p, &mut files); } else { files.push(p); } } files.sort(); files.truncate(8); println!( "{:<20} {:>7} {:>8} {:>9} {:>13} {:>9} {:>13}", "file", "size", "meta", "thumb", "thumb dims", "full", "full dims" ); println!("{}", "-".repeat(88)); let (mut ok, mut failed) = (0, 0); for f in &files { match run_one(f) { Ok(line) => { println!("{line}"); ok += 1; } Err(e) => { println!("{:<22} {e}", truncate(&name(f), 22)); failed += 1; } } } println!("\n{ok} ok, {failed} failed"); if failed > 0 { std::process::exit(1); } } fn run_one(path: &Path) -> Result { let size = std::fs::metadata(path).map_err(|e| e.to_string())?.len(); // The culling path: read only the header region, not the whole file. let probe_bytes = read_prefix(path, dr_decode::PREVIEW_PROBE_BYTES).map_err(|e| e.to_string())?; let t0 = Instant::now(); let fmt = dr_decode::probe(&probe_bytes); let meta = dr_decode::metadata(&probe_bytes).ok(); let meta_ms = t0.elapsed().as_secs_f64() * 1000.0; let all = std::fs::read(path).map_err(|e| e.to_string())?; // The culling rung. let t1 = Instant::now(); let thumb = dr_decode::extract_preview(&all, dr_decode::PreviewSize::Thumbnail) .map_err(|e| format!("thumb: {e}"))?; let thumb_ms = t1.elapsed().as_secs_f64() * 1000.0; // The full-resolution rung, for comparison. let t2 = Instant::now(); let full = dr_decode::extract_preview(&all, dr_decode::PreviewSize::Full) .map_err(|e| format!("full: {e}"))?; let full_ms = t2.elapsed().as_secs_f64() * 1000.0; let model = meta .as_ref() .and_then(|m| m.model.clone()) .unwrap_or_else(|| "?".into()); let budget = if thumb_ms <= 50.0 { "" } else { " OVER BUDGET" }; Ok(format!( "{:<20} {:>6.1}M {:>6.1}ms {:>7.1}ms {:>7}x{:<5} {:>7.1}ms {:>7}x{:<5} {:?} {}{}", truncate(&name(path), 20), size as f64 / 1e6, meta_ms, thumb_ms, thumb.width, thumb.height, full_ms, full.width, full.height, fmt, model.trim(), budget, )) } fn read_prefix(path: &Path, n: u64) -> std::io::Result> { use std::io::Read; let mut f = std::fs::File::open(path)?; let mut buf = vec![0u8; n as usize]; let read = f.read(&mut buf)?; buf.truncate(read); Ok(buf) } fn collect(dir: &Path, out: &mut Vec) { let Ok(entries) = std::fs::read_dir(dir) else { return; }; for e in entries.flatten() { let p = e.path(); if p.is_file() { let ext = p .extension() .map(|s| s.to_string_lossy().to_ascii_lowercase()) .unwrap_or_default(); if dr_types::Format::from_extension(&ext).is_some() { out.push(p); } } } } fn name(p: &Path) -> String { p.file_name().unwrap_or_default().to_string_lossy().into() } fn truncate(s: &str, n: usize) -> String { if s.len() <= n { s.to_string() } else { format!("{}…", &s[..n - 1]) } }