lorawan-encoding 0.6.2

Crate lorawan provides structures and tools for reading and writing LoRaWAN messages from and to a slice of bytes.
Documentation
// Copyright (c) 2018,2020 Ivaylo Petrov
//
// Licensed under the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
//
// author: Ivaylo Petrov <ivajloip@gmail.com>

use lorawan_encoding::maccommandcreator::*;
use lorawan_encoding::maccommands::*;

macro_rules! test_helper {
    ( $data:ident, $name:ident, $type:ident, $size:expr, $( ( $method:ident, $val:expr ) ,)*) => {{
        {
            assert!($type::new_as_mac_cmd(&[]).is_err());
            let mc = $type::new_as_mac_cmd(&$data[..]);
            assert!(mc.is_ok());
            if let (MacCommand::$name(res), size) = mc.unwrap() {
                assert_eq!(size, $size);
                $(
                    assert_eq!(res.$method(), $val);
                )*
            } else {
                panic!("failed to parse {}", stringify!($type));
            }
        }
    }};

    ( $name:ident, $type:ident ) => {{
        {
            let data = vec![];
            let mc = $type::new_as_mac_cmd(&data[..]);
            assert!(mc.is_ok());
            if let (MacCommand::$name(_), size) = mc.unwrap() {
                assert_eq!(size, 0);
            } else {
                panic!("failed to parse {}", stringify!($type));
            }
        }
    }};
}

#[test]
fn test_link_check_req_new() {
    test_helper!(LinkCheckReq, LinkCheckReqPayload);
}

#[test]
fn test_link_check_ans_new() {
    let data = vec![0xa, 0x0f];
    test_helper!(
        data,
        LinkCheckAns,
        LinkCheckAnsPayload,
        2,
        (margin, 10),
        (gateway_count, 15),
    );
}

#[test]
fn test_link_adr_req_new() {
    let data = vec![0x12, 0x04, 0x00, 0x45];
    let expected_channel_mask = ChannelMask::new(&[0x04, 0x00]).unwrap();
    test_helper!(
        data,
        LinkADRReq,
        LinkADRReqPayload,
        4,
        (data_rate, 1),
        (tx_power, 2),
        (channel_mask, expected_channel_mask),
        (redundancy, Redundancy::new(0x45)),
    );
}

#[test]
fn test_link_adr_ans_new() {
    let examples = [
        ([0x00], false, false, false, false),
        ([0x01], true, false, false, false),
        ([0x02], false, true, false, false),
        ([0x04], false, false, true, false),
        ([0x07], true, true, true, true),
    ];
    assert!(LinkADRReqPayload::new_as_mac_cmd(&[]).is_err());
    for &(ref v, ref e_power, ref e_dr, ref e_cm, ref e_ack) in &examples {
        let mc = LinkADRAnsPayload::new_as_mac_cmd(&v[..]);
        assert!(mc.is_ok());
        if let (MacCommand::LinkADRAns(laa), size) = mc.unwrap() {
            assert_eq!(size, 1);
            assert_eq!(laa.channel_mask_ack(), *e_power);
            assert_eq!(laa.data_rate_ack(), *e_dr);
            assert_eq!(laa.powert_ack(), *e_cm);
            assert_eq!(laa.ack(), *e_ack);
        } else {
            panic!("failed to parse LinkADRAnsPayload");
        }
    }
}

#[test]
fn test_duty_cycle_req_new() {
    #![allow(clippy::float_cmp)]
    let data = vec![0x02];
    test_helper!(
        data,
        DutyCycleReq,
        DutyCycleReqPayload,
        1,
        (max_duty_cycle_raw, 2),
        (max_duty_cycle, 0.25),
    );
}

#[test]
fn test_duty_cycle_ans_new() {
    test_helper!(DutyCycleAns, DutyCycleAnsPayload);
}

#[test]
fn test_rx_param_setup_req_new() {
    let data = vec![0x3b, 0x01, 0x02, 0x04];
    test_helper!(
        data,
        RXParamSetupReq,
        RXParamSetupReqPayload,
        4,
        (dl_settings, DLSettings::new(0x3b)),
        (frequency, Frequency::new_from_raw(&data[1..])),
    );
}

