@@ -93,6 +93,20 @@ impl<R: AssetReader> AssetReader for LockGatedReader<R> {
9393 }
9494}
9595
96+ /// Serializes `text` into a `CoolText` that can be loaded.
97+ ///
98+ /// This doesn't support all the features of `CoolText`, so more complex scenarios may require doing
99+ /// this manually.
100+ fn serialize_as_cool_text ( text : & str ) -> String {
101+ let cool_text_ron = CoolTextRon {
102+ text : text. into ( ) ,
103+ dependencies : vec ! [ ] ,
104+ embedded_dependencies : vec ! [ ] ,
105+ sub_texts : vec ! [ ] ,
106+ } ;
107+ ron:: ser:: to_string_pretty ( & cool_text_ron, PrettyConfig :: new ( ) . new_line ( "\n " ) ) . unwrap ( )
108+ }
109+
96110fn create_app_with_asset_processor ( extra_sources : & [ String ] ) -> AppWithProcessor {
97111 let mut app = App :: new ( ) ;
98112 let source_gate = Arc :: new ( RwLock :: new ( ( ) ) ) ;
@@ -945,15 +959,6 @@ fn asset_processor_processes_all_sources() {
945959 // All the assets will have the same path, but they will still be separately processed since
946960 // they are in different sources.
947961 let path = Path :: new ( "asset.cool.ron" ) ;
948- let serialize_as_cool_text = |text : & str | {
949- let cool_text_ron = CoolTextRon {
950- text : text. into ( ) ,
951- dependencies : vec ! [ ] ,
952- embedded_dependencies : vec ! [ ] ,
953- sub_texts : vec ! [ ] ,
954- } ;
955- ron:: ser:: to_string_pretty ( & cool_text_ron, PrettyConfig :: new ( ) . new_line ( "\n " ) ) . unwrap ( )
956- } ;
957962 default_source_dir. insert_asset_text ( path, & serialize_as_cool_text ( "default asset" ) ) ;
958963 custom_1_source_dir. insert_asset_text ( path, & serialize_as_cool_text ( "custom 1 asset" ) ) ;
959964 custom_2_source_dir. insert_asset_text ( path, & serialize_as_cool_text ( "custom 2 asset" ) ) ;
@@ -1244,3 +1249,260 @@ fn nested_loads_of_processed_asset_reprocesses_on_reload() {
12441249
12451250 assert_eq ! ( get_process_count( ) , 7 ) ;
12461251}
1252+
1253+ #[ test]
1254+ fn clears_invalid_data_from_processed_dir ( ) {
1255+ let AppWithProcessor {
1256+ mut app,
1257+ source_gate,
1258+ default_source_dirs :
1259+ ProcessingDirs {
1260+ source : default_source_dir,
1261+ processed : default_processed_dir,
1262+ ..
1263+ } ,
1264+ ..
1265+ } = create_app_with_asset_processor ( & [ ] ) ;
1266+
1267+ type CoolTextProcessor = LoadTransformAndSave <
1268+ CoolTextLoader ,
1269+ RootAssetTransformer < AddText , CoolText > ,
1270+ CoolTextSaver ,
1271+ > ;
1272+ app. init_asset :: < CoolText > ( )
1273+ . init_asset :: < SubText > ( )
1274+ . register_asset_loader ( CoolTextLoader )
1275+ . register_asset_processor ( CoolTextProcessor :: new (
1276+ RootAssetTransformer :: new ( AddText ( " processed" . to_string ( ) ) ) ,
1277+ CoolTextSaver ,
1278+ ) )
1279+ . set_default_asset_processor :: < CoolTextProcessor > ( "cool.ron" ) ;
1280+
1281+ let guard = source_gate. write_blocking ( ) ;
1282+
1283+ default_source_dir. insert_asset_text ( Path :: new ( "a.cool.ron" ) , & serialize_as_cool_text ( "a" ) ) ;
1284+ default_source_dir. insert_asset_text ( Path :: new ( "dir/b.cool.ron" ) , & serialize_as_cool_text ( "b" ) ) ;
1285+ default_source_dir. insert_asset_text (
1286+ Path :: new ( "dir/subdir/c.cool.ron" ) ,
1287+ & serialize_as_cool_text ( "c" ) ,
1288+ ) ;
1289+
1290+ // This asset has the right data, but no meta, so it should be reprocessed.
1291+ let a = Path :: new ( "a.cool.ron" ) ;
1292+ default_processed_dir. insert_asset_text ( a, & serialize_as_cool_text ( "a processed" ) ) ;
1293+ // These assets aren't present in the unprocessed directory, so they should be deleted.
1294+ let missing1 = Path :: new ( "missing1.cool.ron" ) ;
1295+ let missing2 = Path :: new ( "dir/missing2.cool.ron" ) ;
1296+ let missing3 = Path :: new ( "other_dir/missing3.cool.ron" ) ;
1297+ default_processed_dir. insert_asset_text ( missing1, & serialize_as_cool_text ( "missing1" ) ) ;
1298+ default_processed_dir. insert_meta_text ( missing1, "" ) ; // This asset has metadata.
1299+ default_processed_dir. insert_asset_text ( missing2, & serialize_as_cool_text ( "missing2" ) ) ;
1300+ default_processed_dir. insert_asset_text ( missing3, & serialize_as_cool_text ( "missing3" ) ) ;
1301+ // This directory is empty, so it should be deleted.
1302+ let empty_dir = Path :: new ( "empty_dir" ) ;
1303+ let empty_dir_subdir = Path :: new ( "empty_dir/empty_subdir" ) ;
1304+ default_processed_dir. get_or_insert_dir ( empty_dir_subdir) ;
1305+
1306+ run_app_until_finished_processing ( & mut app, guard) ;
1307+
1308+ assert_eq ! (
1309+ read_asset_as_string( & default_processed_dir, a) ,
1310+ serialize_as_cool_text( "a processed" )
1311+ ) ;
1312+ assert ! ( default_processed_dir. get_metadata( a) . is_some( ) ) ;
1313+
1314+ assert ! ( default_processed_dir. get_asset( missing1) . is_none( ) ) ;
1315+ assert ! ( default_processed_dir. get_metadata( missing1) . is_none( ) ) ;
1316+ assert ! ( default_processed_dir. get_asset( missing2) . is_none( ) ) ;
1317+ assert ! ( default_processed_dir. get_asset( missing3) . is_none( ) ) ;
1318+
1319+ assert ! ( default_processed_dir. get_dir( empty_dir_subdir) . is_none( ) ) ;
1320+ assert ! ( default_processed_dir. get_dir( empty_dir) . is_none( ) ) ;
1321+ }
1322+
1323+ #[ test]
1324+ fn only_reprocesses_wrong_hash_on_startup ( ) {
1325+ let no_deps_asset = Path :: new ( "no_deps.cool.ron" ) ;
1326+ let source_changed_asset = Path :: new ( "source_changed.cool.ron" ) ;
1327+ let dep_unchanged_asset = Path :: new ( "dep_unchanged.cool.ron" ) ;
1328+ let dep_changed_asset = Path :: new ( "dep_changed.cool.ron" ) ;
1329+ let default_source_dir;
1330+ let default_processed_dir;
1331+
1332+ #[ derive( TypePath , Clone ) ]
1333+ struct MergeEmbeddedAndAddText ;
1334+
1335+ impl MutateAsset < CoolText > for MergeEmbeddedAndAddText {
1336+ fn mutate ( & self , asset : & mut CoolText ) {
1337+ asset. text . push_str ( " processed" ) ;
1338+ if asset. embedded . is_empty ( ) {
1339+ return ;
1340+ }
1341+ asset. text . push ( ' ' ) ;
1342+ asset. text . push_str ( & asset. embedded ) ;
1343+ }
1344+ }
1345+
1346+ #[ derive( TypePath , Clone ) ]
1347+ struct Count < T > ( Arc < Mutex < u32 > > , T ) ;
1348+
1349+ impl < A : Asset , T : MutateAsset < A > > MutateAsset < A > for Count < T > {
1350+ fn mutate ( & self , asset : & mut A ) {
1351+ * self . 0 . lock ( ) . unwrap_or_else ( PoisonError :: into_inner) += 1 ;
1352+ self . 1 . mutate ( asset) ;
1353+ }
1354+ }
1355+
1356+ let transformer = Count ( Arc :: new ( Mutex :: new ( 0 ) ) , MergeEmbeddedAndAddText ) ;
1357+ type CoolTextProcessor = LoadTransformAndSave <
1358+ CoolTextLoader ,
1359+ RootAssetTransformer < Count < MergeEmbeddedAndAddText > , CoolText > ,
1360+ CoolTextSaver ,
1361+ > ;
1362+
1363+ // Create a scope so that the app is completely gone afterwards (and we can see what happens
1364+ // after reinitializing).
1365+ {
1366+ let AppWithProcessor {
1367+ mut app,
1368+ source_gate,
1369+ default_source_dirs,
1370+ ..
1371+ } = create_app_with_asset_processor ( & [ ] ) ;
1372+ default_source_dir = default_source_dirs. source ;
1373+ default_processed_dir = default_source_dirs. processed ;
1374+
1375+ app. init_asset :: < CoolText > ( )
1376+ . init_asset :: < SubText > ( )
1377+ . register_asset_loader ( CoolTextLoader )
1378+ . register_asset_processor ( CoolTextProcessor :: new (
1379+ RootAssetTransformer :: new ( transformer. clone ( ) ) ,
1380+ CoolTextSaver ,
1381+ ) )
1382+ . set_default_asset_processor :: < CoolTextProcessor > ( "cool.ron" ) ;
1383+
1384+ let guard = source_gate. write_blocking ( ) ;
1385+
1386+ let cool_text_with_embedded = |text : & str , embedded : & Path | {
1387+ let cool_text_ron = CoolTextRon {
1388+ text : text. into ( ) ,
1389+ dependencies : vec ! [ ] ,
1390+ embedded_dependencies : vec ! [ embedded. to_string_lossy( ) . into_owned( ) ] ,
1391+ sub_texts : vec ! [ ] ,
1392+ } ;
1393+ ron:: ser:: to_string_pretty ( & cool_text_ron, PrettyConfig :: new ( ) . new_line ( "\n " ) ) . unwrap ( )
1394+ } ;
1395+
1396+ default_source_dir. insert_asset_text ( no_deps_asset, & serialize_as_cool_text ( "no_deps" ) ) ;
1397+ default_source_dir. insert_asset_text (
1398+ source_changed_asset,
1399+ & serialize_as_cool_text ( "source_changed" ) ,
1400+ ) ;
1401+ default_source_dir. insert_asset_text (
1402+ dep_unchanged_asset,
1403+ & cool_text_with_embedded ( "dep_unchanged" , no_deps_asset) ,
1404+ ) ;
1405+ default_source_dir. insert_asset_text (
1406+ dep_changed_asset,
1407+ & cool_text_with_embedded ( "dep_changed" , source_changed_asset) ,
1408+ ) ;
1409+
1410+ run_app_until_finished_processing ( & mut app, guard) ;
1411+
1412+ assert_eq ! (
1413+ read_asset_as_string( & default_processed_dir, no_deps_asset) ,
1414+ serialize_as_cool_text( "no_deps processed" )
1415+ ) ;
1416+ assert_eq ! (
1417+ read_asset_as_string( & default_processed_dir, source_changed_asset) ,
1418+ serialize_as_cool_text( "source_changed processed" )
1419+ ) ;
1420+ assert_eq ! (
1421+ read_asset_as_string( & default_processed_dir, dep_unchanged_asset) ,
1422+ serialize_as_cool_text( "dep_unchanged processed no_deps processed" )
1423+ ) ;
1424+ assert_eq ! (
1425+ read_asset_as_string( & default_processed_dir, dep_changed_asset) ,
1426+ serialize_as_cool_text( "dep_changed processed source_changed processed" )
1427+ ) ;
1428+ }
1429+
1430+ // Assert and reset the processing count.
1431+ assert_eq ! (
1432+ core:: mem:: take( & mut * transformer. 0 . lock( ) . unwrap_or_else( PoisonError :: into_inner) ) ,
1433+ 4
1434+ ) ;
1435+
1436+ // Hand-make the app, since we need to pass in our already existing Dirs from the last app.
1437+ let mut app = App :: new ( ) ;
1438+ let source_gate = Arc :: new ( RwLock :: new ( ( ) ) ) ;
1439+
1440+ let source_memory_reader = LockGatedReader :: new (
1441+ source_gate. clone ( ) ,
1442+ MemoryAssetReader {
1443+ root : default_source_dir. clone ( ) ,
1444+ } ,
1445+ ) ;
1446+ let processed_memory_reader = MemoryAssetReader {
1447+ root : default_processed_dir. clone ( ) ,
1448+ } ;
1449+ let processed_memory_writer = MemoryAssetWriter {
1450+ root : default_processed_dir. clone ( ) ,
1451+ } ;
1452+
1453+ app. register_asset_source (
1454+ AssetSourceId :: Default ,
1455+ AssetSourceBuilder :: new ( move || Box :: new ( source_memory_reader. clone ( ) ) )
1456+ . with_processed_reader ( move || Box :: new ( processed_memory_reader. clone ( ) ) )
1457+ . with_processed_writer ( move |_| Some ( Box :: new ( processed_memory_writer. clone ( ) ) ) ) ,
1458+ ) ;
1459+
1460+ app. add_plugins ( (
1461+ TaskPoolPlugin :: default ( ) ,
1462+ AssetPlugin {
1463+ mode : AssetMode :: Processed ,
1464+ use_asset_processor_override : Some ( true ) ,
1465+ ..Default :: default ( )
1466+ } ,
1467+ ) ) ;
1468+
1469+ app. init_asset :: < CoolText > ( )
1470+ . init_asset :: < SubText > ( )
1471+ . register_asset_loader ( CoolTextLoader )
1472+ . register_asset_processor ( CoolTextProcessor :: new (
1473+ RootAssetTransformer :: new ( transformer. clone ( ) ) ,
1474+ CoolTextSaver ,
1475+ ) )
1476+ . set_default_asset_processor :: < CoolTextProcessor > ( "cool.ron" ) ;
1477+
1478+ let guard = source_gate. write_blocking ( ) ;
1479+
1480+ default_source_dir
1481+ . insert_asset_text ( source_changed_asset, & serialize_as_cool_text ( "DIFFERENT" ) ) ;
1482+
1483+ run_app_until_finished_processing ( & mut app, guard) ;
1484+
1485+ // Only source_changed and dep_changed assets were reprocessed - all others still have the same
1486+ // hashes.
1487+ assert_eq ! (
1488+ * transformer. 0 . lock( ) . unwrap_or_else( PoisonError :: into_inner) ,
1489+ 2
1490+ ) ;
1491+
1492+ assert_eq ! (
1493+ read_asset_as_string( & default_processed_dir, no_deps_asset) ,
1494+ serialize_as_cool_text( "no_deps processed" )
1495+ ) ;
1496+ assert_eq ! (
1497+ read_asset_as_string( & default_processed_dir, source_changed_asset) ,
1498+ serialize_as_cool_text( "DIFFERENT processed" )
1499+ ) ;
1500+ assert_eq ! (
1501+ read_asset_as_string( & default_processed_dir, dep_unchanged_asset) ,
1502+ serialize_as_cool_text( "dep_unchanged processed no_deps processed" )
1503+ ) ;
1504+ assert_eq ! (
1505+ read_asset_as_string( & default_processed_dir, dep_changed_asset) ,
1506+ serialize_as_cool_text( "dep_changed processed DIFFERENT processed" )
1507+ ) ;
1508+ }
0 commit comments