Skip to content

Commit ecdb663

Browse files
committed
Add a test to check that nested loads of processed assets will propagate changes unconditionally.
1 parent 214ecb2 commit ecdb663

File tree

1 file changed

+213
-1
lines changed

1 file changed

+213
-1
lines changed

crates/bevy_asset/src/processor/tests.rs

Lines changed: 213 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@ use alloc::{
22
boxed::Box,
33
collections::BTreeMap,
44
string::{String, ToString},
5+
sync::Arc,
56
vec,
67
vec::Vec,
78
};
8-
use bevy_platform::collections::HashMap;
9+
use bevy_platform::{
10+
collections::HashMap,
11+
sync::{Mutex, PoisonError},
12+
};
913
use bevy_reflect::TypePath;
1014
use core::marker::PhantomData;
1115
use futures_lite::AsyncWriteExt;
@@ -1033,3 +1037,211 @@ fn asset_processor_processes_all_sources() {
10331037
)"#
10341038
);
10351039
}
1040+
1041+
#[test]
1042+
fn nested_loads_of_processed_asset_reprocesses_on_reload() {
1043+
let AppWithProcessor {
1044+
mut app,
1045+
default_source_dirs:
1046+
ProcessingDirs {
1047+
source: default_source_dir,
1048+
processed: default_processed_dir,
1049+
source_event_sender: default_source_events,
1050+
},
1051+
extra_sources_dirs,
1052+
} = create_app_with_asset_processor(&["custom".into()]);
1053+
let ProcessingDirs {
1054+
source: custom_source_dir,
1055+
processed: custom_processed_dir,
1056+
source_event_sender: custom_source_events,
1057+
} = extra_sources_dirs["custom"].clone();
1058+
1059+
#[derive(Serialize, Deserialize)]
1060+
enum NesterSerialized {
1061+
Leaf(String),
1062+
Path(String),
1063+
}
1064+
1065+
#[derive(Asset, TypePath)]
1066+
struct Nester {
1067+
value: String,
1068+
}
1069+
1070+
struct NesterLoader;
1071+
1072+
impl AssetLoader for NesterLoader {
1073+
type Asset = Nester;
1074+
type Settings = ();
1075+
type Error = std::io::Error;
1076+
1077+
async fn load(
1078+
&self,
1079+
reader: &mut dyn Reader,
1080+
_settings: &Self::Settings,
1081+
load_context: &mut LoadContext<'_>,
1082+
) -> Result<Self::Asset, Self::Error> {
1083+
let mut bytes = vec![];
1084+
reader.read_to_end(&mut bytes).await?;
1085+
1086+
let serialized: NesterSerialized = ron::de::from_bytes(&bytes).unwrap();
1087+
Ok(match serialized {
1088+
NesterSerialized::Leaf(value) => Nester { value },
1089+
NesterSerialized::Path(path) => {
1090+
let loaded_asset = load_context.loader().immediate().load(path).await.unwrap();
1091+
loaded_asset.take()
1092+
}
1093+
})
1094+
}
1095+
1096+
fn extensions(&self) -> &[&str] {
1097+
&["nest"]
1098+
}
1099+
}
1100+
1101+
struct AddTextToNested(String, Arc<Mutex<u32>>);
1102+
1103+
impl MutateAsset<Nester> for AddTextToNested {
1104+
fn mutate(&self, asset: &mut Nester) {
1105+
asset.value.push_str(&self.0);
1106+
1107+
*self.1.lock().unwrap_or_else(PoisonError::into_inner) += 1;
1108+
}
1109+
}
1110+
1111+
fn serialize_as_leaf(value: String) -> String {
1112+
let serialized = NesterSerialized::Leaf(value);
1113+
ron::ser::to_string(&serialized).unwrap()
1114+
}
1115+
1116+
struct NesterSaver;
1117+
1118+
impl AssetSaver for NesterSaver {
1119+
type Asset = Nester;
1120+
type Error = std::io::Error;
1121+
type Settings = ();
1122+
type OutputLoader = NesterLoader;
1123+
1124+
async fn save(
1125+
&self,
1126+
writer: &mut crate::io::Writer,
1127+
asset: crate::saver::SavedAsset<'_, Self::Asset>,
1128+
_settings: &Self::Settings,
1129+
) -> Result<<Self::OutputLoader as AssetLoader>::Settings, Self::Error> {
1130+
let serialized = serialize_as_leaf(asset.get().value.clone());
1131+
writer.write_all(serialized.as_bytes()).await
1132+
}
1133+
}
1134+
1135+
let process_counter = Arc::new(Mutex::new(0));
1136+
1137+
type NesterProcessor = LoadTransformAndSave<
1138+
NesterLoader,
1139+
RootAssetTransformer<AddTextToNested, Nester>,
1140+
NesterSaver,
1141+
>;
1142+
app.init_asset::<Nester>()
1143+
.register_asset_loader(NesterLoader)
1144+
.register_asset_processor(NesterProcessor::new(
1145+
RootAssetTransformer::new(AddTextToNested("-he".into(), process_counter.clone())),
1146+
NesterSaver,
1147+
))
1148+
.set_default_asset_processor::<NesterProcessor>("nest");
1149+
1150+
// This test also checks that processing of nested assets can occur across asset sources.
1151+
custom_source_dir.insert_asset_text(
1152+
Path::new("top.nest"),
1153+
&ron::ser::to_string(&NesterSerialized::Path("middle.nest".into())).unwrap(),
1154+
);
1155+
default_source_dir.insert_asset_text(
1156+
Path::new("middle.nest"),
1157+
&ron::ser::to_string(&NesterSerialized::Path("custom://bottom.nest".into())).unwrap(),
1158+
);
1159+
custom_source_dir.insert_asset_text(Path::new("bottom.nest"), &serialize_as_leaf("he".into()));
1160+
default_source_dir.insert_asset_text(
1161+
Path::new("unrelated.nest"),
1162+
&serialize_as_leaf("tee".into()),
1163+
);
1164+
1165+
run_app_until_finished_processing(&mut app);
1166+
1167+
// The initial processing step should have processed all assets.
1168+
assert_eq!(
1169+
read_asset_as_string(&custom_processed_dir, Path::new("bottom.nest")),
1170+
serialize_as_leaf("he-he".into())
1171+
);
1172+
assert_eq!(
1173+
read_asset_as_string(&default_processed_dir, Path::new("middle.nest")),
1174+
serialize_as_leaf("he-he-he".into())
1175+
);
1176+
assert_eq!(
1177+
read_asset_as_string(&custom_processed_dir, Path::new("top.nest")),
1178+
serialize_as_leaf("he-he-he-he".into())
1179+
);
1180+
assert_eq!(
1181+
read_asset_as_string(&default_processed_dir, Path::new("unrelated.nest")),
1182+
serialize_as_leaf("tee-he".into())
1183+
);
1184+
1185+
let get_process_count = || {
1186+
*process_counter
1187+
.lock()
1188+
.unwrap_or_else(PoisonError::into_inner)
1189+
};
1190+
assert_eq!(get_process_count(), 4);
1191+
1192+
// Now we will only send a single source event, but that should still result in all related
1193+
// assets being reprocessed.
1194+
1195+
custom_source_dir.insert_asset_text(Path::new("bottom.nest"), &serialize_as_leaf("HEE".into()));
1196+
custom_source_events
1197+
.send_blocking(AssetSourceEvent::ModifiedAsset("bottom.nest".into()))
1198+
.unwrap();
1199+
1200+
run_app_until_finished_processing(&mut app);
1201+
1202+
assert_eq!(
1203+
read_asset_as_string(&custom_processed_dir, Path::new("bottom.nest")),
1204+
serialize_as_leaf("HEE-he".into())
1205+
);
1206+
assert_eq!(
1207+
read_asset_as_string(&default_processed_dir, Path::new("middle.nest")),
1208+
serialize_as_leaf("HEE-he-he".into())
1209+
);
1210+
assert_eq!(
1211+
read_asset_as_string(&custom_processed_dir, Path::new("top.nest")),
1212+
serialize_as_leaf("HEE-he-he-he".into())
1213+
);
1214+
assert_eq!(
1215+
read_asset_as_string(&default_processed_dir, Path::new("unrelated.nest")),
1216+
serialize_as_leaf("tee-he".into())
1217+
);
1218+
1219+
assert_eq!(get_process_count(), 7);
1220+
1221+
// Send a modify event to the middle asset without changing the asset bytes. This should do
1222+
// **nothing** since neither its dependencies nor its bytes have changed.
1223+
default_source_events
1224+
.send_blocking(AssetSourceEvent::ModifiedAsset("middle.nest".into()))
1225+
.unwrap();
1226+
1227+
run_app_until_finished_processing(&mut app);
1228+
1229+
assert_eq!(
1230+
read_asset_as_string(&custom_processed_dir, Path::new("bottom.nest")),
1231+
serialize_as_leaf("HEE-he".into())
1232+
);
1233+
assert_eq!(
1234+
read_asset_as_string(&default_processed_dir, Path::new("middle.nest")),
1235+
serialize_as_leaf("HEE-he-he".into())
1236+
);
1237+
assert_eq!(
1238+
read_asset_as_string(&custom_processed_dir, Path::new("top.nest")),
1239+
serialize_as_leaf("HEE-he-he-he".into())
1240+
);
1241+
assert_eq!(
1242+
read_asset_as_string(&default_processed_dir, Path::new("unrelated.nest")),
1243+
serialize_as_leaf("tee-he".into())
1244+
);
1245+
1246+
assert_eq!(get_process_count(), 7);
1247+
}

0 commit comments

Comments
 (0)