1use std::fmt::{Debug, Formatter};
15use std::hash::{Hash, Hasher};
16use std::ops::Deref;
17use std::sync::Arc;
18
19#[cfg(feature = "owned-frame-transport")]
20use std::collections::HashMap;
21#[cfg(feature = "owned-frame-transport")]
22use std::sync::{LazyLock, Mutex as StdMutex};
23
24use async_trait::async_trait;
25
26#[cfg(feature = "owned-frame-transport")]
27use crate::UOwnedFrame;
28use crate::{UCode, UMessage, UStatus, UUri, UUriError};
29
30#[cfg(feature = "owned-frame-transport")]
31mod owned_transport_sealed {
32 pub trait Sealed {}
33}
34
35pub fn verify_filter_criteria(
47 source_filter: &UUri,
48 sink_filter: Option<&UUri>,
49) -> Result<(), Box<UStatus>> {
50 if let Some(sink_filter_uuri) = sink_filter {
51 if sink_filter_uuri.is_notification_destination()
52 && source_filter.is_notification_destination()
53 {
54 return Err(Box::from(UStatus::fail_with_code(
55 UCode::InvalidArgument,
56 "source and sink filters must not both have resource ID 0",
57 )));
58 }
59 if sink_filter_uuri.is_rpc_method()
60 && !source_filter.has_wildcard_resource_id()
61 && !source_filter.is_notification_destination()
62 {
63 return Err(Box::from(UStatus::fail_with_code(
64 UCode::InvalidArgument,
65 "source filter must either have the wildcard resource ID or resource ID 0, if sink filter matches RPC method resource ID")));
66 }
67 } else if !source_filter.has_wildcard_resource_id() && !source_filter.is_event() {
68 return Err(Box::from(UStatus::fail_with_code(
69 UCode::InvalidArgument,
70 "source filter must either have the wildcard resource ID or a resource ID from topic range, if sink filter is empty")));
71 }
72 Ok(())
74}
75
76#[cfg_attr(any(test, feature = "test-util"), mockall::automock)]
84pub trait LocalUriProvider: Send + Sync {
85 fn get_authority(&self) -> String;
87 fn get_resource_uri(&self, resource_id: u16) -> UUri;
89 fn get_source_uri(&self) -> UUri;
92}
93
94#[derive(Debug)]
96pub struct StaticUriProvider {
97 local_uri: UUri,
98}
99
100impl StaticUriProvider {
101 pub fn new(
118 authority: impl Into<String>,
119 entity_id: u32,
120 major_version: u8,
121 ) -> Result<Self, UUriError> {
122 UUri::try_from_parts(authority.into().as_str(), entity_id, major_version, 0x0000)
123 .map(|local_uri| StaticUriProvider { local_uri })
124 }
125}
126
127impl LocalUriProvider for StaticUriProvider {
128 fn get_authority(&self) -> String {
129 self.local_uri.authority_name().to_owned()
130 }
131
132 fn get_resource_uri(&self, resource_id: u16) -> UUri {
133 self.local_uri.clone_with_resource_id(resource_id)
134 }
135
136 fn get_source_uri(&self) -> UUri {
137 self.local_uri.clone()
138 }
139}
140
141impl From<UUri> for StaticUriProvider {
142 fn from(value: UUri) -> Self {
143 Self::from(&value)
144 }
145}
146
147impl From<&UUri> for StaticUriProvider {
148 fn from(source_uri: &UUri) -> Self {
166 StaticUriProvider {
167 local_uri: source_uri.clone_with_resource_id(0x0000),
168 }
169 }
170}
171
172#[cfg_attr(any(test, feature = "test-util"), mockall::automock)]
182#[async_trait]
183pub trait UListener: Send + Sync {
184 async fn on_receive(&self, msg: UMessage);
196}
197
198#[async_trait]
210pub trait UTransport: Send + Sync {
211 async fn send(&self, message: UMessage) -> Result<(), UStatus>;
223
224 async fn receive(
238 &self,
239 _source_filter: &UUri,
240 _sink_filter: Option<&UUri>,
241 ) -> Result<UMessage, UStatus> {
242 Err(UStatus::fail_with_code(
243 UCode::Unimplemented,
244 "not implemented",
245 ))
246 }
247
248 async fn register_listener(
267 &self,
268 _source_filter: &UUri,
269 _sink_filter: Option<&UUri>,
270 _listener: Arc<dyn UListener>,
271 ) -> Result<(), UStatus> {
272 Err(UStatus::fail_with_code(
273 UCode::Unimplemented,
274 "not implemented",
275 ))
276 }
277
278 async fn unregister_listener(
295 &self,
296 _source_filter: &UUri,
297 _sink_filter: Option<&UUri>,
298 _listener: Arc<dyn UListener>,
299 ) -> Result<(), UStatus> {
300 Err(UStatus::fail_with_code(
301 UCode::Unimplemented,
302 "not implemented",
303 ))
304 }
305}
306
307#[cfg(feature = "owned-frame-transport")]
308fn verify_owned_filter_criteria(
309 source_filter: &UUri,
310 sink_filter: Option<&UUri>,
311) -> Result<(), UStatus> {
312 verify_filter_criteria(source_filter, sink_filter).map_err(|status| *status)
313}
314
315#[cfg(feature = "owned-frame-transport")]
316#[derive(Clone, Debug, Eq, Hash, PartialEq)]
317struct OwnedListenerRegistrationKey {
318 transport: usize,
319 source_filter: UUri,
320 sink_filter: Option<UUri>,
321 listener: usize,
322}
323
324#[cfg(feature = "owned-frame-transport")]
325static OWNED_LISTENER_REGISTRY: LazyLock<
326 StdMutex<HashMap<OwnedListenerRegistrationKey, Arc<dyn UOwnedListener>>>,
327> = LazyLock::new(|| StdMutex::new(HashMap::new()));
328
329#[cfg(feature = "owned-frame-transport")]
330fn owned_transport_pointer<T: ?Sized>(transport: &T) -> usize {
331 let ptr = transport as *const T;
332 let thin_ptr = ptr as *const ();
333 thin_ptr as usize
334}
335
336#[cfg(feature = "owned-frame-transport")]
337fn owned_listener_pointer(listener: &Arc<dyn UOwnedListener>) -> usize {
338 let ptr = Arc::as_ptr(listener);
339 let thin_ptr = ptr as *const ();
340 thin_ptr as usize
341}
342
343#[cfg(feature = "owned-frame-transport")]
344fn owned_listener_registration_key<T: ?Sized>(
345 transport: &T,
346 source_filter: &UUri,
347 sink_filter: Option<&UUri>,
348 listener: usize,
349) -> OwnedListenerRegistrationKey {
350 OwnedListenerRegistrationKey {
351 transport: owned_transport_pointer(transport),
352 source_filter: source_filter.clone(),
353 sink_filter: sink_filter.cloned(),
354 listener,
355 }
356}
357
358#[cfg(feature = "owned-frame-transport")]
362fn registered_owned_listener(
363 key: &OwnedListenerRegistrationKey,
364 listener: Arc<dyn UOwnedListener>,
365) -> (Arc<dyn UOwnedListener>, bool) {
366 let mut registry = OWNED_LISTENER_REGISTRY
367 .lock()
368 .expect("owned listener registry lock poisoned");
369 if let Some(existing) = registry.get(key) {
370 return (existing.clone(), false);
371 }
372
373 registry.insert(key.clone(), listener.clone());
374 (listener, true)
375}
376
377#[cfg(feature = "owned-frame-transport")]
378fn owned_listener_for_unregister(
379 key: &OwnedListenerRegistrationKey,
380 fallback: Arc<dyn UOwnedListener>,
381) -> Arc<dyn UOwnedListener> {
382 OWNED_LISTENER_REGISTRY
383 .lock()
384 .expect("owned listener registry lock poisoned")
385 .get(key)
386 .cloned()
387 .unwrap_or(fallback)
388}
389
390#[cfg(feature = "owned-frame-transport")]
394#[cfg_attr(any(test, feature = "test-util"), mockall::automock)]
395#[async_trait]
396pub trait UOwnedListener: Send + Sync {
397 async fn on_receive_owned(&self, frame: UOwnedFrame);
399}
400
401#[cfg(feature = "owned-frame-transport")]
420#[async_trait]
421pub trait UOwnedTransportImpl: Send + Sync {
422 async fn send_validated_owned(&self, frame: UOwnedFrame) -> Result<(), UStatus>;
424
425 async fn receive_validated_owned(
427 &self,
428 _source_filter: &UUri,
429 _sink_filter: Option<&UUri>,
430 ) -> Result<UOwnedFrame, UStatus> {
431 Err(UStatus::fail_with_code(
432 UCode::Unimplemented,
433 "not implemented",
434 ))
435 }
436
437 async fn register_validated_owned_listener(
439 &self,
440 _source_filter: &UUri,
441 _sink_filter: Option<&UUri>,
442 _listener: Arc<dyn UOwnedListener>,
443 ) -> Result<(), UStatus> {
444 Err(UStatus::fail_with_code(
445 UCode::Unimplemented,
446 "not implemented",
447 ))
448 }
449
450 async fn unregister_validated_owned_listener(
452 &self,
453 _source_filter: &UUri,
454 _sink_filter: Option<&UUri>,
455 _listener: Arc<dyn UOwnedListener>,
456 ) -> Result<(), UStatus> {
457 Err(UStatus::fail_with_code(
458 UCode::Unimplemented,
459 "not implemented",
460 ))
461 }
462}
463
464#[cfg(feature = "owned-frame-transport")]
465impl<T> owned_transport_sealed::Sealed for T where T: UOwnedTransportImpl + ?Sized {}
466
467#[cfg(feature = "owned-frame-transport")]
473#[async_trait]
474pub trait UOwnedTransport: owned_transport_sealed::Sealed + Send + Sync {
475 async fn send_owned(&self, frame: UOwnedFrame) -> Result<(), UStatus>;
477
478 async fn receive_owned(
480 &self,
481 source_filter: &UUri,
482 sink_filter: Option<&UUri>,
483 ) -> Result<UOwnedFrame, UStatus>;
484
485 async fn register_owned_listener(
487 &self,
488 source_filter: &UUri,
489 sink_filter: Option<&UUri>,
490 listener: Arc<dyn UOwnedListener>,
491 ) -> Result<(), UStatus>;
492
493 async fn unregister_owned_listener(
495 &self,
496 source_filter: &UUri,
497 sink_filter: Option<&UUri>,
498 listener: Arc<dyn UOwnedListener>,
499 ) -> Result<(), UStatus>;
500}
501
502#[cfg(feature = "owned-frame-transport")]
503#[async_trait]
504impl<T> UOwnedTransport for T
505where
506 T: UOwnedTransportImpl + ?Sized,
507{
508 async fn send_owned(&self, frame: UOwnedFrame) -> Result<(), UStatus> {
509 self.send_validated_owned(frame).await
510 }
511
512 async fn receive_owned(
513 &self,
514 source_filter: &UUri,
515 sink_filter: Option<&UUri>,
516 ) -> Result<UOwnedFrame, UStatus> {
517 verify_owned_filter_criteria(source_filter, sink_filter)?;
518 let frame =
519 UOwnedTransportImpl::receive_validated_owned(self, source_filter, sink_filter).await?;
520 Ok(frame)
521 }
522
523 async fn register_owned_listener(
524 &self,
525 source_filter: &UUri,
526 sink_filter: Option<&UUri>,
527 listener: Arc<dyn UOwnedListener>,
528 ) -> Result<(), UStatus> {
529 verify_owned_filter_criteria(source_filter, sink_filter)?;
530 let key = owned_listener_registration_key(
531 self,
532 source_filter,
533 sink_filter,
534 owned_listener_pointer(&listener),
535 );
536 let (listener, inserted) = registered_owned_listener(&key, listener);
537 let result = UOwnedTransportImpl::register_validated_owned_listener(
538 self,
539 source_filter,
540 sink_filter,
541 listener,
542 )
543 .await;
544 if result.is_err() && inserted {
545 OWNED_LISTENER_REGISTRY
546 .lock()
547 .expect("owned listener registry lock poisoned")
548 .remove(&key);
549 }
550 result
551 }
552
553 async fn unregister_owned_listener(
554 &self,
555 source_filter: &UUri,
556 sink_filter: Option<&UUri>,
557 listener: Arc<dyn UOwnedListener>,
558 ) -> Result<(), UStatus> {
559 verify_owned_filter_criteria(source_filter, sink_filter)?;
560 let key = owned_listener_registration_key(
561 self,
562 source_filter,
563 sink_filter,
564 owned_listener_pointer(&listener),
565 );
566 let listener = owned_listener_for_unregister(&key, listener);
567 let result = UOwnedTransportImpl::unregister_validated_owned_listener(
568 self,
569 source_filter,
570 sink_filter,
571 listener,
572 )
573 .await;
574 if result.is_ok() {
575 OWNED_LISTENER_REGISTRY
576 .lock()
577 .expect("owned listener registry lock poisoned")
578 .remove(&key);
579 }
580 result
581 }
582}
583
584#[cfg(not(tarpaulin_include))]
585#[cfg(all(feature = "owned-frame-transport", any(test, feature = "test-util")))]
586mockall::mock! {
587 pub UOwnedTransport {
589 pub async fn do_send_validated_owned(&self, frame: UOwnedFrame) -> Result<(), UStatus>;
595 pub async fn do_receive_validated_owned<'a>(&'a self, source_filter: &'a UUri, sink_filter: Option<&'a UUri>) -> Result<UOwnedFrame, UStatus>;
601 pub async fn do_register_validated_owned_listener<'a>(&'a self, source_filter: &'a UUri, sink_filter: Option<&'a UUri>, listener: Arc<dyn UOwnedListener>) -> Result<(), UStatus>;
607 pub async fn do_unregister_validated_owned_listener<'a>(&'a self, source_filter: &'a UUri, sink_filter: Option<&'a UUri>, listener: Arc<dyn UOwnedListener>) -> Result<(), UStatus>;
613 }
614}
615
616#[cfg(not(tarpaulin_include))]
617#[cfg(all(feature = "owned-frame-transport", any(test, feature = "test-util")))]
618#[async_trait]
619impl UOwnedTransportImpl for MockUOwnedTransport {
620 async fn send_validated_owned(&self, frame: UOwnedFrame) -> Result<(), UStatus> {
621 self.do_send_validated_owned(frame).await
622 }
623
624 async fn receive_validated_owned(
625 &self,
626 source_filter: &UUri,
627 sink_filter: Option<&UUri>,
628 ) -> Result<UOwnedFrame, UStatus> {
629 self.do_receive_validated_owned(source_filter, sink_filter)
630 .await
631 }
632
633 async fn register_validated_owned_listener(
634 &self,
635 source_filter: &UUri,
636 sink_filter: Option<&UUri>,
637 listener: Arc<dyn UOwnedListener>,
638 ) -> Result<(), UStatus> {
639 self.do_register_validated_owned_listener(source_filter, sink_filter, listener)
640 .await
641 }
642
643 async fn unregister_validated_owned_listener(
644 &self,
645 source_filter: &UUri,
646 sink_filter: Option<&UUri>,
647 listener: Arc<dyn UOwnedListener>,
648 ) -> Result<(), UStatus> {
649 self.do_unregister_validated_owned_listener(source_filter, sink_filter, listener)
650 .await
651 }
652}
653
654#[cfg(not(tarpaulin_include))]
655#[cfg(any(test, feature = "test-util"))]
656mockall::mock! {
657 pub Transport {
660 pub async fn do_send(&self, message: UMessage) -> Result<(), UStatus>;
666 pub async fn do_register_listener<'a>(&'a self, source_filter: &'a UUri, sink_filter: Option<&'a UUri>, listener: Arc<dyn UListener>) -> Result<(), UStatus>;
672 pub async fn do_unregister_listener<'a>(&'a self, source_filter: &'a UUri, sink_filter: Option<&'a UUri>, listener: Arc<dyn UListener>) -> Result<(), UStatus>;
678 }
679}
680
681#[cfg(not(tarpaulin_include))]
682#[cfg(any(test, feature = "test-util"))]
683#[async_trait]
684impl UTransport for MockTransport {
687 async fn send(&self, message: UMessage) -> Result<(), UStatus> {
688 self.do_send(message).await
689 }
690 async fn register_listener(
691 &self,
692 source_filter: &UUri,
693 sink_filter: Option<&UUri>,
694 listener: Arc<dyn UListener>,
695 ) -> Result<(), UStatus> {
696 self.do_register_listener(source_filter, sink_filter, listener)
697 .await
698 }
699 async fn unregister_listener(
700 &self,
701 source_filter: &UUri,
702 sink_filter: Option<&UUri>,
703 listener: Arc<dyn UListener>,
704 ) -> Result<(), UStatus> {
705 self.do_unregister_listener(source_filter, sink_filter, listener)
706 .await
707 }
708}
709
710#[derive(Clone)]
725pub struct ComparableListener {
726 listener: Arc<dyn UListener>,
727}
728
729impl ComparableListener {
730 pub fn new(listener: Arc<dyn UListener>) -> Self {
732 Self { listener }
733 }
734 #[must_use]
736 pub fn into_inner(&self) -> Arc<dyn UListener> {
737 self.listener.clone()
738 }
739
740 fn pointer_address(&self) -> usize {
742 let ptr = Arc::as_ptr(&self.listener);
744 let thin_ptr = ptr as *const ();
746 thin_ptr as usize
748 }
749}
750
751impl Deref for ComparableListener {
752 type Target = dyn UListener;
753
754 fn deref(&self) -> &Self::Target {
755 &*self.listener
756 }
757}
758
759impl Hash for ComparableListener {
760 fn hash<H: Hasher>(&self, state: &mut H) {
764 Arc::as_ptr(&self.listener).hash(state);
765 }
766}
767
768impl PartialEq for ComparableListener {
769 fn eq(&self, other: &Self) -> bool {
776 Arc::ptr_eq(&self.listener, &other.listener)
777 }
778}
779
780impl Eq for ComparableListener {}
781
782impl Debug for ComparableListener {
783 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
784 write!(f, "ComparableListener: {}", self.pointer_address())
785 }
786}
787
788#[cfg(test)]
789mod tests {
790 use crate::{ComparableListener, UListener, UMessage, UMessageBuilder};
791 use std::{
792 hash::{DefaultHasher, Hash, Hasher},
793 ops::Deref,
794 str::FromStr,
795 sync::Arc,
796 };
797
798 use super::*;
799
800 #[test]
801 fn test_static_uri_provider_get_source() {
802 let provider = StaticUriProvider::new("my-vehicle", 0x4210, 0x05)
803 .expect("failed to create URI provider");
804 let source_uri = provider.get_source_uri();
805 assert_eq!(source_uri.authority_name(), "my-vehicle");
806 assert_eq!(source_uri.uentity_type_id(), 0x4210);
807 assert_eq!(source_uri.uentity_major_version(), 0x05);
808 assert_eq!(source_uri.resource_id(), 0x0000);
809 }
810
811 #[test]
812 fn test_static_uri_provider_get_resource() {
813 let provider = StaticUriProvider::new("my-vehicle", 0x4210, 0x05)
814 .expect("failed to create URI provider");
815 let resource_uri = provider.get_resource_uri(0x1234);
816 assert_eq!(resource_uri.authority_name(), "my-vehicle");
817 assert_eq!(resource_uri.uentity_type_id(), 0x4210);
818 assert_eq!(resource_uri.uentity_major_version(), 0x05);
819 assert_eq!(resource_uri.resource_id(), 0x1234);
820 }
821
822 #[tokio::test]
823 async fn test_deref_returns_wrapped_listener() {
824 let empty_message = UMessageBuilder::publish(
825 UUri::try_from_parts("my-vehicle", 0x4210, 0x05, 0x9000)
826 .expect("failed to create topic"),
827 )
828 .build()
829 .expect("failed to build message");
830 let mut mock_listener = MockUListener::new();
831 mock_listener.expect_on_receive().once().return_const(());
832 let listener_one = Arc::new(mock_listener);
833 let comparable_listener_one = ComparableListener::new(listener_one);
834 comparable_listener_one
835 .deref()
836 .on_receive(empty_message)
837 .await;
838 }
839
840 #[tokio::test]
841 async fn test_to_inner_returns_reference_to_wrapped_listener() {
842 let empty_message = UMessageBuilder::publish(
843 UUri::try_from_parts("my-vehicle", 0x4210, 0x05, 0x9000)
844 .expect("failed to create topic"),
845 )
846 .build()
847 .expect("failed to build message");
848 let mut mock_listener = MockUListener::new();
849 mock_listener.expect_on_receive().once().return_const(());
850 let listener_one = Arc::new(mock_listener);
851 let comparable_listener_one = ComparableListener::new(listener_one);
852 comparable_listener_one
853 .into_inner()
854 .on_receive(empty_message)
855 .await;
856 }
857
858 #[tokio::test]
859 async fn test_eq_and_hash_are_consistent_for_comparable_listeners_wrapping_same_listener() {
860 let empty_message = UMessageBuilder::publish(
861 UUri::try_from_parts("my-vehicle", 0x4210, 0x05, 0x9000)
862 .expect("failed to create topic"),
863 )
864 .build()
865 .expect("failed to build message");
866 let mut mock_listener = MockUListener::new();
867 mock_listener.expect_on_receive().times(2).return_const(());
868 let listener_one = Arc::new(mock_listener);
869 let listener_two = listener_one.clone();
870 listener_one.on_receive(empty_message.clone()).await;
871 listener_two.on_receive(empty_message.clone()).await;
872 let comparable_listener_one = ComparableListener::new(listener_one);
873 let comparable_listener_two = ComparableListener::new(listener_two);
874 assert!(&comparable_listener_one.eq(&comparable_listener_two));
875
876 let mut hasher = DefaultHasher::new();
877 comparable_listener_one.hash(&mut hasher);
878 let hash_one = hasher.finish();
879 let mut hasher = DefaultHasher::new();
880 comparable_listener_two.hash(&mut hasher);
881 let hash_two = hasher.finish();
882 assert_eq!(hash_one, hash_two);
883 }
884
885 #[tokio::test]
886 async fn test_eq_and_hash_are_consistent_for_comparable_listeners_wrapping_different_listeners()
887 {
888 let empty_message = UMessageBuilder::publish(
889 UUri::try_from_parts("my-vehicle", 0x4210, 0x05, 0x9000)
890 .expect("failed to create topic"),
891 )
892 .build()
893 .expect("failed to build message");
894
895 let mut mock_listener_one = MockUListener::new();
896 mock_listener_one
897 .expect_on_receive()
898 .once()
899 .return_const(());
900 let listener_one = Arc::new(mock_listener_one);
901 let mut mock_listener_two = MockUListener::new();
902 mock_listener_two
903 .expect_on_receive()
904 .once()
905 .return_const(());
906 let listener_two = Arc::new(mock_listener_two);
907 listener_one.on_receive(empty_message.clone()).await;
908 listener_two.on_receive(empty_message.clone()).await;
909 let comparable_listener_one = ComparableListener::new(listener_one);
910 let comparable_listener_two = ComparableListener::new(listener_two);
911 assert!(!&comparable_listener_one.eq(&comparable_listener_two));
912
913 let mut hasher = DefaultHasher::new();
914 comparable_listener_one.hash(&mut hasher);
915 let hash_one = hasher.finish();
916 let mut hasher = DefaultHasher::new();
917 comparable_listener_two.hash(&mut hasher);
918 let hash_two = hasher.finish();
919 assert_ne!(hash_one, hash_two);
920 }
921
922 #[tokio::test]
923 async fn test_utransport_default_implementations() {
924 struct EmptyTransport {}
925 #[async_trait::async_trait]
926 impl UTransport for EmptyTransport {
927 async fn send(&self, _message: UMessage) -> Result<(), UStatus> {
928 todo!()
929 }
930 }
931
932 let transport = EmptyTransport {};
933 let listener = Arc::new(MockUListener::new());
934
935 assert!(transport
936 .receive(&UUri::any(), None)
937 .await
938 .is_err_and(|e| e.code() == UCode::Unimplemented));
939 assert!(transport
940 .register_listener(&UUri::any(), None, listener.clone())
941 .await
942 .is_err_and(|e| e.code() == UCode::Unimplemented));
943 assert!(transport
944 .unregister_listener(&UUri::any(), None, listener)
945 .await
946 .is_err_and(|e| e.code() == UCode::Unimplemented));
947 }
948
949 #[test]
950 fn test_comparable_listener_pointer_address() {
951 let bar = Arc::new(MockUListener::new());
952 let comp_listener = ComparableListener::new(bar);
953
954 let comp_listener_thread = comp_listener.clone();
955 let handle = std::thread::spawn(move || comp_listener_thread.pointer_address());
956
957 let comp_listener_address_other_thread = handle.join().unwrap();
958 let comp_listener_address_this_thread = comp_listener.pointer_address();
959
960 assert_eq!(
961 comp_listener_address_this_thread,
962 comp_listener_address_other_thread
963 );
964 }
965
966 #[test]
967 fn test_comparable_listener_debug_outputs() {
968 let bar = Arc::new(MockUListener::new());
969 let comp_listener = ComparableListener::new(bar);
970 let debug_output = format!("{comp_listener:?}");
971 assert!(!debug_output.is_empty());
972 }
973
974 #[test_case::test_case(
975 "//vehicle1/AA/1/FFFF",
976 Some("//vehicle2/BB/1/FFFF");
977 "source and sink both having wildcard resource ID")]
978 #[test_case::test_case(
979 "//vehicle1/AA/1/9000",
980 Some("//vehicle2/BB/1/0");
981 "sending notification")]
982 #[test_case::test_case(
983 "//vehicle1/AA/1/0",
984 Some("//vehicle2/BB/1/1");
985 "RPC method invocation")]
986 #[test_case::test_case(
987 "//vehicle1/AA/1/FFFF",
988 Some("//vehicle2/BB/1/1");
989 "receiving RPC requests using wildcard resource ID")]
990 #[test_case::test_case(
991 "//vehicle1/AA/1/0",
992 Some("//vehicle2/BB/1/1");
993 "receiving RPC requests using default resource ID")]
994 #[test_case::test_case(
995 "//vehicle1/AA/1/9000",
996 None;
997 "receiving events published to specific topic")]
998 #[test_case::test_case(
999 "//vehicle1/AA/1/FFFF",
1000 None;
1001 "receiving events published to any topic")]
1002 fn test_verify_filter_criteria_succeeds_for(source: &str, sink: Option<&str>) {
1003 let source_filter = UUri::from_str(source).expect("invalid source URI");
1004 let sink_filter = sink.map(|s| UUri::from_str(s).expect("invalid sink URI"));
1005 assert!(verify_filter_criteria(&source_filter, sink_filter.as_ref()).is_ok());
1006 }
1007
1008 #[test_case::test_case(
1009 UUri::from_str("//vehicle1/AA/1/0").unwrap(),
1010 Some(UUri::from_str("//vehicle2/BB/1/0").unwrap());
1011 "source and sink both having resource ID 0")]
1012 #[test_case::test_case(
1013 UUri::from_str("//vehicle1/AA/1/CC").unwrap(),
1014 Some(UUri::from_str("//vehicle2/BB/1/1A").unwrap());
1015 "sink is RPC but source has invalid resource ID")]
1016 #[test_case::test_case(
1017 UUri::from_str("//vehicle1/AA/1/CC").unwrap(),
1018 None;
1019 "sink is empty but source has non-topic resource ID")]
1020 fn test_verify_filter_criteria_fails_for(source_filter: UUri, sink_filter: Option<UUri>) {
1021 assert!(verify_filter_criteria(&source_filter, sink_filter.as_ref())
1022 .is_err_and(|err| matches!(err.code(), UCode::InvalidArgument)));
1023 }
1024}
1025
1026#[cfg(all(test, feature = "owned-frame-transport"))]
1027mod owned_transport_tests {
1028 use std::str::FromStr;
1029 use std::sync::Mutex;
1030
1031 use async_trait::async_trait;
1032 use bytes::Bytes;
1033
1034 use super::*;
1035 use crate::{PayloadEncoding, UFrameMetadata, UMessageBuilder};
1036
1037 fn topic() -> UUri {
1038 UUri::try_from_parts("vehicle", 0x4210, 0x01, 0x9000).expect("failed to create topic")
1039 }
1040
1041 fn invalid_source_filter() -> UUri {
1042 UUri::from_str("//vehicle/4210/1/10").expect("invalid test URI")
1043 }
1044
1045 fn metadata_with_encoding() -> UFrameMetadata {
1046 let message = UMessageBuilder::publish(topic()).build().expect("message");
1047 crate::frame::metadata::try_project_attributes_to_frame_metadata(
1048 message.attributes(),
1049 Some(PayloadEncoding::RAW),
1050 )
1051 .expect("metadata")
1052 }
1053
1054 fn valid_frame() -> UOwnedFrame {
1055 UOwnedFrame::with_payload(metadata_with_encoding(), Bytes::new()).expect("valid frame")
1056 }
1057
1058 #[tokio::test]
1059 async fn owned_transport_defaults_are_unimplemented() {
1060 struct EmptyTransport;
1061
1062 #[async_trait]
1063 impl UOwnedTransportImpl for EmptyTransport {
1064 async fn send_validated_owned(&self, _frame: UOwnedFrame) -> Result<(), UStatus> {
1065 Ok(())
1066 }
1067 }
1068
1069 let transport = EmptyTransport;
1070 let listener = Arc::new(MockUOwnedListener::new());
1071 let source = UUri::any();
1072
1073 assert!(transport
1074 .receive_owned(&source, None)
1075 .await
1076 .is_err_and(|status| status.code() == UCode::Unimplemented));
1077 assert!(transport
1078 .register_owned_listener(&source, None, listener.clone())
1079 .await
1080 .is_err_and(|status| status.code() == UCode::Unimplemented));
1081 assert!(transport
1082 .unregister_owned_listener(&source, None, listener)
1083 .await
1084 .is_err_and(|status| status.code() == UCode::Unimplemented));
1085 }
1086
1087 #[tokio::test]
1091 async fn owned_transport_validates_filters_before_receive_impl() {
1092 #[derive(Default)]
1093 struct ReceiveCountingTransport {
1094 receive_count: Mutex<usize>,
1095 }
1096
1097 #[async_trait]
1098 impl UOwnedTransportImpl for ReceiveCountingTransport {
1099 async fn send_validated_owned(&self, _frame: UOwnedFrame) -> Result<(), UStatus> {
1100 Ok(())
1101 }
1102
1103 async fn receive_validated_owned(
1104 &self,
1105 _source_filter: &UUri,
1106 _sink_filter: Option<&UUri>,
1107 ) -> Result<UOwnedFrame, UStatus> {
1108 *self
1109 .receive_count
1110 .lock()
1111 .expect("receive count lock poisoned") += 1;
1112 Ok(valid_frame())
1113 }
1114 }
1115
1116 let transport = ReceiveCountingTransport::default();
1117 let status = transport
1118 .receive_owned(&invalid_source_filter(), None)
1119 .await
1120 .expect_err("invalid filters must be rejected");
1121
1122 assert_eq!(status.code(), UCode::InvalidArgument);
1123 assert_eq!(*transport.receive_count.lock().unwrap(), 0);
1124 }
1125
1126 #[tokio::test]
1127 async fn owned_transport_validates_filters_before_register_impl() {
1128 #[derive(Default)]
1129 struct RegisterCountingTransport {
1130 register_count: Mutex<usize>,
1131 }
1132
1133 #[async_trait]
1134 impl UOwnedTransportImpl for RegisterCountingTransport {
1135 async fn send_validated_owned(&self, _frame: UOwnedFrame) -> Result<(), UStatus> {
1136 Ok(())
1137 }
1138
1139 async fn register_validated_owned_listener(
1140 &self,
1141 _source_filter: &UUri,
1142 _sink_filter: Option<&UUri>,
1143 _listener: Arc<dyn UOwnedListener>,
1144 ) -> Result<(), UStatus> {
1145 *self
1146 .register_count
1147 .lock()
1148 .expect("register count lock poisoned") += 1;
1149 Ok(())
1150 }
1151 }
1152
1153 let transport = RegisterCountingTransport::default();
1154 let listener = Arc::new(MockUOwnedListener::new());
1155 let status = transport
1156 .register_owned_listener(&invalid_source_filter(), None, listener)
1157 .await
1158 .expect_err("invalid filters must be rejected");
1159
1160 assert_eq!(status.code(), UCode::InvalidArgument);
1161 assert_eq!(*transport.register_count.lock().unwrap(), 0);
1162 }
1163
1164 #[derive(Default)]
1165 struct CountingOwnedListener {
1166 count: Mutex<usize>,
1167 }
1168
1169 impl CountingOwnedListener {
1170 fn count(&self) -> usize {
1171 *self
1172 .count
1173 .lock()
1174 .expect("owned listener count lock poisoned")
1175 }
1176 }
1177
1178 #[async_trait]
1179 impl UOwnedListener for CountingOwnedListener {
1180 async fn on_receive_owned(&self, _frame: UOwnedFrame) {
1181 *self
1182 .count
1183 .lock()
1184 .expect("owned listener count lock poisoned") += 1;
1185 }
1186 }
1187
1188 #[derive(Default)]
1189 struct ListenerSpyOwnedTransport {
1190 listener: Mutex<Option<Arc<dyn UOwnedListener>>>,
1191 }
1192
1193 #[async_trait]
1194 impl UOwnedTransportImpl for ListenerSpyOwnedTransport {
1195 async fn send_validated_owned(&self, _frame: UOwnedFrame) -> Result<(), UStatus> {
1196 Ok(())
1197 }
1198
1199 async fn register_validated_owned_listener(
1200 &self,
1201 _source_filter: &UUri,
1202 _sink_filter: Option<&UUri>,
1203 listener: Arc<dyn UOwnedListener>,
1204 ) -> Result<(), UStatus> {
1205 *self.listener.lock().expect("listener lock poisoned") = Some(listener);
1206 Ok(())
1207 }
1208 }
1209
1210 #[tokio::test]
1211 async fn validating_owned_listener_drops_invalid_frames_before_user_callback() {
1212 let transport = ListenerSpyOwnedTransport::default();
1213 let listener = Arc::new(CountingOwnedListener::default());
1214 transport
1215 .register_owned_listener(&UUri::any(), None, listener.clone())
1216 .await
1217 .expect("listener registered");
1218 let registered_listener = transport
1219 .listener
1220 .lock()
1221 .expect("listener lock poisoned")
1222 .clone()
1223 .expect("implementation should receive validating listener");
1224
1225 registered_listener.on_receive_owned(valid_frame()).await;
1228 assert_eq!(listener.count(), 1);
1229 }
1230
1231 #[tokio::test]
1232 async fn mock_owned_transport_delegates_send() {
1233 let frame = valid_frame();
1234 let expected = frame.clone();
1235 let mut transport = MockUOwnedTransport::new();
1236 transport
1237 .expect_do_send_validated_owned()
1238 .once()
1239 .withf(move |actual| actual == &expected)
1240 .return_const(Ok(()));
1241
1242 transport.send_owned(frame).await.unwrap();
1243 }
1244}