#![cfg(feature = "posix")]
use core::ptr::null;
use std::ffi::CString;
use osal_rs::utils::{
bytes_to_hex, bytes_to_hex_into_slice, hex_to_bytes, hex_to_bytes_into_slice,
register_bit_size, AsSyncStr, Bytes, CpuRegisterSize, Error, OsalRsBool, Result, MAX_DELAY,
};
use osal_rs::{log_debug, log_info};
const TAG: &str = "UtilsTests";
#[test]
fn test_bytes_construction() -> Result<()> {
log_info!(TAG, "Starting test_bytes_construction");
let empty = Bytes::<16>::new();
assert_eq!(empty.len(), 0);
assert!(empty.is_empty());
assert_eq!(empty.size(), 16);
assert_eq!(empty.capacity(), 16);
let from_str = Bytes::<16>::from_str("Hello");
assert_eq!(from_str.as_str(), "Hello");
assert_eq!(from_str.len(), 5);
let from_bytes = Bytes::<8>::from_bytes(b"ABCDE");
assert_eq!(from_bytes.as_str(), "ABCDE");
let c_string = CString::new("CText").unwrap();
let from_char_ptr = Bytes::<16>::from_char_ptr(c_string.as_ptr());
assert_eq!(from_char_ptr.as_str(), "CText");
let from_cstr = Bytes::<16>::from_cstr(c_string.as_ptr());
assert_eq!(from_cstr.as_str(), "CText");
let raw = *b"UCHAR!!!";
let from_uchar_ptr = Bytes::<8>::from_uchar_ptr(raw.as_ptr());
assert_eq!(from_uchar_ptr.as_raw_bytes(), &raw[..]);
let from_sync_str = Bytes::<16>::from_as_sync_str(&"Synced");
assert_eq!(from_sync_str.as_str(), "Synced");
log_debug!(TAG, "register_bit_size: {:?}", register_bit_size());
assert!(matches!(register_bit_size(), CpuRegisterSize::Bit32 | CpuRegisterSize::Bit64));
log_info!(TAG, "test_bytes_construction PASSED");
Ok(())
}
#[test]
fn test_bytes_str_conversion() -> Result<()> {
log_info!(TAG, "Starting test_bytes_str_conversion");
let mut bytes = Bytes::<16>::from_str("Hello World");
assert_eq!(bytes.as_str(), "Hello World");
assert_eq!(bytes.len(), 11);
assert!(!bytes.is_empty());
assert_eq!(bytes.as_raw_bytes(), b"Hello World");
assert!(bytes.is_string());
assert_eq!(bytes.to_bytes().len(), 16);
let mut dest = String::from("................");
bytes.fill_str(dest.as_mut_str())?;
log_debug!(TAG, "fill_str result prefix: {}", &dest[..11]);
assert_eq!(&dest[..11], "Hello World");
let c_str = bytes.as_cstr();
assert_eq!(c_str.to_bytes(), b"Hello World");
let mut mutable = Bytes::<16>::from_str("Mutable");
let c_str_mut = mutable.as_cstr_mut();
assert_eq!(c_str_mut.to_bytes(), b"Mutable");
log_info!(TAG, "test_bytes_str_conversion PASSED");
Ok(())
}
#[test]
fn test_bytes_append_prepend() -> Result<()> {
log_info!(TAG, "Starting test_bytes_append_prepend");
let mut bytes = Bytes::<16>::from_str("Hello");
bytes.append_str(" World");
assert_eq!(bytes.as_str(), "Hello World");
let mut bytes2 = Bytes::<16>::from_str("Data: ");
bytes2.append_bytes(&[0x41, 0x42, 0x43]);
assert_eq!(bytes2.as_str(), "Data: ABC");
let mut bytes3 = Bytes::<16>::from_str("Hello");
let other = Bytes::<8>::from_str(" World");
bytes3.append(&other);
assert_eq!(bytes3.as_str(), "Hello World");
let mut bytes4 = Bytes::<16>::from_str("Hello");
let suffix = Bytes::<8>::from_str(" World");
bytes4.append_as_sync_str(&suffix);
assert_eq!(bytes4.as_str(), "Hello World");
let mut prepend1 = Bytes::<16>::from_str("World");
prepend1.prepend_str("Hello ");
assert_eq!(prepend1.as_str(), "Hello World");
let mut prepend2 = Bytes::<16>::from_str("World");
prepend2.prepend_bytes(b"Hello ");
assert_eq!(prepend2.as_str(), "Hello World");
let mut prepend3 = Bytes::<16>::from_str("World");
let prefix = Bytes::<8>::from_str("Hello ");
prepend3.prepend(&prefix);
assert_eq!(prepend3.as_str(), "Hello World");
let mut prepend4 = Bytes::<16>::from_str("World");
let prefix2 = Bytes::<8>::from_str("Hello ");
prepend4.prepend_as_sync_str(&prefix2);
assert_eq!(prepend4.as_str(), "Hello World");
log_info!(TAG, "test_bytes_append_prepend PASSED");
Ok(())
}
#[test]
fn test_bytes_mutation() -> Result<()> {
log_info!(TAG, "Starting test_bytes_mutation");
let mut bytes = Bytes::<16>::from_str("Test");
assert!(!bytes.is_empty());
bytes.clear();
assert!(bytes.is_empty());
assert_eq!(bytes.len(), 0);
let mut hw = Bytes::<16>::from_str("Hello");
assert_eq!(hw.pop(), Some(b'o'));
assert_eq!(hw.as_str(), "Hell");
hw.push(b'!')?;
assert_eq!(hw.as_str(), "Hell!");
assert_eq!(hw.pop_char(), Some('!'));
assert_eq!(hw.as_str(), "Hell");
hw.push_char('o')?;
assert_eq!(hw.as_str(), "Hello");
let mut replaced = Bytes::<16>::from_str("Hello World");
replaced.replace(b"World", b"Rust!")?;
assert_eq!(replaced.as_str(), "Hello Rust!");
let mut too_small = Bytes::<8>::from_str("Hello");
assert!(too_small.replace(b"Hello", b"Hello World").is_err());
let mut formatted = Bytes::<32>::new();
formatted.format(format_args!("Hello {}", 42));
assert_eq!(formatted.as_str(), "Hello 42");
log_info!(TAG, "test_bytes_mutation PASSED");
Ok(())
}
#[test]
fn test_hex_helpers() -> Result<()> {
log_info!(TAG, "Starting test_hex_helpers");
let data = [0x01u8, 0x23, 0xAB, 0xFF];
let hex = bytes_to_hex(&data);
assert_eq!(hex, "0123abff");
let mut buffer = [0u8; 8];
let written = bytes_to_hex_into_slice(&data, &mut buffer);
assert_eq!(written, 8);
assert_eq!(&buffer, b"0123abff");
let decoded = hex_to_bytes("0123abff")?;
assert_eq!(decoded.as_slice(), &data);
assert!(hex_to_bytes("ABC").is_err());
let mut out = [0u8; 4];
let n = hex_to_bytes_into_slice("0123abff", &mut out)?;
assert_eq!(n, 4);
assert_eq!(out, data);
let mut too_small = [0u8; 2];
assert!(hex_to_bytes_into_slice("0123abff", &mut too_small).is_err());
log_info!(TAG, "test_hex_helpers PASSED");
Ok(())
}
#[test]
fn test_error_display_all_variants() -> Result<()> {
log_info!(TAG, "Starting test_error_display_all_variants");
let cases: [(Error<'static>, &str); 20] = [
(Error::OutOfMemory, "Out of memory"),
(Error::QueueSendTimeout, "Queue send timeout"),
(Error::QueueReceiveTimeout, "Queue receive timeout"),
(Error::MutexTimeout, "Mutex timeout"),
(Error::MutexLockFailed, "Mutex lock failed"),
(Error::Timeout, "Operation timeout"),
(Error::QueueFull, "Queue full"),
(Error::StringConversionError, "String conversion error"),
(Error::TaskNotFound, "Task not found"),
(Error::InvalidQueueSize, "Invalid queue size"),
(Error::NullPtr, "Null pointer encountered"),
(Error::NotFound, "Item not found"),
(Error::OutOfIndex, "Index out of bounds"),
(Error::InvalidType, "Invalid type for operation"),
(Error::Empty, "No data available"),
(Error::WriteError("disk"), "Write error occurred: disk"),
(Error::ReadError("eof"), "Read error occurred: eof"),
(Error::ReturnWithCode(-7), "Return with code: -7"),
(Error::Unhandled("boom"), "Unhandled error: boom"),
(
Error::UnhandledOwned(String::from("owned boom")),
"Unhandled error owned: owned boom",
),
];
for (error, expected) in &cases {
let rendered = format!("{}", error);
log_debug!(TAG, "{:?} -> {}", error, rendered);
assert_eq!(rendered, *expected);
}
assert_eq!(Error::Timeout, Error::Timeout);
assert_ne!(Error::Timeout, Error::QueueFull);
assert!(!format!("{:?}", Error::NullPtr).is_empty());
log_info!(TAG, "test_error_display_all_variants PASSED");
Ok(())
}
#[test]
fn test_osal_rs_bool_and_constants() -> Result<()> {
log_info!(TAG, "Starting test_osal_rs_bool_and_constants");
assert_ne!(OsalRsBool::True, OsalRsBool::False);
assert_eq!(OsalRsBool::True, OsalRsBool::True);
log_debug!(TAG, "OsalRsBool: {:?} / {:?}", OsalRsBool::True, OsalRsBool::False);
assert!(MAX_DELAY.as_millis() > 0);
const SIZE: CpuRegisterSize = register_bit_size();
assert_eq!(SIZE, register_bit_size());
assert!(matches!(SIZE, CpuRegisterSize::Bit32 | CpuRegisterSize::Bit64));
assert_eq!(
SIZE,
if size_of::<usize>() == 8 {
CpuRegisterSize::Bit64
} else {
CpuRegisterSize::Bit32
}
);
log_info!(TAG, "test_osal_rs_bool_and_constants PASSED");
Ok(())
}
#[test]
fn test_bytes_trait_conversions() -> Result<()> {
log_info!(TAG, "Starting test_bytes_trait_conversions");
let default_bytes: Bytes<16> = Default::default();
assert_eq!(default_bytes.len(), 0);
assert_eq!(default_bytes.as_raw_bytes(), b"");
assert_eq!(default_bytes.to_bytes(), Bytes::<16>::new().to_bytes());
let parsed: Bytes<16> = "Hello".parse().unwrap();
let converted: Bytes<16> = "Hello".into();
assert_eq!(parsed.as_str(), "Hello");
assert_eq!(converted.as_str(), "Hello");
assert_eq!(parsed.to_bytes(), converted.to_bytes());
let mut indexed: Bytes<8> = "abc".into();
assert_eq!(indexed[0], b'a');
assert_eq!(indexed.iter().position(|&b| b == 0), Some(3));
indexed[0] = b'A';
indexed[3] = b'd';
assert_eq!(indexed.as_str(), "Abcd");
let shown: Bytes<16> = "shown".into();
assert_eq!(format!("{}", shown), "shown");
assert!(format!("{:?}", shown).starts_with("Bytes(["));
let invalid = Bytes::<2>::from_bytes(&[0xFF, 0xFE]);
assert!(!format!("{}", invalid).is_empty());
let cloned = shown.clone();
assert_eq!(cloned.as_str(), shown.as_str());
log_info!(TAG, "test_bytes_trait_conversions PASSED");
Ok(())
}
#[test]
fn test_as_sync_str_trait_object() -> Result<()> {
log_info!(TAG, "Starting test_as_sync_str_trait_object");
let hello: Bytes<16> = "hello".into();
let hello_again: Bytes<32> = "hello".into();
let world: Bytes<16> = "world".into();
let a: &dyn AsSyncStr = &hello;
let b: &dyn AsSyncStr = &hello_again;
let c: &dyn AsSyncStr = &world;
assert_eq!(a.as_str(), "hello");
assert!(a == b);
assert!(a != c);
log_debug!(TAG, "dyn AsSyncStr Display: {} / Debug: {:?}", a, a);
assert_eq!(format!("{}", a), "hello");
assert_eq!(format!("{:?}", a), "hello");
log_info!(TAG, "test_as_sync_str_trait_object PASSED");
Ok(())
}
#[test]
fn test_bytes_null_pointer_constructors() -> Result<()> {
log_info!(TAG, "Starting test_bytes_null_pointer_constructors");
assert_eq!(Bytes::<16>::from_char_ptr(null()).len(), 0);
assert_eq!(Bytes::<16>::from_cstr(null()).len(), 0);
assert_eq!(Bytes::<16>::from_uchar_ptr(null()).len(), 0);
log_info!(TAG, "test_bytes_null_pointer_constructors PASSED");
Ok(())
}
#[test]
fn test_bytes_truncating_constructors() -> Result<()> {
log_info!(TAG, "Starting test_bytes_truncating_constructors");
let long = CString::new("This is a very long string").unwrap();
let from_char_ptr = Bytes::<8>::from_char_ptr(long.as_ptr());
assert_eq!(from_char_ptr.as_raw_bytes(), b"This is ");
let from_cstr = Bytes::<8>::from_cstr(long.as_ptr());
assert_eq!(from_cstr.as_raw_bytes(), b"This is ");
let from_bytes = Bytes::<4>::from_bytes(b"abcdefgh");
assert_eq!(from_bytes.as_raw_bytes(), b"abcd");
let from_str = Bytes::<3>::from_str("Hello");
assert_eq!(from_str.as_raw_bytes(), b"Hel");
let from_sync = Bytes::<4>::from_as_sync_str(&"abcdefgh");
assert_eq!(from_sync.as_raw_bytes(), b"abcd");
let exact = Bytes::<5>::from_str("Hello");
assert_eq!(exact.len(), 5);
assert!(!exact.is_empty());
assert_eq!(exact.as_str(), "Hello");
log_info!(TAG, "test_bytes_truncating_constructors PASSED");
Ok(())
}
#[test]
fn test_bytes_append_prepend_truncation() -> Result<()> {
log_info!(TAG, "Starting test_bytes_append_prepend_truncation");
let mut append_str = Bytes::<8>::from_str("Hello");
append_str.append_str(" World");
assert_eq!(append_str.as_raw_bytes(), b"Hello Wo");
let mut append_bytes = Bytes::<8>::from_str("Hello");
append_bytes.append_bytes(b" World");
assert_eq!(append_bytes.as_raw_bytes(), b"Hello Wo");
let mut append_other = Bytes::<8>::from_str("Hello");
append_other.append(&Bytes::<16>::from_str(" World"));
assert_eq!(append_other.as_raw_bytes(), b"Hello Wo");
let mut append_sync = Bytes::<8>::from_str("Hello");
append_sync.append_as_sync_str(&Bytes::<16>::from_str(" World"));
assert_eq!(append_sync.as_raw_bytes(), b"Hello Wo");
let mut full = Bytes::<5>::from_str("Hello");
full.append_str("!!!");
assert_eq!(full.as_raw_bytes(), b"Hello");
let mut prepend_str = Bytes::<8>::from_str("World");
prepend_str.prepend_str("Hello ");
assert_eq!(prepend_str.as_raw_bytes(), b"Hello Wo");
let mut prepend_bytes = Bytes::<8>::from_str("World");
prepend_bytes.prepend_bytes(b"Hello ");
assert_eq!(prepend_bytes.as_raw_bytes(), b"Hello Wo");
let mut prepend_other = Bytes::<8>::from_str("end");
prepend_other.prepend(&Bytes::<32>::from_str("begin_"));
assert_eq!(prepend_other.as_raw_bytes(), b"begin_en");
let mut prepend_sync = Bytes::<8>::from_str("World");
prepend_sync.prepend_as_sync_str(&Bytes::<16>::from_str("Hello "));
assert_eq!(prepend_sync.as_raw_bytes(), b"Hello Wo");
let mut swamped = Bytes::<4>::from_str("xy");
swamped.prepend_str("abcdef");
assert_eq!(swamped.as_raw_bytes(), b"abcd");
let mut empty = Bytes::<8>::new();
empty.prepend_str("hi");
assert_eq!(empty.as_str(), "hi");
log_info!(TAG, "test_bytes_append_prepend_truncation PASSED");
Ok(())
}
#[test]
fn test_bytes_replace_variants() -> Result<()> {
log_info!(TAG, "Starting test_bytes_replace_variants");
let mut same = Bytes::<16>::from_str("Hello World");
same.replace(b"World", b"Rust!")?;
assert_eq!(same.as_str(), "Hello Rust!");
let mut shorter = Bytes::<16>::from_str("Hello World");
shorter.replace(b"World", b"Yo")?;
assert_eq!(shorter.as_str(), "Hello Yo");
assert_eq!(shorter.len(), 8);
let mut longer = Bytes::<24>::from_str("a-b");
longer.replace(b"-", b"+++")?;
assert_eq!(longer.as_str(), "a+++b");
let mut repeated = Bytes::<16>::from_str("aXbXc");
repeated.replace(b"X", b"-")?;
assert_eq!(repeated.as_str(), "a-b-c");
let mut absent = Bytes::<16>::from_str("Hello");
absent.replace(b"zzz", b"!")?;
assert_eq!(absent.as_str(), "Hello");
let mut empty_pattern = Bytes::<16>::from_str("Hello");
empty_pattern.replace(b"", b"!")?;
assert_eq!(empty_pattern.as_str(), "Hello");
let mut deleted = Bytes::<16>::from_str("a-b-c");
deleted.replace(b"-", b"")?;
assert_eq!(deleted.as_str(), "abc");
let mut too_small = Bytes::<8>::from_str("Hello");
assert!(matches!(
too_small.replace(b"Hello", b"Hello World"),
Err(Error::StringConversionError)
));
assert_eq!(too_small.as_str(), "Hello");
log_info!(TAG, "test_bytes_replace_variants PASSED");
Ok(())
}
#[test]
fn test_bytes_push_pop_edges() -> Result<()> {
log_info!(TAG, "Starting test_bytes_push_pop_edges");
let mut empty = Bytes::<8>::new();
assert_eq!(empty.pop(), None);
assert_eq!(empty.pop_char(), None);
let mut small = Bytes::<2>::new();
small.push(b'a')?;
small.push(b'b')?;
assert!(matches!(small.push(b'c'), Err(Error::StringConversionError)));
assert!(matches!(
small.push_char('c'),
Err(Error::StringConversionError)
));
assert_eq!(small.as_raw_bytes(), b"ab");
let mut roomy = Bytes::<16>::new();
assert!(matches!(
roomy.push_char('é'),
Err(Error::StringConversionError)
));
roomy.push_char('o')?;
assert_eq!(roomy.as_str(), "o");
let mut word = Bytes::<8>::from_str("hi");
assert_eq!(word.pop(), Some(b'i'));
assert_eq!(word.pop_char(), Some('h'));
assert_eq!(word.pop(), None);
assert!(word.is_empty());
log_info!(TAG, "test_bytes_push_pop_edges PASSED");
Ok(())
}
#[test]
fn test_bytes_invalid_utf8() -> Result<()> {
log_info!(TAG, "Starting test_bytes_invalid_utf8");
let invalid = Bytes::<4>::from_bytes(&[0xFF, 0xFE, 0xFD, 0xFC]);
assert!(!invalid.is_string());
assert_eq!(invalid.as_str(), "Bytes::as_str() Conversion error - invalid UTF-8");
let valid = Bytes::<8>::from_str("ok");
assert!(valid.is_string());
let mut invalid_fill = Bytes::<4>::from_bytes(&[0xFF, 0xFE, 0xFD, 0xFC]);
let mut dest = String::from("....");
assert!(matches!(
invalid_fill.fill_str(dest.as_mut_str()),
Err(Error::StringConversionError)
));
let mut source = Bytes::<8>::from_str("abcdefgh");
let mut short_dest = String::from("...");
source.fill_str(short_dest.as_mut_str())?;
assert_eq!(short_dest, "abc");
log_info!(TAG, "test_bytes_invalid_utf8 PASSED");
Ok(())
}
#[test]
fn test_bytes_clear_and_capacity() -> Result<()> {
log_info!(TAG, "Starting test_bytes_clear_and_capacity");
let mut bytes = Bytes::<16>::from_str("something");
assert_eq!(bytes.capacity(), 16);
assert_eq!(bytes.size(), 16);
assert_eq!(bytes.to_bytes().len(), 16);
bytes.clear();
assert!(bytes.is_empty());
assert_eq!(bytes.len(), 0);
assert_eq!(bytes.as_str(), "");
assert_eq!(bytes.as_cstr().to_bytes(), b"");
bytes.clear();
assert!(bytes.is_empty());
log_info!(TAG, "test_bytes_clear_and_capacity PASSED");
Ok(())
}
#[test]
fn test_bytes_format_and_write() -> Result<()> {
log_info!(TAG, "Starting test_bytes_format_and_write");
use core::fmt::Write;
let mut buffer = Bytes::<32>::from_str("stale content");
buffer.format(format_args!("Hello {}", 42));
assert_eq!(buffer.as_str(), "Hello 42");
let mut tiny = Bytes::<8>::new();
tiny.format(format_args!("{:.2}", 3.14159));
assert_eq!(tiny.as_str(), "3.14");
let mut overflowing = Bytes::<4>::new();
overflowing.format(format_args!("{}", "much too long"));
assert_eq!(overflowing.as_raw_bytes(), b"much");
let mut written = Bytes::<16>::from_str("a=");
write!(written, "{}", 7).unwrap();
write!(written, ",b={}", 8).unwrap();
assert_eq!(written.as_str(), "a=7,b=8");
log_info!(TAG, "test_bytes_format_and_write PASSED");
Ok(())
}
#[test]
fn test_hex_helper_edges() -> Result<()> {
log_info!(TAG, "Starting test_hex_helper_edges");
assert_eq!(bytes_to_hex(&[]), "");
assert_eq!(bytes_to_hex_into_slice(&[], &mut []), 0);
assert!(hex_to_bytes("")?.is_empty());
assert_eq!(hex_to_bytes_into_slice("", &mut [])?, 0);
assert_eq!(hex_to_bytes("ABCDEF")?, hex_to_bytes("abcdef")?);
assert!(matches!(hex_to_bytes("ABC"), Err(Error::StringConversionError)));
let mut out = [0u8; 4];
assert!(matches!(
hex_to_bytes_into_slice("ABC", &mut out),
Err(Error::StringConversionError)
));
assert!(hex_to_bytes("zz").is_err());
assert!(hex_to_bytes_into_slice("zz", &mut out).is_err());
let mut oversized = [b'.'; 12];
let written = bytes_to_hex_into_slice(&[0x01, 0x23, 0xAB, 0xFF], &mut oversized);
log_debug!(TAG, "bytes_to_hex_into_slice wrote {} bytes", written);
assert_eq!(written, 8);
assert_eq!(&oversized[..8], b"0123abff");
assert_eq!(&oversized[8..], b"....");
let data = [0x00u8, 0x0F, 0xF0, 0xFF];
let hex = bytes_to_hex(&data);
assert_eq!(hex, "000ff0ff");
assert_eq!(hex_to_bytes(&hex)?, data);
let mut round = [0u8; 4];
assert_eq!(hex_to_bytes_into_slice(&hex, &mut round)?, 4);
assert_eq!(round, data);
log_info!(TAG, "test_hex_helper_edges PASSED");
Ok(())
}
#[cfg(feature = "serde")]
#[test]
fn test_bytes_serde_round_trip() -> Result<()> {
use osal_rs_serde::{from_bytes, to_dyn_bytes};
log_info!(TAG, "Starting test_bytes_serde_round_trip");
let text: Bytes<16> = "payload".into();
let mut encoded = Vec::new();
let written = to_dyn_bytes(&text, &mut encoded).unwrap();
log_debug!(TAG, "serialized {:?} into {} bytes", text.as_str(), written);
assert!(written > 0);
let decoded: Bytes<16> = from_bytes(&encoded).unwrap();
assert_eq!(decoded.as_str(), "payload");
let binary = Bytes::<4>::from_bytes(&[0xFF, 0x00, 0xFE, 0x01]);
let mut binary_encoded = Vec::new();
assert!(to_dyn_bytes(&binary, &mut binary_encoded).unwrap() > 0);
let empty = Bytes::<8>::new();
let mut empty_encoded = Vec::new();
to_dyn_bytes(&empty, &mut empty_encoded).unwrap();
let empty_decoded: Bytes<8> = from_bytes(&empty_encoded).unwrap();
assert!(empty_decoded.is_empty());
log_info!(TAG, "test_bytes_serde_round_trip PASSED");
Ok(())
}