use std::{fs, path::Path, str::FromStr, sync::Arc};
use gled_model::{
asset::{
Asset, AssetTrait, animation::Animation, curve::Curve, midi_controller::MidiController,
output_device::OutputDevice, palette::Palette, project::Project, scene::Scene,
},
asset_id::AssetId,
collection::Collections,
};
use gled_protocol::{
assets::{AnyAsset, AnyAssetId, AssetChanges, all_assets, collections_from},
codec::{decode, encode},
message::ServerMessage,
};
use uuid::Uuid;
fn load<T: AssetTrait>(collections: &mut Collections) {
load_from::<T>(
&Path::new(env!("CARGO_MANIFEST_DIR")).join("../gled-model/tests/fixtures"),
collections,
);
}
fn load_from<T: AssetTrait>(root: &Path, collections: &mut Collections) {
let Ok(entries) = fs::read_dir(root.join(T::DIR_NAME)) else {
return;
};
for entry in entries {
let path = entry.expect("entry").path();
if path.extension().and_then(|ext| ext.to_str()) != Some("json") {
continue;
}
let stem = path
.file_stem()
.and_then(|stem| stem.to_str())
.expect("name");
let id = AssetId::<T>::from_uuid(Uuid::from_str(stem).expect("uuid name"));
if let Ok(asset) = Asset::<T>::read(fs::read(&path).expect("readable").as_slice(), id) {
collections.get_mut::<T>().insert_shared(asset);
}
}
}
fn fixtures() -> Collections {
let mut collections = Collections::default();
load::<Animation>(&mut collections);
load::<Curve>(&mut collections);
load::<MidiController>(&mut collections);
load::<OutputDevice>(&mut collections);
load::<Palette>(&mut collections);
load::<Project>(&mut collections);
load::<Scene>(&mut collections);
collections
}
#[test]
fn every_asset_survives_the_wire() {
let collections = fixtures();
let count = all_assets(&collections).len();
let message = ServerMessage::Assets(all_assets(&collections));
let bytes = encode(&message).expect("encodes");
println!("{count} fixture assets: {} bytes", bytes.len());
let ServerMessage::Assets(assets) = decode(&bytes).expect("decodes") else {
panic!("not an Assets message");
};
assert_eq!(assets.len(), count);
let rebuilt = collections_from(assets);
assert!(AssetChanges::between(&collections, &rebuilt).is_empty());
}
#[test]
fn changes_hold_only_what_changed() {
let old = fixtures();
let mut new = old.clone();
let curve = Asset::all(&new).into_iter().next().expect("a curve");
let mut edited: Asset<Curve> = Arc::unwrap_or_clone(curve);
edited.data.linked = !edited.data.linked;
new.get_mut::<Curve>().set_asset(edited.clone());
let palette = Asset::<Palette>::all(&new)
.into_iter()
.next()
.expect("a palette");
new.get_mut::<Palette>().delete_asset(palette.id);
let added = Asset::<Palette>::new(vec!["Added".to_owned()]);
new.get_mut::<Palette>().set_asset(added.clone());
let changes = AssetChanges::between(&old, &new);
assert_eq!(changes.changed.len(), 2);
assert!(changes.changed.contains(&AnyAsset::Curve(Arc::new(edited))));
assert!(
changes
.changed
.contains(&AnyAsset::Palette(Arc::new(added)))
);
assert_eq!(changes.deleted, vec![AnyAssetId::Palette(palette.id)]);
let changes: AssetChanges = decode(&encode(&changes).expect("encodes")).expect("decodes");
let mut replica = old.clone();
changes.apply_to(&mut replica);
assert!(AssetChanges::between(&replica, &new).is_empty());
}
#[test]
fn equal_copies_are_unchanged() {
let old = fixtures();
let reloaded = fixtures();
assert!(AssetChanges::between(&old, &reloaded).is_empty());
}
#[test]
#[ignore = "needs the local asset repository"]
fn the_local_repository_survives_the_wire() {
let root = std::env::var_os("GLED_ASSETS")
.map(std::path::PathBuf::from)
.or_else(|| {
std::env::var_os("HOME").map(|home| Path::new(&home).join(".local/share/gled2"))
})
.expect("an asset repository");
let mut collections = Collections::default();
load_from::<Animation>(&root, &mut collections);
load_from::<Curve>(&root, &mut collections);
load_from::<MidiController>(&root, &mut collections);
load_from::<OutputDevice>(&root, &mut collections);
load_from::<Palette>(&root, &mut collections);
load_from::<Project>(&root, &mut collections);
load_from::<Scene>(&root, &mut collections);
let assets = all_assets(&collections);
let count = assets.len();
let bytes = encode(&ServerMessage::Assets(assets)).expect("encodes");
println!("{count} assets: {} bytes", bytes.len());
let ServerMessage::Assets(assets) = decode(&bytes).expect("decodes") else {
panic!("not an Assets message");
};
let rebuilt = collections_from(assets);
let changes = AssetChanges::between(&collections, &rebuilt);
assert!(
changes.is_empty(),
"changed on the wire: {:?}",
changes.changed.iter().map(AnyAsset::id).collect::<Vec<_>>()
);
}