#![warn(rust_2018_idioms)]
use std::error::Error as _;
#[cfg(feature = "std")]
use std::io;
use smol_bytes::{
BufTryGetError, Buffer, Bytes, BytesMut, FromBytesError, FromUtf8Error, InvalidIntegerLength,
OutOfBounds, RangeOutOfBounds, TryGetError, TryGetIntegerError, TryPutError, TryPutIntegerError,
Utf8Bytes, Utf8BytesMut, Utf8IndexError,
};
#[test]
fn try_put_error_display_and_io() {
let big = [0u8; 63];
let err: TryPutError = Buffer::try_from(&big[..]).unwrap_err();
assert_eq!(err.requested, 63);
assert_eq!(err.available, 62);
assert_eq!(
err.to_string(),
"Not enough bytes remaining in buffer to write value (requested 63 but only 62 available)"
);
assert!(err.source().is_none());
#[cfg(feature = "std")]
{
let io_err: io::Error = err.into();
assert_eq!(io_err.kind(), io::ErrorKind::Other);
assert!(
io_err
.to_string()
.contains("Not enough bytes remaining in buffer to write value")
);
}
}
#[test]
fn invalid_integer_length_display_from_and_io() {
let err = InvalidIntegerLength(9);
assert_eq!(
err.to_string(),
"invalid integer length: 9 (must be less or equal to 8)"
);
let promoted: TryPutIntegerError = err.into();
assert_eq!(promoted, TryPutIntegerError::InvalidLength(err));
assert_eq!(
promoted.to_string(),
"invalid integer length: 9 (must be less or equal to 8)"
);
#[cfg(feature = "std")]
{
let io_err: io::Error = err.into();
assert_eq!(io_err.kind(), io::ErrorKind::InvalidInput);
assert!(io_err.to_string().contains("invalid integer length: 9"));
}
}
#[test]
fn try_put_integer_error_not_enough_space() {
let mut buf = Buffer::try_from(&[0u8; 60][..]).unwrap();
let err = buf.try_put_uint(0x010203, 3).unwrap_err();
assert_eq!(
err,
TryPutIntegerError::NotEnoughSpace(TryPutError {
requested: 3,
available: 2,
})
);
assert_eq!(
err.to_string(),
"Not enough bytes remaining in buffer to write value (requested 3 but only 2 available)"
);
#[cfg(feature = "std")]
{
let io_err: io::Error = err.into();
assert_eq!(io_err.kind(), io::ErrorKind::Other);
}
}
#[test]
fn try_put_integer_error_invalid_length() {
let mut buf = Buffer::new();
let err = buf.try_put_uint(0, 9).unwrap_err();
assert_eq!(
err,
TryPutIntegerError::InvalidLength(InvalidIntegerLength(9))
);
assert_eq!(
err.to_string(),
"invalid integer length: 9 (must be less or equal to 8)"
);
}
#[test]
fn out_of_bounds_display_and_io() {
let mut buf = Buffer::try_from(&b"abc"[..]).unwrap();
let err: OutOfBounds = buf.try_advance(5).unwrap_err();
assert_eq!(err, OutOfBounds::new(5, 3));
assert_eq!(
err.to_string(),
"index out of bounds: requested 5 but only 3 available"
);
let mut buf = Buffer::try_from(&b"abc"[..]).unwrap();
assert_eq!(buf.try_split_off(10).unwrap_err(), OutOfBounds::new(10, 3));
#[cfg(feature = "std")]
{
let io_err: io::Error = err.into();
assert_eq!(io_err.kind(), io::ErrorKind::InvalidInput);
assert!(
io_err
.to_string()
.contains("index out of bounds: requested 5")
);
}
}
#[test]
fn range_out_of_bounds_reversed_and_oob() {
let buf = Buffer::try_from(&b"hello"[..]).unwrap();
let (start, end) = (3usize, 2usize);
let err: RangeOutOfBounds = buf.try_slice(start..end).unwrap_err();
assert_eq!(err, RangeOutOfBounds::new(3, 2, 5));
assert_eq!(
err.to_string(),
"range out of bounds: requested 3..2 but only 5 available"
);
let err2 = buf.try_slice(0..10).unwrap_err();
assert_eq!(err2, RangeOutOfBounds::new(0, 10, 5));
#[cfg(feature = "std")]
{
let io_err: io::Error = err.into();
assert_eq!(io_err.kind(), io::ErrorKind::InvalidInput);
assert!(
io_err
.to_string()
.contains("range out of bounds: requested 3..2")
);
}
}
#[test]
fn utf8_error_invalid_char_boundary() {
let mut s = Utf8BytesMut::from("café");
let err: Utf8IndexError = s.try_split_to(4).unwrap_err();
assert_eq!(err, Utf8IndexError::InvalidCharBoundary { index: 4 });
assert_eq!(
err.to_string(),
"index 4 does not lie on a UTF-8 character boundary"
);
#[cfg(feature = "std")]
{
let io_err: io::Error = err.into();
assert_eq!(io_err.kind(), io::ErrorKind::InvalidInput);
assert!(
io_err
.to_string()
.contains("does not lie on a UTF-8 character boundary")
);
}
}
#[test]
fn utf8_index_error_preserves_index_and_range_context() {
let mut s = Utf8Bytes::from("hi");
let index_error: Utf8IndexError = s.try_split_to(10).unwrap_err();
assert_eq!(
index_error,
Utf8IndexError::IndexOutOfBounds(OutOfBounds::new(10, 2))
);
assert!(index_error.source().is_some());
let (start, end) = (3, 2);
let range_error: Utf8IndexError = s.try_slice(start..end).unwrap_err();
assert_eq!(
range_error,
Utf8IndexError::RangeOutOfBounds(RangeOutOfBounds::new(3, 2, 2))
);
assert!(range_error.source().is_some());
}
#[test]
fn variable_width_read_errors_are_typed_and_atomic() {
let mut buffer = Buffer::from(*b"abc");
let before = buffer.as_slice().to_vec();
assert_eq!(
buffer.try_get_uint(9),
Err(TryGetIntegerError::InvalidLength(InvalidIntegerLength(9)))
);
assert_eq!(buffer.as_slice(), before);
assert_eq!(
buffer.try_get_int_le(4),
Err(TryGetIntegerError::NotEnoughBytes(TryGetError {
requested: 4,
available: 3,
}))
);
assert_eq!(buffer.as_slice(), before);
for result in [
buffer.try_get_uint(9).map(|_| ()),
buffer.try_get_uint_le(9).map(|_| ()),
buffer.try_get_uint_ne(9).map(|_| ()),
buffer.try_get_int(9).map(|_| ()),
buffer.try_get_int_le(9).map(|_| ()),
buffer.try_get_int_ne(9).map(|_| ()),
] {
assert_eq!(
result,
Err(TryGetIntegerError::InvalidLength(InvalidIntegerLength(9)))
);
}
assert_eq!(buffer.as_slice(), before);
let crate_error = TryGetError {
requested: 4,
available: 3,
};
let upstream: BufTryGetError = crate_error.into();
assert_eq!(TryGetError::from(upstream), crate_error);
let mut trait_buffer = Buffer::from(*b"abc");
let upstream: BufTryGetError = bytes::Buf::try_get_uint(&mut trait_buffer, 4).unwrap_err();
assert_eq!(TryGetError::from(upstream), crate_error);
}
#[test]
fn owned_utf8_errors_recover_original_storage_without_copying() {
let mut raw = BytesMut::from(vec![b'x'; 80]);
raw.as_mut_slice()[79] = 0xff;
let pointer = raw.as_slice().as_ptr();
let error: FromUtf8Error<BytesMut> = match Utf8BytesMut::from_utf8(raw) {
Err(error) => error,
Ok(_) => panic!("invalid bytes unexpectedly validated"),
};
assert_eq!(error.as_bytes().as_ptr(), pointer);
assert!(error.utf8_error().valid_up_to() < error.as_bytes().len());
assert!(error.source().is_some());
let raw = error.into_bytes();
assert_eq!(raw.as_slice().as_ptr(), pointer);
assert_eq!(raw.as_slice()[79], 0xff);
let mut raw_bytes = vec![b'x'; 80];
raw_bytes[79] = 0xff;
let mut raw = Bytes::from(raw_bytes);
let _ = raw.split_to(5);
let pointer = raw.as_slice().as_ptr();
let error = match Utf8Bytes::from_utf8(raw) {
Err(error) => error,
Ok(_) => panic!("invalid bytes unexpectedly validated"),
};
assert_eq!(error.as_bytes().as_ptr(), pointer);
assert_eq!(error.into_bytes().as_slice().as_ptr(), pointer);
let buffer = Buffer::try_from(&[b'a', 0xff][..]).unwrap();
let error = match smol_bytes::Utf8Buffer::from_utf8(buffer) {
Err(error) => error,
Ok(_) => panic!("invalid bytes unexpectedly validated"),
};
assert_eq!(error.as_bytes(), b"a\xff");
assert_eq!(error.into_bytes().as_slice(), b"a\xff");
}
#[test]
fn raw_bytes_try_into_mut_returns_the_same_strategy_on_failure() {
let original = Bytes::from_static(&[b'x'; 80]);
let recovered: Bytes = match original.try_into_mut() {
Ok(_) => panic!("static bytes unexpectedly became mutable without a copy"),
Err(bytes) => bytes,
};
assert_eq!(recovered.as_slice(), &[b'x'; 80]);
}
#[test]
fn from_bytes_error_invalid_utf8() {
let raw: Vec<u8> = vec![0xFF, 0xFE];
let err: FromBytesError = smol_bytes::Utf8Buffer::try_from(raw.as_slice()).unwrap_err();
assert!(matches!(err, FromBytesError::InvalidUtf8(_)));
let text = err.to_string();
assert!(text.starts_with("invalid UTF-8: "), "got: {text}");
let inner = core::str::from_utf8(raw.as_slice()).unwrap_err();
assert!(text.contains(&inner.to_string()));
assert!(err.source().is_none());
let direct: FromBytesError = inner.into();
assert_eq!(direct, err);
#[cfg(feature = "std")]
{
let io_err: io::Error = err.into();
assert_eq!(io_err.kind(), io::ErrorKind::InvalidInput);
assert!(io_err.to_string().contains("invalid UTF-8"));
}
}
#[test]
fn from_bytes_error_too_large() {
let long = [b'a'; 63];
let err: FromBytesError = smol_bytes::Utf8Buffer::try_from(&long[..]).unwrap_err();
assert!(matches!(err, FromBytesError::TooLarge(_)));
assert_eq!(
err.to_string(),
"Not enough bytes remaining in buffer to write value (requested 63 but only 62 available)"
);
assert!(err.source().is_none());
let put_err = TryPutError {
requested: 63,
available: 62,
};
let converted: FromBytesError = put_err.into();
assert_eq!(converted, err);
#[cfg(feature = "std")]
{
let io_err: io::Error = err.into();
assert_eq!(io_err.kind(), io::ErrorKind::InvalidInput);
}
}