use std::path::{Path, PathBuf};
use std::process::Command;
use std::sync::atomic::{AtomicUsize, Ordering};
const BINARY: &str = env!("CARGO_BIN_EXE_numbers-le");
static COUNTER: AtomicUsize = AtomicUsize::new(0);
const ALIASES: [&str; 43] = [
"json",
"jsonc",
"yaml",
"yml",
"csv",
"tsv",
"toml",
"ini",
"cfg",
"conf",
"env",
"dotenv",
"python",
"py",
"rust",
"rs",
"go",
"java",
"kotlin",
"kt",
"kts",
"csharp",
"cs",
"cpp",
"cc",
"cxx",
"hpp",
"hh",
"c",
"h",
"javascript",
"js",
"mjs",
"cjs",
"javascriptreact",
"jsx",
"typescript",
"ts",
"typescriptreact",
"tsx",
"sql",
"shellscript",
"sh",
];
const UNKNOWN: [&str; 12] = [
"md",
"txt",
"log",
"rst",
"adoc",
"properties",
"gradle",
"bat",
"ps1",
"swift",
"rb",
"lua",
];
const SUPPORTED: [&str; 18] = [
"json",
"yaml",
"csv",
"toml",
"ini",
"env",
"python",
"rust",
"go",
"java",
"kotlin",
"csharp",
"cpp",
"c",
"javascript",
"typescript",
"sql",
"shellscript",
];
struct Tree {
root: PathBuf,
}
impl Tree {
fn new(name: &str) -> Self {
let unique = COUNTER.fetch_add(1, Ordering::Relaxed);
let root = std::env::temp_dir().join(format!(
"numbers-le-matrix-{name}-{}-{unique}",
std::process::id()
));
let _ = std::fs::remove_dir_all(&root);
std::fs::create_dir_all(&root).expect("a temporary directory");
Self {
root: std::fs::canonicalize(&root).expect("a canonical directory"),
}
}
fn write(&self, relative: &str, contents: &str) {
std::fs::write(self.root.join(relative), contents).expect("a file");
}
}
impl Drop for Tree {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.root);
}
}
const DOCUMENT: &str = "# rate 8080\n";
fn reports(root: &Path) -> Vec<serde_json::Value> {
let output = Command::new(BINARY)
.arg(root)
.output()
.expect("the binary runs");
assert!(
matches!(output.status.code(), Some(0 | 1)),
"the walk did not finish: {}",
String::from_utf8_lossy(&output.stderr)
);
String::from_utf8_lossy(&output.stdout)
.lines()
.filter(|line| !line.trim().is_empty())
.map(|line| serde_json::from_str(line).expect("stdout carries only JSON"))
.collect()
}
fn basename(report: &serde_json::Value) -> String {
let file = report["file"].as_str().unwrap_or_default();
file.rsplit('/').next().unwrap_or(file).to_string()
}
#[test]
fn every_extension_the_crate_names_produces_a_report_line() {
let tree = Tree::new("aliases");
let mut expected: Vec<String> = Vec::new();
for extension in ALIASES.into_iter().chain(UNKNOWN) {
let name = format!("case.{extension}");
tree.write(&name, DOCUMENT);
expected.push(name);
}
expected.sort();
let scanned = reports(&tree.root);
let mut named: Vec<String> = scanned.iter().map(basename).collect();
named.sort();
let missing: Vec<&String> = expected.iter().filter(|one| !named.contains(one)).collect();
assert!(
missing.is_empty(),
"{} of {} files produced no report line: {missing:?}",
missing.len(),
expected.len()
);
assert_eq!(named, expected, "the walk reported a file nobody wrote");
for report in &scanned {
let diagnostics = report["diagnostics"].as_array().expect("diagnostics");
assert!(
diagnostics.iter().all(|one| one["code"] != "skipped"),
"{} was reported as unread",
basename(report)
);
}
}
#[test]
fn every_offered_format_reads_a_document_forced_onto_it() {
let tree = Tree::new("forced");
tree.write("case.txt", DOCUMENT);
let path = tree.root.join("case.txt");
for format in SUPPORTED {
let output = Command::new(BINARY)
.args(["--format", format])
.arg(&path)
.output()
.expect("the binary runs");
let line = String::from_utf8_lossy(&output.stdout)
.lines()
.next()
.unwrap_or_default()
.to_string();
let report: serde_json::Value =
serde_json::from_str(&line).unwrap_or_else(|_| panic!("{format}: no report line"));
assert_eq!(
report["format"], format,
"{format} was offered and resolved to something else"
);
}
}
#[test]
fn every_offered_format_has_a_corpus_document() {
let corpus: serde_json::Value =
serde_json::from_str(include_str!("../fixtures/extraction.json"))
.expect("the corpus is valid JSON");
let documents = corpus["documents"].as_array().expect("documents");
let covered: Vec<&str> = documents
.iter()
.filter_map(|case| case["fileType"].as_str())
.collect();
let missing: Vec<&str> = SUPPORTED
.into_iter()
.chain(["unknown"])
.filter(|format| !covered.contains(format))
.collect();
assert!(
missing.is_empty(),
"offered with no corpus document, so nothing checks their parity: {missing:?}"
);
}