From 05d954e1ab2421ad4a7662e3163bbd49ffe646c1 Mon Sep 17 00:00:00 2001 From: Greeble <166992735+greeble-dev@users.noreply.github.com> Date: Wed, 2 Jul 2025 23:39:04 +0100 Subject: [PATCH] Add test that repros #11111 (different loader settings produce same asset) (#19094) ## Objective Add a test that reproduces #11111 (and partially #18267). The bug is that asset loader settings are effectively ignored if the same asset is loaded multiple times with different settings. ## Solution Add a unit test to `bevy_assets/lib.rs`. The test will be marked as `#[ignore]` until #11111 is fixed. ```rust // Load the same asset with different settings. let handle_1 = load(asset_server, "test.u8", 1); let handle_2 = load(asset_server, "test.u8", 2); // Handles should be different. assert_ne!(handle_1, handle_2); ``` ## Concerns I'm not 100% sure that the current behaviour is actually broken - I can't see anything in the asset system design docs that explicitly says different settings should create different asset ids. UPDATE: Sentiment from issue comments and discord varies between "bug" and "undesirable consequence of design decisions, alternatives should be explored". So I've concluded that the test is valid and desirable. ## Testing ```sh cargo test -p bevy_asset --features multi_threaded # Or to repro the issue: cargo test -p bevy_asset --features multi_threaded -- --ignored ``` --- crates/bevy_asset/src/lib.rs | 88 ++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index 16da4313f0..8186b6315d 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -2000,4 +2000,92 @@ mod tests { app.world_mut().run_schedule(Update); } + + #[test] + #[ignore = "blocked on https://github.com/bevyengine/bevy/issues/11111"] + fn same_asset_different_settings() { + // Test loading the same asset twice with different settings. This should + // produce two distinct assets. + + // First, implement an asset that's a single u8, whose value is copied from + // the loader settings. + + #[derive(Asset, TypePath)] + struct U8Asset(u8); + + #[derive(Serialize, Deserialize, Default)] + struct U8LoaderSettings(u8); + + struct U8Loader; + + impl AssetLoader for U8Loader { + type Asset = U8Asset; + type Settings = U8LoaderSettings; + type Error = crate::loader::LoadDirectError; + + async fn load( + &self, + _: &mut dyn Reader, + settings: &Self::Settings, + _: &mut LoadContext<'_>, + ) -> Result { + Ok(U8Asset(settings.0)) + } + + fn extensions(&self) -> &[&str] { + &["u8"] + } + } + + // Create a test asset. + + let dir = Dir::default(); + dir.insert_asset(Path::new("test.u8"), &[]); + + let asset_source = AssetSource::build() + .with_reader(move || Box::new(MemoryAssetReader { root: dir.clone() })); + + // Set up the app. + + let mut app = App::new(); + + app.register_asset_source(AssetSourceId::Default, asset_source) + .add_plugins((TaskPoolPlugin::default(), AssetPlugin::default())) + .init_asset::() + .register_asset_loader(U8Loader); + + let asset_server = app.world().resource::(); + + // Load the test asset twice but with different settings. + + fn load(asset_server: &AssetServer, path: &str, value: u8) -> Handle { + asset_server.load_with_settings::( + path, + move |s: &mut U8LoaderSettings| s.0 = value, + ) + } + + let handle_1 = load(asset_server, "test.u8", 1); + let handle_2 = load(asset_server, "test.u8", 2); + + // Handles should be different. + + assert_ne!(handle_1, handle_2); + + run_app_until(&mut app, |world| { + let (Some(asset_1), Some(asset_2)) = ( + world.resource::>().get(&handle_1), + world.resource::>().get(&handle_2), + ) else { + return None; + }; + + // Values should match the settings. + + assert_eq!(asset_1.0, 1); + assert_eq!(asset_2.0, 2); + + Some(()) + }); + } }