#![cfg(feature = "experimental-write")] extern crate fixcol; use std::io::{Error as IoError, ErrorKind, Write}; use fixcol::error::Error; use fixcol::{WriteFixed, WriteFixedAll}; /// A writable buffer that accepts a maximum number of bytes and then errors /// /// It's useful when you want to test a system's response to I/O errors struct FakeBuffer { max_size: usize, data_size: usize, data: Vec, } impl FakeBuffer { pub fn new(size: usize) -> Self { Self { max_size: size, data_size: 0, data: Vec::new(), } } pub fn as_slice(&self) -> &[u8] { &self.data.as_slice() } pub fn as_string(&self) -> Option { std::str::from_utf8(&self.as_slice()) .ok() .map(|x| x.to_string()) } } impl Write for FakeBuffer { fn write(&mut self, buf: &[u8]) -> std::io::Result { if self.data_size + buf.len() <= self.max_size { let written = self.data.write(buf).unwrap(); self.data_size += written; Ok(written) } else { let err = IoError::new(ErrorKind::WriteZero, "Out of space"); Err(err) } } fn flush(&mut self) -> std::io::Result<()> { Ok(()) } } // // Tests of struct writes // const EXPECTED_STRUCT_TEXT: &'static str = r#"91 115 221159 92 0 151171 "#; #[derive(Debug, Eq, PartialEq, WriteFixed)] struct Point { #[fixcol(width = 3)] x: u8, #[fixcol(width = 3)] y: u8, } impl Point { pub fn new(x: u8, y: u8) -> Self { Self { x, y } } pub fn sample() -> Vec { vec![ Point::new(91, 115), Point::new(221, 159), Point::new(92, 0), Point::new(151, 171), ] } } #[test] fn struct_normal_buffer_control() { let points = Point::sample(); let mut buf: Vec = Vec::new(); let res = points.write_fixed_all(&mut buf); let text = std::str::from_utf8(buf.as_slice()).unwrap().to_string(); assert!(res.is_ok()); assert_eq!(text, EXPECTED_STRUCT_TEXT); } #[test] fn struct_adequate_size_control() { let points = Point::sample(); let mut buf = FakeBuffer::new(50); let res = points.write_fixed_all(&mut buf); let text = buf.as_string().unwrap(); assert!(res.is_ok()); assert_eq!(text, EXPECTED_STRUCT_TEXT); } #[test] fn struct_out_of_space_test() { let points = Point::sample(); let mut buf = FakeBuffer::new(20); let res = points.write_fixed_all(&mut buf); assert!(res.is_err()); match res.unwrap_err() { Error::DataError(_) => panic!("Should have had I/O Error"), Error::IoError(e) => { assert_eq!(e.to_string(), "Out of space"); assert_eq!(e.kind(), ErrorKind::WriteZero); } } } // // Test of enum writes // #[derive(Debug, Eq, PartialEq, WriteFixed)] #[fixcol(key_width = 1)] enum Datum { #[fixcol(key = "S")] Scalar(#[fixcol(width = 10, align = "right")] u16), #[fixcol(key = "P")] Pair { #[fixcol(width = 5)] x: u16, #[fixcol(width = 5)] y: u16, }, #[fixcol(key = "U")] Unit, } impl Datum { pub fn sample() -> Vec { vec![ Datum::Pair { x: 53542, y: 72 }, Datum::Unit, Datum::Unit, Datum::Scalar(1234), ] } } const EXPECTED_ENUM_TEXT: &'static str = r#"P5354272 U U S 1234 "#; #[test] fn enum_normal_buffer_control() { let data = Datum::sample(); let mut buf: Vec = Vec::new(); let res = data.write_fixed_all(&mut buf); let text = std::str::from_utf8(buf.as_slice()).unwrap().to_string(); assert!(res.is_ok()); assert_eq!(text, EXPECTED_ENUM_TEXT); } #[test] fn enum_adequate_size_control() { let data = Datum::sample(); let mut buf = FakeBuffer::new(50); let res = data.write_fixed_all(&mut buf); let text = buf.as_string().unwrap(); assert!(res.is_ok()); assert_eq!(text, EXPECTED_ENUM_TEXT); } #[test] fn out_of_space_in_struct_variant() { let data = Datum::sample(); let mut buf = FakeBuffer::new(7); let res = data.write_fixed_all(&mut buf); assert!(res.is_err()); match res.unwrap_err() { Error::DataError(_) => panic!("Should have had I/O Error"), Error::IoError(e) => { assert_eq!(e.to_string(), "Out of space"); assert_eq!(e.kind(), ErrorKind::WriteZero); } } // Confirm we failed in the struct variant let expected = "P53542"; assert_eq!(buf.as_string().unwrap(), expected); } #[test] fn out_of_space_in_tuple_variant() { let data = Datum::sample(); let mut buf = FakeBuffer::new(15); let res = data.write_fixed_all(&mut buf); assert!(res.is_err()); match res.unwrap_err() { Error::DataError(_) => panic!("Should have had I/O Error"), Error::IoError(e) => { assert_eq!(e.to_string(), "Out of space"); assert_eq!(e.kind(), ErrorKind::WriteZero); } } // Confirm we failed in the tuple variant let expected = "P5354272 \nU\nU"; assert_eq!(buf.as_string().unwrap(), expected); } #[test] fn out_of_space_in_unit_variant() { let data = Datum::sample(); let mut buf = FakeBuffer::new(14); let res = data.write_fixed_all(&mut buf); assert!(res.is_err()); match res.unwrap_err() { Error::DataError(_) => panic!("Should have had I/O Error"), Error::IoError(e) => { assert_eq!(e.to_string(), "Out of space"); assert_eq!(e.kind(), ErrorKind::WriteZero); } } // Confirm we failed in the unit variant let expected = "P5354272 \nU\n"; assert_eq!(buf.as_string().unwrap(), expected); }