@@ -2657,6 +2657,28 @@ mod tests {
26572657 assert_eq ! ( get_started_load_count( app. world( ) ) , 2 ) ;
26582658 }
26592659
2660+ /// A trivial asset loader that just immediately produces a [`TestAsset`].
2661+ struct TrivialLoader ;
2662+
2663+ impl AssetLoader for TrivialLoader {
2664+ type Asset = TestAsset ;
2665+ type Settings = ( ) ;
2666+ type Error = std:: io:: Error ;
2667+
2668+ async fn load (
2669+ & self ,
2670+ _reader : & mut dyn Reader ,
2671+ _settings : & Self :: Settings ,
2672+ _load_context : & mut LoadContext < ' _ > ,
2673+ ) -> Result < Self :: Asset , Self :: Error > {
2674+ Ok ( TestAsset )
2675+ }
2676+
2677+ fn extensions ( & self ) -> & [ & str ] {
2678+ & [ "txt" ]
2679+ }
2680+ }
2681+
26602682 #[ test]
26612683 fn get_strong_handle_prevents_reload_when_asset_still_alive ( ) {
26622684 let mut app = App :: new ( ) ;
@@ -2676,27 +2698,6 @@ mod tests {
26762698 ) )
26772699 . init_asset :: < TestAsset > ( ) ;
26782700
2679- struct TrivialLoader ;
2680-
2681- impl AssetLoader for TrivialLoader {
2682- type Asset = TestAsset ;
2683- type Settings = ( ) ;
2684- type Error = std:: io:: Error ;
2685-
2686- async fn load (
2687- & self ,
2688- _reader : & mut dyn Reader ,
2689- _settings : & Self :: Settings ,
2690- _load_context : & mut LoadContext < ' _ > ,
2691- ) -> Result < Self :: Asset , Self :: Error > {
2692- Ok ( TestAsset )
2693- }
2694-
2695- fn extensions ( & self ) -> & [ & str ] {
2696- & [ "txt" ]
2697- }
2698- }
2699-
27002701 app. register_asset_loader ( TrivialLoader ) ;
27012702
27022703 let asset_server = app. world ( ) . resource :: < AssetServer > ( ) . clone ( ) ;
@@ -2739,4 +2740,94 @@ mod tests {
27392740 // assert_eq!(get_started_load_count(app.world()), 1);
27402741 assert_eq ! ( get_started_load_count( app. world( ) ) , 2 ) ;
27412742 }
2743+
2744+ // Creates a new asset source that reads from the returned dir.
2745+ fn create_dir_source ( ) -> ( AssetSourceBuilder , Dir ) {
2746+ let dir = Dir :: default ( ) ;
2747+ let dir_clone = dir. clone ( ) ;
2748+ (
2749+ AssetSourceBuilder :: new ( move || {
2750+ Box :: new ( MemoryAssetReader {
2751+ root : dir_clone. clone ( ) ,
2752+ } )
2753+ } ) ,
2754+ dir,
2755+ )
2756+ }
2757+
2758+ #[ test]
2759+ fn can_load_asset_from_runtime_added_sources ( ) {
2760+ let mut app = App :: new ( ) ;
2761+ let ( default_source, _) = create_dir_source ( ) ;
2762+ app. add_plugins ( (
2763+ TaskPoolPlugin :: default ( ) ,
2764+ AssetPlugin {
2765+ default_source : DefaultAssetSource :: FromBuilder ( Mutex :: new ( default_source) ) ,
2766+ ..Default :: default ( )
2767+ } ,
2768+ DiagnosticsPlugin ,
2769+ ) ) ;
2770+
2771+ app. init_asset :: < TestAsset > ( )
2772+ . register_asset_loader ( TrivialLoader ) ;
2773+
2774+ let asset_server = app. world ( ) . resource :: < AssetServer > ( ) . clone ( ) ;
2775+ let custom_handle: Handle < TestAsset > = asset_server. load ( "custom://asset.txt" ) ;
2776+
2777+ run_app_until ( & mut app, |_| {
2778+ match asset_server. get_load_state ( & custom_handle) . unwrap ( ) {
2779+ LoadState :: Loading => None ,
2780+ LoadState :: Failed ( err) => match err. as_ref ( ) {
2781+ AssetLoadError :: MissingAssetSourceError ( _) => Some ( ( ) ) ,
2782+ err => panic ! ( "Unexpected load error: {err}" ) ,
2783+ } ,
2784+ err => panic ! ( "Unexpected state for asset load: {err:?}" ) ,
2785+ }
2786+ } ) ;
2787+
2788+ // Drop the handle, and let the app update to react to that.
2789+ drop ( custom_handle) ;
2790+ app. update ( ) ;
2791+
2792+ let ( mut custom_source, custom_dir) = create_dir_source ( ) ;
2793+ custom_dir. insert_asset_text ( Path :: new ( "asset.txt" ) , "" ) ;
2794+
2795+ asset_server
2796+ . add_source ( "custom" , & mut custom_source)
2797+ . unwrap ( ) ;
2798+
2799+ // Now that we have added the "custom" asset source, loading the asset should work!
2800+ let custom_handle: Handle < TestAsset > = asset_server. load ( "custom://asset.txt" ) ;
2801+ run_app_until ( & mut app, |_| {
2802+ match asset_server. get_load_state ( & custom_handle) . unwrap ( ) {
2803+ LoadState :: Loading => None ,
2804+ LoadState :: Loaded => Some ( ( ) ) ,
2805+ err => panic ! ( "Unexpected state for asset load: {err:?}" ) ,
2806+ }
2807+ } ) ;
2808+
2809+ // Removing the source shouldn't change anything about the asset, so it should still be alive.
2810+ asset_server. remove_source ( "custom" ) . unwrap ( ) ;
2811+ app. update ( ) ;
2812+ assert ! ( app
2813+ . world( )
2814+ . resource:: <Assets <TestAsset >>( )
2815+ . contains( & custom_handle) ) ;
2816+
2817+ drop ( custom_handle) ;
2818+ app. update ( ) ;
2819+
2820+ // After removing the "custom" asset source, trying to load the asset again should fail.
2821+ let custom_handle: Handle < TestAsset > = asset_server. load ( "custom://asset.txt" ) ;
2822+ run_app_until ( & mut app, |_| {
2823+ match asset_server. get_load_state ( & custom_handle) . unwrap ( ) {
2824+ LoadState :: Loading => None ,
2825+ LoadState :: Failed ( err) => match err. as_ref ( ) {
2826+ AssetLoadError :: MissingAssetSourceError ( _) => Some ( ( ) ) ,
2827+ err => panic ! ( "Unexpected load error: {err}" ) ,
2828+ } ,
2829+ err => panic ! ( "Unexpected state for asset load: {err:?}" ) ,
2830+ }
2831+ } ) ;
2832+ }
27422833}
0 commit comments