Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5dd23a9c authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "liblmp: Add tests from Bluetooth SIG LMP TestSuite"

parents e81bd659 4abf488e
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -41,3 +41,22 @@ genrule {
        "lmp_packets.rs",
    ],
}

rust_test_host {
    name: "liblmp_tests",
    crate_name: "lmp",
    srcs: [
        "src/lib.rs",
        ":LmpGeneratedPackets_rust",
    ],
    auto_gen_config: true,
    edition: "2018",
    proc_macros: ["libnum_derive", "libpaste"],
    rustlibs: [
        "libbt_packets",
        "libbytes",
        "libnum_traits",
        "libthiserror",
        "libpin_utils",
    ],
}
+88 −0
Original line number Diff line number Diff line
@@ -618,3 +618,91 @@ pub async fn respond(ctx: &impl Context, request: lmp::IoCapabilityReqPacket) ->

    Ok(())
}

#[cfg(test)]
mod tests {
    use crate::procedure::Context;
    use crate::test::{sequence, TestContext};
    // simple pairing is part of authentication procedure
    use super::super::authentication::initiate;
    use super::super::authentication::respond;

    #[test]
    fn initiate_size() {
        let context = crate::test::TestContext::new();
        let procedure = super::initiate(&context);

        fn assert_max_size<T>(_value: T, limit: usize) {
            let type_name = std::any::type_name::<T>();
            let size = std::mem::size_of::<T>();
            println!("Size of {}: {}", type_name, size);
            assert!(size < limit)
        }

        assert_max_size(procedure, 250);
    }

    #[test]
    fn numeric_comparaison_initiator_success() {
        let context = TestContext::new();
        let procedure = initiate;

        include!("../../test/SP/BV-06-C.in");
    }

    #[test]
    fn numeric_comparaison_responder_success() {
        let context = TestContext::new();
        let procedure = respond;

        include!("../../test/SP/BV-07-C.in");
    }

    #[test]
    fn numeric_comparaison_initiator_failure_on_initiating_side() {
        let context = TestContext::new();
        let procedure = initiate;

        include!("../../test/SP/BV-08-C.in");
    }

    #[test]
    fn numeric_comparaison_responder_failure_on_initiating_side() {
        let context = TestContext::new();
        let procedure = respond;

        include!("../../test/SP/BV-09-C.in");
    }

    #[test]
    fn numeric_comparaison_initiator_failure_on_responding_side() {
        let context = TestContext::new();
        let procedure = initiate;

        include!("../../test/SP/BV-10-C.in");
    }

    #[test]
    fn numeric_comparaison_responder_failure_on_responding_side() {
        let context = TestContext::new();
        let procedure = respond;

        include!("../../test/SP/BV-11-C.in");
    }

    #[test]
    fn passkey_entry_initiator_success() {
        let context = TestContext::new();
        let procedure = initiate;

        include!("../../test/SP/BV-12-C.in");
    }

    #[test]
    fn passkey_entry_responder_success() {
        let context = TestContext::new();
        let procedure = respond;

        include!("../../test/SP/BV-13-C.in");
    }
}
+87 −0
Original line number Diff line number Diff line
use std::cell::RefCell;
use std::collections::VecDeque;
use std::convert::{TryFrom, TryInto};
use std::future::Future;
use std::pin::Pin;
use std::task::{self, Poll};

use crate::packets::{hci, lmp};

use crate::procedure::Context;

#[derive(Default)]
pub struct TestContext {
    pub in_lmp_packets: RefCell<VecDeque<lmp::PacketPacket>>,
    pub out_lmp_packets: RefCell<VecDeque<lmp::PacketPacket>>,
    pub hci_events: RefCell<VecDeque<hci::EventPacket>>,
    pub hci_commands: RefCell<VecDeque<hci::CommandPacket>>,
}

impl TestContext {
    pub fn new() -> Self {
        Default::default()
    }
}

impl Context for TestContext {
    fn poll_hci_command<C: TryFrom<hci::CommandPacket>>(&self) -> Poll<C> {
        let command =
            self.hci_commands.borrow().front().and_then(|command| command.clone().try_into().ok());

        if let Some(command) = command {
            self.hci_commands.borrow_mut().pop_front();
            Poll::Ready(command)
        } else {
            Poll::Pending
        }
    }

    fn poll_lmp_packet<P: TryFrom<lmp::PacketPacket>>(&self) -> Poll<P> {
        let packet =
            self.in_lmp_packets.borrow().front().and_then(|packet| packet.clone().try_into().ok());

        if let Some(packet) = packet {
            self.in_lmp_packets.borrow_mut().pop_front();
            Poll::Ready(packet)
        } else {
            Poll::Pending
        }
    }

    fn send_hci_event<E: Into<hci::EventPacket>>(&self, event: E) {
        self.hci_events.borrow_mut().push_back(event.into());
    }

    fn send_lmp_packet<P: Into<lmp::PacketPacket>>(&self, packet: P) {
        self.out_lmp_packets.borrow_mut().push_back(packet.into());
    }

