#[cfg_attr(not(test), expect(dead_code, reason = "the corpus is a test fixture"))]
pub(crate) fn document(name: &str) -> &'static str {
match name {
"numbers.json" => include_str!("../../fixtures/documents/numbers.json"),
"numbers.yaml" => include_str!("../../fixtures/documents/numbers.yaml"),
"numbers.toml" => include_str!("../../fixtures/documents/numbers.toml"),
"mixed-array.toml" => include_str!("../../fixtures/documents/mixed-array.toml"),
"numbers.ini" => include_str!("../../fixtures/documents/numbers.ini"),
"numbers.env" => include_str!("../../fixtures/documents/numbers.env"),
"numbers.csv" => include_str!("../../fixtures/documents/numbers.csv"),
"numbers.tsv" => include_str!("../../fixtures/documents/numbers.tsv"),
"numbers.jsonc" => include_str!("../../fixtures/documents/numbers.jsonc"),
"numbers.txt" => include_str!("../../fixtures/documents/numbers.txt"),
"numbers.py" => include_str!("../../fixtures/documents/numbers.py"),
"numbers.rs" => include_str!("../../fixtures/documents/numbers.rs"),
"numbers.go" => include_str!("../../fixtures/documents/numbers.go"),
"numbers.java" => include_str!("../../fixtures/documents/numbers.java"),
"numbers.ts" => include_str!("../../fixtures/documents/numbers.ts"),
"numbers.js" => include_str!("../../fixtures/documents/numbers.js"),
"numbers.kt" => include_str!("../../fixtures/documents/numbers.kt"),
"numbers.cs" => include_str!("../../fixtures/documents/numbers.cs"),
"numbers.cpp" => include_str!("../../fixtures/documents/numbers.cpp"),
"numbers.c" => include_str!("../../fixtures/documents/numbers.c"),
"numbers.sql" => include_str!("../../fixtures/documents/numbers.sql"),
"numbers.sh" => include_str!("../../fixtures/documents/numbers.sh"),
other => panic!("the corpus has no document named {other}"),
}
}
#[cfg(test)]
mod tests {
use serde::Deserialize;
use crate::extract::render::js_number;
const CORPUS: &str = include_str!("../../fixtures/extraction.json");
#[derive(Debug, Deserialize)]
struct Corpus {
rendering: Vec<RenderCase>,
}
#[derive(Debug, Deserialize)]
struct RenderCase {
input: String,
expected: String,
}
#[test]
fn every_pinned_rendering_matches_javascript() {
let corpus: Corpus = serde_json::from_str(CORPUS).expect("the corpus is valid JSON");
assert!(!corpus.rendering.is_empty(), "the corpus pins no rendering");
for case in corpus.rendering {
let value: f64 = case.input.parse().expect(&case.input);
assert_eq!(js_number(value), case.expected, "rendering {}", case.input);
}
}
#[test]
fn serde_json_still_rounds_a_large_integer_differently() {
let token = "123456789012345680000";
let via_serde: f64 = serde_json::from_str(token).expect("a number");
let via_std: f64 = token.parse().expect("a number");
assert_ne!(
via_serde.to_bits(),
via_std.to_bits(),
"serde_json now parses this correctly — json.rs can stop working around it"
);
assert_eq!(
js_number(via_std),
token,
"str::parse is the one that agrees"
);
}
#[test]
fn the_corpus_pins_both_notation_boundaries() {
let corpus: Corpus = serde_json::from_str(CORPUS).expect("the corpus is valid JSON");
let pinned: Vec<&str> = corpus
.rendering
.iter()
.map(|case| case.expected.as_str())
.collect();
for boundary in ["100000000000000000000", "1e+21", "0.000001", "1e-7"] {
assert!(
pinned.contains(&boundary),
"the corpus no longer pins {boundary}"
);
}
}
}
#[cfg(test)]
mod document_tests {
use serde::Deserialize;
use super::document;
use crate::extract::{Notation, Number, Options, extract};
const CORPUS: &str = include_str!("../../fixtures/extraction.json");
#[derive(Debug, Deserialize)]
struct Corpus {
documents: Vec<Case>,
}
#[derive(Debug, Deserialize)]
struct Case {
name: String,
file: String,
#[serde(rename = "fileType")]
file_type: String,
expected: Vec<Expected>,
errors: Vec<String>,
}
#[derive(Debug, Deserialize)]
struct Expected {
value: String,
notation: Notation,
}
#[test]
fn every_corpus_document_reproduces() {
let corpus: Corpus = serde_json::from_str(CORPUS).expect("the corpus is valid JSON");
assert!(!corpus.documents.is_empty(), "the corpus is empty");
for case in corpus.documents {
if case.file == "mixed-array.toml" {
assert!(
!case.errors.is_empty(),
"the divergence has stopped being one"
);
continue;
}
let expected: Vec<Number> = case
.expected
.into_iter()
.map(|pinned| Number {
value: pinned.value,
notation: pinned.notation,
})
.collect();
assert_eq!(
extract(document(&case.file), &case.file_type, Options),
expected,
"{}",
case.name
);
}
}
#[test]
fn the_corpus_covers_every_extractor() {
use crate::extract::{FALLBACK_FORMAT, SUPPORTED_FORMATS};
let corpus: Corpus = serde_json::from_str(CORPUS).expect("the corpus is valid JSON");
for format in SUPPORTED_FORMATS.into_iter().chain([FALLBACK_FORMAT]) {
assert!(
corpus.documents.iter().any(|case| case.file_type == format),
"no corpus case reads {format}"
);
}
}
#[test]
fn the_corpus_pins_every_notation() {
let corpus: Corpus = serde_json::from_str(CORPUS).expect("the corpus is valid JSON");
for notation in [
Notation::Decimal,
Notation::Hex,
Notation::Binary,
Notation::Octal,
Notation::Scientific,
Notation::BigInt,
] {
assert!(
corpus
.documents
.iter()
.flat_map(|case| &case.expected)
.any(|pinned| pinned.notation == notation),
"no corpus case pins {notation:?}"
);
}
}
}