Skip to content

Commit c000e0a

Browse files
authored
add unit tests for recovering from parsing errors of sbp (#1347)
This PR adds a unit test showing that parsing errors fallsback to an invalid message. Seeing this unit test makes it clear why some of the questions @silverjam was having about invoking this on certain inputs causes it to pass and others cause it to fail. Additionally the round-tripping on arbitrary bytes is not possible yet because both the iter_frame & iter_messages will only return a message if there is something that starts with the prelude byte, and it returns the bytes that exist between that prelude and HEADER_LEN, whatever the length of the payload is on byte 5 + the CRC_LEN. This means that if there are extra bytes sent in the stream for aliasing for instance, the framer ignores those bytes, and they are never even tried to be parsed. @silverjam gave two examples that do not throw an error as the unknown fallback is currently implemented. One of those has been added as a unit test here. But the main takeaway is the invalid-fallback as currently written only works on messages that are properly framed but perhaps wrongly labeled or with a CRC error.
1 parent 9a8bac0 commit c000e0a

File tree

1 file changed

+88
-18
lines changed

1 file changed

+88
-18
lines changed

rust/sbp/src/de.rs

Lines changed: 88 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ use dencode::FramedRead;
1111
use futures::StreamExt;
1212

1313
use crate::{
14-
messages::invalid::Invalid, messages::SbpMsgParseError, HandleParseError, Sbp, CRC_LEN,
15-
HEADER_LEN, MAX_FRAME_LEN, PAYLOAD_INDEX, PREAMBLE,
14+
messages::{invalid::Invalid, SbpMsgParseError},
15+
HandleParseError, Sbp, CRC_LEN, HEADER_LEN, MAX_FRAME_LEN, PAYLOAD_INDEX, PREAMBLE,
1616
};
1717

1818
/// Deserialize the IO stream into an iterator of messages.
@@ -265,6 +265,7 @@ impl dencode::Decoder for FramerImpl {
265265
"payload_index must be less than header len"
266266
);
267267
let end = HEADER_LEN + src[PAYLOAD_INDEX] as usize + CRC_LEN;
268+
268269
if src.len() < end {
269270
src.reserve(MAX_FRAME_LEN);
270271
return Ok(None);
@@ -435,7 +436,10 @@ mod tests {
435436
io::{Cursor, Write},
436437
};
437438

438-
use crate::{messages::navigation::MsgBaselineEcef, wire_format::WireFormat};
439+
use crate::{
440+
messages::{invalid::Invalid, navigation::MsgBaselineEcef},
441+
wire_format::WireFormat,
442+
};
439443

440444
use super::*;
441445

@@ -497,12 +501,27 @@ mod tests {
497501
fn test_parse_bad_message() {
498502
// Properly framed data but the payload isn't right given the message type
499503
let data: Vec<u8> = vec![0x55, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x65, 0x8D];
500-
let bytes = BytesMut::from(&data[..]);
501-
let mut actual = crate::de::Decoder::new(bytes.reader());
502-
assert!(matches!(
503-
actual.next().unwrap(),
504-
Err(Error::SbpMsgParseError(_))
505-
));
504+
let input = Cursor::new(data.clone());
505+
let mut msgs = iter_messages(input);
506+
if let Some(Err(Error::SbpMsgParseError(e))) = msgs.next() {
507+
let invalid = Invalid::from(e);
508+
assert_eq!(
509+
invalid,
510+
Invalid {
511+
msg_id: Some(257),
512+
sender_id: Some(257),
513+
crc: Some(36197),
514+
invalid_frame: data
515+
}
516+
)
517+
} else {
518+
panic!(
519+
"expecting unable to parse error because message is framed \
520+
but not valid"
521+
);
522+
}
523+
// msgs should be exhausted
524+
assert_eq!(msgs.count(), 0);
506525
}
507526

508527
#[test]
@@ -511,7 +530,7 @@ mod tests {
511530
0x55u8, 0x0b, 0x02, 0xd3, 0x88, 0x14, 0x28, 0xf4, 0x7a, 0x13, 0x96, 0x62, 0xee, 0xff,
512531
0xbe, 0x40, 0x14, 0x00, 0xf6, 0xa3, 0x09, 0x00, 0x00, 0x00, 0x0e, 0x00, 0xdb, 0xbf,
513532
];
514-
payload.append(&mut payload.clone());
533+
payload.extend(payload.clone());
515534
let input = Cursor::new(payload);
516535
let mut count = 0;
517536
for msg in iter_messages(input) {
@@ -521,22 +540,73 @@ mod tests {
521540
assert_eq!(count, 2);
522541
}
523542

543+
#[test]
544+
fn test_no_message_if_improperly_framed() {
545+
let corrupted_input: Vec<u8> = vec![
546+
0x55, 0x28, 0x61, 0xb2, 0x99, 0xba, 0xd6, 0x1d, 0x42, 0x15, 0xf7, 0x9f, 0x36, 0xf8,
547+
0xca, 0x72, 0x97, 0x41, 0xaf, 0x29, 0xb9, 0x0b, 0xf1, 0x0e, 0xd3, 0x1d, 0xef, 0xab,
548+
0xd4, 0x70, 0xac, 0x1e, 0x02, 0x71, 0xa0, 0x59, 0xc1, 0x78, 0x95, 0x5d, 0xf9, 0xe5,
549+
0xf9, 0x00, 0x6a, 0x38, 0x06, 0x69, 0x06, 0x8d, 0xf1, 0x80, 0xa3, 0xe2, 0xa1, 0x3c,
550+
0x6b, 0xab, 0x42, 0xe8, 0x18, 0x0a, 0xf0, 0x70,
551+
];
552+
let mut msgs = iter_messages(Cursor::new(corrupted_input.clone()));
553+
match msgs.next() {
554+
None if corrupted_input[PAYLOAD_INDEX] as usize > corrupted_input.len() => {
555+
// in theory some day we want to recover here with a invalid message
556+
// however that will require a change to how framed reads are
557+
// taken in
558+
}
559+
_ => {
560+
panic!(
561+
"Expecting None because the data are not large enough to hold the \
562+
expected frame"
563+
);
564+
}
565+
}
566+
}
524567
#[test]
525568
fn test_parse_crc_error() {
526-
let packet = vec![
527-
// Start with a mostly valid message, with a single byte error
528-
0x55, 0x0c, // This byte should be 0x0b, changed to intentionally cause a CRC error
529-
0x02, 0xd3, 0x88, 0x14, 0x28, 0xf4, 0x7a, 0x13, 0x96, 0x62, 0xee, 0xff, 0xbe, 0x40,
530-
0x14, 0x00, 0xf6, 0xa3, 0x09, 0x00, 0x00, 0x00, 0x0e, 0x00, 0xdb, 0xbf, 0xde, 0xad,
531-
0xbe, 0xef, // Include another valid message to properly parse
569+
let valid_message = vec![
532570
0x55, 0x0b, 0x02, 0xd3, 0x88, 0x14, 0x28, 0xf4, 0x7a, 0x13, 0x96, 0x62, 0xee, 0xff,
533571
0xbe, 0x40, 0x14, 0x00, 0xf6, 0xa3, 0x09, 0x00, 0x00, 0x00, 0x0e, 0x00, 0xdb, 0xbf,
534572
0xde, 0xad, 0xbe, 0xef,
535573
];
574+
575+
// A mostly valid message
576+
let invalid_message_bytes = {
577+
let mut invalid = valid_message.clone();
578+
// add one to one of the bytes in the header to intentionally cause a CRC error
579+
invalid[1] += 1;
580+
invalid
581+
};
582+
583+
let packet: Vec<_> = invalid_message_bytes
584+
.iter()
585+
.cloned()
586+
.chain(valid_message.into_iter())
587+
.collect();
588+
536589
let mut msgs = iter_messages(Cursor::new(packet));
537590

538-
let res = msgs.next().unwrap().unwrap_err();
539-
assert!(matches!(res, Error::CrcError(..)));
591+
let crc_err = if let Some(Err(Error::CrcError(inner_err))) = msgs.next() {
592+
inner_err
593+
} else {
594+
panic!("expecting CrcError because CRC is incorrect.")
595+
};
596+
597+
let invalid_msg = Invalid::from(crc_err);
598+
assert_eq!(
599+
invalid_msg,
600+
Invalid {
601+
msg_id: Some(524),
602+
sender_id: Some(35027),
603+
crc: Some(49115),
604+
// note that the message here is missing the values in
605+
// 28..32 (not sure if that is desired behavior but hard
606+
// to fix without more breaking changes)
607+
invalid_frame: invalid_message_bytes[0..28].to_vec(),
608+
}
609+
);
540610

541611
let res = msgs.next().unwrap();
542612
assert!(res.is_ok());

0 commit comments

Comments
 (0)