@@ -3258,4 +3258,231 @@ mod tests {
32583258 // This assertion exists to "prove" that this problem exists.
32593259 assert ! ( processed_dir. get_asset( gltfx_path) . is_none( ) ) ;
32603260 }
3261+
3262+ #[ test]
3263+ fn same_asset_different_settings ( ) {
3264+ // Test loading the same asset twice with different settings. This should
3265+ // produce two distinct assets.
3266+
3267+ // First, implement an asset that's a single u8, whose value is copied from
3268+ // the loader settings.
3269+
3270+ #[ derive( Asset , TypePath ) ]
3271+ struct U8Asset ( u8 ) ;
3272+
3273+ #[ derive( Serialize , Deserialize , Default ) ]
3274+ struct U8LoaderSettings ( u8 ) ;
3275+
3276+ struct U8Loader ;
3277+
3278+ impl AssetLoader for U8Loader {
3279+ type Asset = U8Asset ;
3280+ type Settings = U8LoaderSettings ;
3281+ type Error = crate :: loader:: LoadDirectError ;
3282+
3283+ async fn load (
3284+ & self ,
3285+ _: & mut dyn Reader ,
3286+ settings : & Self :: Settings ,
3287+ _: & mut LoadContext < ' _ > ,
3288+ ) -> Result < Self :: Asset , Self :: Error > {
3289+ Ok ( U8Asset ( settings. 0 ) )
3290+ }
3291+
3292+ fn extensions ( & self ) -> & [ & str ] {
3293+ & [ "u8" ]
3294+ }
3295+ }
3296+
3297+ // Create a test asset.
3298+
3299+ let dir = Dir :: default ( ) ;
3300+ dir. insert_asset ( Path :: new ( "test.u8" ) , & [ ] ) ;
3301+
3302+ let asset_source = AssetSource :: build ( )
3303+ . with_reader ( move || Box :: new ( MemoryAssetReader { root : dir. clone ( ) } ) ) ;
3304+
3305+ // Set up the app.
3306+
3307+ let mut app = App :: new ( ) ;
3308+
3309+ app. register_asset_source ( AssetSourceId :: Default , asset_source)
3310+ . add_plugins ( ( TaskPoolPlugin :: default ( ) , AssetPlugin :: default ( ) ) )
3311+ . init_asset :: < U8Asset > ( )
3312+ . register_asset_loader ( U8Loader ) ;
3313+
3314+ let asset_server = app. world ( ) . resource :: < AssetServer > ( ) ;
3315+
3316+ // Load the test asset twice but with different settings.
3317+
3318+ fn load ( asset_server : & AssetServer , path : & ' static str , value : u8 ) -> Handle < U8Asset > {
3319+ asset_server. load_with_settings :: < U8Asset , U8LoaderSettings > (
3320+ path,
3321+ move |s : & mut U8LoaderSettings | s. 0 = value,
3322+ )
3323+ }
3324+
3325+ let handle_1 = load ( asset_server, "test.u8" , 1 ) ;
3326+ let handle_2 = load ( asset_server, "test.u8" , 2 ) ;
3327+
3328+ // Handles should be different.
3329+
3330+ // These handles should be different, but due to
3331+ // https://github.com/bevyengine/bevy/pull/21564, they are not. Once 21564 is fixed, we
3332+ // should replace these expects.
3333+ //
3334+ // assert_ne!(handle_1, handle_2);
3335+ assert_eq ! ( handle_1, handle_2) ;
3336+
3337+ run_app_until ( & mut app, |world| {
3338+ let ( Some ( asset_1) , Some ( asset_2) ) = (
3339+ world. resource :: < Assets < U8Asset > > ( ) . get ( & handle_1) ,
3340+ world. resource :: < Assets < U8Asset > > ( ) . get ( & handle_2) ,
3341+ ) else {
3342+ return None ;
3343+ } ;
3344+
3345+ // Values should match the settings.
3346+
3347+ // These values should be different, but due to
3348+ // https://github.com/bevyengine/bevy/pull/21564, they are not. Once 21564 is fixed, we
3349+ // should replace these expects.
3350+ //
3351+ // assert_eq!(asset_1.0, 1);
3352+ // assert_eq!(asset_2.0, 2);
3353+ assert_eq ! ( asset_1. 0 , asset_2. 0 ) ;
3354+
3355+ Some ( ( ) )
3356+ } ) ;
3357+ }
3358+
3359+ #[ test]
3360+ fn loading_two_subassets_does_not_start_two_loads ( ) {
3361+ let mut app = App :: new ( ) ;
3362+
3363+ let dir = Dir :: default ( ) ;
3364+ dir. insert_asset ( Path :: new ( "test.txt" ) , & [ ] ) ;
3365+
3366+ let asset_source = AssetSource :: build ( )
3367+ . with_reader ( move || Box :: new ( MemoryAssetReader { root : dir. clone ( ) } ) ) ;
3368+
3369+ app. register_asset_source ( AssetSourceId :: Default , asset_source)
3370+ . add_plugins ( ( TaskPoolPlugin :: default ( ) , AssetPlugin :: default ( ) ) )
3371+ . init_asset :: < TestAsset > ( ) ;
3372+
3373+ struct TwoSubassetLoader ;
3374+
3375+ impl AssetLoader for TwoSubassetLoader {
3376+ type Asset = TestAsset ;
3377+ type Settings = ( ) ;
3378+ type Error = std:: io:: Error ;
3379+
3380+ async fn load (
3381+ & self ,
3382+ _reader : & mut dyn Reader ,
3383+ _settings : & Self :: Settings ,
3384+ load_context : & mut LoadContext < ' _ > ,
3385+ ) -> Result < Self :: Asset , Self :: Error > {
3386+ load_context. add_labeled_asset ( "A" . into ( ) , TestAsset ) ;
3387+ load_context. add_labeled_asset ( "B" . into ( ) , TestAsset ) ;
3388+ Ok ( TestAsset )
3389+ }
3390+
3391+ fn extensions ( & self ) -> & [ & str ] {
3392+ & [ "txt" ]
3393+ }
3394+ }
3395+
3396+ app. register_asset_loader ( TwoSubassetLoader ) ;
3397+
3398+ let asset_server = app. world ( ) . resource :: < AssetServer > ( ) . clone ( ) ;
3399+ let _subasset_1: Handle < TestAsset > = asset_server. load ( "test.txt#A" ) ;
3400+ let _subasset_2: Handle < TestAsset > = asset_server. load ( "test.txt#B" ) ;
3401+
3402+ app. update ( ) ;
3403+
3404+ // Due to https://github.com/bevyengine/bevy/issues/12756, this expectation fails. Once
3405+ // #12756 is fixed, we should swap these asserts.
3406+ //
3407+ // assert_eq!(get_started_load_count(app.world()), 1);
3408+ assert_eq ! ( get_started_load_count( app. world( ) ) , 2 ) ;
3409+ }
3410+
3411+ #[ test]
3412+ fn get_strong_handle_prevents_reload_when_asset_still_alive ( ) {
3413+ let mut app = App :: new ( ) ;
3414+
3415+ let dir = Dir :: default ( ) ;
3416+ dir. insert_asset ( Path :: new ( "test.txt" ) , & [ ] ) ;
3417+
3418+ let asset_source = AssetSource :: build ( )
3419+ . with_reader ( move || Box :: new ( MemoryAssetReader { root : dir. clone ( ) } ) ) ;
3420+
3421+ app. register_asset_source ( AssetSourceId :: Default , asset_source)
3422+ . add_plugins ( ( TaskPoolPlugin :: default ( ) , AssetPlugin :: default ( ) ) )
3423+ . init_asset :: < TestAsset > ( ) ;
3424+
3425+ struct TrivialLoader ;
3426+
3427+ impl AssetLoader for TrivialLoader {
3428+ type Asset = TestAsset ;
3429+ type Settings = ( ) ;
3430+ type Error = std:: io:: Error ;
3431+
3432+ async fn load (
3433+ & self ,
3434+ _reader : & mut dyn Reader ,
3435+ _settings : & Self :: Settings ,
3436+ _load_context : & mut LoadContext < ' _ > ,
3437+ ) -> Result < Self :: Asset , Self :: Error > {
3438+ Ok ( TestAsset )
3439+ }
3440+
3441+ fn extensions ( & self ) -> & [ & str ] {
3442+ & [ "txt" ]
3443+ }
3444+ }
3445+
3446+ app. register_asset_loader ( TrivialLoader ) ;
3447+
3448+ let asset_server = app. world ( ) . resource :: < AssetServer > ( ) . clone ( ) ;
3449+ let original_handle: Handle < TestAsset > = asset_server. load ( "test.txt" ) ;
3450+
3451+ // Wait for the asset to load.
3452+ run_app_until ( & mut app, |world| {
3453+ world
3454+ . resource :: < Assets < TestAsset > > ( )
3455+ . get ( & original_handle)
3456+ . map ( |_| ( ) )
3457+ } ) ;
3458+
3459+ assert_eq ! ( get_started_load_count( app. world( ) ) , 1 ) ;
3460+
3461+ // Get a new strong handle from the original handle's ID.
3462+ let new_handle = app
3463+ . world_mut ( )
3464+ . resource_mut :: < Assets < TestAsset > > ( )
3465+ . get_strong_handle ( original_handle. id ( ) )
3466+ . unwrap ( ) ;
3467+
3468+ // Drop the original handle. This should still leave the asset alive.
3469+ drop ( original_handle) ;
3470+
3471+ app. update ( ) ;
3472+ assert ! ( app
3473+ . world( )
3474+ . resource:: <Assets <TestAsset >>( )
3475+ . get( & new_handle)
3476+ . is_some( ) ) ;
3477+
3478+ let _other_handle: Handle < TestAsset > = asset_server. load ( "test.txt" ) ;
3479+ app. update ( ) ;
3480+ // The asset server should **not** have started a new load, since the asset is still alive.
3481+
3482+ // Due to https://github.com/bevyengine/bevy/issues/20651, we do get a second load. Once
3483+ // #20651 is fixed, we should swap these asserts.
3484+ //
3485+ // assert_eq!(get_started_load_count(app.world()), 1);
3486+ assert_eq ! ( get_started_load_count( app. world( ) ) , 2 ) ;
3487+ }
32613488}
0 commit comments