extern crate alloc;
use crate::inmemory::*;
fn test_read(message: &impl coap_message::ReadableMessage<Code = u8>) {
use coap_message::*;
assert!(message.code() == 0x45);
let expected_options = [
(11, b"short".to_vec()),
(11, b"very-long-option".to_vec()),
(65000, b"".to_vec()),
];
let found_options: alloc::vec::Vec<_> = message
.options()
.map(|o| (o.number(), o.value().to_vec()))
.collect();
assert!(found_options == expected_options);
assert!(message.payload() == b"payload");
}
#[test]
fn test_inmemory() {
let local_body =
alloc::boxed::Box::new(b"\xb5short\r\x03very-long-option\xe0\xfc\xd0\xffpayload");
let message = Message::new_from_slice(0x45, local_body.as_slice());
test_read(&message);
}
#[test]
fn test_inmemory_truncated() {
let local_body =
alloc::boxed::Box::new(b"\xb5short\r\x03very-long-option\xe0\xfc\xd0\xffpayload\0\0\0\0");
let message = Message::new_until(SliceBuffer::new(0x45, *local_body), local_body.len() - 4);
test_read(&message);
}
#[test]
#[cfg(feature = "downcast")]
fn test_inmemory_downcast() {
let local_body =
alloc::boxed::Box::new(b"\xb5short\r\x03very-long-option\xe0\xfc\xd0\xffpayload");
let message = Message::new_from_slice(0x45, local_body.as_slice());
fn hide_type<'a>(
message: &'a impl coap_message::ReadableMessage,
) -> &'a (impl coap_message::ReadableMessage + 'a) {
message
}
let hidden = hide_type(&message);
let message_again = Message::<SliceBuffer<'static>>::downcast_from(hidden).unwrap();
test_read(message_again);
}
#[test]
fn test_inmemory_write() {
use coap_message::*;
let mut code = 0;
let mut buffer = [0; 100];
let mut message = MessageMut::new_in_slice(&mut code, &mut buffer);
message.set_code(0x45);
assert!(
message.options().next().is_none(),
"Nascent message returned options"
);
assert!(
message.payload().is_empty(),
"Nascent message returned payload"
);
message.add_option(11, b"-----").unwrap();
message.add_option(11, b"very-long-option").unwrap();
message.add_option(65000, b"").unwrap();
assert_eq!(
message.options().count(),
3,
"Written options are not read back"
);
message.set_payload(b"payload123").unwrap();
assert_eq!(
message.payload(),
b"payload123",
"Written payload is not read back"
);
message.truncate(7).unwrap();
assert_eq!(
message.payload(),
b"payload",
"Truncated payload is not read back"
);
message.mutate_options(|n, v| {
if n == 11 && v.len() == 5 {
v.copy_from_slice(b"short");
}
});
let (len, _) = message.finish();
assert!(code == 0x45);
assert!(&buffer[..len] == b"\xb5short\r\x03very-long-option\xe0\xfc\xd0\xffpayload");
}
#[test]
fn test_insert_option_between() {
use coap_message::*;
let mut code = 0;
let mut buffer = [0; 100];
let mut message = MessageMut::new_in_slice(&mut code, &mut buffer);
message.set_code(0x45);
message.add_option(11, b"-----").unwrap();
message.add_option(65000, b"").unwrap();
message.set_payload(b"payload123").unwrap();
assert_eq!(
message.payload(),
b"payload123",
"Written payload is not read back"
);
message.insert_option(11, b"very-long-option").unwrap();
assert_eq!(
message.options().count(),
3,
"Written options are not read back"
);
assert_eq!(
message.payload(),
b"payload123",
"Payload is destroyed by option insertion"
);
message.truncate(7).unwrap();
assert_eq!(
message.payload(),
b"payload",
"Truncated payload is not read back"
);
message.mutate_options(|n, v| {
if n == 11 && v.len() == 5 {
v.copy_from_slice(b"short");
}
});
let (len, _) = message.finish();
assert_eq!(code, 0x45);
assert_eq!(
&buffer[..len],
b"\xb5short\r\x03very-long-option\xe0\xfc\xd0\xffpayload"
);
}
#[test]
fn test_insert_option_last() {
use coap_message::*;
let mut code = 0;
let mut buffer = [0; 100];
let mut message = MessageMut::new_in_slice(&mut code, &mut buffer);
message.set_code(0x45);
message.insert_option(11, b"-----").unwrap();
message.insert_option(11, b"very-long-option").unwrap();
message.insert_option(65000, b"").unwrap();
message.set_payload(b"payload123").unwrap();
let (len, _) = message.finish();
assert_eq!(code, 0x45);
assert_eq!(
&buffer[..len],
b"\xb5-----\r\x03very-long-option\xe0\xfc\xd0\xffpayload123"
);
}
#[test]
fn test_insert_option_encoding_length_changed() {
use coap_message::*;
let mut code = 0;
let mut buffer = [0; 100];
let mut message = MessageMut::new_in_slice(&mut code, &mut buffer);
message.set_code(0x45);
message.add_option(65000, b"").unwrap();
message.insert_option(65000 - 1, b"").unwrap();
let (len, _) = message.finish();
assert_eq!(code, 0x45);
assert_eq!(&buffer[..len], b"\xe0\xfc\xda\x10");
}
#[test]
fn test_retain_options() {
use coap_message::*;
let mut code = 0;
let mut buffer = [0; 100];
let mut message = MessageMut::new_in_slice(&mut code, &mut buffer);
message.set_code(0x45);
message.insert_option(10, b"-----").unwrap();
message.insert_option(10, b"+++++").unwrap();
message.insert_option(11, b"very-long-option").unwrap();
message.insert_option(65000, b"").unwrap();
message.set_payload(b"payload123").unwrap();
let p = |number, data: &[u8]| number == 10 && data == b"-----" || number == 65000;
assert!(p(65000, b""));
message.retain_options(|number, data| number == 10 && data == b"-----" || number == 65000);
let (len, _) = message.finish();
assert_eq!(code, 0x45);
assert_eq!(&buffer[..len], b"\xa5-----\xe0\xfc\xd1\xffpayload123");
}
#[test]
fn test_retain_options_last() {
use coap_message::*;
let mut code = 0;
let mut buffer = [0; 100];
let mut message = MessageMut::new_in_slice(&mut code, &mut buffer);
message.set_code(0x45);
message.insert_option(11, b"-----").unwrap();
message.insert_option(11, b"very-long-option").unwrap();
message.insert_option(65000, b"").unwrap();
message.set_payload(b"payload123").unwrap();
message.retain_options(|number, _| number != 65000);
assert!(message.add_option(10, b"").is_err());
assert!(message.add_option(11, b"").is_ok());
let (len, _) = message.finish();
assert_eq!(code, 0x45);
assert_eq!(
&buffer[..len],
b"\xb5-----\r\x03very-long-option\x00\xffpayload123"
);
}
#[test]
fn test_retain_options_last_nopayload() {
use coap_message::*;
let mut code = 0;
let mut buffer = [0; 100];
let mut message = MessageMut::new_in_slice(&mut code, &mut buffer);
message.set_code(0x45);
message.insert_option(11, b"-----").unwrap();
message.insert_option(11, b"very-long-option").unwrap();
message.insert_option(65000, b"").unwrap();
message.retain_options(|number, _| number != 65000);
assert!(message.add_option(10, b"").is_err());
assert!(message.add_option(11, b"").is_ok());
let (len, _) = message.finish();
assert_eq!(code, 0x45);
assert_eq!(&buffer[..len], b"\xb5-----\r\x03very-long-option\x00");
}
#[test]
fn test_retain_options_empty() {
use coap_message::*;
let mut code = 0;
let mut buffer = [0; 100];
let mut message = MessageMut::new_in_slice(&mut code, &mut buffer);
message.set_code(0x45);
message.retain_options(|_, _| panic!("Got unexpected options!"));
let _ = message.finish();
assert_eq!([0; 100], buffer);
}
#[test]
fn test_inmemory_pushout() {
use coap_message::MinimalWritableMessage as _;
use coap_message::MutableWritableMessage as _;
use coap_message::ReadableMessage as _;
let mut code = 0;
let mut tail = [0u8; 5];
let mut message = MessageMut::new_in_slice(&mut code, &mut tail);
message.set_payload(&[1, 2, 3, 4]).unwrap();
message.add_option(1, &[1, 2, 3, 4]).unwrap_err();
message.add_option(1, &[]).unwrap_err();
message.truncate(3).unwrap();
message.add_option(1, &[]).unwrap();
assert_eq!(message.payload(), &[1, 2, 3]);
assert_eq!(message.finish().0, 5);
}
#[test]
#[cfg(feature = "downcast")]
fn test_inmemory_write_downcast() {
use coap_message::*;
let mut code = 0;
let mut buffer = [0; 100];
let mut message = MessageMut::new_in_slice(&mut code, &mut buffer);
fn hide_type_mut(
message: &mut impl coap_message::MinimalWritableMessage,
) -> &mut impl coap_message::MinimalWritableMessage {
message
}
let hidden = hide_type_mut(&mut message);
#[cfg(feature = "alloc")]
assert!(MessageMut::<BoxBuffer>::downcast_from(hidden).is_none());
let concrete_again = MessageMut::<SliceBufferMut<'static>>::downcast_from(hidden).unwrap();
concrete_again.set_code(0x45);
concrete_again.add_option(11, b"-----").unwrap();
concrete_again.add_option(11, b"very-long-option").unwrap();
message.add_option(65000, b"").unwrap();
message.set_payload(b"payload123").unwrap();
message.truncate(7).unwrap();
message.mutate_options(|n, v| {
if n == 11 && v.len() == 5 {
v.copy_from_slice(b"short");
}
});
let (len, _) = message.finish();
assert!(code == 0x45);
assert!(&buffer[..len] == b"\xb5short\r\x03very-long-option\xe0\xfc\xd0\xffpayload");
}
#[test]
fn test_inmemory_mutable() {
use coap_message::MutableWritableMessage;
let mut code = 0x45;
let mut buffer = *b"\xb5short\r\x03very-long-option\xe0\xfc\xd0\xffpayload";
let mut message = MessageMut::new_from_encoded_slice(&mut code, &mut buffer).unwrap();
test_read(&message);
assert!(message.payload_mut_with_len(7).unwrap() == b"payload");
message.payload_mut_with_len(7).unwrap()[6] = b'D';
assert!(&buffer[buffer.len() - 1..] == b"D");
}
#[test]
fn test_inmemory_write_untruncate() {
use coap_message::*;
const BUFF_LEN: usize = 100;
let mut code = 0;
let mut buffer = [0; BUFF_LEN];
let mut message = MessageMut::new_in_slice(&mut code, &mut buffer);
message.set_code(0x45);
message.add_option(11, b"-----").unwrap();
message.add_option(11, b"very-long-option").unwrap();
message.add_option(65000, b"").unwrap();
let payload = b"payload123";
message.set_payload(payload).unwrap();
assert_eq!(message.payload().len(), payload.len());
let initial_len = BUFF_LEN - message.available_space() + payload.len() + 1;
let too_long = message.untruncate(BUFF_LEN - initial_len + 1);
assert!(too_long.is_err());
message.untruncate(BUFF_LEN - initial_len).unwrap();
assert_eq!(
message.payload().len(),
BUFF_LEN - initial_len + payload.len()
);
message.truncate(payload.len() - 3).unwrap();
assert_eq!(message.payload().len(), payload.len() - 3);
message
.untruncate(payload.len() + 10 - message.payload().len())
.unwrap();
assert_eq!(message.payload().len(), payload.len() + 10);
let _ = message.finish();
assert_eq!(code, 0x45);
assert_eq!(
&buffer[..initial_len],
b"\xb5-----\r\x03very-long-option\xe0\xfc\xd0\xffpayload123"
);
}
#[test]
fn test_inmemory_write_untruncate_no_payload() {
use coap_message::*;
const BUFF_LEN: usize = 100;
let mut code = 0;
let mut buffer = [0; BUFF_LEN];
let mut message = MessageMut::new_in_slice(&mut code, &mut buffer);
message.set_code(0x45);
message.add_option(11, b"-----").unwrap();
message.add_option(11, b"very-long-option").unwrap();
message.add_option(65000, b"").unwrap();
assert_eq!(message.payload().len(), 0);
let initial_len = BUFF_LEN - message.available_space();
let too_long = message.untruncate(message.available_space());
assert!(too_long.is_err());
message.untruncate(message.available_space() - 1).unwrap();
assert_eq!(message.payload().len(), message.available_space() - 1);
let _ = message.finish();
assert_eq!(code, 0x45);
#[expect(clippy::range_plus_one, reason = "the +1 here is an extra byte")]
{
assert_eq!(
&buffer[..initial_len + 1],
b"\xb5-----\r\x03very-long-option\xe0\xfc\xd0\xff"
);
}
}
#[test]
fn test_inmemory_errors() {
use coap_message::*;
let bodies = [
(b"\xb5sh".as_slice(), 0),
(b"\xbf".as_slice(), 0),
(b"\x0e\xff\xff".as_slice(), 0),
(b"\xe0\xfe\x00\xd0\xff".as_slice(), 1),
];
for (body, discard_options) in bodies {
let message = Message::new_from_slice(0x45, body);
assert!(b"" == message.payload());
let mut optit = message.options();
for _ in 0..discard_options {
assert!(optit.next().is_some_and(|o| o.number() != OPTION_INVALID));
}
assert!(optit.next().map(|o| o.number()) == Some(OPTION_INVALID));
}
}
#[test]
fn test_encoding() {
use crate::option_extension::encode_extensions;
let example_options = [
(11, &b"short"[..]),
(11, &b"very-long-option"[..]),
(65000, &b""[..]),
];
let expected_encoding = b"\xb5short\r\x03very-long-option\xe0\xfc\xd0";
let mut encoded = alloc::vec::Vec::new();
let mut option = 0u16;
for e in &example_options {
let delta = e.0 - option;
option = e.0;
encoded.extend_from_slice(encode_extensions(delta, e.1.len() as _).as_ref());
encoded.extend_from_slice(e.1);
}
assert_eq!(&expected_encoding.as_ref(), &encoded);
}
#[test]
fn test_truncate() {
use coap_message::*;
let mut code = 0;
let mut buffer = [0; 100];
let mut message = MessageMut::new_in_slice(&mut code, &mut buffer);
message.set_code(0x45);
message.add_option(11, b"-----").unwrap();
message.add_option(11, b"very-long-option").unwrap();
message.add_option(65000, b"").unwrap();
message.truncate(0).unwrap();
assert_eq!(&[0u8; 0], message.payload());
let payload = b"payload123";
message.set_payload(payload).unwrap();
message.truncate(0).unwrap();
assert_eq!(&[0u8; 0], message.payload());
message.untruncate(payload.len()).unwrap();
message.truncate(payload.len() - 3).unwrap();
assert_eq!(&payload[..payload.len() - 3], message.payload());
}