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