molframe-core 0.1.0

Core types, storage and analysis contracts for molframe
Documentation
use super::*;

#[test]
fn plain_bytes_are_returned_as_they_were_given() {
    let input = InputBuffer::from_bytes(b"data_test\n".to_vec());
    assert_eq!(input.as_bytes(), b"data_test\n");
    assert_eq!(input.len(), 10);
    assert!(!input.is_empty());
    assert_eq!(input.origin(), None);
}

#[test]
fn taking_owned_bytes_preserves_the_vector_allocation() {
    let bytes = vec![1_u8, 2, 3, 4];
    let pointer = bytes.as_ptr();
    let input = InputBuffer::from_bytes(bytes);

    assert_eq!(input.as_bytes().as_ptr(), pointer);
    assert_eq!(input.as_bytes(), [1, 2, 3, 4]);
}

#[test]
fn a_compression_container_is_recognised_from_its_leading_bytes() {
    assert_eq!(Compression::sniff(&[0x1f, 0x8b, 0x08]), Compression::Gzip);
    assert_eq!(
        Compression::sniff(&[0x28, 0xb5, 0x2f, 0xfd]),
        Compression::Zstd
    );
    assert_eq!(Compression::sniff(b"data_x"), Compression::None);
    assert_eq!(Compression::sniff(&[]), Compression::None);
}

#[test]
fn an_input_can_name_where_it_came_from_for_a_diagnostic_to_quote() {
    let input = InputBuffer::from_bytes(Vec::new()).with_origin("entry.cif");
    assert_eq!(input.origin(), Some("entry.cif"));
    assert!(input.is_empty());
}

#[test]
fn a_buffer_that_expands_beyond_the_limit_is_refused_rather_than_obeyed() {
    let limits = Limits {
        decompressed_bytes: 100,
        ..Limits::default()
    };
    assert!(check_expansion(101, 10, limits).is_err());
    assert!(check_expansion(99, 10, limits).is_ok());
}

#[test]
fn a_buffer_that_expands_faster_than_the_ratio_allows_is_refused() {
    let limits = Limits {
        compression_ratio: 10,
        ..Limits::default()
    };
    assert!(
        check_expansion(100, 10, limits).is_ok(),
        "tenfold is at the limit"
    );
    assert!(check_expansion(2_000, 10, limits).is_err());
}

#[test]
fn standard_limits_do_not_impose_historical_dataset_caps() {
    let limits = Limits::default();
    assert_eq!(limits.decompressed_bytes, u64::MAX);
    assert_eq!(limits.rows_per_category, u64::MAX);
    assert_eq!(limits.dictionary_entries, u32::MAX - 1);
}

#[test]
fn opening_a_file_that_is_not_there_reports_a_resource_finding() {
    let error = InputBuffer::open("no/such/entry.cif", Limits::default());
    assert_eq!(error.err().map(|finding| finding.code()), Some(Code::E1901));
}

#[test]
fn a_stream_is_read_and_bounded_like_a_file() {
    let input = InputBuffer::from_reader(std::io::Cursor::new(b"data_stream\n"), Limits::default());
    assert_eq!(
        input.as_ref().map(InputBuffer::as_bytes),
        Ok(b"data_stream\n".as_slice())
    );

    let limits = Limits {
        decompressed_bytes: 4,
        ..Limits::default()
    };
    assert!(InputBuffer::from_reader(std::io::Cursor::new(b"12345"), limits).is_err());
}

#[cfg(feature = "mmap")]
#[test]
fn a_stream_uses_disk_backed_storage_instead_of_retaining_an_input_vector() {
    let input =
        match InputBuffer::from_reader(std::io::Cursor::new(b"data_stream\n"), Limits::default()) {
            Ok(input) => input,
            Err(finding) => panic!("stream failed: {finding}"),
        };
    assert_eq!(input.kind(), InputKind::Mapped);
    assert_eq!(input.as_bytes(), b"data_stream\n");
}

#[cfg(feature = "mmap")]
#[test]
fn a_large_stream_is_consumed_through_a_fixed_size_window() {
    use std::io::Read;
    use std::sync::Arc;
    use std::sync::atomic::{AtomicUsize, Ordering};

    struct RepeatingReader {
        remaining: usize,
        largest_request: Arc<AtomicUsize>,
    }

    impl Read for RepeatingReader {
        fn read(&mut self, destination: &mut [u8]) -> std::io::Result<usize> {
            self.largest_request
                .fetch_max(destination.len(), Ordering::Relaxed);
            let count = destination.len().min(self.remaining);
            destination[..count].fill(b'x');
            self.remaining -= count;
            Ok(count)
        }
    }

    let largest_request = Arc::new(AtomicUsize::new(0));
    let reader = RepeatingReader {
        remaining: 2 * 1024 * 1024,
        largest_request: Arc::clone(&largest_request),
    };
    let input = match InputBuffer::from_reader(reader, Limits::default()) {
        Ok(input) => input,
        Err(finding) => panic!("stream failed: {finding}"),
    };
    assert_eq!(input.kind(), InputKind::Mapped);
    assert_eq!(input.len(), 2 * 1024 * 1024);
    assert!(largest_request.load(Ordering::Relaxed) <= 64 * 1024);
}

