1#[cfg(test)]
2use portable_atomic::{AtomicBool, Ordering};
3use std::borrow::Cow;
4use std::sync::{Arc, Condvar, Mutex, MutexGuard, Weak};
5use std::time::Duration;
6#[cfg(not(target_arch = "wasm32"))]
7use std::time::Instant;
8use std::{fmt, io, thread};
9
10#[cfg(target_arch = "wasm32")]
11use instant::Instant;
12#[cfg(test)]
13use once_cell::sync::Lazy;
14
15use crate::draw_target::ProgressDrawTarget;
16use crate::state::{AtomicPosition, BarState, ProgressFinish, Reset, TabExpandedString};
17use crate::style::ProgressStyle;
18use crate::{ProgressBarIter, ProgressIterator, ProgressState};
19
20/// A progress bar or spinner
21///
22/// The progress bar is an [`Arc`] around its internal state. When the progress bar is cloned it
23/// just increments the refcount (so the original and its clone share the same state).
24#[derive(Clone)]
25pub struct ProgressBar {
26 state: Arc<Mutex<BarState>>,
27 pos: Arc<AtomicPosition>,
28 ticker: Arc<Mutex<Option<Ticker>>>,
29}
30
31impl fmt::Debug for ProgressBar {
32 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33 f.debug_struct(name:"ProgressBar").finish()
34 }
35}
36
37impl ProgressBar {
38 /// Creates a new progress bar with a given length
39 ///
40 /// This progress bar by default draws directly to stderr, and refreshes a maximum of 15 times
41 /// a second. To change the refresh rate, set the draw target to one with a different refresh
42 /// rate.
43 pub fn new(len: u64) -> Self {
44 Self::with_draw_target(Some(len), ProgressDrawTarget::stderr())
45 }
46
47 /// Creates a completely hidden progress bar
48 ///
49 /// This progress bar still responds to API changes but it does not have a length or render in
50 /// any way.
51 pub fn hidden() -> Self {
52 Self::with_draw_target(None, ProgressDrawTarget::hidden())
53 }
54
55 /// Creates a new progress bar with a given length and draw target
56 pub fn with_draw_target(len: Option<u64>, draw_target: ProgressDrawTarget) -> Self {
57 let pos = Arc::new(AtomicPosition::new());
58 Self {
59 state: Arc::new(Mutex::new(BarState::new(len, draw_target, pos.clone()))),
60 pos,
61 ticker: Arc::new(Mutex::new(None)),
62 }
63 }
64
65 /// Get a clone of the current progress bar style.
66 pub fn style(&self) -> ProgressStyle {
67 self.state().style.clone()
68 }
69
70 /// A convenience builder-like function for a progress bar with a given style
71 pub fn with_style(self, style: ProgressStyle) -> Self {
72 self.set_style(style);
73 self
74 }
75
76 /// A convenience builder-like function for a progress bar with a given tab width
77 pub fn with_tab_width(self, tab_width: usize) -> Self {
78 self.state().set_tab_width(tab_width);
79 self
80 }
81
82 /// A convenience builder-like function for a progress bar with a given prefix
83 ///
84 /// For the prefix to be visible, the `{prefix}` placeholder must be present in the template
85 /// (see [`ProgressStyle`]).
86 pub fn with_prefix(self, prefix: impl Into<Cow<'static, str>>) -> Self {
87 let mut state = self.state();
88 state.state.prefix = TabExpandedString::new(prefix.into(), state.tab_width);
89 drop(state);
90 self
91 }
92
93 /// A convenience builder-like function for a progress bar with a given message
94 ///
95 /// For the message to be visible, the `{msg}` placeholder must be present in the template (see
96 /// [`ProgressStyle`]).
97 pub fn with_message(self, message: impl Into<Cow<'static, str>>) -> Self {
98 let mut state = self.state();
99 state.state.message = TabExpandedString::new(message.into(), state.tab_width);
100 drop(state);
101 self
102 }
103
104 /// A convenience builder-like function for a progress bar with a given position
105 pub fn with_position(self, pos: u64) -> Self {
106 self.state().state.set_pos(pos);
107 self
108 }
109
110 /// A convenience builder-like function for a progress bar with a given elapsed time
111 pub fn with_elapsed(self, elapsed: Duration) -> Self {
112 self.state().state.started = Instant::now().checked_sub(elapsed).unwrap();
113 self
114 }
115
116 /// Sets the finish behavior for the progress bar
117 ///
118 /// This behavior is invoked when [`ProgressBar`] or
119 /// [`ProgressBarIter`] completes and
120 /// [`ProgressBar::is_finished()`] is false.
121 /// If you don't want the progress bar to be automatically finished then
122 /// call `on_finish(None)`.
123 ///
124 /// [`ProgressBar`]: crate::ProgressBar
125 /// [`ProgressBarIter`]: crate::ProgressBarIter
126 /// [`ProgressBar::is_finished()`]: crate::ProgressBar::is_finished
127 pub fn with_finish(self, finish: ProgressFinish) -> Self {
128 self.state().on_finish = finish;
129 self
130 }
131
132 /// Creates a new spinner
133 ///
134 /// This spinner by default draws directly to stderr. This adds the default spinner style to it.
135 pub fn new_spinner() -> Self {
136 let rv = Self::with_draw_target(None, ProgressDrawTarget::stderr());
137 rv.set_style(ProgressStyle::default_spinner());
138 rv
139 }
140
141 /// Overrides the stored style
142 ///
143 /// This does not redraw the bar. Call [`ProgressBar::tick()`] to force it.
144 pub fn set_style(&self, style: ProgressStyle) {
145 self.state().set_style(style);
146 }
147
148 /// Sets the tab width (default: 8). All tabs will be expanded to this many spaces.
149 pub fn set_tab_width(&mut self, tab_width: usize) {
150 let mut state = self.state();
151 state.set_tab_width(tab_width);
152 state.draw(true, Instant::now()).unwrap();
153 }
154
155 /// Spawns a background thread to tick the progress bar
156 ///
157 /// When this is enabled a background thread will regularly tick the progress bar in the given
158 /// interval. This is useful to advance progress bars that are very slow by themselves.
159 ///
160 /// When steady ticks are enabled, calling [`ProgressBar::tick()`] on a progress bar does not
161 /// have any effect.
162 pub fn enable_steady_tick(&self, interval: Duration) {
163 // The way we test for ticker termination is with a single static `AtomicBool`. Since cargo
164 // runs tests concurrently, we have a `TICKER_TEST` lock to make sure tests using ticker
165 // don't step on each other. This check catches attempts to use tickers in tests without
166 // acquiring the lock.
167 #[cfg(test)]
168 {
169 let guard = TICKER_TEST.try_lock();
170 let lock_acquired = guard.is_ok();
171 // Drop the guard before panicking to avoid poisoning the lock (which would cause other
172 // ticker tests to fail)
173 drop(guard);
174 if lock_acquired {
175 panic!("you must acquire the TICKER_TEST lock in your test to use this method");
176 }
177 }
178
179 if interval.is_zero() {
180 return;
181 }
182
183 self.stop_and_replace_ticker(Some(interval));
184 }
185
186 /// Undoes [`ProgressBar::enable_steady_tick()`]
187 pub fn disable_steady_tick(&self) {
188 self.stop_and_replace_ticker(None);
189 }
190
191 fn stop_and_replace_ticker(&self, interval: Option<Duration>) {
192 let mut ticker_state = self.ticker.lock().unwrap();
193 if let Some(ticker) = ticker_state.take() {
194 ticker.stop();
195 }
196
197 *ticker_state = interval.map(|interval| Ticker::new(interval, &self.state));
198 }
199
200 /// Manually ticks the spinner or progress bar
201 ///
202 /// This automatically happens on any other change to a progress bar.
203 pub fn tick(&self) {
204 self.tick_inner(Instant::now());
205 }
206
207 fn tick_inner(&self, now: Instant) {
208 // Only tick if a `Ticker` isn't installed
209 if self.ticker.lock().unwrap().is_none() {
210 self.state().tick(now);
211 }
212 }
213
214 /// Advances the position of the progress bar by `delta`
215 pub fn inc(&self, delta: u64) {
216 self.pos.inc(delta);
217 let now = Instant::now();
218 if self.pos.allow(now) {
219 self.tick_inner(now);
220 }
221 }
222
223 /// A quick convenience check if the progress bar is hidden
224 pub fn is_hidden(&self) -> bool {
225 self.state().draw_target.is_hidden()
226 }
227
228 /// Indicates that the progress bar finished
229 pub fn is_finished(&self) -> bool {
230 self.state().state.is_finished()
231 }
232
233 /// Print a log line above the progress bar
234 ///
235 /// If the progress bar is hidden (e.g. when standard output is not a terminal), `println()`
236 /// will not do anything. If you want to write to the standard output in such cases as well, use
237 /// [`suspend`] instead.
238 ///
239 /// If the progress bar was added to a [`MultiProgress`], the log line will be
240 /// printed above all other progress bars.
241 ///
242 /// [`suspend`]: ProgressBar::suspend
243 /// [`MultiProgress`]: crate::MultiProgress
244 pub fn println<I: AsRef<str>>(&self, msg: I) {
245 self.state().println(Instant::now(), msg.as_ref());
246 }
247
248 /// Update the `ProgressBar`'s inner [`ProgressState`]
249 pub fn update(&self, f: impl FnOnce(&mut ProgressState)) {
250 self.state()
251 .update(Instant::now(), f, self.ticker.lock().unwrap().is_none());
252 }
253
254 /// Sets the position of the progress bar
255 pub fn set_position(&self, pos: u64) {
256 self.pos.set(pos);
257 let now = Instant::now();
258 if self.pos.allow(now) {
259 self.tick_inner(now);
260 }
261 }
262
263 /// Sets the length of the progress bar
264 pub fn set_length(&self, len: u64) {
265 self.state().set_length(Instant::now(), len);
266 }
267
268 /// Increase the length of the progress bar
269 pub fn inc_length(&self, delta: u64) {
270 self.state().inc_length(Instant::now(), delta);
271 }
272
273 /// Sets the current prefix of the progress bar
274 ///
275 /// For the prefix to be visible, the `{prefix}` placeholder must be present in the template
276 /// (see [`ProgressStyle`]).
277 pub fn set_prefix(&self, prefix: impl Into<Cow<'static, str>>) {
278 let mut state = self.state();
279 state.state.prefix = TabExpandedString::new(prefix.into(), state.tab_width);
280 state.update_estimate_and_draw(Instant::now());
281 }
282
283 /// Sets the current message of the progress bar
284 ///
285 /// For the message to be visible, the `{msg}` placeholder must be present in the template (see
286 /// [`ProgressStyle`]).
287 pub fn set_message(&self, msg: impl Into<Cow<'static, str>>) {
288 let mut state = self.state();
289 state.state.message = TabExpandedString::new(msg.into(), state.tab_width);
290 state.update_estimate_and_draw(Instant::now());
291 }
292
293 /// Creates a new weak reference to this `ProgressBar`
294 pub fn downgrade(&self) -> WeakProgressBar {
295 WeakProgressBar {
296 state: Arc::downgrade(&self.state),
297 pos: Arc::downgrade(&self.pos),
298 ticker: Arc::downgrade(&self.ticker),
299 }
300 }
301
302 /// Resets the ETA calculation
303 ///
304 /// This can be useful if the progress bars made a large jump or was paused for a prolonged
305 /// time.
306 pub fn reset_eta(&self) {
307 self.state().reset(Instant::now(), Reset::Eta);
308 }
309
310 /// Resets elapsed time and the ETA calculation
311 pub fn reset_elapsed(&self) {
312 self.state().reset(Instant::now(), Reset::Elapsed);
313 }
314
315 /// Resets all of the progress bar state
316 pub fn reset(&self) {
317 self.state().reset(Instant::now(), Reset::All);
318 }
319
320 /// Finishes the progress bar and leaves the current message
321 pub fn finish(&self) {
322 self.state()
323 .finish_using_style(Instant::now(), ProgressFinish::AndLeave);
324 }
325
326 /// Finishes the progress bar and sets a message
327 ///
328 /// For the message to be visible, the `{msg}` placeholder must be present in the template (see
329 /// [`ProgressStyle`]).
330 pub fn finish_with_message(&self, msg: impl Into<Cow<'static, str>>) {
331 self.state()
332 .finish_using_style(Instant::now(), ProgressFinish::WithMessage(msg.into()));
333 }
334
335 /// Finishes the progress bar and completely clears it
336 pub fn finish_and_clear(&self) {
337 self.state()
338 .finish_using_style(Instant::now(), ProgressFinish::AndClear);
339 }
340
341 /// Finishes the progress bar and leaves the current message and progress
342 pub fn abandon(&self) {
343 self.state()
344 .finish_using_style(Instant::now(), ProgressFinish::Abandon);
345 }
346
347 /// Finishes the progress bar and sets a message, and leaves the current progress
348 ///
349 /// For the message to be visible, the `{msg}` placeholder must be present in the template (see
350 /// [`ProgressStyle`]).
351 pub fn abandon_with_message(&self, msg: impl Into<Cow<'static, str>>) {
352 self.state().finish_using_style(
353 Instant::now(),
354 ProgressFinish::AbandonWithMessage(msg.into()),
355 );
356 }
357
358 /// Finishes the progress bar using the behavior stored in the [`ProgressStyle`]
359 ///
360 /// See [`ProgressBar::with_finish()`].
361 pub fn finish_using_style(&self) {
362 let mut state = self.state();
363 let finish = state.on_finish.clone();
364 state.finish_using_style(Instant::now(), finish);
365 }
366
367 /// Sets a different draw target for the progress bar
368 ///
369 /// This can be used to draw the progress bar to stderr (this is the default):
370 ///
371 /// ```rust,no_run
372 /// # use indicatif::{ProgressBar, ProgressDrawTarget};
373 /// let pb = ProgressBar::new(100);
374 /// pb.set_draw_target(ProgressDrawTarget::stderr());
375 /// ```
376 ///
377 /// **Note:** Calling this method on a [`ProgressBar`] linked with a [`MultiProgress`] (after
378 /// running [`MultiProgress::add`]) will unlink this progress bar. If you don't want this
379 /// behavior, call [`MultiProgress::set_draw_target`] instead.
380 ///
381 /// [`MultiProgress`]: crate::MultiProgress
382 /// [`MultiProgress::add`]: crate::MultiProgress::add
383 /// [`MultiProgress::set_draw_target`]: crate::MultiProgress::set_draw_target
384 pub fn set_draw_target(&self, target: ProgressDrawTarget) {
385 let mut state = self.state();
386 state.draw_target.disconnect(Instant::now());
387 state.draw_target = target;
388 }
389
390 /// Hide the progress bar temporarily, execute `f`, then redraw the progress bar
391 ///
392 /// Useful for external code that writes to the standard output.
393 ///
394 /// If the progress bar was added to a MultiProgress, it will suspend the entire MultiProgress
395 ///
396 /// **Note:** The internal lock is held while `f` is executed. Other threads trying to print
397 /// anything on the progress bar will be blocked until `f` finishes.
398 /// Therefore, it is recommended to avoid long-running operations in `f`.
399 ///
400 /// ```rust,no_run
401 /// # use indicatif::ProgressBar;
402 /// let mut pb = ProgressBar::new(3);
403 /// pb.suspend(|| {
404 /// println!("Log message");
405 /// })
406 /// ```
407 pub fn suspend<F: FnOnce() -> R, R>(&self, f: F) -> R {
408 self.state().suspend(Instant::now(), f)
409 }
410
411 /// Wraps an [`Iterator`] with the progress bar
412 ///
413 /// ```rust,no_run
414 /// # use indicatif::ProgressBar;
415 /// let v = vec![1, 2, 3];
416 /// let pb = ProgressBar::new(3);
417 /// for item in pb.wrap_iter(v.iter()) {
418 /// // ...
419 /// }
420 /// ```
421 pub fn wrap_iter<It: Iterator>(&self, it: It) -> ProgressBarIter<It> {
422 it.progress_with(self.clone())
423 }
424
425 /// Wraps an [`io::Read`] with the progress bar
426 ///
427 /// ```rust,no_run
428 /// # use std::fs::File;
429 /// # use std::io;
430 /// # use indicatif::ProgressBar;
431 /// # fn test () -> io::Result<()> {
432 /// let source = File::open("work.txt")?;
433 /// let mut target = File::create("done.txt")?;
434 /// let pb = ProgressBar::new(source.metadata()?.len());
435 /// io::copy(&mut pb.wrap_read(source), &mut target);
436 /// # Ok(())
437 /// # }
438 /// ```
439 pub fn wrap_read<R: io::Read>(&self, read: R) -> ProgressBarIter<R> {
440 ProgressBarIter {
441 progress: self.clone(),
442 it: read,
443 }
444 }
445
446 /// Wraps an [`io::Write`] with the progress bar
447 ///
448 /// ```rust,no_run
449 /// # use std::fs::File;
450 /// # use std::io;
451 /// # use indicatif::ProgressBar;
452 /// # fn test () -> io::Result<()> {
453 /// let mut source = File::open("work.txt")?;
454 /// let target = File::create("done.txt")?;
455 /// let pb = ProgressBar::new(source.metadata()?.len());
456 /// io::copy(&mut source, &mut pb.wrap_write(target));
457 /// # Ok(())
458 /// # }
459 /// ```
460 pub fn wrap_write<W: io::Write>(&self, write: W) -> ProgressBarIter<W> {
461 ProgressBarIter {
462 progress: self.clone(),
463 it: write,
464 }
465 }
466
467 #[cfg(feature = "tokio")]
468 #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
469 /// Wraps an [`tokio::io::AsyncWrite`] with the progress bar
470 ///
471 /// ```rust,no_run
472 /// # use tokio::fs::File;
473 /// # use tokio::io;
474 /// # use indicatif::ProgressBar;
475 /// # async fn test() -> io::Result<()> {
476 /// let mut source = File::open("work.txt").await?;
477 /// let mut target = File::open("done.txt").await?;
478 /// let pb = ProgressBar::new(source.metadata().await?.len());
479 /// io::copy(&mut source, &mut pb.wrap_async_write(target)).await?;
480 /// # Ok(())
481 /// # }
482 /// ```
483 pub fn wrap_async_write<W: tokio::io::AsyncWrite + Unpin>(
484 &self,
485 write: W,
486 ) -> ProgressBarIter<W> {
487 ProgressBarIter {
488 progress: self.clone(),
489 it: write,
490 }
491 }
492
493 #[cfg(feature = "tokio")]
494 #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
495 /// Wraps an [`tokio::io::AsyncRead`] with the progress bar
496 ///
497 /// ```rust,no_run
498 /// # use tokio::fs::File;
499 /// # use tokio::io;
500 /// # use indicatif::ProgressBar;
501 /// # async fn test() -> io::Result<()> {
502 /// let mut source = File::open("work.txt").await?;
503 /// let mut target = File::open("done.txt").await?;
504 /// let pb = ProgressBar::new(source.metadata().await?.len());
505 /// io::copy(&mut pb.wrap_async_read(source), &mut target).await?;
506 /// # Ok(())
507 /// # }
508 /// ```
509 pub fn wrap_async_read<R: tokio::io::AsyncRead + Unpin>(&self, read: R) -> ProgressBarIter<R> {
510 ProgressBarIter {
511 progress: self.clone(),
512 it: read,
513 }
514 }
515
516 /// Wraps a [`futures::Stream`](https://docs.rs/futures/0.3/futures/stream/trait.StreamExt.html) with the progress bar
517 ///
518 /// ```
519 /// # use indicatif::ProgressBar;
520 /// # futures::executor::block_on(async {
521 /// use futures::stream::{self, StreamExt};
522 /// let pb = ProgressBar::new(10);
523 /// let mut stream = pb.wrap_stream(stream::iter('a'..='z'));
524 ///
525 /// assert_eq!(stream.next().await, Some('a'));
526 /// assert_eq!(stream.count().await, 25);
527 /// # }); // block_on
528 /// ```
529 #[cfg(feature = "futures")]
530 #[cfg_attr(docsrs, doc(cfg(feature = "futures")))]
531 pub fn wrap_stream<S: futures_core::Stream>(&self, stream: S) -> ProgressBarIter<S> {
532 ProgressBarIter {
533 progress: self.clone(),
534 it: stream,
535 }
536 }
537
538 /// Returns the current position
539 pub fn position(&self) -> u64 {
540 self.state().state.pos()
541 }
542
543 /// Returns the current length
544 pub fn length(&self) -> Option<u64> {
545 self.state().state.len()
546 }
547
548 /// Returns the current ETA
549 pub fn eta(&self) -> Duration {
550 self.state().state.eta()
551 }
552
553 /// Returns the current rate of progress
554 pub fn per_sec(&self) -> f64 {
555 self.state().state.per_sec()
556 }
557
558 /// Returns the current expected duration
559 pub fn duration(&self) -> Duration {
560 self.state().state.duration()
561 }
562
563 /// Returns the current elapsed time
564 pub fn elapsed(&self) -> Duration {
565 self.state().state.elapsed()
566 }
567
568 /// Index in the `MultiState`
569 pub(crate) fn index(&self) -> Option<usize> {
570 self.state().draw_target.remote().map(|(_, idx)| idx)
571 }
572
573 /// Current message
574 pub fn message(&self) -> String {
575 self.state().state.message.expanded().to_string()
576 }
577
578 /// Current prefix
579 pub fn prefix(&self) -> String {
580 self.state().state.prefix.expanded().to_string()
581 }
582
583 #[inline]
584 pub(crate) fn state(&self) -> MutexGuard<'_, BarState> {
585 self.state.lock().unwrap()
586 }
587}
588
589/// A weak reference to a `ProgressBar`.
590///
591/// Useful for creating custom steady tick implementations
592#[derive(Clone, Default)]
593pub struct WeakProgressBar {
594 state: Weak<Mutex<BarState>>,
595 pos: Weak<AtomicPosition>,
596 ticker: Weak<Mutex<Option<Ticker>>>,
597}
598
599impl WeakProgressBar {
600 /// Create a new `WeakProgressBar` that returns `None` when [`upgrade`] is called.
601 ///
602 /// [`upgrade`]: WeakProgressBar::upgrade
603 pub fn new() -> Self {
604 Self::default()
605 }
606
607 /// Attempts to upgrade the Weak pointer to a [`ProgressBar`], delaying dropping of the inner
608 /// value if successful. Returns `None` if the inner value has since been dropped.
609 ///
610 /// [`ProgressBar`]: struct.ProgressBar.html
611 pub fn upgrade(&self) -> Option<ProgressBar> {
612 let state: Arc> = self.state.upgrade()?;
613 let pos: Arc = self.pos.upgrade()?;
614 let ticker: Arc>> = self.ticker.upgrade()?;
615 Some(ProgressBar { state, pos, ticker })
616 }
617}
618
619pub(crate) struct Ticker {
620 stopping: Arc<(Mutex<bool>, Condvar)>,
621 join_handle: Option<thread::JoinHandle<()>>,
622}
623
624impl Drop for Ticker {
625 fn drop(&mut self) {
626 self.stop();
627 self.join_handle.take().map(|handle: JoinHandle<()>| handle.join());
628 }
629}
630
631#[cfg(test)]
632static TICKER_RUNNING: AtomicBool = AtomicBool::new(false);
633
634impl Ticker {
635 pub(crate) fn new(interval: Duration, bar_state: &Arc<Mutex<BarState>>) -> Self {
636 debug_assert!(!interval.is_zero());
637
638 // A `Mutex<bool>` is used as a flag to indicate whether the ticker was requested to stop.
639 // The `Condvar` is used a notification mechanism: when the ticker is dropped, we notify
640 // the thread and interrupt the ticker wait.
641 #[allow(clippy::mutex_atomic)]
642 let stopping = Arc::new((Mutex::new(false), Condvar::new()));
643 let control = TickerControl {
644 stopping: stopping.clone(),
645 state: Arc::downgrade(bar_state),
646 };
647
648 let join_handle = thread::spawn(move || control.run(interval));
649 Self {
650 stopping,
651 join_handle: Some(join_handle),
652 }
653 }
654
655 pub(crate) fn stop(&self) {
656 *self.stopping.0.lock().unwrap() = true;
657 self.stopping.1.notify_one();
658 }
659}
660
661struct TickerControl {
662 stopping: Arc<(Mutex<bool>, Condvar)>,
663 state: Weak<Mutex<BarState>>,
664}
665
666impl TickerControl {
667 fn run(&self, interval: Duration) {
668 #[cfg(test)]
669 TICKER_RUNNING.store(true, Ordering::SeqCst);
670
671 while let Some(arc) = self.state.upgrade() {
672 let mut state = arc.lock().unwrap();
673 if state.state.is_finished() {
674 break;
675 }
676
677 state.tick(Instant::now());
678
679 drop(state); // Don't forget to drop the lock before sleeping
680 drop(arc); // Also need to drop Arc otherwise BarState won't be dropped
681
682 // Wait for `interval` but return early if we are notified to stop
683 let (_, result) = self
684 .stopping
685 .1
686 .wait_timeout_while(self.stopping.0.lock().unwrap(), interval, |stopped| {
687 !*stopped
688 })
689 .unwrap();
690
691 // If the wait didn't time out, it means we were notified to stop
692 if !result.timed_out() {
693 break;
694 }
695 }
696
697 #[cfg(test)]
698 TICKER_RUNNING.store(false, Ordering::SeqCst);
699 }
700}
701
702// Tests using the global TICKER_RUNNING flag need to be serialized
703#[cfg(test)]
704pub(crate) static TICKER_TEST: Lazy<Mutex<()>> = Lazy::new(Mutex::default);
705
706#[cfg(test)]
707mod tests {
708 use super::*;
709
710 #[allow(clippy::float_cmp)]
711 #[test]
712 fn test_pbar_zero() {
713 let pb = ProgressBar::new(0);
714 assert_eq!(pb.state().state.fraction(), 1.0);
715 }
716
717 #[allow(clippy::float_cmp)]
718 #[test]
719 fn test_pbar_maxu64() {
720 let pb = ProgressBar::new(!0);
721 assert_eq!(pb.state().state.fraction(), 0.0);
722 }
723
724 #[test]
725 fn test_pbar_overflow() {
726 let pb = ProgressBar::new(1);
727 pb.set_draw_target(ProgressDrawTarget::hidden());
728 pb.inc(2);
729 pb.finish();
730 }
731
732 #[test]
733 fn test_get_position() {
734 let pb = ProgressBar::new(1);
735 pb.set_draw_target(ProgressDrawTarget::hidden());
736 pb.inc(2);
737 let pos = pb.position();
738 assert_eq!(pos, 2);
739 }
740
741 #[test]
742 fn test_weak_pb() {
743 let pb = ProgressBar::new(0);
744 let weak = pb.downgrade();
745 assert!(weak.upgrade().is_some());
746 ::std::mem::drop(pb);
747 assert!(weak.upgrade().is_none());
748 }
749
750 #[test]
751 fn it_can_wrap_a_reader() {
752 let bytes = &b"I am an implementation of io::Read"[..];
753 let pb = ProgressBar::new(bytes.len() as u64);
754 let mut reader = pb.wrap_read(bytes);
755 let mut writer = Vec::new();
756 io::copy(&mut reader, &mut writer).unwrap();
757 assert_eq!(writer, bytes);
758 }
759
760 #[test]
761 fn it_can_wrap_a_writer() {
762 let bytes = b"implementation of io::Read";
763 let mut reader = &bytes[..];
764 let pb = ProgressBar::new(bytes.len() as u64);
765 let writer = Vec::new();
766 let mut writer = pb.wrap_write(writer);
767 io::copy(&mut reader, &mut writer).unwrap();
768 assert_eq!(writer.it, bytes);
769 }
770
771 #[test]
772 fn ticker_thread_terminates_on_drop() {
773 let _guard = TICKER_TEST.lock().unwrap();
774 assert!(!TICKER_RUNNING.load(Ordering::SeqCst));
775
776 let pb = ProgressBar::new_spinner();
777 pb.enable_steady_tick(Duration::from_millis(50));
778
779 // Give the thread time to start up
780 thread::sleep(Duration::from_millis(250));
781
782 assert!(TICKER_RUNNING.load(Ordering::SeqCst));
783
784 drop(pb);
785 assert!(!TICKER_RUNNING.load(Ordering::SeqCst));
786 }
787
788 #[test]
789 fn ticker_thread_terminates_on_drop_2() {
790 let _guard = TICKER_TEST.lock().unwrap();
791 assert!(!TICKER_RUNNING.load(Ordering::SeqCst));
792
793 let pb = ProgressBar::new_spinner();
794 pb.enable_steady_tick(Duration::from_millis(50));
795 let pb2 = pb.clone();
796
797 // Give the thread time to start up
798 thread::sleep(Duration::from_millis(250));
799
800 assert!(TICKER_RUNNING.load(Ordering::SeqCst));
801
802 drop(pb);
803 assert!(TICKER_RUNNING.load(Ordering::SeqCst));
804
805 drop(pb2);
806 assert!(!TICKER_RUNNING.load(Ordering::SeqCst));
807 }
808}
809