wsi-rs 0.5.2

wsi-rs whole-slide image reader
Documentation
use super::*;
use std::io::Write;
use std::sync::atomic::Ordering;

fn mirax_sentinel_path() -> PathBuf {
    PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("../../downloads/openslide-testdata-extracted/mirax/mirax-cmu1/CMU-1.mrxs")
}

#[test]
fn associated_thumbnail_is_cached_after_first_read() {
    let sentinel_path = mirax_sentinel_path();
    if !sentinel_path.is_file() {
        eprintln!(
            "skipping corpus-backed MIRAX thumbnail cache test; missing {}",
            sentinel_path.display()
        );
        return;
    }
    MIRAX_ASSOCIATED_CACHE_HITS.store(0, Ordering::Relaxed);
    let slide = MiraxSlide::parse(&sentinel_path).expect("parse MIRAX sentinel");
    let first = slide
        .read_associated("thumbnail")
        .expect("read thumbnail once");
    let second = slide
        .read_associated("thumbnail")
        .expect("read thumbnail twice");
    assert_eq!(first.width, second.width);
    assert_eq!(first.height, second.height);
    assert_eq!(
        MIRAX_ASSOCIATED_CACHE_HITS.load(Ordering::Relaxed),
        1,
        "second thumbnail read should hit the cache"
    );
}

#[test]
fn truncated_quickhash_range_returns_contextual_unexpected_eof_without_prefix_hash() {
    let mut source = tempfile::NamedTempFile::new().expect("temporary MIRAX data file");
    source.write_all(b"abcd").expect("write MIRAX data");
    source.flush().expect("flush MIRAX data");
    let mut files = HashMap::new();
    let mut quickhash = Quickhash1::new();

    let error =
        helpers::quickhash_file_part_cached(&mut quickhash, &mut files, source.path(), 2, 4)
            .expect_err("declared MIRAX range past EOF must not produce a prefix hash");

    let WsiError::IoWithPath { source: io, path } = error else {
        panic!("expected contextual I/O error, got {error:?}");
    };
    assert_eq!(io.kind(), std::io::ErrorKind::UnexpectedEof);
    assert_eq!(path, source.path());
    assert!(io.to_string().contains("offset 2"), "{io}");
    assert!(io.to_string().contains("4 bytes"), "{io}");
    assert_eq!(
        quickhash.finish(),
        Quickhash1::new().finish(),
        "failed range must not commit a prefix into the dataset hash"
    );
}