forked from pulldown-cmark/pulldown-cmark
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbuild.rs
152 lines (120 loc) · 4.64 KB
/
build.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
fn main() {
generate_tests_from_spec()
}
// If the "gen-tests" feature is absent,
// this function will be compiled down to nothing
#[cfg(not(feature="gen-tests"))]
fn generate_tests_from_spec() {}
// If the feature is present, generate tests
// from any .txt file present in the specs/ directory
//
// Test cases are present in the files in the
// following format:
//
// ```````````````````````````````` example
// markdown
// .
// expected html output
// ````````````````````````````````
#[cfg(feature="gen-tests")]
fn generate_tests_from_spec() {
use std::path::{PathBuf};
use std::fs::{self, File};
use std::io::{Read, Write};
// This is a hardcoded path to the CommonMark spec because it is not situated in
// the specs/ directory. It's in an array to easily chain it to the other iterator
// and make it easy to eventually add other hardcoded paths in the future if needed
let hardcoded = ["./third_party/CommonMark/spec.txt"];
let hardcoded_iter = hardcoded.into_iter()
.map(PathBuf::from);
// Create an iterator over the files in the specs/ directory that have a .txt extension
let 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);
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/")
.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(b"// This file is auto-generated by the build script\n").unwrap();
spec_rs.write(b"// Please, do not modify it manually\n").unwrap();
spec_rs.write(b"\nextern crate pulldown_cmark;\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}"##;
use pulldown_cmark::{{Parser, html, Options, OPTION_ENABLE_TABLES, OPTION_ENABLE_FOOTNOTES}};
let mut s = String::new();
let mut opts = Options::empty();
opts.insert(OPTION_ENABLE_TABLES);
opts.insert(OPTION_ENABLE_FOOTNOTES);
let p = Parser::new_ext(&original, opts);
html::push_html(&mut s, p);
assert_eq!(expected, s);
}}"###,
spec_name,
i=i+1,
original=testcase.original,
expected=testcase.expected
),
).unwrap();
n_tests += 1;
}
println!("cargo:warning=Generated {} tests in {:?}", n_tests, rs_test_file);
}
}
#[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: spec }
}
}
#[cfg(feature="gen-tests")]
pub struct TestCase {
pub original: String,
pub expected: String,
}
#[cfg(feature="gen-tests")]
impl<'a> Iterator for Spec<'a> {
type Item = TestCase;
fn next(&mut self) -> Option<TestCase> {
let spec = self.spec;
let i_start = match self.spec.find("```````````````````````````````` example\n").map(|pos| pos + 41) {
Some(pos) => pos,
None => return None,
};
let i_end = match self.spec[i_start..].find("\n.\n").map(|pos| (pos + 1) + i_start){
Some(pos) => pos,
None => return None,
};
let e_end = match self.spec[i_end + 2..].find("````````````````````````````````\n").map(|pos| pos + i_end + 2){
Some(pos) => pos,
None => return None,
};
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")
};
Some(test_case)
}
}