fn main() {
generate_tests_from_spec()
}
#[cfg(not(feature = "gen-tests"))]
fn generate_tests_from_spec() {}
#[cfg(feature = "gen-tests")]
fn generate_tests_from_spec() {
use std::fs::{self, File};
use std::io::{Read, Write};
use std::path::PathBuf;
let hardcoded = [
"./third_party/CommonMark/spec.txt",
"./third_party/CommonMark/smart_punct.txt",
"./third_party/GitHub/gfm_table.txt",
"./third_party/GitHub/gfm_strikethrough.txt",
"./third_party/GitHub/gfm_tasklist.txt",
];
let hardcoded_iter = hardcoded.iter().map(PathBuf::from);
let mut spec_files = fs::read_dir("./specs")
.expect("Could not find the 'specs' directory")
.filter_map(Result::ok)
.map(|d| d.path())
.filter(|p| p.extension().map(|e| e.to_owned()).is_some())
.chain(hardcoded_iter)
.collect::<Vec<_>>();
spec_files.sort_by(|p, q| p.file_stem().cmp(&q.file_stem()));
let spec_files = spec_files;
for file_path in &spec_files {
let mut raw_spec = String::new();
File::open(&file_path)
.and_then(|mut f| f.read_to_string(&mut raw_spec))
.expect("Could not read the spec file");
let rs_test_file = PathBuf::from("./tests/suite/")
.join(file_path.file_name().expect("Invalid filename"))
.with_extension("rs");
let mut spec_rs =
File::create(&rs_test_file).expect(&format!("Could not create {:?}", rs_test_file));
let spec_name = file_path.file_stem().unwrap().to_str().unwrap();
let spec = Spec::new(&raw_spec);
let mut n_tests = 0;
spec_rs
.write_all(b"// This file is auto-generated by the build script\n")
.unwrap();
spec_rs
.write_all(b"// Please, do not modify it manually\n")
.unwrap();
spec_rs
.write_all(b"\nuse super::test_markdown_html;\n")
.unwrap();
for (i, testcase) in spec.enumerate() {
spec_rs
.write_fmt(format_args!(
r###"
#[test]
fn {}_test_{i}() {{
let original = r##"{original}"##;
let expected = r##"{expected}"##;
test_markdown_html(original, expected, {smart_punct}, {metadata_blocks}, {old_footnotes});
}}
"###,
spec_name,
i = i + 1,
original = testcase.original,
expected = testcase.expected,
smart_punct = testcase.smart_punct,
metadata_blocks = testcase.metadata_blocks,
old_footnotes = testcase.old_footnotes,
))
.unwrap();
n_tests += 1;
}
println!(
"cargo:warning=Generated {} tests in {:?}",
n_tests, rs_test_file
);
}
let suite_mod_file = PathBuf::from("./tests/suite/mod").with_extension("rs");
let mut mod_rs =
File::create(&suite_mod_file).expect(&format!("Could not create {:?}", &suite_mod_file));
mod_rs
.write_all(b"// This file is auto-generated by the build script\n")
.unwrap();
mod_rs
.write_all(b"// Please, do not modify it manually\n")
.unwrap();
mod_rs
.write_all(b"\npub use super::test_markdown_html;\n\n")
.unwrap();
for file_path in &spec_files {
let mod_name = file_path.file_stem().unwrap().to_str().unwrap();
mod_rs.write_all(b"mod ").unwrap();
mod_rs.write_all(mod_name.as_bytes()).unwrap();
mod_rs.write_all(b";\n").unwrap();
}
}
#[cfg(feature = "gen-tests")]
pub struct Spec<'a> {
spec: &'a str,
}
#[cfg(feature = "gen-tests")]
impl<'a> Spec<'a> {
pub fn new(spec: &'a str) -> Self {
Spec { spec }
}
}
#[cfg(feature = "gen-tests")]
pub struct TestCase {
pub original: String,
pub expected: String,
pub smart_punct: bool,
pub metadata_blocks: bool,
pub old_footnotes: bool,
}
#[cfg(feature = "gen-tests")]
impl<'a> Iterator for Spec<'a> {
type Item = TestCase;
fn next(&mut self) -> Option<TestCase> {
let spec = self.spec;
let prefix = "```````````````````````````````` example";
let (i_start, smart_punct, metadata_blocks, old_footnotes) =
self.spec.find(prefix).and_then(|pos| {
let smartpunct_suffix = "_smartpunct\n";
let metadata_blocks_suffix = "_metadata_blocks\n";
let old_footnotes_suffix = "_old_footnotes\n";
if spec[(pos + prefix.len())..].starts_with(smartpunct_suffix) {
Some((
pos + prefix.len() + smartpunct_suffix.len(),
true,
false,
false,
))
} else if spec[(pos + prefix.len())..].starts_with(metadata_blocks_suffix) {
Some((
pos + prefix.len() + metadata_blocks_suffix.len(),
false,
true,
false,
))
} else if spec[(pos + prefix.len())..].starts_with(old_footnotes_suffix) {
Some((
pos + prefix.len() + old_footnotes_suffix.len(),
false,
false,
true,
))
} else if spec[(pos + prefix.len())..].starts_with('\n') {
Some((pos + prefix.len() + 1, false, false, false))
} else {
None
}
})?;
let i_end = self.spec[i_start..]
.find("\n.\n")
.map(|pos| (pos + 1) + i_start)?;
let e_end = self.spec[i_end + 2..]
.find("````````````````````````````````\n")
.map(|pos| pos + i_end + 2)?;
self.spec = &self.spec[e_end + 33..];
let test_case = TestCase {
original: spec[i_start..i_end].to_string().replace("→", "\t"),
expected: spec[i_end + 2..e_end].to_string().replace("→", "\t"),
smart_punct,
metadata_blocks,
old_footnotes,
};
Some(test_case)
}
}