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
```
This commit is contained in:
Greeble 2025-07-02 23:39:04 +01:00 committed by GitHub
parent 8098cf21ca
commit 05d954e1ab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -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<Self::Asset, Self::Error> {
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::<U8Asset>()
.register_asset_loader(U8Loader);
let asset_server = app.world().resource::<AssetServer>();
// Load the test asset twice but with different settings.
fn load(asset_server: &AssetServer, path: &str, value: u8) -> Handle<U8Asset> {
asset_server.load_with_settings::<U8Asset, U8LoaderSettings>(
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::<Assets<U8Asset>>().get(&handle_1),
world.resource::<Assets<U8Asset>>().get(&handle_2),
) else {
return None;
};
// Values should match the settings.
assert_eq!(asset_1.0, 1);
assert_eq!(asset_2.0, 2);
Some(())
});
}
}