#![allow(
clippy::expect_used,
reason = "a missing submodule or unreadable case should fail the test loudly"
)]
use std::fs;
use std::path::{Path, PathBuf};
mod common;
use jsonvet::{Config, Dialect, Rule, Severity, check};
const CASES: usize = 114;
fn root() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("../../tests/json5-tests")
}
fn suite() -> Vec<PathBuf> {
let mut files = Vec::new();
let mut dirs = vec![root()];
while let Some(dir) = dirs.pop() {
for entry in fs::read_dir(&dir)
.expect("the json5-tests submodule is checked out: run `git submodule update --init`")
{
let path = entry.expect("readable directory").path();
if path.is_dir() {
dirs.push(path);
} else if matches!(
path.extension().and_then(|e| e.to_str()),
Some("json" | "json5" | "js" | "txt")
) {
files.push(path);
}
}
}
files.sort();
files
}
fn mislabeled() -> Vec<(String, String)> {
let list = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../tests/json5-tests-mislabeled.tsv");
fs::read_to_string(list)
.expect("tests/json5-tests-mislabeled.tsv")
.lines()
.filter(|line| !line.is_empty() && !line.starts_with('#'))
.map(|line| {
let (case, why) = line.split_once('\t').expect("a path, a tab, a reason");
(case.to_owned(), why.to_owned())
})
.collect()
}
fn passes(path: &Path, dialect: Dialect) -> bool {
let text = fs::read_to_string(path).expect("UTF-8 test file");
check(
&text,
&Config::new(dialect).rule(Rule::DuplicateKey, Severity::Off),
)
.passed()
}
#[test]
fn json5_tests_suite() {
let files = suite();
assert_eq!(
files.len(),
CASES,
"wrong number of cases; is the submodule checked out (`git submodule update --init`)?"
);
let mislabeled = mislabeled();
let mut wrong = Vec::new();
for path in &files {
let ext = path
.extension()
.and_then(|e| e.to_str())
.unwrap_or_default();
let as_json5 = passes(path, Dialect::Json5);
let as_json = passes(path, Dialect::Json);
let expected_json5 = matches!(ext, "json" | "json5");
let expected_json = ext == "json";
if as_json5 != expected_json5 {
wrong.push(format!("{} as json5: passed={as_json5}", path.display()));
}
let waived = mislabeled.iter().any(|(case, _)| path.ends_with(case));
if as_json != expected_json && !waived {
wrong.push(format!("{} as json: passed={as_json}", path.display()));
}
let text = fs::read_to_string(path).expect("UTF-8 test file");
for violation in common::nesting_violations(&text) {
wrong.push(format!("{}: {violation}", path.display()));
}
}
assert!(
wrong.is_empty(),
"{} of {} cases wrong:\n{}",
wrong.len(),
files.len(),
wrong.join("\n")
);
}
#[test]
fn mislabeled_entries_are_still_needed() {
let mislabeled = mislabeled();
for (case, why) in mislabeled {
let path = root().join(&case);
assert!(
path.is_file(),
"{case} is listed as mislabeled but is not in the suite"
);
assert!(
passes(&path, Dialect::Json5),
"{case} should pass as json5 ({why})"
);
assert!(
!passes(&path, Dialect::Json),
"{case} should fail as json ({why}); drop it from the list if the suite is fixed"
);
}
}