//! The verdict and the command line must match the media server's exactly. //! //! A wrong "it plays" ships a file that opens to a black screen on the device //! it was converted for; a wrong "it does not" spends forty minutes of GPU //! time re-encoding something that was already fine. The command line has to //! agree argument for argument, because a dropped `-pix_fmt` or a `-movflags` //! in the wrong place is a file that encodes successfully and still will not //! play. use serde_json::Value; /// The encoder the server defaults to when nothing names one. const DEFAULT_ENCODER: &str = "h264_nvenc"; fn named(name: &str) -> avplan::Encoder { *avplan::encoder::PREFERRED .iter() .find(|encoder| encoder.name() == name) .unwrap_or_else(|| panic!("{name} is not an encoder")) } fn records(text: &str) -> Vec { text.lines() .filter(|line| !line.trim().is_empty()) .map(|line| serde_json::from_str(line).expect("valid JSON")) .collect() } fn cases() -> Vec { let text = std::fs::read_to_string(parity::dir().join("cases/avplan/cases.ndjson")) .expect("a readable case file"); records(&text) } #[test] fn every_verdict_matches_the_original() { let golden = records(&parity::read_golden_text("avplan/cases.ndjson")); let cases = cases(); assert_eq!(cases.len(), golden.len(), "case count moved — regenerate"); let mut wrong: Vec = Vec::new(); for (case, theirs) in cases.iter().zip(&golden) { let name = case["name"].as_str().expect("a name"); let path = case["path"].as_str().expect("a path"); assert_eq!(theirs["path"].as_str(), Some(path), "cases out of order"); let streams = avplan::Streams { video: case["video"].as_str().map(str::to_string), audio: case["audio"].as_str().map(str::to_string), container: avplan::container_of(path), }; let device = case["vaapi_device"] .as_str() .unwrap_or(avplan::DEFAULT_VAAPI_DEVICE); let output = case["output"] .as_str() .map(str::to_string) .unwrap_or_else(|| avplan::output_beside(path)); let chosen = named(case["encoder"].as_str().unwrap_or(DEFAULT_ENCODER)); let ours = avplan::plan(path, &streams, Some(chosen), &output, device); let mut note = |field: &str, ours: String, theirs: &Value| { wrong.push(format!("{name}.{field}: original {theirs}, rust {ours}")); }; if Some(ours.playable) != theirs["playable"].as_bool() { note("playable", ours.playable.to_string(), &theirs["playable"]); } if Some(ours.copies_audio) != theirs["copies_audio"].as_bool() { note( "copies_audio", ours.copies_audio.to_string(), &theirs["copies_audio"], ); } let their_encoder = theirs["encoder"].as_str(); let our_encoder = ours.encoder.map(avplan::Encoder::name); if our_encoder != their_encoder { note("encoder", format!("{our_encoder:?}"), &theirs["encoder"]); } let their_argv: Vec<&str> = theirs["argv"] .as_array() .expect("an argument list") .iter() .map(|arg| arg.as_str().expect("a string argument")) .collect(); if ours.argv != their_argv { note("argv", format!("{:?}", ours.argv), &theirs["argv"]); } } assert!( wrong.is_empty(), "{} verdicts differ:\n{}", wrong.len(), wrong.join("\n") ); } /// Every branch of the encoder table has to be exercised, or the parity test /// proves nothing about the three encoders nobody's laptop happens to have. #[test] fn every_encoder_appears_among_the_cases() { let named: Vec = cases() .iter() .filter_map(|case| case["encoder"].as_str().map(str::to_string)) .collect(); for encoder in avplan::encoder::PREFERRED { assert!( named.iter().any(|name| name == encoder.name()), "no case uses {}", encoder.name() ); } } /// Catches a golden file drifting away from an original that has moved on. #[test] #[cfg(feature = "python")] fn the_golden_file_still_matches_the_live_original() { let cases = std::fs::read_to_string(parity::dir().join("cases/avplan/cases.ndjson")) .expect("a readable case file"); let live = parity::run_node_oracle("compatible.mjs", &cases).expect("the oracle runs"); assert_eq!(live, parity::read_golden_text("avplan/cases.ndjson")); }