haem-frames 0.1.1

Shared framing and embedding messages for isolated workers
Documentation
use haem_frames::frame;
use std::io::{self, Cursor, Read};
use std::num::NonZeroU32;

#[test]
fn tagged_frame_is_big_endian_and_counts_the_tag() -> io::Result<()> {
    let mut bytes = Vec::new();
    frame::write(&mut bytes, 4, b"{}", NonZeroU32::MAX)?;
    assert_eq!(bytes, b"\0\0\0\x03\x04{}");
    assert_eq!(
        frame::read(&mut Cursor::new(bytes), NonZeroU32::MAX)?,
        Some(b"\x04{}".to_vec())
    );
    Ok(())
}

#[test]
fn cut_and_oversized_frames_never_adopt_their_tail() -> io::Result<()> {
    let limit = NonZeroU32::MIN;
    let mut oversized = Cursor::new(b"\0\0\0\x02ab".to_vec());
    assert!(frame::read(&mut oversized, limit).is_err());
    assert_eq!(oversized.position(), 4);
    let mut tail = Vec::new();
    oversized.read_to_end(&mut tail)?;
    assert_eq!(tail, b"ab");
    Ok(())
}

#[test]
fn clean_eof_differs_from_a_cut_header_or_payload() -> io::Result<()> {
    assert_eq!(frame::read(&mut Cursor::new([]), NonZeroU32::MAX)?, None);
    for bytes in [
        b"\0".as_slice(),
        b"\0\0\0\x02a".as_slice(),
        b"\0\0\0\0".as_slice(),
    ] {
        assert!(frame::read(&mut Cursor::new(bytes), NonZeroU32::MAX).is_err());
    }
    Ok(())
}

#[test]
fn rejected_write_emits_no_header() {
    let mut output = Vec::new();
    assert!(frame::write(&mut output, 4, b"{}", NonZeroU32::MIN).is_err());
    assert!(output.is_empty());
}

/// Test (a): a header naming more than the limit is its own typed fault,
/// carrying the length and the limit, and the body is never read.
#[test]
fn an_oversized_header_is_a_typed_fault_found_before_the_body() -> Result<(), String> {
    let limit = NonZeroU32::MIN.saturating_add(9);
    let mut input = Cursor::new(b"\0\0\0\x0bbody never read".to_vec());
    let error = frame::read(&mut input, limit)
        .err()
        .ok_or("an oversized frame was read")?;
    assert_eq!(
        frame::Fault::of(&error),
        Some(frame::Fault::TooLarge {
            length: 11,
            limit: 10
        })
    );
    assert_eq!(input.position(), 4, "the reader went past the header");
    Ok(())
}

/// An empty or cut frame is malformed, a fault apart from too large.
#[test]
fn an_empty_or_cut_frame_is_a_malformed_fault_not_too_large() {
    for (bytes, fault) in [
        (b"\0\0\0\0".as_slice(), frame::Fault::Empty),
        (b"\0".as_slice(), frame::Fault::Cut),
        (b"\0\0\0\x02a".as_slice(), frame::Fault::Cut),
    ] {
        let read = frame::read(&mut Cursor::new(bytes), NonZeroU32::MAX);
        assert_eq!(read.err().as_ref().and_then(frame::Fault::of), Some(fault));
    }
}