[feat] add robust progress management utilities and new tests
This commit is contained in:
91
tests/progress.rs
Normal file
91
tests/progress.rs
Normal file
@@ -0,0 +1,91 @@
|
||||
use polyscribe::progress::{ProgressFactory, ProgressMode, SelectionInput, select_mode, ProgressManager};
|
||||
|
||||
#[test]
|
||||
fn test_factory_decide_mode_none_when_disabled() {
|
||||
let pf = ProgressFactory::new(true); // force disabled
|
||||
assert!(matches!(pf.decide_mode(0), ProgressMode::None));
|
||||
assert!(matches!(pf.decide_mode(1), ProgressMode::None));
|
||||
assert!(matches!(pf.decide_mode(2), ProgressMode::None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_select_mode_zero_inputs_is_none() {
|
||||
let si = SelectionInput {
|
||||
inputs_len: 0,
|
||||
no_progress_flag: false,
|
||||
stderr_tty_override: Some(true),
|
||||
env_no_progress: false,
|
||||
};
|
||||
let (enabled, mode) = select_mode(si);
|
||||
assert!(enabled);
|
||||
assert!(matches!(mode, ProgressMode::None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_select_mode_one_input_is_single() {
|
||||
let si = SelectionInput {
|
||||
inputs_len: 1,
|
||||
no_progress_flag: false,
|
||||
stderr_tty_override: Some(true),
|
||||
env_no_progress: false,
|
||||
};
|
||||
let (enabled, mode) = select_mode(si);
|
||||
assert!(enabled);
|
||||
assert!(matches!(mode, ProgressMode::Single));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_select_mode_multi_inputs_is_multi() {
|
||||
let si = SelectionInput {
|
||||
inputs_len: 3,
|
||||
no_progress_flag: false,
|
||||
stderr_tty_override: Some(true),
|
||||
env_no_progress: false,
|
||||
};
|
||||
let (enabled, mode) = select_mode(si);
|
||||
assert!(enabled);
|
||||
match mode {
|
||||
ProgressMode::Multi { total_inputs } => assert_eq!(total_inputs, 3),
|
||||
_ => panic!("expected multi mode"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_env_no_progress_disables() {
|
||||
// Simulate env flag influence by passing env_no_progress=true
|
||||
unsafe { std::env::set_var("NO_PROGRESS", "1"); }
|
||||
let si = SelectionInput {
|
||||
inputs_len: 5,
|
||||
no_progress_flag: false,
|
||||
stderr_tty_override: Some(true),
|
||||
env_no_progress: true,
|
||||
};
|
||||
let (enabled, mode) = select_mode(si);
|
||||
assert!(!enabled);
|
||||
assert!(matches!(mode, ProgressMode::None));
|
||||
unsafe { std::env::remove_var("NO_PROGRESS"); }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_completed_never_exceeds_total_and_item_updates_do_not_affect_total() {
|
||||
// create hidden multiprogress for tests
|
||||
let pm = ProgressManager::new_for_tests_multi_hidden(3);
|
||||
pm.set_total(3);
|
||||
// Start an item and update progress a few times
|
||||
let item = pm.start_item("Test item");
|
||||
item.set_progress(0.1);
|
||||
item.set_progress(0.4);
|
||||
item.set_message("stage1");
|
||||
// Ensure total unchanged
|
||||
let (pos, len) = pm.total_state_for_tests().unwrap();
|
||||
assert_eq!(len, 3);
|
||||
assert_eq!(pos, 0);
|
||||
// Mark 4 times completed, but expect clamp at 3
|
||||
pm.inc_completed();
|
||||
pm.inc_completed();
|
||||
pm.inc_completed();
|
||||
pm.inc_completed();
|
||||
let (pos, len) = pm.total_state_for_tests().unwrap();
|
||||
assert_eq!(len, 3);
|
||||
assert_eq!(pos, 3);
|
||||
}
|
30
tests/progress_manager.rs
Normal file
30
tests/progress_manager.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use polyscribe::progress::ProgressManager;
|
||||
|
||||
#[test]
|
||||
fn test_total_and_completed_clamp() {
|
||||
let pm = ProgressManager::new_for_tests_multi_hidden(3);
|
||||
pm.set_total(3);
|
||||
pm.inc_completed();
|
||||
pm.inc_completed();
|
||||
pm.inc_completed();
|
||||
// Extra increments should not exceed total
|
||||
pm.inc_completed();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_start_item_does_not_change_total() {
|
||||
let pm = ProgressManager::new_for_tests_multi_hidden(2);
|
||||
pm.set_total(2);
|
||||
let item = pm.start_item("file1");
|
||||
item.set_progress(0.5);
|
||||
// No panic; total bar position should be unaffected. We cannot introspect position without
|
||||
// exposing internals; this test ensures API usability without side effects.
|
||||
item.finish_with("done");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pause_and_resume_prompt() {
|
||||
let pm = ProgressManager::test_new_multi(1);
|
||||
pm.pause_for_prompt();
|
||||
pm.resume_after_prompt();
|
||||
}
|
86
tests/prompt_spacing.rs
Normal file
86
tests/prompt_spacing.rs
Normal file
@@ -0,0 +1,86 @@
|
||||
use std::io::Write as _;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
fn manifest_path(rel: &str) -> std::path::PathBuf {
|
||||
let mut p = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
p.push(rel);
|
||||
p
|
||||
}
|
||||
|
||||
fn collect_stderr_lines(output: &std::process::Output) -> Vec<String> {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
stderr.lines().map(|s| s.to_string()).collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn speaker_prompt_spacing_single_vs_multi_is_consistent() {
|
||||
let exe = env!("CARGO_BIN_EXE_polyscribe");
|
||||
let input1 = manifest_path("input/1-s0wlz.json");
|
||||
let input2 = manifest_path("input/2-vikingowl.json");
|
||||
|
||||
// Single mode
|
||||
let mut child1 = Command::new(exe)
|
||||
.arg(input1.as_os_str())
|
||||
.arg("--set-speaker-names")
|
||||
.arg("-m")
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()
|
||||
.expect("failed to spawn polyscribe (single)");
|
||||
{
|
||||
let s = child1.stdin.as_mut().unwrap();
|
||||
writeln!(s, "Alpha").unwrap();
|
||||
}
|
||||
let out1 = child1.wait_with_output().unwrap();
|
||||
assert!(out1.status.success());
|
||||
let lines1 = collect_stderr_lines(&out1);
|
||||
|
||||
// Multi mode
|
||||
let mut child2 = Command::new(exe)
|
||||
.arg(input1.as_os_str())
|
||||
.arg(input2.as_os_str())
|
||||
.arg("--set-speaker-names")
|
||||
.arg("-m")
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()
|
||||
.expect("failed to spawn polyscribe (multi)");
|
||||
{
|
||||
let s = child2.stdin.as_mut().unwrap();
|
||||
writeln!(s, "Alpha").unwrap();
|
||||
writeln!(s, "Beta").unwrap();
|
||||
}
|
||||
let out2 = child2.wait_with_output().unwrap();
|
||||
assert!(out2.status.success());
|
||||
let lines2 = collect_stderr_lines(&out2);
|
||||
|
||||
// Helper to count blank separators around echo block
|
||||
fn analyze(lines: &[String]) -> (usize, usize, usize) {
|
||||
// count: prompts, blanks, echoes (either legacy "Speaker for " or new mapping lines starting with " - ")
|
||||
let mut prompts = 0;
|
||||
let mut blanks = 0;
|
||||
let mut echoes = 0;
|
||||
for l in lines {
|
||||
if l.starts_with("Enter speaker name for ") { prompts += 1; }
|
||||
if l.trim().is_empty() { blanks += 1; }
|
||||
if l.starts_with("Speaker for ") || l.starts_with(" - ") { echoes += 1; }
|
||||
}
|
||||
(prompts, blanks, echoes)
|
||||
}
|
||||
|
||||
let (p1, b1, e1) = analyze(&lines1);
|
||||
let (p2, b2, e2) = analyze(&lines2);
|
||||
|
||||
// Expect one prompt/echo for single, two for multi
|
||||
assert_eq!(p1, 1);
|
||||
assert_eq!(e1, 1);
|
||||
assert_eq!(p2, 2);
|
||||
assert_eq!(e2, 2);
|
||||
|
||||
// Each mode should have exactly two blank separators: one between prompts and echoes and one after echoes
|
||||
// Note: other logs may be absent in tests; we count exactly 2 blanks for single and multi here
|
||||
assert!(b1 >= 2, "expected at least two blank separators in single mode, got {}: {:?}", b1, lines1);
|
||||
assert!(b2 >= 2, "expected at least two blank separators in multi mode, got {}: {:?}", b2, lines2);
|
||||
}
|
Reference in New Issue
Block a user