@@ -706,7 +706,8 @@ mod tests {
706706 io:: {
707707 gated:: { GateOpener , GatedReader } ,
708708 memory:: { Dir , MemoryAssetReader } ,
709- AssetReader , AssetReaderError , AssetSource , AssetSourceId , Reader ,
709+ AssetReader , AssetReaderError , AssetSource , AssetSourceEvent , AssetSourceId ,
710+ AssetWatcher , Reader ,
710711 } ,
711712 loader:: { AssetLoader , LoadContext } ,
712713 Asset , AssetApp , AssetEvent , AssetId , AssetLoadError , AssetLoadFailedEvent , AssetPath ,
@@ -730,8 +731,9 @@ mod tests {
730731 use bevy_platform:: collections:: { HashMap , HashSet } ;
731732 use bevy_reflect:: TypePath ;
732733 use core:: time:: Duration ;
734+ use crossbeam_channel:: Sender ;
733735 use serde:: { Deserialize , Serialize } ;
734- use std:: path:: Path ;
736+ use std:: path:: { Path , PathBuf } ;
735737 use thiserror:: Error ;
736738
737739 #[ derive( Asset , TypePath , Debug , Default ) ]
@@ -2064,4 +2066,167 @@ mod tests {
20642066 Err ( InvalidGenerationError :: Removed { index } )
20652067 ) ;
20662068 }
2069+
2070+ // Creates a basic app with the default asset source engineered to get back the asset event
2071+ // sender.
2072+ fn create_app_with_source_event_sender ( ) -> ( App , Dir , Sender < AssetSourceEvent > ) {
2073+ let mut app = App :: new ( ) ;
2074+ let dir = Dir :: default ( ) ;
2075+ let memory_reader = MemoryAssetReader { root : dir. clone ( ) } ;
2076+
2077+ // Create a channel to pass the source event sender back to us.
2078+ let ( sender_sender, sender_receiver) = crossbeam_channel:: bounded ( 1 ) ;
2079+
2080+ struct FakeWatcher ;
2081+ impl AssetWatcher for FakeWatcher { }
2082+
2083+ app. register_asset_source (
2084+ AssetSourceId :: Default ,
2085+ AssetSource :: build ( )
2086+ . with_reader ( move || Box :: new ( memory_reader. clone ( ) ) )
2087+ . with_watcher ( move |sender| {
2088+ sender_sender. send ( sender) . unwrap ( ) ;
2089+ Some ( Box :: new ( FakeWatcher ) )
2090+ } ) ,
2091+ )
2092+ . add_plugins ( (
2093+ TaskPoolPlugin :: default ( ) ,
2094+ AssetPlugin {
2095+ watch_for_changes_override : Some ( true ) ,
2096+ ..Default :: default ( )
2097+ } ,
2098+ ) ) ;
2099+
2100+ let sender = sender_receiver. try_recv ( ) . unwrap ( ) ;
2101+
2102+ ( app, dir, sender)
2103+ }
2104+
2105+ fn collect_asset_events < A : Asset > ( world : & mut World ) -> Vec < AssetEvent < A > > {
2106+ world
2107+ . resource_mut :: < Messages < AssetEvent < A > > > ( )
2108+ . drain ( )
2109+ . collect ( )
2110+ }
2111+
2112+ fn collect_asset_load_failed_events < A : Asset > (
2113+ world : & mut World ,
2114+ ) -> Vec < AssetLoadFailedEvent < A > > {
2115+ world
2116+ . resource_mut :: < Messages < AssetLoadFailedEvent < A > > > ( )
2117+ . drain ( )
2118+ . collect ( )
2119+ }
2120+
2121+ #[ test]
2122+ fn reloads_asset_after_source_event ( ) {
2123+ let ( mut app, dir, source_events) = create_app_with_source_event_sender ( ) ;
2124+ let asset_server = app. world ( ) . resource :: < AssetServer > ( ) . clone ( ) ;
2125+
2126+ dir. insert_asset_text (
2127+ Path :: new ( "abc.cool.ron" ) ,
2128+ r#"(
2129+ text: "a",
2130+ dependencies: [],
2131+ embedded_dependencies: [],
2132+ sub_texts: [],
2133+ )"# ,
2134+ ) ;
2135+
2136+ app. init_asset :: < CoolText > ( )
2137+ . init_asset :: < SubText > ( )
2138+ . register_asset_loader ( CoolTextLoader ) ;
2139+
2140+ let handle: Handle < CoolText > = asset_server. load ( "abc.cool.ron" ) ;
2141+ run_app_until ( & mut app, |world| {
2142+ let messages = collect_asset_events ( world) ;
2143+ if messages. is_empty ( ) {
2144+ return None ;
2145+ }
2146+ assert_eq ! (
2147+ messages,
2148+ [
2149+ AssetEvent :: LoadedWithDependencies { id: handle. id( ) } ,
2150+ AssetEvent :: Added { id: handle. id( ) } ,
2151+ ]
2152+ ) ;
2153+ Some ( ( ) )
2154+ } ) ;
2155+
2156+ // Sending an asset event should result in the asset being reloaded - resulting in a
2157+ // "Modified" message.
2158+ source_events
2159+ . send ( AssetSourceEvent :: ModifiedAsset ( PathBuf :: from (
2160+ "abc.cool.ron" ,
2161+ ) ) )
2162+ . unwrap ( ) ;
2163+
2164+ run_app_until ( & mut app, |world| {
2165+ let messages = collect_asset_events ( world) ;
2166+ if messages. is_empty ( ) {
2167+ return None ;
2168+ }
2169+ assert_eq ! (
2170+ messages,
2171+ [
2172+ AssetEvent :: LoadedWithDependencies { id: handle. id( ) } ,
2173+ AssetEvent :: Modified { id: handle. id( ) }
2174+ ]
2175+ ) ;
2176+ Some ( ( ) )
2177+ } ) ;
2178+ }
2179+
2180+ #[ test]
2181+ fn added_asset_reloads_previously_missing_asset ( ) {
2182+ let ( mut app, dir, source_events) = create_app_with_source_event_sender ( ) ;
2183+ let asset_server = app. world ( ) . resource :: < AssetServer > ( ) . clone ( ) ;
2184+
2185+ app. init_asset :: < CoolText > ( )
2186+ . init_asset :: < SubText > ( )
2187+ . register_asset_loader ( CoolTextLoader ) ;
2188+
2189+ let handle: Handle < CoolText > = asset_server. load ( "abc.cool.ron" ) ;
2190+ run_app_until ( & mut app, |world| {
2191+ let failed_ids = collect_asset_load_failed_events ( world)
2192+ . drain ( ..)
2193+ . map ( |event| event. id )
2194+ . collect :: < Vec < _ > > ( ) ;
2195+ if failed_ids. is_empty ( ) {
2196+ return None ;
2197+ }
2198+ assert_eq ! ( failed_ids, [ handle. id( ) ] ) ;
2199+ Some ( ( ) )
2200+ } ) ;
2201+
2202+ // The asset has already been considered as failed to load. Now we add the asset data, and
2203+ // send an AddedAsset event.
2204+ dir. insert_asset_text (
2205+ Path :: new ( "abc.cool.ron" ) ,
2206+ r#"(
2207+ text: "a",
2208+ dependencies: [],
2209+ embedded_dependencies: [],
2210+ sub_texts: [],
2211+ )"# ,
2212+ ) ;
2213+ source_events
2214+ . send ( AssetSourceEvent :: AddedAsset ( PathBuf :: from ( "abc.cool.ron" ) ) )
2215+ . unwrap ( ) ;
2216+
2217+ run_app_until ( & mut app, |world| {
2218+ let messages = collect_asset_events ( world) ;
2219+ if messages. is_empty ( ) {
2220+ return None ;
2221+ }
2222+ assert_eq ! (
2223+ messages,
2224+ [
2225+ AssetEvent :: LoadedWithDependencies { id: handle. id( ) } ,
2226+ AssetEvent :: Added { id: handle. id( ) }
2227+ ]
2228+ ) ;
2229+ Some ( ( ) )
2230+ } ) ;
2231+ }
20672232}
0 commit comments