#![cfg(not(target_arch = "wasm32"))]
#![allow(clippy::needless_collect, clippy::let_underscore_must_use)]
#[allow(dead_code, unreachable_pub)]
#[path = "support/mod.rs"]
mod test_support;
use std::collections::BTreeMap;
use assert_matches::assert_matches;
use telltale_types::LocalTypeR;
use telltale_vm::buffer::{
BackpressurePolicy, BoundedBuffer, BufferConfig, BufferMode, EnqueueResult,
};
use telltale_vm::coroutine::Value;
use telltale_vm::instr::Endpoint;
use telltale_vm::session::{SessionStatus, SessionStore};
use telltale_vm::vm::{VMConfig, VM};
use test_support::PassthroughHandler;
#[test]
fn test_session_active_to_closed() {
let image = test_support::simple_send_recv_image("A", "B", "msg");
let mut vm = VM::new(VMConfig::default());
let sid = vm.load_choreography(&image).unwrap();
let handler = PassthroughHandler;
vm.run(&handler, 100).unwrap();
let session = vm.sessions().get(sid).unwrap();
assert_matches!(session.status, SessionStatus::Active);
}
#[test]
fn test_session_active_to_closed_clears_pending_buffers() {
let mut store = SessionStore::new();
let sid = store.open(
vec!["A".into(), "B".into()],
&BufferConfig::default(),
&BTreeMap::new(),
);
let session = store.get_mut(sid).unwrap();
let _ = session.send("A", "B", Value::Nat(1)).unwrap();
store.close(sid).unwrap();
let session = store.get(sid).unwrap();
assert_matches!(session.status, SessionStatus::Closed);
assert!(session.buffers.is_empty());
}
#[test]
fn test_session_ids_monotonic() {
let mut store = SessionStore::new();
let sid1 = store.open(
vec!["A".into(), "B".into()],
&BufferConfig::default(),
&BTreeMap::new(),
);
let sid2 = store.open(
vec!["A".into(), "B".into()],
&BufferConfig::default(),
&BTreeMap::new(),
);
let sid3 = store.open(
vec!["A".into(), "B".into()],
&BufferConfig::default(),
&BTreeMap::new(),
);
assert!(sid1 < sid2);
assert!(sid2 < sid3);
}
#[test]
fn test_active_count_tracks_sessions() {
let mut store = SessionStore::new();
assert_eq!(store.active_count(), 0);
let sid1 = store.open(
vec!["A".into(), "B".into()],
&BufferConfig::default(),
&BTreeMap::new(),
);
assert_eq!(store.active_count(), 1);
let _sid2 = store.open(
vec!["A".into(), "B".into()],
&BufferConfig::default(),
&BTreeMap::new(),
);
assert_eq!(store.active_count(), 2);
store.close(sid1).unwrap();
assert_eq!(store.active_count(), 1);
}
#[test]
fn test_fifo_block() {
let config = BufferConfig {
mode: BufferMode::Fifo,
initial_capacity: 2,
policy: BackpressurePolicy::Block,
};
let mut buf = BoundedBuffer::new(&config);
buf.enqueue(Value::Nat(1));
buf.enqueue(Value::Nat(2));
assert_matches!(buf.enqueue(Value::Nat(3)), EnqueueResult::WouldBlock);
}
#[test]
fn test_fifo_drop() {
let config = BufferConfig {
mode: BufferMode::Fifo,
initial_capacity: 2,
policy: BackpressurePolicy::Drop,
};
let mut buf = BoundedBuffer::new(&config);
buf.enqueue(Value::Nat(1));
buf.enqueue(Value::Nat(2));
assert_matches!(buf.enqueue(Value::Nat(3)), EnqueueResult::Dropped);
assert_eq!(buf.dequeue(), Some(Value::Nat(1)));
assert_eq!(buf.dequeue(), Some(Value::Nat(2)));
}
#[test]
fn test_fifo_error() {
let config = BufferConfig {
mode: BufferMode::Fifo,
initial_capacity: 2,
policy: BackpressurePolicy::Error,
};
let mut buf = BoundedBuffer::new(&config);
buf.enqueue(Value::Nat(1));
buf.enqueue(Value::Nat(2));
assert_matches!(buf.enqueue(Value::Nat(3)), EnqueueResult::Full);
}
#[test]
fn test_fifo_resize() {
let config = BufferConfig {
mode: BufferMode::Fifo,
initial_capacity: 2,
policy: BackpressurePolicy::Resize { max_capacity: 8 },
};
let mut buf = BoundedBuffer::new(&config);
buf.enqueue(Value::Nat(1));
buf.enqueue(Value::Nat(2));
assert_matches!(buf.enqueue(Value::Nat(3)), EnqueueResult::Ok);
assert_eq!(buf.len(), 3);
assert_eq!(buf.dequeue(), Some(Value::Nat(1)));
assert_eq!(buf.dequeue(), Some(Value::Nat(2)));
assert_eq!(buf.dequeue(), Some(Value::Nat(3)));
}
#[test]
fn test_latest_value_overwrites() {
let config = BufferConfig {
mode: BufferMode::LatestValue,
initial_capacity: 1,
policy: BackpressurePolicy::Block,
};
let mut buf = BoundedBuffer::new(&config);
buf.enqueue(Value::Nat(1));
buf.enqueue(Value::Nat(2));
buf.enqueue(Value::Nat(3));
assert_eq!(buf.dequeue(), Some(Value::Nat(3)));
}
#[test]
fn test_latest_value_dequeue_clears() {
let config = BufferConfig {
mode: BufferMode::LatestValue,
initial_capacity: 1,
policy: BackpressurePolicy::Block,
};
let mut buf = BoundedBuffer::new(&config);
buf.enqueue(Value::Nat(1));
assert_eq!(buf.len(), 1);
buf.dequeue();
assert_eq!(buf.len(), 0);
assert!(buf.is_empty());
}
#[test]
fn test_buffer_empty_dequeue_none() {
let mut buf: BoundedBuffer<Value> = BoundedBuffer::new(&BufferConfig::default());
assert_eq!(buf.dequeue(), None);
}
#[test]
fn test_buffer_resize_preserves_order() {
let config = BufferConfig {
mode: BufferMode::Fifo,
initial_capacity: 2,
policy: BackpressurePolicy::Resize { max_capacity: 16 },
};
let mut buf = BoundedBuffer::new(&config);
buf.enqueue(Value::Nat(0));
buf.dequeue();
for i in 1..=5 {
buf.enqueue(Value::Nat(i));
}
for i in 1..=5 {
assert_eq!(buf.dequeue(), Some(Value::Nat(i)));
}
}
#[test]
fn test_two_sessions_independent_types() {
let image1 = test_support::simple_send_recv_image("A", "B", "msg");
let image2 = test_support::simple_send_recv_image("A", "B", "data");
let mut vm = VM::new(VMConfig::default());
let sid1 = vm.load_choreography(&image1).unwrap();
let sid2 = vm.load_choreography(&image2).unwrap();
let ep1a = Endpoint {
sid: sid1,
role: "A".into(),
};
let ep2a = Endpoint {
sid: sid2,
role: "A".into(),
};
let t1 = vm.sessions().lookup_type(&ep1a).cloned();
let t2 = vm.sessions().lookup_type(&ep2a).cloned();
assert_matches!(t1, Some(LocalTypeR::Send { .. }));
assert_matches!(t2, Some(LocalTypeR::Send { .. }));
let handler = PassthroughHandler;
vm.run(&handler, 200).unwrap();
assert!(vm.sessions().lookup_type(&ep1a).is_none());
assert!(vm.sessions().lookup_type(&ep2a).is_none());
}
#[test]
fn test_two_sessions_independent_buffers() {
let mut store = SessionStore::new();
let sid1 = store.open(
vec!["A".into(), "B".into()],
&BufferConfig::default(),
&BTreeMap::new(),
);
let sid2 = store.open(
vec!["A".into(), "B".into()],
&BufferConfig::default(),
&BTreeMap::new(),
);
store
.get_mut(sid1)
.unwrap()
.send("A", "B", Value::Nat(1))
.unwrap();
assert!(!store.get(sid2).unwrap().has_message("A", "B"));
}
#[test]
fn test_three_sessions_complete_independently() {
let image = test_support::simple_send_recv_image("A", "B", "msg");
let mut vm = VM::new(VMConfig::default());
let sid1 = vm.load_choreography(&image).unwrap();
let sid2 = vm.load_choreography(&image).unwrap();
let sid3 = vm.load_choreography(&image).unwrap();
let handler = PassthroughHandler;
vm.run(&handler, 500).unwrap();
for sid in [sid1, sid2, sid3] {
assert!(
vm.session_coroutines(sid).iter().all(|c| c.is_terminal()),
"session {sid} should have all terminal coroutines"
);
}
}