@@ -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