//! A module used to test codegen. //! //! This imports a single codegen output; while modifying the codegen crate, //! this file can be regenerated to check that changes compile, without needing //! to rebuild everything. //! //! To rebuild this input and test it, run: //! //! $ cargo run --bin=codegen resources/test_plan.toml && cargo test pub mod records { include!("../generated/generated_test_records.rs"); } pub mod formats { include!("../generated/generated_test_formats.rs"); } pub mod read_args { include!("../generated/generated_test_read_args.rs"); } pub mod offsets_arrays { include!("../generated/generated_test_offsets_arrays.rs"); #[cfg(test)] use font_test_data::bebuffer::BeBuffer; pub struct VarSizeDummy<'a> { #[allow(dead_code)] count: u16, pub bytes: &'a [u8], } impl VarSize for VarSizeDummy<'_> { type Size = u16; } impl<'a> FontRead<'a> for VarSizeDummy<'a> { fn read(data: FontData<'a>) -> Result { let count: u16 = data.read_at(0)?; let bytes = data .as_bytes() .get(2..2 + (count as usize)) .ok_or(ReadError::OutOfBounds)?; Ok(Self { count, bytes }) } } #[test] fn array_offsets() { let builder = BeBuffer::new() .push(MajorMinor::VERSION_1_0) .push(12_u16) // offset to 0xdead .push(0u16) // nullable .push(2u16) // array len .push(12u16) // array offset .extend([0xdead_u16, 0xbeef]); let table = KindsOfOffsets::read(builder.data().into()).unwrap(); assert_eq!(table.nonnullable().unwrap().value(), 0xdead); let array = table.array().unwrap(); assert_eq!(array, &[0xdead, 0xbeef]); } #[test] fn var_len_array_empty() { let builder = BeBuffer::new().push(0u16).push(0xdeadbeef_u32); let table = VarLenHaver::read(builder.data().into()).unwrap(); assert_eq!(table.other_field(), 0xdeadbeef); } #[test] fn var_len_array_some() { let builder = BeBuffer::new() .push(3u16) .push(0u16) // first item in array is empty .push(2u16) .extend([1u8, 1]) .push(5u16) .extend([7u8, 7, 7, 7, 7]) .push(0xdeadbeef_u32); let table = VarLenHaver::read(builder.data().into()).unwrap(); let kids = table .var_len() .iter() .map(|x| x.unwrap()) .collect::>(); assert!(kids[0].bytes.is_empty()); assert_eq!(kids[1].bytes, &[1, 1]); assert_eq!(kids[2].bytes, &[7, 7, 7, 7, 7]); assert_eq!(table.other_field(), 0xdeadbeef) } #[test] #[cfg(feature = "experimental_traverse")] fn array_offsets_traverse() { let mut builder = BeBuffer::new() .push(MajorMinor::VERSION_1_1) .push(22_u16) // offset to [0xf00, 0xba4] .push(0u16) // nullable .push(2u16) // array len .push(26u16) // offset to [69, 70] .push(30u16) // record_array_offset .push(0u16) // versioned_nullable_record_array_offset .push(42u16) // versioned nonnullable offset .push(0u32); // versioned nullable offset // let data_start = builder.len(); assert_eq!(data_start, 22); builder = builder .extend([0xf00u16, 0xba4]) .extend([69u16, 70]) .push(3u16) // shmecord[0] .push(9u32) .push(5u16) // shmecord[1] .push(0xdead_beefu32) .extend([0xb01du16, 0xface]); // versioned nonnullable offset; let table = KindsOfOffsets::read(builder.data().into()).unwrap(); // traversal should not crash let _ = format!("{table:?}"); assert_eq!( table.versioned_nonnullable().unwrap().unwrap().value(), 0xb01d ); } #[test] fn versioned_array_bad_data() { let buf = BeBuffer::new() .push(1u16) // version .push(1u16) // count .push(2u16) // scalar array .push(3u16) .push(4u32); // shmecord array let table = KindsOfArrays::read(buf.data().into()).unwrap(); assert!(table.versioned_scalars().is_none()); // should be there but isn't } } pub mod flags { include!("../generated/generated_test_flags.rs"); #[test] fn basics() { let all = ValueFormat::all(); let none = ValueFormat::empty(); assert!(all.contains(ValueFormat::X_PLACEMENT)); assert!(all.contains(ValueFormat::Y_PLACEMENT)); assert!(!none.contains(ValueFormat::X_PLACEMENT)); assert!(!none.contains(ValueFormat::Y_PLACEMENT)); assert_eq!(none, ValueFormat::default()); } #[test] fn formatting() { let all = ValueFormat::all(); assert_eq!(format!("{all:?}"), "X_PLACEMENT | Y_PLACEMENT"); let none = ValueFormat::empty(); assert_eq!(format!("{none:?}"), "(empty)"); let xplace = ValueFormat::X_PLACEMENT; assert_eq!(format!("{xplace:?}"), "X_PLACEMENT"); } // not exactly a test, but this will fail to compile if these are missing #[test] fn impl_traits() { fn impl_check() {} impl_check::(); } } pub mod enums { include!("../generated/generated_test_enum.rs"); } pub mod count_all { use crate::FontData; include!("../generated/generated_test_count_all.rs"); /// Test for count(..) with element sizes > 1 #[test] fn element_size_greater_than_one_with_padding() { // Size of 13 ensures we have an extra padding byte let bytes = [0u8; 13]; // Generated table has a 2 byte field above the array let remainder_len = bytes.len() - 2; let data = FontData::new(&bytes); // Trailing array with 16-bit elements assert!(remainder_len % 2 != 0); let count16 = CountAll16::read(data).unwrap(); assert_eq!(count16.remainder().len(), remainder_len / 2); // Trailing array with 32-bit elements assert!(remainder_len % 4 != 0); let count32 = CountAll32::read(data).unwrap(); assert_eq!(count32.remainder().len(), remainder_len / 4); } } pub mod conditions { #[cfg(test)] use font_test_data::bebuffer::BeBuffer; use font_types::MajorMinor; include!("../generated/generated_test_conditions.rs"); #[test] fn majorminor_1() { let bytes = BeBuffer::new().push(MajorMinor::VERSION_1_0).push(0u16); let table = MajorMinorVersion::read(bytes.data().into()).unwrap(); assert_eq!(table.always_present(), 0); } #[test] fn majorminor_1_1() { let bytes = BeBuffer::new().push(MajorMinor::VERSION_1_1).push(0u16); let too_small = MajorMinorVersion::read(bytes.data().into()).unwrap(); // this is expected to be present but the data is malformed; we will // still parse the table but checked read of the field will fail assert!(too_small.if_11().is_none()); let bytes = BeBuffer::new() .push(MajorMinor::VERSION_1_1) .push(0u16) .push(1u16); let table = MajorMinorVersion::read(bytes.data().into()).unwrap(); assert_eq!(table.if_11(), Some(1)); } #[test] fn major_minor_2() { let bytes = BeBuffer::new().push(MajorMinor::VERSION_2_0).push(0u16); let too_small = MajorMinorVersion::read(bytes.data().into()).unwrap(); assert!(too_small.if_11().is_none()); assert!(too_small.if_20().is_none()); let bytes = BeBuffer::new() .push(MajorMinor::VERSION_2_0) .push(0u16) .push(2u32); let table = MajorMinorVersion::read(bytes.data().into()).unwrap(); assert_eq!(table.if_11(), None); assert_eq!(table.if_20(), Some(2)); } #[cfg(test)] fn make_flag_data(flags: GotFlags) -> BeBuffer { let mut buf = BeBuffer::new().push(42u16).push(flags); if flags.contains(GotFlags::FOO) { buf = buf.push(0xf00_u16); } if flags.contains(GotFlags::BAR) { buf = buf.push(0xba4_u16); } if flags.contains(GotFlags::FOO) || flags.contains(GotFlags::BAZ) { buf = buf.push(0xba2_u16); } buf } #[test] fn flags_none() { let data = make_flag_data(GotFlags::empty()); let table = FlagDay::read(data.data().into()).unwrap(); assert!(table.foo().is_none()); assert!(table.bar().is_none()); } #[test] fn flags_foo() { let data = make_flag_data(GotFlags::FOO); let table = FlagDay::read(data.data().into()).unwrap(); assert_eq!(table.foo(), Some(0xf00)); assert!(table.bar().is_none()); } #[test] fn flags_bar() { let data = make_flag_data(GotFlags::BAR); let table = FlagDay::read(data.data().into()).unwrap(); assert!(table.foo().is_none()); assert_eq!(table.bar(), Some(0xba4)); } #[test] fn flags_foobar() { let data = make_flag_data(GotFlags::BAR | GotFlags::FOO); let table = FlagDay::read(data.data().into()).unwrap(); assert_eq!(table.foo(), Some(0xf00)); assert_eq!(table.bar(), Some(0xba4)); } } pub mod generic_group { include!("../generated/generated_test_generic_group.rs"); #[cfg(test)] use font_test_data::bebuffer::BeBuffer; /// Build bytes for a MyLookup with one subtable offset pointing at data /// immediately after the header. /// Layout: [lookup_type: u16, sub_table_count: u16, offset0: Offset16, ...subtable data] #[cfg(test)] fn make_lookup_with_format1(lookup_type: u16) -> BeBuffer { BeBuffer::new() .push(lookup_type) // lookup_type .push(1u16) // sub_table_count .push(6u16) // offset to subtable (6 bytes from start) // subtable data (MySubtableFormat1): format=1, value=42 .push(1u16) .push(42u16) } #[test] fn parse_lookup_group_type_one() { let buf = make_lookup_with_format1(1); let group = MyLookupGroup::read(buf.data().into()).unwrap(); assert!(matches!(group, MyLookupGroup::TypeOne(_))); let lookup = group.of_unit_type(); assert_eq!(lookup.lookup_type(), 1); assert_eq!(lookup.sub_table_count(), 1); } #[test] fn parse_lookup_group_type_two() { let buf = make_lookup_with_format1(2); let group = MyLookupGroup::read(buf.data().into()).unwrap(); assert!(matches!(group, MyLookupGroup::TypeTwo(_))); } #[test] fn parse_lookup_group_invalid_type() { let buf = make_lookup_with_format1(99); let result = MyLookupGroup::read(buf.data().into()); assert!(matches!(result, Err(ReadError::InvalidFormat(99)))) } #[test] fn parse_subtable_format_dispatch() { // Format 1 let buf = BeBuffer::new().push(1u16).push(42u16); let sub = MySubtable::read(buf.data().into()).unwrap(); assert!(matches!(sub, MySubtable::Format1(_))); if let MySubtable::Format1(f1) = sub { assert_eq!(f1.value(), 42); } // Format 2 let buf = BeBuffer::new() .push(2u16) // format .push(2u16) // count .extend([10u16, 20]); let sub = MySubtable::read(buf.data().into()).unwrap(); assert!(matches!(sub, MySubtable::Format2(_))); if let MySubtable::Format2(f2) = sub { assert_eq!(f2.count(), 2); } } }