#[test]
fn test_rx_param_setup_ans_new() {
    let examples = [
        ([0x00], false, false, false, false),
        ([0x01], true, false, false, false),
        ([0x02], false, true, false, false),
        ([0x04], false, false, true, false),
        ([0x07], true, true, true, true),
    ];
    assert!(RXParamSetupAnsPayload::new_as_mac_cmd(&[]).is_err());
    for &(ref v, ref e_ch, ref e_rx2_dr, ref e_rx1_dr_offset, ref e_ack) in &examples {
        let mc = RXParamSetupAnsPayload::new_as_mac_cmd(&v[..]);
        assert!(mc.is_ok());
        if let (MacCommand::RXParamSetupAns(psa), size) = mc.unwrap() {
            assert_eq!(size, 1);
            assert_eq!(psa.channel_ack(), *e_ch);
            assert_eq!(psa.rx2_data_rate_ack(), *e_rx2_dr);
            assert_eq!(psa.rx1_dr_offset_ack(), *e_rx1_dr_offset);
            assert_eq!(psa.ack(), *e_ack);
        } else {
            panic!("failed to parse RXParamSetupAnsPayload");
        }
    }
}

#[test]
fn test_dev_status_req() {
    test_helper!(DevStatusReq, DevStatusReqPayload);
}

#[test]
fn test_dev_status_ans() {
    let data = vec![0xfe, 0x3f];
    test_helper!(
        data,
        DevStatusAns,
        DevStatusAnsPayload,
        2,
        (battery, 254),
        (margin, -1),
    );
}

#[test]
fn test_new_channel_req() {
    let data = vec![0x03, 0x01, 0x02, 0x04, 0xa5];
    test_helper!(
        data,
        NewChannelReq,
        NewChannelReqPayload,
        5,
        (channel_index, 3),
        (frequency, Frequency::new_from_raw(&data[1..4])),
        (data_rate_range, DataRateRange::new_from_raw(data[4])),
    );
}

#[test]
fn test_new_channel_ans() {
    let examples = [
        ([0x00], false, false, false),
        ([0x01], true, false, false),
        ([0x02], false, true, false),
        ([0x03], true, true, true),
    ];
    assert!(NewChannelAnsPayload::new_as_mac_cmd(&[]).is_err());
    for &(ref v, ref e_ch_freq, ref e_drr, ref e_ack) in &examples {
        let mc = NewChannelAnsPayload::new_as_mac_cmd(&v[..]);
        assert!(mc.is_ok());
        if let (MacCommand::NewChannelAns(nca), size) = mc.unwrap() {
            assert_eq!(size, 1);
            assert_eq!(nca.data_rate_range_ack(), *e_drr);
            assert_eq!(nca.channel_freq_ack(), *e_ch_freq);
            assert_eq!(nca.ack(), *e_ack);
        } else {
            panic!("failed to parse RXParamSetupAnsPayload");
        }
    }
}

#[test]
fn test_rx_timing_setup_req() {
    let data = vec![0x02];
    test_helper!(
        data,
        RXTimingSetupReq,
        RXTimingSetupReqPayload,
        1,
        (delay, 2),
    );
}

#[test]
fn test_rx_timing_setup_ans() {
    test_helper!(RXTimingSetupAns, RXTimingSetupAnsPayload);
}

#[test]
fn test_parse_mac_commands_empty_downlink() {
    assert_eq!(parse_mac_commands(&[], false).count(), 0);
}

#[test]
fn test_parse_mac_commands_empty_uplink() {
    assert_eq!(parse_mac_commands(&[], true).count(), 0);
}

#[test]
fn test_parse_mac_commands_with_multiple_cmds() {
    let data = mac_cmds_payload();
    let mut commands = parse_mac_commands(&data[..], true);
    assert_eq!(
        commands.next(),
        Some(MacCommand::LinkCheckReq(LinkCheckReqPayload()))
    );
    let expected = LinkADRAnsPayload::new_as_mac_cmd(&data[2..]).unwrap().0;
    assert_eq!(commands.next(), Some(expected));
}

