From a474433d12e72a353bb6fcc347dd18d1b4c6ebb2 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Thu, 12 Jan 2023 15:56:43 +0100 Subject: Reimplement check_invalid_utf8_is_rejected as BehaviourTest Signed-off-by: Matthias Beyer --- .../src/behaviour/invalid_utf8_is_rejected.rs | 47 ++++++++++++ mqtt-tester/src/behaviour/mod.rs | 1 + mqtt-tester/src/client_report.rs | 83 ++++++---------------- 3 files changed, 71 insertions(+), 60 deletions(-) create mode 100644 mqtt-tester/src/behaviour/invalid_utf8_is_rejected.rs (limited to 'mqtt-tester/src') diff --git a/mqtt-tester/src/behaviour/invalid_utf8_is_rejected.rs b/mqtt-tester/src/behaviour/invalid_utf8_is_rejected.rs new file mode 100644 index 0000000..eea0fa5 --- /dev/null +++ b/mqtt-tester/src/behaviour/invalid_utf8_is_rejected.rs @@ -0,0 +1,47 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// + +use mqtt_format::v3::{connect_return::MConnectReturnCode, packet::MConnack}; + +use crate::{ + behaviour_test::BehaviourTest, + command::{Input, Output}, + executable::ClientExecutableCommand, +}; + +pub struct InvalidUtf8IsRejected; + +#[async_trait::async_trait] +impl BehaviourTest for InvalidUtf8IsRejected { + fn commands(&self) -> Vec> { + vec![] + } + + async fn execute(&self, mut input: Input, _output: Output) -> Result<(), miette::Error> { + input + .send_packet(MConnack { + session_present: false, + connect_return_code: MConnectReturnCode::Accepted, + }) + .await?; + + input + .send(&[ + 0b0011_0000, // PUBLISH packet, DUP = 0, QoS = 0, Retain = 0 + 0b0000_0111, // Length + // Now the variable header + 0b0000_0000, + 0b0000_0010, + 0x61, + 0xC1, // An invalid UTF-8 byte + 0b0000_0000, // Packet identifier + 0b0000_0001, + 0x1, // Payload + ]) + .await?; + Ok(()) + } +} diff --git a/mqtt-tester/src/behaviour/mod.rs b/mqtt-tester/src/behaviour/mod.rs index 48f9c92..690d42c 100644 --- a/mqtt-tester/src/behaviour/mod.rs +++ b/mqtt-tester/src/behaviour/mod.rs @@ -4,4 +4,5 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. // +pub mod invalid_utf8_is_rejected; pub mod wait_for_connect; diff --git a/mqtt-tester/src/client_report.rs b/mqtt-tester/src/client_report.rs index 34dfcc2..f6962c2 100644 --- a/mqtt-tester/src/client_report.rs +++ b/mqtt-tester/src/client_report.rs @@ -6,7 +6,6 @@ use std::path::PathBuf; use std::sync::Arc; -use std::time::Duration; use futures::FutureExt; use miette::IntoDiagnostic; @@ -20,6 +19,7 @@ use mqtt_format::v3::qos::MQualityOfService; use mqtt_format::v3::strings::MString; use mqtt_format::v3::subscription_request::MSubscriptionRequests; +use crate::behaviour::invalid_utf8_is_rejected::InvalidUtf8IsRejected; use crate::behaviour::wait_for_connect::WaitForConnect; use crate::behaviour_test::BehaviourTest; use crate::executable::ClientExecutable; @@ -36,7 +36,6 @@ pub async fn create_client_report( let executable = ClientExecutable::new(client_exe_path); let reports = vec![ - check_invalid_utf8_is_rejected(&executable).boxed_local(), check_receiving_server_packet(&executable).boxed_local(), check_invalid_first_packet_is_rejected(&executable).boxed_local(), check_utf8_with_nullchar_is_rejected(&executable).boxed_local(), @@ -51,7 +50,8 @@ pub async fn create_client_report( check_connect_flag_username_zero_means_password_zero(&executable).boxed_local(), ]; - let flows = vec![Box::new(WaitForConnect)]; + let flows: Vec> = + vec![Box::new(WaitForConnect), Box::new(InvalidUtf8IsRejected)]; let invariants: Vec> = vec![Arc::new(NoUsernameMeansNoPassword)]; @@ -107,74 +107,37 @@ macro_rules! mk_report { }; } +#[macro_export] macro_rules! wait_for_output { ($output:ident, timeout_ms: $timeout_ms:literal, out_success => $success:block, out_failure => $failure:block ) => {{ - let (result, output) = - match tokio::time::timeout(Duration::from_millis($timeout_ms), $output).await { - Ok(Ok(out)) => ( - if out.status.success() { - $success - } else { - $failure - }, - Some(out.stderr), - ), - Ok(Err(_)) | Err(_) => (ReportResult::Failure, None), - }; + #[allow(unused_imports)] + use futures::Future; + + let (result, output) = match tokio::time::timeout( + std::time::Duration::from_millis($timeout_ms), + $output, + ) + .await + { + Ok(Ok(out)) => ( + if out.status.success() { + $success + } else { + $failure + }, + Some(out.stderr), + ), + Ok(Err(_)) | Err(_) => (ReportResult::Failure, None), + }; (result, output) }}; } -async fn check_invalid_utf8_is_rejected(executable: &ClientExecutable) -> miette::Result { - let (client, mut input, _output) = executable - .call(&[]) - .map(crate::command::Command::new)? - .spawn()?; - - input - .send_packet(MConnack { - session_present: false, - connect_return_code: MConnectReturnCode::Accepted, - }) - .await?; - - input - .send(&[ - 0b0011_0000, // PUBLISH packet, DUP = 0, QoS = 0, Retain = 0 - 0b0000_0111, // Length - // Now the variable header - 0b0000_0000, - 0b0000_0010, - 0x61, - 0xC1, // An invalid UTF-8 byte - 0b0000_0000, // Packet identifier - 0b0000_0001, - 0x1, // Payload - ]) - .await?; - - let output = client.wait_with_output(); - let (result, output) = wait_for_output! { - output, - timeout_ms: 100, - out_success => { ReportResult::Failure }, - out_failure => { ReportResult::Success } - }; - - Ok(mk_report! { - name: "Check if invalid UTF-8 is rejected", - desc: "Invalid UTF-8 is not allowed per the MQTT spec. Any receiver should immediately close the connection upon receiving such a packet.", - normative: "[MQTT-1.5.3-1, MQTT-1.5.3-2]", - result, - output - }) -} - async fn check_receiving_server_packet(executable: &ClientExecutable) -> miette::Result { let (client, mut input, _output) = executable .call(&[]) -- cgit v1.2.3 From a7b0b6b4ceef21dce50ef07251a190b7077ea279 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Thu, 12 Jan 2023 16:09:26 +0100 Subject: Let BehaviourTest provide more information about tests This patch adds functions the BehaviourTest trait for providing more information about the individual test cases. These are then used to construct Report objects from the test runs. Signed-off-by: Matthias Beyer --- mqtt-tester/src/behaviour_test.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'mqtt-tester/src') diff --git a/mqtt-tester/src/behaviour_test.rs b/mqtt-tester/src/behaviour_test.rs index a601c5c..fd334df 100644 --- a/mqtt-tester/src/behaviour_test.rs +++ b/mqtt-tester/src/behaviour_test.rs @@ -7,6 +7,7 @@ use crate::{ command::{Input, Output}, executable::ClientExecutableCommand, + report::ReportResult, }; #[async_trait::async_trait] @@ -14,4 +15,16 @@ pub trait BehaviourTest { fn commands(&self) -> Vec>; async fn execute(&self, mut input: Input, mut output: Output) -> Result<(), miette::Error>; + + fn report_name(&self) -> &str; + fn report_desc(&self) -> &str; + fn report_normative(&self) -> &str; + + fn translate_client_exit_code(&self, success: bool) -> ReportResult { + if success { + ReportResult::Success + } else { + ReportResult::Failure + } + } } -- cgit v1.2.3 From 092d5c5817f54d01c011cae01d424603844ff539 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Thu, 12 Jan 2023 16:10:36 +0100 Subject: Use BehaviourTest interfaces to construct Report objects Signed-off-by: Matthias Beyer --- mqtt-tester/src/client_report.rs | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) (limited to 'mqtt-tester/src') diff --git a/mqtt-tester/src/client_report.rs b/mqtt-tester/src/client_report.rs index f6962c2..09bc638 100644 --- a/mqtt-tester/src/client_report.rs +++ b/mqtt-tester/src/client_report.rs @@ -55,6 +55,7 @@ pub async fn create_client_report( let invariants: Vec> = vec![Arc::new(NoUsernameMeansNoPassword)]; + let mut collected_reports = Vec::with_capacity(flows.len()); for flow in flows { let commands = flow.commands(); @@ -65,16 +66,34 @@ pub async fn create_client_report( output.with_invariants(invariants.iter().cloned()); - flow.execute(input, output).await?; - client.wait_with_output().await.into_diagnostic()?; + let flow_result = flow.execute(input, output).await; + let client_output = client.wait_with_output().await.into_diagnostic()?; + + collected_reports.push({ + Report { + name: String::from(flow.report_name()), + description: String::from(flow.report_desc()), + normative_statement_number: String::from(flow.report_normative()), + result: match flow_result { + Ok(_) => flow.translate_client_exit_code(client_output.status.success()), + Err(_e) => ReportResult::Failure, + }, + output: Some(client_output.stdout), + } + }) } - futures::stream::iter(reports) - .buffered(parallelism.get()) - .collect::>() - .await - .into_iter() - .collect::, _>>() + Ok({ + futures::stream::iter(reports) + .buffered(parallelism.get()) + .collect::>() + .await + .into_iter() + .collect::, _>>()? + .into_iter() + .chain(collected_reports.into_iter()) + .collect() + }) } #[macro_export] -- cgit v1.2.3 From cb90c08e5f9143385f8cd3080b5883f18ed29ea7 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Thu, 12 Jan 2023 16:18:24 +0100 Subject: Add missing meta information to behaviour tests Signed-off-by: Matthias Beyer --- .../src/behaviour/invalid_utf8_is_rejected.rs | 21 +++++++++++++++++++++ mqtt-tester/src/behaviour/wait_for_connect.rs | 21 +++++++++++++++++++++ 2 files changed, 42 insertions(+) (limited to 'mqtt-tester/src') diff --git a/mqtt-tester/src/behaviour/invalid_utf8_is_rejected.rs b/mqtt-tester/src/behaviour/invalid_utf8_is_rejected.rs index eea0fa5..efe7a6d 100644 --- a/mqtt-tester/src/behaviour/invalid_utf8_is_rejected.rs +++ b/mqtt-tester/src/behaviour/invalid_utf8_is_rejected.rs @@ -10,6 +10,7 @@ use crate::{ behaviour_test::BehaviourTest, command::{Input, Output}, executable::ClientExecutableCommand, + report::ReportResult, }; pub struct InvalidUtf8IsRejected; @@ -44,4 +45,24 @@ impl BehaviourTest for InvalidUtf8IsRejected { .await?; Ok(()) } + + fn report_name(&self) -> &str { + "Check if invalid UTF-8 is rejected" + } + + fn report_desc(&self) -> &str { + "Invalid UTF-8 is not allowed per the MQTT spec. Any receiver should immediately close the connection upon receiving such a packet." + } + + fn report_normative(&self) -> &str { + "[MQTT-1.5.3-1, MQTT-1.5.3-2]" + } + + fn translate_client_exit_code(&self, success: bool) -> ReportResult { + if success { + ReportResult::Failure + } else { + ReportResult::Success + } + } } diff --git a/mqtt-tester/src/behaviour/wait_for_connect.rs b/mqtt-tester/src/behaviour/wait_for_connect.rs index d61fe85..561acca 100644 --- a/mqtt-tester/src/behaviour/wait_for_connect.rs +++ b/mqtt-tester/src/behaviour/wait_for_connect.rs @@ -8,6 +8,7 @@ use crate::{ behaviour_test::BehaviourTest, command::{Input, Output}, executable::ClientExecutableCommand, + report::ReportResult, }; pub struct WaitForConnect; @@ -40,6 +41,26 @@ impl BehaviourTest for WaitForConnect { ) .await } + + fn report_name(&self) -> &str { + "Wait for client to connect" + } + + fn report_desc(&self) -> &str { + "A client should send a CONNECT packet to connect to the server" + } + + fn report_normative(&self) -> &str { + "none" + } + + fn translate_client_exit_code(&self, success: bool) -> ReportResult { + if success { + ReportResult::Success + } else { + ReportResult::Failure + } + } } fn find_connect_flags(bytes: &[u8]) -> Option { -- cgit v1.2.3 From 1ace785db2e462134729faa82c47234ae14f0d2b Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Fri, 13 Jan 2023 08:55:04 +0100 Subject: Reimpl test as BehaviourTest: ReceivingServerPacket Signed-off-by: Matthias Beyer --- mqtt-tester/src/behaviour/mod.rs | 1 + .../src/behaviour/receiving_server_packet.rs | 68 ++++++++++++++++++++++ mqtt-tester/src/client_report.rs | 52 +++-------------- 3 files changed, 76 insertions(+), 45 deletions(-) create mode 100644 mqtt-tester/src/behaviour/receiving_server_packet.rs (limited to 'mqtt-tester/src') diff --git a/mqtt-tester/src/behaviour/mod.rs b/mqtt-tester/src/behaviour/mod.rs index 690d42c..0a22101 100644 --- a/mqtt-tester/src/behaviour/mod.rs +++ b/mqtt-tester/src/behaviour/mod.rs @@ -5,4 +5,5 @@ // pub mod invalid_utf8_is_rejected; +pub mod receiving_server_packet; pub mod wait_for_connect; diff --git a/mqtt-tester/src/behaviour/receiving_server_packet.rs b/mqtt-tester/src/behaviour/receiving_server_packet.rs new file mode 100644 index 0000000..f88888e --- /dev/null +++ b/mqtt-tester/src/behaviour/receiving_server_packet.rs @@ -0,0 +1,68 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// + +use mqtt_format::v3::{ + connect_return::MConnectReturnCode, + identifier::MPacketIdentifier, + packet::{MConnack, MSubscribe}, + subscription_request::MSubscriptionRequests, +}; + +use crate::{ + behaviour_test::BehaviourTest, + command::{Input, Output}, + executable::ClientExecutableCommand, + report::ReportResult, +}; + +pub struct ReceivingServerPacket; + +#[async_trait::async_trait] +impl BehaviourTest for ReceivingServerPacket { + fn commands(&self) -> Vec> { + vec![] + } + + async fn execute(&self, mut input: Input, _output: Output) -> Result<(), miette::Error> { + input + .send_packet(MConnack { + session_present: false, + connect_return_code: MConnectReturnCode::Accepted, + }) + .await?; + + input + .send_packet(MSubscribe { + id: MPacketIdentifier(1), + subscriptions: MSubscriptionRequests { + count: 1, + data: b"a/b", + }, + }) + .await?; + Ok(()) + } + + fn report_name(&self) -> &str { + "Check if invalid packets are rejected" + } + + fn report_desc(&self) -> &str { + "Unexpected packets are a protocol error and the client MUST close the connection." + } + + fn report_normative(&self) -> &str { + "[MQTT-4.8.0-1]" + } + + fn translate_client_exit_code(&self, success: bool) -> ReportResult { + if success { + ReportResult::Failure + } else { + ReportResult::Success + } + } +} diff --git a/mqtt-tester/src/client_report.rs b/mqtt-tester/src/client_report.rs index 09bc638..c5914fc 100644 --- a/mqtt-tester/src/client_report.rs +++ b/mqtt-tester/src/client_report.rs @@ -13,13 +13,13 @@ use mqtt_format::v3::connect_return::MConnectReturnCode; use mqtt_format::v3::header::MPacketKind; use mqtt_format::v3::identifier::MPacketIdentifier; -use mqtt_format::v3::packet::{MConnack, MConnect, MPacket, MPuback, MPublish, MSubscribe}; +use mqtt_format::v3::packet::{MConnack, MConnect, MPacket, MPuback, MPublish}; use mqtt_format::v3::qos::MQualityOfService; use mqtt_format::v3::strings::MString; -use mqtt_format::v3::subscription_request::MSubscriptionRequests; use crate::behaviour::invalid_utf8_is_rejected::InvalidUtf8IsRejected; +use crate::behaviour::receiving_server_packet::ReceivingServerPacket; use crate::behaviour::wait_for_connect::WaitForConnect; use crate::behaviour_test::BehaviourTest; use crate::executable::ClientExecutable; @@ -36,7 +36,6 @@ pub async fn create_client_report( let executable = ClientExecutable::new(client_exe_path); let reports = vec![ - check_receiving_server_packet(&executable).boxed_local(), check_invalid_first_packet_is_rejected(&executable).boxed_local(), check_utf8_with_nullchar_is_rejected(&executable).boxed_local(), check_connack_flags_are_set_as_reserved(&executable).boxed_local(), @@ -50,8 +49,11 @@ pub async fn create_client_report( check_connect_flag_username_zero_means_password_zero(&executable).boxed_local(), ]; - let flows: Vec> = - vec![Box::new(WaitForConnect), Box::new(InvalidUtf8IsRejected)]; + let flows: Vec> = vec![ + Box::new(WaitForConnect), + Box::new(InvalidUtf8IsRejected), + Box::new(ReceivingServerPacket), + ]; let invariants: Vec> = vec![Arc::new(NoUsernameMeansNoPassword)]; @@ -157,46 +159,6 @@ macro_rules! wait_for_output { }}; } -async fn check_receiving_server_packet(executable: &ClientExecutable) -> miette::Result { - let (client, mut input, _output) = executable - .call(&[]) - .map(crate::command::Command::new)? - .spawn()?; - - input - .send_packet(MConnack { - session_present: false, - connect_return_code: MConnectReturnCode::Accepted, - }) - .await?; - - input - .send_packet(MSubscribe { - id: MPacketIdentifier(1), - subscriptions: MSubscriptionRequests { - count: 1, - data: b"a/b", - }, - }) - .await?; - - let output = client.wait_with_output(); - let (result, output) = wait_for_output! { - output, - timeout_ms: 100, - out_success => { ReportResult::Failure }, - out_failure => { ReportResult::Success } - }; - - Ok(mk_report! { - name: "Check if invalid packets are rejected", - desc: "Unexpected packets are a protocol error and the client MUST close the connection.", - normative: "[MQTT-4.8.0-1]", - result, - output - }) -} - async fn check_invalid_first_packet_is_rejected( executable: &ClientExecutable, ) -> miette::Result { -- cgit v1.2.3 From 7248a578c841312bfc00e0b74fede405e80e992c Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Fri, 13 Jan 2023 09:01:19 +0100 Subject: Use test structs publicly in behaviour module to ease imports Signed-off-by: Matthias Beyer --- mqtt-tester/src/behaviour/mod.rs | 4 ++++ mqtt-tester/src/client_report.rs | 9 +++------ 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'mqtt-tester/src') diff --git a/mqtt-tester/src/behaviour/mod.rs b/mqtt-tester/src/behaviour/mod.rs index 0a22101..b6fda19 100644 --- a/mqtt-tester/src/behaviour/mod.rs +++ b/mqtt-tester/src/behaviour/mod.rs @@ -7,3 +7,7 @@ pub mod invalid_utf8_is_rejected; pub mod receiving_server_packet; pub mod wait_for_connect; + +pub use self::invalid_utf8_is_rejected::InvalidUtf8IsRejected; +pub use self::receiving_server_packet::ReceivingServerPacket; +pub use self::wait_for_connect::WaitForConnect; diff --git a/mqtt-tester/src/client_report.rs b/mqtt-tester/src/client_report.rs index c5914fc..81fbf1c 100644 --- a/mqtt-tester/src/client_report.rs +++ b/mqtt-tester/src/client_report.rs @@ -18,9 +18,6 @@ use mqtt_format::v3::packet::{MConnack, MConnect, MPacket, MPuback, MPublish}; use mqtt_format::v3::qos::MQualityOfService; use mqtt_format::v3::strings::MString; -use crate::behaviour::invalid_utf8_is_rejected::InvalidUtf8IsRejected; -use crate::behaviour::receiving_server_packet::ReceivingServerPacket; -use crate::behaviour::wait_for_connect::WaitForConnect; use crate::behaviour_test::BehaviourTest; use crate::executable::ClientExecutable; use crate::invariant::no_username_means_no_password::NoUsernameMeansNoPassword; @@ -50,9 +47,9 @@ pub async fn create_client_report( ]; let flows: Vec> = vec![ - Box::new(WaitForConnect), - Box::new(InvalidUtf8IsRejected), - Box::new(ReceivingServerPacket), + Box::new(crate::behaviour::WaitForConnect), + Box::new(crate::behaviour::InvalidUtf8IsRejected), + Box::new(crate::behaviour::ReceivingServerPacket), ]; let invariants: Vec> = vec![Arc::new(NoUsernameMeansNoPassword)]; -- cgit v1.2.3 From 1551929eb814d805e181bf432d217a4314e20f9c Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Fri, 13 Jan 2023 08:55:04 +0100 Subject: Reimpl test as BehaviourTest: InvalidFirstPacketIsRejected Signed-off-by: Matthias Beyer --- .../behaviour/invalid_first_packet_is_rejected.rs | 59 ++++++++++++++++++++++ mqtt-tester/src/behaviour/mod.rs | 2 + mqtt-tester/src/client_report.rs | 40 +-------------- 3 files changed, 62 insertions(+), 39 deletions(-) create mode 100644 mqtt-tester/src/behaviour/invalid_first_packet_is_rejected.rs (limited to 'mqtt-tester/src') diff --git a/mqtt-tester/src/behaviour/invalid_first_packet_is_rejected.rs b/mqtt-tester/src/behaviour/invalid_first_packet_is_rejected.rs new file mode 100644 index 0000000..a4c95ad --- /dev/null +++ b/mqtt-tester/src/behaviour/invalid_first_packet_is_rejected.rs @@ -0,0 +1,59 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// + +use mqtt_format::v3::{packet::MConnect, strings::MString}; + +use crate::{ + behaviour_test::BehaviourTest, + command::{Input, Output}, + executable::ClientExecutableCommand, + report::ReportResult, +}; + +pub struct InvalidFirstPacketIsRejected; + +#[async_trait::async_trait] +impl BehaviourTest for InvalidFirstPacketIsRejected { + fn commands(&self) -> Vec> { + vec![] + } + + async fn execute(&self, mut input: Input, _output: Output) -> Result<(), miette::Error> { + input + .send_packet(MConnect { + protocol_name: MString { value: "foo" }, + protocol_level: 0, + clean_session: true, + will: None, + username: None, + password: None, + keep_alive: 0, + client_id: MString { value: "client" }, + }) + .await?; + Ok(()) + } + + fn report_name(&self) -> &str { + "Check if invalid first packet is rejected" + } + + fn report_desc(&self) -> &str { + "The first packet from the server must be a ConnAck. Any other packet is invalid and the client should close the connection" + } + + fn report_normative(&self) -> &str { + "[MQTT-3.2.0-1]" + } + + fn translate_client_exit_code(&self, success: bool) -> ReportResult { + if success { + ReportResult::Failure + } else { + ReportResult::Success + } + } +} diff --git a/mqtt-tester/src/behaviour/mod.rs b/mqtt-tester/src/behaviour/mod.rs index b6fda19..4a15faf 100644 --- a/mqtt-tester/src/behaviour/mod.rs +++ b/mqtt-tester/src/behaviour/mod.rs @@ -4,10 +4,12 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. // +pub mod invalid_first_packet_is_rejected; pub mod invalid_utf8_is_rejected; pub mod receiving_server_packet; pub mod wait_for_connect; +pub use self::invalid_first_packet_is_rejected::InvalidFirstPacketIsRejected; pub use self::invalid_utf8_is_rejected::InvalidUtf8IsRejected; pub use self::receiving_server_packet::ReceivingServerPacket; pub use self::wait_for_connect::WaitForConnect; diff --git a/mqtt-tester/src/client_report.rs b/mqtt-tester/src/client_report.rs index 81fbf1c..4f3de51 100644 --- a/mqtt-tester/src/client_report.rs +++ b/mqtt-tester/src/client_report.rs @@ -33,7 +33,6 @@ pub async fn create_client_report( let executable = ClientExecutable::new(client_exe_path); let reports = vec![ - check_invalid_first_packet_is_rejected(&executable).boxed_local(), check_utf8_with_nullchar_is_rejected(&executable).boxed_local(), check_connack_flags_are_set_as_reserved(&executable).boxed_local(), check_publish_qos_zero_with_ident_fails(&executable).boxed_local(), @@ -50,6 +49,7 @@ pub async fn create_client_report( Box::new(crate::behaviour::WaitForConnect), Box::new(crate::behaviour::InvalidUtf8IsRejected), Box::new(crate::behaviour::ReceivingServerPacket), + Box::new(crate::behaviour::InvalidFirstPacketIsRejected), ]; let invariants: Vec> = vec![Arc::new(NoUsernameMeansNoPassword)]; @@ -156,44 +156,6 @@ macro_rules! wait_for_output { }}; } -async fn check_invalid_first_packet_is_rejected( - executable: &ClientExecutable, -) -> miette::Result { - let (client, mut input, _output) = executable - .call(&[]) - .map(crate::command::Command::new)? - .spawn()?; - - input - .send_packet(MConnect { - protocol_name: MString { value: "foo" }, - protocol_level: 0, - clean_session: true, - will: None, - username: None, - password: None, - keep_alive: 0, - client_id: MString { value: "client" }, - }) - .await?; - - let output = client.wait_with_output(); - let (result, output) = wait_for_output! { - output, - timeout_ms: 100, - out_success => { ReportResult::Failure }, - out_failure => { ReportResult::Success } - }; - - Ok(mk_report! { - name: "Check if invalid first packet is rejected", - desc: "The first packet from the server must be a ConnAck. Any other packet is invalid and the client should close the connection", - normative: "[MQTT-3.2.0-1]", - result, - output - }) -} - async fn check_utf8_with_nullchar_is_rejected( executable: &ClientExecutable, ) -> miette::Result { -- cgit v1.2.3 From c9d4d8da5f6fd0171bafdb52420f3e7caf663aec Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Fri, 13 Jan 2023 08:55:04 +0100 Subject: Reimpl test as BehaviourTest: Utf8WithNullcharIsRejected Signed-off-by: Matthias Beyer --- mqtt-tester/src/behaviour/mod.rs | 2 + .../behaviour/utf8_with_nullchar_is_rejected.rs | 76 ++++++++++++++++++++++ mqtt-tester/src/client_report.rs | 56 +--------------- 3 files changed, 79 insertions(+), 55 deletions(-) create mode 100644 mqtt-tester/src/behaviour/utf8_with_nullchar_is_rejected.rs (limited to 'mqtt-tester/src') diff --git a/mqtt-tester/src/behaviour/mod.rs b/mqtt-tester/src/behaviour/mod.rs index 4a15faf..94f917c 100644 --- a/mqtt-tester/src/behaviour/mod.rs +++ b/mqtt-tester/src/behaviour/mod.rs @@ -7,9 +7,11 @@ pub mod invalid_first_packet_is_rejected; pub mod invalid_utf8_is_rejected; pub mod receiving_server_packet; +pub mod utf8_with_nullchar_is_rejected; pub mod wait_for_connect; pub use self::invalid_first_packet_is_rejected::InvalidFirstPacketIsRejected; pub use self::invalid_utf8_is_rejected::InvalidUtf8IsRejected; pub use self::receiving_server_packet::ReceivingServerPacket; +pub use self::utf8_with_nullchar_is_rejected::Utf8WithNullcharIsRejected; pub use self::wait_for_connect::WaitForConnect; diff --git a/mqtt-tester/src/behaviour/utf8_with_nullchar_is_rejected.rs b/mqtt-tester/src/behaviour/utf8_with_nullchar_is_rejected.rs new file mode 100644 index 0000000..32283b6 --- /dev/null +++ b/mqtt-tester/src/behaviour/utf8_with_nullchar_is_rejected.rs @@ -0,0 +1,76 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// + +use mqtt_format::v3::{ + connect_return::MConnectReturnCode, header::MPacketKind, packet::MConnack, + qos::MQualityOfService, +}; + +use crate::{ + behaviour_test::BehaviourTest, + command::{Input, Output}, + executable::ClientExecutableCommand, + report::ReportResult, +}; + +pub struct Utf8WithNullcharIsRejected; + +#[async_trait::async_trait] +impl BehaviourTest for Utf8WithNullcharIsRejected { + fn commands(&self) -> Vec> { + vec![] + } + + async fn execute(&self, mut input: Input, _output: Output) -> Result<(), miette::Error> { + input + .send_packet(MConnack { + session_present: false, + connect_return_code: MConnectReturnCode::Accepted, + }) + .await?; + + input + .send(&[ + (MPacketKind::Publish { + dup: false, + qos: MQualityOfService::AtMostOnce, + retain: false, + }) + .to_byte(), + 0b0000_0111, // Length + // Now the variable header + 0b0000_0000, + 0b0000_0010, + 0x61, + 0x00, // Zero byte + 0b0000_0000, // Packet identifier + 0b0000_0001, + 0x1, // Payload + ]) + .await?; + Ok(()) + } + + fn report_name(&self) -> &str { + "Check if connection gets closed if UTF-8 string contains nullchar" + } + + fn report_desc(&self) -> &str { + "The A UTF-8 encoded string MUST NOT include an encoding of the null character U+0000" + } + + fn report_normative(&self) -> &str { + "[MQTT-1.5.3-2]" + } + + fn translate_client_exit_code(&self, success: bool) -> ReportResult { + if success { + ReportResult::Success + } else { + ReportResult::Failure + } + } +} diff --git a/mqtt-tester/src/client_report.rs b/mqtt-tester/src/client_report.rs index 4f3de51..d21240a 100644 --- a/mqtt-tester/src/client_report.rs +++ b/mqtt-tester/src/client_report.rs @@ -33,7 +33,6 @@ pub async fn create_client_report( let executable = ClientExecutable::new(client_exe_path); let reports = vec![ - check_utf8_with_nullchar_is_rejected(&executable).boxed_local(), check_connack_flags_are_set_as_reserved(&executable).boxed_local(), check_publish_qos_zero_with_ident_fails(&executable).boxed_local(), check_publish_qos_2_is_acked(&executable).boxed_local(), @@ -50,6 +49,7 @@ pub async fn create_client_report( Box::new(crate::behaviour::InvalidUtf8IsRejected), Box::new(crate::behaviour::ReceivingServerPacket), Box::new(crate::behaviour::InvalidFirstPacketIsRejected), + Box::new(crate::behaviour::Utf8WithNullcharIsRejected), ]; let invariants: Vec> = vec![Arc::new(NoUsernameMeansNoPassword)]; @@ -156,60 +156,6 @@ macro_rules! wait_for_output { }}; } -async fn check_utf8_with_nullchar_is_rejected( - executable: &ClientExecutable, -) -> miette::Result { - let (client, mut input, _output) = executable - .call(&[]) - .map(crate::command::Command::new)? - .spawn()?; - - input - .send_packet(MConnack { - session_present: false, - connect_return_code: MConnectReturnCode::Accepted, - }) - .await?; - - input - .send(&[ - (MPacketKind::Publish { - dup: false, - qos: MQualityOfService::AtMostOnce, - retain: false, - }) - .to_byte(), - 0b0000_0111, // Length - // Now the variable header - 0b0000_0000, - 0b0000_0010, - 0x61, - 0x00, // Zero byte - 0b0000_0000, // Packet identifier - 0b0000_0001, - 0x1, // Payload - ]) - .await?; - - let output = client.wait_with_output(); - let (result, output) = wait_for_output! { - output, - timeout_ms: 100, - out_success => { ReportResult::Failure }, - out_failure => { ReportResult::Success } - }; - - Ok(Report { - name: String::from("Check if connection gets closed if UTF-8 string contains nullchar"), - description: String::from( - "The A UTF-8 encoded string MUST NOT include an encoding of the null character U+0000", - ), - normative_statement_number: String::from("[MQTT-1.5.3-2]"), - result, - output, - }) -} - async fn check_connack_flags_are_set_as_reserved( executable: &ClientExecutable, ) -> miette::Result { -- cgit v1.2.3 From dbf82eb15fa328b8aad1cc6ede36c575c0eaae8c Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Fri, 13 Jan 2023 08:55:04 +0100 Subject: Reimpl test as BehaviourTest: ConnackFlagsAreSetAsReserved Signed-off-by: Matthias Beyer --- .../behaviour/connack_flags_are_set_as_reserved.rs | 53 ++++++++++++++++++++++ mqtt-tester/src/behaviour/mod.rs | 2 + mqtt-tester/src/client_report.rs | 39 +--------------- 3 files changed, 56 insertions(+), 38 deletions(-) create mode 100644 mqtt-tester/src/behaviour/connack_flags_are_set_as_reserved.rs (limited to 'mqtt-tester/src') diff --git a/mqtt-tester/src/behaviour/connack_flags_are_set_as_reserved.rs b/mqtt-tester/src/behaviour/connack_flags_are_set_as_reserved.rs new file mode 100644 index 0000000..49012c1 --- /dev/null +++ b/mqtt-tester/src/behaviour/connack_flags_are_set_as_reserved.rs @@ -0,0 +1,53 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// + +use crate::{ + behaviour_test::BehaviourTest, + command::{Input, Output}, + executable::ClientExecutableCommand, + report::ReportResult, +}; + +pub struct ConnackFlagsAreSetAsReserved; + +#[async_trait::async_trait] +impl BehaviourTest for ConnackFlagsAreSetAsReserved { + fn commands(&self) -> Vec> { + vec![] + } + + async fn execute(&self, mut input: Input, _output: Output) -> Result<(), miette::Error> { + input + .send(&[ + 0b0010_0000 | 0b0000_1000, // CONNACK + garbage + 0b0000_0010, // Remaining length + 0b0000_0000, // No session present + 0b0000_0000, // Connection accepted + ]) + .await?; + Ok(()) + } + + fn report_name(&self) -> &str { + "Flag-Bit is set to 1 where it should be 0" + } + + fn report_desc(&self) -> &str { + "CONNACK flag bits are marked as Reserved and must be set accordingly to spec" + } + + fn report_normative(&self) -> &str { + "[MQTT-2.2.2-1]" + } + + fn translate_client_exit_code(&self, success: bool) -> ReportResult { + if success { + ReportResult::Failure + } else { + ReportResult::Success + } + } +} diff --git a/mqtt-tester/src/behaviour/mod.rs b/mqtt-tester/src/behaviour/mod.rs index 94f917c..e831a05 100644 --- a/mqtt-tester/src/behaviour/mod.rs +++ b/mqtt-tester/src/behaviour/mod.rs @@ -4,12 +4,14 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. // +pub mod connack_flags_are_set_as_reserved; pub mod invalid_first_packet_is_rejected; pub mod invalid_utf8_is_rejected; pub mod receiving_server_packet; pub mod utf8_with_nullchar_is_rejected; pub mod wait_for_connect; +pub use self::connack_flags_are_set_as_reserved::ConnackFlagsAreSetAsReserved; pub use self::invalid_first_packet_is_rejected::InvalidFirstPacketIsRejected; pub use self::invalid_utf8_is_rejected::InvalidUtf8IsRejected; pub use self::receiving_server_packet::ReceivingServerPacket; diff --git a/mqtt-tester/src/client_report.rs b/mqtt-tester/src/client_report.rs index d21240a..b83b519 100644 --- a/mqtt-tester/src/client_report.rs +++ b/mqtt-tester/src/client_report.rs @@ -11,7 +11,6 @@ use futures::FutureExt; use miette::IntoDiagnostic; use mqtt_format::v3::connect_return::MConnectReturnCode; -use mqtt_format::v3::header::MPacketKind; use mqtt_format::v3::identifier::MPacketIdentifier; use mqtt_format::v3::packet::{MConnack, MConnect, MPacket, MPuback, MPublish}; @@ -33,7 +32,6 @@ pub async fn create_client_report( let executable = ClientExecutable::new(client_exe_path); let reports = vec![ - check_connack_flags_are_set_as_reserved(&executable).boxed_local(), check_publish_qos_zero_with_ident_fails(&executable).boxed_local(), check_publish_qos_2_is_acked(&executable).boxed_local(), check_first_packet_from_client_is_connect(&executable).boxed_local(), @@ -50,6 +48,7 @@ pub async fn create_client_report( Box::new(crate::behaviour::ReceivingServerPacket), Box::new(crate::behaviour::InvalidFirstPacketIsRejected), Box::new(crate::behaviour::Utf8WithNullcharIsRejected), + Box::new(crate::behaviour::ConnackFlagsAreSetAsReserved), ]; let invariants: Vec> = vec![Arc::new(NoUsernameMeansNoPassword)]; @@ -156,42 +155,6 @@ macro_rules! wait_for_output { }}; } -async fn check_connack_flags_are_set_as_reserved( - executable: &ClientExecutable, -) -> miette::Result { - let (client, mut input, _output) = executable - .call(&[]) - .map(crate::command::Command::new)? - .spawn()?; - - input - .send(&[ - 0b0010_0000 | 0b0000_1000, // CONNACK + garbage - 0b0000_0010, // Remaining length - 0b0000_0000, // No session present - 0b0000_0000, // Connection accepted - ]) - .await?; - - let output = client.wait_with_output(); - let (result, output) = wait_for_output! { - output, - timeout_ms: 100, - out_success => { ReportResult::Failure }, - out_failure => { ReportResult::Success } - }; - - Ok(Report { - name: String::from("Flag-Bit is set to 1 where it should be 0"), - description: String::from( - "CONNACK flag bits are marked as Reserved and must be set accordingly to spec", - ), - normative_statement_number: String::from("[MQTT-2.2.2-1]"), - result, - output, - }) -} - async fn check_publish_qos_zero_with_ident_fails( executable: &ClientExecutable, ) -> miette::Result { -- cgit v1.2.3 From b8956c1924a66588ea410bb7cc15416c376f473f Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Fri, 13 Jan 2023 08:55:04 +0100 Subject: Reimpl test as BehaviourTest: PublishQosZeroWithIdentFails Signed-off-by: Matthias Beyer --- mqtt-tester/src/behaviour/mod.rs | 2 + .../behaviour/publish_qos_zero_with_ident_fails.rs | 70 ++++++++++++++++++++++ mqtt-tester/src/client_report.rs | 45 +------------- 3 files changed, 73 insertions(+), 44 deletions(-) create mode 100644 mqtt-tester/src/behaviour/publish_qos_zero_with_ident_fails.rs (limited to 'mqtt-tester/src') diff --git a/mqtt-tester/src/behaviour/mod.rs b/mqtt-tester/src/behaviour/mod.rs index e831a05..39a7d03 100644 --- a/mqtt-tester/src/behaviour/mod.rs +++ b/mqtt-tester/src/behaviour/mod.rs @@ -7,6 +7,7 @@ pub mod connack_flags_are_set_as_reserved; pub mod invalid_first_packet_is_rejected; pub mod invalid_utf8_is_rejected; +pub mod publish_qos_zero_with_ident_fails; pub mod receiving_server_packet; pub mod utf8_with_nullchar_is_rejected; pub mod wait_for_connect; @@ -14,6 +15,7 @@ pub mod wait_for_connect; pub use self::connack_flags_are_set_as_reserved::ConnackFlagsAreSetAsReserved; pub use self::invalid_first_packet_is_rejected::InvalidFirstPacketIsRejected; pub use self::invalid_utf8_is_rejected::InvalidUtf8IsRejected; +pub use self::publish_qos_zero_with_ident_fails::PublishQosZeroWithIdentFails; pub use self::receiving_server_packet::ReceivingServerPacket; pub use self::utf8_with_nullchar_is_rejected::Utf8WithNullcharIsRejected; pub use self::wait_for_connect::WaitForConnect; diff --git a/mqtt-tester/src/behaviour/publish_qos_zero_with_ident_fails.rs b/mqtt-tester/src/behaviour/publish_qos_zero_with_ident_fails.rs new file mode 100644 index 0000000..fe5544d --- /dev/null +++ b/mqtt-tester/src/behaviour/publish_qos_zero_with_ident_fails.rs @@ -0,0 +1,70 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// + +use mqtt_format::v3::{ + connect_return::MConnectReturnCode, + identifier::MPacketIdentifier, + packet::{MConnack, MPublish}, + qos::MQualityOfService, + strings::MString, +}; + +use crate::{ + behaviour_test::BehaviourTest, + command::{Input, Output}, + executable::ClientExecutableCommand, + report::ReportResult, +}; + +pub struct PublishQosZeroWithIdentFails; + +#[async_trait::async_trait] +impl BehaviourTest for PublishQosZeroWithIdentFails { + fn commands(&self) -> Vec> { + vec![] + } + + async fn execute(&self, mut input: Input, _output: Output) -> Result<(), miette::Error> { + input + .send_packet(MConnack { + session_present: false, + connect_return_code: MConnectReturnCode::Accepted, + }) + .await?; + + input + .send_packet(MPublish { + dup: false, + qos: MQualityOfService::AtMostOnce, // QoS 0 + retain: false, + topic_name: MString { value: "a" }, + id: Some(MPacketIdentifier(1)), + payload: &[0x00], + }) + .await?; + Ok(()) + } + + fn report_name(&self) -> &str { + "A PUBLISH packet with QoS zero must not contain a packet identifier" + } + + fn report_desc(&self) -> &str { + "A PUBLISH Packet MUST NOT contain a Packet Identifier if its QoS value is set to 0." + } + + fn report_normative(&self) -> &str { + "[MQTT-2.3.1-5]" + } + + fn translate_client_exit_code(&self, success: bool) -> ReportResult { + if success { + ReportResult::Failure + } else { + ReportResult::Success + } + } +} diff --git a/mqtt-tester/src/client_report.rs b/mqtt-tester/src/client_report.rs index b83b519..d6de3da 100644 --- a/mqtt-tester/src/client_report.rs +++ b/mqtt-tester/src/client_report.rs @@ -32,7 +32,6 @@ pub async fn create_client_report( let executable = ClientExecutable::new(client_exe_path); let reports = vec![ - check_publish_qos_zero_with_ident_fails(&executable).boxed_local(), check_publish_qos_2_is_acked(&executable).boxed_local(), check_first_packet_from_client_is_connect(&executable).boxed_local(), check_connect_packet_protocol_name(&executable).boxed_local(), @@ -49,6 +48,7 @@ pub async fn create_client_report( Box::new(crate::behaviour::InvalidFirstPacketIsRejected), Box::new(crate::behaviour::Utf8WithNullcharIsRejected), Box::new(crate::behaviour::ConnackFlagsAreSetAsReserved), + Box::new(crate::behaviour::PublishQosZeroWithIdentFails), ]; let invariants: Vec> = vec![Arc::new(NoUsernameMeansNoPassword)]; @@ -155,49 +155,6 @@ macro_rules! wait_for_output { }}; } -async fn check_publish_qos_zero_with_ident_fails( - executable: &ClientExecutable, -) -> miette::Result { - let (client, mut input, _output) = executable - .call(&[]) - .map(crate::command::Command::new)? - .spawn()?; - - input - .send_packet(MConnack { - session_present: false, - connect_return_code: MConnectReturnCode::Accepted, - }) - .await?; - - input - .send_packet(MPublish { - dup: false, - qos: MQualityOfService::AtMostOnce, // QoS 0 - retain: false, - topic_name: MString { value: "a" }, - id: Some(MPacketIdentifier(1)), - payload: &[0x00], - }) - .await?; - - let output = client.wait_with_output(); - let (result, output) = wait_for_output! { - output, - timeout_ms: 100, - out_success => { ReportResult::Failure }, - out_failure => { ReportResult::Success } - }; - - Ok(mk_report! { - name: "A PUBLISH packet with QoS zero must not contain a packet identifier", - desc: "A PUBLISH Packet MUST NOT contain a Packet Identifier if its QoS value is set to 0.", - normative: "[MQTT-2.3.1-5]", - result, - output - }) -} - async fn check_publish_qos_2_is_acked(executable: &ClientExecutable) -> miette::Result { let (client, mut input, mut output) = executable .call(&[]) -- cgit v1.2.3 From 659d70186a6577d0cae53e276dfeb38c3d09abaa Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Fri, 13 Jan 2023 08:55:04 +0100 Subject: Reimpl test as BehaviourTest: PublishQos2IsAcked Signed-off-by: Matthias Beyer --- mqtt-tester/src/behaviour/mod.rs | 2 + .../src/behaviour/publish_qos_2_is_acked.rs | 71 ++++++++++++++++++++++ mqtt-tester/src/client_report.rs | 54 +--------------- 3 files changed, 75 insertions(+), 52 deletions(-) create mode 100644 mqtt-tester/src/behaviour/publish_qos_2_is_acked.rs (limited to 'mqtt-tester/src') diff --git a/mqtt-tester/src/behaviour/mod.rs b/mqtt-tester/src/behaviour/mod.rs index 39a7d03..422bfd9 100644 --- a/mqtt-tester/src/behaviour/mod.rs +++ b/mqtt-tester/src/behaviour/mod.rs @@ -7,6 +7,7 @@ pub mod connack_flags_are_set_as_reserved; pub mod invalid_first_packet_is_rejected; pub mod invalid_utf8_is_rejected; +pub mod publish_qos_2_is_acked; pub mod publish_qos_zero_with_ident_fails; pub mod receiving_server_packet; pub mod utf8_with_nullchar_is_rejected; @@ -15,6 +16,7 @@ pub mod wait_for_connect; pub use self::connack_flags_are_set_as_reserved::ConnackFlagsAreSetAsReserved; pub use self::invalid_first_packet_is_rejected::InvalidFirstPacketIsRejected; pub use self::invalid_utf8_is_rejected::InvalidUtf8IsRejected; +pub use self::publish_qos_2_is_acked::PublishQos2IsAcked; pub use self::publish_qos_zero_with_ident_fails::PublishQosZeroWithIdentFails; pub use self::receiving_server_packet::ReceivingServerPacket; pub use self::utf8_with_nullchar_is_rejected::Utf8WithNullcharIsRejected; diff --git a/mqtt-tester/src/behaviour/publish_qos_2_is_acked.rs b/mqtt-tester/src/behaviour/publish_qos_2_is_acked.rs new file mode 100644 index 0000000..95dfeae --- /dev/null +++ b/mqtt-tester/src/behaviour/publish_qos_2_is_acked.rs @@ -0,0 +1,71 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// + +use mqtt_format::v3::{ + connect_return::MConnectReturnCode, + identifier::MPacketIdentifier, + packet::{MConnack, MPublish}, + qos::MQualityOfService, + strings::MString, +}; + +use crate::{ + behaviour_test::BehaviourTest, + command::{Input, Output}, + executable::ClientExecutableCommand, + report::ReportResult, +}; + +pub struct PublishQos2IsAcked; + +#[async_trait::async_trait] +impl BehaviourTest for PublishQos2IsAcked { + fn commands(&self) -> Vec> { + vec![] + } + + async fn execute(&self, mut input: Input, _output: Output) -> Result<(), miette::Error> { + input + .send_packet(MConnack { + session_present: false, + connect_return_code: MConnectReturnCode::Accepted, + }) + .await?; + + input + .send_packet(MPublish { + dup: false, + qos: MQualityOfService::AtLeastOnce, // QoS 2 + retain: false, + topic_name: MString { value: "a" }, + id: Some(MPacketIdentifier(1)), + payload: &[0x00], + }) + .await?; + + Ok(()) + } + + fn report_name(&self) -> &str { + "A PUBLISH packet is replied to with Puback with the same id" + } + + fn report_desc(&self) -> &str { + "A PUBACK, PUBREC or PUBREL Packet MUST contain the same Packet Identifier as the PUBLISH Packet that was originally sent." + } + + fn report_normative(&self) -> &str { + "[MQTT-2.3.1-6]" + } + + fn translate_client_exit_code(&self, success: bool) -> ReportResult { + if success { + ReportResult::Failure + } else { + ReportResult::Success + } + } +} diff --git a/mqtt-tester/src/client_report.rs b/mqtt-tester/src/client_report.rs index d6de3da..e3b143e 100644 --- a/mqtt-tester/src/client_report.rs +++ b/mqtt-tester/src/client_report.rs @@ -9,12 +9,9 @@ use std::sync::Arc; use futures::FutureExt; use miette::IntoDiagnostic; -use mqtt_format::v3::connect_return::MConnectReturnCode; -use mqtt_format::v3::identifier::MPacketIdentifier; -use mqtt_format::v3::packet::{MConnack, MConnect, MPacket, MPuback, MPublish}; +use mqtt_format::v3::packet::{MConnect, MPacket}; -use mqtt_format::v3::qos::MQualityOfService; use mqtt_format::v3::strings::MString; use crate::behaviour_test::BehaviourTest; @@ -32,7 +29,6 @@ pub async fn create_client_report( let executable = ClientExecutable::new(client_exe_path); let reports = vec![ - check_publish_qos_2_is_acked(&executable).boxed_local(), check_first_packet_from_client_is_connect(&executable).boxed_local(), check_connect_packet_protocol_name(&executable).boxed_local(), check_connect_packet_reserved_flag_zero(&executable).boxed_local(), @@ -49,6 +45,7 @@ pub async fn create_client_report( Box::new(crate::behaviour::Utf8WithNullcharIsRejected), Box::new(crate::behaviour::ConnackFlagsAreSetAsReserved), Box::new(crate::behaviour::PublishQosZeroWithIdentFails), + Box::new(crate::behaviour::PublishQos2IsAcked), ]; let invariants: Vec> = vec![Arc::new(NoUsernameMeansNoPassword)]; @@ -155,53 +152,6 @@ macro_rules! wait_for_output { }}; } -async fn check_publish_qos_2_is_acked(executable: &ClientExecutable) -> miette::Result { - let (client, mut input, mut output) = executable - .call(&[]) - .map(crate::command::Command::new)? - .spawn()?; - - input - .send_packet(MConnack { - session_present: false, - connect_return_code: MConnectReturnCode::Accepted, - }) - .await?; - - input - .send_packet(MPublish { - dup: false, - qos: MQualityOfService::AtLeastOnce, // QoS 2 - retain: false, - topic_name: MString { value: "a" }, - id: Some(MPacketIdentifier(1)), - payload: &[0x00], - }) - .await?; - - output - .wait_for_packet(MPuback { - id: MPacketIdentifier(1), - }) - .await?; - - let output = client.wait_with_output(); - let (result, output) = wait_for_output! { - output, - timeout_ms: 100, - out_success => { ReportResult::Success }, - out_failure => { ReportResult::Failure } - }; - - Ok(mk_report! { - name: "A PUBLISH packet is replied to with Puback with the same id", - desc: "A PUBACK, PUBREC or PUBREL Packet MUST contain the same Packet Identifier as the PUBLISH Packet that was originally sent.", - normative: "[MQTT-2.3.1-6]", - result, - output - }) -} - async fn check_first_packet_from_client_is_connect( executable: &ClientExecutable, ) -> miette::Result { -- cgit v1.2.3 From 5b6b2f606f01024df958b434e76204fe12c3c602 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Fri, 13 Jan 2023 08:55:04 +0100 Subject: Reimpl test as BehaviourTest: FirstPacketFromClientIsConnect Signed-off-by: Matthias Beyer --- .../first_packet_from_client_is_connect.rs | 63 ++++++++++++++++++++++ mqtt-tester/src/behaviour/mod.rs | 2 + mqtt-tester/src/client_report.rs | 41 +------------- 3 files changed, 66 insertions(+), 40 deletions(-) create mode 100644 mqtt-tester/src/behaviour/first_packet_from_client_is_connect.rs (limited to 'mqtt-tester/src') diff --git a/mqtt-tester/src/behaviour/first_packet_from_client_is_connect.rs b/mqtt-tester/src/behaviour/first_packet_from_client_is_connect.rs new file mode 100644 index 0000000..17525b6 --- /dev/null +++ b/mqtt-tester/src/behaviour/first_packet_from_client_is_connect.rs @@ -0,0 +1,63 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// + +use mqtt_format::v3::packet::MPacket; + +use crate::{ + behaviour_test::BehaviourTest, + command::{Input, Output}, + executable::ClientExecutableCommand, + report::ReportResult, +}; + +pub struct FirstPacketFromClientIsConnect; + +#[async_trait::async_trait] +impl BehaviourTest for FirstPacketFromClientIsConnect { + fn commands(&self) -> Vec> { + vec![] + } + + async fn execute(&self, _input: Input, mut output: Output) -> Result<(), miette::Error> { + output + .wait_and_check( + &(|bytes: &[u8]| -> bool { + let packet = match nom::combinator::all_consuming( + mqtt_format::v3::packet::mpacket, + )(bytes) + { + Ok((_, packet)) => packet, + Err(_e) => return false, + }; + + std::matches!(packet, MPacket::Connect { .. }) + }), + ) + .await?; + + Ok(()) + } + + fn report_name(&self) -> &str { + "First packet send by client must be CONNECT" + } + + fn report_desc(&self) -> &str { + "After a Network Connection is established by a Client to a Server, the first Packet sent from the Client to the Server MUST be a CONNECT Packet." + } + + fn report_normative(&self) -> &str { + "[MQTT-3.1.0-1]" + } + + fn translate_client_exit_code(&self, success: bool) -> ReportResult { + if success { + ReportResult::Failure + } else { + ReportResult::Success + } + } +} diff --git a/mqtt-tester/src/behaviour/mod.rs b/mqtt-tester/src/behaviour/mod.rs index 422bfd9..0309b50 100644 --- a/mqtt-tester/src/behaviour/mod.rs +++ b/mqtt-tester/src/behaviour/mod.rs @@ -5,6 +5,7 @@ // pub mod connack_flags_are_set_as_reserved; +pub mod first_packet_from_client_is_connect; pub mod invalid_first_packet_is_rejected; pub mod invalid_utf8_is_rejected; pub mod publish_qos_2_is_acked; @@ -14,6 +15,7 @@ pub mod utf8_with_nullchar_is_rejected; pub mod wait_for_connect; pub use self::connack_flags_are_set_as_reserved::ConnackFlagsAreSetAsReserved; +pub use self::first_packet_from_client_is_connect::FirstPacketFromClientIsConnect; pub use self::invalid_first_packet_is_rejected::InvalidFirstPacketIsRejected; pub use self::invalid_utf8_is_rejected::InvalidUtf8IsRejected; pub use self::publish_qos_2_is_acked::PublishQos2IsAcked; diff --git a/mqtt-tester/src/client_report.rs b/mqtt-tester/src/client_report.rs index e3b143e..f02359d 100644 --- a/mqtt-tester/src/client_report.rs +++ b/mqtt-tester/src/client_report.rs @@ -29,7 +29,6 @@ pub async fn create_client_report( let executable = ClientExecutable::new(client_exe_path); let reports = vec![ - check_first_packet_from_client_is_connect(&executable).boxed_local(), check_connect_packet_protocol_name(&executable).boxed_local(), check_connect_packet_reserved_flag_zero(&executable).boxed_local(), check_connect_flag_username_set_username_present(&executable).boxed_local(), @@ -46,6 +45,7 @@ pub async fn create_client_report( Box::new(crate::behaviour::ConnackFlagsAreSetAsReserved), Box::new(crate::behaviour::PublishQosZeroWithIdentFails), Box::new(crate::behaviour::PublishQos2IsAcked), + Box::new(crate::behaviour::FirstPacketFromClientIsConnect), ]; let invariants: Vec> = vec![Arc::new(NoUsernameMeansNoPassword)]; @@ -152,45 +152,6 @@ macro_rules! wait_for_output { }}; } -async fn check_first_packet_from_client_is_connect( - executable: &ClientExecutable, -) -> miette::Result { - let (client, _input, mut output) = executable - .call(&[]) - .map(crate::command::Command::new)? - .spawn()?; - - output - .wait_and_check( - &(|bytes: &[u8]| -> bool { - let packet = - match nom::combinator::all_consuming(mqtt_format::v3::packet::mpacket)(bytes) { - Ok((_, packet)) => packet, - Err(_e) => return false, - }; - - std::matches!(packet, MPacket::Connect { .. }) - }), - ) - .await?; - - let output = client.wait_with_output(); - let (result, output) = wait_for_output! { - output, - timeout_ms: 100, - out_success => { ReportResult::Success }, - out_failure => { ReportResult::Failure } - }; - - Ok(mk_report! { - name: "First packet received send by client must be CONNECT", - desc: "After a Network Connection is established by a Client to a Server, the first Packet sent from the Client to the Server MUST be a CONNECT Packet.", - normative: "[MQTT-3.1.0-1]", - result, - output - }) -} - async fn check_connect_packet_protocol_name( executable: &ClientExecutable, ) -> miette::Result { -- cgit v1.2.3 From 368f9626a2c18a1a2d7732651e5ec729b67aa2b3 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Fri, 13 Jan 2023 10:16:58 +0100 Subject: Remove unused fn Signed-off-by: Matthias Beyer --- mqtt-tester/src/command.rs | 26 -------------------------- 1 file changed, 26 deletions(-) (limited to 'mqtt-tester/src') diff --git a/mqtt-tester/src/command.rs b/mqtt-tester/src/command.rs index 9e01585..0358a98 100644 --- a/mqtt-tester/src/command.rs +++ b/mqtt-tester/src/command.rs @@ -116,32 +116,6 @@ impl Output { Ok(buf) } - pub async fn wait_for_packet<'m, P>(&mut self, packet: P) -> miette::Result<()> - where - P: Into>, - { - let mut buf = vec![]; - packet - .into() - .write_to(std::pin::Pin::new(&mut buf)) - .await - .into_diagnostic()?; - - let bytes = self.wait_for(&buf).await?; - - match mqtt_format::v3::packet::mpacket(&bytes) { - Ok((_, packet)) => self - .attached_invariants - .iter() - .flat_map(|inv| inv.test_invariant(&packet)) - .collect::, _>>() - .map(|_| ()), - Err(e) => { - miette::bail!("Failed to parse as MQTT packet: {}", e) - } - } - } - pub async fn wait_and_check(&mut self, check: impl CheckBytes) -> miette::Result<()> { match tokio::time::timeout(std::time::Duration::from_millis(100), async { let mut buffer = BytesMut::new(); -- cgit v1.2.3 From ff71a051fb621eb6a906f82a148fa1f943d955d4 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Fri, 13 Jan 2023 10:16:58 +0100 Subject: Remove unused fn Signed-off-by: Matthias Beyer --- mqtt-tester/src/command.rs | 23 ----------------------- 1 file changed, 23 deletions(-) (limited to 'mqtt-tester/src') diff --git a/mqtt-tester/src/command.rs b/mqtt-tester/src/command.rs index 0358a98..d0d9979 100644 --- a/mqtt-tester/src/command.rs +++ b/mqtt-tester/src/command.rs @@ -93,29 +93,6 @@ impl Output { self.attached_invariants.extend(i); } - async fn wait_for(&mut self, expected_bytes: &[u8]) -> miette::Result> { - let mut buf = vec![0; expected_bytes.len()]; - match tokio::time::timeout( - std::time::Duration::from_millis(100), - self.stdout.read_exact(&mut buf), - ) - .await - { - Ok(Ok(_)) => { - if buf != expected_bytes { - return Err(miette::miette!( - "Received Bytes did not match expected bytes: {:?} != {:?}", - buf, - expected_bytes - )); - } - } - Ok(Err(e)) => return Err(e).into_diagnostic(), - Err(_elapsed) => return Err(miette::miette!("Did not hear from server until timeout")), - } - Ok(buf) - } - pub async fn wait_and_check(&mut self, check: impl CheckBytes) -> miette::Result<()> { match tokio::time::timeout(std::time::Duration::from_millis(100), async { let mut buffer = BytesMut::new(); -- cgit v1.2.3