blob: 6b69e0d42cda9f39080336f9e1fbc6629587e8d6 [file] [log] [blame]
extern crate clap;
extern crate diff;
extern crate bindgen;
extern crate shlex;
use bindgen::Builder;
use std::fs;
use std::io::{BufRead, BufReader, Error, ErrorKind, Read, Write};
use std::path::PathBuf;
#[path="../src/options.rs"]
mod options;
use options::builder_from_flags;
fn compare_generated_header(header: &PathBuf,
builder: Builder)
-> Result<(), Error> {
let file_name = try!(header.file_name()
.ok_or(Error::new(ErrorKind::Other, "spawn_bindgen expects a file")));
let mut expected = PathBuf::from(header);
expected.pop();
expected.pop();
expected.push("expectations");
expected.push("tests");
expected.push(file_name);
expected.set_extension("rs");
// We skip the generate() error here so we get a full diff below
let output = match builder.generate() {
Ok(bindings) => bindings.to_string(),
Err(_) => "".to_string(),
};
let mut buffer = String::new();
{
if let Ok(expected_file) = fs::File::open(&expected) {
try!(BufReader::new(expected_file).read_to_string(&mut buffer));
}
}
if output == buffer {
if !output.is_empty() {
return Ok(());
}
return Err(Error::new(ErrorKind::Other,
"Something's gone really wrong!"));
}
println!("diff expected generated");
println!("--- expected: {:?}", expected);
println!("+++ generated from: {:?}", header);
for diff in diff::lines(&buffer, &output) {
match diff {
diff::Result::Left(l) => println!("-{}", l),
diff::Result::Both(l, _) => println!(" {}", l),
diff::Result::Right(r) => println!("+{}", r),
}
}
// Override the diff.
{
let mut expected_file = try!(fs::File::create(&expected));
try!(expected_file.write_all(output.as_bytes()));
}
Err(Error::new(ErrorKind::Other, "Header and binding differ!"))
}
fn create_bindgen_builder(header: &PathBuf) -> Result<Option<Builder>, Error> {
let source = try!(fs::File::open(header));
let reader = BufReader::new(source);
// Scoop up bindgen-flags from test header
let mut flags = Vec::with_capacity(2);
for line in reader.lines().take(3) {
let line = try!(line);
if line.contains("bindgen-flags: ") {
let extra_flags = line.split("bindgen-flags: ")
.last()
.and_then(shlex::split)
.unwrap();
flags.extend(extra_flags.into_iter());
} else if line.contains("bindgen-unstable") &&
cfg!(feature = "testing_only_llvm_stable") {
return Ok(None);
} else if line.contains("bindgen-osx-only") {
let prepend_flags = ["--raw-line", "#![cfg(target_os=\"macos\")]"];
flags = prepend_flags.into_iter()
.map(ToString::to_string)
.chain(flags)
.collect();
}
}
// Fool builder_from_flags() into believing it has real env::args_os...
// - add "bindgen" as executable name 0th element
// - add header filename as 1st element
// - prepend raw lines so they're in the right order for expected output
// - append the test header's bindgen flags
let header_str = try!(header.to_str()
.ok_or(Error::new(ErrorKind::Other, "Invalid header file name")));
let prepend = ["bindgen",
"--with-derive-default",
header_str,
"--raw-line",
"",
"--raw-line",
"#![allow(non_snake_case)]",
"--raw-line",
""];
let args = prepend.into_iter()
.map(ToString::to_string)
.chain(flags.into_iter());
builder_from_flags(args)
.map(|(builder, _, _)| Some(builder.no_unstable_rust()))
}
macro_rules! test_header {
($function:ident, $header:expr) => (
#[test]
fn $function() {
let header = PathBuf::from($header);
let result = create_bindgen_builder(&header)
.and_then(|builder| {
if let Some(builder) = builder {
compare_generated_header(&header, builder)
} else {
Ok(())
}
});
if let Err(err) = result {
panic!("{}", err);
}
}
)
}
// This file is generated by build.rs
include!(concat!(env!("OUT_DIR"), "/tests.rs"));