dot15d4-frame 0.1.2

Frame parsing and building for the dot15d4 crate
Documentation
use super::*;

#[test]
fn parse_imm_ack() {
    let frame = [0x02, 0x10, 0x01];

    let frame = Frame::new(&frame).unwrap();

    let fc = frame.frame_control();
    assert_eq!(fc.frame_type(), FrameType::Ack);
    assert!(!fc.security_enabled());
    assert!(!fc.frame_pending());
    assert!(!fc.ack_request());
    assert!(!fc.pan_id_compression());
    assert!(!fc.sequence_number_suppression());
    assert!(!fc.information_elements_present());
    assert!(fc.dst_addressing_mode() == AddressingMode::Absent);
    assert!(fc.frame_version() == FrameVersion::Ieee802154_2006);
    assert!(fc.src_addressing_mode() == AddressingMode::Absent);
    assert!(frame.sequence_number() == Some(1));
    assert!(frame.addressing().is_none());
    assert!(frame.information_elements().is_none());
    assert!(frame.payload().is_none());
}

#[test]
fn emit_imm_ack() {
    let imm_ack = FrameBuilder::new_imm_ack(1).finalize().unwrap();

    let mut buffer = vec![0; imm_ack.buffer_len()];
    imm_ack.emit(&mut Frame::new_unchecked(&mut buffer[..]));

    assert_eq!(buffer, [0x02, 0x10, 0x01]);
}

#[test]
fn parse_ack_frame() {
    let frame = [
        0x02, 0x2e, 0x37, 0xcd, 0xab, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x0f,
        0xe1, 0x8f,
    ];

    let frame = Frame::new(&frame).unwrap();

    let fc = frame.frame_control();
    assert_eq!(fc.frame_type(), FrameType::Ack);
    assert!(!fc.security_enabled());
    assert!(!fc.frame_pending());
    assert!(!fc.ack_request());
    assert!(!fc.pan_id_compression());
    assert!(!fc.sequence_number_suppression());
    assert!(fc.information_elements_present());
    assert!(fc.dst_addressing_mode() == AddressingMode::Extended);
    assert!(fc.frame_version() == FrameVersion::Ieee802154_2020);
    assert!(fc.src_addressing_mode() == AddressingMode::Absent);

    assert!(frame.sequence_number() == Some(55));

    let addressing = frame.addressing().unwrap();
    assert_eq!(addressing.dst_pan_id(), Some(0xabcd));
    assert_eq!(
        addressing.dst_address(),
        Some(Address::Extended([
            0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02
        ]))
    );
    assert_eq!(addressing.src_pan_id(), None);
    assert_eq!(addressing.src_address(), Some(Address::Absent));

    let ie = frame.information_elements().unwrap();
    let mut headers = ie.header_information_elements();

    let time_correction = headers.next().unwrap();
    assert_eq!(
        time_correction.element_id(),
        HeaderElementId::TimeCorrection
    );

    let time_correction = TimeCorrection::new(time_correction.content()).unwrap();
    assert_eq!(time_correction.len(), 2);
    assert_eq!(
        time_correction.time_correction(),
        crate::time::Duration::from_us(-31)
    );
    assert!(time_correction.nack());
}

#[test]
fn emit_ack_frame() {
    let frame = FrameBuilder::new_ack()
        .set_sequence_number(55)
        .set_dst_pan_id(0xabcd)
        .set_dst_address(Address::Extended([
            0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02,
        ]))
        .add_header_information_element(HeaderInformationElementRepr::TimeCorrection(
            TimeCorrectionRepr {
                time_correction: crate::time::Duration::from_us(-31),
                nack: true,
            },
        ))
        .finalize()
        .unwrap();

    let mut buffer = vec![0; frame.buffer_len()];
    frame.emit(&mut Frame::new_unchecked(&mut buffer[..]));

    assert_eq!(
        buffer,
        [
            0x02, 0x2e, 0x37, 0xcd, 0xab, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02,
            0x0f, 0xe1, 0x8f,
        ]
    );
}

