Skip to content

Commit

Permalink
example test for item discovery callback (new_item_found)
Browse files Browse the repository at this point in the history
  • Loading branch information
mxyns committed Sep 9, 2024
1 parent e3167c8 commit bf34530
Show file tree
Hide file tree
Showing 5 changed files with 267 additions and 1 deletion.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion bindgen-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ syn = { version = "2.0" }
tempfile = "3"
similar = { version = "2.2.1", features = ["inline"] }
owo-colors = "3.5.0"
regex = "1.7.1"

[features]
logging = ["bindgen/logging"]
Expand All @@ -23,4 +24,4 @@ runtime = ["bindgen/runtime"]

__testing_only_extra_assertions = ["bindgen/__testing_only_extra_assertions"]
__testing_only_libclang_9 = ["bindgen/__testing_only_libclang_9"]
__testing_only_libclang_16 = ["bindgen/__testing_only_libclang_16"]
__testing_only_libclang_16 = ["bindgen/__testing_only_libclang_16"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Unions
void function_using_anonymous_struct(struct {} arg0);

struct NamedStruct {
};

typedef struct NamedStruct AliasOfNamedStruct;


// Unions
void function_using_anonymous_union(union {} arg0);

union NamedUnion {
};

typedef union NamedUnion AliasOfNamedUnion;
246 changes: 246 additions & 0 deletions bindgen-tests/tests/parse_callbacks/item_discovery_callback/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;

use regex::Regex;

use bindgen::callbacks::{DiscoveredItem, DiscoveredItemId, ParseCallbacks};
use bindgen::Builder;

#[derive(Debug, Default)]
struct ItemDiscovery(Rc<RefCell<ItemCache>>);

pub type ItemCache = HashMap<DiscoveredItemId, DiscoveredItem>;

impl ParseCallbacks for ItemDiscovery {
fn new_item_found(&self, _id: DiscoveredItemId, _item: DiscoveredItem) {
self.0.borrow_mut().insert(_id, _item);
}
}
#[test]
pub fn test_item_discovery_callback() {
let discovery = ItemDiscovery::default();
let info = Rc::clone(&discovery.0);

Builder::default()
.header(concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/parse_callbacks/item_discovery_callback/header_item_discovery.h"
))
.parse_callbacks(Box::new(discovery))
.generate()
.expect("TODO: panic message");

let expected = ItemCache::from([
(
DiscoveredItemId::new(10),
DiscoveredItem::Struct {
original_name: Some("NamedStruct".to_string()),
final_name: "NamedStruct".to_string(),
},
),
(
DiscoveredItemId::new(11),
DiscoveredItem::Alias {
alias_name: "AliasOfNamedStruct".to_string(),
alias_for: DiscoveredItemId::new(10),
},
),
(
DiscoveredItemId::new(20),
DiscoveredItem::Union {
original_name: Some("NamedUnion".to_string()),
final_name: "NamedUnion".to_string(),
},
),
(
DiscoveredItemId::new(21),
DiscoveredItem::Alias {
alias_name: "AliasOfNamedUnion".to_string(),
alias_for: DiscoveredItemId::new(20),
},
),
(
DiscoveredItemId::new(30),
DiscoveredItem::Struct {
original_name: None,
final_name: "_bindgen_ty_*".to_string(),
},
),
(
DiscoveredItemId::new(40),
DiscoveredItem::Union {
original_name: None,
final_name: "_bindgen_ty_*".to_string(),
},
),
]);

compare_item_caches(info.borrow().clone(), expected);
}

pub fn compare_item_caches(generated: ItemCache, expected: ItemCache) {
// We can't use a simple Eq::eq comparison because of two reasons:
// - anonymous structs/unions will have a final name generated by bindgen which may change
// if the header file or the bindgen logic is altered
// - aliases have a DiscoveredItemId that we can't directly compare for the same instability reasons
for expected_item in expected.values() {
let found = generated.iter().find(|(_generated_id, generated_item)| {
compare_item_info(
expected_item,
generated_item,
&expected,
&generated,
)
});

if found.is_none() {
panic!(
"Missing Expected Item: {:#?}\n in {:#?}",
expected_item, generated
);
}
}
}

fn compare_item_info(
expected_item: &DiscoveredItem,
generated_item: &DiscoveredItem,
expected: &ItemCache,
generated: &ItemCache,
) -> bool {
if std::mem::discriminant(expected_item) !=
std::mem::discriminant(generated_item)
{
return false;
}

match generated_item {
DiscoveredItem::Struct { .. } => {
compare_struct_info(expected_item, generated_item)
}
DiscoveredItem::Union { .. } => {
compare_union_info(expected_item, generated_item)
}
DiscoveredItem::Alias { .. } => compare_alias_info(
expected_item,
generated_item,
expected,
generated,
),
}
}

pub fn compare_names(expected_name: &str, generated_name: &str) -> bool {
if let Ok(regex) = Regex::new(expected_name) {
regex.is_match(generated_name)
} else {
false
}
}

pub fn compare_struct_info(
expected_item: &DiscoveredItem,
generated_item: &DiscoveredItem,
) -> bool {
let DiscoveredItem::Struct {
original_name: expected_original_name,
final_name: expected_final_name,
} = expected_item
else {
unreachable!()
};

let DiscoveredItem::Struct {
original_name: generated_original_name,
final_name: generated_final_name,
} = generated_item
else {
unreachable!()
};

if !compare_names(expected_final_name, generated_final_name) {
return false;
}

match (expected_original_name, generated_original_name) {
(None, None) => true,
(Some(expected_original_name), Some(generated_original_name)) => {
compare_names(expected_original_name, generated_original_name)
}
_ => false,
}
}

pub fn compare_union_info(
expected_item: &DiscoveredItem,
generated_item: &DiscoveredItem,
) -> bool {
let DiscoveredItem::Union {
original_name: expected_original_name,
final_name: expected_final_name,
} = expected_item
else {
unreachable!()
};

let DiscoveredItem::Union {
original_name: generated_original_name,
final_name: generated_final_name,
} = generated_item
else {
unreachable!()
};

if !compare_names(expected_final_name, generated_final_name) {
return false;
}

match (expected_original_name, generated_original_name) {
(None, None) => true,
(Some(expected_original_name), Some(generated_original_name)) => {
compare_names(expected_original_name, generated_original_name)
}
_ => false,
}
}

pub fn compare_alias_info(
expected_item: &DiscoveredItem,
generated_item: &DiscoveredItem,
expected: &ItemCache,
generated: &ItemCache,
) -> bool {
let DiscoveredItem::Alias {
alias_name: expected_alias_name,
alias_for: expected_alias_for,
} = expected_item
else {
unreachable!()
};

let DiscoveredItem::Alias {
alias_name: generated_alias_name,
alias_for: generated_alias_for,
} = generated_item
else {
unreachable!()
};

if !compare_names(expected_alias_name, generated_alias_name) {
return false;
}

// Assumes correct test definition
let expected_aliased = expected.get(expected_alias_for).unwrap();

// We must have the aliased type in the cache
let generated_aliased =
if let Some(generated_aliased) = generated.get(generated_alias_for) {
generated_aliased
} else {
return false;
};

compare_item_info(expected_aliased, generated_aliased, expected, generated)
}
2 changes: 2 additions & 0 deletions bindgen-tests/tests/parse_callbacks/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
mod item_discovery_callback;

use bindgen::callbacks::*;
use bindgen::FieldVisibilityKind;

Expand Down

0 comments on commit bf34530

Please sign in to comment.