Added some tests at least for the broadcast buffering.
This commit is contained in:
126
Cargo.lock
generated
126
Cargo.lock
generated
@@ -17,6 +17,15 @@ version = "2.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "android_system_properties"
|
name = "android_system_properties"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
@@ -172,6 +181,12 @@ version = "1.10.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cargo-husky"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b02b629252fe8ef6460461409564e2c21d0c8e77e0944f3d189ff06c4e932ad"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.43"
|
version = "1.2.43"
|
||||||
@@ -858,6 +873,12 @@ version = "0.32.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
|
checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glob"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.12.3"
|
version = "0.12.3"
|
||||||
@@ -1666,6 +1687,15 @@ dependencies = [
|
|||||||
"zerocopy",
|
"zerocopy",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-crate"
|
||||||
|
version = "3.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983"
|
||||||
|
dependencies = [
|
||||||
|
"toml_edit 0.23.7",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.103"
|
version = "1.0.103"
|
||||||
@@ -1823,6 +1853,41 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.12.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "relative-path"
|
||||||
|
version = "1.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest"
|
name = "reqwest"
|
||||||
version = "0.12.24"
|
version = "0.12.24"
|
||||||
@@ -1899,6 +1964,7 @@ name = "robotnik"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"better-panic",
|
"better-panic",
|
||||||
|
"cargo-husky",
|
||||||
"clap",
|
"clap",
|
||||||
"color-eyre",
|
"color-eyre",
|
||||||
"config",
|
"config",
|
||||||
@@ -1907,6 +1973,7 @@ dependencies = [
|
|||||||
"genai",
|
"genai",
|
||||||
"human-panic",
|
"human-panic",
|
||||||
"irc",
|
"irc",
|
||||||
|
"rstest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
@@ -1926,6 +1993,36 @@ dependencies = [
|
|||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rstest"
|
||||||
|
version = "0.24.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "03e905296805ab93e13c1ec3a03f4b6c4f35e9498a3d5fa96dc626d22c03cd89"
|
||||||
|
dependencies = [
|
||||||
|
"futures-timer",
|
||||||
|
"futures-util",
|
||||||
|
"rstest_macros",
|
||||||
|
"rustc_version",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rstest_macros"
|
||||||
|
version = "0.24.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ef0053bbffce09062bee4bcc499b0fbe7a57b879f1efe088d6d8d4c7adcdef9b"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"glob",
|
||||||
|
"proc-macro-crate",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex",
|
||||||
|
"relative-path",
|
||||||
|
"rustc_version",
|
||||||
|
"syn",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-ini"
|
name = "rust-ini"
|
||||||
version = "0.21.3"
|
version = "0.21.3"
|
||||||
@@ -1948,6 +2045,15 @@ version = "2.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
|
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_version"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
||||||
|
dependencies = [
|
||||||
|
"semver",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "1.1.2"
|
version = "1.1.2"
|
||||||
@@ -2070,6 +2176,12 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "1.0.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.228"
|
version = "1.0.228"
|
||||||
@@ -2489,7 +2601,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_spanned 0.6.9",
|
"serde_spanned 0.6.9",
|
||||||
"toml_datetime 0.6.11",
|
"toml_datetime 0.6.11",
|
||||||
"toml_edit",
|
"toml_edit 0.19.15",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2537,6 +2649,18 @@ dependencies = [
|
|||||||
"winnow 0.5.40",
|
"winnow 0.5.40",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_edit"
|
||||||
|
version = "0.23.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap 2.12.0",
|
||||||
|
"toml_datetime 0.7.3",
|
||||||
|
"toml_parser",
|
||||||
|
"winnow 0.7.13",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_parser"
|
name = "toml_parser"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
|
|||||||
36
Cargo.toml
36
Cargo.toml
@@ -4,26 +4,46 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# TODO: make this a dev and/or debug dependency later.
|
|
||||||
better-panic = "0.3.0"
|
better-panic = "0.3.0"
|
||||||
clap = { version = "4.5", features = [ "derive" ] }
|
|
||||||
color-eyre = "0.6.3"
|
color-eyre = "0.6.3"
|
||||||
config = { version = "0.15", features = [ "toml" ] }
|
|
||||||
directories = "6.0"
|
directories = "6.0"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
human-panic = "2.0"
|
human-panic = "2.0"
|
||||||
genai = "0.4.3"
|
genai = "0.4.3"
|
||||||
irc = "1.1"
|
irc = "1.1"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
tokio = { version = "1", features = [ "io-util", "macros", "net", "rt-multi-thread", "sync" ] }
|
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tracing-subscriber = "0.3"
|
tracing-subscriber = "0.3"
|
||||||
|
|
||||||
|
[dependencies.clap]
|
||||||
|
version = "4.5"
|
||||||
|
features = [ "derive" ]
|
||||||
|
|
||||||
|
[dependencies.config]
|
||||||
|
version = "0.15"
|
||||||
|
features = [ "toml" ]
|
||||||
|
|
||||||
|
[dependencies.serde]
|
||||||
|
version = "1.0"
|
||||||
|
features = [ "derive" ]
|
||||||
|
|
||||||
|
[dependencies.tokio]
|
||||||
|
version = "1"
|
||||||
|
features = [ "io-util", "macros", "net", "rt-multi-thread", "sync" ]
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
rstest = "0.24"
|
||||||
|
|
||||||
|
[dev-dependencies.cargo-husky]
|
||||||
|
version = "1"
|
||||||
|
features = [
|
||||||
|
"run-cargo-check",
|
||||||
|
"run-cargo-clippy",
|
||||||
|
]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
strip = true
|
strip = true
|
||||||
opt-level = "z" # Optimize for size
|
opt-level = "z"
|
||||||
lto = true # Link-time optimization
|
lto = true
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
# Comment this if unwinding is needed. Compiling without release works too.
|
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use tokio::{
|
|||||||
net::{UnixListener, UnixStream},
|
net::{UnixListener, UnixStream},
|
||||||
sync::{RwLock, broadcast},
|
sync::{RwLock, broadcast},
|
||||||
};
|
};
|
||||||
use tracing::error;
|
use tracing::{error, info};
|
||||||
|
|
||||||
use crate::event::Event;
|
use crate::event::Event;
|
||||||
|
|
||||||
@@ -51,7 +51,8 @@ impl EventManager {
|
|||||||
|
|
||||||
loop {
|
loop {
|
||||||
match listener.accept().await {
|
match listener.accept().await {
|
||||||
Ok((stream, _)) => {
|
Ok((stream, _addr)) => {
|
||||||
|
info!("New broadcast subscriber");
|
||||||
// Spawn a new stream for the plugin. The loop
|
// Spawn a new stream for the plugin. The loop
|
||||||
// runs recursively from there.
|
// runs recursively from there.
|
||||||
let broadcaster = Arc::clone(&self);
|
let broadcaster = Arc::clone(&self);
|
||||||
@@ -87,3 +88,188 @@ impl EventManager {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use rstest::rstest;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_new_event_manager_has_empty_buffer() {
|
||||||
|
let manager = EventManager::new().unwrap();
|
||||||
|
let events = manager.events.read().await;
|
||||||
|
assert_eq!(events.len(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_broadcast_adds_event_to_buffer() {
|
||||||
|
let manager = EventManager::new().unwrap();
|
||||||
|
let event = Event::new("test message");
|
||||||
|
|
||||||
|
manager.broadcast(&event).await.unwrap();
|
||||||
|
|
||||||
|
let events = manager.events.read().await;
|
||||||
|
assert_eq!(events.len(), 1);
|
||||||
|
assert!(events[0].contains("test message"));
|
||||||
|
assert!(events[0].ends_with('\n'));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_broadcast_serializes_event_as_json() {
|
||||||
|
let manager = EventManager::new().unwrap();
|
||||||
|
let event = Event::new("hello world");
|
||||||
|
|
||||||
|
manager.broadcast(&event).await.unwrap();
|
||||||
|
|
||||||
|
let events = manager.events.read().await;
|
||||||
|
let stored = &events[0];
|
||||||
|
|
||||||
|
// Should be valid JSON
|
||||||
|
let parsed: serde_json::Value = serde_json::from_str(stored.trim()).unwrap();
|
||||||
|
assert_eq!(parsed["message"], "hello world");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[case(1)]
|
||||||
|
#[case(10)]
|
||||||
|
#[case(100)]
|
||||||
|
#[case(999)]
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_buffer_holds_events_below_max(#[case] count: usize) {
|
||||||
|
let manager = EventManager::new().unwrap();
|
||||||
|
|
||||||
|
for i in 0..count {
|
||||||
|
let event = Event::new(format!("event {}", i));
|
||||||
|
manager.broadcast(&event).await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let events = manager.events.read().await;
|
||||||
|
assert_eq!(events.len(), count);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_buffer_at_exactly_max_capacity() {
|
||||||
|
let manager = EventManager::new().unwrap();
|
||||||
|
|
||||||
|
// Fill to exactly EVENT_BUF_MAX (1000)
|
||||||
|
for i in 0..EVENT_BUF_MAX {
|
||||||
|
let event = Event::new(format!("event {}", i));
|
||||||
|
manager.broadcast(&event).await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let events = manager.events.read().await;
|
||||||
|
assert_eq!(events.len(), EVENT_BUF_MAX);
|
||||||
|
assert!(events[0].contains("event 0"));
|
||||||
|
assert!(events[EVENT_BUF_MAX - 1].contains("event 999"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[case(1)]
|
||||||
|
#[case(10)]
|
||||||
|
#[case(100)]
|
||||||
|
#[case(500)]
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_buffer_overflow_evicts_oldest_fifo(#[case] overflow: usize) {
|
||||||
|
let manager = EventManager::new().unwrap();
|
||||||
|
let total = EVENT_BUF_MAX + overflow;
|
||||||
|
|
||||||
|
// Broadcast more events than buffer can hold
|
||||||
|
for i in 0..total {
|
||||||
|
let event = Event::new(format!("event {}", i));
|
||||||
|
manager.broadcast(&event).await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let events = manager.events.read().await;
|
||||||
|
|
||||||
|
// Buffer should still be at max capacity
|
||||||
|
assert_eq!(events.len(), EVENT_BUF_MAX);
|
||||||
|
|
||||||
|
// Oldest events (0 through overflow-1) should be evicted
|
||||||
|
// Buffer should contain events [overflow..total)
|
||||||
|
let first_event = &events[0];
|
||||||
|
let last_event = &events[EVENT_BUF_MAX - 1];
|
||||||
|
|
||||||
|
assert!(first_event.contains(&format!("event {}", overflow)));
|
||||||
|
assert!(last_event.contains(&format!("event {}", total - 1)));
|
||||||
|
|
||||||
|
// Verify the evicted events are NOT in the buffer
|
||||||
|
let buffer_string = events.iter().cloned().collect::<String>();
|
||||||
|
assert!(!buffer_string.contains(r#""message":"event 0""#));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_multiple_broadcasts_maintain_order() {
|
||||||
|
let manager = EventManager::new().unwrap();
|
||||||
|
let messages = vec!["first", "second", "third", "fourth", "fifth"];
|
||||||
|
|
||||||
|
for msg in &messages {
|
||||||
|
let event = Event::new(*msg);
|
||||||
|
manager.broadcast(&event).await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let events = manager.events.read().await;
|
||||||
|
assert_eq!(events.len(), messages.len());
|
||||||
|
|
||||||
|
for (i, expected) in messages.iter().enumerate() {
|
||||||
|
assert!(events[i].contains(expected));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_buffer_wraparound_maintains_newest_events() {
|
||||||
|
let manager = EventManager::new().unwrap();
|
||||||
|
|
||||||
|
// Fill buffer completely
|
||||||
|
for i in 0..EVENT_BUF_MAX {
|
||||||
|
let event = Event::new(format!("old {}", i));
|
||||||
|
manager.broadcast(&event).await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add 5 more events
|
||||||
|
for i in 0..5 {
|
||||||
|
let event = Event::new(format!("new {}", i));
|
||||||
|
manager.broadcast(&event).await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let events = manager.events.read().await;
|
||||||
|
assert_eq!(events.len(), EVENT_BUF_MAX);
|
||||||
|
|
||||||
|
// First 5 old events should be gone
|
||||||
|
let buffer_string = events.iter().cloned().collect::<String>();
|
||||||
|
assert!(!buffer_string.contains(r#""message":"old 0""#));
|
||||||
|
assert!(!buffer_string.contains(r#""message":"old 4""#));
|
||||||
|
|
||||||
|
// But old 5 should still be there (now at the front)
|
||||||
|
assert!(events[0].contains("old 5"));
|
||||||
|
|
||||||
|
// New events should be at the end
|
||||||
|
assert!(events[EVENT_BUF_MAX - 5].contains("new 0"));
|
||||||
|
assert!(events[EVENT_BUF_MAX - 1].contains("new 4"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_concurrent_broadcasts_all_stored() {
|
||||||
|
let manager = Arc::new(EventManager::new().unwrap());
|
||||||
|
let mut handles = vec![];
|
||||||
|
|
||||||
|
// Spawn 10 concurrent tasks, each broadcasting 10 events
|
||||||
|
for task_id in 0..10 {
|
||||||
|
let manager_clone = Arc::clone(&manager);
|
||||||
|
let handle = tokio::spawn(async move {
|
||||||
|
for i in 0..10 {
|
||||||
|
let event = Event::new(format!("task {} event {}", task_id, i));
|
||||||
|
manager_clone.broadcast(&event).await.unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
handles.push(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for all tasks to complete
|
||||||
|
for handle in handles {
|
||||||
|
handle.await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let events = manager.events.read().await;
|
||||||
|
assert_eq!(events.len(), 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user