#[test]
fn parse_data_frame() {
    let frame = [
        0x41, 0xd8, 0x01, 0xcd, 0xab, 0xff, 0xff, 0xc7, 0xd9, 0xb5, 0x14, 0x00, 0x4b, 0x12, 0x00,
        0x2b, 0x00, 0x00, 0x00,
    ];

    let frame = Frame::new(&frame).unwrap();

    let fc = frame.frame_control();
    assert_eq!(fc.frame_type(), FrameType::Data);
    assert!(!fc.security_enabled());
    assert!(!fc.frame_pending());
    assert!(!fc.ack_request());
    assert!(fc.pan_id_compression());

    assert!(fc.dst_addressing_mode() == AddressingMode::Short);
    assert!(fc.frame_version() == FrameVersion::Ieee802154_2006);
    assert!(fc.src_addressing_mode() == AddressingMode::Extended);

    assert!(frame.sequence_number() == Some(1));

    let addressing = frame.addressing().unwrap();
    assert_eq!(addressing.dst_pan_id(), Some(0xabcd));
    assert_eq!(addressing.dst_address(), Some(Address::BROADCAST));
    assert_eq!(addressing.src_pan_id(), None);
    assert_eq!(
        addressing.src_address(),
        Some(Address::Extended([
            0x00, 0x12, 0x4b, 0x00, 0x14, 0xb5, 0xd9, 0xc7
        ]))
    );

    assert!(frame.information_elements().is_none());

    assert!(frame.payload() == Some(&[0x2b, 0x00, 0x00, 0x00][..]));
}

#[test]
fn emit_data_frame() {
    let frame = FrameBuilder::new_data(&[0x2b, 0x00, 0x00, 0x00])
        .set_sequence_number(1)
        .set_dst_pan_id(0xabcd)
        .set_dst_address(Address::BROADCAST)
        .set_src_pan_id(0xabcd)
        .set_src_address(Address::Extended([
            0x00, 0x12, 0x4b, 0x00, 0x14, 0xb5, 0xd9, 0xc7,
        ]))
        .finalize()
        .unwrap();

    let mut buffer = vec![0; frame.buffer_len()];

    frame.emit(&mut Frame::new_unchecked(&mut buffer[..]));

    assert_eq!(
        buffer,
        [
            0x41, 0xd8, 0x01, 0xcd, 0xab, 0xff, 0xff, 0xc7, 0xd9, 0xb5, 0x14, 0x00, 0x4b, 0x12,
            0x00, 0x2b, 0x00, 0x00, 0x00,
        ]
    );
}

#[test]
fn parse_enhanced_beacon() {
    let frame: [u8; 35] = [
        0x40, 0xeb, 0xcd, 0xab, 0xff, 0xff, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
        0x3f, 0x11, 0x88, 0x06, 0x1a, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1c, 0x00, 0x01,
        0xc8, 0x00, 0x01, 0x1b, 0x00,
    ];

    let frame = Frame::new(&frame).unwrap();
    let fc = frame.frame_control();
    assert_eq!(fc.frame_type(), FrameType::Beacon);
    assert!(!fc.security_enabled());
    assert!(!fc.frame_pending());
    assert!(!fc.ack_request());
    assert!(fc.pan_id_compression());
    assert!(fc.sequence_number_suppression());
    assert!(fc.information_elements_present());
    assert_eq!(fc.dst_addressing_mode(), AddressingMode::Short);
    assert_eq!(fc.src_addressing_mode(), AddressingMode::Extended);
    assert_eq!(fc.frame_version(), FrameVersion::Ieee802154_2020);

    let addressing = frame.addressing().unwrap();
    assert_eq!(addressing.dst_pan_id(), Some(0xabcd),);
    assert_eq!(addressing.src_pan_id(), None,);
    assert_eq!(addressing.dst_address(), Some(Address::BROADCAST));
    assert_eq!(
        addressing.src_address(),
        Some(Address::Extended([
            0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01
        ]))
    );
    assert_eq!(addressing.len(), 12);

    let ie = frame.information_elements().unwrap();

    let mut headers = ie.header_information_elements();
    let terminator = headers.next().unwrap();
    assert_eq!(terminator.element_id(), HeaderElementId::HeaderTermination1);
    assert_eq!(terminator.len(), 0);

    assert_eq!(headers.next(), None);

    let mut payloads = ie.payload_information_elements();

    let mlme = payloads.next().unwrap();
    assert_eq!(mlme.group_id(), PayloadGroupId::Mlme);
    assert_eq!(mlme.len() + 2, 19);
    assert_eq!(payloads.next(), None);

    let mut nested_iterator = NestedInformationElementsIterator::new(mlme.content());

    let tsch_sync = nested_iterator.next().unwrap();
    assert_eq!(
        tsch_sync.sub_id(),
        NestedSubId::Short(NestedSubIdShort::TschSynchronization)
    );
    assert_eq!(
        TschSynchronization::new(tsch_sync.content())
            .unwrap()
            .absolute_slot_number(),
        14
    );
    assert_eq!(
        TschSynchronization::new(tsch_sync.content())
            .unwrap()
            .join_metric(),
        0
    );

    let tsch_timeslot = nested_iterator.next().unwrap();
    assert_eq!(
        tsch_timeslot.sub_id(),
        NestedSubId::Short(NestedSubIdShort::TschTimeslot)
    );
    assert_eq!(TschTimeslot::new(tsch_timeslot.content()).unwrap().id(), 0);

    let channel_hopping = nested_iterator.next().unwrap();
    assert_eq!(
        channel_hopping.sub_id(),
        NestedSubId::Long(NestedSubIdLong::ChannelHopping)
    );
    assert_eq!(
        ChannelHopping::new(channel_hopping.content())
            .unwrap()
            .hopping_sequence_id(),
        0
    );

    let slotframe = nested_iterator.next().unwrap();
    assert_eq!(
        slotframe.sub_id(),
        NestedSubId::Short(NestedSubIdShort::TschSlotframeAndLink)
    );
    assert_eq!(
        TschSlotframeAndLink::new(slotframe.content())
            .unwrap()
            .number_of_slot_frames(),
        0
    );

    assert_eq!(nested_iterator.next(), None);
    assert!(frame.payload().is_none());
}

