use crate::notes::NoteState;
use crate::notes::generator::notes_storage::{NotesStorage, NotesStorageEditHook};
use crate::notes::long::LongNoteTypeStorage;
use crate::notes::short::ShortNoteTypeStorage;
use super::test_common::{TestLong, TestShort, World, long_data, short_data};
crate::generate_notes_storage! {
enum TestNoteType,
#[derive(PartialEq)]
enum TestNote,
#[derive(Debug, PartialEq)]
enum TestNoteSnapshot,
enum TestNoteRef,
enum TestNoteMut,
#[derive(Debug, Clone)]
struct TestNotesStorage {
note_type Short(TestShort) => shorts: ShortNoteTypeStorage<TestShort>,
note_type Long(TestLong) => longs: LongNoteTypeStorage<TestLong>,
edit_hook MutatePosHook { amt: 1.0 },
}
}
impl TestNotesStorage {
fn health_check(&self) {
self.shorts.health_check();
self.longs.health_check();
}
}
struct MutatePosHook {
amt: f32,
}
#[test]
fn owned_conversions_release_notes_storage_borrows() {
fn shared<S: NotesStorage>(
storage: &S,
ids: &[(u64, S::NoteTypeEnum)],
) -> Vec<S::NoteSnapshotEnum> {
storage
.query_notes_ref(ids.iter().cloned())
.map(|note| note.to_snapshot())
.collect()
}
fn mutable<S: NotesStorage>(
storage: &mut S,
ids: &[(u64, S::NoteTypeEnum)],
) -> Vec<S::NoteSnapshotEnum> {
let mut notes = Vec::new();
storage.query_notes_mut(ids.iter().cloned(), |note| notes.push(note.to_snapshot()));
notes
}
use crate::notes::{GroupId, Interval};
use crate::rhythm::{BeatTime, ClockTime, Time};
let mut world = World::new(7);
let mut short = short_data(2.0);
short.time = Time::Beat(BeatTime::new(4.0).unwrap());
short.gr_id = GroupId::Named("g6".into()); let mut long = long_data(5.0);
long.time = Interval::try_new(
Time::Beat(BeatTime::new(2.0).unwrap()),
Time::Beat(BeatTime::new(6.0).unwrap()),
)
.expect("test endpoints use the same time unit");
long.gr_id = short.gr_id.clone();
let mut storage = TestNotesStorage::with_notes(
vec![TestNote::Short(short), TestNote::Long(long)],
&mut world.note_groups,
&mut world.tempo_tracks,
&mut world.scroll_tracks,
);
let ids = [(1, TestNoteType::Short), (1, TestNoteType::Long)];
storage.query_notes_mut(ids.into_iter(), |note| match note {
TestNoteMut::Short(n) => *n.runtime_data.0 = 12.0,
TestNoteMut::Long(n) => *n.runtime_data.0 = 34.0,
});
let expected: Vec<_> = storage.query_notes(ids.into_iter()).collect();
let seconds = |value| ClockTime::from_seconds(value).unwrap();
assert_eq!(
expected[0].clock_time_interval(),
Interval::new(seconds(2.0), seconds(2.0))
);
assert_eq!(
expected[1].clock_time_interval(),
Interval::new(seconds(1.0), seconds(3.0))
);
let shared_notes = shared(&storage, &ids);
let mutable_notes = mutable(&mut storage, &ids);
let shared_inherent: Vec<_> = storage
.query_notes_ref(ids.into_iter())
.map(|note| note.to_snapshot())
.collect();
let mut mutable_inherent = Vec::new();
storage.query_notes_mut(ids.into_iter(), |note| {
mutable_inherent.push(note.to_snapshot());
match note {
TestNoteMut::Short(n) => *n.runtime_data.0 = 56.0,
TestNoteMut::Long(n) => *n.runtime_data.0 = 78.0,
}
});
assert!(matches!(&expected[0], TestNoteSnapshot::Short(n) if n.runtime_data.0 == 12.0));
assert!(matches!(&expected[1], TestNoteSnapshot::Long(n) if n.runtime_data.0 == 34.0));
assert_eq!(shared_inherent, expected);
assert_eq!(mutable_inherent, expected);
let current: Vec<_> = storage.query_notes(ids.into_iter()).collect();
assert!(matches!(¤t[0], TestNoteSnapshot::Short(n) if n.runtime_data.0 == 56.0));
assert!(matches!(¤t[1], TestNoteSnapshot::Long(n) if n.runtime_data.0 == 78.0));
let mutate = |storage: &mut TestNotesStorage, notes: &[TestNoteSnapshot]| {
assert_eq!(notes.len(), 2);
assert_eq!(
notes[0].clock_time_interval().start(),
ClockTime::from_seconds(2.0).unwrap()
);
storage.remove_note(TestNoteType::Short, notes[0].id());
};
mutate(&mut storage, &shared_notes);
assert_eq!(shared_notes, expected);
assert_eq!(mutable_notes, expected);
assert_eq!(storage.query_notes(ids.into_iter()).count(), 1);
}
impl NotesStorageEditHook<TestNotesStorage> for MutatePosHook {
fn pre_add(&mut self, _storage: &mut TestNotesStorage, notes: &mut [TestNote]) {
for note in notes {
match note {
TestNote::Short(n) => n.pos += self.amt,
TestNote::Long(n) => n.pos += self.amt,
}
}
}
fn pre_update(&mut self, _storage: &mut TestNotesStorage, notes: &mut [(u64, TestNote)]) {
for (_, note) in notes {
match note {
TestNote::Short(n) => n.pos += self.amt,
TestNote::Long(n) => n.pos += self.amt,
}
}
}
}
#[test]
fn pre_add_and_pre_update_hook_mutations_are_stored() {
let mut world = World::new(0);
let mut storage = TestNotesStorage::with_notes(
vec![TestNote::Short(short_data(1.0))],
&mut world.note_groups,
&mut world.tempo_tracks,
&mut world.scroll_tracks,
);
let stored: Vec<(u64, TestNote)> = storage
.query_notes([(1, TestNoteType::Short)].into_iter())
.map(|snapshot| (snapshot.id(), snapshot.into_owned_note()))
.collect();
assert_eq!(stored, vec![(1, TestNote::Short(short_data(2.0)))]);
storage.add_note(
&mut world.note_groups,
&mut world.tempo_tracks,
&mut world.scroll_tracks,
TestNote::Long(long_data(1.0)),
);
let stored: Vec<(u64, TestNote)> = storage
.query_notes([(1, TestNoteType::Long)].into_iter())
.map(|snapshot| (snapshot.id(), snapshot.into_owned_note()))
.collect();
assert_eq!(stored, vec![(1, TestNote::Long(long_data(2.0)))]);
storage.add_notes(
&mut world.note_groups,
&mut world.tempo_tracks,
&mut world.scroll_tracks,
vec![TestNote::Short(short_data(20.0))],
);
let stored: Vec<(u64, TestNote)> = storage
.query_notes([(2, TestNoteType::Short)].into_iter())
.map(|snapshot| (snapshot.id(), snapshot.into_owned_note()))
.collect();
assert_eq!(stored, vec![(2, TestNote::Short(short_data(21.0)))]);
storage.remove_notes(vec![(1, TestNoteType::Long), (2, TestNoteType::Short)]);
storage.replace_note(
&mut world.note_groups,
&mut world.tempo_tracks,
&mut world.scroll_tracks,
1,
TestNote::Short(short_data(5.0)),
);
let stored: Vec<(u64, TestNote)> = storage
.query_notes([(1, TestNoteType::Short)].into_iter())
.map(|snapshot| (snapshot.id(), snapshot.into_owned_note()))
.collect();
assert_eq!(stored, vec![(1, TestNote::Short(short_data(6.0)))]);
storage.replace_notes(
&mut world.note_groups,
&mut world.tempo_tracks,
&mut world.scroll_tracks,
vec![(1, TestNote::Short(short_data(10.0)))],
);
let stored: Vec<(u64, TestNote)> = storage
.query_notes([(1, TestNoteType::Short)].into_iter())
.map(|snapshot| (snapshot.id(), snapshot.into_owned_note()))
.collect();
assert_eq!(stored, vec![(1, TestNote::Short(short_data(11.0)))]);
storage.health_check();
let mut extracted = vec![];
storage.extract_notes(vec![(1, TestNoteType::Short)], |snapshot| {
extracted.push((snapshot.id(), snapshot.into_owned_note()))
});
assert_eq!(extracted, vec![(1, TestNote::Short(short_data(11.0)))]);
storage.remove_note(TestNoteType::Short, 999);
storage.health_check();
}
#[test]
fn note_methods_are_available_through_storage_bounds() {
use crate::notes::{Note, NoteState};
fn check<S: NotesStorage>(
storage: &mut S,
ids: impl Iterator<Item = (u64, S::NoteTypeEnum)> + Clone,
) {
let owned: Vec<_> = storage
.query_notes(ids.clone())
.map(|note| {
(
note.start_time(),
note.duration(),
note.group_id().clone(),
note.clock_time_interval(),
note.id(),
)
})
.collect();
let shared: Vec<_> = storage
.query_notes_ref(ids.clone())
.map(|note| {
(
note.start_time(),
note.duration(),
note.group_id().clone(),
note.clock_time_interval(),
note.id(),
)
})
.collect();
let mut mutable = Vec::new();
storage.query_notes_mut(ids, |note| {
mutable.push((
note.start_time(),
note.duration(),
note.group_id().clone(),
note.clock_time_interval(),
note.id(),
));
});
assert_eq!(owned.len(), 2);
assert_eq!(owned, shared);
assert_eq!(shared, mutable);
}
let mut world = World::new(0);
let mut storage = TestNotesStorage::with_notes(
vec![
TestNote::Short(short_data(1.0)),
TestNote::Long(long_data(2.0)),
],
&mut world.note_groups,
&mut world.tempo_tracks,
&mut world.scroll_tracks,
);
check(
&mut storage,
[(1, TestNoteType::Short), (1, TestNoteType::Long)].into_iter(),
);
}
#[test]
fn hooks_mutate_every_value_in_bulk_add_and_replace() {
let mut world = World::new(0);
let mut storage = TestNotesStorage::with_notes(
vec![
TestNote::Short(short_data(1.0)),
TestNote::Short(short_data(2.0)),
],
&mut world.note_groups,
&mut world.tempo_tracks,
&mut world.scroll_tracks,
);
let values = |storage: &TestNotesStorage| {
storage
.query_notes([(1, TestNoteType::Short), (2, TestNoteType::Short)].into_iter())
.map(|note| note.into_owned_note())
.collect::<Vec<_>>()
};
assert_eq!(
values(&storage),
vec![
TestNote::Short(short_data(2.0)),
TestNote::Short(short_data(3.0))
]
);
storage.replace_notes(
&mut world.note_groups,
&mut world.tempo_tracks,
&mut world.scroll_tracks,
vec![
(1, TestNote::Short(short_data(10.0))),
(2, TestNote::Short(short_data(20.0))),
],
);
assert_eq!(
values(&storage),
vec![
TestNote::Short(short_data(11.0)),
TestNote::Short(short_data(21.0))
]
);
}