Skip to content

Commit b23d978

Browse files
mxynspvdrz
authored andcommitted
example test for item discovery callback (new_item_found)
1 parent dd28f0b commit b23d978

File tree

5 files changed

+266
-0
lines changed

5 files changed

+266
-0
lines changed

Diff for: Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: bindgen-tests/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ bindgen = { workspace = true, default-features = true, features = ["__cli", "exp
1212
owo-colors.workspace = true
1313
prettyplease = { workspace = true, features = ["verbatim"] }
1414
proc-macro2.workspace = true
15+
regex.workspace = true
1516
shlex.workspace = true
1617
similar = { workspace = true, features = ["inline"] }
1718
syn.workspace = true
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Unions
2+
void function_using_anonymous_struct(struct {} arg0);
3+
4+
struct NamedStruct {
5+
};
6+
7+
typedef struct NamedStruct AliasOfNamedStruct;
8+
9+
10+
// Unions
11+
void function_using_anonymous_union(union {} arg0);
12+
13+
union NamedUnion {
14+
};
15+
16+
typedef union NamedUnion AliasOfNamedUnion;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
use std::cell::RefCell;
2+
use std::collections::HashMap;
3+
use std::rc::Rc;
4+
5+
use regex::Regex;
6+
7+
use bindgen::callbacks::{DiscoveredItem, DiscoveredItemId, ParseCallbacks};
8+
use bindgen::Builder;
9+
10+
#[derive(Debug, Default)]
11+
struct ItemDiscovery(Rc<RefCell<ItemCache>>);
12+
13+
pub type ItemCache = HashMap<DiscoveredItemId, DiscoveredItem>;
14+
15+
impl ParseCallbacks for ItemDiscovery {
16+
fn new_item_found(&self, _id: DiscoveredItemId, _item: DiscoveredItem) {
17+
self.0.borrow_mut().insert(_id, _item);
18+
}
19+
}
20+
#[test]
21+
pub fn test_item_discovery_callback() {
22+
let discovery = ItemDiscovery::default();
23+
let info = Rc::clone(&discovery.0);
24+
25+
Builder::default()
26+
.header(concat!(
27+
env!("CARGO_MANIFEST_DIR"),
28+
"/tests/parse_callbacks/item_discovery_callback/header_item_discovery.h"
29+
))
30+
.parse_callbacks(Box::new(discovery))
31+
.generate()
32+
.expect("TODO: panic message");
33+
34+
let expected = ItemCache::from([
35+
(
36+
DiscoveredItemId::new(10),
37+
DiscoveredItem::Struct {
38+
original_name: Some("NamedStruct".to_string()),
39+
final_name: "NamedStruct".to_string(),
40+
},
41+
),
42+
(
43+
DiscoveredItemId::new(11),
44+
DiscoveredItem::Alias {
45+
alias_name: "AliasOfNamedStruct".to_string(),
46+
alias_for: DiscoveredItemId::new(10),
47+
},
48+
),
49+
(
50+
DiscoveredItemId::new(20),
51+
DiscoveredItem::Union {
52+
original_name: Some("NamedUnion".to_string()),
53+
final_name: "NamedUnion".to_string(),
54+
},
55+
),
56+
(
57+
DiscoveredItemId::new(21),
58+
DiscoveredItem::Alias {
59+
alias_name: "AliasOfNamedUnion".to_string(),
60+
alias_for: DiscoveredItemId::new(20),
61+
},
62+
),
63+
(
64+
DiscoveredItemId::new(30),
65+
DiscoveredItem::Struct {
66+
original_name: None,
67+
final_name: "_bindgen_ty_*".to_string(),
68+
},
69+
),
70+
(
71+
DiscoveredItemId::new(40),
72+
DiscoveredItem::Union {
73+
original_name: None,
74+
final_name: "_bindgen_ty_*".to_string(),
75+
},
76+
),
77+
]);
78+
79+
compare_item_caches(info.borrow().clone(), expected);
80+
}
81+
82+
pub fn compare_item_caches(generated: ItemCache, expected: ItemCache) {
83+
// We can't use a simple Eq::eq comparison because of two reasons:
84+
// - anonymous structs/unions will have a final name generated by bindgen which may change
85+
// if the header file or the bindgen logic is altered
86+
// - aliases have a DiscoveredItemId that we can't directly compare for the same instability reasons
87+
for expected_item in expected.values() {
88+
let found = generated.iter().find(|(_generated_id, generated_item)| {
89+
compare_item_info(
90+
expected_item,
91+
generated_item,
92+
&expected,
93+
&generated,
94+
)
95+
});
96+
97+
if found.is_none() {
98+
panic!(
99+
"Missing Expected Item: {:#?}\n in {:#?}",
100+
expected_item, generated
101+
);
102+
}
103+
}
104+
}
105+
106+
fn compare_item_info(
107+
expected_item: &DiscoveredItem,
108+
generated_item: &DiscoveredItem,
109+
expected: &ItemCache,
110+
generated: &ItemCache,
111+
) -> bool {
112+
if std::mem::discriminant(expected_item) !=
113+
std::mem::discriminant(generated_item)
114+
{
115+
return false;
116+
}
117+
118+
match generated_item {
119+
DiscoveredItem::Struct { .. } => {
120+
compare_struct_info(expected_item, generated_item)
121+
}
122+
DiscoveredItem::Union { .. } => {
123+
compare_union_info(expected_item, generated_item)
124+
}
125+
DiscoveredItem::Alias { .. } => compare_alias_info(
126+
expected_item,
127+
generated_item,
128+
expected,
129+
generated,
130+
),
131+
}
132+
}
133+
134+
pub fn compare_names(expected_name: &str, generated_name: &str) -> bool {
135+
if let Ok(regex) = Regex::new(expected_name) {
136+
regex.is_match(generated_name)
137+
} else {
138+
false
139+
}
140+
}
141+
142+
pub fn compare_struct_info(
143+
expected_item: &DiscoveredItem,
144+
generated_item: &DiscoveredItem,
145+
) -> bool {
146+
let DiscoveredItem::Struct {
147+
original_name: expected_original_name,
148+
final_name: expected_final_name,
149+
} = expected_item
150+
else {
151+
unreachable!()
152+
};
153+
154+
let DiscoveredItem::Struct {
155+
original_name: generated_original_name,
156+
final_name: generated_final_name,
157+
} = generated_item
158+
else {
159+
unreachable!()
160+
};
161+
162+
if !compare_names(expected_final_name, generated_final_name) {
163+
return false;
164+
}
165+
166+
match (expected_original_name, generated_original_name) {
167+
(None, None) => true,
168+
(Some(expected_original_name), Some(generated_original_name)) => {
169+
compare_names(expected_original_name, generated_original_name)
170+
}
171+
_ => false,
172+
}
173+
}
174+
175+
pub fn compare_union_info(
176+
expected_item: &DiscoveredItem,
177+
generated_item: &DiscoveredItem,
178+
) -> bool {
179+
let DiscoveredItem::Union {
180+
original_name: expected_original_name,
181+
final_name: expected_final_name,
182+
} = expected_item
183+
else {
184+
unreachable!()
185+
};
186+
187+
let DiscoveredItem::Union {
188+
original_name: generated_original_name,
189+
final_name: generated_final_name,
190+
} = generated_item
191+
else {
192+
unreachable!()
193+
};
194+
195+
if !compare_names(expected_final_name, generated_final_name) {
196+
return false;
197+
}
198+
199+
match (expected_original_name, generated_original_name) {
200+
(None, None) => true,
201+
(Some(expected_original_name), Some(generated_original_name)) => {
202+
compare_names(expected_original_name, generated_original_name)
203+
}
204+
_ => false,
205+
}
206+
}
207+
208+
pub fn compare_alias_info(
209+
expected_item: &DiscoveredItem,
210+
generated_item: &DiscoveredItem,
211+
expected: &ItemCache,
212+
generated: &ItemCache,
213+
) -> bool {
214+
let DiscoveredItem::Alias {
215+
alias_name: expected_alias_name,
216+
alias_for: expected_alias_for,
217+
} = expected_item
218+
else {
219+
unreachable!()
220+
};
221+
222+
let DiscoveredItem::Alias {
223+
alias_name: generated_alias_name,
224+
alias_for: generated_alias_for,
225+
} = generated_item
226+
else {
227+
unreachable!()
228+
};
229+
230+
if !compare_names(expected_alias_name, generated_alias_name) {
231+
return false;
232+
}
233+
234+
// Assumes correct test definition
235+
let expected_aliased = expected.get(expected_alias_for).unwrap();
236+
237+
// We must have the aliased type in the cache
238+
let generated_aliased =
239+
if let Some(generated_aliased) = generated.get(generated_alias_for) {
240+
generated_aliased
241+
} else {
242+
return false;
243+
};
244+
245+
compare_item_info(expected_aliased, generated_aliased, expected, generated)
246+
}

Diff for: bindgen-tests/tests/parse_callbacks/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
mod item_discovery_callback;
2+
13
use bindgen::callbacks::*;
24
use bindgen::FieldVisibilityKind;
35

0 commit comments

Comments
 (0)