summaryrefslogtreecommitdiff
path: root/vendor/indicatif/tests/render.rs
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2024-01-08 00:21:28 +0300
committerValentin Popov <valentin@popov.link>2024-01-08 00:21:28 +0300
commit1b6a04ca5504955c571d1c97504fb45ea0befee4 (patch)
tree7579f518b23313e8a9748a88ab6173d5e030b227 /vendor/indicatif/tests/render.rs
parent5ecd8cf2cba827454317368b68571df0d13d7842 (diff)
downloadfparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.tar.xz
fparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.zip
Initial vendor packages
Signed-off-by: Valentin Popov <valentin@popov.link>
Diffstat (limited to 'vendor/indicatif/tests/render.rs')
-rw-r--r--vendor/indicatif/tests/render.rs1822
1 files changed, 1822 insertions, 0 deletions
diff --git a/vendor/indicatif/tests/render.rs b/vendor/indicatif/tests/render.rs
new file mode 100644
index 0000000..a891e72
--- /dev/null
+++ b/vendor/indicatif/tests/render.rs
@@ -0,0 +1,1822 @@
+#![cfg(feature = "in_memory")]
+
+use std::time::Duration;
+
+use indicatif::{
+ InMemoryTerm, MultiProgress, MultiProgressAlignment, ProgressBar, ProgressDrawTarget,
+ ProgressFinish, ProgressStyle, TermLike,
+};
+use pretty_assertions::assert_eq;
+
+#[test]
+fn basic_progress_bar() {
+ let in_mem = InMemoryTerm::new(10, 80);
+ let pb = ProgressBar::with_draw_target(
+ Some(10),
+ ProgressDrawTarget::term_like(Box::new(in_mem.clone())),
+ );
+
+ assert_eq!(in_mem.contents(), String::new());
+
+ pb.tick();
+ assert_eq!(
+ in_mem.contents(),
+ "░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10"
+ );
+
+ pb.inc(1);
+ assert_eq!(
+ in_mem.contents(),
+ "███████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 1/10"
+ );
+
+ pb.finish();
+ assert_eq!(
+ in_mem.contents(),
+ "██████████████████████████████████████████████████████████████████████████ 10/10"
+ );
+}
+
+#[test]
+fn progress_bar_builder_method_order() {
+ let in_mem = InMemoryTerm::new(10, 80);
+ // Test that `with_style` doesn't overwrite the message or prefix
+ let pb = ProgressBar::with_draw_target(
+ Some(10),
+ ProgressDrawTarget::term_like(Box::new(in_mem.clone())),
+ )
+ .with_message("crate")
+ .with_prefix("Downloading")
+ .with_style(
+ ProgressStyle::with_template("{prefix:>12.cyan.bold} {msg}: {wide_bar} {pos}/{len}")
+ .unwrap(),
+ );
+
+ assert_eq!(in_mem.contents(), String::new());
+
+ pb.tick();
+ assert_eq!(
+ in_mem.contents(),
+ " Downloading crate: ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10"
+ );
+}
+
+#[test]
+fn progress_bar_percent_with_no_length() {
+ let in_mem = InMemoryTerm::new(10, 80);
+ let pb = ProgressBar::with_draw_target(
+ None,
+ ProgressDrawTarget::term_like(Box::new(in_mem.clone())),
+ )
+ .with_style(ProgressStyle::with_template("{wide_bar} {percent}%").unwrap());
+
+ assert_eq!(in_mem.contents(), String::new());
+
+ pb.tick();
+
+ assert_eq!(
+ in_mem.contents(),
+ "░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0%"
+ );
+
+ pb.set_length(10);
+
+ pb.inc(1);
+ assert_eq!(
+ in_mem.contents(),
+ "███████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 10%"
+ );
+
+ pb.finish();
+ assert_eq!(
+ in_mem.contents(),
+ "███████████████████████████████████████████████████████████████████████████ 100%"
+ );
+}
+
+#[test]
+fn multi_progress_single_bar_and_leave() {
+ let in_mem = InMemoryTerm::new(10, 80);
+ let mp =
+ MultiProgress::with_draw_target(ProgressDrawTarget::term_like(Box::new(in_mem.clone())));
+
+ let pb1 = mp.add(ProgressBar::new(10).with_finish(ProgressFinish::AndLeave));
+
+ assert_eq!(in_mem.contents(), String::new());
+
+ pb1.tick();
+ assert_eq!(
+ in_mem.contents(),
+ r#"░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10"#
+ );
+
+ drop(pb1);
+ assert_eq!(
+ in_mem.contents(),
+ r#"██████████████████████████████████████████████████████████████████████████ 10/10"#
+ );
+}
+
+#[test]
+fn multi_progress_single_bar_and_clear() {
+ let in_mem = InMemoryTerm::new(10, 80);
+ let mp =
+ MultiProgress::with_draw_target(ProgressDrawTarget::term_like(Box::new(in_mem.clone())));
+
+ let pb1 = mp.add(ProgressBar::new(10));
+
+ assert_eq!(in_mem.contents(), String::new());
+
+ pb1.tick();
+ assert_eq!(
+ in_mem.contents(),
+ r#"░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10"#
+ );
+
+ drop(pb1);
+ assert_eq!(in_mem.contents(), "");
+}
+#[test]
+fn multi_progress_two_bars() {
+ let in_mem = InMemoryTerm::new(10, 80);
+ let mp =
+ MultiProgress::with_draw_target(ProgressDrawTarget::term_like(Box::new(in_mem.clone())));
+
+ let pb1 = mp.add(ProgressBar::new(10).with_finish(ProgressFinish::AndLeave));
+ let pb2 = mp.add(ProgressBar::new(5));
+
+ assert_eq!(in_mem.contents(), String::new());
+
+ pb1.tick();
+ assert_eq!(
+ in_mem.contents(),
+ r#"░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10"#
+ );
+
+ pb2.tick();
+
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10
+░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/5"#
+ .trim_start()
+ );
+
+ drop(pb1);
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+██████████████████████████████████████████████████████████████████████████ 10/10
+░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/5"#
+ .trim_start()
+ );
+
+ drop(pb2);
+
+ assert_eq!(
+ in_mem.contents(),
+ r#"██████████████████████████████████████████████████████████████████████████ 10/10"#
+ );
+}
+
+#[test]
+fn multi_progress() {
+ let in_mem = InMemoryTerm::new(10, 80);
+ let mp =
+ MultiProgress::with_draw_target(ProgressDrawTarget::term_like(Box::new(in_mem.clone())));
+
+ let pb1 = mp.add(ProgressBar::new(10).with_finish(ProgressFinish::AndLeave));
+ let pb2 = mp.add(ProgressBar::new(5));
+ let pb3 = mp.add(ProgressBar::new(100));
+
+ assert_eq!(in_mem.contents(), String::new());
+
+ pb1.tick();
+ assert_eq!(
+ in_mem.contents(),
+ r#"░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10"#
+ );
+
+ pb2.tick();
+
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10
+░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/5"#
+ .trim_start()
+ );
+
+ pb3.tick();
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10
+░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/5
+░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/100"#
+ .trim_start()
+ );
+
+ drop(pb1);
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+██████████████████████████████████████████████████████████████████████████ 10/10
+░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/5
+░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/100"#
+ .trim_start()
+ );
+
+ drop(pb2);
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+██████████████████████████████████████████████████████████████████████████ 10/10
+░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/100"#
+ .trim_start()
+ );
+
+ drop(pb3);
+
+ assert_eq!(
+ in_mem.contents(),
+ r#"██████████████████████████████████████████████████████████████████████████ 10/10"#
+ );
+}
+
+#[test]
+fn multi_progress_println() {
+ let in_mem = InMemoryTerm::new(10, 80);
+ let mp =
+ MultiProgress::with_draw_target(ProgressDrawTarget::term_like(Box::new(in_mem.clone())));
+
+ let pb1 = mp.add(ProgressBar::new(10));
+ let pb2 = mp.add(ProgressBar::new(5));
+ let pb3 = mp.add(ProgressBar::new(100));
+
+ assert_eq!(in_mem.contents(), "");
+
+ pb1.inc(2);
+ mp.println("message printed :)").unwrap();
+
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+message printed :)
+███████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 2/10
+ "#
+ .trim()
+ );
+
+ mp.println("another great message!").unwrap();
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+message printed :)
+another great message!
+███████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 2/10
+ "#
+ .trim()
+ );
+
+ pb2.inc(1);
+ pb3.tick();
+ mp.println("one last message").unwrap();
+
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+message printed :)
+another great message!
+one last message
+███████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 2/10
+███████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 1/5
+░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/100
+ "#
+ .trim()
+ );
+
+ drop(pb1);
+ drop(pb2);
+ drop(pb3);
+
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+message printed :)
+another great message!
+one last message"#
+ .trim()
+ );
+}
+
+#[test]
+fn multi_progress_suspend() {
+ let in_mem = InMemoryTerm::new(10, 80);
+ let mp =
+ MultiProgress::with_draw_target(ProgressDrawTarget::term_like(Box::new(in_mem.clone())));
+
+ let pb1 = mp.add(ProgressBar::new(10));
+ let pb2 = mp.add(ProgressBar::new(10));
+
+ assert_eq!(in_mem.contents(), "");
+
+ pb1.inc(2);
+ mp.println("message printed :)").unwrap();
+
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+message printed :)
+███████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 2/10
+ "#
+ .trim()
+ );
+
+ mp.suspend(|| {
+ in_mem.write_line("This is write_line output!").unwrap();
+ in_mem.write_line("And so is this").unwrap();
+ in_mem.move_cursor_down(1).unwrap();
+ });
+
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+message printed :)
+This is write_line output!
+And so is this
+
+███████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 2/10
+ "#
+ .trim()
+ );
+
+ pb2.inc(1);
+ mp.println("Another line printed").unwrap();
+
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+message printed :)
+This is write_line output!
+And so is this
+
+Another line printed
+███████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 2/10
+███████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 1/10
+ "#
+ .trim()
+ );
+
+ drop(pb1);
+ drop(pb2);
+
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+message printed :)
+This is write_line output!
+And so is this
+
+Another line printed"#
+ .trim()
+ );
+}
+
+#[test]
+fn ticker_drop() {
+ let in_mem = InMemoryTerm::new(10, 80);
+ let mp =
+ MultiProgress::with_draw_target(ProgressDrawTarget::term_like(Box::new(in_mem.clone())));
+
+ let mut spinner: Option<ProgressBar> = None;
+
+ for i in 0..5 {
+ let new_spinner = mp.add(
+ ProgressBar::new_spinner()
+ .with_finish(ProgressFinish::AndLeave)
+ .with_message(format!("doing stuff {i}")),
+ );
+ new_spinner.enable_steady_tick(Duration::from_millis(100));
+ spinner.replace(new_spinner);
+ }
+
+ drop(spinner);
+ assert_eq!(
+ in_mem.contents(),
+ " doing stuff 0\n doing stuff 1\n doing stuff 2\n doing stuff 3\n doing stuff 4"
+ );
+}
+
+#[test]
+fn manually_inc_ticker() {
+ let in_mem = InMemoryTerm::new(10, 80);
+ let mp =
+ MultiProgress::with_draw_target(ProgressDrawTarget::term_like(Box::new(in_mem.clone())));
+
+ let spinner = mp.add(ProgressBar::new_spinner().with_message("msg"));
+
+ assert_eq!(in_mem.contents(), "");
+
+ spinner.inc(1);
+ assert_eq!(in_mem.contents(), "⠁ msg");
+
+ spinner.inc(1);
+ assert_eq!(in_mem.contents(), "⠉ msg");
+
+ // set_message / set_prefix shouldn't increase tick
+ spinner.set_message("new message");
+ spinner.set_prefix("prefix");
+ assert_eq!(in_mem.contents(), "⠉ new message");
+}
+
+#[test]
+fn multi_progress_prune_zombies() {
+ let in_mem = InMemoryTerm::new(10, 80);
+ let mp =
+ MultiProgress::with_draw_target(ProgressDrawTarget::term_like(Box::new(in_mem.clone())));
+
+ let pb0 = mp
+ .add(ProgressBar::new(10))
+ .with_finish(ProgressFinish::AndLeave);
+ let pb1 = mp.add(ProgressBar::new(15));
+ pb0.tick();
+ assert_eq!(
+ in_mem.contents(),
+ "░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10"
+ );
+
+ pb0.inc(1);
+ assert_eq!(
+ in_mem.contents(),
+ "███████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 1/10"
+ );
+
+ drop(pb0);
+
+ // Clear the screen
+ mp.clear().unwrap();
+
+ // Write a line that we expect to remain. This helps ensure the adjustment to last_line_count is
+ // working as expected, and `MultiState` isn't erasing lines when it shouldn't.
+ in_mem.write_line("don't erase me plz").unwrap();
+
+ // pb0 is dead, so only pb1 should be drawn from now on
+ pb1.tick();
+ assert_eq!(
+ in_mem.contents(),
+ "don't erase me plz\n░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/15"
+ );
+}
+
+#[test]
+fn multi_progress_prune_zombies_2() {
+ let in_mem = InMemoryTerm::new(10, 80);
+ let mp =
+ MultiProgress::with_draw_target(ProgressDrawTarget::term_like(Box::new(in_mem.clone())));
+
+ let pb1 = mp.add(ProgressBar::new(10).with_finish(ProgressFinish::AndLeave));
+ let pb2 = mp.add(ProgressBar::new(5));
+ let pb3 = mp
+ .add(ProgressBar::new(100))
+ .with_finish(ProgressFinish::Abandon);
+ let pb4 = mp
+ .add(ProgressBar::new(500))
+ .with_finish(ProgressFinish::AndLeave);
+ let pb5 = mp.add(ProgressBar::new(7));
+
+ assert_eq!(in_mem.contents(), String::new());
+
+ pb1.tick();
+ assert_eq!(
+ in_mem.contents(),
+ r#"░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10"#
+ );
+
+ pb2.tick();
+
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10
+░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/5"#
+ .trim_start()
+ );
+
+ pb3.tick();
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10
+░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/5
+░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/100"#
+ .trim_start()
+ );
+
+ drop(pb1);
+ drop(pb2);
+ drop(pb3);
+
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+██████████████████████████████████████████████████████████████████████████ 10/10
+░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/100"#
+ .trim_start()
+ );
+
+ mp.clear().unwrap();
+
+ assert_eq!(in_mem.contents(), "");
+
+ // A sacrificial line we expect shouldn't be touched
+ in_mem.write_line("don't erase plz").unwrap();
+
+ mp.println("Test friend :)").unwrap();
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+don't erase plz
+Test friend :)"#
+ .trim_start()
+ );
+
+ pb4.tick();
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+don't erase plz
+Test friend :)
+░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/500"#
+ .trim_start()
+ );
+
+ drop(pb4);
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+don't erase plz
+Test friend :)
+████████████████████████████████████████████████████████████████████████ 500/500"#
+ .trim_start()
+ );
+
+ mp.clear().unwrap();
+ assert_eq!(in_mem.contents(), "don't erase plz\nTest friend :)");
+
+ pb5.tick();
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+don't erase plz
+Test friend :)
+░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/7"#
+ .trim_start()
+ );
+
+ mp.println("not your friend, buddy").unwrap();
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+don't erase plz
+Test friend :)
+not your friend, buddy
+░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/7"#
+ .trim_start()
+ );
+
+ pb5.inc(1);
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+don't erase plz
+Test friend :)
+not your friend, buddy
+██████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 1/7"#
+ .trim_start()
+ );
+
+ mp.clear().unwrap();
+ in_mem.write_line("don't erase me either").unwrap();
+
+ pb5.inc(1);
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+don't erase plz
+Test friend :)
+not your friend, buddy
+don't erase me either
+█████████████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 2/7"#
+ .trim_start()
+ );
+
+ drop(pb5);
+
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+don't erase plz
+Test friend :)
+not your friend, buddy
+don't erase me either"#
+ .trim_start()
+ );
+}
+
+#[test]
+fn basic_tab_expansion() {
+ let in_mem = InMemoryTerm::new(10, 80);
+ let mp =
+ MultiProgress::with_draw_target(ProgressDrawTarget::term_like(Box::new(in_mem.clone())));
+
+ let mut spinner = mp.add(ProgressBar::new_spinner().with_message("Test\t:)"));
+ spinner.tick();
+
+ // 8 is the default number of spaces
+ assert_eq!(in_mem.contents(), "⠁ Test :)");
+
+ spinner.set_tab_width(4);
+ assert_eq!(in_mem.contents(), "⠁ Test :)");
+}
+
+#[test]
+fn tab_expansion_in_template() {
+ let in_mem = InMemoryTerm::new(10, 80);
+ let mp =
+ MultiProgress::with_draw_target(ProgressDrawTarget::term_like(Box::new(in_mem.clone())));
+
+ let mut spinner = mp.add(
+ ProgressBar::new_spinner()
+ .with_message("Test\t:)")
+ .with_prefix("Pre\tfix!")
+ .with_style(ProgressStyle::with_template("{spinner}{prefix}\t{msg}").unwrap()),
+ );
+
+ spinner.tick();
+ assert_eq!(in_mem.contents(), "⠁Pre fix! Test :)");
+
+ spinner.set_tab_width(4);
+ assert_eq!(in_mem.contents(), "⠁Pre fix! Test :)");
+
+ spinner.set_tab_width(2);
+ assert_eq!(in_mem.contents(), "⠁Pre fix! Test :)");
+}
+
+#[test]
+fn progress_style_tab_width_unification() {
+ let in_mem = InMemoryTerm::new(10, 80);
+ let mp =
+ MultiProgress::with_draw_target(ProgressDrawTarget::term_like(Box::new(in_mem.clone())));
+
+ // Style will have default of 8 spaces for tabs
+ let style = ProgressStyle::with_template("{msg}\t{msg}").unwrap();
+
+ let spinner = mp.add(
+ ProgressBar::new_spinner()
+ .with_message("OK")
+ .with_tab_width(4),
+ );
+
+ // Setting the spinner's style to |style| should override the style's tab width with that of bar
+ spinner.set_style(style);
+ spinner.tick();
+ assert_eq!(in_mem.contents(), "OK OK");
+}
+
+#[test]
+fn multi_progress_clear_println() {
+ let in_mem = InMemoryTerm::new(10, 80);
+ let mp =
+ MultiProgress::with_draw_target(ProgressDrawTarget::term_like(Box::new(in_mem.clone())));
+
+ mp.println("Test of println").unwrap();
+ // Should have no effect
+ mp.clear().unwrap();
+ assert_eq!(in_mem.contents(), "Test of println");
+}
+
+#[test]
+fn multi_progress_clear_zombies_no_ticks() {
+ _multi_progress_clear_zombies(0);
+}
+
+#[test]
+fn multi_progress_clear_zombies_one_tick() {
+ _multi_progress_clear_zombies(1);
+}
+
+#[test]
+fn multi_progress_clear_zombies_two_ticks() {
+ _multi_progress_clear_zombies(2);
+}
+
+// In the old (broken) implementation, zombie handling sometimes worked differently depending on
+// how many draws were between certain operations. Let's make sure that doesn't happen again.
+fn _multi_progress_clear_zombies(ticks: usize) {
+ let in_mem = InMemoryTerm::new(10, 80);
+ let mp =
+ MultiProgress::with_draw_target(ProgressDrawTarget::term_like(Box::new(in_mem.clone())));
+ let style = ProgressStyle::with_template("{msg}").unwrap();
+
+ let pb1 = mp.add(
+ ProgressBar::new_spinner()
+ .with_style(style.clone())
+ .with_message("pb1"),
+ );
+ pb1.tick();
+
+ let pb2 = mp.add(
+ ProgressBar::new_spinner()
+ .with_style(style)
+ .with_message("pb2"),
+ );
+
+ pb2.tick();
+ assert_eq!(in_mem.contents(), "pb1\npb2");
+
+ pb1.finish_with_message("pb1 done");
+ drop(pb1);
+ assert_eq!(in_mem.contents(), "pb1 done\npb2");
+
+ for _ in 0..ticks {
+ pb2.tick();
+ }
+
+ mp.clear().unwrap();
+ assert_eq!(in_mem.contents(), "");
+}
+
+// This test reproduces examples/multi.rs in a simpler form
+#[test]
+fn multi_zombie_handling() {
+ let in_mem = InMemoryTerm::new(10, 80);
+ let mp =
+ MultiProgress::with_draw_target(ProgressDrawTarget::term_like(Box::new(in_mem.clone())));
+ let style = ProgressStyle::with_template("{msg}").unwrap();
+
+ let pb1 = mp.add(
+ ProgressBar::new_spinner()
+ .with_style(style.clone())
+ .with_message("pb1"),
+ );
+ pb1.tick();
+ let pb2 = mp.add(
+ ProgressBar::new_spinner()
+ .with_style(style.clone())
+ .with_message("pb2"),
+ );
+ pb2.tick();
+ let pb3 = mp.add(
+ ProgressBar::new_spinner()
+ .with_style(style)
+ .with_message("pb3"),
+ );
+ pb3.tick();
+
+ mp.println("pb1 done!").unwrap();
+ pb1.finish_with_message("done");
+ assert_eq!(in_mem.contents(), "pb1 done!\ndone\npb2\npb3");
+ drop(pb1);
+
+ assert_eq!(in_mem.contents(), "pb1 done!\ndone\npb2\npb3");
+
+ pb2.tick();
+ assert_eq!(in_mem.contents(), "pb1 done!\ndone\npb2\npb3");
+ pb3.tick();
+ assert_eq!(in_mem.contents(), "pb1 done!\ndone\npb2\npb3");
+
+ mp.println("pb3 done!").unwrap();
+ assert_eq!(in_mem.contents(), "pb1 done!\npb3 done!\npb2\npb3");
+
+ pb3.finish_with_message("done");
+ drop(pb3);
+
+ pb2.tick();
+
+ mp.println("pb2 done!").unwrap();
+ pb2.finish_with_message("done");
+ drop(pb2);
+
+ assert_eq!(
+ in_mem.contents(),
+ "pb1 done!\npb3 done!\npb2 done!\ndone\ndone"
+ );
+
+ mp.clear().unwrap();
+
+ assert_eq!(in_mem.contents(), "pb1 done!\npb3 done!\npb2 done!");
+}
+
+#[test]
+fn multi_progress_multiline_msg() {
+ let in_mem = InMemoryTerm::new(10, 80);
+ let mp =
+ MultiProgress::with_draw_target(ProgressDrawTarget::term_like(Box::new(in_mem.clone())));
+
+ let pb1 = mp.add(ProgressBar::new_spinner().with_message("test1"));
+ let pb2 = mp.add(ProgressBar::new_spinner().with_message("test2"));
+
+ assert_eq!(in_mem.contents(), "");
+
+ pb1.inc(1);
+ pb2.inc(1);
+
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+⠁ test1
+⠁ test2
+ "#
+ .trim()
+ );
+
+ pb1.set_message("test1\n test1 line2\n test1 line3");
+
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+⠁ test1
+ test1 line2
+ test1 line3
+⠁ test2
+ "#
+ .trim()
+ );
+
+ pb1.inc(1);
+ pb2.inc(1);
+
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+⠉ test1
+ test1 line2
+ test1 line3
+⠉ test2
+ "#
+ .trim()
+ );
+
+ pb2.set_message("test2\n test2 line2");
+
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+⠉ test1
+ test1 line2
+ test1 line3
+⠉ test2
+ test2 line2
+ "#
+ .trim()
+ );
+
+ pb1.set_message("single line again");
+
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+⠉ single line again
+⠉ test2
+ test2 line2
+ "#
+ .trim()
+ );
+
+ pb1.finish_with_message("test1 done!");
+ pb2.finish_with_message("test2 done!");
+
+ assert_eq!(
+ in_mem.contents(),
+ r#" test1 done!
+ test2 done!"#
+ );
+}
+
+#[test]
+fn multi_progress_bottom_alignment() {
+ let in_mem = InMemoryTerm::new(10, 80);
+ let mp =
+ MultiProgress::with_draw_target(ProgressDrawTarget::term_like(Box::new(in_mem.clone())));
+ mp.set_alignment(MultiProgressAlignment::Bottom);
+
+ let pb1 = mp.add(ProgressBar::new_spinner().with_message("test1"));
+ let pb2 = mp.add(ProgressBar::new_spinner().with_message("test2"));
+
+ pb1.tick();
+ pb2.tick();
+ pb1.finish_and_clear();
+
+ assert_eq!(in_mem.contents(), "\n⠁ test2");
+
+ pb2.finish_and_clear();
+ // `InMemoryTerm::contents` normally gets rid of trailing newlines, so write some text to ensure
+ // the newlines are seen.
+ in_mem.write_line("anchor").unwrap();
+ assert_eq!(in_mem.contents(), "\n\nanchor");
+}
+
+#[test]
+fn progress_bar_terminal_wrap() {
+ use std::cmp::min;
+ let in_mem = InMemoryTerm::new(10, 20);
+
+ let mut downloaded = 0;
+ let total_size = 231231231;
+
+ let pb = ProgressBar::with_draw_target(
+ None,
+ ProgressDrawTarget::term_like(Box::new(in_mem.clone())),
+ );
+ pb.set_style(ProgressStyle::default_bar()
+ .template("{msg:>12.cyan.bold} {spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {bytes}/{total_bytes}").unwrap()
+ .progress_chars("#>-"));
+
+ pb.set_message("Downloading");
+ assert_eq!(
+ in_mem.contents(),
+ r#" Downloading ⠁ [00:0
+0:00] [-------------
+--------------------
+-------] 0 B/0 B"#
+ );
+
+ let new = min(downloaded + 223211, total_size);
+ downloaded = new;
+ pb.set_position(new);
+ assert_eq!(
+ in_mem.contents(),
+ r#" Downloading ⠁ [00:0
+0:00] [-------------
+--------------------
+-------] 217.98 KiB/
+217.98 KiB"#
+ );
+
+ let new = min(downloaded + 223211, total_size);
+ pb.set_position(new);
+ assert_eq!(
+ in_mem.contents(),
+ r#" Downloading ⠉ [00:0
+0:00] [-------------
+--------------------
+-------] 435.96 KiB/
+435.96 KiB"#
+ );
+
+ pb.set_style(
+ ProgressStyle::default_bar()
+ .template("{msg:>12.green.bold} downloading {total_bytes:.green} in {elapsed:.green}")
+ .unwrap(),
+ );
+ pb.finish_with_message("Finished");
+ assert_eq!(
+ in_mem.contents(),
+ r#" Finished downloa
+ding 435.96 KiB in 0
+s"#
+ );
+
+ println!("{:?}", in_mem.contents())
+}
+
+#[test]
+fn spinner_terminal_cleared_log_line_with_ansi_codes() {
+ let in_mem = InMemoryTerm::new(10, 100);
+
+ let pb = ProgressBar::with_draw_target(
+ Some(10),
+ ProgressDrawTarget::term_like(Box::new(in_mem.clone())),
+ );
+ pb.set_style(ProgressStyle::default_spinner());
+ assert_eq!(in_mem.contents(), String::new());
+
+ pb.finish_and_clear();
+ // Visually empty, but consists of an ANSII code
+ pb.println("\u{1b}[1m");
+
+ pb.println("text\u{1b}[0m");
+ assert_eq!(in_mem.contents(), "\ntext");
+}
+
+#[test]
+fn multi_progress_println_terminal_wrap() {
+ let in_mem = InMemoryTerm::new(10, 48);
+ let mp =
+ MultiProgress::with_draw_target(ProgressDrawTarget::term_like(Box::new(in_mem.clone())));
+
+ let pb1 = mp.add(ProgressBar::new(10));
+ let pb2 = mp.add(ProgressBar::new(5));
+ let pb3 = mp.add(ProgressBar::new(100));
+
+ assert_eq!(in_mem.contents(), "");
+
+ pb1.inc(2);
+ mp.println("message printed that is longer than terminal width :)")
+ .unwrap();
+ assert_eq!(
+ in_mem.contents(),
+ r#"message printed that is longer than terminal wid
+th :)
+████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 2/10"#
+ );
+
+ mp.println("another great message!").unwrap();
+ assert_eq!(
+ in_mem.contents(),
+ r#"message printed that is longer than terminal wid
+th :)
+another great message!
+████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 2/10"#
+ );
+
+ pb2.inc(1);
+ pb3.tick();
+ mp.println("one last message but this one is also longer than terminal width")
+ .unwrap();
+
+ assert_eq!(
+ in_mem.contents(),
+ r#"message printed that is longer than terminal wid
+th :)
+another great message!
+one last message but this one is also longer tha
+n terminal width
+████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 2/10
+████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 1/5
+░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/100"#
+ .trim()
+ );
+
+ drop(pb1);
+ drop(pb2);
+ drop(pb3);
+
+ assert_eq!(
+ in_mem.contents(),
+ r#"message printed that is longer than terminal wid
+th :)
+another great message!
+one last message but this one is also longer tha
+n terminal width"#
+ .trim()
+ );
+}
+
+#[test]
+fn basic_progress_bar_newline() {
+ let in_mem = InMemoryTerm::new(10, 80);
+ let pb = ProgressBar::with_draw_target(
+ Some(10),
+ ProgressDrawTarget::term_like(Box::new(in_mem.clone())),
+ );
+
+ assert_eq!(in_mem.contents(), String::new());
+
+ pb.println("\nhello");
+ pb.tick();
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+hello
+░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10"#
+ );
+
+ pb.inc(1);
+ pb.println("");
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+hello
+
+███████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 1/10"#
+ );
+
+ pb.finish();
+ assert_eq!(
+ in_mem.contents(),
+ "
+hello
+
+██████████████████████████████████████████████████████████████████████████ 10/10"
+ );
+}
+
+#[test]
+fn multi_progress_many_bars() {
+ let in_mem = InMemoryTerm::new(4, 80);
+ let mp =
+ MultiProgress::with_draw_target(ProgressDrawTarget::term_like(Box::new(in_mem.clone())));
+
+ let pb1 = mp.add(ProgressBar::new(10).with_finish(ProgressFinish::AndLeave));
+ let mut spinners = vec![];
+ for i in 0..7 {
+ let spinner = ProgressBar::new_spinner().with_message(i.to_string());
+ mp.add(spinner.clone());
+ spinners.push(spinner);
+ }
+
+ assert_eq!(in_mem.contents(), String::new());
+
+ pb1.tick();
+ assert_eq!(
+ in_mem.contents(),
+ r#"░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10"#
+ );
+ assert_eq!(
+ in_mem.moves_since_last_check(),
+ r#"Str("░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10")
+Str("")
+Flush
+"#
+ );
+
+ for spinner in &spinners {
+ spinner.tick()
+ }
+
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10
+⠁ 0
+⠁ 1
+⠁ 2"#
+ .trim_start()
+ );
+ assert_eq!(
+ in_mem.moves_since_last_check(),
+ r#"Clear
+Str("░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10")
+Str("")
+NewLine
+Str("⠁ 0")
+Str(" ")
+Flush
+Up(1)
+Clear
+Down(1)
+Clear
+Up(1)
+Str("░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10")
+Str("")
+NewLine
+Str("⠁ 0")
+Str("")
+NewLine
+Str("⠁ 1")
+Str(" ")
+Flush
+Up(2)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Up(2)
+Str("░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10")
+Str("")
+NewLine
+Str("⠁ 0")
+Str("")
+NewLine
+Str("⠁ 1")
+Str("")
+NewLine
+Str("⠁ 2")
+Str(" ")
+Flush
+Up(3)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Up(3)
+Str("░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10")
+Str("")
+NewLine
+Str("⠁ 0")
+Str("")
+NewLine
+Str("⠁ 1")
+Str("")
+NewLine
+Str("⠁ 2")
+Str("")
+Flush
+Up(3)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Up(3)
+Str("░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10")
+Str("")
+NewLine
+Str("⠁ 0")
+Str("")
+NewLine
+Str("⠁ 1")
+Str("")
+NewLine
+Str("⠁ 2")
+Str("")
+Flush
+Up(3)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Up(3)
+Str("░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10")
+Str("")
+NewLine
+Str("⠁ 0")
+Str("")
+NewLine
+Str("⠁ 1")
+Str("")
+NewLine
+Str("⠁ 2")
+Str("")
+Flush
+Up(3)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Up(3)
+Str("░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10")
+Str("")
+NewLine
+Str("⠁ 0")
+Str("")
+NewLine
+Str("⠁ 1")
+Str("")
+NewLine
+Str("⠁ 2")
+Str("")
+Flush
+"#
+ );
+
+ drop(pb1);
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+██████████████████████████████████████████████████████████████████████████ 10/10
+⠁ 0
+⠁ 1
+⠁ 2"#
+ .trim_start()
+ );
+ assert_eq!(
+ in_mem.moves_since_last_check(),
+ r#"Up(3)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Up(3)
+Str("██████████████████████████████████████████████████████████████████████████ 10/10")
+Str("")
+NewLine
+Str("⠁ 0")
+Str("")
+NewLine
+Str("⠁ 1")
+Str("")
+NewLine
+Str("⠁ 2")
+Str("")
+Flush
+"#
+ );
+
+ drop(spinners);
+
+ assert_eq!(in_mem.contents(), r#""#);
+ assert_eq!(
+ in_mem.moves_since_last_check(),
+ r#"Up(2)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Up(2)
+Str("⠁ 1")
+Str("")
+NewLine
+Str("⠁ 2")
+Str("")
+NewLine
+Str("⠁ 3")
+Str("")
+NewLine
+Str("⠁ 4")
+Str("")
+Flush
+Up(3)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Up(3)
+Str("⠁ 2")
+Str("")
+NewLine
+Str("⠁ 3")
+Str("")
+NewLine
+Str("⠁ 4")
+Str("")
+NewLine
+Str("⠁ 5")
+Str("")
+Flush
+Up(3)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Up(3)
+Str("⠁ 3")
+Str("")
+NewLine
+Str("⠁ 4")
+Str("")
+NewLine
+Str("⠁ 5")
+Str("")
+NewLine
+Str("⠁ 6")
+Str(" ")
+Flush
+Up(3)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Up(3)
+Str("⠁ 4")
+Str("")
+NewLine
+Str("⠁ 5")
+Str("")
+NewLine
+Str("⠁ 6")
+Str(" ")
+Flush
+Up(2)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Up(2)
+Str("⠁ 5")
+Str("")
+NewLine
+Str("⠁ 6")
+Str(" ")
+Flush
+Up(1)
+Clear
+Down(1)
+Clear
+Up(1)
+Str("⠁ 6")
+Str(" ")
+Flush
+Clear
+Str("")
+Flush
+"#
+ );
+}
+
+#[test]
+fn multi_progress_many_spinners() {
+ let in_mem = InMemoryTerm::new(4, 80);
+ let mp =
+ MultiProgress::with_draw_target(ProgressDrawTarget::term_like(Box::new(in_mem.clone())));
+
+ let pb1 = mp.add(ProgressBar::new(10).with_finish(ProgressFinish::AndLeave));
+ let mut spinners = vec![];
+ for i in 0..7 {
+ let spinner = ProgressBar::new_spinner().with_message(i.to_string());
+ mp.add(spinner.clone());
+ spinners.push(spinner);
+ }
+
+ assert_eq!(in_mem.contents(), String::new());
+
+ pb1.tick();
+ assert_eq!(
+ in_mem.contents(),
+ r#"░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10"#
+ );
+ assert_eq!(
+ in_mem.moves_since_last_check(),
+ r#"Str("░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10")
+Str("")
+Flush
+"#
+ );
+
+ for spinner in &spinners {
+ spinner.tick()
+ }
+
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10
+⠁ 0
+⠁ 1
+⠁ 2"#
+ .trim_start()
+ );
+
+ assert_eq!(
+ in_mem.moves_since_last_check(),
+ r#"Clear
+Str("░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10")
+Str("")
+NewLine
+Str("⠁ 0")
+Str(" ")
+Flush
+Up(1)
+Clear
+Down(1)
+Clear
+Up(1)
+Str("░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10")
+Str("")
+NewLine
+Str("⠁ 0")
+Str("")
+NewLine
+Str("⠁ 1")
+Str(" ")
+Flush
+Up(2)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Up(2)
+Str("░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10")
+Str("")
+NewLine
+Str("⠁ 0")
+Str("")
+NewLine
+Str("⠁ 1")
+Str("")
+NewLine
+Str("⠁ 2")
+Str(" ")
+Flush
+Up(3)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Up(3)
+Str("░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10")
+Str("")
+NewLine
+Str("⠁ 0")
+Str("")
+NewLine
+Str("⠁ 1")
+Str("")
+NewLine
+Str("⠁ 2")
+Str("")
+Flush
+Up(3)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Up(3)
+Str("░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10")
+Str("")
+NewLine
+Str("⠁ 0")
+Str("")
+NewLine
+Str("⠁ 1")
+Str("")
+NewLine
+Str("⠁ 2")
+Str("")
+Flush
+Up(3)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Up(3)
+Str("░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10")
+Str("")
+NewLine
+Str("⠁ 0")
+Str("")
+NewLine
+Str("⠁ 1")
+Str("")
+NewLine
+Str("⠁ 2")
+Str("")
+Flush
+Up(3)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Up(3)
+Str("░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10")
+Str("")
+NewLine
+Str("⠁ 0")
+Str("")
+NewLine
+Str("⠁ 1")
+Str("")
+NewLine
+Str("⠁ 2")
+Str("")
+Flush
+"#
+ );
+
+ spinners.remove(3);
+
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10
+⠁ 0
+⠁ 1
+⠁ 2"#
+ .trim_start()
+ );
+
+ assert_eq!(
+ in_mem.moves_since_last_check(),
+ r#"Up(3)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Up(3)
+Str("░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10")
+Str("")
+NewLine
+Str("⠁ 0")
+Str("")
+NewLine
+Str("⠁ 1")
+Str("")
+NewLine
+Str("⠁ 2")
+Str("")
+Flush
+"#
+ );
+
+ spinners.remove(4);
+
+ assert_eq!(
+ in_mem.contents(),
+ r#"
+░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10
+⠁ 0
+⠁ 1
+⠁ 2"#
+ .trim_start()
+ );
+ assert_eq!(
+ in_mem.moves_since_last_check(),
+ r#"Up(3)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Up(3)
+Str("░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10")
+Str("")
+NewLine
+Str("⠁ 0")
+Str("")
+NewLine
+Str("⠁ 1")
+Str("")
+NewLine
+Str("⠁ 2")
+Str("")
+Flush
+"#
+ );
+
+ drop(spinners);
+
+ assert_eq!(
+ in_mem.contents(),
+ r#"░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10"#
+ );
+ assert_eq!(
+ in_mem.moves_since_last_check(),
+ r#"Up(3)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Up(3)
+Str("░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10")
+Str("")
+NewLine
+Str("⠁ 1")
+Str("")
+NewLine
+Str("⠁ 2")
+Str("")
+NewLine
+Str("⠁ 4")
+Str("")
+Flush
+Up(3)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Up(3)
+Str("░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10")
+Str("")
+NewLine
+Str("⠁ 2")
+Str("")
+NewLine
+Str("⠁ 4")
+Str("")
+NewLine
+Str("⠁ 6")
+Str(" ")
+Flush
+Up(3)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Up(3)
+Str("░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10")
+Str("")
+NewLine
+Str("⠁ 4")
+Str("")
+NewLine
+Str("⠁ 6")
+Str(" ")
+Flush
+Up(2)
+Clear
+Down(1)
+Clear
+Down(1)
+Clear
+Up(2)
+Str("░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10")
+Str("")
+NewLine
+Str("⠁ 6")
+Str(" ")
+Flush
+Up(1)
+Clear
+Down(1)
+Clear
+Up(1)
+Str("░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/10")
+Str("")
+Flush
+"#
+ );
+}
+
+#[test]
+fn orphan_lines() {
+ let in_mem = InMemoryTerm::new(10, 80);
+
+ let pb = ProgressBar::with_draw_target(
+ Some(10),
+ ProgressDrawTarget::term_like(Box::new(in_mem.clone())),
+ );
+ assert_eq!(in_mem.contents(), String::new());
+
+ for i in 0..=10 {
+ if i != 0 {
+ pb.inc(1);
+ }
+
+ let n = 5 + i;
+
+ pb.println("\n".repeat(n));
+ }
+
+ pb.finish();
+}
+
+#[test]
+fn orphan_lines_message_above_progress_bar() {
+ let in_mem = InMemoryTerm::new(10, 80);
+
+ let pb = ProgressBar::with_draw_target(
+ Some(10),
+ ProgressDrawTarget::term_like(Box::new(in_mem.clone())),
+ );
+ assert_eq!(in_mem.contents(), String::new());
+
+ for i in 0..=10 {
+ if i != 0 {
+ pb.inc(1);
+ }
+
+ let n = 5 + i;
+
+ // Test with messages of differing numbers of lines. The messages have the form:
+ // n - 1 newlines followed by n * 11 dashes (`-`). The value of n ranges from 5
+ // (less than the terminal height) to 15 (greater than the terminal height). The
+ // number 11 is intentionally not a factor of the terminal width (80), but large
+ // enough that the strings of dashes eventually wrap.
+ pb.println(format!("{}{}", "\n".repeat(n - 1), "-".repeat(n * 11)));
+
+ // Check that the line above the progress bar is a string of dashes of length
+ // n * 11 mod the terminal width.
+ assert_eq!(
+ format!("{}", "-".repeat(n * 11 % 80)),
+ in_mem.contents().lines().rev().nth(1).unwrap(),
+ );
+ }
+
+ pb.finish();
+}