dynamic-loader-cache 0.2.3

Reader of the dynamic loader shared libraries cache
Documentation
use core::mem::{offset_of, size_of};
use std::io::{Cursor, Write};
use std::path::Path;

use proptest::prelude::*;

use super::{Cache, Entry, Header, ParsedHeader, MAGIC};

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-1.7.0/ld.so.cache").unwrap();
    print_cache(&cache);
}

#[test]
fn load_compat() {
    let cache = Cache::load("tests/ld.so-1.7.0/ld.so.cache.compat").unwrap();
    print_cache(&cache);
}

#[test]
fn parse_empty() {
    ParsedHeader::parse(Path::new("empty"), &[]).unwrap_err();
}

prop_compose! {
    /// Generate a random `size`, then random `lib_count` and `bytes`,
    /// in a fashion that makes sense to the parser.
    ///
    /// `lib_count` and `bytes` are random values that depend on the random `size`.
    fn header_components()
        (size in proptest::num::u16::ANY)
        (
            lib_count in 0_u32..=(u32::from(size) / size_of::<Entry>() as u32),
            bytes in prop::collection::vec(0_u8.., 0..=(2 * usize::from(size))),
        )
        -> (u32, Vec<u8>)
    {
        (lib_count, bytes)
    }
}

proptest! {
    #[test]
    fn load_random(
        (lib_count, bytes) in header_components(),
        random_u8 in proptest::num::u8::ANY,
        random_u32 in proptest::num::u32::ANY,
    ) {
        load_random0([random_u8], lib_count, &bytes)?;
        load_random0([random_u8], random_u32, &bytes)?;
    }
}

fn load_random0(padding: [u8; 1], lib_count: u32, bytes: &[u8]) -> Result<(), TestCaseError> {
    let mut cursor = Cursor::new(Vec::<u8>::with_capacity(size_of::<Header>() + bytes.len()));
    cursor.write_all(MAGIC)?;
    cursor.write_all(&padding)?;
    cursor.write_all(&lib_count.to_ne_bytes())?;
    cursor.write_all(bytes)?;
    let bytes = cursor.into_inner();

    let Ok(p_header) = ParsedHeader::parse(Path::new("random"), &bytes) else {
        return Ok(());
    };
    let p_lib_count = u32::try_from(p_header.lib_count).unwrap();

    let lib_count_bytes = bytes
        .get(offset_of!(Header, lib_count)..(offset_of!(Header, lib_count) + 4))
        .unwrap_or(&[]);
    prop_assert_eq!(lib_count_bytes, p_lib_count.to_ne_bytes());

    // TODO(KAT): Test iterators.

    Ok(())
}