extern crate serde_json;
extern crate slug;
use std::collections::BTreeMap;
use std::env;
use std::path::Path;
use std::fs::{self, File};
use std::io::{Read, Write};
use self::serde_json::Value;
pub fn main() {
let out_dir = env::var_os("OUT_DIR").expect("OUT_DIR not specified");
let bench_path = Path::new(&out_dir).join("benches.rs");
let mut bench_file = File::create(&bench_path).expect("Could not create file");
let compliance_path = Path::new(&out_dir).join("compliance_tests.rs");
let mut compliance_file = File::create(&compliance_path).expect("Could not create file");
let suites = load_test_suites();
for (suite_num, &(ref filename, ref suite)) in suites.iter().enumerate() {
let suite_obj = suite.as_object().expect("Suite not object");
let given = suite_obj.get("given").expect("No given value");
let cases = suite_obj.get("cases").expect("No cases value");
let short_filename = filename.replace(".json", "").replace("tests/compliance/", "");
let given_string = serde_json::to_string(given).unwrap();
for (case_num, case) in cases.as_array().expect("cases not array").iter().enumerate() {
let case_obj = case.as_object().expect("case not object");
if case_obj.get("bench").is_some() {
generate_bench(&short_filename, suite_num, case_num, case_obj,
&given_string, &mut bench_file);
} else {
generate_test(&short_filename, suite_num, case_num, case_obj,
&given_string, &mut compliance_file);
}
}
}
}
pub fn load_test_suites() -> Vec<(String, Value)> {
let mut result = vec![];
let files = fs::read_dir("tests/compliance").expect("Invalid directory: tests/compliance");
for filename in files {
let path = filename.expect("Invalid file").path();
let file_path = path.to_str().expect("Could not to_str file").to_string();
let mut f = File::open(path).expect("Unable to open file");
let mut file_data = String::new();
f.read_to_string(&mut file_data).expect("Could to read JSON to string");
let mut suite_json: Value = serde_json::from_str(&file_data).expect("invalid JSON");
let mut suites = suite_json.as_array_mut().expect("Test suite is not a JSON array");
while let Some(suite) = suites.pop() {
result.push((file_path.clone(), suite));
}
}
result
}
#[inline]
fn get_expr(case: &BTreeMap<String, Value>) -> &str {
case.get("expression")
.expect("No expression in case")
.as_str()
.expect("Could not convert case to string")
}
#[inline]
fn slugify(s: &str) -> String {
let mut s = slug::slugify(s).replace("-", "_");
if s.len() > 25 {
s.truncate(25);
}
s
}
#[inline]
fn generate_fn_name(filename: &str,
suite_num: usize,
case_num: usize,
case: &BTreeMap<String, Value>) -> String {
let expr = get_expr(case);
let description = match case.get("comment") {
Some(ref c) => c.as_str().expect("comment is not a string"),
None => expr
};
format!("{}_{}_{}_{}", slugify(filename), suite_num, case_num, slugify(description))
}
fn generate_bench(filename: &str,
suite_num: usize,
case_num: usize,
case: &BTreeMap<String, Value>,
given_string: &str,
f: &mut File) {
let expr = get_expr(case);
let expr_string = expr.replace("\"", "\\\"");
let fn_suffix = generate_fn_name(filename, suite_num, case_num, case);
let bench_type = case.get("bench").unwrap().as_str().expect("bench is not a string");
if bench_type != "parse" && bench_type != "full" && bench_type != "interpret" {
panic!("invalid bench type: {}", bench_type);
}
if bench_type == "parse" || bench_type == "full" {
f.write_all(format!("\
#[bench]
fn {}_parse_lex(b: &mut Bencher) {{
b.iter(|| {{ parse({:?}).ok() }});
}}
", fn_suffix, expr_string).as_bytes()).expect("Error writing parse benchmark");
}
if bench_type == "interpret" || bench_type == "full" {
f.write_all(format!("\
#[bench]
fn {}_interpret(b: &mut Bencher) {{
let data = Rcvar::new(Variable::from_json({:?}).expect(\"Invalid JSON given\"));
let expr = compile({:?}).unwrap();
b.iter(|| {{ expr.search(&data).ok() }});
}}
", fn_suffix, given_string, expr_string).as_bytes()).expect("Error writing interpret benchmark");
}
if bench_type == "full" {
f.write_all(format!("\
#[bench]
fn {}_full(b: &mut Bencher) {{
let data = Rcvar::new(Variable::from_json({:?}).expect(\"Invalid JSON given\"));
b.iter(|| {{ compile({:?}).unwrap().search(&data).ok() }});
}}
", fn_suffix, given_string, expr_string).as_bytes()).expect("Error writing interpret benchmark");
}
}
fn generate_test(filename: &str,
suite_num: usize,
case_num: usize,
case: &BTreeMap<String, Value>,
given_string: &str,
f: &mut File) {
let fn_suffix = generate_fn_name(filename, suite_num, case_num, case);
let case_string = serde_json::to_string(case).expect("Could not encode case");
f.write_all(format!("\
#[test]
fn test_{}() {{
let case: TestCase = TestCase::from_str({:?}).unwrap();
let data = Rcvar::new(Variable::from_json({:?}).unwrap());
case.assert({:?}, data).unwrap();
}}
", fn_suffix, case_string, given_string, filename).as_bytes()).expect("Unable to write test");
}