use ::quickcheck::{Arbitrary, Gen};
fn drive<F: FnMut(&mut Gen)>(size: usize, rounds: usize, mut body: F) {
let mut g = Gen::new(size);
for _ in 0..rounds {
body(&mut g);
}
}
#[test]
fn geo_location_invariant_lat_lon_in_range() {
drive(64, 256, |g| {
let geo = crate::capture::GeoLocation::arbitrary(g);
assert!(
(-90.0..=90.0).contains(&geo.lat()),
"lat out of range: {}",
geo.lat()
);
assert!(
(-180.0..=180.0).contains(&geo.lon()),
"lon out of range: {}",
geo.lon()
);
if let Some(alt) = geo.altitude() {
assert!(
alt.is_finite(),
"altitude must be finite when Some, got {alt}"
);
}
});
}
#[test]
fn fingerprint_invariant_algorithm_non_empty() {
drive(64, 256, |g| {
let fp = crate::audio::Fingerprint::arbitrary(g);
assert!(!fp.algorithm().is_empty(), "algorithm must be non-empty");
});
}
#[test]
fn cover_art_invariant_mime_and_data_non_empty() {
drive(64, 256, |g| {
let c = crate::audio::CoverArt::arbitrary(g);
assert!(!c.mime().is_empty(), "mime must be non-empty");
assert!(!c.data().is_empty(), "data must be non-empty");
});
}
#[test]
fn smoke_yields_values_for_representative_types() {
drive(64, 64, |g| {
let _ = crate::codec::VideoCodec::arbitrary(g);
let _ = crate::color::Info::arbitrary(g);
let _ = crate::frame::FrameRate::arbitrary(g);
let _ = crate::lang::Language::arbitrary(g);
let _ = crate::disposition::TrackDisposition::arbitrary(g);
let _ = crate::audio::Tags::arbitrary(g);
});
}
#[test]
fn reachability_small_coded_enums_hit_all_named() {
use ::std::collections::HashSet;
let mut br: HashSet<crate::audio::BitRateMode> = HashSet::new();
drive(64, 2048, |g| {
br.insert(crate::audio::BitRateMode::arbitrary(g));
});
assert_eq!(br.len(), 3, "BitRateMode coverage: {br:?}");
}
#[test]
fn reachability_track_origin_hits_named_and_escape() {
use crate::subtitle::TrackOrigin;
use ::std::collections::HashSet;
let mut named: HashSet<TrackOrigin> = HashSet::new();
let mut saw_other = false;
drive(64, 2048, |g| match TrackOrigin::arbitrary(g) {
TrackOrigin::Other(_) => saw_other = true,
other => {
named.insert(other);
}
});
assert_eq!(named.len(), 4, "TrackOrigin named coverage: {named:?}");
assert!(saw_other, "TrackOrigin `Other` arm never generated");
}
#[test]
fn reachability_rotation_hits_named_and_escape() {
use crate::frame::Rotation;
let mut saw_named = false;
let mut saw_other = false;
drive(64, 2048, |g| match Rotation::arbitrary(g) {
Rotation::Other(_) => saw_other = true,
_ => saw_named = true,
});
assert!(
saw_named && saw_other,
"Rotation missing arms: named={saw_named} other={saw_other}"
);
}
#[test]
fn reachability_sample_format_all_named_plus_arms() {
use crate::audio::SampleFormat;
use ::std::collections::HashSet;
let mut named: HashSet<::std::string::String> = HashSet::new();
let mut saw_other = false;
drive(64, 4096, |g| match SampleFormat::arbitrary(g) {
SampleFormat::Other(_) => saw_other = true,
other => {
named.insert(other.as_str().to_string());
}
});
assert_eq!(
named.len(),
12,
"missing named SampleFormat variants; observed: {named:?}"
);
assert!(saw_other, "SampleFormat: never observed `Other(_)`");
}
#[test]
fn reachability_range_weighted_enums_hit_named_codes() {
use ::std::collections::HashSet;
let mut matrix: HashSet<u32> = HashSet::new();
let mut primaries: HashSet<u32> = HashSet::new();
let mut transfer: HashSet<u32> = HashSet::new();
let mut pixel: HashSet<u32> = HashSet::new();
drive(64, 8192, |g| {
matrix.extend(crate::color::Matrix::arbitrary(g).to_u32());
primaries.extend(crate::color::Primaries::arbitrary(g).to_u32());
transfer.extend(crate::color::Transfer::arbitrary(g).to_u32());
pixel.extend(crate::pixel_format::PixelFormat::arbitrary(g).to_u32());
});
let in_range = |s: &HashSet<u32>, max: u32| s.iter().filter(|&&c| c <= max).count();
assert!(
in_range(&matrix, 17) >= 3,
"Matrix named-range coverage too low: {matrix:?}"
);
assert!(
matrix.contains(&crate::color::DOMAIN_EXT_BASE),
"Matrix::Bt601 (DOMAIN_EXT_BASE) never generated"
);
assert!(
in_range(&primaries, 22) >= 3,
"Primaries named-range coverage too low: {primaries:?}"
);
assert!(
in_range(&transfer, 18) >= 3,
"Transfer named-range coverage too low: {transfer:?}"
);
assert!(
in_range(&pixel, 947) >= 3,
"PixelFormat named-range coverage too low: {} distinct",
in_range(&pixel, 947)
);
}
#[test]
fn generated_vocabulary_values_round_trip_through_their_name() {
drive(64, 128, |g| {
macro_rules! rt {
($ty:path) => {{
let v = <$ty>::arbitrary(g);
assert_eq!(v.as_str().parse::<$ty>(), Ok(v.clone()), "{v:?}");
}};
}
rt!(crate::color::Matrix);
rt!(crate::pixel_format::PixelFormat);
rt!(crate::frame::Rotation);
let d = crate::disposition::TrackDisposition::arbitrary(g);
assert_eq!(
crate::disposition::TrackDisposition::from_u32(d.to_u32()),
d
);
});
}
#[test]
fn reachability_tags_language_hits_none_and_some() {
let mut saw_none = false;
let mut saw_some = false;
drive(64, 1024, |g| {
match crate::audio::Tags::arbitrary(g).language() {
None => saw_none = true,
Some(_) => saw_some = true,
}
});
assert!(saw_none, "Tags.language never generated `None`");
assert!(saw_some, "Tags.language never generated `Some(_)`");
}
#[cfg(feature = "serde")]
#[test]
fn arbitrary_values_survive_serde_round_trip() {
drive(64, 4096, |g| {
let sf = crate::audio::SampleFormat::arbitrary(g);
let json = serde_json::to_string(&sf).unwrap();
let back: crate::audio::SampleFormat = serde_json::from_str(&json).unwrap();
assert_eq!(back, sf, "SampleFormat lost identity via serde: {json}");
let vc = crate::codec::VideoCodec::arbitrary(g);
let json = serde_json::to_string(&vc).unwrap();
let back: crate::codec::VideoCodec = serde_json::from_str(&json).unwrap();
assert_eq!(back, vc, "VideoCodec lost identity via serde: {json}");
let ld = crate::audio::Loudness::arbitrary(g);
let json = serde_json::to_string(&ld).unwrap();
let back: crate::audio::Loudness = serde_json::from_str(&json).unwrap();
assert_eq!(back, ld, "Loudness lost identity via serde: {json}");
#[cfg(feature = "bayer")]
{
let wb = crate::frame::WhiteBalance::arbitrary(g);
let json = serde_json::to_string(&wb).unwrap();
let back: crate::frame::WhiteBalance = serde_json::from_str(&json).unwrap();
assert_eq!(back, wb, "WhiteBalance lost identity via serde: {json}");
let ccm = crate::frame::ColorCorrectionMatrix::arbitrary(g);
let json = serde_json::to_string(&ccm).unwrap();
let back: crate::frame::ColorCorrectionMatrix = serde_json::from_str(&json).unwrap();
assert_eq!(
back, ccm,
"ColorCorrectionMatrix lost identity via serde: {json}"
);
let bp = crate::frame::BayerPattern::arbitrary(g);
let json = serde_json::to_string(&bp).unwrap();
let back: crate::frame::BayerPattern = serde_json::from_str(&json).unwrap();
assert_eq!(back, bp, "BayerPattern lost identity via serde: {json}");
}
});
}
#[test]
fn parse_is_idempotent_through_the_text_form() {
macro_rules! idempotent {
($ty:path, $s:expr) => {{
let once: $ty = $s.parse().unwrap();
let twice: $ty = once.as_str().parse().unwrap();
assert_eq!(
once,
twice,
"{} renders {:?}, which does not parse back to it",
stringify!($ty),
once.as_str()
);
}};
}
drive(1234, 2048, |g| {
let s = super::arb_string(g);
idempotent!(crate::codec::VideoCodec, s);
idempotent!(crate::codec::AudioCodec, s);
idempotent!(crate::codec::SubtitleCodec, s);
idempotent!(crate::container::Format, s);
idempotent!(crate::subtitle::Format, s);
idempotent!(crate::audio::ChannelLayout, s);
idempotent!(crate::audio::SampleFormat, s);
idempotent!(crate::audio::ContainerFormat, s);
idempotent!(crate::color::Matrix, s);
idempotent!(crate::color::Primaries, s);
idempotent!(crate::color::Transfer, s);
idempotent!(crate::color::DynamicRange, s);
idempotent!(crate::color::ChromaLocation, s);
idempotent!(crate::color::DcpTargetGamut, s);
idempotent!(crate::pixel_format::PixelFormat, s);
idempotent!(crate::frame::Rotation, s);
idempotent!(crate::frame::FieldOrder, s);
idempotent!(crate::frame::StereoMode, s);
});
}
#[test]
fn one_name_is_one_value_whatever_its_case() {
macro_rules! folds {
($ty:path, $s:expr) => {{
let plain: $ty = $s.parse().unwrap();
let shouted: $ty = $s.to_ascii_uppercase().parse().unwrap();
assert_eq!(plain, shouted, "{} split a name by case", stringify!($ty));
}};
}
drive(4321, 2048, |g| {
let s = super::arb_string(g);
folds!(crate::codec::VideoCodec, s);
folds!(crate::container::Format, s);
folds!(crate::audio::SampleFormat, s);
folds!(crate::color::Matrix, s);
folds!(crate::pixel_format::PixelFormat, s);
folds!(crate::frame::StereoMode, s);
});
}
#[test]
fn reachability_channel_layout_reaches_both_five_point_pairs() {
use crate::audio::ChannelLayout;
use ::std::collections::HashSet;
let mut seen: HashSet<ChannelLayout> = HashSet::new();
drive(64, 4096, |g| {
seen.insert(ChannelLayout::arbitrary(g));
});
for wanted in [
ChannelLayout::N5Point0,
ChannelLayout::N5Point0Back,
ChannelLayout::N5Point1,
ChannelLayout::N5Point1Back,
] {
assert!(
seen.contains(&wanted),
"{wanted:?} ({:?}) is unreachable from the curated ChannelLayout seeds",
wanted.as_str()
);
}
}