#[test]
fn test_parse_mac_commands_with_multiple_cmds_with_payloads() {
    let data = vec![3, 0, 0, 0, 112, 3, 0, 0, 255, 0];
    let mut commands = parse_mac_commands(&data, false);

    assert_eq!(
        commands.next(),
        Some(MacCommand::LinkADRReq(
            LinkADRReqPayload::new(&[0, 0, 0, 112]).unwrap()
        ))
    );

    assert_eq!(
        commands.next(),
        Some(MacCommand::LinkADRReq(
            LinkADRReqPayload::new(&[0, 0, 255, 0]).unwrap()
        ))
    );
}

fn mac_cmds_payload() -> Vec<u8> {
    vec![LinkCheckReqPayload::cid(), LinkADRAnsPayload::cid(), 0x00]
}

#[test]
fn test_dl_settings() {
    let dl_settings = DLSettings::new(0x5b);
    assert_eq!(dl_settings.rx1_dr_offset(), 0x05);
    assert_eq!(dl_settings.rx2_data_rate(), 0x0b);
}

#[test]
fn test_channel_mask() {
    let data = vec![0x03, 0x10];
    let mut expected = vec![false; 16];
    expected[0] = true;
    expected[1] = true;
    expected[12] = true;
    let chan_mask = ChannelMask::new(&data[..]);
    assert!(chan_mask.is_ok());
    assert_eq!(&chan_mask.unwrap().statuses()[..], &expected[..]);
}

#[test]
fn test_redundancy_channel_mask_control() {
    let redundancy = Redundancy::new(0x7f);
    assert_eq!(redundancy.channel_mask_control(), 0x07);
}

#[test]
fn test_redundancy_number_of_transmissions() {
    let redundancy = Redundancy::new(0x7f);
    assert_eq!(redundancy.number_of_transmissions(), 0x0f);
}

#[test]
fn test_frequency_new_bad_payload() {
    assert!(Frequency::new(&[]).is_none());
}

#[test]
fn test_frequency_value() {
    let data = frequency_payload();
    let freq = Frequency::new(&data[..]);
    assert!(freq.is_some());
    assert_eq!(freq.unwrap().value(), 26_265_700);
}

fn frequency_payload() -> Vec<u8> {
    vec![0x01, 0x02, 0x04]
}

#[test]
fn test_data_rate_range() {
    let drr_raw = DataRateRange::new(0xa5);
    assert!(drr_raw.is_ok());
    let drr = drr_raw.unwrap();
    assert_eq!(drr.max_data_rate(), 0x0a);
    assert_eq!(drr.min_data_range(), 0x05);
}

#[test]
fn test_data_rate_range_inversed_min_and_max() {
    let drr = DataRateRange::new(0x5a);
    assert!(drr.is_err());
}

#[test]
fn test_data_rate_range_max_equals_min() {
    let drr_raw = DataRateRange::new(0x55);
    assert!(drr_raw.is_ok());
}

#[test]
fn test_mac_commands_len_with_creators() {
    let rx_timing_setup_req = RXTimingSetupReqCreator::new();
    let dev_status_req = DevStatusReqCreator::new();
    let cmds: Vec<&dyn SerializableMacCommand> = vec![&rx_timing_setup_req, &dev_status_req];

    assert_eq!(mac_commands_len(&cmds[..]), 3);
}

#[test]
fn test_mac_commands_len_with_mac_cmds() {
    let rx_timing_setup_req = RXTimingSetupReqPayload::new_as_mac_cmd(&[0x02]).unwrap().0;
    let dev_status_ans = DevStatusAnsPayload::new_as_mac_cmd(&[0xfe, 0x3f])
        .unwrap()
        .0;
    let cmds: Vec<&dyn SerializableMacCommand> = vec![&rx_timing_setup_req, &dev_status_ans];

    assert_eq!(mac_commands_len(&cmds[..]), 5);
}