wireshift-core 0.1.1

Core typed operations, buffers, and backend traits for wireshift
Documentation
#![allow(missing_docs)]

use std::fs::{self, File, OpenOptions};

use tempfile::{tempdir, NamedTempFile};
use wireshift::ops::{Read, Write};
use wireshift::{BackendPreference, Ring, RingConfig};

fn fallback_ring() -> Ring {
    Ring::new(RingConfig::default().with_backend(BackendPreference::Fallback)).expect("ring")
}

#[test]
fn batch_hundred_reads_complete_with_correct_data() {
    let dir = tempdir().expect("dir");
    let ring = Ring::new(
        RingConfig::default()
            .with_backend(BackendPreference::Fallback)
            .with_queue_depth(32),
    )
    .expect("ring");
    let mut paths = Vec::new();
    for idx in 0..100 {
        let path = dir.path().join(format!("file-{idx}.txt"));
        fs::write(&path, format!("data-{idx:03}")).expect("write");
        paths.push(path);
    }

    let ops = paths
        .iter()
        .map(|path| Read::new(File::open(path).expect("open"), 0, 16).expect("op"));
    let results = ring.batch(ops).expect("batch");

    assert_eq!(results.len(), 100);
    for (idx, result) in results.iter().enumerate() {
        assert_eq!(result.buffer.filled(), format!("data-{idx:03}").as_bytes());
    }
}

#[test]
fn batch_write_then_readback_matches_each_payload() {
    let dir = tempdir().expect("dir");
    let ring = fallback_ring();
    let mut writes = Vec::new();
    let mut expected = Vec::new();

    for idx in 0..24 {
        let path = dir.path().join(format!("batch-{idx}.txt"));
        let file = OpenOptions::new()
            .create(true)
            .truncate(true)
            .write(true)
            .open(&path)
            .expect("open");
        let payload = format!("batch-{idx}-payload");
        expected.push((path, payload.clone()));
        writes.push(Write::new(file, 0, payload.as_bytes()).expect("write"));
    }

    let results = ring.batch(writes).expect("batch");
    assert!(results.iter().all(|bytes| *bytes > 0));
    for (path, payload) in expected {
        assert_eq!(fs::read(path).expect("read"), payload.as_bytes().to_vec());
    }
}

#[test]
fn batch_returns_first_operation_error_with_context() {
    let temp = NamedTempFile::new().expect("temp");
    let ring = fallback_ring();
    let file = File::open(temp.path()).expect("open");
    let dir = File::open(".").expect("dir");

    let error = ring
        .batch([
            Read::new(file, 0, 4).expect("read"),
            Read::new(dir, 0, 4).expect("read"),
        ])
        .expect_err("second read must fail");

    assert!(error.to_string().contains("Fix:"));
}

#[test]
fn batch_can_process_more_than_one_thousand_operations() {
    let dir = tempdir().expect("dir");
    let ring = Ring::new(
        RingConfig::default()
            .with_backend(BackendPreference::Fallback)
            .with_queue_depth(64),
    )
    .expect("ring");
    let mut paths = Vec::new();
    for idx in 0..1_024 {
        let path = dir.path().join(format!("bulk-{idx}.txt"));
        fs::write(&path, b"x").expect("write");
        paths.push(path);
    }

    let results = ring
        .batch(
            paths
                .iter()
                .map(|path| Read::new(File::open(path).expect("open"), 0, 1).expect("op")),
        )
        .expect("batch");

    assert_eq!(results.len(), 1_024);
    assert!(results.iter().all(|result| result.buffer.filled() == b"x"));
}