use mavinspect::parser::Inspector; use mavinspect::protocol::{Filter, Microservices}; fn default_dialect_paths() -> Vec<&'static str> { vec![ "./message_definitions/standard", "./message_definitions/extra", ] } fn extra_dialect_paths() -> Vec<&'static str> { vec!["./message_definitions/extra"] } #[test] fn filter_by_empty_filters() { let inspector = Inspector::builder() .set_sources(&default_dialect_paths()) .set_include(&["common"]) .build() .unwrap(); let protocol = inspector.parse().unwrap(); let filtered_protocol = protocol.filtered(&Filter::default()); let common = filtered_protocol .get_dialect_by_canonical_name("common") .unwrap(); assert!(common .microservices() .contains(Microservices::HEARTBEAT | Microservices::COMMAND)); assert!(common.contains_enum_with_name("MAV_CMD")); assert!(common.contains_command_with_name("MAV_CMD_NAV_LAND")); assert!(common .get_enum_by_name("MAV_CMD") .unwrap() .has_entry_with_name("MAV_CMD_NAV_LAND")); assert!(common.contains_enum_with_name("PRECISION_LAND_MODE")); assert!(common.contains_message_with_name("COMMAND_INT")); assert!(common.contains_message_with_name("COMMAND_LONG")); assert!(common.contains_message_with_name("COMMAND_ACK")); assert!(common.contains_message_with_name("COMMAND_CANCEL")); let minimal = filtered_protocol .get_dialect_by_canonical_name("common") .unwrap(); assert!(minimal.microservices().contains(Microservices::HEARTBEAT)); assert!(common.contains_message_with_name("HEARTBEAT")); } #[test] fn filter_by_microservices_and_messages() { let inspector = Inspector::builder() .set_sources(&default_dialect_paths()) .set_include(&["common", "MAVInspect_test", "minimal", "standard"]) .build() .unwrap(); let protocol = inspector.parse().unwrap(); use mavinspect::protocol::{Filter, Microservices}; // Filter let filtered_protocol = protocol.filtered( &Filter::by_microservices(Microservices::HEARTBEAT | Microservices::COMMAND).with_messages( &[ // From `minimal` "PROTOCOL_VERSION", // From `MavInspect_test` "CLONE", "DEFAULT", ], ), ); // Let's inspect `common` dialect let common = filtered_protocol .get_dialect_by_canonical_name("common") .unwrap(); // it should contain `HEARTBEAT` messages assert!(common.contains_message_with_name("HEARTBEAT")); // it should contain `HEARTBEAT` messages assert!(common.contains_message_with_name("PROTOCOL_VERSION")); // Let's inspect `MAVInspect_test` dialect let mav_inspect_test = filtered_protocol .get_dialect_by_canonical_name("mav_inspect_test") .unwrap(); // it should contain `CLONE` and `DEFAULT` messages assert!(mav_inspect_test.contains_message_with_name("CLONE")); assert!(mav_inspect_test.contains_message_with_name("DEFAULT")); // it should contain `CLONE` and `DEFAULT` enums assert!(mav_inspect_test.contains_enum_with_name("CLONE")); assert!(mav_inspect_test.contains_enum_with_name("DEFAULT")); // it should NOT contain `COPY` or `FROM` enums assert!(!mav_inspect_test.contains_enum_with_name("COPY")); assert!(!mav_inspect_test.contains_enum_with_name("FROM")); } #[test] fn filter_by_all_options() { let inspector = Inspector::builder() .set_sources(&default_dialect_paths()) .set_include(&["common"]) .build() .unwrap(); let protocol = inspector.parse().unwrap(); use mavinspect::protocol::{Filter, Microservices}; // Filter let filtered_protocol = protocol.filtered( &Filter::by_microservices(Microservices::HEARTBEAT | Microservices::MISSION) .with_messages(&["FILE_TRANSFER_PROTOCOL"]) .with_enums(&["GIMBAL_DEVICE_CAP_FLAGS"]) .with_commands(&["MAV_CMD_SET_*"]), ); // Let's inspect `common` dialect let common = filtered_protocol .get_dialect_by_canonical_name("common") .unwrap(); // it should contain `HEARTBEAT` messages assert!(common.contains_message_with_name("HEARTBEAT")); // and `COMMAND_LONG` message since `MISSION` microservice depends on some commands assert!(common.contains_message_with_name("COMMAND_LONG")); // `FILE_TRANSFER_PROTOCOL` (part of file transfer protocol) was explicitly required by `set_messages` assert!(common.contains_message_with_name("FILE_TRANSFER_PROTOCOL")); // but these messages should be filtered out assert!(!common.contains_message_with_name("PROTOCOL_VERSION")); assert!(!common.contains_message_with_name("CAMERA_TRIGGER")); // this enum was explicitly requested assert!(common.contains_enum_with_name("GIMBAL_DEVICE_CAP_FLAGS")); // since we have commands, then `MAV_CMD` enum should be present assert!(common.contains_enum_with_name("MAV_CMD")); // and this command was explicitly requested by `MAV_CMD_SET_*` pattern assert!(common.contains_command_with_name("MAV_CMD_SET_MESSAGE_INTERVAL")); // as well as these commands should be present as a part of mission protocol assert!(common.contains_command_with_name("MAV_CMD_DO_SET_MISSION_CURRENT")); assert!(common.contains_command_with_name("MAV_CMD_NAV_WAYPOINT")); // but not these assert!(!common.contains_command_with_name("MAV_CMD_INJECT_FAILURE")); assert!(!common.contains_command_with_name("MAV_CMD_GET_HOME_POSITION")); } #[test] fn filter_by_microservices_common_heartbeat() { let inspector = Inspector::builder() .set_sources(&default_dialect_paths()) .set_include(&["common"]) .build() .unwrap(); let protocol = inspector.parse().unwrap(); let filtered_protocol = protocol.filtered(&Filter::by_microservices(Microservices::HEARTBEAT)); let dialect = filtered_protocol .get_dialect_by_canonical_name("common") .unwrap(); let microservices = dialect.microservices(); assert!(microservices.contains(Microservices::HEARTBEAT)); assert!(!microservices.contains(Microservices::COMMAND)); assert!(!microservices.contains(Microservices::FTP)); } #[test] fn filter_by_microservices_commands() { let inspector = Inspector::builder() .set_sources(&default_dialect_paths()) .set_include(&["common"]) .build() .unwrap(); let protocol = inspector.parse().unwrap(); let filtered_protocol = protocol.filtered(&Filter::by_microservices(Microservices::COMMAND)); let dialect = filtered_protocol .get_dialect_by_canonical_name("common") .unwrap(); let microservices = dialect.microservices(); assert!(microservices.contains(Microservices::COMMAND)); assert!(dialect.contains_enum_with_name("MAV_CMD")); assert!(dialect.contains_command_with_name("MAV_CMD_NAV_LAND")); assert!(dialect .get_enum_by_name("MAV_CMD") .unwrap() .has_entry_with_name("MAV_CMD_NAV_LAND")); assert!(dialect.contains_enum_with_name("PRECISION_LAND_MODE")); assert!(!microservices.contains(Microservices::PING)); assert!(!microservices.contains(Microservices::HEARTBEAT)); assert!(!microservices.contains(Microservices::MISSION)); assert!(!microservices.contains(Microservices::FTP)); } #[test] fn filter_implicit_command_protocol() { let inspector = Inspector::builder() .set_sources(&default_dialect_paths()) .set_include(&["common"]) .build() .unwrap(); let protocol = inspector.parse().unwrap(); let filtered_protocol = protocol.filtered( &Filter::by_microservices(Microservices::default()) .with_commands(&["MAV_CMD_DO_CHANGE_SPEED", "MAV_CMD_DO_SET_ROI*"]), ); let dialect = filtered_protocol .get_dialect_by_canonical_name("common") .unwrap(); // `MAV_CMD` enum should be present assert!(dialect.contains_enum_with_name("MAV_CMD")); let mav_cmd = dialect.get_enum_by_name("MAV_CMD").unwrap(); // These messages are required by command protocol assert!(dialect.contains_message_with_name("COMMAND_LONG")); assert!(dialect.contains_message_with_name("COMMAND_INT")); assert!(dialect.contains_message_with_name("COMMAND_ACK")); assert!(dialect.contains_message_with_name("COMMAND_CANCEL")); // This enum is required by command protocol assert!(dialect.contains_enum_with_name("MAV_FRAME")); // This enum is required by `MAV_CMD_DO_CHANGE_SPEED` command assert!(dialect.contains_enum_with_name("SPEED_TYPE")); // These enums are required by `MAV_CMD_DO_SET_ROI*` commands assert!(dialect.contains_enum_with_name("MAV_ROI")); // These commands were explicitly requested assert!(mav_cmd.has_entry_with_name("MAV_CMD_DO_CHANGE_SPEED")); assert!(mav_cmd.has_entry_with_name("MAV_CMD_DO_SET_ROI")); assert!(mav_cmd.has_entry_with_name("MAV_CMD_DO_SET_ROI_LOCATION")); assert!(mav_cmd.has_entry_with_name("MAV_CMD_DO_SET_ROI_NONE")); /* and others */ // These commands should not be present assert!(!mav_cmd.has_entry_with_name("MAV_CMD_DO_INVERTED_FLIGHT")); assert!(!mav_cmd.has_entry_with_name("MAV_CMD_DO_GRIPPER")); assert!(!mav_cmd.has_entry_with_name("MAV_CMD_PREFLIGHT_CALIBRATION")); /* and others */ } #[test] fn filter_by_microservices_mission() { let inspector = Inspector::builder() .set_sources(&default_dialect_paths()) .set_include(&["common"]) .build() .unwrap(); let protocol = inspector.parse().unwrap(); let filtered_protocol = protocol.filtered(&Filter::by_microservices(Microservices::MISSION)); let dialect = filtered_protocol .get_dialect_by_canonical_name("common") .unwrap(); let microservices = dialect.microservices(); assert!(microservices.contains(Microservices::MISSION)); assert!(microservices.contains(Microservices::COMMAND)); assert!(dialect.contains_enum_with_name("MAV_CMD")); assert!(dialect.contains_command_with_name("MAV_CMD_NAV_LAND")); assert!(!dialect.contains_command_with_name("MAV_CMD_REQUEST_MESSAGE")); assert!(!microservices.contains(Microservices::FTP)); } #[test] fn filter_by_microservices_explicit_enums() { let inspector = Inspector::builder() .set_sources(&default_dialect_paths()) .set_include(&["common"]) .build() .unwrap(); let protocol = inspector.parse().unwrap(); let filtered_protocol = protocol.filtered(&Filter::by_microservices(Microservices::COMPONENT_METADATA)); let dialect = filtered_protocol .get_dialect_by_canonical_name("common") .unwrap(); let microservices = dialect.microservices(); assert!(microservices.contains(Microservices::COMPONENT_METADATA)); assert!(microservices.contains(Microservices::COMMAND)); assert!(dialect.contains_enum_with_name("MAV_CMD")); assert!(dialect.contains_enum_with_name("COMP_METADATA_TYPE")); assert!(dialect.contains_command_with_name("MAV_CMD_REQUEST_MESSAGE")); assert!(!dialect.contains_command_with_name("MAV_CMD_NAV_LAND")); assert!(!microservices.contains(Microservices::FTP)); } #[test] fn filter_by_microservices_diverse() { let inspector = Inspector::builder() .set_sources(&default_dialect_paths()) .set_include(&["common"]) .build() .unwrap(); let protocol = inspector.parse().unwrap(); let filtered_protocol = protocol.filtered(&Filter::by_microservices( Microservices::COMMAND | Microservices::PING | Microservices::HEARTBEAT, )); let dialect = filtered_protocol .get_dialect_by_canonical_name("common") .unwrap(); let microservices = dialect.microservices(); assert!(microservices.contains(Microservices::HEARTBEAT)); assert!(microservices.contains(Microservices::COMMAND)); assert!(microservices.contains(Microservices::PING)); assert!(!microservices.contains(Microservices::MISSION)); assert!(!microservices.contains(Microservices::FTP)); } #[test] fn fingerprints_are_stable() { let inspector = Inspector::builder() .set_sources(&extra_dialect_paths()) .set_include(&["mav_inspect_test"]) .build() .unwrap(); let protocol = inspector.parse().unwrap(); let mav_inspect_test = protocol .get_dialect_by_canonical_name("mav_inspect_test") .unwrap(); let minimal = protocol.get_dialect_by_canonical_name("minimal").unwrap(); // The following assertions are potentially fragile since they depend on MAVLink XML definitions assert_eq!(mav_inspect_test.fingerprint(), 1576349849285860208); assert_eq!(minimal.fingerprint(), 9732507902367574693); assert_eq!(protocol.fingerprint(), 10369982728649782296); } #[test] fn enum_entry_prefix_stripping() { let inspector = Inspector::builder() .set_sources(&extra_dialect_paths()) .set_include(&["crazy_flight"]) .build() .unwrap(); let protocol = inspector.parse().unwrap(); let dialect = protocol .get_dialect_by_canonical_name("crazy_flight") .unwrap(); let mav_enum = dialect .get_enum_by_name("CRAZYFLIGHT_INSANITY_LEVEL") .unwrap(); for entry in mav_enum.entries() { assert_eq!( format!("{}_{}", mav_enum.name(), entry.name_stripped()), entry.name() ); } }