//! Track switching and probe tracks (`draft-ugur-moq-fast-switching-00`, //! `draft-gurel-moq-probing-00`): the committed corpora //! `wai/moqt-conformance/switching/vectors.json` and `wai/moqt-conformance/probe/vectors.json` //! regenerate byte for byte, every case and refusal in them holds, the drafts' own worked //! examples are in them with the drafts' numbers, and a corpus changed by one value is //! caught. The corpora live beside this crate in the monorepo; if one cannot be read the test //! fails, it never passes on nothing. use joule_moq::{probe, switching}; use serde_json::{json, Value}; const WAI: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../../../wai/"); fn committed(corpus: &str) -> (String, Value) { let text = std::fs::read_to_string(format!("{WAI}moqt-conformance/{corpus}/vectors.json")).unwrap_or_else(|_| panic!("the {corpus} corpus")); let doc = serde_json::from_str(&text).expect("JSON"); (text, doc) } fn cases<'a>(doc: &'a Value, section: &str) -> &'a Vec { doc[section].as_array().unwrap_or_else(|| panic!("{section} of the corpus")) } #[test] fn the_switching_corpus_regenerates_and_holds() { let (text, doc) = committed("switching"); assert!(switching::corpus::generate() == text, "run `cargo run -p joule-moq --example adaptation_vectors -- gen`"); assert_eq!(switching::corpus::verify(&doc), Vec::::new()); } #[test] fn the_probe_corpus_regenerates_and_holds() { let (text, doc) = committed("probe"); assert!(probe::corpus::generate() == text, "run `cargo run -p joule-moq --example adaptation_vectors -- gen`"); assert_eq!(probe::corpus::verify(&doc), Vec::::new()); } /// The numbers the draft prints, held to the corpus as literals: §6.4's Frame Number 28 and /// Table 1, and §7's two End Groups. #[test] fn the_switching_corpus_holds_the_drafts_worked_examples() { let (_, doc) = committed("switching"); let frame = cases(&doc, "frames").iter().find(|c| c["track"] == "720p-long" && c["at"] == json!([13, 3])).expect("§6.4's object"); assert_eq!(frame["frame"], 28); let table: Vec<(String, u64, u64)> = cases(&doc, "switch_points") .iter() .filter(|c| c["catalog"] == "figure-1" && c["frame"] == 28) .map(|c| (c["target"].as_str().expect("a target").to_string(), c["target_group"].as_u64().expect("a group"), c["distance"].as_u64().expect("a distance"))) .collect(); assert_eq!(table, vec![("1080p-short".to_string(), 15, 1), ("1080p-medium".to_string(), 15, 1), ("1080p-long".to_string(), 17, 5)]); let pick = cases(&doc, "selections").iter().find(|c| c["current"] == "720p-long" && c["to"] == "1080p-short" && c["policy"] == "full").expect("§6.4's selection"); assert_eq!(pick["chosen"], "1080p-medium"); let plans = cases(&doc, "plans"); let first = plans.iter().find(|c| c["catalog"] == "section-7").expect("§7's first example"); assert_eq!((first["frame"].as_u64(), first["switch_frame"].as_u64(), first["end_group"].as_u64()), (Some(9), Some(13), Some(5))); let second = plans.iter().find(|c| c["current"] == "720p-long" && c["target"] == "1080p-medium").expect("§7's second example"); assert_eq!((second["target_group"].as_u64(), second["end_group"].as_u64()), (Some(15), Some(13))); assert_eq!(second["subscribe"], json!({"filter_type": 2, "start": [15, 0]})); assert_eq!(second["update"], json!({"filter_type": 3, "start": [5, 0], "end_group_delta": 8})); // Every handoff presents consecutive Frame Numbers: no gap and no frame twice. for h in cases(&doc, "handoffs") { let frames: Vec = h["released"].as_array().expect("released").iter().map(|r| r[3].as_u64().expect("a frame")).collect(); assert!(frames.len() > 4 && frames.windows(2).all(|w| w[1] == w[0] + 1), "{}", h["what"]); } } /// §6's Table 4, and §7.3's Table 6, as the draft prints them. #[test] fn the_probe_corpus_holds_the_drafts_tables() { let (_, doc) = committed("probe"); let probe = |name: &str| cases(&doc, "probes").iter().find(|c| c["name"] == name).unwrap_or_else(|| panic!("{name}")).clone(); let paced = probe("d2000-r300000"); assert_eq!((&paced["shape"], &paced["f"], &paced["s"], &paced["objects"], &paced["b"], &paced["groups"]), (&json!("paced"), &json!([50, 1]), &json!(750), &json!(100), &json!(75_000), &json!(1))); let greedy = probe("b500000"); assert_eq!((&greedy["shape"], &greedy["s"], &greedy["objects"], &greedy["groups"], &greedy["bytes"]), (&json!("greedy"), &json!(16_384), &json!(31), &json!(1), &json!(500_000))); assert_eq!(greedy["sample"][2], json!({"index": 30, "group": 0, "object": 30, "len": 8480, "offset_us": null})); let video = probe("d2000-f30-g30-k5-r3000000"); assert_eq!((&video["s"], &video["key_object_bytes"], &video["objects"], &video["groups"]), (&json!(11_029), &json!(55_145), &json!(60), &json!(2))); let row = |outcome: &str| cases(&doc, "errors").iter().find(|r| r["outcome"] == outcome).unwrap_or_else(|| panic!("{outcome}")).clone(); let codes = |outcome: &str| (row(outcome)["downlink"]["name"].as_str().map(str::to_string), row(outcome)["uplink"]["name"].as_str().map(str::to_string)); let named = |d: &str, u: &str| (Some(d.to_string()), Some(u.to_string())); assert_eq!(codes("invalid"), named("DOES_NOT_EXIST", "UNINTERESTED")); assert_eq!(codes("forward_state"), (None, Some("UNINTERESTED".to_string()))); assert_eq!(codes("per_probe_limit"), named("DOES_NOT_EXIST", "UNINTERESTED")); assert_eq!(codes("load_limit"), named("EXCESSIVE_LOAD", "EXCESSIVE_LOAD")); assert_eq!(codes("unauthorized"), named("UNAUTHORIZED", "UNAUTHORIZED")); assert_eq!(codes("other_request"), named("NOT_SUPPORTED", "NOT_SUPPORTED")); // Table 5. assert_eq!(doc["limits"]["max_duration_ms"], 5000); assert_eq!(doc["limits"]["max_bytes"], 10_000_000); assert_eq!(doc["limits"]["max_rate_bps"], 50_000_000); assert_eq!(doc["limits"]["max_object_bytes"], 1_048_576); assert_eq!((&doc["limits"]["session_concurrent"], &doc["limits"]["min_gap_ms"]), (&json!(1), &json!(1000))); assert_eq!((&doc["limits"]["session_bytes"], &doc["limits"]["session_window_ms"]), (&json!(50_000_000), &json!(60_000))); // Every object of every schedule is at the lowest priority with one property, the Timestamp. for s in cases(&doc, "schedules") { for o in s["sent"].as_array().expect("sent") { assert_eq!(o["priority"], 255); assert!(o["properties"].as_str().is_some_and(|p| p.starts_with("10")), "{}", s["what"]); } } } /// The planted faults: a corpus one value away from the committed one is caught, whichever /// side of a case the value is on. #[test] fn a_changed_corpus_is_caught() { type Edit = (&'static str, fn(&mut Value)); let (_, doc) = committed("switching"); let edits: [Edit; 8] = [ ("a Frame Number", |d| d["frames"][0]["frame"] = json!(27)), ("a target group", |d| d["switch_points"][2]["target_group"] = json!(15)), ("the track chosen", |d| d["selections"][0]["chosen"] = json!("1080p-short")), ("an End Group", |d| d["plans"][0]["end_group"] = json!(9)), ("a frame presented twice", |d| { let again = d["handoffs"][0]["released"][3].clone(); d["handoffs"][0]["released"].as_array_mut().expect("released").insert(4, again); }), ("a spacing made a multiple", |d| d["faults"][0]["catalog"]["tracks"][1]["keyframeSpacing"] = json!(8)), ("a mid-group start moved to its Group's first object", |d| { let at = d["faults"].as_array().expect("faults").iter().position(|f| f["reason"] == "switch_start_mid_group").expect("the fault"); d["faults"][at]["at"][1] = json!(0); }), ("one reason for another", |d| { let at = d["faults"].as_array().expect("faults").iter().position(|f| f["reason"] == "switch_start_not_switch_point").expect("the fault"); d["faults"][at]["reason"] = json!("switch_start_mid_group"); }), ]; for (what, edit) in edits { let mut changed = doc.clone(); edit(&mut changed); assert!(!switching::corpus::verify(&changed).is_empty(), "a change went unnoticed: {what}"); } let (_, doc) = committed("probe"); let edits: [Edit; 8] = [ ("an object size", |d| d["probes"][0]["s"] = json!(751)), ("an object count", |d| d["probes"][1]["objects"] = json!(30)), ("the cut object's length", |d| d["probes"][1]["sample"][2]["len"] = json!(16_384)), ("a payload that is not zeros", |d| d["schedules"][0]["sent"][1]["payload_sha256"] = json!("00")), ("an error code", |d| d["errors"][0]["downlink"]["code"] = json!(0x20)), ("a limit in the catalog", |d| d["catalogs"][0]["catalog"]["maxRate"] = json!(1)), ("a refused request made acceptable", |d| { let at = d["faults"].as_array().expect("faults").iter().position(|f| f["reason"] == "probe_duration_over_limit").expect("the fault"); d["faults"][at]["name"] = json!("d5000-r300000"); }), ("a Retry Interval", |d| { let at = d["faults"].as_array().expect("faults").iter().position(|f| f["reason"] == "probe_session_gap").expect("the fault"); d["faults"][at]["retry_interval"] = json!(0); }), ]; for (what, edit) in edits { let mut changed = doc.clone(); edit(&mut changed); assert!(!probe::corpus::verify(&changed).is_empty(), "a change went unnoticed: {what}"); } }