1 use bindgen::{clang_version, Builder};
2 use owo_colors::{OwoColorize, Style};
3 use similar::{ChangeTag, TextDiff};
4 use std::env;
5 use std::fmt;
6 use std::fs;
7 use std::io::{BufRead, BufReader, Error, ErrorKind, Read, Write};
8 use std::path::{Path, PathBuf};
9
10 use crate::options::builder_from_flags;
11
12 #[path = "../../bindgen-cli/options.rs"]
13 mod options;
14
15 mod parse_callbacks;
16
17 // Format the given source string. It can fail if the source string does not contain syntactically
18 // valid Rust.
format_code<S: AsRef<str>>(source: S) -> syn::Result<String>19 fn format_code<S: AsRef<str>>(source: S) -> syn::Result<String> {
20 use prettyplease::unparse;
21 use syn::{parse_str, File};
22
23 let file = parse_str::<File>(source.as_ref())?;
24 Ok(unparse(&file))
25 }
26
should_overwrite_expected() -> bool27 fn should_overwrite_expected() -> bool {
28 if let Some(var) = env::var_os("BINDGEN_OVERWRITE_EXPECTED") {
29 if var == "1" {
30 return true;
31 }
32 if var != "0" && var != "" {
33 panic!("Invalid value of BINDGEN_OVERWRITE_EXPECTED");
34 }
35 }
36 false
37 }
38
error_diff_mismatch( actual: &str, expected: &str, header: Option<&Path>, filename: &Path, ) -> Result<(), Error>39 fn error_diff_mismatch(
40 actual: &str,
41 expected: &str,
42 header: Option<&Path>,
43 filename: &Path,
44 ) -> Result<(), Error> {
45 println!("diff expected generated");
46 println!("--- expected: {:?}", filename);
47 if let Some(header) = header {
48 println!("+++ generated from: {:?}", header);
49 }
50
51 show_diff(expected, actual);
52
53 if should_overwrite_expected() {
54 // Overwrite the expectation with actual output.
55 let mut expectation_file = fs::File::create(filename)?;
56 expectation_file.write_all(actual.as_bytes())?;
57 }
58
59 if let Some(var) = env::var_os("BINDGEN_TESTS_DIFFTOOL") {
60 //usecase: var = "meld" -> You can hand check differences
61 let name = match filename.components().last() {
62 Some(std::path::Component::Normal(name)) => name,
63 _ => panic!("Why is the header variable so weird?"),
64 };
65 let actual_result_path =
66 PathBuf::from(env::var("OUT_DIR").unwrap()).join(name);
67 let mut actual_result_file = fs::File::create(&actual_result_path)?;
68 actual_result_file.write_all(actual.as_bytes())?;
69 std::process::Command::new(var)
70 .args([filename, &actual_result_path])
71 .output()?;
72 }
73
74 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."))
75 }
76
77 struct Line(Option<usize>);
78
79 impl fmt::Display for Line {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result80 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
81 match self.0 {
82 None => write!(f, " "),
83 Some(idx) => write!(f, "{:<4}", idx + 1),
84 }
85 }
86 }
87
show_diff(old: &str, new: &str)88 fn show_diff(old: &str, new: &str) {
89 let diff = TextDiff::from_lines(old, new);
90 for (count, group) in diff.grouped_ops(3).iter().enumerate() {
91 if count > 0 {
92 let message = format!("(chunk {count}/n)");
93 println!("{}", message.cyan().dimmed());
94 }
95 for diff_op in group {
96 for change in diff.iter_inline_changes(diff_op) {
97 let (sign, color) = match change.tag() {
98 ChangeTag::Delete => ("-", Style::new().red()),
99 ChangeTag::Insert => ("+", Style::new().green()),
100 ChangeTag::Equal => (" ", Style::new()),
101 };
102 print!(
103 "{}{}| {}",
104 Line(change.old_index()).style(color).dimmed(),
105 Line(change.new_index()).style(color).dimmed(),
106 sign.style(color).bold(),
107 );
108 for (emphasized, text) in change.iter_strings_lossy() {
109 if emphasized {
110 print!("{}", text.style(color).underline());
111 } else {
112 print!("{}", text.style(color));
113 }
114 }
115 if change.missing_newline() {
116 println!();
117 }
118 }
119 }
120 }
121 }
122
compare_generated_header( header: &Path, builder: BuilderState, check_roundtrip: bool, ) -> Result<(), Error>123 fn compare_generated_header(
124 header: &Path,
125 builder: BuilderState,
126 check_roundtrip: bool,
127 ) -> Result<(), Error> {
128 let file_name = header.file_name().ok_or_else(|| {
129 Error::new(ErrorKind::Other, "compare_generated_header expects a file")
130 })?;
131
132 let mut expectation = PathBuf::from(header);
133 expectation.pop();
134 expectation.pop();
135 expectation.push("expectations");
136 expectation.push("tests");
137
138 let mut looked_at = vec![];
139 let mut expectation_file;
140
141 // Try more specific expectations first.
142 {
143 let mut expectation = expectation.clone();
144
145 if cfg!(feature = "__testing_only_libclang_16") {
146 expectation.push("libclang-16");
147 } else if cfg!(feature = "__testing_only_libclang_9") {
148 expectation.push("libclang-9");
149 } else {
150 match clang_version().parsed {
151 None => expectation.push("libclang-16"),
152 Some(version) => {
153 let (maj, min) = version;
154 let version_str = if maj >= 16 {
155 "16".to_owned()
156 } else if maj >= 9 {
157 "9".to_owned()
158 } else {
159 format!("{}.{}", maj, min)
160 };
161 expectation.push(format!("libclang-{}", version_str));
162 }
163 }
164 }
165
166 expectation.push(file_name);
167 expectation.set_extension("rs");
168 expectation_file = fs::File::open(&expectation).ok();
169 looked_at.push(expectation);
170 }
171
172 // Try the default path otherwise.
173 if expectation_file.is_none() {
174 expectation.push(file_name);
175 expectation.set_extension("rs");
176 expectation_file = fs::File::open(&expectation).ok();
177 looked_at.push(expectation.clone());
178 }
179
180 let mut expected = String::new();
181 match expectation_file {
182 Some(f) => {
183 BufReader::new(f).read_to_string(&mut expected)?;
184 }
185 None => panic!(
186 "missing test expectation file and/or '__testing_only_libclang_$VERSION' \
187 feature for header '{}'; looking for expectation file at '{:?}'",
188 header.display(),
189 looked_at,
190 ),
191 };
192
193 let (builder, roundtrip_builder) = builder.into_builder(check_roundtrip)?;
194
195 // We skip the generate() error here so we get a full diff below
196 let actual = match builder.generate() {
197 Ok(bindings) => format_code(bindings.to_string()).map_err(|err| {
198 Error::new(
199 ErrorKind::Other,
200 format!("Cannot parse the generated bindings: {}", err),
201 )
202 })?,
203 Err(_) => "/* error generating bindings */\n".into(),
204 };
205
206 if actual.is_empty() {
207 return Err(Error::new(
208 ErrorKind::Other,
209 "Something's gone really wrong!",
210 ));
211 }
212 if actual != expected {
213 return error_diff_mismatch(
214 &actual,
215 &expected,
216 Some(header),
217 looked_at.last().unwrap(),
218 );
219 }
220
221 if let Some(roundtrip_builder) = roundtrip_builder {
222 if let Err(e) =
223 compare_generated_header(header, roundtrip_builder, false)
224 {
225 return Err(Error::new(ErrorKind::Other, format!("Checking CLI flags roundtrip errored! You probably need to fix Builder::command_line_flags. {}", e)));
226 }
227 }
228
229 Ok(())
230 }
231
builder() -> Builder232 fn builder() -> Builder {
233 #[cfg(feature = "logging")]
234 let _ = env_logger::try_init();
235
236 bindgen::builder().disable_header_comment()
237 }
238
239 struct BuilderState {
240 builder: Builder,
241 parse_callbacks: Option<String>,
242 }
243
244 impl BuilderState {
into_builder( self, with_roundtrip_builder: bool, ) -> Result<(Builder, Option<BuilderState>), Error>245 fn into_builder(
246 self,
247 with_roundtrip_builder: bool,
248 ) -> Result<(Builder, Option<BuilderState>), Error> {
249 let roundtrip_builder = if with_roundtrip_builder {
250 let mut flags = self.builder.command_line_flags();
251 flags.insert(0, "bindgen".into());
252 let mut builder = builder_from_flags(flags.into_iter())?.0;
253 if let Some(ref parse_cb) = self.parse_callbacks {
254 builder =
255 builder.parse_callbacks(parse_callbacks::lookup(parse_cb));
256 }
257 Some(BuilderState {
258 builder,
259 parse_callbacks: self.parse_callbacks,
260 })
261 } else {
262 None
263 };
264 Ok((self.builder, roundtrip_builder))
265 }
266 }
267
create_bindgen_builder(header: &Path) -> Result<BuilderState, Error>268 fn create_bindgen_builder(header: &Path) -> Result<BuilderState, Error> {
269 #[cfg(feature = "logging")]
270 let _ = env_logger::try_init();
271
272 let source = fs::File::open(header)?;
273 let reader = BufReader::new(source);
274
275 // Scoop up bindgen-flags from test header
276 let mut flags = Vec::with_capacity(2);
277 let mut parse_callbacks = None;
278
279 for line in reader.lines() {
280 let line = line?;
281 if !line.starts_with("// bindgen") {
282 continue;
283 }
284
285 if line.contains("bindgen-flags: ") {
286 let extra_flags = line
287 .split("bindgen-flags: ")
288 .last()
289 .and_then(shlex::split)
290 .unwrap();
291 flags.extend(extra_flags);
292 } else if line.contains("bindgen-osx-only") {
293 let prepend_flags = ["--raw-line", "#![cfg(target_os=\"macos\")]"];
294 flags = prepend_flags
295 .iter()
296 .map(ToString::to_string)
297 .chain(flags)
298 .collect();
299 } else if line.contains("bindgen-parse-callbacks: ") {
300 let parse_cb =
301 line.split("bindgen-parse-callbacks: ").last().unwrap();
302 parse_callbacks = Some(parse_cb.to_owned());
303 }
304 }
305
306 // Different platforms have various different conventions like struct padding, mangling, etc.
307 // We make the default target as x86_64-unknown-linux
308 if flags.iter().all(|flag| !flag.starts_with("--target=")) {
309 if !flags.iter().any(|flag| flag == "--") {
310 flags.push("--".into());
311 }
312 flags.push("--target=x86_64-unknown-linux".into());
313 }
314
315 // Fool builder_from_flags() into believing it has real env::args_os...
316 // - add "bindgen" as executable name 0th element
317 // - add header filename as 1st element
318 // - prepend raw lines so they're in the right order for expected output
319 // - append the test header's bindgen flags
320 let header_str = header.to_str().ok_or_else(|| {
321 Error::new(ErrorKind::Other, "Invalid header file name")
322 })?;
323
324 let prepend = [
325 "bindgen",
326 // We format in `compare_generated_header` ourselves to have a little
327 // more control.
328 "--formatter=none",
329 "--with-derive-default",
330 "--disable-header-comment",
331 "--vtable-generation",
332 header_str,
333 "--raw-line",
334 "",
335 "--raw-line",
336 "#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]",
337 "--raw-line",
338 "",
339 ];
340
341 let args = prepend.iter().map(ToString::to_string).chain(flags);
342
343 let mut builder = builder_from_flags(args)?.0;
344 if let Some(ref parse_cb) = parse_callbacks {
345 builder = builder.parse_callbacks(parse_callbacks::lookup(parse_cb));
346 }
347 Ok(BuilderState {
348 builder,
349 parse_callbacks,
350 })
351 }
352
353 macro_rules! test_header {
354 ($function:ident, $header:expr) => {
355 #[test]
356 fn $function() {
357 let header = PathBuf::from($header);
358 let result = create_bindgen_builder(&header).and_then(|builder| {
359 let check_roundtrip =
360 env::var_os("BINDGEN_DISABLE_ROUNDTRIP_TEST").is_none();
361 compare_generated_header(&header, builder, check_roundtrip)
362 });
363
364 if let Err(err) = result {
365 panic!("{}", err);
366 }
367 }
368 };
369 }
370
371 // This file is generated by build.rs
372 include!(concat!(env!("OUT_DIR"), "/tests.rs"));
373
374 #[test]
375 #[cfg_attr(target_os = "windows", ignore)]
test_clang_env_args()376 fn test_clang_env_args() {
377 std::env::set_var(
378 "BINDGEN_EXTRA_CLANG_ARGS",
379 "-D_ENV_ONE=1 -D_ENV_TWO=\"2 -DNOT_THREE=1\"",
380 );
381 let actual = builder()
382 .disable_header_comment()
383 .header_contents(
384 "test.hpp",
385 "#ifdef _ENV_ONE\nextern const int x[] = { 42 };\n#endif\n\
386 #ifdef _ENV_TWO\nextern const int y[] = { 42 };\n#endif\n\
387 #if defined NOT_THREE && NOT_THREE == 1\nextern const int z[] = { 42 };\n#endif\n",
388 )
389 .generate()
390 .unwrap()
391 .to_string();
392
393 let actual = format_code(actual).unwrap();
394
395 let expected = format_code(
396 "extern \"C\" {
397 pub static x: [::std::os::raw::c_int; 1usize];
398 }
399 extern \"C\" {
400 pub static y: [::std::os::raw::c_int; 1usize];
401 }
402 ",
403 )
404 .unwrap();
405
406 assert_eq!(expected, actual);
407 }
408
409 #[test]
test_header_contents()410 fn test_header_contents() {
411 let actual = builder()
412 .disable_header_comment()
413 .header_contents("test.h", "int foo(const char* a);")
414 .clang_arg("--target=x86_64-unknown-linux")
415 .generate()
416 .unwrap()
417 .to_string();
418
419 let actual = format_code(actual).unwrap();
420
421 let expected = format_code(
422 "extern \"C\" {
423 pub fn foo(a: *const ::std::os::raw::c_char) -> ::std::os::raw::c_int;
424 }
425 ",
426 )
427 .unwrap();
428
429 assert_eq!(expected, actual);
430 }
431
432 #[test]
test_multiple_header_calls_in_builder()433 fn test_multiple_header_calls_in_builder() {
434 let actual = builder()
435 .header(concat!(
436 env!("CARGO_MANIFEST_DIR"),
437 "/tests/headers/func_ptr.h"
438 ))
439 .header(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/headers/char.h"))
440 .clang_arg("--target=x86_64-unknown-linux")
441 .generate()
442 .unwrap()
443 .to_string();
444
445 let actual = format_code(actual).unwrap();
446
447 let expected_filename = concat!(
448 env!("CARGO_MANIFEST_DIR"),
449 "/tests/expectations/tests/test_multiple_header_calls_in_builder.rs"
450 );
451 let expected = include_str!(concat!(
452 env!("CARGO_MANIFEST_DIR"),
453 "/tests/expectations/tests/test_multiple_header_calls_in_builder.rs"
454 ));
455 let expected = format_code(expected).unwrap();
456
457 if actual != expected {
458 println!("Generated bindings differ from expected!");
459 error_diff_mismatch(
460 &actual,
461 &expected,
462 None,
463 Path::new(expected_filename),
464 )
465 .unwrap();
466 }
467 }
468
469 #[test]
test_headers_call_in_builder()470 fn test_headers_call_in_builder() {
471 let actual = builder()
472 .headers([
473 concat!(env!("CARGO_MANIFEST_DIR"), "/tests/headers/func_ptr.h"),
474 concat!(env!("CARGO_MANIFEST_DIR"), "/tests/headers/char.h"),
475 ])
476 .clang_arg("--target=x86_64-unknown-linux")
477 .generate()
478 .unwrap()
479 .to_string();
480
481 let actual = format_code(actual).unwrap();
482
483 let expected_filename = concat!(
484 env!("CARGO_MANIFEST_DIR"),
485 "/tests/expectations/tests/test_multiple_header_calls_in_builder.rs"
486 );
487 let expected = include_str!(concat!(
488 env!("CARGO_MANIFEST_DIR"),
489 "/tests/expectations/tests/test_multiple_header_calls_in_builder.rs"
490 ));
491 let expected = format_code(expected).unwrap();
492
493 if actual != expected {
494 println!("Generated bindings differ from expected!");
495 error_diff_mismatch(
496 &actual,
497 &expected,
498 None,
499 Path::new(expected_filename),
500 )
501 .unwrap();
502 }
503 }
504
505 #[test]
test_multiple_header_contents()506 fn test_multiple_header_contents() {
507 let actual = builder()
508 .header_contents("test.h", "int foo(const char* a);")
509 .header_contents("test2.h", "float foo2(const char* b);")
510 .clang_arg("--target=x86_64-unknown-linux")
511 .generate()
512 .unwrap()
513 .to_string();
514
515 let actual = format_code(actual).unwrap();
516
517 let expected = format_code(
518 "extern \"C\" {
519 pub fn foo2(b: *const ::std::os::raw::c_char) -> f32;
520 }
521 extern \"C\" {
522 pub fn foo(a: *const ::std::os::raw::c_char) -> ::std::os::raw::c_int;
523 }
524 ",
525 )
526 .unwrap();
527
528 assert_eq!(expected, actual);
529 }
530
531 #[test]
test_mixed_header_and_header_contents()532 fn test_mixed_header_and_header_contents() {
533 let actual = builder()
534 .header(concat!(
535 env!("CARGO_MANIFEST_DIR"),
536 "/tests/headers/func_ptr.h"
537 ))
538 .header(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/headers/char.h"))
539 .header_contents("test.h", "int bar(const char* a);")
540 .header_contents("test2.h", "float bar2(const char* b);")
541 .clang_arg("--target=x86_64-unknown-linux")
542 .generate()
543 .unwrap()
544 .to_string();
545
546 let actual = format_code(actual).unwrap();
547
548 let expected_filename = concat!(
549 env!("CARGO_MANIFEST_DIR"),
550 "/tests/expectations/tests/test_mixed_header_and_header_contents.rs"
551 );
552 let expected = include_str!(concat!(
553 env!("CARGO_MANIFEST_DIR"),
554 "/tests/expectations/tests/test_mixed_header_and_header_contents.rs"
555 ));
556 let expected = format_code(expected).unwrap();
557 if expected != actual {
558 error_diff_mismatch(
559 &actual,
560 &expected,
561 None,
562 Path::new(expected_filename),
563 )
564 .unwrap();
565 }
566 }
567
568 #[test]
test_macro_fallback_non_system_dir()569 fn test_macro_fallback_non_system_dir() {
570 let actual = builder()
571 .header(concat!(
572 env!("CARGO_MANIFEST_DIR"),
573 "/tests/macro_fallback_test_headers/one_header.h"
574 ))
575 .header(concat!(
576 env!("CARGO_MANIFEST_DIR"),
577 "/tests/macro_fallback_test_headers/another_header.h"
578 ))
579 .clang_macro_fallback()
580 .clang_arg(format!("-I{}/tests/headers", env!("CARGO_MANIFEST_DIR")))
581 .generate()
582 .unwrap()
583 .to_string();
584
585 let actual = format_code(actual).unwrap();
586
587 let (expected_filename, expected) = match clang_version().parsed {
588 Some((9, _)) => {
589 let expected_filename = concat!(
590 env!("CARGO_MANIFEST_DIR"),
591 "/tests/expectations/tests/libclang-9/macro_fallback_non_system_dir.rs",
592 );
593 let expected = include_str!(concat!(
594 env!("CARGO_MANIFEST_DIR"),
595 "/tests/expectations/tests/libclang-9/macro_fallback_non_system_dir.rs",
596 ));
597 (expected_filename, expected)
598 }
599 _ => {
600 let expected_filename = concat!(
601 env!("CARGO_MANIFEST_DIR"),
602 "/tests/expectations/tests/test_macro_fallback_non_system_dir.rs",
603 );
604 let expected = include_str!(concat!(
605 env!("CARGO_MANIFEST_DIR"),
606 "/tests/expectations/tests/test_macro_fallback_non_system_dir.rs",
607 ));
608 (expected_filename, expected)
609 }
610 };
611 let expected = format_code(expected).unwrap();
612 if expected != actual {
613 error_diff_mismatch(
614 &actual,
615 &expected,
616 None,
617 Path::new(expected_filename),
618 )
619 .unwrap();
620 }
621 }
622
623 #[test]
624 // Doesn't support executing sh file on Windows.
625 // We may want to implement it in Rust so that we support all systems.
626 #[cfg(not(target_os = "windows"))]
no_system_header_includes()627 fn no_system_header_includes() {
628 use std::process::Command;
629 assert!(Command::new("../ci/no-includes.sh")
630 .current_dir(env!("CARGO_MANIFEST_DIR"))
631 .spawn()
632 .expect("should spawn ../ci/no-includes.sh OK")
633 .wait()
634 .expect("should wait for ../ci/no-includes OK")
635 .success());
636 }
637
638 #[test]
emit_depfile()639 fn emit_depfile() {
640 let header = PathBuf::from("tests/headers/enum-default-rust.h");
641 let expected_depfile = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
642 .join("tests")
643 .join("expectations")
644 .join("tests")
645 .join("enum-default-rust.d");
646 let observed_depfile = tempfile::NamedTempFile::new().unwrap();
647 let mut builder = create_bindgen_builder(&header).unwrap();
648 builder.builder = builder.builder.depfile(
649 "tests/expectations/tests/enum-default-rust.rs",
650 observed_depfile.path(),
651 );
652
653 let check_roundtrip =
654 env::var_os("BINDGEN_DISABLE_ROUNDTRIP_TEST").is_none();
655 let (builder, _roundtrip_builder) =
656 builder.into_builder(check_roundtrip).unwrap();
657 let _bindings = builder.generate().unwrap();
658
659 let observed = std::fs::read_to_string(observed_depfile).unwrap();
660 let expected = std::fs::read_to_string(expected_depfile).unwrap();
661 assert_eq!(observed.trim(), expected.trim());
662 }
663
664 #[test]
dump_preprocessed_input()665 fn dump_preprocessed_input() {
666 let arg_keyword =
667 concat!(env!("CARGO_MANIFEST_DIR"), "/tests/headers/arg_keyword.hpp");
668 let empty_layout = concat!(
669 env!("CARGO_MANIFEST_DIR"),
670 "/tests/headers/cpp-empty-layout.hpp"
671 );
672
673 builder()
674 .header(arg_keyword)
675 .header(empty_layout)
676 .dump_preprocessed_input()
677 .expect("should dump preprocessed input");
678
679 fn slurp(p: &str) -> String {
680 let mut contents = String::new();
681 let mut file = fs::File::open(p).unwrap();
682 file.read_to_string(&mut contents).unwrap();
683 contents
684 }
685
686 let bindgen_ii = slurp("__bindgen.ii");
687 let arg_keyword = slurp(arg_keyword);
688 let empty_layout = slurp(empty_layout);
689
690 assert!(
691 bindgen_ii.contains(&arg_keyword),
692 "arg_keyword.hpp is in the preprocessed file"
693 );
694 assert!(
695 bindgen_ii.contains(&empty_layout),
696 "cpp-empty-layout.hpp is in the preprocessed file"
697 );
698 }
699
build_flags_output_helper(builder: &bindgen::Builder)700 fn build_flags_output_helper(builder: &bindgen::Builder) {
701 let mut command_line_flags = builder.command_line_flags();
702 command_line_flags.insert(0, "bindgen".to_string());
703
704 let flags_quoted: Vec<String> = command_line_flags
705 .iter()
706 .map(|x| format!("{}", shlex::try_quote(x).unwrap()))
707 .collect();
708 let flags_str = flags_quoted.join(" ");
709 println!("{}", flags_str);
710
711 let (builder, _output, _verbose) =
712 crate::options::builder_from_flags(command_line_flags.into_iter())
713 .unwrap();
714 builder.generate().expect("failed to generate bindings");
715 }
716
717 #[test]
commandline_multiple_headers()718 fn commandline_multiple_headers() {
719 let bindings = bindgen::Builder::default()
720 .header("tests/headers/char.h")
721 .header("tests/headers/func_ptr.h")
722 .header("tests/headers/16-byte-alignment.h");
723 build_flags_output_helper(&bindings);
724 }
725
726 #[test]
test_wrap_static_fns()727 fn test_wrap_static_fns() {
728 // This test is for testing diffs of the generated C source and header files
729 // TODO: If another such feature is added, convert this test into a more generic
730 // test that looks at `tests/headers/generated` directory.
731 let expect_path = PathBuf::from("tests/expectations/tests/generated")
732 .join("wrap_static_fns");
733 println!("In path is ::: {}", expect_path.display());
734
735 let generated_path =
736 PathBuf::from(env::var("OUT_DIR").unwrap()).join("wrap_static_fns");
737 println!("Out path is ::: {}", generated_path.display());
738
739 let _bindings = Builder::default()
740 .header("tests/headers/wrap-static-fns.h")
741 .wrap_static_fns(true)
742 .wrap_static_fns_path(generated_path.display().to_string())
743 .parse_callbacks(Box::new(parse_callbacks::WrapAsVariadicFn))
744 .generate()
745 .expect("Failed to generate bindings");
746
747 let expected_c = fs::read_to_string(expect_path.with_extension("c"))
748 .expect("Could not read generated wrap_static_fns.c");
749
750 let actual_c = fs::read_to_string(generated_path.with_extension("c"))
751 .expect("Could not read actual wrap_static_fns.c");
752
753 if expected_c != actual_c {
754 error_diff_mismatch(
755 &actual_c,
756 &expected_c,
757 None,
758 &expect_path.with_extension("c"),
759 )
760 .unwrap();
761 }
762 }
763