use serde::Deserialize;
use std::path::{Path, PathBuf};
#[derive(Deserialize)]
struct Manifest {
vectors: Vec<Vector>,
}
#[derive(Deserialize)]
struct Vector {
id: String,
input: String,
#[serde(default)]
expected_md5: Option<String>,
#[serde(default)]
expect: Option<String>,
#[serde(default)]
#[allow(dead_code)]
desc: String,
}
impl Vector {
fn is_error(&self) -> bool {
self.expect.as_deref() == Some("error")
}
}
fn conformance_dir() -> PathBuf {
let manifest = Path::new(env!("CARGO_MANIFEST_DIR"));
manifest
.parent() .and_then(|p| p.parent()) .map(|p| p.join("conformance"))
.expect("não foi possível resolver conformance/ a partir de CARGO_MANIFEST_DIR")
}
fn load_manifest() -> Manifest {
let path = conformance_dir().join("vectors.json");
let raw =
std::fs::read(&path).unwrap_or_else(|e| panic!("falha ao ler {}: {e}", path.display()));
serde_json::from_slice(&raw)
.unwrap_or_else(|e| panic!("vectors.json mal-formado em {}: {e}", path.display()))
}
#[test]
fn todos_vetores_passam() {
let manifest = load_manifest();
let dir = conformance_dir();
let mut fails: Vec<String> = Vec::new();
for v in &manifest.vectors {
let input_path = dir.join(&v.input);
let raw = match std::fs::read(&input_path) {
Ok(b) => b,
Err(e) => {
fails.push(format!("[IO] {}: {} ({e})", v.id, input_path.display()));
continue;
}
};
let result = tiss_hash::hash_tiss(&raw);
if v.is_error() {
match result {
Err(e) => println!("[OK-ERR] {:33} rejeitado: {e}", v.id),
Ok(got) => fails.push(format!(
"[SHOULD-ERR] {:29} esperado Err, obteve Ok({got})",
v.id
)),
}
continue;
}
let expected = match v.expected_md5.as_deref() {
Some(e) => e,
None => {
fails.push(format!(
"[BAD-VEC] {:32} vetor positivo sem expected_md5",
v.id
));
continue;
}
};
match result {
Ok(got) if got == expected => {
println!("[OK] {:35} {got}", v.id);
}
Ok(got) => {
fails.push(format!("[DIFF] {:35} got={got} expected={expected}", v.id));
}
Err(e) => {
fails.push(format!("[ERR] {:35} {e}", v.id));
}
}
}
assert!(
fails.is_empty(),
"{}/{} vetores falharam:\n{}",
fails.len(),
manifest.vectors.len(),
fails.join("\n")
);
}
#[test]
fn vetor_minimal_isolado() {
let dir = conformance_dir();
let raw = std::fs::read(dir.join("inputs/syn_minimal.xml")).unwrap();
let got = tiss_hash::hash_tiss(&raw).unwrap();
assert_eq!(got, "3aa0c578c95cdb861a125f480a8a4de5");
}
#[test]
fn hash_tiss_file_funciona() {
let dir = conformance_dir();
let path = dir.join("inputs/syn_acento.xml");
let h1 = tiss_hash::hash_tiss_file(&path).unwrap();
let h2 = tiss_hash::hash_tiss(&std::fs::read(&path).unwrap()).unwrap();
assert_eq!(h1, h2);
assert_eq!(h1, "a20afc9a89aadaa2179d03d225337662");
}