1#![doc(
20 html_logo_url = "https://commonware.xyz/imgs/rustdoc_logo.svg",
21 html_favicon_url = "https://commonware.xyz/favicon.ico"
22)]
23
24use commonware_macros::stability_scope;
25
26#[macro_use]
27mod macros;
28
29mod network;
30mod process;
31mod storage;
32
33stability_scope!(ALPHA {
34 #[cfg(feature = "arbitrary")]
35 pub mod conformance;
36 pub mod deterministic;
37 pub mod mocks;
38});
39stability_scope!(ALPHA, cfg(not(target_arch = "wasm32")) {
40 pub mod benchmarks;
41});
42stability_scope!(ALPHA, cfg(any(feature = "iouring-storage", feature = "iouring-network")) {
43 mod iouring;
44});
45stability_scope!(BETA, cfg(not(target_arch = "wasm32")) {
46 pub mod tokio;
47});
48stability_scope!(BETA {
49 pub use bytes::{Buf, BufMut};
51 use commonware_macros::select;
52 use commonware_parallel::Rayon;
53 pub use governor::Quota;
55 use iobuf::PoolError;
56 use std::{
57 future::Future,
58 io::Error as IoError,
59 net::SocketAddr,
60 num::NonZeroUsize,
61 sync::Arc,
62 time::{Duration, SystemTime},
63 };
64 pub(crate) use telemetry::metrics::{child_label, prefixed_name, METRICS_PREFIX};
65 use thiserror::Error;
66
67 pub mod iobuf;
68 pub use iobuf::{
69 cache_line_size, page_size, BufferPool, BufferPoolConfig, BufferPoolThreadCache,
70 Builder as IoBufsBuilder, IoBuf, IoBufMut, IoBufs, IoBufsMut,
71 };
72
73 pub mod utils;
74 pub use utils::*;
75
76 pub mod telemetry;
77
78 pub const DEFAULT_BLOB_VERSION: u16 = 0;
80
81 #[derive(Error, Debug, Clone)]
83 pub enum Error {
84 #[error("exited")]
85 Exited,
86 #[error("closed")]
87 Closed,
88 #[error("aborted")]
89 Aborted,
90 #[error("timeout")]
91 Timeout,
92 #[error("bind failed")]
93 BindFailed,
94 #[error("connection failed")]
95 ConnectionFailed,
96 #[error("write failed")]
97 WriteFailed,
98 #[error("read failed")]
99 ReadFailed,
100 #[error("send failed")]
101 SendFailed,
102 #[error("recv failed")]
103 RecvFailed,
104 #[error("dns resolution failed: {0}")]
105 ResolveFailed(String),
106 #[error("partition name invalid, must only contain alphanumeric, dash ('-'), or underscore ('_') characters: {0}")]
107 PartitionNameInvalid(String),
108 #[error("partition creation failed: {0}")]
109 PartitionCreationFailed(String),
110 #[error("partition missing: {0}")]
111 PartitionMissing(String),
112 #[error("partition corrupt: {0}")]
113 PartitionCorrupt(String),
114 #[error("blob open failed: {0}/{1} error: {2}")]
115 BlobOpenFailed(String, String, Arc<IoError>),
116 #[error("blob missing: {0}/{1}")]
117 BlobMissing(String, String),
118 #[error("blob resize failed: {0}/{1} error: {2}")]
119 BlobResizeFailed(String, String, Arc<IoError>),
120 #[error("blob sync failed: {0}/{1} error: {2}")]
121 BlobSyncFailed(String, String, Arc<IoError>),
122 #[error("blob insufficient length")]
123 BlobInsufficientLength,
124 #[error("blob corrupt: {0}/{1} reason: {2}")]
125 BlobCorrupt(String, String, String),
126 #[error("blob version mismatch: expected one of {expected:?}, found {found}")]
127 BlobVersionMismatch {
128 expected: std::ops::RangeInclusive<u16>,
129 found: u16,
130 },
131 #[error("invalid or missing checksum")]
132 InvalidChecksum,
133 #[error("offset overflow")]
134 OffsetOverflow,
135 #[error("io error: {0}")]
136 Io(Arc<IoError>),
137 #[error("buffer pool: {0}")]
138 Pool(#[from] PoolError),
139 }
140
141 impl From<IoError> for Error {
142 fn from(err: IoError) -> Self {
143 Self::Io(Arc::new(err))
144 }
145 }
146
147 pub trait Runner {
150 type Context;
152
153 fn start<F, Fut>(self, f: F) -> Fut::Output
159 where
160 F: FnOnce(Self::Context) -> Fut,
161 Fut: Future;
162 }
163
164 #[derive(Clone, Debug, Default)]
166 pub struct Name {
167 pub label: String,
169 pub attributes: Vec<(String, String)>,
171 }
172
173 pub trait Supervisor: Send + Sync + 'static {
175 fn name(&self) -> Name;
177
178 #[must_use]
188 fn child(&self, label: &'static str) -> Self;
189
190 #[must_use]
255 fn with_attribute(self, key: &'static str, value: impl std::fmt::Display) -> Self;
256 }
257
258 pub trait Spawner: Supervisor {
260 #[must_use]
269 fn shared(self, blocking: bool) -> Self;
270
271 #[must_use]
278 fn dedicated(self) -> Self;
279
280 fn spawn<F, Fut, T>(self, f: F) -> Handle<T>
315 where
316 Self: Sized,
317 F: FnOnce(Self) -> Fut + Send + 'static,
318 Fut: Future<Output = T> + Send + 'static,
319 T: Send + 'static;
320
321 fn stop(
341 self,
342 value: i32,
343 timeout: Option<Duration>,
344 ) -> impl Future<Output = Result<(), Error>> + Send;
345
346 fn stopped(&self) -> signal::Signal;
353 }
354
355 pub trait Strategizer: Spawner {
357 fn strategy(&self, parallelism: NonZeroUsize) -> Rayon;
366 }
367
368 pub trait Metrics: Supervisor {
370 fn register<N: Into<String>, H: Into<String>, M: telemetry::metrics::Metric>(
387 &self,
388 name: N,
389 help: H,
390 metric: M,
391 ) -> telemetry::metrics::Registered<M>;
392
393 fn encode(&self) -> String;
395 }
396
397 pub type RateLimiter<C> = governor::RateLimiter<
402 governor::state::NotKeyed,
403 governor::state::InMemoryState,
404 C,
405 governor::middleware::NoOpMiddleware<<C as governor::clock::Clock>::Instant>,
406 >;
407
408 pub type KeyedRateLimiter<K, C> = governor::RateLimiter<
415 K,
416 governor::state::keyed::HashMapStateStore<K>,
417 C,
418 governor::middleware::NoOpMiddleware<<C as governor::clock::Clock>::Instant>,
419 >;
420
421 pub trait Clock:
427 governor::clock::Clock<Instant = SystemTime>
428 + governor::clock::ReasonablyRealtime
429 + Send
430 + Sync
431 + 'static
432 {
433 fn current(&self) -> SystemTime;
435
436 fn sleep(&self, duration: Duration) -> impl Future<Output = ()> + Send + 'static;
438
439 fn sleep_until(&self, deadline: SystemTime) -> impl Future<Output = ()> + Send + 'static;
441
442 fn timeout<F, T>(
463 &self,
464 duration: Duration,
465 future: F,
466 ) -> impl Future<Output = Result<T, Error>> + Send + '_
467 where
468 F: Future<Output = T> + Send + 'static,
469 T: Send + 'static,
470 {
471 async move {
472 select! {
473 result = future => Ok(result),
474 _ = self.sleep(duration) => Err(Error::Timeout),
475 }
476 }
477 }
478 }
479
480 pub type SinkOf<N> = <<N as Network>::Listener as Listener>::Sink;
482
483 pub type StreamOf<N> = <<N as Network>::Listener as Listener>::Stream;
485
486 pub type ListenerOf<N> = <N as crate::Network>::Listener;
488
489 pub trait Network: Send + Sync + 'static {
492 type Listener: Listener;
496
497 fn bind(
499 &self,
500 socket: SocketAddr,
501 ) -> impl Future<Output = Result<Self::Listener, Error>> + Send;
502
503 fn dial(
505 &self,
506 socket: SocketAddr,
507 ) -> impl Future<Output = Result<(SinkOf<Self>, StreamOf<Self>), Error>> + Send;
508 }
509
510 pub trait Resolver: Send + Sync + 'static {
512 fn resolve(
516 &self,
517 host: &str,
518 ) -> impl Future<Output = Result<Vec<std::net::IpAddr>, Error>> + Send;
519 }
520
521 pub trait Listener: Sync + Send + 'static {
524 type Sink: Sink;
527 type Stream: Stream;
530
531 fn accept(
533 &mut self,
534 ) -> impl Future<Output = Result<(SocketAddr, Self::Sink, Self::Stream), Error>> + Send;
535
536 fn local_addr(&self) -> Result<SocketAddr, std::io::Error>;
538 }
539
540 pub trait Sink: Sync + Send + 'static {
543 fn send(
554 &mut self,
555 bufs: impl Into<IoBufs> + Send,
556 ) -> impl Future<Output = Result<(), Error>> + Send;
557 }
558
559 pub trait Stream: Sync + Send + 'static {
562 fn recv(&mut self, len: usize) -> impl Future<Output = Result<IoBufs, Error>> + Send;
575
576 fn peek(&self, max_len: usize) -> &[u8];
584 }
585
586 pub trait Storage: Send + Sync + 'static {
606 type Blob: Blob;
608
609 fn open(
612 &self,
613 partition: &str,
614 name: &[u8],
615 ) -> impl Future<Output = Result<(Self::Blob, u64), Error>> + Send {
616 async move {
617 let (blob, size, _) = self
618 .open_versioned(partition, name, DEFAULT_BLOB_VERSION..=DEFAULT_BLOB_VERSION)
619 .await?;
620 Ok((blob, size))
621 }
622 }
623
624 fn open_versioned(
641 &self,
642 partition: &str,
643 name: &[u8],
644 versions: std::ops::RangeInclusive<u16>,
645 ) -> impl Future<Output = Result<(Self::Blob, u64, u16), Error>> + Send;
646
647 fn remove(
666 &self,
667 partition: &str,
668 name: Option<&[u8]>,
669 ) -> impl Future<Output = Result<(), Error>> + Send;
670
671 fn scan(&self, partition: &str)
673 -> impl Future<Output = Result<Vec<Vec<u8>>, Error>> + Send;
674 }
675
676 #[allow(clippy::len_without_is_empty)]
691 pub trait Blob: Clone + Send + Sync + 'static {
692 fn read_at_buf(
708 &self,
709 offset: u64,
710 len: usize,
711 bufs: impl Into<IoBufsMut> + Send,
712 ) -> impl Future<Output = Result<IoBufsMut, Error>> + Send;
713
714 fn read_at(
719 &self,
720 offset: u64,
721 len: usize,
722 ) -> impl Future<Output = Result<IoBufsMut, Error>> + Send;
723
724 fn write_at(
726 &self,
727 offset: u64,
728 bufs: impl Into<IoBufs> + Send,
729 ) -> impl Future<Output = Result<(), Error>> + Send;
730
731 fn write_at_sync(
738 &self,
739 offset: u64,
740 bufs: impl Into<IoBufs> + Send,
741 ) -> impl Future<Output = Result<(), Error>> + Send;
742
743 fn resize(&self, len: u64) -> impl Future<Output = Result<(), Error>> + Send;
748
749 fn sync(&self) -> impl Future<Output = Result<(), Error>> + Send;
751
752 fn start_sync(&self) -> impl Future<Output = Handle<()>> + Send;
757 }
758
759 pub trait BufferPooler: Send + Sync + 'static {
761 fn network_buffer_pool(&self) -> &BufferPool;
763
764 fn storage_buffer_pool(&self) -> &BufferPool;
766 }
767});
768stability_scope!(BETA, cfg(feature = "external") {
769 pub trait Pacer: Clock + Send + Sync + 'static {
771 fn pace<'a, F, T>(
791 &'a self,
792 latency: Duration,
793 future: F,
794 ) -> impl Future<Output = T> + Send + 'a
795 where
796 F: Future<Output = T> + Send + 'a,
797 T: Send + 'a;
798 }
799
800 pub trait FutureExt: Future + Send + Sized {
805 fn pace<'a, E>(
807 self,
808 pacer: &'a E,
809 latency: Duration,
810 ) -> impl Future<Output = Self::Output> + Send + 'a
811 where
812 E: Pacer + 'a,
813 Self: Send + 'a,
814 Self::Output: Send + 'a,
815 {
816 pacer.pace(latency, self)
817 }
818 }
819
820 impl<F> FutureExt for F where F: Future + Send {}
821});
822
823#[cfg(test)]
824mod tests {
825 use super::*;
826 use crate::telemetry::metrics::{
827 count_running_tasks,
828 raw::{Counter, Family},
829 EncodeLabelKey, EncodeLabelSetTrait as EncodeLabelSet,
830 EncodeLabelValueTrait as EncodeLabelValue, LabelSetEncoder,
831 };
832 use bytes::Bytes;
833 use commonware_macros::select;
834 use commonware_parallel::Strategy as _;
835 use commonware_utils::{
836 channel::{mpsc, oneshot},
837 futures::Pool as FuturesPool,
838 sync::Mutex,
839 NZUsize, SystemTimeExt, NZU32,
840 };
841 use futures::{
842 future::{pending, ready},
843 join, pin_mut, FutureExt,
844 };
845 use std::{
846 collections::HashMap,
847 net::{IpAddr, Ipv4Addr, Ipv6Addr},
848 pin::Pin,
849 str::FromStr,
850 sync::{
851 atomic::{AtomicU32, Ordering},
852 Arc,
853 },
854 task::{Context as TContext, Poll, Waker},
855 };
856 use tracing::{error, Level};
857 use utils::reschedule;
858
859 fn test_error_future<R: Runner>(runner: R) {
860 #[allow(clippy::unused_async)]
861 async fn error_future() -> Result<&'static str, &'static str> {
862 Err("An error occurred")
863 }
864 let result = runner.start(|_| error_future());
865 assert_eq!(result, Err("An error occurred"));
866 }
867
868 #[test]
869 fn test_handle_can_use_futures_pool() {
870 deterministic::Runner::default().start(|_| async move {
871 let mut pool = FuturesPool::<Result<(), Error>>::default();
872 pool.push(Handle::ready(Ok(())));
873 assert!(pool.next_completed().await.is_ok());
874 });
875 }
876
877 fn test_clock_sleep<R: Runner>(runner: R)
878 where
879 R::Context: Spawner + Clock,
880 {
881 runner.start(|context| async move {
882 let start = context.current();
884 let sleep_duration = Duration::from_millis(10);
885 context.sleep(sleep_duration).await;
886
887 let end = context.current();
889 assert!(end.duration_since(start).unwrap() >= sleep_duration);
890 });
891 }
892
893 fn test_clock_sleep_until<R: Runner>(runner: R)
894 where
895 R::Context: Spawner + Clock + Metrics,
896 {
897 runner.start(|context| async move {
898 let now = context.current();
900 context.sleep_until(now + Duration::from_millis(100)).await;
901
902 let elapsed = now.elapsed().unwrap();
904 assert!(elapsed >= Duration::from_millis(100));
905 });
906 }
907
908 fn test_clock_sleep_until_far_future<R: Runner>(runner: R)
909 where
910 R::Context: Spawner + Clock,
911 {
912 runner.start(|context| async move {
913 let sleep = context.sleep_until(SystemTime::limit());
914 let result = context.timeout(Duration::from_millis(1), sleep).await;
915 assert!(matches!(result, Err(Error::Timeout)));
916 });
917 }
918
919 fn test_clock_timeout<R: Runner>(runner: R)
920 where
921 R::Context: Spawner + Clock,
922 {
923 runner.start(|context| async move {
924 let result = context
926 .timeout(Duration::from_millis(100), async { "success" })
927 .await;
928 assert_eq!(result.unwrap(), "success");
929
930 let result = context
932 .timeout(Duration::from_millis(50), pending::<()>())
933 .await;
934 assert!(matches!(result, Err(Error::Timeout)));
935
936 let result = context
938 .timeout(
939 Duration::from_millis(100),
940 context.sleep(Duration::from_millis(50)),
941 )
942 .await;
943 assert!(result.is_ok());
944 });
945 }
946
947 fn test_root_finishes<R: Runner>(runner: R)
948 where
949 R::Context: Spawner,
950 {
951 runner.start(|context| async move {
952 context.spawn(|_| async move {
953 loop {
954 reschedule().await;
955 }
956 });
957 });
958 }
959
960 fn test_spawn_after_abort<R>(runner: R)
961 where
962 R: Runner,
963 R::Context: Spawner,
964 {
965 runner.start(|context| async move {
966 let child = context.child("child");
968
969 let parent_handle = context.spawn(move |_| async move {
971 pending::<()>().await;
972 });
973 parent_handle.abort();
974
975 let child_handle = child.spawn(move |_| async move {
977 pending::<()>().await;
978 });
979 assert!(matches!(child_handle.await, Err(Error::Closed)));
980 });
981 }
982
983 fn test_spawn_abort<R: Runner>(runner: R, dedicated: bool, blocking: bool)
984 where
985 R::Context: Spawner,
986 {
987 runner.start(|context| async move {
988 let context = if dedicated {
989 assert!(!blocking);
990 context.dedicated()
991 } else {
992 context.shared(blocking)
993 };
994
995 let handle = context.spawn(|_| async move {
996 loop {
997 reschedule().await;
998 }
999 });
1000 handle.abort();
1001 assert!(matches!(handle.await, Err(Error::Closed)));
1002 });
1003 }
1004
1005 fn test_panic_aborts_root<R: Runner>(runner: R) {
1006 let result: Result<(), Error> = runner.start(|_| async move {
1007 panic!("blah");
1008 });
1009 result.unwrap_err();
1010 }
1011
1012 fn test_panic_aborts_spawn<R: Runner>(runner: R)
1013 where
1014 R::Context: Spawner + Clock,
1015 {
1016 runner.start(|context| async move {
1017 context.child("panic").spawn(|_| async move {
1018 panic!("blah");
1019 });
1020
1021 loop {
1023 context.sleep(Duration::from_millis(100)).await;
1024 }
1025 });
1026 }
1027
1028 fn test_panic_aborts_spawn_caught<R: Runner>(runner: R)
1029 where
1030 R::Context: Spawner + Clock,
1031 {
1032 let result: Result<(), Error> = runner.start(|context| async move {
1033 let result = context.child("panic").spawn(|_| async move {
1034 panic!("blah");
1035 });
1036 result.await
1037 });
1038 assert!(matches!(result, Err(Error::Exited)));
1039 }
1040
1041 fn test_multiple_panics<R: Runner>(runner: R)
1042 where
1043 R::Context: Spawner + Clock,
1044 {
1045 runner.start(|context| async move {
1046 context.child("panic").spawn(|_| async move {
1047 panic!("boom 1");
1048 });
1049 context.child("panic").spawn(|_| async move {
1050 panic!("boom 2");
1051 });
1052 context.child("panic").spawn(|_| async move {
1053 panic!("boom 3");
1054 });
1055
1056 loop {
1058 context.sleep(Duration::from_millis(100)).await;
1059 }
1060 });
1061 }
1062
1063 fn test_multiple_panics_caught<R: Runner>(runner: R)
1064 where
1065 R::Context: Spawner + Clock,
1066 {
1067 let (res1, res2, res3) = runner.start(|context| async move {
1068 let handle1 = context.child("panic").spawn(|_| async move {
1069 panic!("boom 1");
1070 });
1071 let handle2 = context.child("panic").spawn(|_| async move {
1072 panic!("boom 2");
1073 });
1074 let handle3 = context.child("panic").spawn(|_| async move {
1075 panic!("boom 3");
1076 });
1077
1078 join!(handle1, handle2, handle3)
1079 });
1080 assert!(matches!(res1, Err(Error::Exited)));
1081 assert!(matches!(res2, Err(Error::Exited)));
1082 assert!(matches!(res3, Err(Error::Exited)));
1083 }
1084
1085 fn test_select<R: Runner>(runner: R) {
1086 runner.start(|_| async move {
1087 let output = Mutex::new(0);
1089 select! {
1090 v1 = ready(1) => {
1091 *output.lock() = v1;
1092 },
1093 v2 = ready(2) => {
1094 *output.lock() = v2;
1095 },
1096 };
1097 assert_eq!(*output.lock(), 1);
1098
1099 select! {
1101 v1 = std::future::pending::<i32>() => {
1102 *output.lock() = v1;
1103 },
1104 v2 = ready(2) => {
1105 *output.lock() = v2;
1106 },
1107 };
1108 assert_eq!(*output.lock(), 2);
1109 });
1110 }
1111
1112 fn test_select_loop<R: Runner>(runner: R)
1114 where
1115 R::Context: Clock,
1116 {
1117 runner.start(|context| async move {
1118 let (sender, mut receiver) = mpsc::unbounded_channel();
1120 for _ in 0..2 {
1121 select! {
1122 v = receiver.recv() => {
1123 panic!("unexpected value: {v:?}");
1124 },
1125 _ = context.sleep(Duration::from_millis(100)) => {
1126 continue;
1127 },
1128 };
1129 }
1130
1131 sender.send(0).unwrap();
1133 sender.send(1).unwrap();
1134
1135 select! {
1137 _ = async {} => {
1138 },
1140 v = receiver.recv() => {
1141 panic!("unexpected value: {v:?}");
1142 },
1143 };
1144
1145 for i in 0..2 {
1147 select! {
1148 _ = context.sleep(Duration::from_millis(100)) => {
1149 panic!("timeout");
1150 },
1151 v = receiver.recv() => {
1152 assert_eq!(v.unwrap(), i);
1153 },
1154 };
1155 }
1156 });
1157 }
1158
1159 fn test_storage_operations<R: Runner>(runner: R)
1160 where
1161 R::Context: Storage,
1162 {
1163 runner.start(|context| async move {
1164 let partition = "test_partition";
1165 let name = b"test_blob";
1166
1167 let (blob, size) = context
1169 .open(partition, name)
1170 .await
1171 .expect("Failed to open blob");
1172 assert_eq!(size, 0, "new blob should have size 0");
1173
1174 let data = b"Hello, Storage!";
1176 blob.write_at(0, data)
1177 .await
1178 .expect("Failed to write to blob");
1179
1180 blob.sync().await.expect("Failed to sync blob");
1182
1183 let read = blob
1185 .read_at(0, data.len())
1186 .await
1187 .expect("Failed to read from blob");
1188 assert_eq!(read.coalesce(), data);
1189
1190 blob.sync().await.expect("Failed to sync blob");
1192
1193 let blobs = context
1195 .scan(partition)
1196 .await
1197 .expect("Failed to scan partition");
1198 assert!(blobs.contains(&name.to_vec()));
1199
1200 let (blob, len) = context
1202 .open(partition, name)
1203 .await
1204 .expect("Failed to reopen blob");
1205 assert_eq!(len, data.len() as u64);
1206
1207 let read = blob.read_at(7, 7).await.expect("Failed to read data");
1209 assert_eq!(read.coalesce(), b"Storage");
1210
1211 blob.sync().await.expect("Failed to sync blob");
1213
1214 context
1216 .remove(partition, Some(name))
1217 .await
1218 .expect("Failed to remove blob");
1219
1220 let blobs = context
1222 .scan(partition)
1223 .await
1224 .expect("Failed to scan partition");
1225 assert!(!blobs.contains(&name.to_vec()));
1226
1227 context
1229 .remove(partition, None)
1230 .await
1231 .expect("Failed to remove partition");
1232
1233 let result = context.scan(partition).await;
1235 assert!(matches!(result, Err(Error::PartitionMissing(_))));
1236 });
1237 }
1238
1239 fn test_blob_read_write<R: Runner>(runner: R)
1240 where
1241 R::Context: Storage,
1242 {
1243 runner.start(|context| async move {
1244 let partition = "test_partition";
1245 let name = b"test_blob_rw";
1246
1247 let (blob, _) = context
1249 .open(partition, name)
1250 .await
1251 .expect("Failed to open blob");
1252
1253 let data1 = b"Hello";
1255 let data2 = b"World";
1256 blob.write_at(0, data1)
1257 .await
1258 .expect("Failed to write data1");
1259 blob.write_at(5, data2)
1260 .await
1261 .expect("Failed to write data2");
1262
1263 let read = blob.read_at(0, 10).await.expect("Failed to read data");
1265 let read = read.coalesce();
1266 assert_eq!(&read.as_ref()[..5], data1);
1267 assert_eq!(&read.as_ref()[5..], data2);
1268
1269 let result = blob.read_at(10, 10).await;
1271 assert!(result.is_err());
1272
1273 let data3 = b"Store";
1275 blob.write_at(5, data3)
1276 .await
1277 .expect("Failed to write data3");
1278
1279 let read = blob.read_at(0, 10).await.expect("Failed to read data");
1281 let read = read.coalesce();
1282 assert_eq!(&read.as_ref()[..5], data1);
1283 assert_eq!(&read.as_ref()[5..], data3);
1284
1285 let result = blob.read_at(10, 10).await;
1287 assert!(result.is_err());
1288 });
1289 }
1290
1291 fn test_blob_resize<R: Runner>(runner: R)
1292 where
1293 R::Context: Storage,
1294 {
1295 runner.start(|context| async move {
1296 let partition = "test_partition_resize";
1297 let name = b"test_blob_resize";
1298
1299 let (blob, _) = context
1301 .open(partition, name)
1302 .await
1303 .expect("Failed to open blob");
1304
1305 let data = b"some data";
1306 blob.write_at(0, data.to_vec())
1307 .await
1308 .expect("Failed to write");
1309 blob.sync().await.expect("Failed to sync after write");
1310
1311 let (blob, len) = context.open(partition, name).await.unwrap();
1313 assert_eq!(len, data.len() as u64);
1314
1315 let new_len = (data.len() as u64) * 2;
1317 blob.resize(new_len)
1318 .await
1319 .expect("Failed to resize to extend");
1320 blob.sync().await.expect("Failed to sync after resize");
1321
1322 let (blob, len) = context.open(partition, name).await.unwrap();
1324 assert_eq!(len, new_len);
1325
1326 let read_buf = blob.read_at(0, data.len()).await.unwrap();
1328 assert_eq!(read_buf.coalesce(), data);
1329
1330 let extended_part = blob.read_at(data.len() as u64, data.len()).await.unwrap();
1332 assert_eq!(extended_part.coalesce(), vec![0; data.len()].as_slice());
1333
1334 blob.resize(data.len() as u64).await.unwrap();
1336 blob.sync().await.unwrap();
1337
1338 let (blob, size) = context.open(partition, name).await.unwrap();
1340 assert_eq!(size, data.len() as u64);
1341
1342 let read_buf = blob.read_at(0, data.len()).await.unwrap();
1344 assert_eq!(read_buf.coalesce(), data);
1345 blob.sync().await.unwrap();
1346 });
1347 }
1348
1349 fn test_many_partition_read_write<R: Runner>(runner: R)
1350 where
1351 R::Context: Storage,
1352 {
1353 runner.start(|context| async move {
1354 let partitions = ["partition1", "partition2", "partition3"];
1355 let name = b"test_blob_rw";
1356 let data1 = b"Hello";
1357 let data2 = b"World";
1358
1359 for (additional, partition) in partitions.iter().enumerate() {
1360 let (blob, _) = context
1362 .open(partition, name)
1363 .await
1364 .expect("Failed to open blob");
1365
1366 blob.write_at(0, data1)
1368 .await
1369 .expect("Failed to write data1");
1370 blob.write_at(5 + additional as u64, data2)
1371 .await
1372 .expect("Failed to write data2");
1373
1374 blob.sync().await.expect("Failed to sync blob");
1376 }
1377
1378 for (additional, partition) in partitions.iter().enumerate() {
1379 let (blob, len) = context
1381 .open(partition, name)
1382 .await
1383 .expect("Failed to open blob");
1384 assert_eq!(len, (data1.len() + data2.len() + additional) as u64);
1385
1386 let read = blob
1388 .read_at(0, 10 + additional)
1389 .await
1390 .expect("Failed to read data");
1391 let read = read.coalesce();
1392 assert_eq!(&read.as_ref()[..5], b"Hello");
1393 assert_eq!(&read.as_ref()[5 + additional..], b"World");
1394 }
1395 });
1396 }
1397
1398 fn test_blob_read_past_length<R: Runner>(runner: R)
1399 where
1400 R::Context: Storage,
1401 {
1402 runner.start(|context| async move {
1403 let partition = "test_partition";
1404 let name = b"test_blob_rw";
1405
1406 let (blob, _) = context
1408 .open(partition, name)
1409 .await
1410 .expect("Failed to open blob");
1411
1412 let result = blob.read_at(0, 10).await;
1414 assert!(result.is_err());
1415
1416 let data = b"Hello, Storage!".to_vec();
1418 blob.write_at(0, data)
1419 .await
1420 .expect("Failed to write to blob");
1421
1422 let result = blob.read_at(0, 20).await;
1424 assert!(result.is_err());
1425 })
1426 }
1427
1428 fn test_blob_clone_and_concurrent_read<R: Runner>(runner: R)
1429 where
1430 R::Context: Spawner + Storage + Metrics,
1431 {
1432 runner.start(|context| async move {
1433 let partition = "test_partition";
1434 let name = b"test_blob_rw";
1435
1436 let (blob, _) = context
1438 .open(partition, name)
1439 .await
1440 .expect("Failed to open blob");
1441
1442 let data = b"Hello, Storage!";
1444 blob.write_at(0, data)
1445 .await
1446 .expect("Failed to write to blob");
1447
1448 blob.sync().await.expect("Failed to sync blob");
1450
1451 let check1 = context.child("check1").spawn({
1453 let blob = blob.clone();
1454 let data_len = data.len();
1455 move |_| async move {
1456 let read = blob
1457 .read_at(0, data_len)
1458 .await
1459 .expect("Failed to read from blob");
1460 assert_eq!(read.coalesce(), data);
1461 }
1462 });
1463 let check2 = context.child("check2").spawn({
1464 let blob = blob.clone();
1465 let data_len = data.len();
1466 move |_| async move {
1467 let read = blob
1468 .read_at(0, data_len)
1469 .await
1470 .expect("Failed to read from blob");
1471 assert_eq!(read.coalesce(), data);
1472 }
1473 });
1474
1475 let result = join!(check1, check2);
1477 assert!(result.0.is_ok());
1478 assert!(result.1.is_ok());
1479
1480 let read = blob
1482 .read_at(0, data.len())
1483 .await
1484 .expect("Failed to read from blob");
1485 assert_eq!(read.coalesce(), data);
1486
1487 drop(blob);
1489
1490 let buffer = context.encode();
1492 assert!(buffer.contains("open_blobs 0"));
1493 });
1494 }
1495
1496 fn test_shutdown<R: Runner>(runner: R)
1497 where
1498 R::Context: Spawner + Metrics + Clock,
1499 {
1500 let kill = 9;
1501 runner.start(|context| async move {
1502 let before = context.child("before").spawn(move |context| async move {
1504 let mut signal = context.stopped();
1505 let value = (&mut signal).await.unwrap();
1506 assert_eq!(value, kill);
1507 drop(signal);
1508 });
1509
1510 let result = context.child("stop").stop(kill, None).await;
1512 assert!(result.is_ok());
1513
1514 let after = context.child("after").spawn(move |context| async move {
1516 let value = context.stopped().await.unwrap();
1518 assert_eq!(value, kill);
1519 });
1520
1521 let result = join!(before, after);
1523 assert!(result.0.is_ok());
1524 assert!(result.1.is_ok());
1525 });
1526 }
1527
1528 fn test_shutdown_multiple_signals<R: Runner>(runner: R)
1529 where
1530 R::Context: Spawner + Metrics + Clock,
1531 {
1532 let kill = 42;
1533 runner.start(|context| async move {
1534 let (started_tx, mut started_rx) = mpsc::channel(3);
1535 let counter = Arc::new(AtomicU32::new(0));
1536
1537 let task = |context: R::Context, cleanup_duration: Duration| {
1540 let counter = counter.clone();
1541 let started_tx = started_tx.clone();
1542 context.spawn(move |context| async move {
1543 let mut signal = context.stopped();
1545 started_tx.send(()).await.unwrap();
1546
1547 let value = (&mut signal).await.unwrap();
1549 assert_eq!(value, kill);
1550 context.sleep(cleanup_duration).await;
1551 counter.fetch_add(1, Ordering::SeqCst);
1552
1553 drop(signal);
1555 })
1556 };
1557
1558 let task1 = task(context.child("cleanup"), Duration::from_millis(10));
1559 let task2 = task(context.child("cleanup"), Duration::from_millis(20));
1560 let task3 = task(context.child("cleanup"), Duration::from_millis(30));
1561
1562 for _ in 0..3 {
1564 started_rx.recv().await.unwrap();
1565 }
1566
1567 context.stop(kill, None).await.unwrap();
1569 assert_eq!(counter.load(Ordering::SeqCst), 3);
1570
1571 let result = join!(task1, task2, task3);
1573 assert!(result.0.is_ok());
1574 assert!(result.1.is_ok());
1575 assert!(result.2.is_ok());
1576 });
1577 }
1578
1579 fn test_shutdown_timeout<R: Runner>(runner: R)
1580 where
1581 R::Context: Spawner + Metrics + Clock,
1582 {
1583 let kill = 42;
1584 runner.start(|context| async move {
1585 let (started_tx, started_rx) = oneshot::channel();
1587
1588 context.child("signal").spawn(move |context| async move {
1590 let signal = context.stopped();
1591 started_tx.send(()).unwrap();
1592 pending::<()>().await;
1593 signal.await.unwrap();
1594 });
1595
1596 started_rx.await.unwrap();
1598 let result = context.stop(kill, Some(Duration::from_millis(100))).await;
1599
1600 assert!(matches!(result, Err(Error::Timeout)));
1602 });
1603 }
1604
1605 fn test_shutdown_multiple_stop_calls<R: Runner>(runner: R)
1606 where
1607 R::Context: Spawner + Metrics + Clock,
1608 {
1609 let kill1 = 42;
1610 let kill2 = 43;
1611
1612 runner.start(|context| async move {
1613 let (started_tx, started_rx) = oneshot::channel();
1614 let counter = Arc::new(AtomicU32::new(0));
1615
1616 let task = context.child("blocking_task").spawn({
1618 let counter = counter.clone();
1619 move |context| async move {
1620 let mut signal = context.stopped();
1622 started_tx.send(()).unwrap();
1623
1624 let value = (&mut signal).await.unwrap();
1626 assert_eq!(value, kill1);
1627 context.sleep(Duration::from_millis(50)).await;
1628
1629 counter.fetch_add(1, Ordering::SeqCst);
1631 drop(signal);
1632 }
1633 });
1634
1635 started_rx.await.unwrap();
1637
1638 let stop_task1 = context.child("stop").stop(kill1, None);
1641 pin_mut!(stop_task1);
1642 let stop_task2 = context.child("stop").stop(kill2, None);
1643 pin_mut!(stop_task2);
1644
1645 assert!(stop_task1.as_mut().now_or_never().is_none());
1647 assert!(stop_task2.as_mut().now_or_never().is_none());
1648
1649 assert!(stop_task1.await.is_ok());
1651 assert!(stop_task2.await.is_ok());
1652
1653 let sig = context.stopped().await;
1655 assert_eq!(sig.unwrap(), kill1);
1656
1657 let result = task.await;
1659 assert!(result.is_ok());
1660 assert_eq!(counter.load(Ordering::SeqCst), 1);
1661
1662 assert!(context.stop(kill2, None).now_or_never().unwrap().is_ok());
1664 });
1665 }
1666
1667 fn test_unfulfilled_shutdown<R: Runner>(runner: R)
1668 where
1669 R::Context: Spawner + Metrics,
1670 {
1671 runner.start(|context| async move {
1672 context.child("before").spawn(move |context| async move {
1674 let mut signal = context.stopped();
1675 let value = (&mut signal).await.unwrap();
1676
1677 assert_eq!(value, 42);
1679 drop(signal);
1680 });
1681
1682 reschedule().await;
1684 });
1685 }
1686
1687 fn test_spawn_dedicated<R: Runner>(runner: R)
1688 where
1689 R::Context: Spawner,
1690 {
1691 runner.start(|context| async move {
1692 let handle = context.dedicated().spawn(|_| async move { 42 });
1693 assert!(matches!(handle.await, Ok(42)));
1694 });
1695 }
1696
1697 fn test_spawn<R: Runner>(runner: R)
1698 where
1699 R::Context: Spawner + Clock,
1700 {
1701 runner.start(|context| async move {
1702 let child_handle = Arc::new(Mutex::new(None));
1703 let child_handle2 = child_handle.clone();
1704
1705 let (parent_initialized_tx, parent_initialized_rx) = oneshot::channel();
1706 let (parent_complete_tx, parent_complete_rx) = oneshot::channel();
1707 let parent_handle = context.spawn(move |context| async move {
1708 let handle = context.spawn(|_| async {});
1710
1711 *child_handle2.lock() = Some(handle);
1713
1714 parent_initialized_tx.send(()).unwrap();
1715
1716 parent_complete_rx.await.unwrap();
1718 });
1719
1720 parent_initialized_rx.await.unwrap();
1722
1723 let child_handle = child_handle.lock().take().unwrap();
1725 assert!(child_handle.await.is_ok());
1726
1727 parent_complete_tx.send(()).unwrap();
1729
1730 assert!(parent_handle.await.is_ok());
1732 });
1733 }
1734
1735 fn test_spawn_abort_on_parent_abort<R: Runner>(runner: R)
1736 where
1737 R::Context: Spawner + Clock,
1738 {
1739 runner.start(|context| async move {
1740 let child_handle = Arc::new(Mutex::new(None));
1741 let child_handle2 = child_handle.clone();
1742
1743 let (parent_initialized_tx, parent_initialized_rx) = oneshot::channel();
1744 let parent_handle = context.spawn(move |context| async move {
1745 let handle = context.spawn(|_| pending::<()>());
1747
1748 *child_handle2.lock() = Some(handle);
1750
1751 parent_initialized_tx.send(()).unwrap();
1752
1753 pending::<()>().await
1755 });
1756
1757 parent_initialized_rx.await.unwrap();
1759
1760 parent_handle.abort();
1762 assert!(matches!(parent_handle.await, Err(Error::Closed)));
1763
1764 let child_handle = child_handle.lock().take().unwrap();
1766 assert!(matches!(child_handle.await, Err(Error::Closed)));
1767 });
1768 }
1769
1770 fn test_spawn_abort_on_parent_completion<R: Runner>(runner: R)
1771 where
1772 R::Context: Spawner + Clock,
1773 {
1774 runner.start(|context| async move {
1775 let child_handle = Arc::new(Mutex::new(None));
1776 let child_handle2 = child_handle.clone();
1777
1778 let (parent_complete_tx, parent_complete_rx) = oneshot::channel();
1779 let parent_handle = context.spawn(move |context| async move {
1780 let handle = context.spawn(|_| pending::<()>());
1782
1783 *child_handle2.lock() = Some(handle);
1785
1786 parent_complete_rx.await.unwrap();
1788 });
1789
1790 parent_complete_tx.send(()).unwrap();
1792
1793 assert!(parent_handle.await.is_ok());
1795
1796 let child_handle = child_handle.lock().take().unwrap();
1798 assert!(matches!(child_handle.await, Err(Error::Closed)));
1799 });
1800 }
1801
1802 fn test_spawn_cascading_abort<R: Runner>(runner: R)
1803 where
1804 R::Context: Spawner + Clock,
1805 {
1806 runner.start(|context| async move {
1807 let c0 = context.child("c0");
1817 let g0 = c0.child("g0");
1818 let g1 = c0.child("g1");
1819 let c1 = context.child("c1");
1820 let g2 = c1.child("g2");
1821 let g3 = c1.child("g3");
1822 let c2 = context.child("c2");
1823 let g4 = c2.child("g4");
1824 let g5 = c2.child("g5");
1825
1826 let handles = Arc::new(Mutex::new(Vec::new()));
1828 let (initialized_tx, mut initialized_rx) = mpsc::channel(9);
1829 let root_task = context.spawn({
1830 let handles = handles.clone();
1831 move |_| async move {
1832 for (context, grandchildren) in [(c0, [g0, g1]), (c1, [g2, g3]), (c2, [g4, g5])]
1833 {
1834 let handle = context.spawn({
1835 let handles = handles.clone();
1836 let initialized_tx = initialized_tx.clone();
1837 move |_| async move {
1838 for grandchild in grandchildren {
1839 let handle = grandchild.spawn(|_| async {
1840 pending::<()>().await;
1841 });
1842 handles.lock().push(handle);
1843 initialized_tx.send(()).await.unwrap();
1844 }
1845
1846 pending::<()>().await;
1847 }
1848 });
1849 handles.lock().push(handle);
1850 initialized_tx.send(()).await.unwrap();
1851 }
1852
1853 pending::<()>().await;
1854 }
1855 });
1856
1857 for _ in 0..9 {
1859 initialized_rx.recv().await.unwrap();
1860 }
1861
1862 assert_eq!(handles.lock().len(), 9);
1864
1865 root_task.abort();
1867 assert!(matches!(root_task.await, Err(Error::Closed)));
1868
1869 let handles = handles.lock().drain(..).collect::<Vec<_>>();
1871 for handle in handles {
1872 assert!(matches!(handle.await, Err(Error::Closed)));
1873 }
1874 });
1875 }
1876
1877 fn test_child_survives_sibling_completion<R: Runner>(runner: R)
1878 where
1879 R::Context: Spawner + Clock,
1880 {
1881 runner.start(|context| async move {
1882 let (child_started_tx, child_started_rx) = oneshot::channel();
1883 let (child_complete_tx, child_complete_rx) = oneshot::channel();
1884 let (child_handle_tx, child_handle_rx) = oneshot::channel();
1885 let (sibling_started_tx, sibling_started_rx) = oneshot::channel();
1886 let (sibling_complete_tx, sibling_complete_rx) = oneshot::channel();
1887 let (sibling_handle_tx, sibling_handle_rx) = oneshot::channel();
1888 let (parent_complete_tx, parent_complete_rx) = oneshot::channel();
1889
1890 let parent = context.spawn(move |context| async move {
1891 let child_handle = context.child("child").spawn(|_| async move {
1893 child_started_tx.send(()).unwrap();
1894 child_complete_rx.await.unwrap();
1896 });
1897 assert!(
1898 child_handle_tx.send(child_handle).is_ok(),
1899 "child handle receiver dropped"
1900 );
1901
1902 let sibling_handle = context.child("sibling").spawn(move |_| async move {
1904 sibling_started_tx.send(()).unwrap();
1905 sibling_complete_rx.await.unwrap();
1907 });
1908 assert!(
1909 sibling_handle_tx.send(sibling_handle).is_ok(),
1910 "sibling handle receiver dropped"
1911 );
1912
1913 parent_complete_rx.await.unwrap();
1915 });
1916
1917 child_started_rx.await.unwrap();
1919 sibling_started_rx.await.unwrap();
1920
1921 sibling_complete_tx.send(()).unwrap();
1923 assert!(sibling_handle_rx.await.is_ok());
1924
1925 child_complete_tx.send(()).unwrap();
1927 assert!(child_handle_rx.await.is_ok());
1928
1929 parent_complete_tx.send(()).unwrap();
1931 assert!(parent.await.is_ok());
1932 });
1933 }
1934
1935 fn test_spawn_clone_chain<R: Runner>(runner: R)
1936 where
1937 R::Context: Spawner + Clock,
1938 {
1939 runner.start(|context| async move {
1940 let (parent_started_tx, parent_started_rx) = oneshot::channel();
1941 let (child_started_tx, child_started_rx) = oneshot::channel();
1942 let (grandchild_started_tx, grandchild_started_rx) = oneshot::channel();
1943 let (child_handle_tx, child_handle_rx) = oneshot::channel();
1944 let (grandchild_handle_tx, grandchild_handle_rx) = oneshot::channel();
1945
1946 let parent = context.child("parent").spawn({
1947 move |context| async move {
1948 let child = context.child("child").spawn({
1949 move |context| async move {
1950 let grandchild = context.child("grandchild").spawn({
1951 move |_| async move {
1952 grandchild_started_tx.send(()).unwrap();
1953 pending::<()>().await;
1954 }
1955 });
1956 assert!(
1957 grandchild_handle_tx.send(grandchild).is_ok(),
1958 "grandchild handle receiver dropped"
1959 );
1960 child_started_tx.send(()).unwrap();
1961 pending::<()>().await;
1962 }
1963 });
1964 assert!(
1965 child_handle_tx.send(child).is_ok(),
1966 "child handle receiver dropped"
1967 );
1968 parent_started_tx.send(()).unwrap();
1969 pending::<()>().await;
1970 }
1971 });
1972
1973 parent_started_rx.await.unwrap();
1974 child_started_rx.await.unwrap();
1975 grandchild_started_rx.await.unwrap();
1976
1977 let child_handle = child_handle_rx.await.unwrap();
1978 let grandchild_handle = grandchild_handle_rx.await.unwrap();
1979
1980 parent.abort();
1981 assert!(parent.await.is_err());
1982
1983 assert!(child_handle.await.is_err());
1984 assert!(grandchild_handle.await.is_err());
1985 });
1986 }
1987
1988 fn test_spawn_sparse_clone_chain<R: Runner>(runner: R)
1989 where
1990 R::Context: Spawner + Clock,
1991 {
1992 runner.start(|context| async move {
1993 let (leaf_started_tx, leaf_started_rx) = oneshot::channel();
1994 let (leaf_handle_tx, leaf_handle_rx) = oneshot::channel();
1995
1996 let parent = context.child("parent").spawn({
1997 move |context| async move {
1998 let clone1 = context.child("clone1");
1999 let clone2 = clone1.child("clone2");
2000 let clone3 = clone2.child("clone3");
2001
2002 let leaf = clone3.spawn({
2003 move |_| async move {
2004 leaf_started_tx.send(()).unwrap();
2005 pending::<()>().await;
2006 }
2007 });
2008
2009 leaf_handle_tx
2010 .send(leaf)
2011 .unwrap_or_else(|_| panic!("leaf handle receiver dropped"));
2012 pending::<()>().await;
2013 }
2014 });
2015
2016 leaf_started_rx.await.unwrap();
2017 let leaf_handle = leaf_handle_rx.await.unwrap();
2018
2019 parent.abort();
2020 assert!(parent.await.is_err());
2021 assert!(leaf_handle.await.is_err());
2022 });
2023 }
2024
2025 fn test_spawn_blocking<R: Runner>(runner: R, dedicated: bool)
2026 where
2027 R::Context: Spawner,
2028 {
2029 runner.start(|context| async move {
2030 let context = if dedicated {
2031 context.dedicated()
2032 } else {
2033 context.shared(true)
2034 };
2035
2036 let handle = context.spawn(|_| async move { 42 });
2037 let result = handle.await;
2038 assert!(matches!(result, Ok(42)));
2039 });
2040 }
2041
2042 fn test_spawn_blocking_panic<R: Runner>(runner: R, dedicated: bool)
2043 where
2044 R::Context: Spawner + Clock,
2045 {
2046 runner.start(|context| async move {
2047 if dedicated {
2048 context.child("blocking").dedicated().spawn(|_| async move {
2049 panic!("blocking task panicked");
2050 });
2051 } else {
2052 context
2053 .child("blocking")
2054 .shared(true)
2055 .spawn(|_| async move {
2056 panic!("blocking task panicked");
2057 });
2058 }
2059
2060 loop {
2062 context.sleep(Duration::from_millis(100)).await;
2063 }
2064 });
2065 }
2066
2067 fn test_spawn_blocking_panic_caught<R: Runner>(runner: R, dedicated: bool)
2068 where
2069 R::Context: Spawner + Clock,
2070 {
2071 let result: Result<(), Error> = runner.start(|context| async move {
2072 let handle = if dedicated {
2073 context.child("blocking").dedicated().spawn(|_| async move {
2074 panic!("blocking task panicked");
2075 })
2076 } else {
2077 context
2078 .child("blocking")
2079 .shared(true)
2080 .spawn(|_| async move {
2081 panic!("blocking task panicked");
2082 })
2083 };
2084 handle.await
2085 });
2086 assert!(matches!(result, Err(Error::Exited)));
2087 }
2088
2089 fn test_circular_reference_prevents_cleanup<R: Runner>(runner: R) {
2090 runner.start(|_| async move {
2091 let dropper = Arc::new(());
2093 let executor = deterministic::Runner::default();
2094 executor.start({
2095 let dropper = dropper.clone();
2096 move |context| async move {
2097 let (setup_tx, mut setup_rx) = mpsc::unbounded_channel::<()>();
2099 let (tx1, mut rx1) = mpsc::unbounded_channel::<()>();
2100 let (tx2, mut rx2) = mpsc::unbounded_channel::<()>();
2101
2102 context.child("task1").spawn({
2104 let setup_tx = setup_tx.clone();
2105 let dropper = dropper.clone();
2106 move |_| async move {
2107 tx2.send(()).unwrap();
2109 rx1.recv().await.unwrap();
2110 setup_tx.send(()).unwrap();
2111
2112 while rx1.recv().await.is_some() {}
2114 drop(tx2);
2115 drop(dropper);
2116 }
2117 });
2118
2119 context.child("task2").spawn(move |_| async move {
2121 tx1.send(()).unwrap();
2123 rx2.recv().await.unwrap();
2124 setup_tx.send(()).unwrap();
2125
2126 while rx2.recv().await.is_some() {}
2128 drop(tx1);
2129 drop(dropper);
2130 });
2131
2132 setup_rx.recv().await.unwrap();
2134 setup_rx.recv().await.unwrap();
2135 }
2136 });
2137
2138 Arc::try_unwrap(dropper).expect("references remaining");
2140 });
2141 }
2142
2143 fn test_late_waker<R: Runner>(runner: R)
2144 where
2145 R::Context: Metrics + Spawner,
2146 {
2147 struct CaptureWaker {
2150 tx: Option<oneshot::Sender<Waker>>,
2151 sent: bool,
2152 }
2153 impl Future for CaptureWaker {
2154 type Output = ();
2155 fn poll(mut self: Pin<&mut Self>, cx: &mut TContext<'_>) -> Poll<Self::Output> {
2156 if !self.sent {
2157 if let Some(tx) = self.tx.take() {
2158 let _ = tx.send(cx.waker().clone());
2160 }
2161 self.sent = true;
2162 }
2163 Poll::Pending
2164 }
2165 }
2166
2167 struct WakeOnDrop(Option<Waker>);
2169 impl Drop for WakeOnDrop {
2170 fn drop(&mut self) {
2171 if let Some(w) = self.0.take() {
2172 w.wake_by_ref();
2173 }
2174 }
2175 }
2176
2177 let holder = runner.start(|context| async move {
2179 let (tx, rx) = oneshot::channel::<Waker>();
2181
2182 context.child("capture_waker").spawn(move |_| async move {
2184 CaptureWaker {
2185 tx: Some(tx),
2186 sent: false,
2187 }
2188 .await;
2189 });
2190
2191 utils::reschedule().await;
2193
2194 let waker = rx.await.expect("waker not received");
2196
2197 WakeOnDrop(Some(waker))
2199 });
2200
2201 drop(holder);
2204 }
2205
2206 fn test_metrics<R: Runner>(runner: R)
2207 where
2208 R::Context: Metrics,
2209 {
2210 runner.start(|context| async move {
2211 assert_eq!(context.name().label, "");
2213
2214 let counter = Counter::<u64>::default();
2216 let _registered = context.register("test", "test", counter.clone());
2217
2218 counter.inc();
2220
2221 let buffer = context.encode();
2223 assert!(buffer.contains("test_total 1"));
2224
2225 let context = context.child("nested");
2227 let nested_counter = Counter::<u64>::default();
2228 let _nested_registered = context.register("test", "test", nested_counter.clone());
2229
2230 nested_counter.inc();
2232
2233 let buffer = context.encode();
2235 assert!(buffer.contains("nested_test_total 1"));
2236 assert!(buffer.contains("test_total 1"));
2237 });
2238 }
2239
2240 fn test_metrics_with_attribute<R: Runner>(runner: R)
2241 where
2242 R::Context: Metrics,
2243 {
2244 runner.start(|context| async move {
2245 let ctx_epoch5 = context.child("consensus").with_attribute("epoch", "e5");
2247
2248 let counter = Counter::<u64>::default();
2250 let _epoch5 = ctx_epoch5.register("votes", "vote count", counter.clone());
2251 counter.inc();
2252
2253 let buffer = context.encode();
2255 assert!(
2256 buffer.contains("consensus_votes_total{epoch=\"e5\"} 1"),
2257 "Expected metric with epoch attribute, got: {}",
2258 buffer
2259 );
2260
2261 let ctx_epoch6 = context.child("consensus").with_attribute("epoch", "e6");
2263 let counter2 = Counter::<u64>::default();
2264 let _epoch6 = ctx_epoch6.register("votes", "vote count", counter2.clone());
2265 counter2.inc();
2266 counter2.inc();
2267
2268 let buffer = context.encode();
2270 assert!(
2271 buffer.contains("consensus_votes_total{epoch=\"e5\"} 1"),
2272 "Expected metric with epoch=e5, got: {}",
2273 buffer
2274 );
2275 assert!(
2276 buffer.contains("consensus_votes_total{epoch=\"e6\"} 2"),
2277 "Expected metric with epoch=e6, got: {}",
2278 buffer
2279 );
2280
2281 assert_eq!(
2283 buffer.matches("# HELP consensus_votes").count(),
2284 1,
2285 "HELP should appear exactly once, got: {}",
2286 buffer
2287 );
2288 assert_eq!(
2289 buffer.matches("# TYPE consensus_votes").count(),
2290 1,
2291 "TYPE should appear exactly once, got: {}",
2292 buffer
2293 );
2294
2295 let ctx_multi = context
2297 .child("engine")
2298 .with_attribute("region", "us")
2299 .with_attribute("instance", "i1");
2300 let counter3 = Counter::<u64>::default();
2301 let _multi = ctx_multi.register("requests", "request count", counter3.clone());
2302 counter3.inc();
2303
2304 let buffer = context.encode();
2305 assert!(
2306 buffer.contains("engine_requests_total{instance=\"i1\",region=\"us\"} 1"),
2307 "Expected metric with sorted attributes, got: {}",
2308 buffer
2309 );
2310 });
2311 }
2312
2313 #[test]
2314 fn test_deterministic_metrics_with_attribute() {
2315 let executor = deterministic::Runner::default();
2316 test_metrics_with_attribute(executor);
2317 }
2318
2319 #[test]
2320 fn test_tokio_metrics_with_attribute() {
2321 let runner = tokio::Runner::default();
2322 test_metrics_with_attribute(runner);
2323 }
2324
2325 fn test_metrics_attribute_with_nested_label<R: Runner>(runner: R)
2326 where
2327 R::Context: Metrics,
2328 {
2329 runner.start(|context| async move {
2330 let ctx = context
2332 .child("orchestrator")
2333 .with_attribute("epoch", "e5")
2334 .child("engine");
2335
2336 let counter = Counter::<u64>::default();
2338 let _registered = ctx.register("votes", "vote count", counter.clone());
2339 counter.inc();
2340
2341 let buffer = context.encode();
2343 assert!(
2344 buffer.contains("orchestrator_engine_votes_total{epoch=\"e5\"} 1"),
2345 "Expected metric with preserved epoch attribute, got: {}",
2346 buffer
2347 );
2348
2349 let ctx2 = context
2351 .child("outer")
2352 .with_attribute("region", "us")
2353 .child("middle")
2354 .with_attribute("az", "east")
2355 .child("inner");
2356
2357 let counter2 = Counter::<u64>::default();
2358 let _registered2 = ctx2.register("requests", "request count", counter2.clone());
2359 counter2.inc();
2360 counter2.inc();
2361
2362 let buffer = context.encode();
2363 assert!(
2364 buffer.contains("outer_middle_inner_requests_total{az=\"east\",region=\"us\"} 2"),
2365 "Expected metric with all attributes preserved and sorted, got: {}",
2366 buffer
2367 );
2368 });
2369 }
2370
2371 #[test]
2372 fn test_deterministic_metrics_attribute_with_nested_label() {
2373 let executor = deterministic::Runner::default();
2374 test_metrics_attribute_with_nested_label(executor);
2375 }
2376
2377 #[test]
2378 fn test_tokio_metrics_attribute_with_nested_label() {
2379 let runner = tokio::Runner::default();
2380 test_metrics_attribute_with_nested_label(runner);
2381 }
2382
2383 fn test_metrics_attributes_isolated_between_contexts<R: Runner>(runner: R)
2384 where
2385 R::Context: Metrics,
2386 {
2387 runner.start(|context| async move {
2388 let ctx_a = context.child("component_a").with_attribute("epoch", 1);
2390 let ctx_b = context.child("component_b").with_attribute("epoch", 2);
2391
2392 let c1 = Counter::<u64>::default();
2394 let _ctx_a_requests = ctx_a.register("requests", "help", c1);
2395
2396 let c2 = Counter::<u64>::default();
2398 let _ctx_b_requests = ctx_b.register("requests", "help", c2);
2399
2400 let c3 = Counter::<u64>::default();
2402 let _ctx_a_errors = ctx_a.register("errors", "help", c3);
2403
2404 let output = context.encode();
2405
2406 assert!(
2408 output.contains("component_a_requests_total{epoch=\"1\"} 0"),
2409 "ctx_a requests should have epoch=1: {output}"
2410 );
2411 assert!(
2412 output.contains("component_a_errors_total{epoch=\"1\"} 0"),
2413 "ctx_a errors should have epoch=1: {output}"
2414 );
2415 assert!(
2416 !output.contains("component_a_requests_total{epoch=\"2\"}"),
2417 "ctx_a requests should not have epoch=2: {output}"
2418 );
2419
2420 assert!(
2422 output.contains("component_b_requests_total{epoch=\"2\"} 0"),
2423 "ctx_b should have epoch=2: {output}"
2424 );
2425 assert!(
2426 !output.contains("component_b_requests_total{epoch=\"1\"}"),
2427 "ctx_b should not have epoch=1: {output}"
2428 );
2429 });
2430 }
2431
2432 #[test]
2433 fn test_deterministic_metrics_attributes_isolated_between_contexts() {
2434 let executor = deterministic::Runner::default();
2435 test_metrics_attributes_isolated_between_contexts(executor);
2436 }
2437
2438 #[test]
2439 fn test_tokio_metrics_attributes_isolated_between_contexts() {
2440 let runner = tokio::Runner::default();
2441 test_metrics_attributes_isolated_between_contexts(runner);
2442 }
2443
2444 fn test_metrics_spawn_attribute_cardinality<R: Runner>(runner: R)
2451 where
2452 R::Context: Spawner + Metrics + Clock,
2453 {
2454 runner.start(|context| async move {
2455 const ROUNDS: u64 = 128;
2456
2457 let mut handles = Vec::with_capacity(ROUNDS as usize);
2458 for round in 0..ROUNDS {
2459 let handle = context
2460 .child("deferred_verify")
2461 .with_attribute("round", round)
2462 .spawn(move |_| async move { round });
2463 handles.push(handle);
2464 }
2465 for (expected, handle) in handles.into_iter().enumerate() {
2466 assert_eq!(handle.await.expect("task failed"), expected as u64);
2467 }
2468
2469 while count_running_tasks(&context, "deferred_verify") > 0 {
2473 context.sleep(Duration::from_millis(10)).await;
2474 }
2475 let buffer = context.encode();
2476
2477 let spawned_lines = buffer
2481 .lines()
2482 .filter(|line| {
2483 line.starts_with("runtime_tasks_spawned_total{")
2484 && line.contains("name=\"deferred_verify\"")
2485 })
2486 .count();
2487 let running_lines = buffer
2488 .lines()
2489 .filter(|line| {
2490 line.starts_with("runtime_tasks_running{")
2491 && line.contains("name=\"deferred_verify\"")
2492 })
2493 .count();
2494 assert_eq!(
2495 spawned_lines, 1,
2496 "expected exactly 1 runtime_tasks_spawned entry for deferred_verify, got {spawned_lines}: {buffer}",
2497 );
2498 assert_eq!(
2499 running_lines, 1,
2500 "expected exactly 1 runtime_tasks_running entry for deferred_verify, got {running_lines}: {buffer}",
2501 );
2502
2503 let spawned_value = format!(
2505 "runtime_tasks_spawned_total{{name=\"deferred_verify\",kind=\"Task\",execution=\"Shared\"}} {ROUNDS}"
2506 );
2507 assert!(
2508 buffer.contains(&spawned_value),
2509 "expected accumulated spawned counter `{spawned_value}`, got: {buffer}",
2510 );
2511 let running_value = "runtime_tasks_running{name=\"deferred_verify\",kind=\"Task\",execution=\"Shared\"} 0";
2512 assert!(
2513 buffer.contains(running_value),
2514 "expected running gauge to return to 0, got: {buffer}",
2515 );
2516
2517 assert!(
2520 !buffer
2521 .lines()
2522 .any(|line| line.starts_with("runtime_tasks_")
2523 && line.contains("round=")),
2524 "task metrics must not carry `round` attribute: {buffer}",
2525 );
2526 });
2527 }
2528
2529 #[test]
2530 fn test_deterministic_metrics_spawn_attribute_cardinality() {
2531 let executor = deterministic::Runner::default();
2532 test_metrics_spawn_attribute_cardinality(executor);
2533 }
2534
2535 #[test]
2536 fn test_tokio_metrics_spawn_attribute_cardinality() {
2537 let runner = tokio::Runner::default();
2538 test_metrics_spawn_attribute_cardinality(runner);
2539 }
2540
2541 fn test_metrics_attributes_sorted_deterministically<R: Runner>(runner: R)
2542 where
2543 R::Context: Metrics,
2544 {
2545 runner.start(|context| async move {
2546 let ctx_ab = context
2548 .child("service")
2549 .with_attribute("region", "us")
2550 .with_attribute("env", "prod");
2551
2552 let ctx_ba = context
2553 .child("service")
2554 .with_attribute("env", "prod")
2555 .with_attribute("region", "us");
2556
2557 let c1 = Counter::<u64>::default();
2559 let _requests = ctx_ab.register("requests", "help", c1.clone());
2560 c1.inc();
2561
2562 let c2 = Counter::<u64>::default();
2564 let _errors = ctx_ba.register("errors", "help", c2.clone());
2565 c2.inc();
2566 c2.inc();
2567
2568 let output = context.encode();
2569
2570 assert!(
2572 output.contains("service_requests_total{env=\"prod\",region=\"us\"} 1"),
2573 "requests should have sorted labels: {output}"
2574 );
2575 assert!(
2576 output.contains("service_errors_total{env=\"prod\",region=\"us\"} 2"),
2577 "errors should have sorted labels: {output}"
2578 );
2579
2580 assert!(
2582 !output.contains("region=\"us\",env=\"prod\""),
2583 "should not have unsorted label order: {output}"
2584 );
2585 });
2586 }
2587
2588 #[test]
2589 fn test_deterministic_metrics_attributes_sorted_deterministically() {
2590 let executor = deterministic::Runner::default();
2591 test_metrics_attributes_sorted_deterministically(executor);
2592 }
2593
2594 #[test]
2595 fn test_tokio_metrics_attributes_sorted_deterministically() {
2596 let runner = tokio::Runner::default();
2597 test_metrics_attributes_sorted_deterministically(runner);
2598 }
2599
2600 fn test_metrics_nested_labels_with_attributes<R: Runner>(runner: R)
2601 where
2602 R::Context: Metrics,
2603 {
2604 runner.start(|context| async move {
2605 let svc_a = context.child("service_a");
2607
2608 let svc_a_v2 = context.child("service_a").with_attribute("version", 2);
2610
2611 let svc_b_worker = context.child("service_b").child("worker");
2613
2614 let svc_b_worker_shard = context
2616 .child("service_b")
2617 .child("worker")
2618 .with_attribute("shard", 99);
2619
2620 let svc_b_manager = context.child("service_b").child("manager");
2622
2623 let svc_c = context.child("service_c");
2625
2626 let c1 = Counter::<u64>::default();
2628 let _svc_a = svc_a.register("requests", "help", c1);
2629
2630 let c2 = Counter::<u64>::default();
2631 let _svc_a_v2 = svc_a_v2.register("requests", "help", c2);
2632
2633 let c3 = Counter::<u64>::default();
2634 let _svc_b_worker = svc_b_worker.register("tasks", "help", c3);
2635
2636 let c4 = Counter::<u64>::default();
2637 let _svc_b_worker_shard = svc_b_worker_shard.register("tasks", "help", c4);
2638
2639 let c5 = Counter::<u64>::default();
2640 let _svc_b_manager = svc_b_manager.register("decisions", "help", c5);
2641
2642 let c6 = Counter::<u64>::default();
2643 let _svc_c = svc_c.register("requests", "help", c6);
2644
2645 let output = context.encode();
2646
2647 assert!(
2649 output.contains("service_a_requests_total 0"),
2650 "svc_a plain should exist: {output}"
2651 );
2652 assert!(
2653 output.contains("service_a_requests_total{version=\"2\"} 0"),
2654 "svc_a_v2 should have version=2: {output}"
2655 );
2656
2657 assert!(
2659 output.contains("service_b_worker_tasks_total 0"),
2660 "svc_b_worker plain should exist: {output}"
2661 );
2662 assert!(
2663 output.contains("service_b_worker_tasks_total{shard=\"99\"} 0"),
2664 "svc_b_worker_shard should have shard=99: {output}"
2665 );
2666
2667 assert!(
2669 output.contains("service_b_manager_decisions_total 0"),
2670 "svc_b_manager should have no attributes: {output}"
2671 );
2672 assert!(
2673 !output.contains("service_b_manager_decisions_total{"),
2674 "svc_b_manager should have no attributes at all: {output}"
2675 );
2676
2677 assert!(
2679 output.contains("service_c_requests_total 0"),
2680 "svc_c should have no attributes: {output}"
2681 );
2682 assert!(
2683 !output.contains("service_c_requests_total{"),
2684 "svc_c should have no attributes at all: {output}"
2685 );
2686
2687 assert!(
2689 !output.contains("service_b_manager_decisions_total{shard="),
2690 "svc_b_manager should not have shard: {output}"
2691 );
2692 assert!(
2693 !output.contains("service_a_requests_total{shard="),
2694 "svc_a should not have shard: {output}"
2695 );
2696 assert!(
2697 !output.contains("service_c_requests_total{version="),
2698 "svc_c should not have version: {output}"
2699 );
2700 });
2701 }
2702
2703 #[test]
2704 fn test_deterministic_metrics_nested_labels_with_attributes() {
2705 let executor = deterministic::Runner::default();
2706 test_metrics_nested_labels_with_attributes(executor);
2707 }
2708
2709 #[test]
2710 fn test_tokio_metrics_nested_labels_with_attributes() {
2711 let runner = tokio::Runner::default();
2712 test_metrics_nested_labels_with_attributes(runner);
2713 }
2714
2715 fn test_metrics_family_with_attributes<R: Runner>(runner: R)
2716 where
2717 R::Context: Metrics,
2718 {
2719 runner.start(|context| async move {
2720 #[derive(Clone, Debug, Hash, PartialEq, Eq, EncodeLabelSet)]
2721 struct RequestLabels {
2722 method: String,
2723 status: u16,
2724 }
2725
2726 let ctx = context
2728 .child("api")
2729 .with_attribute("region", "us_east")
2730 .with_attribute("env", "prod");
2731
2732 let requests: Family<RequestLabels, Counter<u64>> = Family::default();
2734 let _requests = ctx.register("requests", "HTTP requests", requests.clone());
2735
2736 requests
2738 .get_or_create(&RequestLabels {
2739 method: "GET".to_string(),
2740 status: 200,
2741 })
2742 .inc();
2743 requests
2744 .get_or_create(&RequestLabels {
2745 method: "POST".to_string(),
2746 status: 201,
2747 })
2748 .inc();
2749 requests
2750 .get_or_create(&RequestLabels {
2751 method: "GET".to_string(),
2752 status: 404,
2753 })
2754 .inc();
2755
2756 let output = context.encode();
2757
2758 assert!(
2762 output.contains(
2763 "api_requests_total{env=\"prod\",region=\"us_east\",method=\"GET\",status=\"200\"} 1"
2764 ),
2765 "GET 200 should have merged labels: {output}"
2766 );
2767 assert!(
2768 output.contains(
2769 "api_requests_total{env=\"prod\",region=\"us_east\",method=\"POST\",status=\"201\"} 1"
2770 ),
2771 "POST 201 should have merged labels: {output}"
2772 );
2773 assert!(
2774 output.contains(
2775 "api_requests_total{env=\"prod\",region=\"us_east\",method=\"GET\",status=\"404\"} 1"
2776 ),
2777 "GET 404 should have merged labels: {output}"
2778 );
2779
2780 let ctx_plain = context.child("api_plain");
2782 let plain_requests: Family<RequestLabels, Counter<u64>> = Family::default();
2783 let _plain_requests =
2784 ctx_plain.register("requests", "HTTP requests", plain_requests.clone());
2785
2786 plain_requests
2787 .get_or_create(&RequestLabels {
2788 method: "DELETE".to_string(),
2789 status: 204,
2790 })
2791 .inc();
2792
2793 let output = context.encode();
2794
2795 assert!(
2797 output.contains("api_plain_requests_total{method=\"DELETE\",status=\"204\"} 1"),
2798 "plain DELETE should have only family labels: {output}"
2799 );
2800 assert!(
2801 !output.contains("api_plain_requests_total{env="),
2802 "plain should not have env attribute: {output}"
2803 );
2804 assert!(
2805 !output.contains("api_plain_requests_total{region="),
2806 "plain should not have region attribute: {output}"
2807 );
2808 });
2809 }
2810
2811 #[test]
2812 fn test_deterministic_metrics_family_with_attributes() {
2813 let executor = deterministic::Runner::default();
2814 test_metrics_family_with_attributes(executor);
2815 }
2816
2817 #[test]
2818 fn test_tokio_metrics_family_with_attributes() {
2819 let runner = tokio::Runner::default();
2820 test_metrics_family_with_attributes(runner);
2821 }
2822
2823 fn test_register_and_encode<R: Runner>(runner: R)
2824 where
2825 R::Context: Metrics,
2826 {
2827 runner.start(|context| async move {
2828 let counter =
2829 context
2830 .child("engine")
2831 .register("votes", "vote count", Counter::<u64>::default());
2832 counter.inc();
2833
2834 let buffer = context.encode();
2835 assert!(
2836 buffer.contains("engine_votes_total 1"),
2837 "registered metric should appear in encode: {buffer}"
2838 );
2839 });
2840 }
2841
2842 #[test]
2843 fn test_deterministic_register_and_encode() {
2844 let executor = deterministic::Runner::default();
2845 test_register_and_encode(executor);
2846 }
2847
2848 #[test]
2849 fn test_tokio_register_and_encode() {
2850 let runner = tokio::Runner::default();
2851 test_register_and_encode(runner);
2852 }
2853
2854 fn test_register_drop_removes_metrics<R: Runner>(runner: R)
2855 where
2856 R::Context: Metrics,
2857 {
2858 runner.start(|context| async move {
2859 let permanent = context.child("permanent").register(
2860 "counter",
2861 "permanent counter",
2862 Counter::<u64>::default(),
2863 );
2864 permanent.inc();
2865
2866 let counter =
2867 context
2868 .child("engine")
2869 .register("votes", "vote count", Counter::<u64>::default());
2870 counter.inc();
2871
2872 let buffer = context.encode();
2873 assert!(buffer.contains("permanent_counter_total 1"));
2874 assert!(buffer.contains("engine_votes_total 1"));
2875
2876 drop(counter);
2877
2878 let buffer = context.encode();
2879 assert!(
2880 buffer.contains("permanent_counter_total 1"),
2881 "other registered metrics should survive handle drop: {buffer}"
2882 );
2883 assert!(
2884 !buffer.contains("engine_votes"),
2885 "metric should be removed after handle drop: {buffer}"
2886 );
2887 });
2888 }
2889
2890 #[test]
2891 fn test_deterministic_register_drop_removes_metrics() {
2892 let executor = deterministic::Runner::default();
2893 test_register_drop_removes_metrics(executor);
2894 }
2895
2896 #[test]
2897 fn test_tokio_register_drop_removes_metrics() {
2898 let runner = tokio::Runner::default();
2899 test_register_drop_removes_metrics(runner);
2900 }
2901
2902 fn test_register_with_attributes<R: Runner>(runner: R)
2903 where
2904 R::Context: Metrics,
2905 {
2906 runner.start(|context| async move {
2907 let epoch1 = context.child("engine").with_attribute("epoch", 1).register(
2908 "votes",
2909 "vote count",
2910 Counter::<u64>::default(),
2911 );
2912 epoch1.inc();
2913
2914 let epoch2 = context.child("engine").with_attribute("epoch", 2).register(
2915 "votes",
2916 "vote count",
2917 Counter::<u64>::default(),
2918 );
2919 epoch2.inc();
2920 epoch2.inc();
2921
2922 let buffer = context.encode();
2923 assert!(buffer.contains("engine_votes_total{epoch=\"1\"} 1"));
2924 assert!(buffer.contains("engine_votes_total{epoch=\"2\"} 2"));
2925
2926 assert_eq!(
2927 buffer.matches("# HELP engine_votes").count(),
2928 1,
2929 "HELP should appear once: {buffer}"
2930 );
2931 assert_eq!(
2932 buffer.matches("# TYPE engine_votes").count(),
2933 1,
2934 "TYPE should appear once: {buffer}"
2935 );
2936
2937 drop(epoch1);
2938 let buffer = context.encode();
2939 assert!(
2940 !buffer.contains("epoch=\"1\""),
2941 "epoch 1 should be gone: {buffer}"
2942 );
2943 assert!(buffer.contains("engine_votes_total{epoch=\"2\"} 2"));
2944
2945 drop(epoch2);
2946 let buffer = context.encode();
2947 assert!(
2948 !buffer.contains("engine_votes"),
2949 "all epoch metrics should be gone: {buffer}"
2950 );
2951 });
2952 }
2953
2954 #[test]
2955 fn test_deterministic_register_with_attributes() {
2956 let executor = deterministic::Runner::default();
2957 test_register_with_attributes(executor);
2958 }
2959
2960 #[test]
2961 fn test_tokio_register_with_attributes() {
2962 let runner = tokio::Runner::default();
2963 test_register_with_attributes(runner);
2964 }
2965
2966 fn test_reregister_after_drop<R: Runner>(runner: R)
2967 where
2968 R::Context: Metrics,
2969 {
2970 runner.start(|context| async move {
2971 let votes = context.child("engine").with_attribute("epoch", 1).register(
2972 "votes",
2973 "vote count",
2974 Counter::<u64>::default(),
2975 );
2976 drop(votes);
2977
2978 let replacement = context.child("engine").with_attribute("epoch", 1).register(
2979 "votes",
2980 "vote count",
2981 Counter::<u64>::default(),
2982 );
2983 drop(replacement);
2984 });
2985 }
2986
2987 #[test]
2988 fn test_deterministic_reregister_after_drop() {
2989 let executor = deterministic::Runner::default();
2990 test_reregister_after_drop(executor);
2991 }
2992
2993 #[test]
2994 fn test_tokio_reregister_after_drop() {
2995 let runner = tokio::Runner::default();
2996 test_reregister_after_drop(runner);
2997 }
2998
2999 fn test_register_clone_keeps_metric_alive<R: Runner>(runner: R)
3000 where
3001 R::Context: Metrics,
3002 {
3003 runner.start(|context| async move {
3004 let registered =
3005 context
3006 .child("engine")
3007 .register("votes", "vote count", Counter::<u64>::default());
3008 registered.inc();
3009 let clone = registered.clone();
3010
3011 let buffer = context.encode();
3012 assert!(
3013 buffer.contains("engine_votes_total 1"),
3014 "metric should remain registered while any handle exists: {buffer}"
3015 );
3016
3017 drop(registered);
3018 let buffer = context.encode();
3019 assert!(
3020 buffer.contains("engine_votes_total 1"),
3021 "metric should survive while clone is retained: {buffer}"
3022 );
3023
3024 drop(clone);
3025 let buffer = context.encode();
3026 assert!(
3027 !buffer.contains("engine_votes"),
3028 "metric should be removed when all handle clones are dropped: {buffer}"
3029 );
3030 });
3031 }
3032
3033 #[test]
3034 fn test_deterministic_register_clone_keeps_metric_alive() {
3035 let executor = deterministic::Runner::default();
3036 test_register_clone_keeps_metric_alive(executor);
3037 }
3038
3039 #[test]
3040 fn test_tokio_register_clone_keeps_metric_alive() {
3041 let runner = tokio::Runner::default();
3042 test_register_clone_keeps_metric_alive(runner);
3043 }
3044
3045 fn test_encode_single_eof<R: Runner>(runner: R)
3046 where
3047 R::Context: Metrics,
3048 {
3049 runner.start(|context| async move {
3050 let root_counter = context.register("root", "root metric", Counter::<u64>::default());
3051 root_counter.inc();
3052
3053 let child =
3054 context
3055 .child("engine")
3056 .register("ops", "child metric", Counter::<u64>::default());
3057 child.inc();
3058
3059 let buffer = context.encode();
3060 assert!(
3061 buffer.contains("root_total 1"),
3062 "root metric missing: {buffer}"
3063 );
3064 assert!(
3065 buffer.contains("engine_ops_total 1"),
3066 "child metric missing: {buffer}"
3067 );
3068 assert_eq!(
3069 buffer.matches("# EOF").count(),
3070 1,
3071 "expected exactly one EOF marker: {buffer}"
3072 );
3073 assert!(
3074 buffer.ends_with("# EOF\n"),
3075 "EOF must be the last line: {buffer}"
3076 );
3077 });
3078 }
3079
3080 #[test]
3081 fn test_deterministic_encode_single_eof() {
3082 let executor = deterministic::Runner::default();
3083 test_encode_single_eof(executor);
3084 }
3085
3086 #[test]
3087 fn test_tokio_encode_single_eof() {
3088 let runner = tokio::Runner::default();
3089 test_encode_single_eof(runner);
3090 }
3091
3092 fn test_family_with_attributes<R: Runner>(runner: R)
3093 where
3094 R::Context: Metrics,
3095 {
3096 #[derive(Clone, Debug, Hash, PartialEq, Eq)]
3097 struct Peer {
3098 name: String,
3099 }
3100 impl EncodeLabelSet for Peer {
3101 fn encode(&self, encoder: &mut LabelSetEncoder<'_>) -> Result<(), std::fmt::Error> {
3102 let mut label = encoder.encode_label();
3103 let mut key = label.encode_label_key()?;
3104 EncodeLabelKey::encode(&"peer", &mut key)?;
3105 let mut value = key.encode_label_value()?;
3106 EncodeLabelValue::encode(&self.name.as_str(), &mut value)?;
3107 value.finish()
3108 }
3109 }
3110
3111 runner.start(|context| async move {
3112 let family = context
3113 .child("batcher")
3114 .with_attribute("epoch", 1)
3115 .register(
3116 "votes",
3117 "votes per peer",
3118 Family::<Peer, Counter>::default(),
3119 );
3120 family
3121 .get_or_create(&Peer {
3122 name: "alice".into(),
3123 })
3124 .inc();
3125 family.get_or_create(&Peer { name: "bob".into() }).inc();
3126
3127 let buffer = context.encode();
3128 assert!(
3129 buffer.contains("batcher_votes_total{epoch=\"1\",peer=\"alice\"} 1"),
3130 "family with attributes should combine labels: {buffer}"
3131 );
3132 assert!(
3133 buffer.contains("batcher_votes_total{epoch=\"1\",peer=\"bob\"} 1"),
3134 "family with attributes should combine labels: {buffer}"
3135 );
3136
3137 drop(family);
3138 let buffer = context.encode();
3139 assert!(
3140 !buffer.contains("batcher_votes"),
3141 "family metrics should be removed: {buffer}"
3142 );
3143 });
3144 }
3145
3146 #[test]
3147 fn test_deterministic_family_with_attributes() {
3148 let executor = deterministic::Runner::default();
3149 test_family_with_attributes(executor);
3150 }
3151
3152 #[test]
3153 fn test_tokio_family_with_attributes() {
3154 let runner = tokio::Runner::default();
3155 test_family_with_attributes(runner);
3156 }
3157
3158 #[test]
3159 fn test_deterministic_future() {
3160 let runner = deterministic::Runner::default();
3161 test_error_future(runner);
3162 }
3163
3164 #[test]
3165 fn test_deterministic_clock_sleep() {
3166 let executor = deterministic::Runner::default();
3167 test_clock_sleep(executor);
3168 }
3169
3170 #[test]
3171 fn test_deterministic_clock_sleep_until() {
3172 let executor = deterministic::Runner::default();
3173 test_clock_sleep_until(executor);
3174 }
3175
3176 #[test]
3177 fn test_deterministic_clock_sleep_until_far_future() {
3178 let executor = deterministic::Runner::default();
3179 test_clock_sleep_until_far_future(executor);
3180 }
3181
3182 #[test]
3183 fn test_deterministic_clock_timeout() {
3184 let executor = deterministic::Runner::default();
3185 test_clock_timeout(executor);
3186 }
3187
3188 #[test]
3189 fn test_deterministic_root_finishes() {
3190 let executor = deterministic::Runner::default();
3191 test_root_finishes(executor);
3192 }
3193
3194 #[test]
3195 fn test_deterministic_spawn_after_abort() {
3196 let executor = deterministic::Runner::default();
3197 test_spawn_after_abort(executor);
3198 }
3199
3200 #[test]
3201 fn test_deterministic_spawn_abort() {
3202 let executor = deterministic::Runner::default();
3203 test_spawn_abort(executor, false, false);
3204 }
3205
3206 #[test]
3207 #[should_panic(expected = "blah")]
3208 fn test_deterministic_panic_aborts_root() {
3209 let runner = deterministic::Runner::default();
3210 test_panic_aborts_root(runner);
3211 }
3212
3213 #[test]
3214 #[should_panic(expected = "blah")]
3215 fn test_deterministic_panic_aborts_root_caught() {
3216 let cfg = deterministic::Config::default().with_catch_panics(true);
3217 let runner = deterministic::Runner::new(cfg);
3218 test_panic_aborts_root(runner);
3219 }
3220
3221 #[test]
3222 #[should_panic(expected = "blah")]
3223 fn test_deterministic_panic_aborts_spawn() {
3224 let executor = deterministic::Runner::default();
3225 test_panic_aborts_spawn(executor);
3226 }
3227
3228 #[test]
3229 fn test_deterministic_panic_aborts_spawn_caught() {
3230 let cfg = deterministic::Config::default().with_catch_panics(true);
3231 let executor = deterministic::Runner::new(cfg);
3232 test_panic_aborts_spawn_caught(executor);
3233 }
3234
3235 #[test]
3236 #[should_panic(expected = "boom")]
3237 fn test_deterministic_multiple_panics() {
3238 let executor = deterministic::Runner::default();
3239 test_multiple_panics(executor);
3240 }
3241
3242 #[test]
3243 fn test_deterministic_multiple_panics_caught() {
3244 let cfg = deterministic::Config::default().with_catch_panics(true);
3245 let executor = deterministic::Runner::new(cfg);
3246 test_multiple_panics_caught(executor);
3247 }
3248
3249 #[test]
3250 fn test_deterministic_select() {
3251 let executor = deterministic::Runner::default();
3252 test_select(executor);
3253 }
3254
3255 #[test]
3256 fn test_deterministic_select_loop() {
3257 let executor = deterministic::Runner::default();
3258 test_select_loop(executor);
3259 }
3260
3261 #[test]
3262 fn test_deterministic_storage_operations() {
3263 let executor = deterministic::Runner::default();
3264 test_storage_operations(executor);
3265 }
3266
3267 #[test]
3268 fn test_deterministic_blob_read_write() {
3269 let executor = deterministic::Runner::default();
3270 test_blob_read_write(executor);
3271 }
3272
3273 #[test]
3274 fn test_deterministic_blob_resize() {
3275 let executor = deterministic::Runner::default();
3276 test_blob_resize(executor);
3277 }
3278
3279 #[test]
3280 fn test_deterministic_many_partition_read_write() {
3281 let executor = deterministic::Runner::default();
3282 test_many_partition_read_write(executor);
3283 }
3284
3285 #[test]
3286 fn test_deterministic_blob_read_past_length() {
3287 let executor = deterministic::Runner::default();
3288 test_blob_read_past_length(executor);
3289 }
3290
3291 #[test]
3292 fn test_deterministic_blob_clone_and_concurrent_read() {
3293 let executor = deterministic::Runner::default();
3295 test_blob_clone_and_concurrent_read(executor);
3296 }
3297
3298 #[test]
3299 fn test_deterministic_shutdown() {
3300 let executor = deterministic::Runner::default();
3301 test_shutdown(executor);
3302 }
3303
3304 #[test]
3305 fn test_deterministic_shutdown_multiple_signals() {
3306 let executor = deterministic::Runner::default();
3307 test_shutdown_multiple_signals(executor);
3308 }
3309
3310 #[test]
3311 fn test_deterministic_shutdown_timeout() {
3312 let executor = deterministic::Runner::default();
3313 test_shutdown_timeout(executor);
3314 }
3315
3316 #[test]
3317 fn test_deterministic_shutdown_multiple_stop_calls() {
3318 let executor = deterministic::Runner::default();
3319 test_shutdown_multiple_stop_calls(executor);
3320 }
3321
3322 #[test]
3323 fn test_deterministic_unfulfilled_shutdown() {
3324 let executor = deterministic::Runner::default();
3325 test_unfulfilled_shutdown(executor);
3326 }
3327
3328 #[test]
3329 fn test_deterministic_spawn_dedicated() {
3330 let executor = deterministic::Runner::default();
3331 test_spawn_dedicated(executor);
3332 }
3333
3334 #[test]
3335 fn test_deterministic_spawn() {
3336 let runner = deterministic::Runner::default();
3337 test_spawn(runner);
3338 }
3339
3340 #[test]
3341 fn test_deterministic_spawn_abort_on_parent_abort() {
3342 let runner = deterministic::Runner::default();
3343 test_spawn_abort_on_parent_abort(runner);
3344 }
3345
3346 #[test]
3347 fn test_deterministic_spawn_abort_on_parent_completion() {
3348 let runner = deterministic::Runner::default();
3349 test_spawn_abort_on_parent_completion(runner);
3350 }
3351
3352 #[test]
3353 fn test_deterministic_spawn_cascading_abort() {
3354 let runner = deterministic::Runner::default();
3355 test_spawn_cascading_abort(runner);
3356 }
3357
3358 #[test]
3359 fn test_deterministic_child_survives_sibling_completion() {
3360 let runner = deterministic::Runner::default();
3361 test_child_survives_sibling_completion(runner);
3362 }
3363
3364 #[test]
3365 fn test_deterministic_spawn_clone_chain() {
3366 let runner = deterministic::Runner::default();
3367 test_spawn_clone_chain(runner);
3368 }
3369
3370 #[test]
3371 fn test_deterministic_spawn_sparse_clone_chain() {
3372 let runner = deterministic::Runner::default();
3373 test_spawn_sparse_clone_chain(runner);
3374 }
3375
3376 #[test]
3377 fn test_deterministic_spawn_blocking() {
3378 for dedicated in [false, true] {
3379 let executor = deterministic::Runner::default();
3380 test_spawn_blocking(executor, dedicated);
3381 }
3382 }
3383
3384 #[test]
3385 #[should_panic(expected = "blocking task panicked")]
3386 fn test_deterministic_spawn_blocking_panic() {
3387 for dedicated in [false, true] {
3388 let executor = deterministic::Runner::default();
3389 test_spawn_blocking_panic(executor, dedicated);
3390 }
3391 }
3392
3393 #[test]
3394 fn test_deterministic_spawn_blocking_panic_caught() {
3395 for dedicated in [false, true] {
3396 let cfg = deterministic::Config::default().with_catch_panics(true);
3397 let executor = deterministic::Runner::new(cfg);
3398 test_spawn_blocking_panic_caught(executor, dedicated);
3399 }
3400 }
3401
3402 #[test]
3403 fn test_deterministic_spawn_blocking_abort() {
3404 for (dedicated, blocking) in [(false, true), (true, false)] {
3405 let executor = deterministic::Runner::default();
3406 test_spawn_abort(executor, dedicated, blocking);
3407 }
3408 }
3409
3410 #[test]
3411 fn test_deterministic_circular_reference_prevents_cleanup() {
3412 let executor = deterministic::Runner::default();
3413 test_circular_reference_prevents_cleanup(executor);
3414 }
3415
3416 #[test]
3417 fn test_deterministic_late_waker() {
3418 let executor = deterministic::Runner::default();
3419 test_late_waker(executor);
3420 }
3421
3422 #[test]
3423 fn test_deterministic_metrics() {
3424 let executor = deterministic::Runner::default();
3425 test_metrics(executor);
3426 }
3427
3428 #[test]
3429 fn test_deterministic_resolver() {
3430 let executor = deterministic::Runner::default();
3431 executor.start(|context| async move {
3432 let ip1: IpAddr = "192.168.1.1".parse().unwrap();
3434 let ip2: IpAddr = "192.168.1.2".parse().unwrap();
3435 context.resolver_register("example.com", Some(vec![ip1, ip2]));
3436
3437 let addrs = context.resolve("example.com").await.unwrap();
3439 assert_eq!(addrs, vec![ip1, ip2]);
3440
3441 let result = context.resolve("unknown.com").await;
3443 assert!(matches!(result, Err(Error::ResolveFailed(_))));
3444
3445 context.resolver_register("example.com", None);
3447 let result = context.resolve("example.com").await;
3448 assert!(matches!(result, Err(Error::ResolveFailed(_))));
3449 });
3450 }
3451
3452 #[test]
3453 fn test_tokio_error_future() {
3454 let runner = tokio::Runner::default();
3455 test_error_future(runner);
3456 }
3457
3458 #[test]
3459 fn test_tokio_clock_sleep() {
3460 let executor = tokio::Runner::default();
3461 test_clock_sleep(executor);
3462 }
3463
3464 #[test]
3465 fn test_tokio_clock_sleep_until() {
3466 let executor = tokio::Runner::default();
3467 test_clock_sleep_until(executor);
3468 }
3469
3470 #[test]
3471 fn test_tokio_clock_sleep_until_far_future() {
3472 let executor = tokio::Runner::default();
3473 test_clock_sleep_until_far_future(executor);
3474 }
3475
3476 #[test]
3477 fn test_tokio_clock_timeout() {
3478 let executor = tokio::Runner::default();
3479 test_clock_timeout(executor);
3480 }
3481
3482 #[test]
3483 fn test_tokio_root_finishes() {
3484 let executor = tokio::Runner::default();
3485 test_root_finishes(executor);
3486 }
3487
3488 #[test]
3489 fn test_tokio_spawn_after_abort() {
3490 let executor = tokio::Runner::default();
3491 test_spawn_after_abort(executor);
3492 }
3493
3494 #[test]
3495 fn test_tokio_spawn_abort() {
3496 let executor = tokio::Runner::default();
3497 test_spawn_abort(executor, false, false);
3498 }
3499
3500 #[test]
3501 #[should_panic(expected = "blah")]
3502 fn test_tokio_panic_aborts_root() {
3503 let executor = tokio::Runner::default();
3504 test_panic_aborts_root(executor);
3505 }
3506
3507 #[test]
3508 #[should_panic(expected = "blah")]
3509 fn test_tokio_panic_aborts_root_caught() {
3510 let cfg = tokio::Config::default().with_catch_panics(true);
3511 let executor = tokio::Runner::new(cfg);
3512 test_panic_aborts_root(executor);
3513 }
3514
3515 #[test]
3516 #[should_panic(expected = "blah")]
3517 fn test_tokio_panic_aborts_spawn() {
3518 let executor = tokio::Runner::default();
3519 test_panic_aborts_spawn(executor);
3520 }
3521
3522 #[test]
3523 fn test_tokio_panic_aborts_spawn_caught() {
3524 let cfg = tokio::Config::default().with_catch_panics(true);
3525 let executor = tokio::Runner::new(cfg);
3526 test_panic_aborts_spawn_caught(executor);
3527 }
3528
3529 #[test]
3530 #[should_panic(expected = "boom")]
3531 fn test_tokio_multiple_panics() {
3532 let executor = tokio::Runner::default();
3533 test_multiple_panics(executor);
3534 }
3535
3536 #[test]
3537 fn test_tokio_multiple_panics_caught() {
3538 let cfg = tokio::Config::default().with_catch_panics(true);
3539 let executor = tokio::Runner::new(cfg);
3540 test_multiple_panics_caught(executor);
3541 }
3542
3543 #[test]
3544 fn test_tokio_select() {
3545 let executor = tokio::Runner::default();
3546 test_select(executor);
3547 }
3548
3549 #[test]
3550 fn test_tokio_select_loop() {
3551 let executor = tokio::Runner::default();
3552 test_select_loop(executor);
3553 }
3554
3555 #[test]
3556 fn test_tokio_storage_operations() {
3557 let executor = tokio::Runner::default();
3558 test_storage_operations(executor);
3559 }
3560
3561 #[test]
3562 fn test_tokio_blob_read_write() {
3563 let executor = tokio::Runner::default();
3564 test_blob_read_write(executor);
3565 }
3566
3567 #[test]
3568 fn test_tokio_blob_resize() {
3569 let executor = tokio::Runner::default();
3570 test_blob_resize(executor);
3571 }
3572
3573 #[test]
3574 fn test_tokio_many_partition_read_write() {
3575 let executor = tokio::Runner::default();
3576 test_many_partition_read_write(executor);
3577 }
3578
3579 #[test]
3580 fn test_tokio_blob_read_past_length() {
3581 let executor = tokio::Runner::default();
3582 test_blob_read_past_length(executor);
3583 }
3584
3585 #[test]
3586 fn test_tokio_blob_clone_and_concurrent_read() {
3587 let executor = tokio::Runner::default();
3589 test_blob_clone_and_concurrent_read(executor);
3590 }
3591
3592 #[test]
3593 fn test_tokio_shutdown() {
3594 let executor = tokio::Runner::default();
3595 test_shutdown(executor);
3596 }
3597
3598 #[test]
3599 fn test_tokio_shutdown_multiple_signals() {
3600 let executor = tokio::Runner::default();
3601 test_shutdown_multiple_signals(executor);
3602 }
3603
3604 #[test]
3605 fn test_tokio_shutdown_timeout() {
3606 let executor = tokio::Runner::default();
3607 test_shutdown_timeout(executor);
3608 }
3609
3610 #[test]
3611 fn test_tokio_shutdown_multiple_stop_calls() {
3612 let executor = tokio::Runner::default();
3613 test_shutdown_multiple_stop_calls(executor);
3614 }
3615
3616 #[test]
3617 fn test_tokio_unfulfilled_shutdown() {
3618 let executor = tokio::Runner::default();
3619 test_unfulfilled_shutdown(executor);
3620 }
3621
3622 #[test]
3623 fn test_tokio_spawn_dedicated() {
3624 let executor = tokio::Runner::default();
3625 test_spawn_dedicated(executor);
3626 }
3627
3628 #[test]
3629 fn test_tokio_spawn() {
3630 let runner = tokio::Runner::default();
3631 test_spawn(runner);
3632 }
3633
3634 #[test]
3635 fn test_tokio_spawn_abort_on_parent_abort() {
3636 let runner = tokio::Runner::default();
3637 test_spawn_abort_on_parent_abort(runner);
3638 }
3639
3640 #[test]
3641 fn test_tokio_spawn_abort_on_parent_completion() {
3642 let runner = tokio::Runner::default();
3643 test_spawn_abort_on_parent_completion(runner);
3644 }
3645
3646 #[test]
3647 fn test_tokio_spawn_cascading_abort() {
3648 let runner = tokio::Runner::default();
3649 test_spawn_cascading_abort(runner);
3650 }
3651
3652 #[test]
3653 fn test_tokio_child_survives_sibling_completion() {
3654 let runner = tokio::Runner::default();
3655 test_child_survives_sibling_completion(runner);
3656 }
3657
3658 #[test]
3659 fn test_tokio_spawn_clone_chain() {
3660 let runner = tokio::Runner::default();
3661 test_spawn_clone_chain(runner);
3662 }
3663
3664 #[test]
3665 fn test_tokio_spawn_sparse_clone_chain() {
3666 let runner = tokio::Runner::default();
3667 test_spawn_sparse_clone_chain(runner);
3668 }
3669
3670 #[test]
3671 fn test_tokio_spawn_blocking() {
3672 for dedicated in [false, true] {
3673 let executor = tokio::Runner::default();
3674 test_spawn_blocking(executor, dedicated);
3675 }
3676 }
3677
3678 #[test]
3679 #[should_panic(expected = "blocking task panicked")]
3680 fn test_tokio_spawn_blocking_panic() {
3681 for dedicated in [false, true] {
3682 let executor = tokio::Runner::default();
3683 test_spawn_blocking_panic(executor, dedicated);
3684 }
3685 }
3686
3687 #[test]
3688 fn test_tokio_spawn_blocking_panic_caught() {
3689 for dedicated in [false, true] {
3690 let cfg = tokio::Config::default().with_catch_panics(true);
3691 let executor = tokio::Runner::new(cfg);
3692 test_spawn_blocking_panic_caught(executor, dedicated);
3693 }
3694 }
3695
3696 #[test]
3697 fn test_tokio_spawn_blocking_abort() {
3698 for (dedicated, blocking) in [(false, true), (true, false)] {
3699 let executor = tokio::Runner::default();
3700 test_spawn_abort(executor, dedicated, blocking);
3701 }
3702 }
3703
3704 #[test]
3705 fn test_tokio_circular_reference_prevents_cleanup() {
3706 let executor = tokio::Runner::default();
3707 test_circular_reference_prevents_cleanup(executor);
3708 }
3709
3710 #[test]
3711 fn test_tokio_late_waker() {
3712 let executor = tokio::Runner::default();
3713 test_late_waker(executor);
3714 }
3715
3716 #[test]
3717 fn test_tokio_metrics() {
3718 let executor = tokio::Runner::default();
3719 test_metrics(executor);
3720 }
3721
3722 #[test]
3723 fn test_tokio_process_rss_metric() {
3724 let executor = tokio::Runner::default();
3725 executor.start(|context| async move {
3726 loop {
3727 let metrics = context.encode();
3729 if !metrics.contains("runtime_process_rss") {
3730 context.sleep(Duration::from_millis(100)).await;
3731 continue;
3732 }
3733
3734 for line in metrics.lines() {
3736 if line.starts_with("runtime_process_rss")
3737 && !line.starts_with("runtime_process_rss{")
3738 {
3739 let parts: Vec<&str> = line.split_whitespace().collect();
3740 if parts.len() >= 2 {
3741 let rss_value: i64 =
3742 parts[1].parse().expect("Failed to parse RSS value");
3743 if rss_value > 0 {
3744 return;
3745 }
3746 }
3747 }
3748 }
3749 }
3750 });
3751 }
3752
3753 #[test]
3754 fn test_tokio_telemetry() {
3755 let executor = tokio::Runner::default();
3756 executor.start(|context| async move {
3757 let address = SocketAddr::from_str("127.0.0.1:8000").unwrap();
3759
3760 tokio::telemetry::init(
3762 context.child("metrics"),
3763 tokio::telemetry::Logs {
3764 level: Level::INFO,
3765 json: false,
3766 },
3767 Some(address),
3768 None,
3769 );
3770
3771 let counter: Counter<u64> = Counter::default();
3773 let _registered = context.register("test_counter", "Test counter", counter.clone());
3774 counter.inc();
3775
3776 async fn read_line<St: Stream>(stream: &mut St) -> Result<String, Error> {
3778 let mut line = Vec::new();
3779 loop {
3780 let received = stream.recv(1).await?;
3781 let byte = received.coalesce().as_ref()[0];
3782 if byte == b'\n' {
3783 if line.last() == Some(&b'\r') {
3784 line.pop(); }
3786 break;
3787 }
3788 line.push(byte);
3789 }
3790 String::from_utf8(line).map_err(|_| Error::ReadFailed)
3791 }
3792
3793 async fn read_headers<St: Stream>(
3794 stream: &mut St,
3795 ) -> Result<HashMap<String, String>, Error> {
3796 let mut headers = HashMap::new();
3797 loop {
3798 let line = read_line(stream).await?;
3799 if line.is_empty() {
3800 break;
3801 }
3802 let parts: Vec<&str> = line.splitn(2, ": ").collect();
3803 if parts.len() == 2 {
3804 headers.insert(parts[0].to_string(), parts[1].to_string());
3805 }
3806 }
3807 Ok(headers)
3808 }
3809
3810 async fn read_body<St: Stream>(
3811 stream: &mut St,
3812 content_length: usize,
3813 ) -> Result<String, Error> {
3814 let received = stream.recv(content_length).await?;
3815 String::from_utf8(received.coalesce().into()).map_err(|_| Error::ReadFailed)
3816 }
3817
3818 let client_handle = context.child("client").spawn(move |context| async move {
3820 let (mut sink, mut stream) = loop {
3821 match context.dial(address).await {
3822 Ok((sink, stream)) => break (sink, stream),
3823 Err(e) => {
3824 error!(err =?e, "failed to connect");
3826 context.sleep(Duration::from_millis(10)).await;
3827 }
3828 }
3829 };
3830
3831 let request = format!(
3833 "GET /metrics HTTP/1.1\r\nHost: {address}\r\nConnection: close\r\n\r\n"
3834 );
3835 sink.send(Bytes::from(request)).await.unwrap();
3836
3837 let status_line = read_line(&mut stream).await.unwrap();
3839 assert_eq!(status_line, "HTTP/1.1 200 OK");
3840
3841 let headers = read_headers(&mut stream).await.unwrap();
3843 println!("Headers: {headers:?}");
3844 let content_length = headers
3845 .get("content-length")
3846 .unwrap()
3847 .parse::<usize>()
3848 .unwrap();
3849
3850 let body = read_body(&mut stream, content_length).await.unwrap();
3852 assert!(body.contains("test_counter_total 1"));
3853 });
3854
3855 client_handle.await.unwrap();
3857 });
3858 }
3859
3860 #[test]
3861 fn test_tokio_resolver() {
3862 let executor = tokio::Runner::default();
3863 executor.start(|context| async move {
3864 let addrs = context.resolve("localhost").await.unwrap();
3865 assert!(!addrs.is_empty());
3866 for addr in addrs {
3867 assert!(
3868 addr == IpAddr::V4(Ipv4Addr::LOCALHOST)
3869 || addr == IpAddr::V6(Ipv6Addr::LOCALHOST)
3870 );
3871 }
3872 });
3873 }
3874
3875 #[test]
3876 fn test_strategy_tokio() {
3877 let executor = tokio::Runner::default();
3878 executor.start(|context| async move {
3879 let strategy = context.child("pool").strategy(NZUsize!(4));
3881 assert_eq!(strategy.manual().parallelism(), 4);
3882
3883 let sum = strategy.fold(0..10000, || 0i32, |acc, n| acc + n, |a, b| a + b);
3885 assert_eq!(sum, 10000 * 9999 / 2);
3886 });
3887 }
3888
3889 #[test]
3890 fn test_strategy_deterministic() {
3891 let executor = deterministic::Runner::default();
3892 executor.start(|context| async move {
3893 let strategy = context.child("pool").strategy(NZUsize!(4));
3895 assert_eq!(strategy.manual().parallelism(), 4);
3896
3897 let sum = strategy.fold(0..10000, || 0i32, |acc, n| acc + n, |a, b| a + b);
3899 assert_eq!(sum, 10000 * 9999 / 2);
3900 });
3901 }
3902
3903 #[test]
3904 fn test_deterministic_nested_strategy_runs_inline() {
3905 let executor = deterministic::Runner::default();
3906 executor.start(|context| async move {
3907 let strategy = context.child("pool").strategy(NZUsize!(1)).manual();
3908
3909 let output = strategy
3910 .spawn(|strategy| strategy.map_collect_vec(0..2, |i| i + 1))
3911 .await;
3912
3913 assert_eq!(output, vec![1, 2]);
3914 });
3915 }
3916
3917 #[test]
3920 fn test_deterministic_parallel_strategy_spawn_completes() {
3921 let executor = deterministic::Runner::default();
3922 executor.start(|context| async move {
3923 let strategy = context.child("pool").strategy(NZUsize!(2)).manual();
3924 assert_eq!(strategy.parallelism(), 2);
3925
3926 let output = strategy
3927 .spawn(|strategy| strategy.map_collect_vec(0..2, |i| i + 1))
3928 .await;
3929
3930 assert_eq!(output, vec![1, 2]);
3931 });
3932 }
3933
3934 #[test]
3938 fn test_deterministic_strategies_reuse_pool_across_runners() {
3939 let executor = deterministic::Runner::default();
3940 executor.start(|context| async move {
3941 let first = context.child("pool_a").strategy(NZUsize!(1)).manual();
3942 assert_eq!(first.parallelism(), 1);
3943 assert_eq!(first.run(2, || "serial", || "parallel"), "serial");
3944 let output = first
3945 .spawn(|strategy| strategy.map_collect_vec(0..2, |i| i + 1))
3946 .now_or_never()
3947 .expect("single-threaded pool should run spawned work inline");
3948 assert_eq!(output, vec![1, 2]);
3949
3950 let second = context.child("pool_b").strategy(NZUsize!(3)).manual();
3951 assert_eq!(second.parallelism(), 3);
3952 assert_eq!(second.run(2, || "serial", || "parallel"), "parallel");
3953 let output = second
3954 .spawn(|strategy| strategy.map_collect_vec(0..3, |i| i + 1))
3955 .now_or_never()
3956 .expect("single-threaded pool should run spawned work inline");
3957 assert_eq!(output, vec![1, 2, 3]);
3958 });
3959
3960 let executor = deterministic::Runner::default();
3961 executor.start(|context| async move {
3962 let third = context.child("pool_c").strategy(NZUsize!(4)).manual();
3963 assert_eq!(third.parallelism(), 4);
3964 assert_eq!(third.run(2, || "serial", || "parallel"), "parallel");
3965 let output = third
3966 .spawn(|strategy| strategy.map_collect_vec(0..4, |i| i + 1))
3967 .now_or_never()
3968 .expect("single-threaded pool should run spawned work inline");
3969 assert_eq!(output, vec![1, 2, 3, 4]);
3970 });
3971 }
3972
3973 #[test]
3977 fn test_deterministic_pool_survives_suspension() {
3978 let executor = deterministic::Runner::default();
3979 executor.start(|context| async move {
3980 let strategy = context.child("pool").strategy(NZUsize!(2)).manual();
3981 context.sleep(Duration::from_millis(10)).await;
3982
3983 let output = strategy
3984 .spawn(|strategy| strategy.map_collect_vec(0..2, |i| i + 1))
3985 .await;
3986 assert_eq!(output, vec![1, 2]);
3987
3988 context.sleep(Duration::from_millis(10)).await;
3989 let sum = strategy.fold(0..100u64, || 0u64, |acc, i| acc + i, |a, b| a + b);
3990 assert_eq!(sum, 4950);
3991 });
3992 }
3993
3994 #[test]
3995 fn test_tokio_nested_strategy_runs_inline() {
3996 let executor = tokio::Runner::default();
3997 executor.start(|context| async move {
3998 let strategy = context.child("pool").strategy(NZUsize!(1)).manual();
3999
4000 let output = strategy
4001 .spawn(|strategy| strategy.map_collect_vec(0..2, |i| i + 1))
4002 .await;
4003
4004 assert_eq!(output, vec![1, 2]);
4005 });
4006 }
4007
4008 fn test_buffer_pooler<R: Runner>(
4009 runner: R,
4010 expected_network_max_per_class: u32,
4011 expected_storage_max_per_class: u32,
4012 ) where
4013 R::Context: BufferPooler,
4014 {
4015 runner.start(|context| async move {
4016 let net_buf = context.network_buffer_pool().try_alloc(1024).unwrap();
4018 assert!(net_buf.capacity() >= 1024);
4019
4020 let storage_buf = context.storage_buffer_pool().try_alloc(1024).unwrap();
4022 assert!(storage_buf.capacity() >= 4096);
4023
4024 assert_eq!(
4026 context.network_buffer_pool().config().max_per_class.get(),
4027 expected_network_max_per_class
4028 );
4029 assert_eq!(
4030 context.storage_buffer_pool().config().max_per_class.get(),
4031 expected_storage_max_per_class
4032 );
4033 });
4034 }
4035
4036 #[test]
4037 fn test_deterministic_buffer_pooler() {
4038 test_buffer_pooler(deterministic::Runner::default(), 4096, 64);
4039
4040 let runner = deterministic::Runner::new(
4041 deterministic::Config::default()
4042 .with_network_buffer_pool_config(
4043 BufferPoolConfig::for_network().with_max_per_class(NZU32!(64)),
4044 )
4045 .with_storage_buffer_pool_config(
4046 BufferPoolConfig::for_storage().with_max_per_class(NZU32!(8)),
4047 ),
4048 );
4049 test_buffer_pooler(runner, 64, 8);
4050 }
4051
4052 #[test]
4053 fn test_tokio_buffer_pooler() {
4054 test_buffer_pooler(tokio::Runner::default(), 4096, 64);
4055
4056 let runner = tokio::Runner::new(
4057 tokio::Config::default()
4058 .with_network_buffer_pool_config(
4059 BufferPoolConfig::for_network().with_max_per_class(NZU32!(64)),
4060 )
4061 .with_storage_buffer_pool_config(
4062 BufferPoolConfig::for_storage().with_max_per_class(NZU32!(8)),
4063 ),
4064 );
4065 test_buffer_pooler(runner, 64, 8);
4066 }
4067}