@@ -16,13 +16,22 @@ pub struct SpinnerManager {
1616 start_time : Option < Instant > ,
1717 message : Option < String > ,
1818 tracker : Option < JoinHandle < ( ) > > ,
19+ #[ cfg( test) ]
20+ tick_counter : Option < std:: sync:: Arc < std:: sync:: atomic:: AtomicU64 > > ,
1921}
2022
2123impl SpinnerManager {
2224 pub fn new ( ) -> Self {
2325 Self :: default ( )
2426 }
2527
28+ #[ cfg( test) ]
29+ pub fn test_with_tick_counter (
30+ tick_counter : std:: sync:: Arc < std:: sync:: atomic:: AtomicU64 > ,
31+ ) -> Self {
32+ Self { tick_counter : Some ( tick_counter) , ..Self :: default ( ) }
33+ }
34+
2635 /// Start the spinner with a message
2736 pub fn start ( & mut self , message : Option < & str > ) -> Result < ( ) > {
2837 self . stop ( None ) ?;
@@ -80,12 +89,18 @@ impl SpinnerManager {
8089 let spinner_clone = self . spinner . clone ( ) ;
8190 let start_time_clone = self . start_time ;
8291 let message_clone = self . message . clone ( ) ;
92+ #[ cfg( test) ]
93+ let tick_counter_clone = self . tick_counter . clone ( ) ;
8394
8495 // Spwan tracker to keep the track of time in sec.
8596 self . tracker = Some ( tokio:: spawn ( async move {
8697 let mut interval = tokio:: time:: interval ( tokio:: time:: Duration :: from_millis ( 500 ) ) ;
8798 loop {
8899 interval. tick ( ) . await ;
100+ #[ cfg( test) ]
101+ if let Some ( counter) = & tick_counter_clone {
102+ counter. fetch_add ( 1 , std:: sync:: atomic:: Ordering :: SeqCst ) ;
103+ }
89104 // Update the spinner with the current elapsed time
90105 if let ( Some ( spinner) , Some ( start_time) , Some ( message) ) =
91106 ( & spinner_clone, start_time_clone, & message_clone)
@@ -135,6 +150,7 @@ impl SpinnerManager {
135150
136151 // Tracker task will be dropped here.
137152 if let Some ( a) = self . tracker . take ( ) {
153+ a. abort ( ) ;
138154 drop ( a)
139155 }
140156 self . tracker = None ;
@@ -164,3 +180,31 @@ impl SpinnerManager {
164180 self . write_with_restart ( message, |msg| eprintln ! ( "{msg}" ) )
165181 }
166182}
183+ #[ cfg( test) ]
184+ mod tests {
185+ use std:: sync:: Arc ;
186+ use std:: sync:: atomic:: { AtomicU64 , Ordering } ;
187+
188+ use pretty_assertions:: assert_eq;
189+
190+ use super :: SpinnerManager ;
191+
192+ #[ tokio:: test]
193+ async fn test_spinner_tracker_task_is_stopped_on_stop ( ) {
194+ let fixture_counter = Arc :: new ( AtomicU64 :: new ( 0 ) ) ;
195+ let mut fixture_spinner = SpinnerManager :: test_with_tick_counter ( fixture_counter. clone ( ) ) ;
196+
197+ fixture_spinner. start ( Some ( "Test" ) ) . unwrap ( ) ;
198+ tokio:: time:: sleep ( std:: time:: Duration :: from_millis ( 1100 ) ) . await ;
199+
200+ let actual_before_stop = fixture_counter. load ( Ordering :: SeqCst ) ;
201+ assert ! ( actual_before_stop > 0 ) ;
202+
203+ fixture_spinner. stop ( None ) . unwrap ( ) ;
204+ tokio:: time:: sleep ( std:: time:: Duration :: from_millis ( 1100 ) ) . await ;
205+
206+ let actual_after_stop = fixture_counter. load ( Ordering :: SeqCst ) ;
207+ let expected = actual_before_stop;
208+ assert_eq ! ( actual_after_stop, expected) ;
209+ }
210+ }
0 commit comments