|
5 | 5 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. |
6 | 6 | */ |
7 | 7 |
|
| 8 | +use std::cell::Cell; |
8 | 9 | use std::ops::DerefMut; |
| 10 | +use std::rc::Rc; |
9 | 11 |
|
10 | 12 | use godot::obj::WithBaseField; |
11 | 13 | use godot::prelude::*; |
12 | | -use godot::task::{SignalFuture, TaskHandle}; |
| 14 | +use godot::task::{self, SignalFuture, TaskHandle}; |
13 | 15 |
|
14 | 16 | use crate::framework::itest; |
15 | 17 |
|
@@ -143,6 +145,34 @@ fn run_deferred_gd_user_class(ctx: &crate::framework::TestContext) -> TaskHandle |
143 | 145 | guard.create_assertion_task() |
144 | 146 | } |
145 | 147 |
|
| 148 | +// Regression test for https://github.com/godot-rust/gdext/issues/1624: A `run_deferred[_gd]` closure must not run after `SceneTree` teardown, |
| 149 | +// matching Godot's `call_deferred()` which drops such queued calls. |
| 150 | +#[itest(async)] |
| 151 | +fn run_deferred_skipped_when_exiting(ctx: &crate::framework::TestContext) -> TaskHandle { |
| 152 | + let mut test_node = DeferredTestNode::new_alloc(); |
| 153 | + ctx.scene_tree.clone().add_child(&test_node); |
| 154 | + |
| 155 | + // The guard clears the exiting flag when dropped. The deferred call is flushed only a frame later, so we must keep the guard alive until |
| 156 | + // after that flush -- otherwise the flag would be cleared too early and the closure would run. Hence it is moved into the async task below. |
| 157 | + let guard = task::simulate_engine_exiting(); |
| 158 | + |
| 159 | + let ran = Rc::new(Cell::new(false)); |
| 160 | + let ran_setter = ran.clone(); |
| 161 | + test_node.run_deferred_gd(move |_| ran_setter.set(true)); |
| 162 | + |
| 163 | + // `test_completed` fires from `process()`, by which point the deferred queue has already been flushed (see other tests above). |
| 164 | + let mut guard_node = test_node.bind_mut(); |
| 165 | + let run_test: SignalFuture<(StringName,)> = guard_node.signals().test_completed().to_future(); |
| 166 | + drop(guard_node); |
| 167 | + |
| 168 | + task::spawn(async move { |
| 169 | + let _ = run_test.await; |
| 170 | + let was_run = ran.get(); |
| 171 | + drop(guard); // Keep the flag set until after the deferred flush. |
| 172 | + assert!(!was_run, "run_deferred_gd() must not run during shutdown"); |
| 173 | + }) |
| 174 | +} |
| 175 | + |
146 | 176 | #[itest(async)] |
147 | 177 | fn run_deferred_engine_class(ctx: &crate::framework::TestContext) -> TaskHandle { |
148 | 178 | let mut test_node = DeferredTestNode::new_alloc(); |
|
0 commit comments