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

Commit 745b08d7 authored by Martin Geisler's avatar Martin Geisler
Browse files

pdl: Simplify the generation of unit tests

Instead of copy-pasting the PDL code for little- and big-endian unit
tests, we can use a macro to handle this for us. This also helps
enforce a consistent naming scheme for the auto-generated files.

Test: atest pdl_tests
Change-Id: I3d124501a22e807c42b498b10357d8cedf456244
parent d24a3803
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -37,6 +37,9 @@ rust_test_host {
    name: "pdl_tests",
    defaults: ["pdl_defaults"],
    srcs: ["src/main.rs"],
    proc_macros: [
        "libpaste",
    ],
    test_suites: ["general-tests"],
    enabled: false, // rustfmt is only available on x86.
    arch: {
@@ -55,7 +58,8 @@ rust_test_host {
        "tests/generated/generate_chunk_read_multiple_fields.rs",
        "tests/generated/packet_decl_complex_big_endian.rs",
        "tests/generated/packet_decl_complex_little_endian.rs",
        "tests/generated/packet_decl_empty.rs",
        "tests/generated/packet_decl_empty_big_endian.rs",
        "tests/generated/packet_decl_empty_little_endian.rs",
        "tests/generated/packet_decl_simple_big_endian.rs",
        "tests/generated/packet_decl_simple_little_endian.rs",
        "tests/generated/preamble.rs",
+1 −0
Original line number Diff line number Diff line
@@ -23,3 +23,4 @@ bytes = "1.2.1"
num-derive = "0.3.3"
num-traits = "0.2.15"
thiserror = "1.0.37"
paste = "1.0.6"
+66 −111
Original line number Diff line number Diff line
@@ -308,81 +308,69 @@ mod tests {
    use crate::ast;
    use crate::parser::parse_inline;
    use crate::test_utils::{assert_snapshot_eq, rustfmt};
    use paste::paste;

    /// Parse a string fragment as a PDL file.
    /// Create a unit test for the given PDL `code`.
    ///
    /// # Panics
    /// The unit test will compare the generated Rust code for all
    /// declarations with previously saved snapshots. The snapshots
    /// are read from `"tests/generated/{name}_{endianness}_{id}.rs"`
    /// where `is` taken from the declaration.
    ///
    /// Panics on parse errors.
    pub fn parse_str(text: &str) -> ast::File {
        let mut db = ast::SourceDatabase::new();
        parse_inline(&mut db, String::from("stdin"), String::from(text)).expect("parse error")
    }

    #[test]
    fn test_generate_packet_decl_empty() {
        let file = parse_str(
            r#"
              big_endian_packets
              packet Foo {}
            "#,
        );
        let scope = lint::Scope::new(&file).unwrap();
        let decl = &file.declarations[0];
        let actual_code = generate_decl(&scope, &file, decl);
        assert_snapshot_eq("tests/generated/packet_decl_empty.rs", &rustfmt(&actual_code));
    }

    /// When adding new tests, use `touch` to create the missing files
    /// and use `UPDATE_SNAPSHOTS=1 cargo test` to populate them.
    ///
    /// The `code` cannot have an endianness declaration, instead you
    /// must supply either `little_endian` or `big_endian` as
    /// `endianness`.
    macro_rules! make_pdl_test {
        ($name:ident, $code:expr, $endianness:ident) => {
            paste! {
                #[test]
    fn test_generate_packet_decl_simple_little_endian() {
        let file = parse_str(
            r#"
              little_endian_packets

              packet Foo {
                x: 8,
                y: 16,
                z: 24,
              }
            "#,
        );
        let scope = lint::Scope::new(&file).unwrap();
        let decl = &file.declarations[0];
        let actual_code = generate_decl(&scope, &file, decl);
                fn [< test_ $name _ $endianness >]() {
                    let name = stringify!($name);
                    let endianness = stringify!($endianness);
                    let code = format!("{endianness}_packets\n{}", $code);
                    let mut db = ast::SourceDatabase::new();
                    let file = parse_inline(&mut db, String::from("test"), code).unwrap();
                    let actual_code = generate(&db, &file);
                    assert_snapshot_eq(
            "tests/generated/packet_decl_simple_little_endian.rs",
                        &format!("tests/generated/{name}_{endianness}.rs"),
                        &rustfmt(&actual_code),
                    );
                }
            }
        };
    }

    #[test]
    fn test_generate_packet_decl_simple_big_endian() {
        let file = parse_str(
            r#"
              big_endian_packets
    /// Create little- and bit-endian tests for the given PDL `code`.
    ///
    /// The `code` cannot have an endianness declaration: we will
    /// automatically generate unit tests for both
    /// "little_endian_packets" and "big_endian_packets".
    macro_rules! test_pdl {
        ($name:ident, $code:expr $(,)?) => {
            make_pdl_test!($name, $code, little_endian);
            make_pdl_test!($name, $code, big_endian);
        };
    }

    test_pdl!(packet_decl_empty, "packet Foo {}");

    test_pdl!(
        packet_decl_simple,
        r#"
          packet Foo {
            x: 8,
            y: 16,
            z: 24,
          }
            "#,
        );
        let scope = lint::Scope::new(&file).unwrap();
        let decl = &file.declarations[0];
        let actual_code = generate_decl(&scope, &file, decl);
        assert_snapshot_eq(
            "tests/generated/packet_decl_simple_big_endian.rs",
            &rustfmt(&actual_code),
        "#
    );
    }

    #[test]
    fn test_generate_packet_decl_complex_little_endian() {
        let file = parse_str(
    test_pdl!(
        packet_decl_complex,
        r#"
              little_endian_packets

          packet Foo {
            a: 3,
            b: 8,
@@ -393,39 +381,6 @@ mod tests {
          }
        "#,
    );
        let scope = lint::Scope::new(&file).unwrap();
        let decl = &file.declarations[0];
        let actual_code = generate_decl(&scope, &file, decl);
        assert_snapshot_eq(
            "tests/generated/packet_decl_complex_little_endian.rs",
            &rustfmt(&actual_code),
        );
    }

    #[test]
    fn test_generate_packet_decl_complex_big_endian() {
        let file = parse_str(
            r#"
              big_endian_packets

              packet Foo {
                a: 3,
                b: 8,
                c: 5,
                d: 24,
                e: 12,
                f: 4,
              }
            "#,
        );
        let scope = lint::Scope::new(&file).unwrap();
        let decl = &file.declarations[0];
        let actual_code = generate_decl(&scope, &file, decl);
        assert_snapshot_eq(
            "tests/generated/packet_decl_complex_big_endian.rs",
            &rustfmt(&actual_code),
        );
    }

    #[test]
    fn test_get_field_range() {
+37 −0
Original line number Diff line number Diff line
// @generated rust packets from test

#![allow(warnings, missing_docs)]

use bytes::{BufMut, Bytes, BytesMut};
use num_derive::{FromPrimitive, ToPrimitive};
use num_traits::{FromPrimitive, ToPrimitive};
use std::convert::{TryFrom, TryInto};
use std::fmt;
use std::sync::Arc;
use thiserror::Error;

type Result<T> = std::result::Result<T, Error>;

#[derive(Debug, Error)]
pub enum Error {
    #[error("Packet parsing failed")]
    InvalidPacketError,
    #[error("{field} was {value:x}, which is not known")]
    ConstraintOutOfBounds { field: String, value: u64 },
    #[error("when parsing {obj} needed length of {wanted} but got {got}")]
    InvalidLengthError { obj: String, wanted: usize, got: usize },
    #[error("Due to size restrictions a struct could not be parsed.")]
    ImpossibleStructError,
    #[error("when parsing field {obj}.{field}, {value} is not a valid {type_} value")]
    InvalidEnumValueError { obj: String, field: String, value: u64, type_: String },
}

#[derive(Debug, Error)]
#[error("{0}")]
pub struct TryFromError(&'static str);

pub trait Packet {
    fn to_bytes(self) -> Bytes;
    fn to_vec(self) -> Vec<u8>;
}

#[derive(Debug)]
struct FooData {
    a: u8,
+37 −0
Original line number Diff line number Diff line
// @generated rust packets from test

#![allow(warnings, missing_docs)]

use bytes::{BufMut, Bytes, BytesMut};
use num_derive::{FromPrimitive, ToPrimitive};
use num_traits::{FromPrimitive, ToPrimitive};
use std::convert::{TryFrom, TryInto};
use std::fmt;
use std::sync::Arc;
use thiserror::Error;

type Result<T> = std::result::Result<T, Error>;

#[derive(Debug, Error)]
pub enum Error {
    #[error("Packet parsing failed")]
    InvalidPacketError,
    #[error("{field} was {value:x}, which is not known")]
    ConstraintOutOfBounds { field: String, value: u64 },
    #[error("when parsing {obj} needed length of {wanted} but got {got}")]
    InvalidLengthError { obj: String, wanted: usize, got: usize },
    #[error("Due to size restrictions a struct could not be parsed.")]
    ImpossibleStructError,
    #[error("when parsing field {obj}.{field}, {value} is not a valid {type_} value")]
    InvalidEnumValueError { obj: String, field: String, value: u64, type_: String },
}

#[derive(Debug, Error)]
#[error("{0}")]
pub struct TryFromError(&'static str);

pub trait Packet {
    fn to_bytes(self) -> Bytes;
    fn to_vec(self) -> Vec<u8>;
}

#[derive(Debug)]
struct FooData {
    a: u8,
Loading