use std::{
fs,
path::{Path, PathBuf},
};
use anyhow::{Ok, Result};
use tracing_subscriber::{EnvFilter, fmt::format::FmtSpan};
use wty::{
cli::{DictName, GlossaryArgs, GlossaryLangs, IpaArgs, MainArgs, MainLangs, Options},
dict::{DGlossary, DIpa, DMain, WriterFormat, make_dict_from_jsonl},
lang::{Edition, Lang},
path::PathManager,
};
fn cleanup(root: &Path) -> bool {
let entries = fs::read_dir(root).unwrap();
let mut is_empty = true;
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
let child_empty = cleanup(&path);
if child_empty {
fs::remove_dir(&path).unwrap();
} else {
is_empty = false;
}
} else if path
.extension()
.and_then(|e| e.to_str())
.is_some_and(|ext| ext.eq_ignore_ascii_case("zip"))
{
panic!("zip found in tests");
} else {
is_empty = false;
}
}
is_empty
}
fn fixture_options(fixture_dir: &Path, format: WriterFormat) -> Options {
Options {
pretty: true,
experimental: false,
root_dir: fixture_dir.to_path_buf(),
format,
..Default::default()
}
}
fn fixture_main_args(
source: Lang,
target: Edition,
fixture_dir: &Path,
format: WriterFormat,
) -> MainArgs {
MainArgs {
langs: MainLangs { source, target },
dict_name: DictName::default(),
options: fixture_options(fixture_dir, format),
}
}
fn fixture_ipa_args(
source: Lang,
target: Edition,
fixture_dir: &Path,
format: WriterFormat,
) -> IpaArgs {
IpaArgs {
langs: MainLangs { source, target },
dict_name: DictName::default(),
options: fixture_options(fixture_dir, format),
}
}
fn fixture_glossary_args(
source: Edition,
target: Lang,
fixture_dir: &Path,
format: WriterFormat,
) -> GlossaryArgs {
GlossaryArgs {
langs: GlossaryLangs { source, target },
dict_name: DictName::default(),
options: fixture_options(fixture_dir, format),
}
}
fn setup_tracing_test() {
let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("warn"));
tracing_subscriber::fmt()
.with_env_filter(filter)
.with_span_events(FmtSpan::CLOSE)
.with_target(true)
.with_level(true)
.init();
}
#[test]
fn snapshot() {
setup_tracing_test();
let fixture_dir = PathBuf::from("tests");
let fixture_input_dir = fixture_dir.join("kaikki");
let mut cases = Vec::new();
let mut langs_in_testsuite = Vec::new();
for entry in fs::read_dir(&fixture_input_dir).unwrap() {
let entry = entry.unwrap();
let path = entry.path();
if let Some(fname) = path.file_name().and_then(|f| f.to_str())
&& let Some(base) = fname.strip_suffix("-extract.jsonl")
&& let Some((source, target)) = base.split_once('-')
{
let src = source.parse::<Lang>().unwrap();
let tar = target.parse::<Lang>().unwrap();
cases.push((src, tar));
if !langs_in_testsuite.contains(&src) {
langs_in_testsuite.push(src);
}
if !langs_in_testsuite.contains(&tar) {
langs_in_testsuite.push(tar);
}
}
}
tracing::debug!("Found {} cases: {cases:?}", cases.len());
for (source, target) in &cases {
let Result::Ok(target) = (*target).try_into() else {
continue; };
let args = fixture_main_args(*source, target, &fixture_dir, WriterFormat::TestYomitanMain);
if let Err(e) = shapshot_main(args) {
panic!("({source}): {e}");
}
}
for (source, target) in &cases {
if source != target {
continue;
}
let Result::Ok(source) = (*source).try_into() else {
continue; };
for possible_target in &langs_in_testsuite {
if Lang::from(source) == *possible_target {
continue;
}
if source == Edition::Simple || *possible_target == Lang::Simple {
continue;
}
let args = fixture_glossary_args(
source,
*possible_target,
&fixture_dir,
WriterFormat::TestYomitan,
);
make_dict_from_jsonl(DGlossary, args).unwrap();
}
}
for (source, target) in &cases {
let Result::Ok(target) = (*target).try_into() else {
continue; };
let args = fixture_ipa_args(*source, target, &fixture_dir, WriterFormat::TestYomitan);
make_dict_from_jsonl(DIpa, args).unwrap();
}
snapshot_main_html(&fixture_dir).unwrap();
cleanup(&fixture_dir.join("dict"));
}
fn snapshot_main_html(fixture_dir: &Path) -> Result<()> {
let format = WriterFormat::TestHtml;
let args = fixture_main_args(Lang::En, Edition::En, fixture_dir, format);
make_dict_from_jsonl(DMain, args).unwrap();
let args = fixture_main_args(Lang::Ja, Edition::Ja, fixture_dir, format);
make_dict_from_jsonl(DMain, args).unwrap();
let args = fixture_ipa_args(Lang::Ja, Edition::Ja, fixture_dir, format);
make_dict_from_jsonl(DIpa, args).unwrap();
let args = fixture_glossary_args(Edition::Ja, Lang::En, fixture_dir, format);
make_dict_from_jsonl(DGlossary, args).unwrap();
Ok(())
}
fn delete_previous_output(pm: &PathManager) -> Result<()> {
let pathdir_dict_temp = pm.dir_temp_dict();
if pathdir_dict_temp.exists() {
tracing::debug!("Deleting previous output: {pathdir_dict_temp:?}");
fs::remove_dir_all(pathdir_dict_temp)?;
}
Ok(())
}
fn check_git_diff(pm: &PathManager) -> Result<()> {
let output = std::process::Command::new("git")
.args([
"diff",
"--color=always",
"--unified=0", "--",
&pm.dir_temp_dict().to_string_lossy(),
])
.output()?;
if !output.stdout.is_empty() {
eprintln!("{}", String::from_utf8_lossy(&output.stdout));
anyhow::bail!("changes!")
}
Ok(())
}
fn shapshot_main(margs: MainArgs) -> Result<()> {
let pm = &PathManager::try_from(margs.clone())?;
delete_previous_output(pm)?;
make_dict_from_jsonl(DMain, margs)?;
check_git_diff(pm)?;
Ok(())
}