use serde::Deserialize;
use super::{PathType, extract, heuristics};
const EXTRACTION: &str = include_str!("../../fixtures/extraction.json");
const HEURISTICS: &str = include_str!("../../fixtures/heuristics.json");
pub(crate) const DOCUMENTS: [(&str, &str); 13] = [
(
"srcset-data-uri.html",
include_str!("../../fixtures/documents/srcset-data-uri.html"),
),
(
"paths.js",
include_str!("../../fixtures/documents/paths.js"),
),
(
"paths.json",
include_str!("../../fixtures/documents/paths.json"),
),
(
"comments.jsonc",
include_str!("../../fixtures/documents/comments.jsonc"),
),
(
"unicode.json",
include_str!("../../fixtures/documents/unicode.json"),
),
(
"paths.toml",
include_str!("../../fixtures/documents/paths.toml"),
),
(
"paths.csv",
include_str!("../../fixtures/documents/paths.csv"),
),
(
"paths.env",
include_str!("../../fixtures/documents/paths.env"),
),
(
"paths.css",
include_str!("../../fixtures/documents/paths.css"),
),
(
"paths.html",
include_str!("../../fixtures/documents/paths.html"),
),
(
"paths.yml",
include_str!("../../fixtures/documents/paths.yml"),
),
(
"paths.py",
include_str!("../../fixtures/documents/paths.py"),
),
(
"paths.md",
include_str!("../../fixtures/documents/paths.md"),
),
];
pub(crate) fn document(name: &str) -> &'static str {
DOCUMENTS
.iter()
.find(|(file, _)| *file == name)
.map_or_else(
|| panic!("the corpus refers to {name}, which is not embedded"),
|(_, content)| *content,
)
}
#[derive(Debug, Deserialize)]
struct ExtractionCase {
name: String,
file: String,
#[serde(rename = "languageId")]
language_id: String,
expected: Vec<ExpectedPath>,
}
#[derive(Debug, Deserialize, PartialEq, Eq)]
struct ExpectedPath {
value: String,
#[serde(rename = "type")]
kind: String,
line: usize,
column: usize,
context: String,
}
#[derive(Debug, Deserialize)]
struct HeuristicsCorpus {
#[serde(rename = "isPathLike")]
is_path_like: Vec<PathLikeCase>,
#[serde(rename = "classifyPathType")]
classify_path_type: Vec<ClassifyCase>,
}
#[derive(Debug, Deserialize)]
struct PathLikeCase {
input: String,
expected: bool,
}
#[derive(Debug, Deserialize)]
struct ClassifyCase {
input: String,
expected: String,
}
fn kind_name(kind: PathType) -> &'static str {
match kind {
PathType::File => "file",
PathType::Relative => "relative",
PathType::Absolute => "absolute",
PathType::Url => "url",
PathType::Unknown => "unknown",
}
}
#[test]
fn every_extraction_case_reproduces() {
let cases: Vec<ExtractionCase> =
serde_json::from_str(EXTRACTION).expect("the corpus is valid JSON");
assert!(!cases.is_empty(), "the corpus is empty");
for case in cases {
let result = extract(document(&case.file), &case.language_id);
assert!(result.success, "{}: {:?}", case.name, result.errors);
let actual: Vec<ExpectedPath> = result
.paths
.iter()
.map(|path| ExpectedPath {
value: path.value.clone(),
kind: kind_name(path.kind).to_string(),
line: path.position.line,
column: path.position.column,
context: path.context.clone(),
})
.collect();
assert_eq!(actual, case.expected, "{}", case.name);
}
}
#[test]
fn every_heuristics_case_reproduces() {
let corpus: HeuristicsCorpus =
serde_json::from_str(HEURISTICS).expect("the corpus is valid JSON");
for case in corpus.is_path_like {
assert_eq!(
heuristics::is_path_like(&case.input),
case.expected,
"is_path_like {:?}",
case.input
);
}
for case in corpus.classify_path_type {
assert_eq!(
kind_name(heuristics::classify_path_type(&case.input)),
case.expected,
"classify_path_type {:?}",
case.input
);
}
}
#[test]
fn the_corpus_and_the_embedded_documents_match() {
let cases: Vec<ExtractionCase> =
serde_json::from_str(EXTRACTION).expect("the corpus is valid JSON");
for (name, _) in DOCUMENTS {
assert!(
cases.iter().any(|case| case.file == name),
"{name} is embedded but no case uses it"
);
}
for case in &cases {
assert!(
DOCUMENTS.iter().any(|(name, _)| *name == case.file),
"{} names {}, which is not embedded",
case.name,
case.file
);
}
}
#[test]
fn the_unicode_case_pins_utf16_columns() {
let result = extract(document("unicode.json"), "json");
let columns: Vec<usize> = result.paths.iter().map(|p| p.position.column).collect();
assert_eq!(
columns,
[11, 17],
"columns must count UTF-16 code units; bytes would answer [12, 19]"
);
}