use cdrustanoia_core::device::samples_to_bytes; #[test] fn end_to_end_image_rip() { let dir = tempfile::tempdir().unwrap(); let image = dir.path().join("disc.raw"); let out = dir.path().join("out.raw"); let sectors = 24usize; let mut samples = Vec::with_capacity(sectors * 1176); for i in 0..sectors * 1176 { samples.push(((i as i32 * 7919) % 65536 - 32768) as i16); } std::fs::write(&image, samples_to_bytes(&samples)).unwrap(); std::env::set_var("CDRUSTANOIA_TEST_IMAGE", &image); let argv = vec![ "cdparanoia".to_string(), "-q".to_string(), "-r".to_string(), "1".to_string(), out.to_string_lossy().into_owned(), ]; assert_eq!(cdrustanoia_cli::run(&argv), 0); let got = std::fs::read(&out).unwrap(); assert_eq!(got, samples_to_bytes(&samples)); // Output formats and sample offsets use the same source data. let raw_bytes = samples_to_bytes(&samples); let wav = dir.path().join("format.wav"); let argv = vec![ "cdparanoia".to_string(), "-q".to_string(), "-w".to_string(), "1".to_string(), wav.to_string_lossy().into_owned(), ]; assert_eq!(cdrustanoia_cli::run(&argv), 0); let wav_bytes = std::fs::read(&wav).unwrap(); assert_eq!(&wav_bytes[..4], b"RIFF"); assert_eq!(&wav_bytes[44..], raw_bytes.as_slice()); let aiff = dir.path().join("format.aiff"); let argv = vec![ "cdparanoia".to_string(), "-q".to_string(), "-f".to_string(), "1".to_string(), aiff.to_string_lossy().into_owned(), ]; assert_eq!(cdrustanoia_cli::run(&argv), 0); let aiff_bytes = std::fs::read(&aiff).unwrap(); assert_eq!(&aiff_bytes[..4], b"FORM"); let mut big_endian = Vec::with_capacity(samples.len() * 2); for sample in &samples { big_endian.extend_from_slice(&sample.to_be_bytes()); } assert_eq!(&aiff_bytes[54..], big_endian.as_slice()); let offset_out = dir.path().join("offset.raw"); let argv = vec![ "cdparanoia".to_string(), "-q".to_string(), "-r".to_string(), "-O".to_string(), "10".to_string(), "1".to_string(), offset_out.to_string_lossy().into_owned(), ]; assert_eq!(cdrustanoia_cli::run(&argv), 0); let offset_bytes = std::fs::read(&offset_out).unwrap(); assert_eq!(&offset_bytes[..raw_bytes.len() - 40], &raw_bytes[40..]); for mode in ["-Z", "-Y"] { let out = dir .path() .join(format!("mode{}.raw", mode.trim_start_matches('-'))); let argv = vec![ "cdparanoia".to_string(), "-q".to_string(), "-r".to_string(), mode.to_string(), "1".to_string(), out.to_string_lossy().into_owned(), ]; assert_eq!(cdrustanoia_cli::run(&argv), 0); assert_eq!(std::fs::read(&out).unwrap(), raw_bytes); } // The compact image has no simulated cache; -A must complete cleanly. let analyze_log = dir.path().join("analyze.log"); let analyze_argv = vec![ "cdparanoia".to_string(), "-A".to_string(), format!("-L{}", analyze_log.to_string_lossy()), ]; std::env::set_var("CDRUSTANOIA_TEST_IMAGE", &image); assert_eq!(cdrustanoia_cli::run(&analyze_argv), 0); let analyze_text = std::fs::read_to_string(&analyze_log).unwrap(); assert!(analyze_text.contains("Checking drive cache/timing behavior")); std::env::remove_var("CDRUSTANOIA_TEST_IMAGE"); // A payload larger than the async writer's chunk size exercises the // queue and chunk-boundary path end to end. let big_image = dir.path().join("big.raw"); let big_out = dir.path().join("big.out.raw"); let big_sectors = 300usize; let mut big_samples = Vec::with_capacity(big_sectors * 1176); for i in 0..big_sectors * 1176 { big_samples.push((((i as i64 * 65537) % 65536) as i32 - 32768) as i16); } std::fs::write(&big_image, samples_to_bytes(&big_samples)).unwrap(); std::env::set_var("CDRUSTANOIA_TEST_IMAGE", &big_image); let argv = vec![ "cdparanoia".to_string(), "-q".to_string(), "-r".to_string(), "1".to_string(), big_out.to_string_lossy().into_owned(), ]; assert_eq!(cdrustanoia_cli::run(&argv), 0); assert_eq!( std::fs::read(&big_out).unwrap(), samples_to_bytes(&big_samples) ); std::env::set_var("CDRUSTANOIA_TEST_IMAGE", &image); // Multi-track batch mode. let multi = dir.path().join("multi.raw"); let mut multi_samples = Vec::new(); for i in 0..(48 * 1176) { multi_samples.push((((i as i64 * 104729) % 65536) as i32 - 32768) as i16); } std::fs::write(&multi, samples_to_bytes(&multi_samples)).unwrap(); std::fs::write(dir.path().join("multi.raw.toc"), "0 23 0\n24 47 0\n").unwrap(); std::env::set_var("CDRUSTANOIA_TEST_IMAGE", &multi); let argv = vec![ "cdparanoia".to_string(), "-q".to_string(), "-B".to_string(), "1-2".to_string(), format!("{}/", dir.path().display()), ]; assert_eq!(cdrustanoia_cli::run(&argv), 0); let t1 = std::fs::read(dir.path().join("track01.cdda.wav")).unwrap(); let t2 = std::fs::read(dir.path().join("track02.cdda.wav")).unwrap(); assert_eq!(&t1[44..], &samples_to_bytes(&multi_samples[..24 * 1176])); assert_eq!(&t2[44..], &samples_to_bytes(&multi_samples[24 * 1176..])); std::env::remove_var("CDRUSTANOIA_TEST_IMAGE"); } #[test] fn version_output_matches_cdparanoia_shape() { let exe = env!("CARGO_BIN_EXE_cdparanoia"); let out = std::process::Command::new(exe) .arg("-V") .output() .expect("run cdparanoia -V"); assert!(out.status.success()); let expected = format!("{}\n", cdrustanoia_core::VERSION); assert_eq!(String::from_utf8_lossy(&out.stderr), expected); } #[test] fn help_starts_with_cargo_version() { let exe = env!("CARGO_BIN_EXE_cdparanoia"); let out = std::process::Command::new(exe) .arg("--help") .output() .expect("run cdparanoia --help"); assert!(out.status.success()); let text = String::from_utf8_lossy(&out.stdout); let first = text.lines().next().unwrap_or_default(); assert_eq!(first, cdrustanoia_core::VERSION); assert!(!text.contains("2026-11-03")); }