use core::fmt;
use core::mem::size_of;
use std::io::{Cursor, Write};
use std::path::Path;
use nom::number::Endianness;
use proptest::prelude::*;
use super::{
Bucket, Cache, ParsedHeader, MAGIC_BE32, MAGIC_BE64, MAGIC_LE32, MAGIC_LE64, VERSION_2_BE32,
VERSION_2_BE64, VERSION_2_LE32, VERSION_2_LE64,
};
#[repr(C)]
struct Header<ULong: Sized> {
magic: ULong,
version: ULong,
hash_table: ULong,
bucket_count: ULong,
string_table: ULong,
string_table_size: ULong,
end_of_hints: ULong,
dir_list: ULong,
}
fn print_cache(cache: &Cache) {
eprintln!();
for e in cache.iter().unwrap() {
let e = e.unwrap();
eprintln!(
" {} => {}",
e.file_name.to_string_lossy(),
e.full_path.display()
);
}
}
#[test]
fn load() {
let cache = Cache::load("tests/ld.so.hints/ld.so.hints").unwrap();
print_cache(&cache);
}
#[test]
fn parse_empty() {
ParsedHeader::parse(Path::new("empty"), &[]).unwrap_err();
}
prop_compose! {
fn header_components_32()
(size in proptest::num::u16::ANY)
(
hash_table in 0_u32..=u32::from(size),
bucket_count in 0_u32..=(u32::from(size) / size_of::<Bucket>() as u32),
string_table in 0_u32..=u32::from(size),
string_table_size in 0_u32..=u32::from(size),
end_of_hints in 0_u32..=u32::from(size),
bytes in prop::collection::vec(0_u8.., 0..=(2 * usize::from(size)))
)
-> (u32, u32, u32, u32, u32, Vec<u8>)
{
(hash_table, bucket_count, string_table, string_table_size, end_of_hints, bytes)
}
}
prop_compose! {
fn header_components_64()
(size in proptest::num::u16::ANY)
(
hash_table in 0_u64..=u64::from(size),
bucket_count in 0_u64..=(u64::from(size) / size_of::<Bucket>() as u64),
string_table in 0_u64..=u64::from(size),
string_table_size in 0_u64..=u64::from(size),
end_of_hints in 0_u64..=u64::from(size),
bytes in prop::collection::vec(0_u8.., 0..=(2 * usize::from(size)))
)
-> (u64, u64, u64, u64, u64, Vec<u8>)
{
(hash_table, bucket_count, string_table, string_table_size, end_of_hints, bytes)
}
}
proptest! {
#[test]
fn load_random_32(
(hash_table, bucket_count, string_table, string_table_size, end_of_hints, bytes)
in header_components_32(),
dir_list in proptest::num::u32::ANY,
) {
load_random(
Endianness::Little, u32::to_le_bytes, &MAGIC_LE32,
&VERSION_2_LE32, hash_table, bucket_count, string_table,
string_table_size, end_of_hints, dir_list, &bytes)?;
load_random(
Endianness::Big, u32::to_be_bytes, &MAGIC_BE32,
&VERSION_2_BE32, hash_table, bucket_count, string_table,
string_table_size, end_of_hints, dir_list, &bytes)?;
}
#[test]
fn load_random_64(
(hash_table, bucket_count, string_table, string_table_size, end_of_hints, bytes)
in header_components_64(),
dir_list in proptest::num::u64::ANY,
) {
load_random(
Endianness::Little, u64::to_le_bytes, &MAGIC_LE64,
&VERSION_2_LE64, hash_table, bucket_count, string_table,
string_table_size, end_of_hints, dir_list, &bytes)?;
load_random(
Endianness::Big, u64::to_be_bytes, &MAGIC_BE64,
&VERSION_2_BE64, hash_table, bucket_count, string_table,
string_table_size, end_of_hints, dir_list, &bytes)?;
}
}
#[allow(clippy::too_many_arguments)]
fn load_random<T, const N: usize>(
byte_order: Endianness,
long_to_bytes: fn(T) -> [u8; N],
magic: &[u8],
version: &[u8],
hash_table: T,
bucket_count: T,
string_table: T,
string_table_size: T,
end_of_hints: T,
dir_list: T,
bytes: &[u8],
) -> Result<(), TestCaseError>
where
T: fmt::Debug + Copy + Into<u64>,
{
let mut cursor = Cursor::new(Vec::<u8>::with_capacity(
size_of::<Header<T>>() + bytes.len(),
));
cursor.write_all(magic)?;
cursor.write_all(version)?;
cursor.write_all(&long_to_bytes(hash_table))?;
cursor.write_all(&long_to_bytes(bucket_count))?;
cursor.write_all(&long_to_bytes(string_table))?;
cursor.write_all(&long_to_bytes(string_table_size))?;
cursor.write_all(&long_to_bytes(end_of_hints))?;
cursor.write_all(&long_to_bytes(dir_list))?;
cursor.write_all(bytes)?;
let bytes = cursor.into_inner();
let Ok(p_header) = ParsedHeader::parse(Path::new("random"), &bytes) else {
return Ok(());
};
prop_assert_eq!(p_header.byte_order, byte_order);
prop_assert_eq!(p_header.hash_table, hash_table.into());
prop_assert_eq!(p_header.bucket_count, bucket_count.into());
prop_assert_eq!(p_header.string_table, string_table.into());
prop_assert_eq!(p_header.string_table_size, string_table_size.into());
Ok(())
}