164 lines
4.1 KiB
Rust
164 lines
4.1 KiB
Rust
use std::path::PathBuf;
|
|
use std::process::Command;
|
|
|
|
use tempfile::tempdir;
|
|
|
|
use vid_repair_core::config::Config;
|
|
use vid_repair_core::rules::RuleSet;
|
|
use vid_repair_core::scan::scan_file;
|
|
|
|
fn command_available(cmd: &str) -> bool {
|
|
Command::new(cmd)
|
|
.arg("-version")
|
|
.output()
|
|
.map(|out| out.status.success())
|
|
.unwrap_or(false)
|
|
}
|
|
|
|
fn ruleset_dir() -> PathBuf {
|
|
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
manifest_dir
|
|
.parent()
|
|
.expect("workspace root")
|
|
.join("rulesets")
|
|
}
|
|
|
|
fn fixture_dir() -> PathBuf {
|
|
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
manifest_dir
|
|
.parent()
|
|
.expect("workspace root")
|
|
.join("tests")
|
|
.join("fixtures")
|
|
.join("generated")
|
|
}
|
|
|
|
fn should_skip() -> bool {
|
|
!command_available("ffmpeg") || !command_available("ffprobe")
|
|
}
|
|
|
|
#[test]
|
|
fn scan_clean_fixture_has_no_issues() {
|
|
if should_skip() {
|
|
eprintln!("ffmpeg/ffprobe not available; skipping fixture test");
|
|
return;
|
|
}
|
|
|
|
let dir = tempdir().expect("tempdir");
|
|
let output = dir.path().join("clean.mp4");
|
|
|
|
let status = Command::new("ffmpeg")
|
|
.args([
|
|
"-y",
|
|
"-hide_banner",
|
|
"-loglevel",
|
|
"error",
|
|
"-f",
|
|
"lavfi",
|
|
"-i",
|
|
"testsrc=size=128x72:rate=30",
|
|
"-f",
|
|
"lavfi",
|
|
"-i",
|
|
"sine=frequency=1000:sample_rate=44100",
|
|
"-t",
|
|
"1",
|
|
"-c:v",
|
|
"libx264",
|
|
"-pix_fmt",
|
|
"yuv420p",
|
|
"-c:a",
|
|
"aac",
|
|
"-movflags",
|
|
"+faststart",
|
|
output.to_str().unwrap(),
|
|
])
|
|
.status()
|
|
.expect("ffmpeg run");
|
|
|
|
if !status.success() {
|
|
eprintln!("ffmpeg failed to create fixture");
|
|
return;
|
|
}
|
|
|
|
let config = Config::default();
|
|
let ruleset = RuleSet::load_from_dir(&ruleset_dir()).expect("ruleset load");
|
|
|
|
let scan = scan_file(&output, &config, &ruleset).expect("scan file");
|
|
|
|
assert!(scan.issues.is_empty(), "Expected no issues, got {}", scan.issues.len());
|
|
}
|
|
|
|
#[test]
|
|
fn scan_truncated_fixture_has_errors() {
|
|
if should_skip() {
|
|
eprintln!("ffmpeg/ffprobe not available; skipping fixture test");
|
|
return;
|
|
}
|
|
|
|
let path = fixture_dir().join("truncated.mp4");
|
|
if !path.exists() {
|
|
eprintln!("fixture not found: {}; skipping", path.display());
|
|
return;
|
|
}
|
|
|
|
let config = Config::default();
|
|
let ruleset = RuleSet::load_from_dir(&ruleset_dir()).expect("ruleset load");
|
|
let scan = scan_file(&path, &config, &ruleset).expect("scan file");
|
|
|
|
let allowed = [
|
|
"FILE_ENDED_PREMATURELY",
|
|
"INVALID_DATA_FOUND",
|
|
"ERROR_WHILE_DECODING",
|
|
];
|
|
|
|
let matched = scan
|
|
.issues
|
|
.iter()
|
|
.any(|issue| allowed.contains(&issue.code.as_str()));
|
|
|
|
assert!(
|
|
matched,
|
|
"Expected truncated fixture to match one of {:?}, got {:?}",
|
|
allowed,
|
|
scan.issues.iter().map(|i| i.code.clone()).collect::<Vec<_>>()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn scan_corrupt_fixture_has_errors() {
|
|
if should_skip() {
|
|
eprintln!("ffmpeg/ffprobe not available; skipping fixture test");
|
|
return;
|
|
}
|
|
|
|
let path = fixture_dir().join("corrupt_mid.mp4");
|
|
if !path.exists() {
|
|
eprintln!("fixture not found: {}; skipping", path.display());
|
|
return;
|
|
}
|
|
|
|
let config = Config::default();
|
|
let ruleset = RuleSet::load_from_dir(&ruleset_dir()).expect("ruleset load");
|
|
let scan = scan_file(&path, &config, &ruleset).expect("scan file");
|
|
|
|
let allowed = [
|
|
"INVALID_NAL_UNIT_SIZE",
|
|
"MISSING_PICTURE_ACCESS_UNIT",
|
|
"INVALID_DATA_FOUND",
|
|
"ERROR_WHILE_DECODING",
|
|
];
|
|
|
|
let matched = scan
|
|
.issues
|
|
.iter()
|
|
.any(|issue| allowed.contains(&issue.code.as_str()));
|
|
|
|
assert!(
|
|
matched,
|
|
"Expected corrupt fixture to match one of {:?}, got {:?}",
|
|
allowed,
|
|
scan.issues.iter().map(|i| i.code.clone()).collect::<Vec<_>>()
|
|
);
|
|
}
|