Skip to content

Commit 2c3bac2

Browse files
committed
Reduce boilerplate integration tests with godot_itest! macro
1 parent 9c604f2 commit 2c3bac2

13 files changed

+523
-845
lines changed

gdnative-core/src/macros.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,6 @@ macro_rules! godot_test {
250250
}
251251
}
252252

253-
254253
/// Declares a test to be run with the Godot engine (i.e. not a pure Rust unit test).
255254
///
256255
/// Creates a wrapper function that catches panics, prints errors and returns true/false.
@@ -265,4 +264,4 @@ macro_rules! godot_itest {
265264
$crate::godot_test_impl!($test_name $body);
266265
)*
267266
}
268-
}
267+
}

test/src/lib.rs

+45-78
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#![allow(deprecated)]
33

44
use gdnative::prelude::*;
5+
use gdnative_core::godot_itest;
56

67
mod test_as_arg;
78
mod test_async;
@@ -82,22 +83,11 @@ pub extern "C" fn run_tests(
8283
Variant::new(status).leak()
8384
}
8485

85-
fn test_underscore_method_binding() -> bool {
86-
println!(" -- test_underscore_method_binding");
87-
88-
let ok = std::panic::catch_unwind(|| {
89-
let script = gdnative::api::NativeScript::new();
90-
let result = script._new(&[]);
91-
assert_eq!(Variant::nil(), result);
92-
})
93-
.is_ok();
94-
95-
if !ok {
96-
godot_error!(" !! Test test_underscore_method_binding failed");
97-
}
98-
99-
ok
100-
}
86+
godot_itest! { test_underscore_method_binding {
87+
let script = gdnative::api::NativeScript::new();
88+
let result = script._new(&[]);
89+
assert_eq!(Variant::nil(), result);
90+
}}
10191

10292
#[derive(NativeClass)]
10393
#[inherit(Reference)]
@@ -152,31 +142,19 @@ impl Foo {
152142
}
153143
}
154144

155-
fn test_rust_class_construction() -> bool {
156-
println!(" -- test_rust_class_construction");
157-
158-
let ok = std::panic::catch_unwind(|| {
159-
let foo = Foo::new_instance();
145+
godot_itest! { test_rust_class_construction {
146+
let foo = Foo::new_instance();
147+
assert_eq!(Ok(42), foo.map(|foo, owner| { foo.answer(&*owner) }));
160148

161-
assert_eq!(Ok(42), foo.map(|foo, owner| { foo.answer(&*owner) }));
149+
let base = foo.into_base();
150+
assert_eq!(Some(42), unsafe { base.call("answer", &[]).to() });
162151

163-
let base = foo.into_base();
164-
assert_eq!(Some(42), unsafe { base.call("answer", &[]).to() });
152+
let foo = Instance::<Foo, _>::try_from_base(base).expect("should be able to downcast");
153+
assert_eq!(Ok(42), foo.map(|foo, owner| { foo.answer(&*owner) }));
165154

166-
let foo = Instance::<Foo, _>::try_from_base(base).expect("should be able to downcast");
167-
assert_eq!(Ok(42), foo.map(|foo, owner| { foo.answer(&*owner) }));
168-
169-
let base = foo.into_base();
170-
assert!(Instance::<NotFoo, _>::try_from_base(base).is_err());
171-
})
172-
.is_ok();
173-
174-
if !ok {
175-
godot_error!(" !! Test test_rust_class_construction failed");
176-
}
177-
178-
ok
179-
}
155+
let base = foo.into_base();
156+
assert!(Instance::<NotFoo, _>::try_from_base(base).is_err());
157+
}}
180158

181159
#[derive(NativeClass)]
182160
#[inherit(Reference)]
@@ -205,60 +183,49 @@ impl OptionalArgs {
205183
}
206184
}
207185

