use implicit_predicates_gen::{Generated, generate};
use proc_macro2::TokenStream;
const REGENERATE: &str = "run the tests.";
const ORIENT3D: &str = include_str!("fixtures/orient3d.pred");
const INSPHERE: &str = include_str!("fixtures/insphere.pred");
const SOURCES: [(&str, &str); 2] = [
(ORIENT3D, "predicates/orient3d.pred"),
(INSPHERE, "predicates/insphere.pred"),
];
fn generated(source: &str, file: &str) -> Generated {
let mut all = generate(source, file, REGENERATE).unwrap();
assert_eq!(all.len(), 1);
all.remove(0)
}
fn case_literals(checks: &str) -> Vec<&str> {
let start = checks
.find("const BOUND_EDGE_CASES")
.expect("a table of cases");
let table = &checks[start..];
let table = &table[table.find('=').unwrap()..table.find(";\n").unwrap()];
table
.split(|c: char| !c.is_ascii_alphanumeric())
.filter(|word| word.starts_with("0x"))
.collect()
}
#[test]
fn the_checks_file_parses_as_rust() {
for (source, file) in SOURCES {
let g = generated(source, file);
assert!(!g.checks.is_empty(), "{file}");
g.checks
.parse::<TokenStream>()
.unwrap_or_else(|e| panic!("{file}: {e}"));
assert!(g.checks.starts_with(&format!(
"// Generated by implicit-predicates-gen from {file}. Do not edit.\n"
)));
assert!(g.checks.contains("pub fn exact("), "{file}");
assert!(g.checks.contains("pub fn bound_edge_cases("), "{file}");
}
}
#[test]
fn edge_cases_are_emitted_as_bit_patterns() {
for (source, file, points) in [
(ORIENT3D, "predicates/orient3d.pred", 4),
(INSPHERE, "predicates/insphere.pred", 5),
] {
let g = generated(source, file);
assert!(g.checks.contains("f64::from_bits"), "{file}");
let literals = case_literals(&g.checks);
assert_eq!(literals.len() % (3 * points), 0, "{file}");
let cases = literals.len() / (3 * points);
assert!(cases >= 64, "{file}: {cases} cases");
assert_eq!(cases % 2, 0, "{file}");
for literal in literals {
let digits = literal.strip_prefix("0x").unwrap().replace('_', "");
assert_eq!(digits.len(), 16, "{literal}");
let x = f64::from_bits(u64::from_str_radix(&digits, 16).unwrap());
assert!(x.is_finite(), "{literal}");
}
}
}
#[test]
fn generation_of_checks_is_deterministic() {
for (source, file) in SOURCES {
let first = generated(source, file);
let second = generated(source, file);
assert_eq!(first.checks, second.checks, "{file}");
}
}