use serde::Deserialize;
use std::collections::HashMap;
use std::path::Path;
use avatarr_parser::{Quality, QualityModel};
#[derive(Deserialize)]
struct Fixture {
cases: Vec<Case>,
}
#[derive(Deserialize)]
struct Case {
input: String,
}
#[derive(Deserialize)]
struct Expectations {
mappings: HashMap<String, String>,
}
enum Outcome {
Pass,
Fail,
Undecidable,
}
#[test]
fn quality_fixtures_pass_rate_recorded() {
let fixture_path = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../../tests/sonarr-fixtures/quality_parsing.json");
let raw = std::fs::read_to_string(&fixture_path)
.unwrap_or_else(|e| panic!("read fixture {}: {e}", fixture_path.display()));
let fx: Fixture = serde_json::from_str(&raw).expect("parse quality_parsing.json");
let expectations: Expectations = serde_json::from_str(include_str!(
"../../../tests/sonarr-fixtures/quality_parsing_expectations.json"
))
.expect("parse quality_parsing_expectations.json");
let mut pass = 0u32;
let mut fail = 0u32;
let mut undecidable = 0u32;
let total = fx.cases.len() as u32;
let debug_failures = std::env::var("AVATARR_DEBUG_QUALITY_FAILURES").is_ok();
for case in &fx.cases {
let model = avatarr_parser::parse_quality(&case.input);
match compare_against_expected(&case.input, &model, &expectations) {
Outcome::Pass => pass += 1,
Outcome::Undecidable => undecidable += 1,
Outcome::Fail => {
fail += 1;
if debug_failures {
let expected = expectations.mappings.get(&case.input).map(String::as_str);
eprintln!(
" FAIL: input={:?} expected={:?} got={:?}",
case.input, expected, model.quality,
);
}
}
}
}
let denominator = total - undecidable;
let pct = if denominator == 0 {
0.0
} else {
100.0 * pass as f64 / denominator as f64
};
eprintln!(
"quality_fixtures: {pass}/{denominator} pass ({pct:.1}%); \
{fail} fail, {undecidable} undecidable, {total} total cases",
);
assert_eq!(
pass + fail + undecidable,
total,
"outcome counts must sum to total cases"
);
assert!(
pct >= 100.0,
"quality fixture pass rate {pct:.1}% is below the 100% gate ({fail} failures)"
);
}
fn compare_against_expected(
input: &str,
model: &QualityModel,
expectations: &Expectations,
) -> Outcome {
let Some(expected_token) = expectations.mappings.get(input) else {
return Outcome::Undecidable;
};
let lower = expected_token.to_ascii_lowercase();
if matches!(lower.as_str(), "sdtv" | "dvd" | "rawhd" | "unknown") {
return Outcome::Undecidable;
}
let expected = match lower.as_str() {
"webdl1080p" => Quality::Webdl1080p,
"hdtv720p" => Quality::Hdtv720p,
"webdl720p" => Quality::Webdl720p,
"bluray720p" => Quality::Bluray720p,
"bluray1080p" => Quality::Bluray1080p,
"webdl480p" => Quality::Webdl480p,
"hdtv1080p" => Quality::Hdtv1080p,
"webrip480p" => Quality::Webrip480p,
"bluray480p" => Quality::Bluray480p,
"webrip720p" => Quality::Webrip720p,
"webrip1080p" => Quality::Webrip1080p,
"hdtv2160p" => Quality::Hdtv2160p,
"webrip2160p" => Quality::Webrip2160p,
"webdl2160p" => Quality::Webdl2160p,
"bluray2160p" => Quality::Bluray2160p,
"bluray1080premux" => Quality::Bluray1080pRemux,
"bluray2160premux" => Quality::Bluray2160pRemux,
"bluray576p" => Quality::Bluray576p,
_ => return Outcome::Undecidable,
};
if model.quality == expected {
Outcome::Pass
} else {
Outcome::Fail
}
}