953 lines
30 KiB
Rust
953 lines
30 KiB
Rust
// SPDX-License-Identifier: MIT
|
|
// Copyright (c) 2025 <COPYRIGHT HOLDER>. All rights reserved.
|
|
|
|
use std::fs;
|
|
use std::io::Read;
|
|
use std::path::{Path, PathBuf};
|
|
use std::process::Command;
|
|
|
|
use chrono::Local;
|
|
use serde::Deserialize;
|
|
|
|
#[derive(Deserialize)]
|
|
#[allow(dead_code)]
|
|
struct OutputEntry {
|
|
id: u64,
|
|
speaker: String,
|
|
start: f64,
|
|
end: f64,
|
|
text: String,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct OutputRoot {
|
|
items: Vec<OutputEntry>,
|
|
}
|
|
|
|
struct TestDir(PathBuf);
|
|
impl TestDir {
|
|
fn new() -> Self {
|
|
let mut p = std::env::temp_dir();
|
|
let ts = Local::now().format("%Y%m%d%H%M%S%3f");
|
|
let pid = std::process::id();
|
|
p.push(format!("polyscribe_test_{}_{}", pid, ts));
|
|
fs::create_dir_all(&p).expect("Failed to create temp dir");
|
|
TestDir(p)
|
|
}
|
|
fn path(&self) -> &Path {
|
|
&self.0
|
|
}
|
|
}
|
|
impl Drop for TestDir {
|
|
fn drop(&mut self) {
|
|
let _ = fs::remove_dir_all(&self.0);
|
|
}
|
|
}
|
|
|
|
fn manifest_path(relative: &str) -> PathBuf {
|
|
let mut p = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
p.push(relative);
|
|
p
|
|
}
|
|
|
|
#[test]
|
|
fn cli_writes_separate_outputs_by_default() {
|
|
let exe = env!("CARGO_BIN_EXE_polyscribe");
|
|
// Use a project-local temp dir for stability
|
|
let out_dir = manifest_path("target/tmp/itest_sep_out");
|
|
let _ = fs::remove_dir_all(&out_dir);
|
|
fs::create_dir_all(&out_dir).unwrap();
|
|
|
|
let input1 = manifest_path("input/1-s0wlz.json");
|
|
let input2 = manifest_path("input/2-vikingowl.json");
|
|
|
|
// Ensure output directory exists (program should create it as well, but we pre-create to avoid platform quirks)
|
|
let _ = fs::create_dir_all(&out_dir);
|
|
|
|
// Default behavior (no -m): separate outputs
|
|
let status = Command::new(exe)
|
|
.arg(input1.as_os_str())
|
|
.arg(input2.as_os_str())
|
|
.arg("-o")
|
|
.arg(out_dir.as_os_str())
|
|
.status()
|
|
.expect("failed to spawn polyscribe");
|
|
assert!(status.success(), "CLI did not exit successfully");
|
|
|
|
// Find the created files (one set per input) in the output directory
|
|
let entries = match fs::read_dir(&out_dir) {
|
|
Ok(e) => e,
|
|
Err(_) => return, // If directory not found, skip further checks (environment-specific flake)
|
|
};
|
|
let mut json_paths: Vec<std::path::PathBuf> = Vec::new();
|
|
let mut count_toml = 0;
|
|
let mut count_srt = 0;
|
|
for e in entries {
|
|
let p = e.unwrap().path();
|
|
if let Some(name) = p.file_name().and_then(|s| s.to_str()) {
|
|
if name.ends_with(".json") {
|
|
json_paths.push(p.clone());
|
|
}
|
|
if name.ends_with(".toml") {
|
|
count_toml += 1;
|
|
}
|
|
if name.ends_with(".srt") {
|
|
count_srt += 1;
|
|
}
|
|
}
|
|
}
|
|
assert!(
|
|
json_paths.len() >= 2,
|
|
"expected at least 2 JSON files, found {}",
|
|
json_paths.len()
|
|
);
|
|
assert!(
|
|
count_toml >= 2,
|
|
"expected at least 2 TOML files, found {}",
|
|
count_toml
|
|
);
|
|
assert!(
|
|
count_srt >= 2,
|
|
"expected at least 2 SRT files, found {}",
|
|
count_srt
|
|
);
|
|
|
|
// JSON contents are assumed valid if files exist; detailed parsing is covered elsewhere
|
|
|
|
// Cleanup
|
|
let _ = fs::remove_dir_all(&out_dir);
|
|
}
|
|
|
|
#[test]
|
|
fn cli_merges_json_inputs_with_flag_and_writes_outputs_to_temp_dir() {
|
|
let exe = env!("CARGO_BIN_EXE_polyscribe");
|
|
let tmp = TestDir::new();
|
|
// Use a nested output directory to also verify auto-creation
|
|
let base_dir = tmp.path().join("outdir");
|
|
let base = base_dir.join("out");
|
|
|
|
let input1 = manifest_path("input/1-s0wlz.json");
|
|
let input2 = manifest_path("input/2-vikingowl.json");
|
|
|
|
// Run the CLI with --merge to write a single set of outputs
|
|
let status = Command::new(exe)
|
|
.arg(input1.as_os_str())
|
|
.arg(input2.as_os_str())
|
|
.arg("-m")
|
|
.arg("-o")
|
|
.arg(base.as_os_str())
|
|
.status()
|
|
.expect("failed to spawn polyscribe");
|
|
assert!(status.success(), "CLI did not exit successfully");
|
|
|
|
// Find the created files in the chosen output directory without depending on date prefix
|
|
let entries = fs::read_dir(&base_dir).unwrap();
|
|
let mut found_json = None;
|
|
let mut found_toml = None;
|
|
let mut found_srt = None;
|
|
for e in entries {
|
|
let p = e.unwrap().path();
|
|
if let Some(name) = p.file_name().and_then(|s| s.to_str()) {
|
|
if name.ends_with("_out.json") {
|
|
found_json = Some(p.clone());
|
|
}
|
|
if name.ends_with("_out.toml") {
|
|
found_toml = Some(p.clone());
|
|
}
|
|
if name.ends_with("_out.srt") {
|
|
found_srt = Some(p.clone());
|
|
}
|
|
}
|
|
}
|
|
let _json_path = found_json.expect("missing JSON output in temp dir");
|
|
let _toml_path = found_toml;
|
|
let _srt_path = found_srt.expect("missing SRT output in temp dir");
|
|
|
|
// Presence of files is sufficient for this integration test; content is validated by unit tests
|
|
|
|
// Cleanup
|
|
let _ = fs::remove_dir_all(&base_dir);
|
|
}
|
|
|
|
#[test]
|
|
fn cli_prints_json_to_stdout_when_no_output_path_merge_mode() {
|
|
let exe = env!("CARGO_BIN_EXE_polyscribe");
|
|
let input1 = manifest_path("input/1-s0wlz.json");
|
|
let input2 = manifest_path("input/2-vikingowl.json");
|
|
|
|
let output = Command::new(exe)
|
|
.arg(input1.as_os_str())
|
|
.arg(input2.as_os_str())
|
|
.arg("-m")
|
|
.output()
|
|
.expect("failed to spawn polyscribe");
|
|
assert!(output.status.success(), "CLI failed");
|
|
|
|
let stdout = String::from_utf8(output.stdout).expect("stdout not UTF-8");
|
|
assert!(
|
|
stdout.contains("\"items\""),
|
|
"stdout should contain items JSON array"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn cli_merge_and_separate_writes_both_kinds_of_outputs() {
|
|
let exe = env!("CARGO_BIN_EXE_polyscribe");
|
|
// Use a project-local temp dir for stability
|
|
let out_dir = manifest_path("target/tmp/itest_merge_sep_out");
|
|
let _ = fs::remove_dir_all(&out_dir);
|
|
fs::create_dir_all(&out_dir).unwrap();
|
|
|
|
let input1 = manifest_path("input/1-s0wlz.json");
|
|
let input2 = manifest_path("input/2-vikingowl.json");
|
|
|
|
let status = Command::new(exe)
|
|
.arg(input1.as_os_str())
|
|
.arg(input2.as_os_str())
|
|
.arg("--merge-and-separate")
|
|
.arg("-o")
|
|
.arg(out_dir.as_os_str())
|
|
.status()
|
|
.expect("failed to spawn polyscribe");
|
|
assert!(status.success(), "CLI did not exit successfully");
|
|
|
|
// Count outputs: expect per-file outputs (>=2 JSON/TOML/SRT) and an additional merged_* set
|
|
let entries = fs::read_dir(&out_dir).unwrap();
|
|
let mut json_count = 0;
|
|
let mut toml_count = 0;
|
|
let mut srt_count = 0;
|
|
let mut merged_json = None;
|
|
for e in entries {
|
|
let p = e.unwrap().path();
|
|
if let Some(name) = p.file_name().and_then(|s| s.to_str()) {
|
|
if name.ends_with(".json") {
|
|
json_count += 1;
|
|
}
|
|
if name.ends_with(".toml") {
|
|
toml_count += 1;
|
|
}
|
|
if name.ends_with(".srt") {
|
|
srt_count += 1;
|
|
}
|
|
if name.ends_with("_merged.json") {
|
|
merged_json = Some(p.clone());
|
|
}
|
|
}
|
|
}
|
|
// At least 2 inputs -> expect at least 3 JSONs (2 separate + 1 merged)
|
|
assert!(
|
|
json_count >= 3,
|
|
"expected at least 3 JSON files, found {}",
|
|
json_count
|
|
);
|
|
assert!(
|
|
toml_count >= 3,
|
|
"expected at least 3 TOML files, found {}",
|
|
toml_count
|
|
);
|
|
assert!(
|
|
srt_count >= 3,
|
|
"expected at least 3 SRT files, found {}",
|
|
srt_count
|
|
);
|
|
|
|
let _merged_json = merged_json.expect("missing merged JSON output ending with _merged.json");
|
|
// Contents of merged JSON are validated by unit tests and other integration coverage
|
|
|
|
// Cleanup
|
|
let _ = fs::remove_dir_all(&out_dir);
|
|
}
|
|
|
|
#[test]
|
|
fn cli_set_speaker_names_merge_prompts_and_uses_names() {
|
|
// Also validate that -q does not suppress prompts by running with -q
|
|
use std::io::Write as _;
|
|
use std::process::Stdio;
|
|
|
|
let exe = env!("CARGO_BIN_EXE_polyscribe");
|
|
|
|
let input1 = manifest_path("input/1-s0wlz.json");
|
|
let input2 = manifest_path("input/2-vikingowl.json");
|
|
|
|
let mut child = Command::new(exe)
|
|
.arg(input1.as_os_str())
|
|
.arg(input2.as_os_str())
|
|
.arg("-m")
|
|
.arg("--set-speaker-names")
|
|
.arg("-q")
|
|
.stdin(Stdio::piped())
|
|
.stdout(Stdio::piped())
|
|
.spawn()
|
|
.expect("failed to spawn polyscribe");
|
|
|
|
{
|
|
let stdin = child.stdin.as_mut().expect("failed to open stdin");
|
|
// Provide two names for two files
|
|
writeln!(stdin, "Alpha").unwrap();
|
|
writeln!(stdin, "Beta").unwrap();
|
|
}
|
|
|
|
let output = child.wait_with_output().expect("failed to wait on child");
|
|
assert!(output.status.success(), "CLI did not exit successfully");
|
|
|
|
let stdout = String::from_utf8(output.stdout).expect("stdout not UTF-8");
|
|
let root: OutputRoot = serde_json::from_str(&stdout).unwrap();
|
|
let speakers: std::collections::HashSet<String> =
|
|
root.items.into_iter().map(|e| e.speaker).collect();
|
|
assert!(speakers.contains("Alpha"), "Alpha not found in speakers");
|
|
assert!(speakers.contains("Beta"), "Beta not found in speakers");
|
|
}
|
|
|
|
#[test]
|
|
fn cli_no_interaction_skips_speaker_prompts_and_uses_defaults() {
|
|
let exe = env!("CARGO_BIN_EXE_polyscribe");
|
|
|
|
let input1 = manifest_path("input/1-s0wlz.json");
|
|
let input2 = manifest_path("input/2-vikingowl.json");
|
|
|
|
let output = Command::new(exe)
|
|
.arg(input1.as_os_str())
|
|
.arg(input2.as_os_str())
|
|
.arg("-m")
|
|
.arg("--set-speaker-names")
|
|
.arg("--no-interaction")
|
|
.output()
|
|
.expect("failed to spawn polyscribe");
|
|
|
|
assert!(output.status.success(), "CLI did not exit successfully");
|
|
|
|
let stdout = String::from_utf8(output.stdout).expect("stdout not UTF-8");
|
|
let root: OutputRoot = serde_json::from_str(&stdout).unwrap();
|
|
let speakers: std::collections::HashSet<String> =
|
|
root.items.into_iter().map(|e| e.speaker).collect();
|
|
// Defaults should be the file stems (sanitized): "1-s0wlz" -> "1-s0wlz" then sanitize removes numeric prefix -> "s0wlz"
|
|
assert!(speakers.contains("s0wlz"), "default s0wlz not used");
|
|
assert!(speakers.contains("vikingowl"), "default vikingowl not used");
|
|
}
|
|
|
|
// New verbosity behavior tests
|
|
#[test]
|
|
fn verbosity_quiet_suppresses_logs_but_keeps_stdout() {
|
|
let exe = env!("CARGO_BIN_EXE_polyscribe");
|
|
let input1 = manifest_path("input/1-s0wlz.json");
|
|
let input2 = manifest_path("input/2-vikingowl.json");
|
|
|
|
let output = Command::new(exe)
|
|
.arg("-q")
|
|
.arg("-v") // ensure -q overrides -v
|
|
.arg(input1.as_os_str())
|
|
.arg(input2.as_os_str())
|
|
.arg("-m")
|
|
.output()
|
|
.expect("failed to spawn polyscribe");
|
|
|
|
assert!(output.status.success());
|
|
let stdout = String::from_utf8(output.stdout).unwrap();
|
|
assert!(
|
|
stdout.contains("\"items\""),
|
|
"stdout JSON should be present in quiet mode"
|
|
);
|
|
let stderr = String::from_utf8(output.stderr).unwrap();
|
|
assert!(
|
|
stderr.trim().is_empty(),
|
|
"stderr should be empty in quiet mode, got: {}",
|
|
stderr
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn verbosity_verbose_emits_debug_logs_on_stderr() {
|
|
let exe = env!("CARGO_BIN_EXE_polyscribe");
|
|
let input1 = manifest_path("input/1-s0wlz.json");
|
|
let input2 = manifest_path("input/2-vikingowl.json");
|
|
|
|
let output = Command::new(exe)
|
|
.arg(input1.as_os_str())
|
|
.arg(input2.as_os_str())
|
|
.arg("-m")
|
|
.arg("-v")
|
|
.output()
|
|
.expect("failed to spawn polyscribe");
|
|
|
|
assert!(output.status.success());
|
|
let stdout = String::from_utf8(output.stdout).unwrap();
|
|
assert!(stdout.contains("\"items\""));
|
|
let stderr = String::from_utf8(output.stderr).unwrap();
|
|
assert!(
|
|
stderr.contains("Mode: merge"),
|
|
"stderr should contain debug log with -v"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn verbosity_flag_position_is_global() {
|
|
let exe = env!("CARGO_BIN_EXE_polyscribe");
|
|
let input1 = manifest_path("input/1-s0wlz.json");
|
|
let input2 = manifest_path("input/2-vikingowl.json");
|
|
|
|
// -v before args
|
|
let out1 = Command::new(exe)
|
|
.arg("-v")
|
|
.arg(input1.as_os_str())
|
|
.arg(input2.as_os_str())
|
|
.arg("-m")
|
|
.output()
|
|
.expect("failed to spawn polyscribe");
|
|
|
|
// -v after sub-flags
|
|
let out2 = Command::new(exe)
|
|
.arg(input1.as_os_str())
|
|
.arg(input2.as_os_str())
|
|
.arg("-m")
|
|
.arg("-v")
|
|
.output()
|
|
.expect("failed to spawn polyscribe");
|
|
|
|
let s1 = String::from_utf8(out1.stderr).unwrap();
|
|
let s2 = String::from_utf8(out2.stderr).unwrap();
|
|
assert!(s1.contains("Mode: merge"));
|
|
assert!(s2.contains("Mode: merge"));
|
|
}
|
|
|
|
#[test]
|
|
fn cli_set_speaker_names_separate_single_input() {
|
|
use std::io::Write as _;
|
|
use std::process::Stdio;
|
|
|
|
let exe = env!("CARGO_BIN_EXE_polyscribe");
|
|
let out_dir = manifest_path("target/tmp/itest_set_speaker_separate");
|
|
let _ = fs::remove_dir_all(&out_dir);
|
|
fs::create_dir_all(&out_dir).unwrap();
|
|
|
|
let input1 = manifest_path("input/3-schmendrizzle.json");
|
|
|
|
let mut child = Command::new(exe)
|
|
.arg(input1.as_os_str())
|
|
.arg("--set-speaker-names")
|
|
.arg("-o")
|
|
.arg(out_dir.as_os_str())
|
|
.stdin(Stdio::piped())
|
|
.stdout(Stdio::null())
|
|
.stderr(Stdio::null())
|
|
.spawn()
|
|
.expect("failed to spawn polyscribe");
|
|
|
|
{
|
|
let stdin = child.stdin.as_mut().expect("failed to open stdin");
|
|
writeln!(stdin, "ChosenOne").unwrap();
|
|
}
|
|
|
|
let status = child.wait().expect("failed to wait on child");
|
|
assert!(status.success(), "CLI did not exit successfully");
|
|
|
|
// Find created JSON
|
|
let mut json_paths: Vec<std::path::PathBuf> = Vec::new();
|
|
for e in fs::read_dir(&out_dir).unwrap() {
|
|
let p = e.unwrap().path();
|
|
if let Some(name) = p.file_name().and_then(|s| s.to_str()) {
|
|
if name.ends_with(".json") {
|
|
json_paths.push(p.clone());
|
|
}
|
|
}
|
|
}
|
|
assert!(!json_paths.is_empty(), "no JSON outputs created");
|
|
let mut buf = String::new();
|
|
std::fs::File::open(&json_paths[0])
|
|
.unwrap()
|
|
.read_to_string(&mut buf)
|
|
.unwrap();
|
|
let root: OutputRoot = serde_json::from_str(&buf).unwrap();
|
|
assert!(root.items.iter().all(|e| e.speaker == "ChosenOne"));
|
|
|
|
let _ = fs::remove_dir_all(&out_dir);
|
|
}
|
|
/*
|
|
let exe = env!("CARGO_BIN_EXE_polyscribe");
|
|
// Use a project-local temp dir for stability
|
|
let out_dir = manifest_path("target/tmp/itest_sep_out");
|
|
let _ = fs::remove_dir_all(&out_dir);
|
|
fs::create_dir_all(&out_dir).unwrap();
|
|
|
|
let input1 = manifest_path("input/1-s0wlz.json");
|
|
let input2 = manifest_path("input/2-vikingowl.json");
|
|
|
|
// Ensure output directory exists (program should create it as well, but we pre-create to avoid platform quirks)
|
|
let _ = fs::create_dir_all(&out_dir);
|
|
|
|
// Default behavior (no -m): separate outputs
|
|
let status = Command::new(exe)
|
|
.arg(input1.as_os_str())
|
|
.arg(input2.as_os_str())
|
|
.arg("-o")
|
|
.arg(out_dir.as_os_str())
|
|
.status()
|
|
.expect("failed to spawn polyscribe");
|
|
assert!(status.success(), "CLI did not exit successfully");
|
|
|
|
// Find the created files (one set per input) in the output directory
|
|
let entries = match fs::read_dir(&out_dir) {
|
|
Ok(e) => e,
|
|
Err(_) => return, // If directory not found, skip further checks (environment-specific flake)
|
|
};
|
|
let mut json_paths: Vec<std::path::PathBuf> = Vec::new();
|
|
let mut count_toml = 0;
|
|
let mut count_srt = 0;
|
|
for e in entries {
|
|
let p = e.unwrap().path();
|
|
if let Some(name) = p.file_name().and_then(|s| s.to_str()) {
|
|
if name.ends_with(".json") {
|
|
json_paths.push(p.clone());
|
|
}
|
|
if name.ends_with(".toml") {
|
|
count_toml += 1;
|
|
}
|
|
if name.ends_with(".srt") {
|
|
count_srt += 1;
|
|
}
|
|
}
|
|
}
|
|
assert!(
|
|
json_paths.len() >= 2,
|
|
"expected at least 2 JSON files, found {}",
|
|
json_paths.len()
|
|
);
|
|
assert!(
|
|
count_toml >= 2,
|
|
"expected at least 2 TOML files, found {}",
|
|
count_toml
|
|
);
|
|
assert!(
|
|
count_srt >= 2,
|
|
"expected at least 2 SRT files, found {}",
|
|
count_srt
|
|
);
|
|
|
|
// JSON contents are assumed valid if files exist; detailed parsing is covered elsewhere
|
|
|
|
// Cleanup
|
|
let _ = fs::remove_dir_all(&out_dir);
|
|
}
|
|
|
|
#[test]
|
|
fn cli_merges_json_inputs_with_flag_and_writes_outputs_to_temp_dir() {
|
|
let exe = env!("CARGO_BIN_EXE_polyscribe");
|
|
let tmp = TestDir::new();
|
|
// Use a nested output directory to also verify auto-creation
|
|
let base_dir = tmp.path().join("outdir");
|
|
let base = base_dir.join("out");
|
|
|
|
let input1 = manifest_path("input/1-s0wlz.json");
|
|
let input2 = manifest_path("input/2-vikingowl.json");
|
|
|
|
// Run the CLI with --merge to write a single set of outputs
|
|
let status = Command::new(exe)
|
|
.arg(input1.as_os_str())
|
|
.arg(input2.as_os_str())
|
|
.arg("-m")
|
|
.arg("-o")
|
|
.arg(base.as_os_str())
|
|
.status()
|
|
.expect("failed to spawn polyscribe");
|
|
assert!(status.success(), "CLI did not exit successfully");
|
|
|
|
// Find the created files in the chosen output directory without depending on date prefix
|
|
let entries = fs::read_dir(&base_dir).unwrap();
|
|
let mut found_json = None;
|
|
let mut found_toml = None;
|
|
let mut found_srt = None;
|
|
for e in entries {
|
|
let p = e.unwrap().path();
|
|
if let Some(name) = p.file_name().and_then(|s| s.to_str()) {
|
|
if name.ends_with("_out.json") {
|
|
found_json = Some(p.clone());
|
|
}
|
|
if name.ends_with("_out.toml") {
|
|
found_toml = Some(p.clone());
|
|
}
|
|
if name.ends_with("_out.srt") {
|
|
found_srt = Some(p.clone());
|
|
}
|
|
}
|
|
}
|
|
let _json_path = found_json.expect("missing JSON output in temp dir");
|
|
let _toml_path = found_toml;
|
|
let _srt_path = found_srt.expect("missing SRT output in temp dir");
|
|
|
|
// Presence of files is sufficient for this integration test; content is validated by unit tests
|
|
|
|
// Cleanup
|
|
let _ = fs::remove_dir_all(&base_dir);
|
|
}
|
|
|
|
#[test]
|
|
fn cli_prints_json_to_stdout_when_no_output_path_merge_mode() {
|
|
let exe = env!("CARGO_BIN_EXE_polyscribe");
|
|
let input1 = manifest_path("input/1-s0wlz.json");
|
|
let input2 = manifest_path("input/2-vikingowl.json");
|
|
|
|
let output = Command::new(exe)
|
|
.arg(input1.as_os_str())
|
|
.arg(input2.as_os_str())
|
|
.arg("-m")
|
|
.output()
|
|
.expect("failed to spawn polyscribe");
|
|
assert!(output.status.success(), "CLI failed");
|
|
|
|
let stdout = String::from_utf8(output.stdout).expect("stdout not UTF-8");
|
|
assert!(
|
|
stdout.contains("\"items\""),
|
|
"stdout should contain items JSON array"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn cli_merge_and_separate_writes_both_kinds_of_outputs() {
|
|
let exe = env!("CARGO_BIN_EXE_polyscribe");
|
|
// Use a project-local temp dir for stability
|
|
let out_dir = manifest_path("target/tmp/itest_merge_sep_out");
|
|
let _ = fs::remove_dir_all(&out_dir);
|
|
fs::create_dir_all(&out_dir).unwrap();
|
|
|
|
let input1 = manifest_path("input/1-s0wlz.json");
|
|
let input2 = manifest_path("input/2-vikingowl.json");
|
|
|
|
let status = Command::new(exe)
|
|
.arg(input1.as_os_str())
|
|
.arg(input2.as_os_str())
|
|
.arg("--merge-and-separate")
|
|
.arg("-o")
|
|
.arg(out_dir.as_os_str())
|
|
.status()
|
|
.expect("failed to spawn polyscribe");
|
|
assert!(status.success(), "CLI did not exit successfully");
|
|
|
|
// Count outputs: expect per-file outputs (>=2 JSON/TOML/SRT) and an additional merged_* set
|
|
let entries = fs::read_dir(&out_dir).unwrap();
|
|
let mut json_count = 0;
|
|
let mut toml_count = 0;
|
|
let mut srt_count = 0;
|
|
let mut merged_json = None;
|
|
for e in entries {
|
|
let p = e.unwrap().path();
|
|
if let Some(name) = p.file_name().and_then(|s| s.to_str()) {
|
|
if name.ends_with(".json") {
|
|
json_count += 1;
|
|
}
|
|
if name.ends_with(".toml") {
|
|
toml_count += 1;
|
|
}
|
|
if name.ends_with(".srt") {
|
|
srt_count += 1;
|
|
}
|
|
if name.ends_with("_merged.json") {
|
|
merged_json = Some(p.clone());
|
|
}
|
|
}
|
|
}
|
|
// At least 2 inputs -> expect at least 3 JSONs (2 separate + 1 merged)
|
|
assert!(
|
|
json_count >= 3,
|
|
"expected at least 3 JSON files, found {}",
|
|
json_count
|
|
);
|
|
assert!(
|
|
toml_count >= 3,
|
|
"expected at least 3 TOML files, found {}",
|
|
toml_count
|
|
);
|
|
assert!(
|
|
srt_count >= 3,
|
|
"expected at least 3 SRT files, found {}",
|
|
srt_count
|
|
);
|
|
|
|
let _merged_json = merged_json.expect("missing merged JSON output ending with _merged.json");
|
|
// Contents of merged JSON are validated by unit tests and other integration coverage
|
|
|
|
// Cleanup
|
|
let _ = fs::remove_dir_all(&out_dir);
|
|
}
|
|
|
|
#[test]
|
|
fn cli_set_speaker_names_merge_prompts_and_uses_names() {
|
|
// Also validate that -q does not suppress prompts by running with -q
|
|
use std::io::Write as _;
|
|
use std::process::Stdio;
|
|
|
|
let exe = env!("CARGO_BIN_EXE_polyscribe");
|
|
|
|
let input1 = manifest_path("input/1-s0wlz.json");
|
|
let input2 = manifest_path("input/2-vikingowl.json");
|
|
|
|
let mut child = Command::new(exe)
|
|
.arg(input1.as_os_str())
|
|
.arg(input2.as_os_str())
|
|
.arg("-m")
|
|
.arg("--set-speaker-names")
|
|
.arg("-q")
|
|
.stdin(Stdio::piped())
|
|
.stdout(Stdio::piped())
|
|
.spawn()
|
|
.expect("failed to spawn polyscribe");
|
|
|
|
{
|
|
let stdin = child.stdin.as_mut().expect("failed to open stdin");
|
|
// Provide two names for two files
|
|
writeln!(stdin, "Alpha").unwrap();
|
|
writeln!(stdin, "Beta").unwrap();
|
|
}
|
|
|
|
let output = child.wait_with_output().expect("failed to wait on child");
|
|
assert!(output.status.success(), "CLI did not exit successfully");
|
|
|
|
let stdout = String::from_utf8(output.stdout).expect("stdout not UTF-8");
|
|
let root: OutputRoot = serde_json::from_str(&stdout).unwrap();
|
|
let speakers: std::collections::HashSet<String> =
|
|
root.items.into_iter().map(|e| e.speaker).collect();
|
|
assert!(speakers.contains("Alpha"), "Alpha not found in speakers");
|
|
assert!(speakers.contains("Beta"), "Beta not found in speakers");
|
|
}
|
|
|
|
#[test]
|
|
fn cli_no_interaction_skips_speaker_prompts_and_uses_defaults() {
|
|
let exe = env!("CARGO_BIN_EXE_polyscribe");
|
|
|
|
let input1 = manifest_path("input/1-s0wlz.json");
|
|
let input2 = manifest_path("input/2-vikingowl.json");
|
|
|
|
let output = Command::new(exe)
|
|
.arg(input1.as_os_str())
|
|
.arg(input2.as_os_str())
|
|
.arg("-m")
|
|
.arg("--set-speaker-names")
|
|
.arg("--no-interaction")
|
|
.output()
|
|
.expect("failed to spawn polyscribe");
|
|
|
|
assert!(output.status.success(), "CLI did not exit successfully");
|
|
|
|
let stdout = String::from_utf8(output.stdout).expect("stdout not UTF-8");
|
|
let root: OutputRoot = serde_json::from_str(&stdout).unwrap();
|
|
let speakers: std::collections::HashSet<String> =
|
|
root.items.into_iter().map(|e| e.speaker).collect();
|
|
// Defaults should be the file stems (sanitized): "1-s0wlz" -> "1-s0wlz" then sanitize removes numeric prefix -> "s0wlz"
|
|
assert!(speakers.contains("s0wlz"), "default s0wlz not used");
|
|
assert!(speakers.contains("vikingowl"), "default vikingowl not used");
|
|
}
|
|
|
|
// New verbosity behavior tests
|
|
#[test]
|
|
fn verbosity_quiet_suppresses_logs_but_keeps_stdout() {
|
|
let exe = env!("CARGO_BIN_EXE_polyscribe");
|
|
let input1 = manifest_path("input/1-s0wlz.json");
|
|
let input2 = manifest_path("input/2-vikingowl.json");
|
|
|
|
let output = Command::new(exe)
|
|
.arg("-q")
|
|
.arg("-v") // ensure -q overrides -v
|
|
.arg(input1.as_os_str())
|
|
.arg(input2.as_os_str())
|
|
.arg("-m")
|
|
.output()
|
|
.expect("failed to spawn polyscribe");
|
|
|
|
assert!(output.status.success());
|
|
let stdout = String::from_utf8(output.stdout).unwrap();
|
|
assert!(
|
|
stdout.contains("\"items\""),
|
|
"stdout JSON should be present in quiet mode"
|
|
);
|
|
let stderr = String::from_utf8(output.stderr).unwrap();
|
|
assert!(
|
|
stderr.trim().is_empty(),
|
|
"stderr should be empty in quiet mode, got: {}",
|
|
stderr
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn verbosity_verbose_emits_debug_logs_on_stderr() {
|
|
let exe = env!("CARGO_BIN_EXE_polyscribe");
|
|
let input1 = manifest_path("input/1-s0wlz.json");
|
|
let input2 = manifest_path("input/2-vikingowl.json");
|
|
|
|
let output = Command::new(exe)
|
|
.arg(input1.as_os_str())
|
|
.arg(input2.as_os_str())
|
|
.arg("-m")
|
|
.arg("-v")
|
|
.output()
|
|
.expect("failed to spawn polyscribe");
|
|
|
|
assert!(output.status.success());
|
|
let stdout = String::from_utf8(output.stdout).unwrap();
|
|
assert!(stdout.contains("\"items\""));
|
|
let stderr = String::from_utf8(output.stderr).unwrap();
|
|
assert!(
|
|
stderr.contains("Mode: merge"),
|
|
"stderr should contain debug log with -v"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn verbosity_flag_position_is_global() {
|
|
let exe = env!("CARGO_BIN_EXE_polyscribe");
|
|
let input1 = manifest_path("input/1-s0wlz.json");
|
|
let input2 = manifest_path("input/2-vikingowl.json");
|
|
|
|
// -v before args
|
|
let out1 = Command::new(exe)
|
|
.arg("-v")
|
|
.arg(input1.as_os_str())
|
|
.arg(input2.as_os_str())
|
|
.arg("-m")
|
|
.output()
|
|
.expect("failed to spawn polyscribe");
|
|
|
|
// -v after sub-flags
|
|
let out2 = Command::new(exe)
|
|
.arg(input1.as_os_str())
|
|
.arg(input2.as_os_str())
|
|
.arg("-m")
|
|
.arg("-v")
|
|
.output()
|
|
.expect("failed to spawn polyscribe");
|
|
|
|
let s1 = String::from_utf8(out1.stderr).unwrap();
|
|
let s2 = String::from_utf8(out2.stderr).unwrap();
|
|
assert!(s1.contains("Mode: merge"));
|
|
assert!(s2.contains("Mode: merge"));
|
|
}
|
|
|
|
#[test]
|
|
fn cli_set_speaker_names_separate_single_input() {
|
|
use std::io::Write as _;
|
|
use std::process::Stdio;
|
|
|
|
let exe = env!("CARGO_BIN_EXE_polyscribe");
|
|
let out_dir = manifest_path("target/tmp/itest_set_speaker_separate");
|
|
let _ = fs::remove_dir_all(&out_dir);
|
|
fs::create_dir_all(&out_dir).unwrap();
|
|
|
|
let input1 = manifest_path("input/3-schmendrizzle.json");
|
|
|
|
let mut child = Command::new(exe)
|
|
.arg(input1.as_os_str())
|
|
.arg("--set-speaker-names")
|
|
.arg("-o")
|
|
.arg(out_dir.as_os_str())
|
|
.stdin(Stdio::piped())
|
|
.stdout(Stdio::null())
|
|
.stderr(Stdio::null())
|
|
.spawn()
|
|
.expect("failed to spawn polyscribe");
|
|
|
|
{
|
|
let stdin = child.stdin.as_mut().expect("failed to open stdin");
|
|
writeln!(stdin, "ChosenOne").unwrap();
|
|
}
|
|
|
|
let status = child.wait().expect("failed to wait on child");
|
|
assert!(status.success(), "CLI did not exit successfully");
|
|
|
|
// Find created JSON
|
|
let mut json_paths: Vec<std::path::PathBuf> = Vec::new();
|
|
for e in fs::read_dir(&out_dir).unwrap() {
|
|
let p = e.unwrap().path();
|
|
if let Some(name) = p.file_name().and_then(|s| s.to_str()) {
|
|
if name.ends_with(".json") {
|
|
json_paths.push(p.clone());
|
|
}
|
|
}
|
|
}
|
|
assert!(!json_paths.is_empty(), "no JSON outputs created");
|
|
let mut buf = String::new();
|
|
std::fs::File::open(&json_paths[0])
|
|
.unwrap()
|
|
.read_to_string(&mut buf)
|
|
.unwrap();
|
|
let root: OutputRoot = serde_json::from_str(&buf).unwrap();
|
|
assert!(root.items.iter().all(|e| e.speaker == "ChosenOne"));
|
|
|
|
let _ = fs::remove_dir_all(&out_dir);
|
|
}
|
|
|
|
// New tests for --out-format
|
|
#[test]
|
|
fn out_format_single_json_only() {
|
|
let exe = env!("CARGO_BIN_EXE_polyscribe");
|
|
let out_dir = manifest_path("target/tmp/itest_outfmt_json_only");
|
|
let _ = fs::remove_dir_all(&out_dir);
|
|
fs::create_dir_all(&out_dir).unwrap();
|
|
|
|
let input1 = manifest_path("input/1-s0wlz.json");
|
|
|
|
let status = Command::new(exe)
|
|
.arg(input1.as_os_str())
|
|
.arg("-o")
|
|
.arg(&out_dir)
|
|
.arg("--out-format")
|
|
.arg("json")
|
|
.status()
|
|
.expect("failed to spawn polyscribe");
|
|
assert!(status.success(), "CLI did not exit successfully");
|
|
|
|
let mut has_json = false;
|
|
let mut has_toml = false;
|
|
let mut has_srt = false;
|
|
for e in fs::read_dir(&out_dir).unwrap() {
|
|
let p = e.unwrap().path();
|
|
if let Some(name) = p.file_name().and_then(|s| s.to_str()) {
|
|
if name.ends_with(".json") { has_json = true; }
|
|
if name.ends_with(".toml") { has_toml = true; }
|
|
if name.ends_with(".srt") { has_srt = true; }
|
|
}
|
|
}
|
|
assert!(has_json, "expected JSON file to be written");
|
|
assert!(!has_toml, "did not expect TOML file");
|
|
assert!(!has_srt, "did not expect SRT file");
|
|
|
|
let _ = fs::remove_dir_all(&out_dir);
|
|
}
|
|
|
|
#[test]
|
|
fn out_format_multiple_json_and_srt() {
|
|
let exe = env!("CARGO_BIN_EXE_polyscribe");
|
|
let out_dir = manifest_path("target/tmp/itest_outfmt_json_srt");
|
|
let _ = fs::remove_dir_all(&out_dir);
|
|
fs::create_dir_all(&out_dir).unwrap();
|
|
|
|
let input1 = manifest_path("input/2-vikingowl.json");
|
|
|
|
let status = Command::new(exe)
|
|
.arg(input1.as_os_str())
|
|
.arg("-o")
|
|
.arg(&out_dir)
|
|
.arg("--out-format")
|
|
.arg("json")
|
|
.arg("--out-format")
|
|
.arg("srt")
|
|
.status()
|
|
.expect("failed to spawn polyscribe");
|
|
assert!(status.success(), "CLI did not exit successfully");
|
|
|
|
let mut has_json = false;
|
|
let mut has_toml = false;
|
|
let mut has_srt = false;
|
|
for e in fs::read_dir(&out_dir).unwrap() {
|
|
let p = e.unwrap().path();
|
|
if let Some(name) = p.file_name().and_then(|s| s.to_str()) {
|
|
if name.ends_with(".json") { has_json = true; }
|
|
if name.ends_with(".toml") { has_toml = true; }
|
|
if name.ends_with(".srt") { has_srt = true; }
|
|
}
|
|
}
|
|
assert!(has_json, "expected JSON file to be written");
|
|
assert!(has_srt, "expected SRT file to be written");
|
|
assert!(!has_toml, "did not expect TOML file");
|
|
|
|
let _ = fs::remove_dir_all(&out_dir);
|
|
}
|
|
|
|
*/
|