From 4dc8fb49199fea2065db519fc3f06fdadb5348e1 Mon Sep 17 00:00:00 2001 From: andriyDev Date: Wed, 22 Apr 2026 09:19:28 -0700 Subject: [PATCH] Change the ambiguity_detection test to be more strict. (#23846) # Objective - Fixes #23843 by preventing these systems from recurring inside Bevy. - Note this does not fix this for users of Bevy. They would need to setup their own "strict" checking. ## Solution - Disable `auto_insert_apply_deferred` in the `ambiguity_detection` example. Now these stages won't accidentally resolve these ambiguities, so they will be detected! - Stop running the app in `ambiguity_detection` and just manually initialize the schedules. - We have to do this otherwise the schedules just panic because they depend on various resource initializations to be executed by commands (which were previously being applied by the automatically inserted `apply_deferred`). ### Caveats: - The ambiguity_detection CI test now won't detect ambiguities in runtime-added systems. - This is not a pattern we use today, and I'd encourage us to **delete** systems instead so that tooling can grab the systems before the app runs. - To clarify, this is **not** ambiguity detection in general - just Bevy's CI test that is affected. ## Testing - Ran the examples and all the ambiguities have been fixed in previous PRs. --- tests/ecs/ambiguity_detection.rs | 39 +++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/tests/ecs/ambiguity_detection.rs b/tests/ecs/ambiguity_detection.rs index c43f1c6965..e7b0433d4c 100644 --- a/tests/ecs/ambiguity_detection.rs +++ b/tests/ecs/ambiguity_detection.rs @@ -35,17 +35,21 @@ fn main() { let sub_app = app.sub_app_mut(bevy_render::RenderApp); configure_ambiguity_detection(sub_app); + // Make sure all the system stuff is added. app.finish(); app.cleanup(); - app.update(); - let main_app_ambiguities = count_ambiguities(app.main()); + let main_app_ambiguities = count_ambiguities(app.main_mut()); assert_eq!( main_app_ambiguities.total(), 0, "Main app has unexpected ambiguities among the following schedules: \n{main_app_ambiguities:#?}.", ); - let render_app_ambiguities = count_ambiguities(app.sub_app(bevy_render::RenderApp)); + + let render_app = app.sub_app_mut(bevy_render::RenderApp); + // Initialize the MainWorld so the render world systems don't fail initialization. + render_app.init_resource::(); + let render_app_ambiguities = count_ambiguities(render_app); assert_eq!( render_app_ambiguities.total(), 0, @@ -69,6 +73,14 @@ fn configure_ambiguity_detection(sub_app: &mut SubApp) { schedule.set_build_settings(ScheduleBuildSettings { // NOTE: you can change this to `LogLevel::Ignore` to easily see the current number of ambiguities. ambiguity_detection: LogLevel::Warn, + // With auto-inserted apply_deferred stages, these can cause two ambiguous systems to + // become accidentally ordered by one of the apply_deferred stages. Disabling requires + // us to meet a higher bar. We don't just want no ambiguities - we also don't want + // changes to systems or the auto-insert code from "creating" new ambiguities (by + // reordering the graph). However, the cost is that the graph is no longer runnable, + // since Bevy crates often rely on auto-insert apply_deferred to not panic (e.g., + // because a resource wasn't inserted). + auto_insert_apply_deferred: false, use_shortnames: false, ..default() }); @@ -76,12 +88,23 @@ fn configure_ambiguity_detection(sub_app: &mut SubApp) { } /// Returns the number of conflicting systems per schedule. -fn count_ambiguities(sub_app: &SubApp) -> AmbiguitiesCount { - let schedules = sub_app.world().resource::(); +fn count_ambiguities(sub_app: &mut SubApp) -> AmbiguitiesCount { + let schedule_labels = sub_app + .world() + .resource::() + .iter() + .map(|(_, schedule)| schedule.label()) + .collect::>(); let mut ambiguities = >::default(); - for (_, schedule) in schedules.iter() { - let ambiguities_in_schedule = schedule.graph().conflicting_systems().len(); - ambiguities.insert(schedule.label(), ambiguities_in_schedule); + for label in schedule_labels { + let ambiguities_in_schedule = + sub_app + .world_mut() + .schedule_scope(label, |world, schedule| { + schedule.initialize(world).unwrap().unwrap(); + schedule.graph().conflicting_systems().len() + }); + ambiguities.insert(label, ambiguities_in_schedule); } AmbiguitiesCount(ambiguities) }