xmodem 0.4.0

An implementation of the XMODEM file-transfer protocol.
Documentation
//! Test against the `sx` program, and itself
extern crate rand;
extern crate tempfile;
extern crate xmodem;

use rand::{RngCore, rng};
use std::io::{self, Read, Seek, Write};
use std::process::{ChildStdin, ChildStdout, Command, Stdio};
use tempfile::NamedTempFile;
use xmodem::{BlockLength, Checksum, Xmodem};

struct ChildStdInOut {
    stdin: ChildStdin,
    stdout: ChildStdout,
}

impl Read for ChildStdInOut {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.stdout.read(buf)
    }
}

impl Write for ChildStdInOut {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.stdin.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.stdin.flush()
    }
}

#[cfg(test)]
fn xmodem_recv(checksum_mode: Checksum, block_length: BlockLength, data_len: usize) {
    let mut data = vec![0; data_len];
    rng().fill_bytes(&mut data);

    let mut send_file = NamedTempFile::new().unwrap();
    send_file.write_all(&data).unwrap();

    let mut send_builder = Command::new("sb");
    send_builder.arg("--xmodem");
    match block_length {
        BlockLength::OneK => {
            send_builder.arg("--1k");
        }
        _ => {}
    }
    let send = send_builder
        .arg(send_file.path())
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .stderr(Stdio::null())
        .spawn()
        .unwrap();

    let tx_stream = send.stdin.unwrap();
    let rx_stream = send.stdout.unwrap();
    let mut serial_dev = ChildStdInOut {
        stdin: tx_stream,
        stdout: rx_stream,
    };

    let mut xmodem = Xmodem::new();
    let mut recv_data = Vec::new();
    let bytes = xmodem
        .recv(&mut serial_dev, &mut recv_data, checksum_mode)
        .unwrap();
    assert_eq!(bytes, (data_len + 127) & !127);

    let mut sent_data = Vec::new();
    send_file.seek(std::io::SeekFrom::Start(0)).unwrap();
    send_file.read_to_end(&mut sent_data).unwrap();
    let mut padded_data = sent_data.clone();
    for _ in 0..(128 - sent_data.len() % 128) {
        padded_data.push(0x1a);
    }
    assert_eq!(padded_data, recv_data);
}

#[test]
fn xmodem_recv_standard() {
    xmodem_recv(Checksum::Standard, BlockLength::Standard, 2000);
}

#[test]
fn xmodem_recv_crc() {
    xmodem_recv(Checksum::CRC16, BlockLength::Standard, 2000);
}

#[test]
fn xmodem_recv_1k_crc() {
    xmodem_recv(Checksum::CRC16, BlockLength::OneK, 8500);
}

#[test]
fn xmodem_recv_long() {
    xmodem_recv(Checksum::CRC16, BlockLength::Standard, 50000);
}

#[test]
fn xmodem_send_standard() {
    let data_len = 2000;
    let mut data = vec![0; data_len];
    rng().fill_bytes(&mut data);

    let mut recv_file = NamedTempFile::new().unwrap();
    let recv = Command::new("rb")
        .arg("--xmodem")
        .arg(recv_file.path())
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .stderr(Stdio::null())
        .spawn()
        .unwrap();

    let tx_stream = recv.stdin.unwrap();
    let rx_stream = recv.stdout.unwrap();
    let mut serial_dev = ChildStdInOut {
        stdin: tx_stream,
        stdout: rx_stream,
    };

    let mut xmodem = Xmodem::new();
    let bytes = xmodem.send(&mut serial_dev, &mut &data[..]).unwrap();
    assert_eq!(bytes, data_len);

    let mut received_data = Vec::new();
    recv_file.read_to_end(&mut received_data).unwrap();
    let mut padded_data = data.clone();
    for _ in 0..(128 - data.len() % 128) {
        padded_data.push(0x1a);
    }
    assert_eq!(received_data, padded_data);
}

#[test]
fn xmodem_send_crc() {
    let data_len = 2000;
    let mut data = vec![0; data_len];
    rng().fill_bytes(&mut data);

    let mut recv_file = NamedTempFile::new().unwrap();
    let recv = Command::new("rb")
        .arg("--xmodem")
        .arg("--with-crc")
        .arg(recv_file.path())
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .stderr(Stdio::null())
        .spawn()
        .unwrap();

    let tx_stream = recv.stdin.unwrap();
    let rx_stream = recv.stdout.unwrap();
    let mut serial_dev = ChildStdInOut {
        stdin: tx_stream,
        stdout: rx_stream,
    };

    let mut xmodem = Xmodem::new();
    xmodem.send(&mut serial_dev, &mut &data[..]).unwrap();

    let mut received_data = Vec::new();
    recv_file.read_to_end(&mut received_data).unwrap();
    let mut padded_data = data.clone();

    for _ in 0..(128 - data.len() % 128) {
        padded_data.push(0x1a);
    }
    assert_eq!(received_data, padded_data);
}