use super::*;
use std::sync::atomic::{AtomicUsize, Ordering};
#[test]
fn test_message_zero_copy() {
struct NonClone {
val: i32,
}
let msg = Message::new(NonClone { val: 42 });
let msg_clone = msg.clone();
assert_eq!(msg.data().val, 42);
assert_eq!(msg_clone.data().val, 42);
let Err(msg) = msg.try_unwrap() else {
panic!("should fail to unwrap")
};
drop(msg_clone);
let unwrapped_ok = msg.try_unwrap().unwrap();
assert_eq!(unwrapped_ok.val, 42);
}
#[test]
fn test_broadcast_channel() {
let channel = BroadcastChannel::new();
let mut rx1 = channel.subscribe();
let mut rx2 = channel.subscribe();
channel.broadcast(42);
assert_eq!(rx1.try_recv(), Some(42));
assert_eq!(rx2.try_recv(), Some(42));
assert_eq!(rx1.try_recv(), None);
}
#[test]
fn test_collective_ops() {
let values = vec![1, 2, 3, 4];
let result = CollectiveOps::all_reduce(values, |a, b| a + b);
assert_eq!(result, vec![10, 10, 10, 10]);
let data = vec![1, 2, 3, 4, 5, 6];
let scattered = CollectiveOps::scatter(data, 3);
assert_eq!(scattered.num_chunks(), 3);
assert_eq!(scattered.chunks().next(), Some([1, 2].as_slice()));
let gathered = CollectiveOps::gather(scattered);
assert_eq!(gathered, vec![1, 2, 3, 4, 5, 6]);
}
#[test]
fn test_collective_scatter_gather_round_trip() {
let data: Vec<u64> = (0..100).collect();
let scattered = CollectiveOps::scatter(data.clone(), 7);
let chunks: Vec<Vec<u64>> = scattered.chunks().map(<[u64]>::to_vec).collect();
assert_eq!(chunks.len(), 7);
assert_eq!(chunks.concat(), data);
assert_eq!(CollectiveOps::gather(scattered), data);
}
#[test]
fn test_collective_scatter_empty_and_zero_participants() {
let empty = CollectiveOps::scatter(Vec::<u64>::new(), 4);
assert!(empty.is_empty());
assert_eq!(empty.num_chunks(), 0);
let single = CollectiveOps::scatter(vec![1, 2, 3], 0);
assert_eq!(single.chunks().count(), 1);
assert_eq!(single.chunks().next(), Some([1, 2, 3].as_slice()));
}
#[test]
fn test_collective_all_to_all_parity() {
let data: Vec<u64> = (1..=10).collect();
let chunked = CollectiveOps::scatter(data, 4);
let expected: Vec<Vec<u64>> = vec![
vec![1, 4, 7, 10], vec![2, 5, 8], vec![3, 6, 9], vec![], ];
let transposed = CollectiveOps::all_to_all(chunked);
let got: Vec<Vec<u64>> = transposed.chunks().map(<[u64]>::to_vec).collect();
assert_eq!(got, expected);
}
#[test]
fn test_ring_buffer() {
let rb = RingBuffer::new(4);
assert!(rb.try_produce(1).is_ok());
assert!(rb.try_produce(2).is_ok());
assert_eq!(rb.try_consume(), Some(1));
assert_eq!(rb.try_consume(), Some(2));
assert_eq!(rb.try_consume(), None);
}
#[test]
fn test_ring_buffer_drop_safety() {
static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
struct TrackDrop;
impl Drop for TrackDrop {
fn drop(&mut self) {
DROP_COUNT.fetch_add(1, Ordering::SeqCst);
}
}
{
let rb = RingBuffer::new(4);
assert!(rb.try_produce(TrackDrop).is_ok());
assert!(rb.try_produce(TrackDrop).is_ok());
}
assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 2);
}
#[test]
fn test_pubsub() {
let pubsub = PubSub::new();
let rx = pubsub.subscribe("topic1");
assert_eq!(pubsub.publish(&"topic1", 42).unwrap(), 1);
assert_eq!(rx.try_recv().unwrap(), 42);
assert_eq!(pubsub.publish(&"topic2", 99).unwrap(), 0);
}
#[test]
fn test_pubsub_prunes_dropped_subscribers() {
let pubsub = PubSub::new();
let survivors: Vec<_> = (0..4).map(|_| pubsub.subscribe("topic")).collect();
assert_eq!(pubsub.subscriber_count(&"topic"), 4);
let keeper = survivors.into_iter().next().unwrap();
assert_eq!(pubsub.publish(&"topic", 7).unwrap(), 1);
assert_eq!(
pubsub.subscriber_count(&"topic"),
1,
"closed subscriber senders must be pruned during publish"
);
assert_eq!(pubsub.publish(&"topic", 8).unwrap(), 1);
assert_eq!(keeper.try_recv().unwrap(), 7);
assert_eq!(keeper.try_recv().unwrap(), 8);
}
#[test]
fn test_pubsub_removes_empty_topic_after_all_subscribers_drop() {
let pubsub: PubSub<&str, i32> = PubSub::new();
drop(pubsub.subscribe("gone"));
assert_eq!(pubsub.publish(&"gone", 1).unwrap(), 0);
assert_eq!(pubsub.subscriber_count(&"gone"), 0);
}