use super::*;
use crate::dynamic_hdr::hdr10plus::{
ActualPeakLuminance, BezierAnchors, DistributionMaxrgb, Hdr10PlusWindow, Hdr10PlusWindows,
KneePoint,
};
#[cfg(any(feature = "alloc", feature = "std"))]
use crate::dynamic_hdr::slhdr::SlHdrExtension;
use crate::dynamic_hdr::slhdr::{
SlHdrBody, SlHdrMdcvInfo, SlHdrMetadata, SlHdrMode0, SlHdrMode1, SlHdrPayload,
SlHdrPictureInfo, SlHdrTable15, SlHdrTable127,
};
#[cfg(any(feature = "alloc", feature = "std"))]
use alloc::vec;
#[test]
fn bit_reader_single_byte_full() {
let mut r = BitReader::new(&[0b1010_1010]);
assert_eq!(r.read_u8(8).unwrap(), 0b1010_1010);
assert_eq!(r.remaining_bits(), 0);
}
#[test]
fn bit_reader_msb_first_ordering() {
let mut r = BitReader::new(&[0b1100_0011]);
assert_eq!(r.read_u8(4).unwrap(), 0b1100);
assert_eq!(r.read_u8(4).unwrap(), 0b0011);
}
#[test]
fn bit_reader_spans_byte_boundary() {
let mut r = BitReader::new(&[0b10100000, 0b11111000]);
let _ = r.read_u8(5).unwrap(); assert_eq!(r.read_u8(8).unwrap(), 0b000_11111);
}
#[test]
fn bit_reader_read_bool() {
let mut r = BitReader::new(&[0b1000_0000]);
assert!(r.read_bool().unwrap());
assert!(!r.read_bool().unwrap());
}
#[test]
fn bit_reader_read_u32_wide() {
let data = 0xDEAD_BEEFu32.to_be_bytes();
let mut r = BitReader::new(&data);
assert_eq!(r.read_u32(32).unwrap(), 0xDEAD_BEEF);
}
#[test]
fn bit_reader_short_read_is_error() {
let mut r = BitReader::new(&[0xFFu8]);
let _ = r.read_u8(8).unwrap();
assert!(matches!(r.read_u8(1), Err(DecodeError::MalformedPayload)));
}
#[test]
fn bit_writer_single_byte_full() {
let mut w = BitWriter::new();
w.write_u8(0b1010_1010, 8);
let (buf, len) = w.finish();
assert_eq!(len, 1);
assert_eq!(buf[0], 0b1010_1010);
}
#[test]
fn bit_writer_msb_first_ordering() {
let mut w = BitWriter::new();
w.write_u8(0b1100, 4);
w.write_u8(0b0011, 4);
let (buf, len) = w.finish();
assert_eq!(len, 1);
assert_eq!(buf[0], 0b1100_0011);
}
#[test]
fn bit_writer_spans_byte_boundary() {
let mut w = BitWriter::new();
w.write_u8(0b10111, 5); w.write_u8(0b110, 3); w.write_u8(0b01010101, 8); let (buf, len) = w.finish();
assert_eq!(len, 2);
assert_eq!(buf[0], 0b10111_110);
assert_eq!(buf[1], 0b01010101);
}
#[test]
fn bit_writer_write_bool() {
let mut w = BitWriter::new();
w.write_bool(true);
w.write_bool(false);
w.write_bool(true);
let (buf, len) = w.finish();
assert_eq!(len, 1);
assert_eq!(buf[0], 0b1010_0000);
}
#[test]
fn bit_writer_round_trip_with_reader() {
let mut w = BitWriter::new();
w.write_u32(0xDEAD_BEEF, 32);
w.write_u8(0b101, 3);
w.write_u16(0x1234, 13);
let (buf, len) = w.finish();
let mut r = BitReader::new(&buf[..len]);
assert_eq!(r.read_u32(32).unwrap(), 0xDEAD_BEEF);
assert_eq!(r.read_u8(3).unwrap(), 0b101);
assert_eq!(r.read_u16(13).unwrap(), 0x1234);
assert_eq!(r.remaining_bits(), 0);
}
#[test]
fn bit_writer_partial_final_byte() {
let mut w = BitWriter::new();
w.write_u16(0b1_1111_1111, 9);
let (buf, len) = w.finish();
assert_eq!(len, 2);
assert_eq!(buf[0], 0b1111_1111);
assert_eq!(buf[1] >> 7, 1); }
#[test]
fn bit_reader_remaining_bits() {
let mut r = BitReader::new(&[0xFF, 0xFF]);
assert_eq!(r.remaining_bits(), 16);
let _ = r.read_u8(3).unwrap();
assert_eq!(r.remaining_bits(), 13);
}
fn make_packet(seq_num: u8, total_bytes: u16, format_id: u8, chunk: &[u8]) -> [u8; 31] {
let chunk_len = chunk.len().min(23) as u8;
let length = 4 + chunk_len;
let mut packet = [0u8; 31];
packet[0] = 0x20; packet[1] = 0x01; packet[2] = length;
packet[4] = seq_num;
packet[5] = (total_bytes & 0xFF) as u8;
packet[6] = (total_bytes >> 8) as u8;
packet[7] = format_id;
packet[8..8 + chunk_len as usize].copy_from_slice(&chunk[..chunk_len as usize]);
let sum: u8 = packet.iter().fold(0u8, |a, &b| a.wrapping_add(b));
packet[3] = packet[3].wrapping_sub(sum);
packet
}
#[test]
fn round_trip_fields() {
let chunk_data: [u8; 23] = core::array::from_fn(|i| i as u8);
let packet = make_packet(3, 0x0142, 0x04, &chunk_data);
let decoded = DynamicHdrFragment::decode(&packet).unwrap();
assert!(decoded.iter_warnings().next().is_none());
assert_eq!(decoded.value.seq_num, 3);
assert_eq!(decoded.value.total_bytes, 0x0142);
assert_eq!(decoded.value.format_id, 0x04);
assert_eq!(decoded.value.chunk_len, 23);
assert_eq!(&decoded.value.chunk[..23], &chunk_data);
}
#[test]
fn partial_chunk_last_packet() {
let chunk_data = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE];
let packet = make_packet(1, 28, 0x04, &chunk_data);
let decoded = DynamicHdrFragment::decode(&packet).unwrap();
assert_eq!(decoded.value.chunk_len, 5);
assert_eq!(&decoded.value.chunk[..5], &chunk_data);
}
#[test]
fn checksum_mismatch_warning() {
let mut packet = make_packet(0, 23, 0xFF, &[0u8; 23]);
packet[3] = packet[3].wrapping_add(1); let decoded = DynamicHdrFragment::decode(&packet).unwrap();
assert!(
decoded
.iter_warnings()
.any(|w| matches!(w, DynamicHdrWarning::ChecksumMismatch { .. }))
);
}
#[test]
fn truncated_length_is_error() {
let mut packet = make_packet(0, 0, 0x00, &[]);
packet[2] = 28; assert!(matches!(
DynamicHdrFragment::decode(&packet),
Err(DecodeError::Truncated { claimed: 28 })
));
}
#[test]
fn zero_length_payload() {
let mut packet = [0u8; 31];
packet[0] = 0x20;
packet[1] = 0x01;
packet[2] = 0; let sum: u8 = packet.iter().fold(0u8, |a, &b| a.wrapping_add(b));
packet[3] = packet[3].wrapping_sub(sum);
let decoded = DynamicHdrFragment::decode(&packet).unwrap();
assert!(decoded.iter_warnings().next().is_none());
assert_eq!(decoded.value.chunk_len, 0);
}
#[test]
fn decode_sequence_empty_returns_error() {
assert!(matches!(
DynamicHdrInfoFrame::decode_sequence(&[]),
Err(DecodeError::EmptySequence)
));
}
#[test]
fn decode_sequence_single_packet_unknown_format() {
let packet = make_packet(0, 23, 0xFF, &[0xAAu8; 23]);
let decoded = DynamicHdrInfoFrame::decode_sequence(&[packet]).unwrap();
assert!(decoded.iter_warnings().next().is_none());
assert_eq!(
decoded.value,
DynamicHdrInfoFrame::Unknown {
format_id: 0xFF,
#[cfg(any(feature = "alloc", feature = "std"))]
payload: vec![0xAAu8; 23],
}
);
}
#[test]
fn decode_sequence_multi_packet_payload_assembled() {
let p0 = make_packet(0, 46, 0xFF, &[0xAAu8; 23]);
let p1 = make_packet(1, 46, 0xFF, &[0xBBu8; 23]);
let decoded = DynamicHdrInfoFrame::decode_sequence(&[p0, p1]).unwrap();
assert!(decoded.iter_warnings().next().is_none());
#[cfg(any(feature = "alloc", feature = "std"))]
{
let expected: alloc::vec::Vec<u8> = [0xAAu8; 23]
.iter()
.chain([0xBBu8; 23].iter())
.copied()
.collect();
assert_eq!(
decoded.value,
DynamicHdrInfoFrame::Unknown {
format_id: 0xFF,
payload: expected,
}
);
}
}
#[test]
fn decode_sequence_checksum_mismatch_warning() {
let mut p0 = make_packet(0, 23, 0xFF, &[0u8; 23]);
p0[3] = p0[3].wrapping_add(1); let decoded = DynamicHdrInfoFrame::decode_sequence(&[p0]).unwrap();
assert!(
decoded
.iter_warnings()
.any(|w| matches!(w, DynamicHdrWarning::ChecksumMismatch { .. }))
);
}
#[test]
fn decode_sequence_truncated_returns_error() {
let mut p0 = make_packet(0, 23, 0xFF, &[0u8; 23]);
p0[2] = 28; assert!(matches!(
DynamicHdrInfoFrame::decode_sequence(&[p0]),
Err(DecodeError::Truncated { claimed: 28 })
));
}
#[test]
fn decode_sequence_out_of_order_seq_num_warning() {
let mut p0 = make_packet(0, 23, 0xFF, &[0u8; 23]);
p0[4] = 5;
let sum: u8 = p0.iter().fold(0u8, |a, &b| a.wrapping_add(b));
p0[3] = p0[3].wrapping_sub(sum);
let decoded = DynamicHdrInfoFrame::decode_sequence(&[p0]).unwrap();
assert!(decoded.iter_warnings().any(|w| matches!(
w,
DynamicHdrWarning::OutOfOrderPacket { index: 0, found: 5 }
)));
}
#[test]
fn decode_sequence_inconsistent_total_bytes_warning() {
let p0 = make_packet(0, 46, 0xFF, &[0u8; 23]);
let p1 = make_packet(1, 99, 0xFF, &[0u8; 23]);
let decoded = DynamicHdrInfoFrame::decode_sequence(&[p0, p1]).unwrap();
assert!(decoded.iter_warnings().any(|w| matches!(
w,
DynamicHdrWarning::InconsistentTotalBytes {
packet: 1,
expected: 46,
found: 99,
}
)));
}
#[test]
fn decode_sequence_inconsistent_format_id_warning() {
let p0 = make_packet(0, 46, 0xFF, &[0u8; 23]);
let p1 = make_packet(1, 46, 0xFE, &[0u8; 23]);
let decoded = DynamicHdrInfoFrame::decode_sequence(&[p0, p1]).unwrap();
assert!(decoded.iter_warnings().any(|w| matches!(
w,
DynamicHdrWarning::InconsistentFormatId {
packet: 1,
expected: 0xFF,
found: 0xFE,
}
)));
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn decode_sequence_unknown_payload_roundtrip() {
use crate::encode::IntoPackets;
let original_payload: alloc::vec::Vec<u8> = (0u8..50).collect();
let frame = DynamicHdrInfoFrame::Unknown {
format_id: 0x07,
payload: original_payload.clone(),
};
let encoded = frame.into_packets();
assert!(encoded.iter_warnings().next().is_none());
let packets: alloc::vec::Vec<[u8; 31]> = encoded.value.collect();
assert_eq!(packets.len(), 3);
let decoded = DynamicHdrInfoFrame::decode_sequence(&packets).unwrap();
assert!(decoded.iter_warnings().next().is_none());
assert_eq!(
decoded.value,
DynamicHdrInfoFrame::Unknown {
format_id: 0x07,
payload: original_payload,
}
);
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn decode_sequence_hdr10plus_inner_error_propagates() {
let pkt = make_packet(0, 1, 0x04, &[0x01]);
let result = DynamicHdrInfoFrame::decode_sequence(&[pkt]);
assert!(
matches!(result, Err(DecodeError::MalformedPayload)),
"expected MalformedPayload, got {result:?}"
);
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn decode_sequence_slhdr_inner_error_propagates() {
let pkt = make_packet(0, 1, 0x02, &[0xB5]);
let result = DynamicHdrInfoFrame::decode_sequence(&[pkt]);
assert!(
matches!(result, Err(DecodeError::MalformedPayload)),
"expected MalformedPayload, got {result:?}"
);
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn decode_sequence_format_warnings_forwarded() {
let (mut payload, _) = make_minimal_hdr10plus_payload();
payload[2] |= 0b1100_0000; let total = payload.len() as u16;
let pkts: alloc::vec::Vec<[u8; 31]> = payload
.chunks(23)
.enumerate()
.map(|(i, chunk)| make_packet(i as u8, total, 0x04, chunk))
.collect();
let decoded = DynamicHdrInfoFrame::decode_sequence(&pkts).unwrap();
assert!(
decoded
.iter_warnings()
.any(|w| matches!(w, DynamicHdrWarning::ReservedFieldNonZero { .. })),
"expected ReservedFieldNonZero warning to be forwarded"
);
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn into_packets_seq_nums_sequential() {
use crate::encode::IntoPackets;
let frame = DynamicHdrInfoFrame::Unknown {
format_id: 0x04,
payload: alloc::vec![0u8; 50],
};
for (expected_seq, packet) in frame.into_packets().value.enumerate() {
assert_eq!(packet[4], expected_seq as u8);
}
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn into_packets_total_bytes_consistent() {
use crate::encode::IntoPackets;
let payload_len: u16 = 50;
let frame = DynamicHdrInfoFrame::Unknown {
format_id: 0x04,
payload: alloc::vec![0u8; payload_len as usize],
};
for packet in frame.into_packets().value {
let tb = u16::from_le_bytes([packet[5], packet[6]]);
assert_eq!(tb, payload_len);
}
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn into_packets_all_checksums_valid() {
use crate::encode::IntoPackets;
let frame = DynamicHdrInfoFrame::Unknown {
format_id: 0x04,
payload: alloc::vec![0xABu8; 50],
};
for packet in frame.into_packets().value {
let sum: u8 = packet.iter().fold(0u8, |a, &b| a.wrapping_add(b));
assert_eq!(sum, 0, "packet checksum must make all-bytes sum equal 0");
}
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn into_packets_final_partial_chunk() {
use crate::encode::IntoPackets;
let frame = DynamicHdrInfoFrame::Unknown {
format_id: 0x04,
payload: alloc::vec![0xFFu8; 24],
};
let packets: alloc::vec::Vec<[u8; 31]> = frame.into_packets().value.collect();
assert_eq!(packets.len(), 2);
assert_eq!(packets[0][2], 4 + 23);
assert_eq!(packets[1][2], 4 + 1);
for &b in &packets[1][9..] {
assert_eq!(b, 0);
}
}
#[test]
fn into_packets_empty_payload_yields_no_packets() {
use crate::encode::IntoPackets;
let frame = DynamicHdrInfoFrame::Unknown {
format_id: 0x04,
#[cfg(any(feature = "alloc", feature = "std"))]
payload: alloc::vec![],
};
assert!(frame.into_packets().value.next().is_none());
}
#[cfg(any(feature = "alloc", feature = "std"))]
fn make_full_hdr10plus_payload() -> (alloc::vec::Vec<u8>, Hdr10PlusMetadata) {
let mut w = BitWriter::new();
w.write_u8(0x02, 8); w.write_u8(0x01, 8); w.write_bool(true); w.write_u8(0, 2); w.write_u32(2000, 27); w.write_bool(true);
w.write_u8(2, 5); w.write_u8(2, 5); w.write_u8(3, 4); w.write_u8(5, 4); w.write_u8(7, 4); w.write_u8(9, 4); w.write_u8(0, 2);
w.write_u16(10, 16); w.write_u16(20, 16); w.write_u16(200, 16); w.write_u16(300, 16); w.write_u16(100, 16); w.write_u16(150, 16); w.write_u8(45, 8); w.write_u16(50, 16); w.write_u16(60, 16); w.write_u16(30, 16); w.write_bool(true); w.write_u32(1000, 17); w.write_u32(2000, 17); w.write_u32(3000, 17); w.write_u32(500, 17); w.write_u8(2, 4);
w.write_u8(25, 7); w.write_u32(100, 17); w.write_u8(75, 7); w.write_u32(800, 17); w.write_u16(512, 10); w.write_bool(true);
w.write_u8(1, 5); w.write_u8(1, 5); w.write_u8(12, 4); w.write_bool(true);
w.write_u16(100, 12); w.write_u16(200, 12); w.write_u8(2, 4); w.write_u16(300, 10); w.write_u16(400, 10); w.write_bool(true);
w.write_u8(42, 6); let (buf, len) = w.finish();
let payload = buf[..len].to_vec();
let mut tgt_entries = [[0u8; 25]; 25];
tgt_entries[0][0] = 3;
tgt_entries[0][1] = 5;
tgt_entries[1][0] = 7;
tgt_entries[1][1] = 9;
let mut mast_entries = [[0u8; 25]; 25];
mast_entries[0][0] = 12;
let mut dist = DistributionMaxrgb {
count: 2,
..Default::default()
};
dist.percentages[0] = 25;
dist.percentiles[0] = 100;
dist.percentages[1] = 75;
dist.percentiles[1] = 800;
let mut bezier = BezierAnchors {
count: 2,
..Default::default()
};
bezier.anchors[0] = 300;
bezier.anchors[1] = 400;
let expected = Hdr10PlusMetadata {
application_identifier: 0x02,
application_mode: 0x01,
scene_frame_switching_flag: true,
targeted_system_display_maximum_luminance: 2000,
targeted_system_display_actual_peak_luminance_flag: true,
targeted_system_display_actual_peak_luminance: Some(ActualPeakLuminance {
num_rows: 2,
num_cols: 2,
entries: tgt_entries,
}),
windows: Hdr10PlusWindows {
count: 1,
windows: [
Hdr10PlusWindow {
upper_left_corner_x: 10,
upper_left_corner_y: 20,
lower_right_corner_x: 200,
lower_right_corner_y: 300,
center_of_ellipse_x: 100,
center_of_ellipse_y: 150,
rotation_angle: 45,
semimajor_axis_internal_ellipse: 50,
semimajor_axis_external_ellipse: 60,
semiminor_axis_external_ellipse: 30,
overlap_process_option: true,
maxscl: [1000, 2000, 3000],
average_maxrgb: 500,
distribution_maxrgb: dist,
fraction_bright_pixels: 512,
tone_mapping_flag: true,
knee_point: Some(KneePoint { x: 100, y: 200 }),
bezier_curve_anchors: bezier,
},
Hdr10PlusWindow::default(),
Hdr10PlusWindow::default(),
],
},
mastering_display_actual_peak_luminance_flag: true,
mastering_display_actual_peak_luminance: Some(ActualPeakLuminance {
num_rows: 1,
num_cols: 1,
entries: mast_entries,
}),
color_saturation_mapping_flag: true,
color_saturation_weight: Some(42),
};
(payload, expected)
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn hdr10plus_full_fields_decode() {
let (payload, expected) = make_full_hdr10plus_payload();
let mut warnings = alloc::vec::Vec::new();
let got = Hdr10PlusMetadata::decode(&payload, &mut |w| warnings.push(w)).unwrap();
assert!(warnings.is_empty());
assert_eq!(got, expected);
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn hdr10plus_full_fields_round_trip() {
use crate::encode::IntoPackets;
let (_, original) = make_full_hdr10plus_payload();
let frame = DynamicHdrInfoFrame::Hdr10Plus(original.clone());
let pkts: alloc::vec::Vec<[u8; 31]> = frame.into_packets().value.collect();
let decoded = DynamicHdrInfoFrame::decode_sequence(&pkts).unwrap();
match decoded.value {
DynamicHdrInfoFrame::Hdr10Plus(meta) => assert_eq!(meta, original),
other => panic!("expected Hdr10Plus, got {other:?}"),
}
}
#[cfg(any(feature = "alloc", feature = "std"))]
fn make_minimal_hdr10plus_payload() -> (alloc::vec::Vec<u8>, Hdr10PlusMetadata) {
let mut w = BitWriter::new();
w.write_u8(0x01, 8); w.write_u8(0x00, 8); w.write_u8(0, 2); w.write_u32(1000, 27); w.write_bool(false); w.write_u8(0, 2); for _ in 0..6 {
w.write_u16(0, 16);
} w.write_u8(0, 8); for _ in 0..3 {
w.write_u16(0, 16);
} w.write_bool(false); for _ in 0..3 {
w.write_u32(0, 17);
} w.write_u32(0, 17); w.write_u8(0, 4); w.write_u16(0, 10); w.write_bool(false); w.write_bool(false); w.write_bool(false);
let (buf, len) = w.finish();
let payload = buf[..len].to_vec();
let expected = Hdr10PlusMetadata {
application_identifier: 0x01,
application_mode: 0x00,
scene_frame_switching_flag: false,
targeted_system_display_maximum_luminance: 1000,
targeted_system_display_actual_peak_luminance_flag: false,
targeted_system_display_actual_peak_luminance: None,
windows: Hdr10PlusWindows {
count: 1,
windows: [
Hdr10PlusWindow::default(),
Hdr10PlusWindow::default(),
Hdr10PlusWindow::default(),
],
},
mastering_display_actual_peak_luminance_flag: false,
mastering_display_actual_peak_luminance: None,
color_saturation_mapping_flag: false,
color_saturation_weight: None,
};
(payload, expected)
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn hdr10plus_single_window_no_optional_fields() {
let (payload, expected) = make_minimal_hdr10plus_payload();
let mut warnings = alloc::vec::Vec::new();
let got = Hdr10PlusMetadata::decode(&payload, &mut |w| warnings.push(w)).unwrap();
assert!(warnings.is_empty());
assert_eq!(got, expected);
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn hdr10plus_malformed_short_payload_is_error() {
assert!(matches!(
Hdr10PlusMetadata::decode(&[], &mut |_| {}),
Err(DecodeError::MalformedPayload)
));
assert!(matches!(
Hdr10PlusMetadata::decode(&[0x01, 0x00], &mut |_| {}),
Err(DecodeError::MalformedPayload)
));
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn hdr10plus_too_many_bezier_anchors_is_malformed() {
let mut w = BitWriter::new();
w.write_u8(0x01, 8); w.write_u8(0x00, 8); w.write_u8(0, 2); w.write_u32(1000, 27); w.write_bool(false); w.write_u8(0, 2); for _ in 0..6 {
w.write_u16(0, 16);
} w.write_u8(0, 8); for _ in 0..3 {
w.write_u16(0, 16);
} w.write_bool(false); for _ in 0..3 {
w.write_u32(0, 17);
} w.write_u32(0, 17); w.write_u8(0, 4); w.write_u16(0, 10); w.write_bool(false); w.write_bool(true); w.write_u16(0, 12); w.write_u16(0, 12); w.write_u8(10, 4); let (buf, len) = w.finish();
assert!(matches!(
Hdr10PlusMetadata::decode(&buf[..len], &mut |_| {}),
Err(DecodeError::MalformedPayload)
));
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn hdr10plus_four_windows_is_malformed() {
let mut w = BitWriter::new();
w.write_u8(0x01, 8); w.write_u8(0x00, 8); w.write_u8(0, 2); w.write_u32(1000, 27); w.write_bool(false); w.write_u8(3, 2); let (buf, len) = w.finish();
assert!(matches!(
Hdr10PlusMetadata::decode(&buf[..len], &mut |_| {}),
Err(DecodeError::MalformedPayload)
));
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn hdr10plus_unknown_application_mode_warning() {
let (mut payload, _) = make_minimal_hdr10plus_payload();
payload[1] = 0x02; let mut warnings = alloc::vec::Vec::new();
Hdr10PlusMetadata::decode(&payload, &mut |w| warnings.push(w)).unwrap();
assert!(warnings.iter().any(|w| matches!(
w,
DynamicHdrWarning::UnknownEnumValue {
field: "application_mode",
raw: 2
}
)));
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn hdr10plus_reserved_bits_set_warning() {
let (mut payload, _) = make_minimal_hdr10plus_payload();
payload[2] |= 0b1100_0000;
let mut warnings = alloc::vec::Vec::new();
Hdr10PlusMetadata::decode(&payload, &mut |w| warnings.push(w)).unwrap();
assert!(
warnings
.iter()
.any(|w| matches!(w, DynamicHdrWarning::ReservedFieldNonZero { .. }))
);
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn hdr10plus_round_trip() {
use crate::encode::IntoPackets;
let (_, original) = make_minimal_hdr10plus_payload();
let frame = DynamicHdrInfoFrame::Hdr10Plus(original.clone());
let pkts: alloc::vec::Vec<[u8; 31]> = frame.into_packets().value.collect();
let decoded = DynamicHdrInfoFrame::decode_sequence(&pkts).unwrap();
match decoded.value {
DynamicHdrInfoFrame::Hdr10Plus(meta) => assert_eq!(meta, original),
other => panic!("expected Hdr10Plus, got {other:?}"),
}
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn hdr10plus_decode_sequence_dispatches_to_hdr10plus_variant() {
let (payload, _) = make_minimal_hdr10plus_payload();
let total = payload.len() as u16;
let pkts: alloc::vec::Vec<[u8; 31]> = payload
.chunks(23)
.enumerate()
.map(|(i, chunk)| make_packet(i as u8, total, 0x04, chunk))
.collect();
let decoded = DynamicHdrInfoFrame::decode_sequence(&pkts).unwrap();
assert!(matches!(decoded.value, DynamicHdrInfoFrame::Hdr10Plus(_)));
}
#[cfg(any(feature = "alloc", feature = "std"))]
fn make_slhdr_full_body_payload() -> (alloc::vec::Vec<u8>, SlHdrMetadata) {
let mut w = BitWriter::new();
w.write_u8(0xB5, 8);
w.write_u16(0x003C, 16);
w.write_u8(0x01, 8);
w.write_u8(0, 4); w.write_u8(1, 4); w.write_u8(0, 7); w.write_bool(false); w.write_bool(true); w.write_bool(true); w.write_bool(true); w.write_bool(true); w.write_bool(false); w.write_u8(0, 3); w.write_u8(1, 8);
w.write_u16(1000, 16);
w.write_u16(5, 16);
w.write_u8(9, 8);
w.write_u16(400, 16);
w.write_u16(1, 16);
w.write_u16(100, 16);
w.write_u16(200, 16); w.write_u16(300, 16);
w.write_u16(400, 16); w.write_u16(500, 16);
w.write_u16(600, 16); w.write_u16(700, 16);
w.write_u16(800, 16); w.write_u16(900, 16);
w.write_u16(10, 16); for v in [1u16, 2, 3, 4] {
w.write_u16(v, 16);
}
for v in [5u16, 6] {
w.write_u16(v, 16);
}
for v in [7u8, 8, 9] {
w.write_u8(v, 8);
}
w.write_u8(10, 8); w.write_u8(20, 8); w.write_u8(30, 8); w.write_u8(40, 8); w.write_u8(50, 8); w.write_u8(2, 4); w.write_u8(1, 4); w.write_u8(11, 8);
w.write_u8(22, 8); w.write_u8(33, 8);
w.write_u8(44, 8); w.write_u8(55, 8);
w.write_u8(66, 8); let (buf, len) = w.finish();
let payload = buf[..len].to_vec();
let mut ftm = SlHdrTable15 {
count: 2,
..Default::default()
};
ftm.x[0] = 11;
ftm.y[0] = 22;
ftm.x[1] = 33;
ftm.y[1] = 44;
let mut sg = SlHdrTable15 {
count: 1,
..Default::default()
};
sg.x[0] = 55;
sg.y[0] = 66;
let expected = SlHdrMetadata {
itu_t_t35_country_code: 0xB5,
terminal_provider_code: 0x003C,
terminal_provider_oriented_code_message_idc: 0x01,
sl_hdr_mode_value_minus1: 0,
sl_hdr_spec_major_version_idc: 1,
sl_hdr_spec_minor_version_idc: 0,
sl_hdr_cancel_flag: false,
body: Some(SlHdrBody {
sl_hdr_persistence_flag: true,
sl_hdr_payload_mode: 0,
original_picture_info: Some(SlHdrPictureInfo {
primaries: 1,
max_luminance: 1000,
min_luminance: 5,
}),
target_picture_info: Some(SlHdrPictureInfo {
primaries: 9,
max_luminance: 400,
min_luminance: 1,
}),
src_mdcv_info: Some(SlHdrMdcvInfo {
primaries: [[100, 200], [300, 400], [500, 600]],
ref_white_x: 700,
ref_white_y: 800,
max_mastering_luminance: 900,
min_mastering_luminance: 10,
}),
matrix_coefficient_values: [1, 2, 3, 4],
chroma_to_luma_injection: [5, 6],
k_coefficient_values: [7, 8, 9],
payload: SlHdrPayload::Mode0(SlHdrMode0 {
tone_mapping_input_signal_black_level_offset: 10,
tone_mapping_input_signal_white_level_offset: 20,
shadow_gain_control: 30,
highlight_gain_control: 40,
mid_tone_width_adjustment_factor: 50,
tone_mapping_output_fine_tuning: ftm,
saturation_gain: sg,
}),
extension: None,
}),
};
(payload, expected)
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn slhdr_full_body_decode() {
let (payload, expected) = make_slhdr_full_body_payload();
let got = SlHdrMetadata::decode(&payload, &mut |_| {}).unwrap();
assert_eq!(got, expected);
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn slhdr_full_body_round_trip() {
use crate::encode::IntoPackets;
let (_, original) = make_slhdr_full_body_payload();
let frame = DynamicHdrInfoFrame::SlHdr(original.clone());
let pkts: alloc::vec::Vec<[u8; 31]> = frame.into_packets().value.collect();
let decoded = DynamicHdrInfoFrame::decode_sequence(&pkts).unwrap();
match decoded.value {
DynamicHdrInfoFrame::SlHdr(meta) => assert_eq!(meta, original),
other => panic!("expected SlHdr, got {other:?}"),
}
}
#[cfg(any(feature = "alloc", feature = "std"))]
fn make_slhdr_cancelled_payload() -> alloc::vec::Vec<u8> {
let mut w = BitWriter::new();
w.write_u8(0xB5, 8); w.write_u16(0x003C, 16); w.write_u8(0x01, 8); w.write_u8(0, 4); w.write_u8(1, 4); w.write_u8(0, 7); w.write_bool(true); let (buf, len) = w.finish();
buf[..len].to_vec()
}
#[cfg(any(feature = "alloc", feature = "std"))]
fn make_slhdr_mode0_payload() -> alloc::vec::Vec<u8> {
let mut w = BitWriter::new();
w.write_u8(0xB5, 8);
w.write_u16(0x003C, 16);
w.write_u8(0x01, 8);
w.write_u8(0, 4); w.write_u8(1, 4); w.write_u8(0, 7); w.write_bool(false); w.write_bool(true); w.write_bool(false); w.write_bool(false); w.write_bool(false); w.write_bool(false); w.write_u8(0, 3); for _ in 0..4 {
w.write_u16(0, 16);
}
for _ in 0..2 {
w.write_u16(0, 16);
}
for _ in 0..3 {
w.write_u8(0, 8);
}
w.write_u8(10, 8); w.write_u8(20, 8); w.write_u8(30, 8); w.write_u8(40, 8); w.write_u8(50, 8); w.write_u8(0, 4); w.write_u8(0, 4); let (buf, len) = w.finish();
buf[..len].to_vec()
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn slhdr_cancel_flag_produces_no_body() {
let payload = make_slhdr_cancelled_payload();
let meta = SlHdrMetadata::decode(&payload, &mut |_| {}).unwrap();
assert!(meta.sl_hdr_cancel_flag);
assert!(meta.body.is_none());
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn slhdr_cancelled_round_trip() {
use crate::encode::IntoPackets;
let payload = make_slhdr_cancelled_payload();
let original = SlHdrMetadata::decode(&payload, &mut |_| {}).unwrap();
let frame = DynamicHdrInfoFrame::SlHdr(original.clone());
let pkts: alloc::vec::Vec<[u8; 31]> = frame.into_packets().value.collect();
let decoded = DynamicHdrInfoFrame::decode_sequence(&pkts).unwrap();
match decoded.value {
DynamicHdrInfoFrame::SlHdr(meta) => assert_eq!(meta, original),
other => panic!("expected SlHdr, got {other:?}"),
}
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn slhdr_unknown_payload_mode_warning() {
let mut w = BitWriter::new();
w.write_u8(0xB5, 8);
w.write_u16(0x003C, 16);
w.write_u8(0x01, 8);
w.write_u8(0, 4);
w.write_u8(1, 4);
w.write_u8(0, 7);
w.write_bool(false); w.write_bool(false); w.write_bool(false); w.write_bool(false); w.write_bool(false); w.write_bool(false); w.write_u8(7, 3); for _ in 0..4 {
w.write_u16(0, 16);
}
for _ in 0..2 {
w.write_u16(0, 16);
}
for _ in 0..3 {
w.write_u8(0, 8);
}
let (buf, len) = w.finish();
let payload = &buf[..len];
let mut warnings = alloc::vec::Vec::new();
let meta = SlHdrMetadata::decode(payload, &mut |w| warnings.push(w)).unwrap();
assert!(warnings.iter().any(|w| matches!(
w,
DynamicHdrWarning::UnknownEnumValue {
field: "sl_hdr_payload_mode",
raw: 7
}
)));
let body = meta.body.as_ref().unwrap();
assert!(matches!(body.payload, SlHdrPayload::Unknown(7)));
use crate::encode::IntoPackets;
let frame = DynamicHdrInfoFrame::SlHdr(meta.clone());
let pkts: alloc::vec::Vec<[u8; 31]> = frame.into_packets().value.collect();
let decoded = DynamicHdrInfoFrame::decode_sequence(&pkts).unwrap();
match decoded.value {
DynamicHdrInfoFrame::SlHdr(re_meta) => assert_eq!(re_meta.body, meta.body),
other => panic!("expected SlHdr, got {other:?}"),
}
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn slhdr_extension_round_trip() {
use crate::encode::IntoPackets;
let mut w = BitWriter::new();
w.write_u8(0xB5, 8);
w.write_u16(0x003C, 16);
w.write_u8(0x01, 8);
w.write_u8(0, 4);
w.write_u8(1, 4);
w.write_u8(0, 7);
w.write_bool(false); w.write_bool(false); w.write_bool(false); w.write_bool(false); w.write_bool(false); w.write_bool(true); w.write_u8(0, 3); for _ in 0..4 {
w.write_u16(0, 16);
} for _ in 0..2 {
w.write_u16(0, 16);
} for _ in 0..3 {
w.write_u8(0, 8);
} w.write_u8(0, 8);
w.write_u8(0, 8);
w.write_u8(0, 8);
w.write_u8(0, 8);
w.write_u8(0, 8);
w.write_u8(0, 4);
w.write_u8(0, 4); w.write_u8(0x3F, 6); w.write_u16(2, 10); w.write_u8(0xAB, 8); w.write_u8(0xCD, 8); let (buf, len) = w.finish();
let payload = buf[..len].to_vec();
let original = SlHdrMetadata::decode(&payload, &mut |_| {}).unwrap();
let ext = original.body.as_ref().unwrap().extension.as_ref().unwrap();
assert_eq!(ext.extension_6bits, 0x3F);
assert_eq!(ext.data, alloc::vec![0xAB, 0xCD]);
let frame = DynamicHdrInfoFrame::SlHdr(original.clone());
let pkts: alloc::vec::Vec<[u8; 31]> = frame.into_packets().value.collect();
let decoded = DynamicHdrInfoFrame::decode_sequence(&pkts).unwrap();
match decoded.value {
DynamicHdrInfoFrame::SlHdr(meta) => assert_eq!(meta, original),
other => panic!("expected SlHdr, got {other:?}"),
}
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn slhdr_mode0_decode() {
let payload = make_slhdr_mode0_payload();
let meta = SlHdrMetadata::decode(&payload, &mut |_| {}).unwrap();
assert!(!meta.sl_hdr_cancel_flag);
let body = meta.body.as_ref().unwrap();
assert_eq!(body.sl_hdr_payload_mode, 0);
match &body.payload {
SlHdrPayload::Mode0(m) => {
assert_eq!(m.tone_mapping_input_signal_black_level_offset, 10);
assert_eq!(m.tone_mapping_input_signal_white_level_offset, 20);
assert_eq!(m.shadow_gain_control, 30);
assert_eq!(m.highlight_gain_control, 40);
assert_eq!(m.mid_tone_width_adjustment_factor, 50);
assert_eq!(m.tone_mapping_output_fine_tuning.count, 0);
assert_eq!(m.saturation_gain.count, 0);
}
other => panic!("expected Mode0, got {other:?}"),
}
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn slhdr_malformed_short_payload_is_error() {
assert!(matches!(
SlHdrMetadata::decode(&[], &mut |_| {}),
Err(DecodeError::MalformedPayload)
));
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn slhdr_decode_sequence_dispatches_to_slhdr_variant() {
let payload = make_slhdr_cancelled_payload();
let total = payload.len() as u16;
let pkts: alloc::vec::Vec<[u8; 31]> = payload
.chunks(23)
.enumerate()
.map(|(i, chunk)| make_packet(i as u8, total, 0x02, chunk))
.collect();
let decoded = DynamicHdrInfoFrame::decode_sequence(&pkts).unwrap();
assert!(matches!(decoded.value, DynamicHdrInfoFrame::SlHdr(_)));
}
#[cfg(any(feature = "alloc", feature = "std"))]
fn make_slhdr_mode1_alt_sampling_payload() -> alloc::vec::Vec<u8> {
let mut w = BitWriter::new();
w.write_u8(0xB5, 8);
w.write_u16(0x003C, 16);
w.write_u8(0x01, 8);
w.write_u8(0, 4);
w.write_u8(1, 4);
w.write_u8(0, 7);
w.write_bool(false); w.write_bool(true); w.write_bool(false);
w.write_bool(false);
w.write_bool(false); w.write_bool(false); w.write_u8(1, 3); for _ in 0..4 {
w.write_u16(0, 16);
} for _ in 0..2 {
w.write_u16(0, 16);
} for _ in 0..3 {
w.write_u8(0, 8);
} w.write_bool(true); w.write_u8(2, 7); w.write_u16(100, 16); w.write_u16(200, 16); w.write_bool(false); w.write_u8(1, 7); w.write_u16(300, 16); w.write_u16(400, 16); let (buf, len) = w.finish();
buf[..len].to_vec()
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn slhdr_mode1_alt_sampling_round_trip() {
use crate::encode::IntoPackets;
let payload = make_slhdr_mode1_alt_sampling_payload();
let original = SlHdrMetadata::decode(&payload, &mut |_| {}).unwrap();
let body = original.body.as_ref().unwrap();
match &body.payload {
SlHdrPayload::Mode1(m) => {
assert!(m.lm_uniform_sampling_flag);
assert_eq!(m.luminance_mapping.count, 2);
assert_eq!(m.luminance_mapping.y[0], 100);
assert_eq!(m.luminance_mapping.y[1], 200);
assert!(!m.cc_uniform_sampling_flag);
assert_eq!(m.colour_correction.count, 1);
assert_eq!(m.colour_correction.x[0], 300);
assert_eq!(m.colour_correction.y[0], 400);
}
other => panic!("expected Mode1, got {other:?}"),
}
let frame = DynamicHdrInfoFrame::SlHdr(original.clone());
let pkts: alloc::vec::Vec<[u8; 31]> = frame.into_packets().value.collect();
let decoded = DynamicHdrInfoFrame::decode_sequence(&pkts).unwrap();
match decoded.value {
DynamicHdrInfoFrame::SlHdr(meta) => assert_eq!(meta, original),
other => panic!("expected SlHdr, got {other:?}"),
}
}
#[cfg(any(feature = "alloc", feature = "std"))]
fn make_slhdr_mode1_payload() -> alloc::vec::Vec<u8> {
let mut w = BitWriter::new();
w.write_u8(0xB5, 8);
w.write_u16(0x003C, 16);
w.write_u8(0x01, 8);
w.write_u8(0, 4); w.write_u8(1, 4); w.write_u8(0, 7); w.write_bool(false); w.write_bool(true); w.write_bool(false); w.write_bool(false); w.write_bool(false); w.write_bool(false); w.write_u8(1, 3); for _ in 0..4 {
w.write_u16(0, 16);
}
for _ in 0..2 {
w.write_u16(0, 16);
}
for _ in 0..3 {
w.write_u8(0, 8);
}
w.write_bool(false); w.write_u8(2, 7); w.write_u16(100, 16); w.write_u16(200, 16); w.write_u16(300, 16); w.write_u16(400, 16); w.write_bool(true); w.write_u8(1, 7); w.write_u16(500, 16); let (buf, len) = w.finish();
buf[..len].to_vec()
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn slhdr_mode1_round_trip() {
use crate::encode::IntoPackets;
let payload = make_slhdr_mode1_payload();
let original = SlHdrMetadata::decode(&payload, &mut |_| {}).unwrap();
let frame = DynamicHdrInfoFrame::SlHdr(original.clone());
let pkts: alloc::vec::Vec<[u8; 31]> = frame.into_packets().value.collect();
let decoded = DynamicHdrInfoFrame::decode_sequence(&pkts).unwrap();
match decoded.value {
DynamicHdrInfoFrame::SlHdr(meta) => assert_eq!(meta, original),
other => panic!("expected SlHdr, got {other:?}"),
}
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn slhdr_mode0_round_trip() {
use crate::encode::IntoPackets;
let payload = make_slhdr_mode0_payload();
let original = SlHdrMetadata::decode(&payload, &mut |_| {}).unwrap();
let frame = DynamicHdrInfoFrame::SlHdr(original.clone());
let pkts: alloc::vec::Vec<[u8; 31]> = frame.into_packets().value.collect();
let decoded = DynamicHdrInfoFrame::decode_sequence(&pkts).unwrap();
match decoded.value {
DynamicHdrInfoFrame::SlHdr(meta) => assert_eq!(meta, original),
other => panic!("expected SlHdr, got {other:?}"),
}
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn hdr10plus_max_size_encode_no_panic() {
use crate::encode::IntoPackets;
let make_apl = || ActualPeakLuminance {
num_rows: 25,
num_cols: 25,
entries: [[0x0F; 25]; 25],
};
let make_window = || Hdr10PlusWindow {
distribution_maxrgb: DistributionMaxrgb {
count: 15,
percentages: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
percentiles: [0xFFFF; 15],
},
tone_mapping_flag: true,
knee_point: Some(KneePoint { x: 0xFFF, y: 0xFFF }),
bezier_curve_anchors: BezierAnchors {
count: 9,
anchors: [0x3FF; 9],
},
maxscl: [0x1FFFF; 3],
average_maxrgb: 0x1FFFF,
fraction_bright_pixels: 0x3FF,
..Default::default()
};
let meta = Hdr10PlusMetadata {
application_identifier: 4,
application_mode: 1,
scene_frame_switching_flag: true,
targeted_system_display_maximum_luminance: 0x07FF_FFFF,
targeted_system_display_actual_peak_luminance_flag: true,
targeted_system_display_actual_peak_luminance: Some(make_apl()),
windows: Hdr10PlusWindows {
count: 3,
windows: [make_window(), make_window(), make_window()],
},
mastering_display_actual_peak_luminance_flag: true,
mastering_display_actual_peak_luminance: Some(make_apl()),
color_saturation_mapping_flag: true,
color_saturation_weight: Some(0x3F),
};
let frame = DynamicHdrInfoFrame::Hdr10Plus(meta);
let _pkts: alloc::vec::Vec<[u8; 31]> = frame.into_packets().value.collect();
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn slhdr_max_size_encode_no_panic() {
use crate::encode::IntoPackets;
let make_table = || SlHdrTable127 {
count: 127,
x: [0xFFFF; 127],
y: [0xFFFF; 127],
};
let body = SlHdrBody {
sl_hdr_persistence_flag: true,
original_picture_info: Some(SlHdrPictureInfo {
primaries: 0xFF,
max_luminance: 0xFFFF,
min_luminance: 0xFFFF,
}),
target_picture_info: Some(SlHdrPictureInfo {
primaries: 0xFF,
max_luminance: 0xFFFF,
min_luminance: 0xFFFF,
}),
src_mdcv_info: Some(SlHdrMdcvInfo {
primaries: [[0xFFFF; 2]; 3],
ref_white_x: 0xFFFF,
ref_white_y: 0xFFFF,
max_mastering_luminance: 0xFFFF,
min_mastering_luminance: 0xFFFF,
}),
extension: Some(SlHdrExtension {
extension_6bits: 0x3F,
data: alloc::vec![0xAB; 1023],
}),
sl_hdr_payload_mode: 1,
matrix_coefficient_values: [0xFFFF; 4],
chroma_to_luma_injection: [0xFFFF; 2],
k_coefficient_values: [0xFF; 3],
payload: SlHdrPayload::Mode1(SlHdrMode1 {
lm_uniform_sampling_flag: false,
luminance_mapping: make_table(),
cc_uniform_sampling_flag: false,
colour_correction: make_table(),
}),
};
let meta = SlHdrMetadata {
itu_t_t35_country_code: 0xB5,
terminal_provider_code: 0x003C,
terminal_provider_oriented_code_message_idc: 0x01,
sl_hdr_mode_value_minus1: 0,
sl_hdr_spec_major_version_idc: 0,
sl_hdr_spec_minor_version_idc: 0,
sl_hdr_cancel_flag: false,
body: Some(body),
};
let frame = DynamicHdrInfoFrame::SlHdr(meta);
let _pkts: alloc::vec::Vec<[u8; 31]> = frame.into_packets().value.collect();
}
#[cfg(feature = "serde")]
mod serde_tests {
use super::*;
#[test]
fn slhdr_table127_round_trip() {
let mut table = SlHdrTable127::default();
table.count = 3;
table.x[0] = 100;
table.x[1] = 200;
table.x[2] = 300;
table.y[0] = 10;
table.y[1] = 20;
table.y[2] = 30;
let json = serde_json::to_string(&table).unwrap();
let back: SlHdrTable127 = serde_json::from_str(&json).unwrap();
assert_eq!(back.count, 3);
assert_eq!(back.x[..3], [100, 200, 300]);
assert_eq!(back.y[..3], [10, 20, 30]);
}
#[test]
fn slhdr_table127_zero_entries() {
let table = SlHdrTable127::default();
let json = serde_json::to_string(&table).unwrap();
let back: SlHdrTable127 = serde_json::from_str(&json).unwrap();
assert_eq!(back.count, 0);
}
#[test]
fn slhdr_table127_missing_field_is_error() {
let err = serde_json::from_str::<SlHdrTable127>(r#"{"count":1,"x":[10]}"#);
assert!(err.is_err());
}
#[test]
fn slhdr_table127_too_many_elements_is_error() {
let xs: alloc::vec::Vec<u16> = (0u16..128).collect();
let json = serde_json::json!({ "count": 0, "x": xs, "y": [] });
let err = serde_json::from_value::<SlHdrTable127>(json);
assert!(err.is_err());
}
#[test]
fn slhdr_table127_wrong_outer_type_is_error() {
let err = serde_json::from_str::<SlHdrTable127>("42");
assert!(err.is_err());
}
#[test]
fn slhdr_table127_wrong_array_type_is_error() {
let err = serde_json::from_str::<SlHdrTable127>(r#"{"count":0,"x":42,"y":[]}"#);
assert!(err.is_err());
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn slhdr_metadata_round_trip() {
let (_, meta) = make_slhdr_full_body_payload();
let json = serde_json::to_string(&meta).unwrap();
let back: SlHdrMetadata = serde_json::from_str(&json).unwrap();
assert_eq!(meta, back);
}
#[test]
fn slhdr_cancelled_round_trip() {
let meta = SlHdrMetadata {
itu_t_t35_country_code: 0xB5,
terminal_provider_code: 0x003C,
terminal_provider_oriented_code_message_idc: 0x01,
sl_hdr_mode_value_minus1: 0,
sl_hdr_spec_major_version_idc: 1,
sl_hdr_spec_minor_version_idc: 0,
sl_hdr_cancel_flag: true,
body: None,
};
let json = serde_json::to_string(&meta).unwrap();
let back: SlHdrMetadata = serde_json::from_str(&json).unwrap();
assert_eq!(meta, back);
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn hdr10plus_metadata_round_trip() {
let (_, meta) = make_full_hdr10plus_payload();
let json = serde_json::to_string(&meta).unwrap();
let back: Hdr10PlusMetadata = serde_json::from_str(&json).unwrap();
assert_eq!(meta, back);
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn dynamic_hdr_info_frame_hdr10plus_round_trip() {
let (_, meta) = make_full_hdr10plus_payload();
let frame = DynamicHdrInfoFrame::Hdr10Plus(meta);
let json = serde_json::to_string(&frame).unwrap();
let back: DynamicHdrInfoFrame = serde_json::from_str(&json).unwrap();
assert_eq!(frame, back);
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn dynamic_hdr_info_frame_slhdr_round_trip() {
let (_, meta) = make_slhdr_full_body_payload();
let frame = DynamicHdrInfoFrame::SlHdr(meta);
let json = serde_json::to_string(&frame).unwrap();
let back: DynamicHdrInfoFrame = serde_json::from_str(&json).unwrap();
assert_eq!(frame, back);
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn dynamic_hdr_info_frame_unknown_round_trip() {
let frame = DynamicHdrInfoFrame::Unknown {
format_id: 0x99,
payload: alloc::vec![0xAA, 0xBB, 0xCC],
};
let json = serde_json::to_string(&frame).unwrap();
let back: DynamicHdrInfoFrame = serde_json::from_str(&json).unwrap();
assert_eq!(frame, back);
}
#[test]
fn dynamic_hdr_fragment_round_trip() {
let frag = DynamicHdrFragment {
seq_num: 2,
total_bytes: 46,
format_id: 0x04,
chunk: [0xAB; 23],
chunk_len: 23,
};
let json = serde_json::to_string(&frag).unwrap();
let back: DynamicHdrFragment = serde_json::from_str(&json).unwrap();
assert_eq!(frag, back);
}
}