#[cfg(all(feature = "gzip", feature = "mmap"))]
#[test]
fn a_compressed_stream_maps_only_the_finished_expansion() {
    use flate2::Compression as GzipLevel;
    use flate2::write::GzEncoder;
    use std::io::Write as _;

    let mut compressed_writer = GzEncoder::new(Vec::new(), GzipLevel::fast());
    if let Err(error) = compressed_writer.write_all(b"data_stream\n") {
        panic!("compression failed: {error}")
    }
    let compressed_bytes = match compressed_writer.finish() {
        Ok(bytes) => bytes,
        Err(error) => panic!("finish failed: {error}"),
    };
    let input =
        match InputBuffer::from_reader(std::io::Cursor::new(compressed_bytes), Limits::default()) {
            Ok(input) => input,
            Err(finding) => panic!("stream failed: {finding}"),
        };
    assert_eq!(input.kind(), InputKind::Mapped);
    assert_eq!(input.as_bytes(), b"data_stream\n");
}

#[cfg(feature = "mmap")]
#[test]
fn a_large_plain_local_file_is_parsed_directly_from_a_mapping() {
    use std::io::Write as _;

    let path = std::env::temp_dir().join(format!("molframe-input-{}", std::process::id()));
    let mut file = match File::create(&path) {
        Ok(file) => file,
        Err(error) => panic!("create failed: {error}"),
    };
    let Ok(mmap_min_bytes) = usize::try_from(MMAP_MIN_BYTES) else {
        panic!("mmap threshold fits this platform")
    };
    let mut bytes = vec![b' '; mmap_min_bytes];
    bytes[..10].copy_from_slice(b"data_test\n");
    if let Err(error) = file.write_all(&bytes) {
        panic!("write failed: {error}")
    }
    drop(file);

    let input = match InputBuffer::open(&path, Limits::default()) {
        Ok(input) => input,
        Err(finding) => panic!("open failed: {finding}"),
    };
    assert_eq!(input.kind(), InputKind::Mapped);
    assert!(input.as_bytes().starts_with(b"data_test\n"));
    let _removed = std::fs::remove_file(path);
}

#[cfg(feature = "mmap")]
#[test]
fn a_small_local_file_avoids_mapping_overhead() {
    use std::io::Write as _;

    let path = std::env::temp_dir().join(format!("molframe-small-input-{}", std::process::id()));
    let mut file = match File::create(&path) {
        Ok(file) => file,
        Err(error) => panic!("create failed: {error}"),
    };
    if let Err(error) = file.write_all(b"data_test\n") {
        panic!("write failed: {error}")
    }
    drop(file);

    let input = match InputBuffer::open(&path, Limits::default()) {
        Ok(input) => input,
        Err(finding) => panic!("open failed: {finding}"),
    };
    assert_eq!(input.kind(), InputKind::Owned);
    let _removed = std::fs::remove_file(path);
}

#[cfg(all(feature = "gzip", feature = "mmap"))]
#[test]
fn a_gzip_file_expands_into_private_mapped_storage() {
    use flate2::Compression as GzipLevel;
    use flate2::write::GzEncoder;
    use std::io::Write as _;

    let path = std::env::temp_dir().join(format!("molframe-gzip-input-{}", std::process::id()));
    let file = match File::create(&path) {
        Ok(file) => file,
        Err(error) => panic!("create failed: {error}"),
    };
    let mut encoder = GzEncoder::new(file, GzipLevel::fast());
    if let Err(error) = encoder.write_all(b"data_test\n") {
        panic!("compression failed: {error}")
    }
    if let Err(error) = encoder.finish() {
        panic!("finish failed: {error}")
    }

    let input = match InputBuffer::open(&path, Limits::default()) {
        Ok(input) => input,
        Err(finding) => panic!("open failed: {finding}"),
    };
    assert_eq!(input.kind(), InputKind::Mapped);
    assert_eq!(input.as_bytes(), b"data_test\n");
    let _removed = std::fs::remove_file(path);
}

#[cfg(all(feature = "mmap", feature = "zstd"))]
#[test]
fn a_zstd_file_expands_into_private_mapped_storage() {
    let path = std::env::temp_dir().join(format!("molframe-zstd-input-{}", std::process::id()));
    let encoded = match zstd::stream::encode_all(b"data_test\n".as_slice(), 1) {
        Ok(encoded) => encoded,
        Err(error) => panic!("compression failed: {error}"),
    };
    if let Err(error) = std::fs::write(&path, encoded) {
        panic!("write failed: {error}")
    }

    let input = match InputBuffer::open(&path, Limits::default()) {
        Ok(input) => input,
        Err(finding) => panic!("open failed: {finding}"),
    };
    assert_eq!(input.kind(), InputKind::Mapped);
    assert_eq!(input.as_bytes(), b"data_test\n");
    let _removed = std::fs::remove_file(path);
}
#[test]
fn owner_backed_input_adopts_stable_bytes_and_clones_only_the_handle() {
    let owner: std::sync::Arc<[u8]> = std::sync::Arc::from(&b"data_block"[..]);
    let pointer = owner.as_ptr();
    let input = InputBuffer::from_owner(std::sync::Arc::clone(&owner));
    assert_eq!(input.kind(), InputKind::OwnerBacked);
    assert_eq!(input.as_bytes().as_ptr(), pointer);
    let clone = input.clone();
    drop(input);
    assert_eq!(clone.as_bytes(), &b"data_block"[..]);
    assert_eq!(clone.as_bytes().as_ptr(), pointer);
}