    fn peer_address(&self) -> hci::Address {
        hci::Address { bytes: [0; 6] }
    }

    fn peer_handle(&self) -> u16 {
        0x42
    }

    fn peer_extended_features(&self, features_page: u8) -> Option<u64> {
        if features_page == 1 {
            Some(1)
        } else {
            Some(0)
        }
    }

    fn extended_features(&self, features_page: u8) -> u64 {
        if features_page == 1 {
            1
        } else {
            0
        }
    }
}

pub fn poll(future: Pin<&mut impl Future<Output = ()>>) -> Poll<()> {
    let waker = crate::future::noop_waker();
    future.poll(&mut task::Context::from_waker(&waker))
}
+5 −0
Original line number Diff line number Diff line
mod context;
mod sequence;

pub(crate) use context::{poll, TestContext};
pub(crate) use sequence::{sequence, sequence_body};
+115 −0
Original line number Diff line number Diff line
macro_rules! sequence_body {
        ($ctx:ident, ) => { None };
        ($ctx:ident, Lower Tester -> IUT: $packet:ident {
            $($name:ident: $value:expr),* $(,)?
        } $($tail:tt)*) => {{
            use crate::packets::lmp::*;

            let builder = paste! {
                [<$packet Builder>] {
                    $($name: $value),*
                }
            };
            $ctx.0.in_lmp_packets.borrow_mut().push_back(builder.build().into());

            let poll = crate::test::poll($ctx.1.as_mut());

            assert!($ctx.0.in_lmp_packets.borrow().is_empty(), "{} was not consumed by procedure", stringify!($packet));

            println!("Lower Tester -> IUT: {}", stringify!($packet));

            sequence_body!($ctx, $($tail)*).or(Some(poll))
        }};
        ($ctx:ident, Upper Tester -> IUT: $packet:ident {
            $($name:ident: $value:expr),* $(,)?
        } $($tail:tt)*) => {{
            use crate::packets::hci::*;

            let builder = paste! {
                [<$packet Builder>] {
                    $($name: $value),*
                }
            };
            $ctx.0.hci_commands.borrow_mut().push_back(builder.build().into());

            let poll = crate::test::poll($ctx.1.as_mut());

            assert!($ctx.0.hci_commands.borrow().is_empty(), "{} was not consumed by procedure", stringify!($packet));

            println!("Upper Tester -> IUT: {}", stringify!($packet));

            sequence_body!($ctx, $($tail)*).or(Some(poll))
        }};
        ($ctx:ident, IUT -> Upper Tester: $packet:ident {
            $($name:ident: $expected_value:expr),* $(,)?
        } $($tail:tt)*) => {{
            use crate::packets::hci::*;

            paste! {
                let packet: [<$packet Packet>] = $ctx.0.hci_events.borrow_mut().pop_front().expect("No hci packet").try_into().unwrap();
            }

            $(
                let value = paste! { packet.[<get_ $name>]() };
                assert_eq!(value.clone(), $expected_value);
            )*

            println!("IUT -> Upper Tester: {}", stringify!($packet));

            sequence_body!($ctx, $($tail)*)
        }};
        ($ctx:ident, IUT -> Lower Tester: $packet:ident {
            $($name:ident: $expected_value:expr),* $(,)?
        } $($tail:tt)*) => {{
            use crate::packets::lmp::*;

            paste! {
                let packet: [<$packet Packet>] = $ctx.0.out_lmp_packets.borrow_mut().pop_front().expect("No lmp packet").try_into().unwrap();
            }

            $(
                let value = paste! { packet.[<get_ $name>]() };
                assert_eq!(value.clone(), $expected_value);
            )*

            println!("IUT -> Lower Tester: {}", stringify!($packet));

            sequence_body!($ctx, $($tail)*)
        }};
        ($ctx:ident, repeat $number:literal times {
            $($inner:tt)*
        } $($tail:tt)*) => {{
            println!("repeat {}", $number);
            for _ in 0..$number {
                sequence_body!($ctx, $($inner)*);
            }
            println!("endrepeat");

            sequence_body!($ctx, $($tail)*)
        }};
    }

macro_rules! sequence {
        ($procedure_fn:path, $context:path, $($tail:tt)*) => ({
            use paste::paste;
            use std::convert::TryInto;

            let procedure = $procedure_fn(&$context);

            use crate::future::pin;
            pin!(procedure);

            let mut ctx = (&$context, procedure);
            use crate::test::sequence_body;
            let last_poll = sequence_body!(ctx, $($tail)*).unwrap();

            assert!(last_poll.is_ready());
            assert!($context.in_lmp_packets.borrow().is_empty());
            assert!($context.out_lmp_packets.borrow().is_empty());
            assert!($context.hci_commands.borrow().is_empty());
            assert!($context.hci_events.borrow().is_empty());
        });
    }

pub(crate) use sequence;
pub(crate) use sequence_body;
Loading