summaryrefslogtreecommitdiff
path: root/tests/tests.rs
diff options
context:
space:
mode:
Diffstat (limited to 'tests/tests.rs')
-rw-r--r--tests/tests.rs688
1 files changed, 0 insertions, 688 deletions
diff --git a/tests/tests.rs b/tests/tests.rs
deleted file mode 100644
index ac53d739..00000000
--- a/tests/tests.rs
+++ /dev/null
@@ -1,688 +0,0 @@
-extern crate bindgen;
-extern crate clap;
-extern crate diff;
-#[cfg(feature = "logging")]
-extern crate env_logger;
-extern crate shlex;
-
-use bindgen::{clang_version, Builder};
-use std::env;
-use std::fs;
-use std::io::{self, BufRead, BufReader, Error, ErrorKind, Read, Write};
-use std::path::{Path, PathBuf};
-use std::process;
-use std::sync::Once;
-
-#[path = "../src/options.rs"]
-mod options;
-use crate::options::builder_from_flags;
-
-mod parse_callbacks;
-
-// Run `rustfmt` on the given source string and return a tuple of the formatted
-// bindings, and rustfmt's stderr.
-fn rustfmt(source: String) -> (String, String) {
- static CHECK_RUSTFMT: Once = Once::new();
-
- CHECK_RUSTFMT.call_once(|| {
- if env::var_os("RUSTFMT").is_some() {
- return;
- }
-
- let mut rustfmt = {
- let mut p = process::Command::new("rustup");
- p.args(["run", "nightly", "rustfmt", "--version"]);
- p
- };
-
- let have_working_rustfmt = rustfmt
- .stdout(process::Stdio::null())
- .stderr(process::Stdio::null())
- .status()
- .ok()
- .map_or(false, |status| status.success());
-
- if !have_working_rustfmt {
- panic!(
- "
-The latest `rustfmt` is required to run the `bindgen` test suite. Install
-`rustfmt` with:
-
- $ rustup update nightly
- $ rustup component add rustfmt --toolchain nightly
-"
- );
- }
- });
-
- let mut child = match env::var_os("RUSTFMT") {
- Some(r) => process::Command::new(r),
- None => {
- let mut p = process::Command::new("rustup");
- p.args(["run", "nightly", "rustfmt"]);
- p
- }
- };
-
- let mut child = child
- .args([
- "--config-path",
- concat!(env!("CARGO_MANIFEST_DIR"), "/tests/rustfmt.toml"),
- ])
- .stdin(process::Stdio::piped())
- .stdout(process::Stdio::piped())
- .stderr(process::Stdio::piped())
- .spawn()
- .expect("should spawn `rustup run nightly rustfmt`");
-
- let mut stdin = child.stdin.take().unwrap();
- let mut stdout = child.stdout.take().unwrap();
- let mut stderr = child.stderr.take().unwrap();
-
- // Write to stdin in a new thread, so that we can read from stdout on this
- // thread. This keeps the child from blocking on writing to its stdout which
- // might block us from writing to its stdin.
- let stdin_handle =
- ::std::thread::spawn(move || stdin.write_all(source.as_bytes()));
-
- // Read stderr on a new thread for similar reasons.
- let stderr_handle = ::std::thread::spawn(move || {
- let mut output = vec![];
- io::copy(&mut stderr, &mut output)
- .map(|_| String::from_utf8_lossy(&output).to_string())
- });
-
- let mut output = vec![];
- io::copy(&mut stdout, &mut output).expect("Should copy stdout into vec OK");
-
- // Ignore actual rustfmt status because it is often non-zero for trivial
- // things.
- let _ = child.wait().expect("should wait on rustfmt child OK");
-
- stdin_handle
- .join()
- .expect("writer thread should not have panicked")
- .expect("should have written to child rustfmt's stdin OK");
-
- let bindings = String::from_utf8(output)
- .expect("rustfmt should only emit valid utf-8");
-
- let stderr = stderr_handle
- .join()
- .expect("stderr reader thread should not have panicked")
- .expect("should have read child rustfmt's stderr OK");
-
- (bindings, stderr)
-}
-
-fn should_overwrite_expected() -> bool {
- if let Some(var) = env::var_os("BINDGEN_OVERWRITE_EXPECTED") {
- if var == "1" {
- return true;
- }
- if var != "0" && var != "" {
- panic!("Invalid value of BINDGEN_OVERWRITE_EXPECTED");
- }
- }
- false
-}
-
-fn error_diff_mismatch(
- actual: &str,
- expected: &str,
- header: Option<&Path>,
- filename: &Path,
-) -> Result<(), Error> {
- println!("diff expected generated");
- println!("--- expected: {:?}", filename);
- if let Some(header) = header {
- println!("+++ generated from: {:?}", header);
- }
-
- for diff in diff::lines(expected, actual) {
- match diff {
- diff::Result::Left(l) => println!("-{}", l),
- diff::Result::Both(l, _) => println!(" {}", l),
- diff::Result::Right(r) => println!("+{}", r),
- }
- }
-
- if should_overwrite_expected() {
- // Overwrite the expectation with actual output.
- let mut expectation_file = fs::File::create(filename)?;
- expectation_file.write_all(actual.as_bytes())?;
- }
-
- if let Some(var) = env::var_os("BINDGEN_TESTS_DIFFTOOL") {
- //usecase: var = "meld" -> You can hand check differences
- let name = match filename.components().last() {
- Some(std::path::Component::Normal(name)) => name,
- _ => panic!("Why is the header variable so weird?"),
- };
- let actual_result_path =
- PathBuf::from(env::var("OUT_DIR").unwrap()).join(name);
- let mut actual_result_file = fs::File::create(&actual_result_path)?;
- actual_result_file.write_all(actual.as_bytes())?;
- std::process::Command::new(var)
- .args([filename, &actual_result_path])
- .output()?;
- }
-
- Err(Error::new(ErrorKind::Other, "Header and binding differ! Run with BINDGEN_OVERWRITE_EXPECTED=1 in the environment to automatically overwrite the expectation or with BINDGEN_TESTS_DIFFTOOL=meld to do this manually."))
-}
-
-fn compare_generated_header(
- header: &Path,
- builder: BuilderState,
- check_roundtrip: bool,
-) -> Result<(), Error> {
- let file_name = header.file_name().ok_or_else(|| {
- Error::new(ErrorKind::Other, "compare_generated_header expects a file")
- })?;
-
- let mut expectation = PathBuf::from(header);
- expectation.pop();
- expectation.pop();
- expectation.push("expectations");
- expectation.push("tests");
-
- let mut looked_at = vec![];
- let mut expectation_file;
-
- // Try more specific expectations first.
- {
- let mut expectation = expectation.clone();
-
- if cfg!(feature = "testing_only_libclang_9") {
- expectation.push("libclang-9");
- } else if cfg!(feature = "testing_only_libclang_5") {
- expectation.push("libclang-5");
- } else {
- match clang_version().parsed {
- None => expectation.push("libclang-9"),
- Some(version) => {
- let (maj, min) = version;
- let version_str = if maj >= 9 {
- "9".to_owned()
- } else if maj >= 5 {
- "5".to_owned()
- } else if maj >= 4 {
- "4".to_owned()
- } else {
- format!("{}.{}", maj, min)
- };
- expectation.push(format!("libclang-{}", version_str));
- }
- }
- }
-
- expectation.push(file_name);
- expectation.set_extension("rs");
- expectation_file = fs::File::open(&expectation).ok();
- looked_at.push(expectation);
- }
-
- // Try the default path otherwise.
- if expectation_file.is_none() {
- expectation.push(file_name);
- expectation.set_extension("rs");
- expectation_file = fs::File::open(&expectation).ok();
- looked_at.push(expectation.clone());
- }
-
- let mut expected = String::new();
- match expectation_file {
- Some(f) => {
- BufReader::new(f).read_to_string(&mut expected)?;
- }
- None => panic!(
- "missing test expectation file and/or 'testing_only_libclang_$VERSION' \
- feature for header '{}'; looking for expectation file at '{:?}'",
- header.display(),
- looked_at,
- ),
- };
-
- let (builder, roundtrip_builder) = builder.into_builder(check_roundtrip)?;
-
- // We skip the generate() error here so we get a full diff below
- let (actual, rustfmt_stderr) = match builder.generate() {
- Ok(bindings) => {
- let actual = bindings.to_string();
- rustfmt(actual)
- }
- Err(_) => ("<error generating bindings>".to_string(), "".to_string()),
- };
- println!("{}", rustfmt_stderr);
-
- let (expected, rustfmt_stderr) = rustfmt(expected);
- println!("{}", rustfmt_stderr);
-
- if actual.is_empty() {
- return Err(Error::new(
- ErrorKind::Other,
- "Something's gone really wrong!",
- ));
- }
-
- if actual != expected {
- println!("{}", rustfmt_stderr);
- return error_diff_mismatch(
- &actual,
- &expected,
- Some(header),
- looked_at.last().unwrap(),
- );
- }
-
- if let Some(roundtrip_builder) = roundtrip_builder {
- if let Err(e) =
- compare_generated_header(header, roundtrip_builder, false)
- {
- return Err(Error::new(ErrorKind::Other, format!("Checking CLI flags roundtrip errored! You probably need to fix Builder::command_line_flags. {}", e)));
- }
- }
-
- Ok(())
-}
-
-fn builder() -> Builder {
- #[cfg(feature = "logging")]
- let _ = env_logger::try_init();
-
- bindgen::builder().disable_header_comment()
-}
-
-struct BuilderState {
- builder: Builder,
- parse_callbacks: Option<String>,
-}
-
-impl BuilderState {
- fn into_builder(
- self,
- with_roundtrip_builder: bool,
- ) -> Result<(Builder, Option<BuilderState>), Error> {
- let roundtrip_builder = if with_roundtrip_builder {
- let mut flags = self.builder.command_line_flags();
- flags.insert(0, "bindgen".into());
- let mut builder = builder_from_flags(flags.into_iter())?.0;
- if let Some(ref parse_cb) = self.parse_callbacks {
- builder =
- builder.parse_callbacks(parse_callbacks::lookup(parse_cb));
- }
- Some(BuilderState {
- builder,
- parse_callbacks: self.parse_callbacks,
- })
- } else {
- None
- };
- Ok((self.builder, roundtrip_builder))
- }
-}
-
-fn create_bindgen_builder(header: &Path) -> Result<BuilderState, Error> {
- #[cfg(feature = "logging")]
- let _ = env_logger::try_init();
-
- let source = fs::File::open(header)?;
- let reader = BufReader::new(source);
-
- // Scoop up bindgen-flags from test header
- let mut flags = Vec::with_capacity(2);
- let mut parse_callbacks = None;
-
- for line in reader.lines() {
- let line = line?;
- if !line.starts_with("// bindgen") {
- continue;
- }
-
- 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-osx-only") {
- let prepend_flags = ["--raw-line", "#![cfg(target_os=\"macos\")]"];
- flags = prepend_flags
- .iter()
- .map(ToString::to_string)
- .chain(flags)
- .collect();
- } else if line.contains("bindgen-parse-callbacks: ") {
- let parse_cb =
- line.split("bindgen-parse-callbacks: ").last().unwrap();
- parse_callbacks = Some(parse_cb.to_owned());
- }
- }
-
- // Different platforms have various different conventions like struct padding, mangling, etc.
- // We make the default target as x86_64-unknown-linux
- if flags.iter().all(|flag| !flag.starts_with("--target=")) {
- if !flags.iter().any(|flag| flag == "--") {
- flags.push("--".into());
- }
- flags.push("--target=x86_64-unknown-linux".into());
- }
-
- // 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 = header.to_str().ok_or_else(|| {
- Error::new(ErrorKind::Other, "Invalid header file name")
- })?;
-
- let prepend = [
- "bindgen",
- // We format in `compare_generated_header` ourselves to have a little
- // more control.
- "--no-rustfmt-bindings",
- "--with-derive-default",
- "--disable-header-comment",
- "--vtable-generation",
- header_str,
- "--raw-line",
- "",
- "--raw-line",
- "#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]",
- "--raw-line",
- "",
- ];
-
- let args = prepend
- .iter()
- .map(ToString::to_string)
- .chain(flags.into_iter());
-
- let mut builder = builder_from_flags(args)?.0;
- if let Some(ref parse_cb) = parse_callbacks {
- builder = builder.parse_callbacks(parse_callbacks::lookup(parse_cb));
- }
- Ok(BuilderState {
- builder,
- parse_callbacks,
- })
-}
-
-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| {
- let check_roundtrip =
- env::var_os("BINDGEN_DISABLE_ROUNDTRIP_TEST").is_none();
- compare_generated_header(&header, builder, check_roundtrip)
- });
-
- if let Err(err) = result {
- panic!("{}", err);
- }
- }
- };
-}
-
-// This file is generated by build.rs
-include!(concat!(env!("OUT_DIR"), "/tests.rs"));
-
-#[test]
-#[cfg_attr(target_os = "windows", ignore)]
-fn test_clang_env_args() {
- std::env::set_var(
- "BINDGEN_EXTRA_CLANG_ARGS",
- "-D_ENV_ONE=1 -D_ENV_TWO=\"2 -DNOT_THREE=1\"",
- );
- let actual = builder()
- .disable_header_comment()
- .header_contents(
- "test.hpp",
- "#ifdef _ENV_ONE\nextern const int x[] = { 42 };\n#endif\n\
- #ifdef _ENV_TWO\nextern const int y[] = { 42 };\n#endif\n\
- #ifdef NOT_THREE\nextern const int z[] = { 42 };\n#endif\n",
- )
- .generate()
- .unwrap()
- .to_string();
-
- let (actual, stderr) = rustfmt(actual);
- println!("{}", stderr);
-
- let (expected, _) = rustfmt(
- "extern \"C\" {
- pub static x: [::std::os::raw::c_int; 1usize];
-}
-extern \"C\" {
- pub static y: [::std::os::raw::c_int; 1usize];
-}
-"
- .to_string(),
- );
-
- assert_eq!(expected, actual);
-}
-
-#[test]
-fn test_header_contents() {
- let actual = builder()
- .disable_header_comment()
- .header_contents("test.h", "int foo(const char* a);")
- .clang_arg("--target=x86_64-unknown-linux")
- .generate()
- .unwrap()
- .to_string();
-
- let (actual, stderr) = rustfmt(actual);
- println!("{}", stderr);
-
- let (expected, _) = rustfmt(
- "extern \"C\" {
- pub fn foo(a: *const ::std::os::raw::c_char) -> ::std::os::raw::c_int;
-}
-"
- .to_string(),
- );
-
- assert_eq!(expected, actual);
-}
-
-#[test]
-fn test_multiple_header_calls_in_builder() {
- let actual = builder()
- .header(concat!(
- env!("CARGO_MANIFEST_DIR"),
- "/tests/headers/func_ptr.h"
- ))
- .header(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/headers/char.h"))
- .clang_arg("--target=x86_64-unknown-linux")
- .generate()
- .unwrap()
- .to_string();
-
- let (actual, stderr) = rustfmt(actual);
- println!("{}", stderr);
-
- let expected_filename = concat!(
- env!("CARGO_MANIFEST_DIR"),
- "/tests/expectations/tests/test_multiple_header_calls_in_builder.rs"
- );
- let expected = include_str!(concat!(
- env!("CARGO_MANIFEST_DIR"),
- "/tests/expectations/tests/test_multiple_header_calls_in_builder.rs"
- ));
- let (expected, _) = rustfmt(expected.to_string());
-
- if actual != expected {
- println!("Generated bindings differ from expected!");
- error_diff_mismatch(
- &actual,
- &expected,
- None,
- Path::new(expected_filename),
- )
- .unwrap();
- }
-}
-
-#[test]
-fn test_multiple_header_contents() {
- let actual = builder()
- .header_contents("test.h", "int foo(const char* a);")
- .header_contents("test2.h", "float foo2(const char* b);")
- .clang_arg("--target=x86_64-unknown-linux")
- .generate()
- .unwrap()
- .to_string();
-
- let (actual, stderr) = rustfmt(actual);
- println!("{}", stderr);
-
- let (expected, _) = rustfmt(
- "extern \"C\" {
- pub fn foo2(b: *const ::std::os::raw::c_char) -> f32;
-}
-extern \"C\" {
- pub fn foo(a: *const ::std::os::raw::c_char) -> ::std::os::raw::c_int;
-}
-"
- .to_string(),
- );
-
- assert_eq!(expected, actual);
-}
-
-#[test]
-fn test_mixed_header_and_header_contents() {
- let actual = builder()
- .header(concat!(
- env!("CARGO_MANIFEST_DIR"),
- "/tests/headers/func_ptr.h"
- ))
- .header(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/headers/char.h"))
- .header_contents("test.h", "int bar(const char* a);")
- .header_contents("test2.h", "float bar2(const char* b);")
- .clang_arg("--target=x86_64-unknown-linux")
- .generate()
- .unwrap()
- .to_string();
-
- let (actual, stderr) = rustfmt(actual);
- println!("{}", stderr);
-
- let expected_filename = concat!(
- env!("CARGO_MANIFEST_DIR"),
- "/tests/expectations/tests/test_mixed_header_and_header_contents.rs"
- );
- let expected = include_str!(concat!(
- env!("CARGO_MANIFEST_DIR"),
- "/tests/expectations/tests/test_mixed_header_and_header_contents.rs"
- ));
- let (expected, _) = rustfmt(expected.to_string());
- if expected != actual {
- error_diff_mismatch(
- &actual,
- &expected,
- None,
- Path::new(expected_filename),
- )
- .unwrap();
- }
-}
-
-#[test]
-// Doesn't support executing sh file on Windows.
-// We may want to implement it in Rust so that we support all systems.
-#[cfg(not(target_os = "windows"))]
-fn no_system_header_includes() {
- use std::process::Command;
- assert!(Command::new("./ci/no-includes.sh")
- .current_dir(env!("CARGO_MANIFEST_DIR"))
- .spawn()
- .expect("should spawn ./ci/no-includes.sh OK")
- .wait()
- .expect("should wait for ./ci/no-includes OK")
- .success());
-}
-
-#[test]
-fn emit_depfile() {
- let header = PathBuf::from("tests/headers/enum-default-rust.h");
- let expected_depfile = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
- .join("tests")
- .join("expectations")
- .join("tests")
- .join("enum-default-rust.d");
- let observed_depfile = tempfile::NamedTempFile::new().unwrap();
- let mut builder = create_bindgen_builder(&header).unwrap();
- builder.builder = builder.builder.depfile(
- "tests/expectations/tests/enum-default-rust.rs",
- observed_depfile.path(),
- );
-
- let check_roundtrip =
- env::var_os("BINDGEN_DISABLE_ROUNDTRIP_TEST").is_none();
- let (builder, _roundtrip_builder) =
- builder.into_builder(check_roundtrip).unwrap();
- let _bindings = builder.generate().unwrap();
-
- let observed = std::fs::read_to_string(observed_depfile).unwrap();
- let expected = std::fs::read_to_string(expected_depfile).unwrap();
- assert_eq!(observed.trim(), expected.trim());
-}
-
-#[test]
-fn dump_preprocessed_input() {
- let arg_keyword =
- concat!(env!("CARGO_MANIFEST_DIR"), "/tests/headers/arg_keyword.hpp");
- let empty_layout = concat!(
- env!("CARGO_MANIFEST_DIR"),
- "/tests/headers/cpp-empty-layout.hpp"
- );
-
- builder()
- .header(arg_keyword)
- .header(empty_layout)
- .dump_preprocessed_input()
- .expect("should dump preprocessed input");
-
- fn slurp(p: &str) -> String {
- let mut contents = String::new();
- let mut file = fs::File::open(p).unwrap();
- file.read_to_string(&mut contents).unwrap();
- contents
- }
-
- let bindgen_ii = slurp("__bindgen.ii");
- let arg_keyword = slurp(arg_keyword);
- let empty_layout = slurp(empty_layout);
-
- assert!(
- bindgen_ii.contains(&arg_keyword),
- "arg_keyword.hpp is in the preprocessed file"
- );
- assert!(
- bindgen_ii.contains(&empty_layout),
- "cpp-empty-layout.hpp is in the preprocessed file"
- );
-}
-
-#[test]
-fn allowlist_warnings() {
- let header = concat!(
- env!("CARGO_MANIFEST_DIR"),
- "/tests/headers/allowlist_warnings.h"
- );
-
- let bindings = builder()
- .header(header)
- .allowlist_function("doesnt_match_anything")
- .generate()
- .expect("unable to generate bindings");
-
- assert_eq!(1, bindings.warnings().len());
-}