diff --git a/Cargo.toml b/Cargo.toml index 331c72d..6428bfd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,11 +38,13 @@ udiscovery = [] usubscription = [] utwin = [] util = ["tokio/sync"] +test-util = ["mockall"] [dependencies] async-trait = { version = "0.1" } bytes = { version = "1.7" } mediatype = "0.19" +mockall = { version = "0.13", optional = true } protobuf = { version = "3.5", features = ["with-bytes"] } rand = { version = "0.8" } thiserror = { version = "1.0", optional = true } diff --git a/src/communication.rs b/src/communication.rs index 3aa6c1a..86fb69f 100644 --- a/src/communication.rs +++ b/src/communication.rs @@ -20,9 +20,15 @@ pub use default_notifier::SimpleNotifier; pub use default_pubsub::{InMemorySubscriber, SimplePublisher}; pub use in_memory_rpc_client::InMemoryRpcClient; pub use in_memory_rpc_server::InMemoryRpcServer; +#[cfg(any(test, feature = "test-util"))] +pub use notification::MockNotifier; pub use notification::{NotificationError, Notifier}; +#[cfg(any(test, feature = "test-util"))] +pub use pubsub::MockSubscriptionChangeHandler; #[cfg(feature = "usubscription")] pub use pubsub::{PubSubError, Publisher, Subscriber}; +#[cfg(any(test, feature = "test-util"))] +pub use rpc::{MockRequestHandler, MockRpcClient, MockRpcServerImpl}; pub use rpc::{RequestHandler, RpcClient, RpcServer, ServiceInvocationError}; #[cfg(feature = "usubscription")] pub use usubscription_client::RpcClientUSubscription; diff --git a/src/communication/notification.rs b/src/communication/notification.rs index b7ae871..38c2ee9 100644 --- a/src/communication/notification.rs +++ b/src/communication/notification.rs @@ -14,8 +14,6 @@ use std::{error::Error, fmt::Display, sync::Arc}; use async_trait::async_trait; -#[cfg(test)] -use mockall::automock; use crate::communication::RegistrationError; use crate::{UListener, UStatus, UUri}; @@ -50,7 +48,7 @@ impl Error for NotificationError {} /// Please refer to the /// [Communication Layer API Specifications](https://github.com/eclipse-uprotocol/up-spec/blob/main/up-l2/api.adoc). // [impl->req~up-language-comm-api~1] -#[cfg_attr(test, automock)] +#[cfg_attr(any(test, feature = "test-util"), mockall::automock)] #[async_trait] pub trait Notifier: Send + Sync { /// Sends a notification to a uEntity. diff --git a/src/communication/pubsub.rs b/src/communication/pubsub.rs index c9a90fd..bf27d94 100644 --- a/src/communication/pubsub.rs +++ b/src/communication/pubsub.rs @@ -14,8 +14,6 @@ use std::{error::Error, fmt::Display, sync::Arc}; use async_trait::async_trait; -#[cfg(test)] -use mockall::automock; use crate::communication::RegistrationError; use crate::core::usubscription::SubscriptionStatus; @@ -73,7 +71,7 @@ pub trait Publisher: Send + Sync { } // [impl->req~up-language-comm-api~1] -#[cfg_attr(test, automock)] +#[cfg_attr(any(test, feature = "test-util"), mockall::automock)] pub trait SubscriptionChangeHandler: Send + Sync { /// Invoked for each update to the subscription status for a given topic. /// diff --git a/src/communication/rpc.rs b/src/communication/rpc.rs index 79c8706..4a6ee93 100644 --- a/src/communication/rpc.rs +++ b/src/communication/rpc.rs @@ -15,8 +15,6 @@ use std::sync::Arc; use thiserror::Error; use async_trait::async_trait; -#[cfg(test)] -use mockall::automock; use protobuf::MessageFull; use crate::communication::RegistrationError; @@ -139,7 +137,7 @@ impl From for UStatus { /// [Communication Layer API specification](https://github.com/eclipse-uprotocol/up-spec/blob/main/up-l2/api.adoc) /// for details. // [impl->req~up-language-comm-api~1] -#[cfg_attr(test, automock)] +#[cfg_attr(any(test, feature = "test-util"), mockall::automock)] #[async_trait] pub trait RpcClient: Send + Sync { /// Invokes a method on a service. @@ -213,7 +211,7 @@ impl dyn RpcClient { /// A handler for processing incoming RPC requests. /// // [impl->req~up-language-comm-api~1] -#[cfg_attr(test, automock)] +#[cfg_attr(any(test, feature = "test-util"), mockall::automock)] #[async_trait] pub trait RequestHandler: Send + Sync { /// Handles a request to invoke a method with given input parameters. @@ -291,6 +289,41 @@ pub trait RpcServer { ) -> Result<(), RegistrationError>; } +#[cfg(any(test, feature = "test-util"))] +mockall::mock! { + /// This extra struct is necessary in order to comply with mockall's requirements regarding the parameter lifetimes + /// see + pub RpcServerImpl { + pub async fn do_register_endpoint<'a>(&'a self, origin_filter: Option<&'a UUri>, resource_id: u16, request_handler: Arc) -> Result<(), RegistrationError>; + pub async fn do_unregister_endpoint<'a>(&'a self, origin_filter: Option<&'a UUri>, resource_id: u16, request_handler: Arc) -> Result<(), RegistrationError>; + } +} + +#[cfg(any(test, feature = "test-util"))] +#[async_trait] +/// This delegates the invocation of the UTransport functions to the mocked functions of the Transport struct. +/// see +impl RpcServer for MockRpcServerImpl { + async fn register_endpoint( + &self, + origin_filter: Option<&UUri>, + resource_id: u16, + request_handler: Arc, + ) -> Result<(), RegistrationError> { + self.do_register_endpoint(origin_filter, resource_id, request_handler) + .await + } + async fn unregister_endpoint( + &self, + origin_filter: Option<&UUri>, + resource_id: u16, + request_handler: Arc, + ) -> Result<(), RegistrationError> { + self.do_unregister_endpoint(origin_filter, resource_id, request_handler) + .await + } +} + #[cfg(test)] mod tests { use std::sync::Arc; diff --git a/src/lib.rs b/src/lib.rs index 3ea5898..729a3e3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,6 +46,7 @@ For user convenience, all of these modules export their types on up_rust top-lev implementations. Enabled by default. * `utwin` enables support for types required to interact with [uTwin service](https://raw.githubusercontent.com/eclipse-uprotocol/up-spec/v1.6.0-alpha.3/up-l3/utwin/v3/README.adoc) implementations. +* `test-util` provides some useful mock implementations for testing. In particular, provides mock implementations of UTransport and Communication Layer API traits which make implementing unit tests a lot easier. * `util` provides some useful helper structs. In particular, provides a local, in-memory UTransport for exchanging messages within a single process. This transport is also used by the examples illustrating usage of the Communication Layer API. ## References @@ -57,14 +58,16 @@ For user convenience, all of these modules export their types on up_rust top-lev // up_core_api types used and augmented by up_rust - symbols re-exported to toplevel, errors are module-specific #[cfg(feature = "communication")] pub mod communication; + #[cfg(feature = "util")] pub mod local_transport; + mod uattributes; pub use uattributes::{ - NotificationValidator, PublishValidator, RequestValidator, ResponseValidator, - UAttributesValidator, UAttributesValidators, + NotificationValidator, PublishValidator, RequestValidator, ResponseValidator, UAttributes, + UAttributesError, UAttributesValidator, UAttributesValidators, UMessageType, UPayloadFormat, + UPriority, }; -pub use uattributes::{UAttributes, UAttributesError, UMessageType, UPayloadFormat, UPriority}; mod umessage; pub use umessage::{UMessage, UMessageBuilder, UMessageError}; @@ -79,6 +82,9 @@ mod utransport; pub use utransport::{ ComparableListener, LocalUriProvider, StaticUriProvider, UListener, UTransport, }; +#[cfg(feature = "test-util")] +pub use utransport::{MockLocalUriProvider, MockTransport, MockUListener}; + mod uuid; pub use uuid::UUID; diff --git a/src/utransport.rs b/src/utransport.rs index 158f27b..f3692ef 100644 --- a/src/utransport.rs +++ b/src/utransport.rs @@ -18,8 +18,6 @@ use std::ops::Deref; use std::sync::Arc; use async_trait::async_trait; -#[cfg(test)] -use mockall::automock; use crate::{UCode, UMessage, UStatus, UUri}; @@ -28,7 +26,7 @@ use crate::{UCode, UMessage, UStatus, UUri}; /// Implementations may use arbitrary mechanisms to determine the information that /// is necessary for creating URIs, e.g. environment variables, configuration files etc. // [impl->req~up-language-transport-api~1] -#[cfg_attr(test, automock)] +#[cfg_attr(any(test, feature = "test-util"), mockall::automock)] pub trait LocalUriProvider: Send + Sync { /// Gets the _authority_ used for URIs representing this uEntity's resources. fn get_authority(&self) -> String; @@ -149,7 +147,7 @@ impl TryFrom<&UUri> for StaticUriProvider { /// Please refer to the [uProtocol Transport Layer specification](https://github.com/eclipse-uprotocol/up-spec/blob/v1.6.0-alpha.3/up-l1/README.adoc) /// for details. // [impl->req~up-language-transport-api~1] -#[cfg_attr(test, automock)] +#[cfg_attr(any(test, feature = "test-util"), mockall::automock)] #[async_trait] pub trait UListener: Send + Sync { /// Performs some action on receipt of a message. @@ -273,10 +271,10 @@ pub trait UTransport: Send + Sync { } } -#[cfg(test)] +#[cfg(any(test, feature = "test-util"))] mockall::mock! { /// This extra struct is necessary in order to comply with mockall's requirements regarding the parameter lifetimes - /// see https://github.com/asomers/mockall/issues/571 + /// see pub Transport { pub async fn do_send(&self, message: UMessage) -> Result<(), UStatus>; pub async fn do_register_listener<'a>(&'a self, source_filter: &'a UUri, sink_filter: Option<&'a UUri>, listener: Arc) -> Result<(), UStatus>; @@ -284,10 +282,10 @@ mockall::mock! { } } -#[cfg(test)] +#[cfg(any(test, feature = "test-util"))] #[async_trait] /// This delegates the invocation of the UTransport functions to the mocked functions of the Transport struct. -/// see https://github.com/asomers/mockall/issues/571 +/// see impl UTransport for MockTransport { async fn send(&self, message: UMessage) -> Result<(), UStatus> { self.do_send(message).await