Skip to content

Commit b61c4f4

Browse files
committed
Update itests to conform with new safeguard levels
1 parent f267dce commit b61c4f4

File tree

7 files changed

+50
-37
lines changed

7 files changed

+50
-37
lines changed

itest/rust/build.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ fn main() {
272272
rustfmt_if_needed(vec![rust_file]);
273273

274274
godot_bindings::emit_godot_version_cfg();
275+
godot_bindings::emit_safeguard_levels();
275276

276277
// The godot crate has a __codegen-full default feature that enables the godot-codegen/codegen-full feature. When compiling the entire
277278
// workspace itest also gets compiled with full codegen due to feature unification. This causes compiler errors since the

itest/rust/src/builtin_tests/containers/variant_test.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use godot::obj::{Gd, InstanceId, NewAlloc, NewGd};
2121
use godot::sys::GodotFfi;
2222

2323
use crate::common::roundtrip;
24-
use crate::framework::{expect_panic, itest, runs_release};
24+
use crate::framework::{expect_panic, expect_panic_or_ub, itest, runs_release};
2525

2626
const TEST_BASIS: Basis = Basis::from_rows(
2727
Vector3::new(1.0, 2.0, 3.0),
@@ -272,7 +272,7 @@ fn variant_dead_object_conversions() {
272272
);
273273

274274
// Variant::to().
275-
expect_panic("Variant::to() with dead object should panic", || {
275+
expect_panic_or_ub("Variant::to() with dead object should panic", || {
276276
let _: Gd<Node> = variant.to();
277277
});
278278

@@ -361,15 +361,15 @@ fn variant_array_from_untyped_conversions() {
361361
)
362362
}
363363

364-
// Same builtin type INT, but incompatible integers (Debug-only).
364+
// Same builtin type INT, but incompatible integers (strict-safeguards only).
365365
#[itest]
366366
fn variant_array_bad_integer_conversions() {
367367
let i32_array: Array<i32> = array![1, 2, 160, -40];
368368
let i32_variant = i32_array.to_variant();
369369
let i8_back = i32_variant.try_to::<Array<i8>>();
370370

371-
// In Debug mode, we expect an error upon conversion.
372-
#[cfg(debug_assertions)]
371+
// In strict safeguard mode, we expect an error upon conversion.
372+
#[cfg(safeguards_strict)]
373373
{
374374
let err = i8_back.expect_err("Array<i32> -> Array<i8> conversion should fail");
375375
assert_eq!(
@@ -378,8 +378,8 @@ fn variant_array_bad_integer_conversions() {
378378
)
379379
}
380380

381-
// In Release mode, we expect the conversion to succeed, but a panic to occur on element access.
382-
#[cfg(not(debug_assertions))]
381+
// In balanced/disengaged modes, we expect the conversion to succeed, but a panic to occur on element access.
382+
#[cfg(not(safeguards_strict))]
383383
{
384384
let i8_array = i8_back.expect("Array<i32> -> Array<i8> conversion should succeed");
385385
expect_panic("accessing element 160 as i8 should panic", || {

itest/rust/src/framework/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,14 @@ pub fn expect_panic(context: &str, code: impl FnOnce()) {
200200
);
201201
}
202202

203+
/// Run for code that panics in *strict* and *balanced* safeguard levels, but cause UB in *disengaged* level.
204+
///
205+
/// The code is not executed for the latter.
206+
pub fn expect_panic_or_ub(_context: &str, _code: impl FnOnce()) {
207+
#[cfg(safeguards_balanced)]
208+
expect_panic(_context, _code);
209+
}
210+
203211
pin_project_lite::pin_project! {
204212
pub struct ExpectPanicFuture<T: std::future::Future> {
205213
context: &'static str,

itest/rust/src/object_tests/base_init_test.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use godot::builtin::Vector2;
1010
use godot::classes::notify::ObjectNotification;
1111
use godot::classes::{ClassDb, IRefCounted, RefCounted};
1212
use godot::meta::ToGodot;
13-
use godot::obj::{Base, Gd, InstanceId, NewAlloc, NewGd, Singleton, WithBaseField};
13+
use godot::obj::{Base, Gd, InstanceId, NewGd, Singleton, WithBaseField};
1414
use godot::register::{godot_api, GodotClass};
1515
use godot::task::TaskHandle;
1616

@@ -65,13 +65,14 @@ fn base_init_extracted_gd() {
6565

6666
// Checks bad practice of rug-pulling the base pointer.
6767
#[itest]
68+
#[cfg(safeguards_balanced)]
6869
fn base_init_freed_gd() {
6970
let mut free_executed = false;
7071

7172
expect_panic("base object is destroyed", || {
7273
let _obj = Gd::<Based>::from_init_fn(|base| {
7374
let obj = base.to_init_gd();
74-
obj.free(); // Causes the problem, but doesn't panic yet.
75+
obj.free(); // Causes the problem, but doesn't panic yet. UB in safeguards-disengaged.
7576
free_executed = true;
7677

7778
Based { base, i: 456 }
@@ -144,27 +145,28 @@ fn verify_complex_init((obj, base): (Gd<RefcBased>, Gd<RefCounted>)) -> Instance
144145
id
145146
}
146147

147-
#[cfg(debug_assertions)]
148+
#[cfg(safeguards_strict)]
148149
#[itest]
149150
fn base_init_outside_init() {
151+
use godot::obj::NewAlloc;
150152
let mut obj = Based::new_alloc();
151153

152154
expect_panic("to_init_gd() outside init() function", || {
153155
let guard = obj.bind_mut();
154-
let _gd = guard.base.to_init_gd(); // Panics in Debug builds.
156+
let _gd = guard.base.to_init_gd(); // Panics in strict safeguard mode.
155157
});
156158

157159
obj.free();
158160
}
159161

160-
#[cfg(debug_assertions)]
162+
#[cfg(safeguards_strict)]
161163
#[itest]
162164
fn base_init_to_gd() {
163165
expect_panic("WithBaseField::to_gd() inside init() function", || {
164166
let _obj = Gd::<Based>::from_init_fn(|base| {
165167
let temp_obj = Based { base, i: 999 };
166168

167-
// Call to self.to_gd() during initialization should panic in Debug builds.
169+
// Call to self.to_gd() during initialization should panic in strict safeguard mode.
168170
let _gd = godot::obj::WithBaseField::to_gd(&temp_obj);
169171

170172
temp_obj

itest/rust/src/object_tests/base_test.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
use godot::prelude::*;
99

10-
use crate::framework::{expect_panic, itest};
10+
use crate::framework::{expect_panic_or_ub, itest};
1111

1212
#[itest(skip)]
1313
fn base_test_is_weak() {
@@ -82,6 +82,8 @@ fn base_gd_self() {
8282
}
8383

8484
// Hardening against https://github.com/godot-rust/gdext/issues/711.
85+
// Furthermore, this now keeps expect_panic_or_ub() instead of expect_panic(), although it's questionable if much remains with all these checks
86+
// disabled. There is still some logic remaining, and an alternative would be to #[cfg(safeguards_balanced)] this.
8587
#[itest]
8688
fn base_smuggling() {
8789
let (mut obj, extracted_base) = create_object_with_extracted_base();
@@ -98,27 +100,27 @@ fn base_smuggling() {
98100
extracted_base_obj.free();
99101

100102
// Access to object should now fail.
101-
expect_panic("object with dead base: calling base methods", || {
103+
expect_panic_or_ub("object with dead base: calling base methods", || {
102104
obj.get_position();
103105
});
104-
expect_panic("object with dead base: bind()", || {
106+
expect_panic_or_ub("object with dead base: bind()", || {
105107
obj.bind();
106108
});
107-
expect_panic("object with dead base: instance_id()", || {
109+
expect_panic_or_ub("object with dead base: instance_id()", || {
108110
obj.instance_id();
109111
});
110-
expect_panic("object with dead base: clone()", || {
112+
expect_panic_or_ub("object with dead base: clone()", || {
111113
let _ = obj.clone();
112114
});
113-
expect_panic("object with dead base: upcast()", || {
115+
expect_panic_or_ub("object with dead base: upcast()", || {
114116
obj.upcast::<Object>();
115117
});
116118

117119
// Now vice versa: destroy object, access base.
118120
let (obj, extracted_base) = create_object_with_extracted_base();
119121
obj.free();
120122

121-
expect_panic("accessing extracted base of dead object", || {
123+
expect_panic_or_ub("accessing extracted base of dead object", || {
122124
extracted_base.__constructed_gd().get_position();
123125
});
124126
}

itest/rust/src/object_tests/object_swap_test.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
// A lot these tests also exist in the `object_test` module, where they test object lifetime rather than type swapping.
1111
// TODO consolidate them, so that it's less likely to forget edge cases.
1212

13-
// Disabled in Release mode, since we don't perform the subtype check there.
14-
#![cfg(debug_assertions)]
13+
// Disabled in balanced/disengaged levels, since we don't perform the subtype check there.
14+
#![cfg(safeguards_strict)]
1515

1616
use godot::builtin::GString;
1717
use godot::classes::{Node, Node3D, Object};

itest/rust/src/object_tests/object_test.rs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use godot::obj::{Base, Gd, Inherits, InstanceId, NewAlloc, NewGd, RawGd, Singlet
2222
use godot::register::{godot_api, GodotClass};
2323
use godot::sys::{self, interface_fn, GodotFfi};
2424

25-
use crate::framework::{expect_panic, itest, TestContext};
25+
use crate::framework::{expect_panic, expect_panic_or_ub, itest, TestContext};
2626

2727
// TODO:
2828
// * make sure that ptrcalls are used when possible (i.e. when type info available; maybe GDScript integration test)
@@ -171,7 +171,7 @@ fn object_instance_id_when_freed() {
171171
node.clone().free(); // destroys object without moving out of reference
172172
assert!(!node.is_instance_valid());
173173

174-
expect_panic("instance_id() on dead object", move || {
174+
expect_panic_or_ub("instance_id() on dead object", move || {
175175
node.instance_id();
176176
});
177177
}
@@ -235,7 +235,7 @@ fn object_user_bind_after_free() {
235235
let copy = obj.clone();
236236
obj.free();
237237

238-
expect_panic("bind() on dead user object", move || {
238+
expect_panic_or_ub("bind() on dead user object", move || {
239239
let _ = copy.bind();
240240
});
241241
}
@@ -247,7 +247,7 @@ fn object_user_free_during_bind() {
247247

248248
let copy = obj.clone(); // TODO clone allowed while bound?
249249

250-
expect_panic("direct free() on user while it's bound", move || {
250+
expect_panic_or_ub("direct free() on user while it's bound", move || {
251251
copy.free();
252252
});
253253

@@ -275,7 +275,7 @@ fn object_engine_freed_argument_passing(ctx: &TestContext) {
275275

276276
// Destroy object and then pass it to a Godot engine API.
277277
node.free();
278-
expect_panic("pass freed Gd<T> to Godot engine API (T=Node)", || {
278+
expect_panic_or_ub("pass freed Gd<T> to Godot engine API (T=Node)", || {
279279
tree.add_child(&node2);
280280
});
281281
}
@@ -288,10 +288,10 @@ fn object_user_freed_casts() {
288288

289289
// Destroy object and then pass it to a Godot engine API (upcast itself works, see other tests).
290290
obj.free();
291-
expect_panic("Gd<T>::upcast() on dead object (T=user)", || {
291+
expect_panic_or_ub("Gd<T>::upcast() on dead object (T=user)", || {
292292
let _ = obj2.upcast::<Object>();
293293
});
294-
expect_panic("Gd<T>::cast() on dead object (T=user)", || {
294+
expect_panic_or_ub("Gd<T>::cast() on dead object (T=user)", || {
295295
let _ = base_obj.cast::<ObjPayload>();
296296
});
297297
}
@@ -306,7 +306,7 @@ fn object_user_freed_argument_passing() {
306306

307307
// Destroy object and then pass it to a Godot engine API (upcast itself works, see other tests).
308308
obj.free();
309-
expect_panic("pass freed Gd<T> to Godot engine API (T=user)", || {
309+
expect_panic_or_ub("pass freed Gd<T> to Godot engine API (T=user)", || {
310310
engine.register_singleton("NeverRegistered", &obj2);
311311
});
312312
}
@@ -344,7 +344,7 @@ fn object_user_call_after_free() {
344344
let mut copy = obj.clone();
345345
obj.free();
346346

347-
expect_panic("call() on dead user object", move || {
347+
expect_panic_or_ub("call() on dead user object", move || {
348348
let _ = copy.call("get_instance_id", &[]);
349349
});
350350
}
@@ -355,7 +355,7 @@ fn object_engine_use_after_free() {
355355
let copy = node.clone();
356356
node.free();
357357

358-
expect_panic("call method on dead engine object", move || {
358+
expect_panic_or_ub("call method on dead engine object", move || {
359359
copy.get_position();
360360
});
361361
}
@@ -366,7 +366,7 @@ fn object_engine_use_after_free_varcall() {
366366
let mut copy = node.clone();
367367
node.free();
368368

369-
expect_panic("call method on dead engine object", move || {
369+
expect_panic_or_ub("call method on dead engine object", move || {
370370
copy.call_deferred("get_position", &[]);
371371
});
372372
}
@@ -411,13 +411,13 @@ fn object_dead_eq() {
411411

412412
{
413413
let lhs = a.clone();
414-
expect_panic("Gd::eq() panics when one operand is dead", move || {
414+
expect_panic_or_ub("Gd::eq() panics when one operand is dead", move || {
415415
let _ = lhs == b;
416416
});
417417
}
418418
{
419419
let rhs = a.clone();
420-
expect_panic("Gd::ne() panics when one operand is dead", move || {
420+
expect_panic_or_ub("Gd::ne() panics when one operand is dead", move || {
421421
let _ = b2 != rhs;
422422
});
423423
}
@@ -826,7 +826,7 @@ fn object_engine_manual_double_free() {
826826
let node2 = node.clone();
827827
node.free();
828828

829-
expect_panic("double free()", move || {
829+
expect_panic_or_ub("double free()", move || {
830830
node2.free();
831831
});
832832
}
@@ -845,7 +845,7 @@ fn object_user_double_free() {
845845
let obj2 = obj.clone();
846846
obj.call("free", &[]);
847847

848-
expect_panic("double free()", move || {
848+
expect_panic_or_ub("double free()", move || {
849849
obj2.free();
850850
});
851851
}

0 commit comments

Comments
 (0)