@@ -32,7 +32,8 @@ use crate::{
3232 meta:: AssetMeta ,
3333 processor:: {
3434 AssetProcessor , LoadTransformAndSave , LogEntry , Process , ProcessContext , ProcessError ,
35- ProcessorState , ProcessorTransactionLog , ProcessorTransactionLogFactory , WriterContext ,
35+ ProcessStatus , ProcessorState , ProcessorTransactionLog , ProcessorTransactionLogFactory ,
36+ WriterContext ,
3637 } ,
3738 saver:: AssetSaver ,
3839 tests:: { run_app_until, CoolText , CoolTextLoader , CoolTextRon , SubText } ,
@@ -1924,3 +1925,268 @@ fn asset_processor_can_write_multiple_files() {
19241925)"#
19251926 ) ;
19261927}
1928+
1929+ #[ test]
1930+ fn error_on_no_writer ( ) {
1931+ let AppWithProcessor {
1932+ mut app,
1933+ source_gate,
1934+ default_source_dirs : ProcessingDirs {
1935+ source : source_dir, ..
1936+ } ,
1937+ ..
1938+ } = create_app_with_asset_processor ( & [ ] ) ;
1939+
1940+ struct NoWriterProcess ;
1941+
1942+ impl Process for NoWriterProcess {
1943+ type Settings = ( ) ;
1944+
1945+ async fn process (
1946+ & self ,
1947+ _: & mut ProcessContext < ' _ > ,
1948+ _: AssetMeta < ( ) , Self > ,
1949+ _: WriterContext < ' _ > ,
1950+ ) -> Result < ( ) , ProcessError > {
1951+ // Don't start a writer!
1952+ Ok ( ( ) )
1953+ }
1954+ }
1955+
1956+ app. register_asset_processor ( NoWriterProcess )
1957+ . set_default_asset_processor :: < NoWriterProcess > ( "txt" ) ;
1958+
1959+ let guard = source_gate. write_blocking ( ) ;
1960+ source_dir. insert_asset_text ( Path :: new ( "whatever.txt" ) , "" ) ;
1961+
1962+ run_app_until_finished_processing ( & mut app, guard) ;
1963+
1964+ let process_status = bevy_tasks:: block_on (
1965+ app. world ( )
1966+ . resource :: < AssetProcessor > ( )
1967+ . data ( )
1968+ . wait_until_processed ( "whatever.txt" . into ( ) ) ,
1969+ ) ;
1970+ // The process failed due to not having a writer.
1971+ assert_eq ! ( process_status, ProcessStatus :: Failed ) ;
1972+ }
1973+
1974+ #[ test]
1975+ fn error_on_unfinished_writer ( ) {
1976+ let AppWithProcessor {
1977+ mut app,
1978+ source_gate,
1979+ default_source_dirs : ProcessingDirs {
1980+ source : source_dir, ..
1981+ } ,
1982+ ..
1983+ } = create_app_with_asset_processor ( & [ ] ) ;
1984+
1985+ struct UnfinishedWriterProcess ;
1986+
1987+ impl Process for UnfinishedWriterProcess {
1988+ type Settings = ( ) ;
1989+
1990+ async fn process (
1991+ & self ,
1992+ _: & mut ProcessContext < ' _ > ,
1993+ _: AssetMeta < ( ) , Self > ,
1994+ writer_context : WriterContext < ' _ > ,
1995+ ) -> Result < ( ) , ProcessError > {
1996+ let _writer = writer_context. write_single ( ) . await ?;
1997+ // Don't call finish on the writer!
1998+ Ok ( ( ) )
1999+ }
2000+ }
2001+
2002+ app. register_asset_processor ( UnfinishedWriterProcess )
2003+ . set_default_asset_processor :: < UnfinishedWriterProcess > ( "txt" ) ;
2004+
2005+ let guard = source_gate. write_blocking ( ) ;
2006+ source_dir. insert_asset_text ( Path :: new ( "whatever.txt" ) , "" ) ;
2007+
2008+ run_app_until_finished_processing ( & mut app, guard) ;
2009+
2010+ let process_status = bevy_tasks:: block_on (
2011+ app. world ( )
2012+ . resource :: < AssetProcessor > ( )
2013+ . data ( )
2014+ . wait_until_processed ( "whatever.txt" . into ( ) ) ,
2015+ ) ;
2016+ // The process failed due to having a writer that we didn't await finish on.
2017+ assert_eq ! ( process_status, ProcessStatus :: Failed ) ;
2018+ }
2019+
2020+ #[ test]
2021+ fn error_on_single_writer_after_multiple_writer ( ) {
2022+ let AppWithProcessor {
2023+ mut app,
2024+ source_gate,
2025+ default_source_dirs : ProcessingDirs {
2026+ source : source_dir, ..
2027+ } ,
2028+ ..
2029+ } = create_app_with_asset_processor ( & [ ] ) ;
2030+
2031+ struct SingleAfterMultipleWriterProcess ;
2032+
2033+ impl Process for SingleAfterMultipleWriterProcess {
2034+ type Settings = ( ) ;
2035+
2036+ async fn process (
2037+ & self ,
2038+ _: & mut ProcessContext < ' _ > ,
2039+ _: AssetMeta < ( ) , Self > ,
2040+ writer_context : WriterContext < ' _ > ,
2041+ ) -> Result < ( ) , ProcessError > {
2042+ // Properly write a "multiple".
2043+ let writer = writer_context
2044+ . write_multiple ( Path :: new ( "multi.txt" ) )
2045+ . await ?;
2046+ writer. finish :: < CoolTextLoader > ( ( ) ) . await ?;
2047+
2048+ // Now trying writing "single", which conflicts!
2049+ let writer = writer_context. write_single ( ) . await ?;
2050+ writer. finish :: < CoolTextLoader > ( ( ) ) . await ?;
2051+
2052+ Ok ( ( ) )
2053+ }
2054+ }
2055+
2056+ app. register_asset_processor ( SingleAfterMultipleWriterProcess )
2057+ . set_default_asset_processor :: < SingleAfterMultipleWriterProcess > ( "txt" ) ;
2058+
2059+ let guard = source_gate. write_blocking ( ) ;
2060+ source_dir. insert_asset_text ( Path :: new ( "whatever.txt" ) , "" ) ;
2061+
2062+ run_app_until_finished_processing ( & mut app, guard) ;
2063+
2064+ let process_status = bevy_tasks:: block_on (
2065+ app. world ( )
2066+ . resource :: < AssetProcessor > ( )
2067+ . data ( )
2068+ . wait_until_processed ( "whatever.txt" . into ( ) ) ,
2069+ ) ;
2070+ // The process failed due to having a single writer after a multiple writer.
2071+ assert_eq ! ( process_status, ProcessStatus :: Failed ) ;
2072+ }
2073+
2074+ #[ test]
2075+ fn processor_can_parallelize_multiple_writes ( ) {
2076+ let AppWithProcessor {
2077+ mut app,
2078+ source_gate,
2079+ default_source_dirs :
2080+ ProcessingDirs {
2081+ source : source_dir,
2082+ processed : processed_dir,
2083+ ..
2084+ } ,
2085+ ..
2086+ } = create_app_with_asset_processor ( & [ ] ) ;
2087+
2088+ struct ParallelizedWriterProcess ;
2089+
2090+ impl Process for ParallelizedWriterProcess {
2091+ type Settings = ( ) ;
2092+
2093+ async fn process (
2094+ & self ,
2095+ _: & mut ProcessContext < ' _ > ,
2096+ _: AssetMeta < ( ) , Self > ,
2097+ writer_context : WriterContext < ' _ > ,
2098+ ) -> Result < ( ) , ProcessError > {
2099+ let mut writer_1 = writer_context. write_multiple ( Path :: new ( "a.txt" ) ) . await ?;
2100+ let mut writer_2 = writer_context. write_multiple ( Path :: new ( "b.txt" ) ) . await ?;
2101+
2102+ // Note: this call is blocking, so it's undesirable in production code using
2103+ // single-threaded mode (e.g., platforms like Wasm). For this test though, it's not a
2104+ // big deal.
2105+ bevy_tasks:: IoTaskPool :: get ( ) . scope ( |scope| {
2106+ scope. spawn ( async {
2107+ writer_1. write_all ( b"abc123" ) . await . unwrap ( ) ;
2108+ writer_1. finish :: < CoolTextLoader > ( ( ) ) . await . unwrap ( ) ;
2109+ } ) ;
2110+ scope. spawn ( async {
2111+ writer_2. write_all ( b"def456" ) . await . unwrap ( ) ;
2112+ writer_2. finish :: < CoolTextLoader > ( ( ) ) . await . unwrap ( ) ;
2113+ } ) ;
2114+ } ) ;
2115+
2116+ Ok ( ( ) )
2117+ }
2118+ }
2119+
2120+ app. register_asset_processor ( ParallelizedWriterProcess )
2121+ . set_default_asset_processor :: < ParallelizedWriterProcess > ( "txt" ) ;
2122+
2123+ let guard = source_gate. write_blocking ( ) ;
2124+ source_dir. insert_asset_text ( Path :: new ( "whatever.txt" ) , "" ) ;
2125+
2126+ run_app_until_finished_processing ( & mut app, guard) ;
2127+
2128+ assert_eq ! (
2129+ & read_asset_as_string( & processed_dir, Path :: new( "whatever.txt/a.txt" ) ) ,
2130+ "abc123"
2131+ ) ;
2132+ assert_eq ! (
2133+ & read_asset_as_string( & processed_dir, Path :: new( "whatever.txt/b.txt" ) ) ,
2134+ "def456"
2135+ ) ;
2136+ }
2137+
2138+ #[ test]
2139+ fn error_on_two_multiple_writes_for_same_path ( ) {
2140+ let AppWithProcessor {
2141+ mut app,
2142+ source_gate,
2143+ default_source_dirs : ProcessingDirs {
2144+ source : source_dir, ..
2145+ } ,
2146+ ..
2147+ } = create_app_with_asset_processor ( & [ ] ) ;
2148+
2149+ struct TwoMultipleWritesForSamePathProcess ;
2150+
2151+ impl Process for TwoMultipleWritesForSamePathProcess {
2152+ type Settings = ( ) ;
2153+
2154+ async fn process (
2155+ & self ,
2156+ _: & mut ProcessContext < ' _ > ,
2157+ _: AssetMeta < ( ) , Self > ,
2158+ writer_context : WriterContext < ' _ > ,
2159+ ) -> Result < ( ) , ProcessError > {
2160+ // Properly write a "multiple".
2161+ let writer = writer_context
2162+ . write_multiple ( Path :: new ( "multi.txt" ) )
2163+ . await ?;
2164+ writer. finish :: < CoolTextLoader > ( ( ) ) . await ?;
2165+
2166+ // Properly write to the same "multiple".
2167+ let writer = writer_context
2168+ . write_multiple ( Path :: new ( "multi.txt" ) )
2169+ . await ?;
2170+ writer. finish :: < CoolTextLoader > ( ( ) ) . await ?;
2171+
2172+ Ok ( ( ) )
2173+ }
2174+ }
2175+
2176+ app. register_asset_processor ( TwoMultipleWritesForSamePathProcess )
2177+ . set_default_asset_processor :: < TwoMultipleWritesForSamePathProcess > ( "txt" ) ;
2178+
2179+ let guard = source_gate. write_blocking ( ) ;
2180+ source_dir. insert_asset_text ( Path :: new ( "whatever.txt" ) , "" ) ;
2181+
2182+ run_app_until_finished_processing ( & mut app, guard) ;
2183+
2184+ let process_status = bevy_tasks:: block_on (
2185+ app. world ( )
2186+ . resource :: < AssetProcessor > ( )
2187+ . data ( )
2188+ . wait_until_processed ( "whatever.txt" . into ( ) ) ,
2189+ ) ;
2190+ // The process failed due to writing "multiple" to the same path twice.
2191+ assert_eq ! ( process_status, ProcessStatus :: Failed ) ;
2192+ }
0 commit comments