Skip to content

Commit 6ccf263

Browse files
committed
test(runtime): add comprehensive tests for TokioContext
Add 16 tests covering the TokioContext implementation: - Constructor tests (new, default) - Spawner trait (spawn, with_label, stop, stopped) - Clock trait (now, sleep) - Clone/state sharing behavior - Debug implementation
1 parent acba20e commit 6ccf263

1 file changed

Lines changed: 169 additions & 0 deletions

File tree

crates/runtime/src/tokio_runtime.rs

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,172 @@ impl Clock for TokioContext {
8787
tokio::time::sleep(duration).await;
8888
}
8989
}
90+
91+
#[cfg(test)]
92+
mod tests {
93+
use super::*;
94+
95+
#[test]
96+
fn test_new_creates_context() {
97+
let ctx = TokioContext::new();
98+
assert!(ctx.label.is_empty());
99+
}
100+
101+
#[test]
102+
fn test_default_creates_context() {
103+
let ctx = TokioContext::default();
104+
assert!(ctx.label.is_empty());
105+
}
106+
107+
#[tokio::test]
108+
async fn test_spawn_returns_result() {
109+
let ctx = TokioContext::new();
110+
let handle = ctx.spawn(|_| async { 42 });
111+
let result = handle.join().await.unwrap();
112+
assert_eq!(result, 42);
113+
}
114+
115+
#[tokio::test]
116+
async fn test_spawn_receives_context() {
117+
let ctx = TokioContext::new().with_label("parent");
118+
let handle = ctx.spawn(|c| async move { c.label.clone() });
119+
let result = handle.join().await.unwrap();
120+
assert_eq!(result, "parent");
121+
}
122+
123+
#[tokio::test]
124+
async fn test_spawn_multiple_tasks() {
125+
let ctx = TokioContext::new();
126+
127+
let h1 = ctx.spawn(|_| async { 1 });
128+
let h2 = ctx.spawn(|_| async { 2 });
129+
let h3 = ctx.spawn(|_| async { 3 });
130+
131+
let r1 = h1.join().await.unwrap();
132+
let r2 = h2.join().await.unwrap();
133+
let r3 = h3.join().await.unwrap();
134+
135+
assert_eq!(r1 + r2 + r3, 6);
136+
}
137+
138+
#[test]
139+
fn test_with_label_single() {
140+
let ctx = TokioContext::new();
141+
let labeled = ctx.with_label("test");
142+
assert_eq!(labeled.label, "test");
143+
}
144+
145+
#[test]
146+
fn test_with_label_nested() {
147+
let ctx = TokioContext::new();
148+
let first = ctx.with_label("parent");
149+
let second = first.with_label("child");
150+
assert_eq!(second.label, "parent:child");
151+
}
152+
153+
#[test]
154+
fn test_with_label_deeply_nested() {
155+
let ctx = TokioContext::new();
156+
let labeled = ctx.with_label("a").with_label("b").with_label("c");
157+
assert_eq!(labeled.label, "a:b:c");
158+
}
159+
160+
#[test]
161+
fn test_with_label_preserves_stop_channel() {
162+
let ctx = TokioContext::new();
163+
let labeled = ctx.with_label("test");
164+
assert!(Arc::ptr_eq(&ctx.stop_tx, &labeled.stop_tx));
165+
}
166+
167+
#[tokio::test]
168+
async fn test_stop_signals_stopped() {
169+
let ctx = TokioContext::new();
170+
let ctx_clone = ctx.clone();
171+
172+
let handle = ctx.spawn(|c| async move {
173+
match c.stopped().await {
174+
Signal::Closed(code) => code,
175+
Signal::Open => -999,
176+
}
177+
});
178+
179+
ctx_clone.stop(42, None).await;
180+
181+
let result = handle.join().await.unwrap();
182+
assert_eq!(result, 42);
183+
}
184+
185+
#[tokio::test]
186+
async fn test_stop_with_different_codes() {
187+
for code in [0, 1, -1, 100, i32::MAX, i32::MIN] {
188+
let ctx = TokioContext::new();
189+
let ctx_clone = ctx.clone();
190+
191+
let handle = ctx.spawn(|c| async move {
192+
match c.stopped().await {
193+
Signal::Closed(c) => c,
194+
Signal::Open => -999,
195+
}
196+
});
197+
198+
ctx_clone.stop(code, None).await;
199+
200+
let result = handle.join().await.unwrap();
201+
assert_eq!(result, code);
202+
}
203+
}
204+
205+
#[tokio::test]
206+
async fn test_stop_propagates_to_labeled_context() {
207+
let ctx = TokioContext::new();
208+
let labeled = ctx.with_label("child");
209+
210+
let handle = labeled.spawn(|c| async move {
211+
match c.stopped().await {
212+
Signal::Closed(code) => code,
213+
Signal::Open => -999,
214+
}
215+
});
216+
217+
ctx.stop(123, None).await;
218+
219+
let result = handle.join().await.unwrap();
220+
assert_eq!(result, 123);
221+
}
222+
223+
#[tokio::test]
224+
async fn test_now_returns_increasing_time() {
225+
let ctx = TokioContext::new();
226+
let t1 = ctx.now();
227+
tokio::time::sleep(Duration::from_millis(10)).await;
228+
let t2 = ctx.now();
229+
assert!(t2 > t1);
230+
}
231+
232+
#[tokio::test]
233+
async fn test_sleep_waits_minimum_duration() {
234+
let ctx = TokioContext::new();
235+
let sleep_duration = Duration::from_millis(50);
236+
237+
let start = Instant::now();
238+
ctx.sleep(sleep_duration).await;
239+
let elapsed = start.elapsed();
240+
241+
assert!(elapsed >= sleep_duration);
242+
}
243+
244+
#[tokio::test]
245+
async fn test_clone_shares_state() {
246+
let ctx1 = TokioContext::new();
247+
let ctx2 = ctx1.clone();
248+
assert!(Arc::ptr_eq(&ctx1.stop_tx, &ctx2.stop_tx));
249+
}
250+
251+
#[tokio::test]
252+
async fn test_debug_impl() {
253+
let ctx = TokioContext::new().with_label("test");
254+
let debug_str = format!("{:?}", ctx);
255+
assert!(debug_str.contains("TokioContext"));
256+
assert!(debug_str.contains("test"));
257+
}
258+
}

0 commit comments

Comments
 (0)