#[test]
fn emit_enhanced_beacon() {
    let frame = FrameRepr {
        frame_control: FrameControlRepr {
            frame_type: FrameType::Beacon,
            security_enabled: false,
            frame_pending: false,
            ack_request: false,
            pan_id_compression: true,
            sequence_number_suppression: true,
            information_elements_present: true,
            dst_addressing_mode: AddressingMode::Short,
            src_addressing_mode: AddressingMode::Extended,
            frame_version: FrameVersion::Ieee802154_2020,
        },
        sequence_number: None,
        addressing_fields: Some(AddressingFieldsRepr {
            dst_pan_id: Some(0xabcd),
            src_pan_id: None,
            dst_address: Some(Address::BROADCAST),
            src_address: Some(Address::Extended([
                0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
            ])),
        }),
        information_elements: Some(InformationElementsRepr {
            header_information_elements: heapless::Vec::new(),
            payload_information_elements: heapless::Vec::from_iter([
                PayloadInformationElementRepr::Mlme(heapless::Vec::from_iter([
                    NestedInformationElementRepr::TschSynchronization(TschSynchronizationRepr {
                        absolute_slot_number: 14,
                        join_metric: 0,
                    }),
                    NestedInformationElementRepr::TschTimeslot(TschTimeslotRepr { id: 0 }),
                    NestedInformationElementRepr::ChannelHopping(ChannelHoppingRepr {
                        hopping_sequence_id: 0,
                    }),
                    NestedInformationElementRepr::TschSlotframeAndLink(TschSlotframeAndLinkRepr {
                        number_of_slot_frames: 0,
                    }),
                ])),
            ]),
        }),
        payload: None,
    };

    let mut buffer = vec![0; frame.buffer_len()];
    frame.emit(&mut Frame::new_unchecked(&mut buffer[..]));

    assert_eq!(
        buffer,
        [
            0x40, 0xeb, 0xcd, 0xab, 0xff, 0xff, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
            0x00, 0x3f, 0x11, 0x88, 0x06, 0x1a, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1c,
            0x00, 0x01, 0xc8, 0x00, 0x01, 0x1b, 0x00
        ],
    );
}

/// https://github.com/thvdveld/dot15d4/issues/29
/// Setting `dst_pan_id` to a different value than `src_pan_id` made the `emit` function panic.
#[test]
fn issue29() {
    let frame = FrameBuilder::new_data(&[0x2b, 0x00, 0x00, 0x00])
        .set_sequence_number(1)
        .set_dst_pan_id(0xabce)
        .set_dst_address(Address::Short([0x02, 0x04]))
        .set_src_pan_id(0xabcd)
        .set_src_address(Address::Extended([
            0x00, 0x12, 0x4b, 0x00, 0x14, 0xb5, 0xd9, 0xc7,
        ]))
        .finalize()
        .unwrap();

    let mut buffer = vec![0; frame.buffer_len()];

    frame.emit(&mut Frame::new_unchecked(&mut buffer[..]));

    println!("{:?}", frame);
    println!("packet = {:#04X?}", buffer);
}