208-
fn test_from_instance_id() -> bool {
209-
println!(" -- test_from_instance_id");
186+
godot_itest! { test_from_instance_id {
187+
assert!(unsafe { Node::try_from_instance_id(22).is_none() });
188+
assert!(unsafe { Node::try_from_instance_id(42).is_none() });
189+
assert!(unsafe { Node::try_from_instance_id(503).is_none() });
210190

211-
let ok = std::panic::catch_unwind(|| {
212-
assert!(unsafe { Node::try_from_instance_id(22).is_none() });
213-
assert!(unsafe { Node::try_from_instance_id(42).is_none() });
214-
assert!(unsafe { Node::try_from_instance_id(503).is_none() });
191+
let instance_id;
215192

216-
let instance_id;
193+
{
194+
let foo = unsafe { Node::new().into_shared().assume_safe() };
195+
foo.set_name("foo");
217196

218-
{
219-
let foo = unsafe { Node::new().into_shared().assume_safe() };
220-
foo.set_name("foo");
197+
instance_id = foo.get_instance_id();
221198

222-
instance_id = foo.get_instance_id();
223-
224-
assert!(unsafe { Reference::try_from_instance_id(instance_id).is_none() });
225-
226-
let reconstructed = unsafe { Node::from_instance_id(instance_id) };
227-
assert_eq!("foo", reconstructed.name().to_string());
228-
229-
unsafe { foo.assume_unique().free() };
230-
}
199+
assert!(unsafe { Reference::try_from_instance_id(instance_id).is_none() });
231200

232-
assert!(unsafe { Node::try_from_instance_id(instance_id).is_none() });
201+
let reconstructed = unsafe { Node::from_instance_id(instance_id) };
202+
assert_eq!("foo", reconstructed.name().to_string());
233203

234-
let instance_id;
204+
unsafe { foo.assume_unique().free() };
205+
}
235206

236-
{
237-
let foo = Reference::new().into_shared();
238-
let foo = unsafe { foo.assume_safe() };
239-
foo.set_meta("foo", "bar");
207+
assert!(unsafe { Node::try_from_instance_id(instance_id).is_none() });
240208

241-
instance_id = foo.get_instance_id();
209+
let instance_id;
242210

243-
assert!(unsafe { Node::try_from_instance_id(instance_id).is_none() });
211+
{
212+
let foo = Reference::new().into_shared();
213+
let foo = unsafe { foo.assume_safe() };
214+
foo.set_meta("foo", "bar");
244215

245-
let reconstructed = unsafe { Reference::from_instance_id(instance_id) };
246-
assert_eq!(
247-
"bar",
248-
String::from_variant(&reconstructed.get_meta("foo")).unwrap()
249-
);
250-
}
216+
instance_id = foo.get_instance_id();
251217

252-
assert!(unsafe { Reference::try_from_instance_id(instance_id).is_none() });
253-
})
254-
.is_ok();
218+
assert!(unsafe { Node::try_from_instance_id(instance_id).is_none() });
255219

256-
if !ok {
257-
godot_error!(" !! Test test_from_instance_id failed");
220+
let reconstructed = unsafe { Reference::from_instance_id(instance_id) };
221+
assert_eq!(
222+
"bar",
223+
String::from_variant(&reconstructed.get_meta("foo")).unwrap()
224+
);
258225
}
259226

260-
ok
261-
}
227+
assert!(unsafe { Reference::try_from_instance_id(instance_id).is_none() });
228+
}}
262229

263230
fn init(handle: InitHandle) {
264231
handle.add_class::<Foo>();

test/src/test_as_arg.rs

+7-17
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,10 @@ pub(crate) fn register(handle: InitHandle) {
88
}
99

1010
pub(crate) fn run_tests() -> bool {
11-
println!(" -- test_as_arg");
11+
let mut ok = true;
1212

13-
let ok = std::panic::catch_unwind(|| {
14-
println!(" -- test_ref_as_arg");
15-
test_ref_as_arg();
16-
17-
println!(" -- test_instance_as_arg");
18-
test_instance_as_arg();
19-
})
20-
.is_ok();
21-
22-
if !ok {
23-
godot_error!(" !! Test test_as_arg failed");
24-
}
13+
ok &= test_as_arg_ref();
14+
ok &= test_as_arg_instance();
2515

2616
ok
2717
}
@@ -40,7 +30,7 @@ impl MyNode {}
4030

4131
// ----------------------------------------------------------------------------------------------------------------------------------------------
4232

43-
fn test_ref_as_arg() {
33+
crate::godot_itest! { test_as_arg_ref {
4434
// Ref<T, Unique>
4535
add_node_with(|n: Ref<Node2D, Unique>| n);
4636

@@ -56,9 +46,9 @@ fn test_ref_as_arg() {
5646

5747
// TRef<T, Shared>
5848
add_node_with(|n: Ref<Node2D, Unique>| unsafe { n.into_shared().assume_safe() });
59-
}
49+
}}
6050

61-
fn test_instance_as_arg() {
51+
crate::godot_itest! { test_as_arg_instance {
6252
// Instance<T, Unique>
6353
add_instance_with(|n: Instance<MyNode, Unique>| n);
6454

@@ -74,7 +64,7 @@ fn test_instance_as_arg() {
7464

7565
// TInstance<T, Shared>
7666
add_instance_with(|n: Instance<MyNode, Unique>| unsafe { n.into_shared().assume_safe() });
77-
}
67+
}}
7868

7969
fn add_node_with<F, T>(to_arg: F)
8070
where

test/src/test_constructor.rs

+29-40
Original file line numberDiff line numberDiff line change
@@ -27,43 +27,32 @@ fn test_constructor() -> bool {
2727
true
2828
}
2929

30-
fn test_from_class_name() -> bool {
31-
println!(" -- test_from_class_name");
32-
33-
let ok = std::panic::catch_unwind(|| {
34-
// Since this method is restricted to Godot types, there is no way we can detect
35-
// here whether any invalid objects are leaked. Instead, the CI script is modified
36-
// to look at stdout for any reported leaks.
37-
38-
let node = Ref::<Node, _>::by_class_name("Node2D").unwrap();
39-
assert_eq!("Node2D", node.get_class().to_string());
40-
let node = node.cast::<Node2D>().unwrap();
41-
assert_eq!("Node2D", node.get_class().to_string());
42-
let _ = node.position();
43-
node.free();
44-
45-
let shader = Ref::<Reference, _>::by_class_name("Shader").unwrap();
46-
assert_eq!("Shader", &shader.get_class().to_string());
47-
let shader = shader.cast::<Shader>().unwrap();
48-
assert_eq!("Shader", &shader.get_class().to_string());
49-
50-
let none = Ref::<Object, _>::by_class_name("Shader");
51-
assert!(none.is_none());
52-
53-
let none = Ref::<Node2D, _>::by_class_name("Spatial");
54-
assert!(none.is_none());
55-
56-
let none = Ref::<Shader, _>::by_class_name("AudioEffectReverb");
57-
assert!(none.is_none());
58-
59-
let none = Ref::<Object, _>::by_class_name("ClassThatDoesNotExistProbably");
60-
assert!(none.is_none());
61-
})
62-
.is_ok();
63-
64-
if !ok {
65-
godot_error!(" !! Test test_from_class_name failed");
66-
}
67-
68-
ok
69-
}
30+
crate::godot_itest! { test_from_class_name {
31+
// Since this method is restricted to Godot types, there is no way we can detect
32+
// here whether any invalid objects are leaked. Instead, the CI script is modified
33+
// to look at stdout for any reported leaks.
34+
35+
let node = Ref::<Node, _>::by_class_name("Node2D").unwrap();
36+
assert_eq!("Node2D", node.get_class().to_string());
37+
let node = node.cast::<Node2D>().unwrap();
38+
assert_eq!("Node2D", node.get_class().to_string());
39+
let _ = node.position();
40+
node.free();
41+
42+
let shader = Ref::<Reference, _>::by_class_name("Shader").unwrap();
43+
assert_eq!("Shader", &shader.get_class().to_string());
44+
let shader = shader.cast::<Shader>().unwrap();
45+
assert_eq!("Shader", &shader.get_class().to_string());
46+
47+
let none = Ref::<Object, _>::by_class_name("Shader");
48+
assert!(none.is_none());
49+
50+
let none = Ref::<Node2D, _>::by_class_name("Spatial");
51+
assert!(none.is_none());
52+
53+
let none = Ref::<Shader, _>::by_class_name("AudioEffectReverb");
54+
assert!(none.is_none());
55+
56+
let none = Ref::<Object, _>::by_class_name("ClassThatDoesNotExistProbably");
57+
assert!(none.is_none());
58+
}}

0 commit comments

Comments
 (0)