mod fixtures;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use fixtures::{Relabelled, graph_of};
use sva_engine::{
Cache, Expected, Hash, Medium, MemoryCache, Pack, Payload, Tier, Tiered, VecMedium,
};
use sva_samples::{AutomationFrame, Buffer, FilterTrace, Label, PSYCHOACOUSTIC_V1, Rule, Source};
const READ: Expected = Expected::Samples {
rate: 8_000,
width: 1,
samples: 64,
};
fn tone(seed: u64) -> Payload {
Payload::Samples(Box::new(Buffer::mono(
8_000,
(0..64)
.map(|i| (i as f64 * 0.37 + seed as f64).sin())
.collect(),
)))
}
fn label() -> Label {
Label::new(
Source::Measured,
PSYCHOACOUSTIC_V1.name,
8_000,
sva_samples::Detail::Point {
rule: Rule::PointSampled,
alias_db: Some(-96.5),
},
)
}
fn trace() -> FilterTrace {
FilterTrace {
node: "written-by".to_string(),
site: 1,
channel: Some(0),
shape: "lowpass",
clamped: false,
trace_secs: 0.25,
frames: vec![AutomationFrame {
t_secs: 0.0,
cutoff: 900.0,
q: 0.8,
gain_db: 0.0,
}],
}
}
struct Flaky {
inner: VecMedium,
writable: AtomicU64,
truncates: AtomicBool,
reads: AtomicBool,
flushes: AtomicU64,
}
impl Flaky {
fn new() -> Flaky {
Flaky {
inner: VecMedium::default(),
writable: AtomicU64::new(u64::MAX),
truncates: AtomicBool::new(true),
reads: AtomicBool::new(true),
flushes: AtomicU64::new(0),
}
}
}
impl Medium for Flaky {
fn size(&self) -> u64 {
self.inner.size()
}
fn read_at(&self, off: u64, buf: &mut [u8]) -> bool {
self.reads.load(Ordering::Relaxed) && self.inner.read_at(off, buf)
}
fn write_at(&self, off: u64, bytes: &[u8]) -> bool {
let limit = self.writable.load(Ordering::Relaxed);
let fits = limit.saturating_sub(off).min(bytes.len() as u64) as usize;
self.inner.write_at(off, &bytes[..fits]) && fits == bytes.len()
}
fn truncate(&self, len: u64) -> bool {
self.truncates.load(Ordering::Relaxed) && self.inner.truncate(len)
}
fn flush(&self) -> bool {
self.flushes.fetch_add(1, Ordering::Relaxed);
true
}
}
fn reopened(pack: &Pack<VecMedium>, max_bytes: u64) -> Pack<VecMedium> {
Pack::open(VecMedium::holding(pack.medium().bytes()), max_bytes)
}
#[test]
fn a_reopened_pack_answers_the_bytes_label_and_traces_it_was_written() {
let pack = Pack::open(VecMedium::default(), u64::MAX);
pack.store(Hash(1, 2), &tone(1), &[trace()], Some(&label()));
pack.store(Hash(3, 4), &tone(3), &[], None);
let again = reopened(&pack, u64::MAX);
let entry = again.load(Hash(1, 2), "asked-as", READ).expect("a hit");
assert_eq!(entry.payload, tone(1), "bit for bit");
assert_eq!(entry.label, Some(label()));
assert_eq!(entry.traces[0].node, "asked-as", "the reader names it");
assert_eq!(entry.traces[0].frames, trace().frames);
assert_eq!(entry.tier, Tier::Persistent);
assert_eq!(
again.load(Hash(3, 4), "n", READ).map(|e| e.payload),
Some(tone(3))
);
assert_eq!(again.faults(), 0);
}
#[test]
fn the_later_of_two_records_under_one_key_wins() {
let pack = Pack::open(VecMedium::default(), u64::MAX);
pack.store(Hash(1, 1), &tone(1), &[], None);
pack.store(Hash(1, 1), &tone(2), &[], None);
let again = reopened(&pack, u64::MAX);
assert_eq!(
again.load(Hash(1, 1), "n", READ).map(|e| e.payload),
Some(tone(2))
);
}
#[test]
fn a_torn_tail_is_a_miss_and_is_cut_off() {
let pack = Pack::open(VecMedium::default(), u64::MAX);
pack.store(Hash(1, 1), &tone(1), &[], None);
let whole = pack.medium().size();
pack.store(Hash(2, 2), &tone(2), &[], None);
let mut bytes = pack.medium().bytes();
bytes.truncate(bytes.len() - 3);
let again = Pack::open(VecMedium::holding(bytes), u64::MAX);
assert!(
again.load(Hash(1, 1), "n", READ).is_some(),
"the whole record"
);
assert!(again.load(Hash(2, 2), "n", READ).is_none(), "the torn one");
assert_eq!(
again.medium().size(),
whole,
"cut back to the last whole record"
);
assert_eq!(again.held_bytes(), whole);
}
#[test]
fn a_flipped_byte_ends_the_pack_at_the_record_it_landed_in() {
let pack = Pack::open(VecMedium::default(), u64::MAX);
pack.store(Hash(1, 1), &tone(1), &[], None);
let first = pack.medium().size() as usize;
pack.store(Hash(2, 2), &tone(2), &[], None);
pack.store(Hash(3, 3), &tone(3), &[], None);
let mut bytes = pack.medium().bytes();
bytes[first + (first / 2)] ^= 0x10;
let again = Pack::open(VecMedium::holding(bytes), u64::MAX);
assert!(again.load(Hash(1, 1), "n", READ).is_some());
for key in [Hash(2, 2), Hash(3, 3)] {
assert!(again.load(key, "n", READ).is_none(), "{key}: at or past it");
assert!(!again.holds(key));
}
assert_eq!(again.medium().size() as usize, first);
}
#[test]
fn a_sweep_over_the_cap_keeps_the_most_recently_read_and_compacts() {
let keys: Vec<Hash> = (0..8).map(|i| Hash(i, 0)).collect();
let pack = Pack::open(VecMedium::default(), u64::MAX);
pack.store(keys[0], &tone(0), &[], None);
let record = pack.medium().size();
let capped = Pack::open(VecMedium::default(), record * 5);
for (i, &key) in keys.iter().enumerate() {
capped.store(key, &tone(i as u64), &[], None);
}
for &key in &keys[4..] {
assert!(capped.load(key, "n", READ).is_some());
}
capped.sweep();
assert!(capped.held_bytes() <= capped.max_bytes());
assert_eq!(
capped.held_bytes(),
capped.medium().size(),
"the medium shrank"
);
assert!(capped.evicted_bytes() > 0);
for &key in &keys[..4] {
assert!(!capped.holds(key), "the least recently read went first");
}
let again = reopened(&capped, record * 5);
for (i, &key) in keys.iter().enumerate().skip(4) {
assert_eq!(
again.load(key, "n", READ).map(|e| e.payload),
Some(tone(i as u64)),
"a survivor moved and still reads whole"
);
}
}
#[test]
fn a_sweep_under_the_cap_drops_nothing() {
let pack = Pack::open(VecMedium::default(), u64::MAX);
pack.store(Hash(1, 1), &tone(1), &[], None);
pack.sweep();
assert_eq!(pack.evicted_bytes(), 0);
assert_eq!(pack.held_bytes(), pack.medium().size());
}
#[test]
fn an_entry_another_codec_wrote_is_a_miss_and_not_a_fault() {
let pack = Pack::open(VecMedium::default(), u64::MAX);
pack.store(Hash(1, 1), &tone(1), &[], None);
let other = reopened(&pack, u64::MAX).coded(Box::new(Relabelled));
assert!(other.load(Hash(1, 1), "n", READ).is_none());
assert_eq!(other.faults(), 0);
assert!(other.holds(Hash(1, 1)), "kept for the codec that wrote it");
assert!(
reopened(&pack, u64::MAX)
.load(Hash(1, 1), "n", READ)
.is_some()
);
}
#[test]
fn only_samples_are_offered_a_record_whatever_they_cost() {
let pack = Pack::open(VecMedium::default(), u64::MAX);
for (kind, kept) in [
(sva_engine::PayloadKind::Samples, true),
(sva_engine::PayloadKind::Frames, false),
(sva_engine::PayloadKind::Symbolic, false),
] {
assert_eq!(
pack.worth_storing(std::time::Duration::ZERO, 1 << 20, kind),
kept,
"{kind:?}"
);
}
}
#[test]
fn a_tiered_store_promotes_a_persistent_hit_into_memory() {
let pack = Pack::open(VecMedium::default(), u64::MAX);
pack.store(Hash(1, 1), &tone(1), &[], Some(&label()));
let tiered = Tiered::new(MemoryCache::new(), reopened(&pack, u64::MAX));
let first = tiered.load(Hash(1, 1), "n", READ).expect("a hit");
assert_eq!(first.tier, Tier::Persistent);
let second = tiered.load(Hash(1, 1), "n", READ).expect("a hit");
assert_eq!(second.tier, Tier::Memory, "promoted");
assert_eq!(first.payload, second.payload);
assert_eq!(first.label, second.label);
tiered.store(Hash(2, 2), &tone(2), &[], None);
assert!(tiered.front.holds(Hash(2, 2)) && tiered.back.holds(Hash(2, 2)));
}
#[test]
fn a_refused_write_is_a_counted_miss_and_the_pack_keeps_working() {
for cuts in [true, false] {
let flaky = Flaky::new();
flaky.truncates.store(cuts, Ordering::Relaxed);
let pack = Pack::open(flaky, u64::MAX);
pack.store(Hash(1, 1), &tone(1), &[], None);
let whole = pack.medium().size();
pack.medium()
.writable
.store(whole + whole / 2, Ordering::Relaxed);
pack.store(Hash(2, 2), &tone(2), &[], None);
assert!(pack.load(Hash(2, 2), "n", READ).is_none(), "cuts {cuts}");
assert!(!pack.holds(Hash(2, 2)));
assert_eq!(
pack.faults(),
1,
"cuts {cuts}: the refusal is on the record"
);
assert_eq!(pack.held_bytes(), whole, "cuts {cuts}");
pack.medium().writable.store(u64::MAX, Ordering::Relaxed);
pack.store(
Hash(3, 3),
&Payload::Samples(Box::new(Buffer::mono(8_000, vec![0.5; 64]))),
&[],
None,
);
assert!(pack.load(Hash(3, 3), "n", READ).is_some(), "cuts {cuts}");
assert!(pack.load(Hash(1, 1), "n", READ).is_some(), "cuts {cuts}");
let again = Pack::open(VecMedium::holding(pack.medium().inner.bytes()), u64::MAX);
assert!(again.load(Hash(1, 1), "n", READ).is_some(), "cuts {cuts}");
assert!(again.load(Hash(3, 3), "n", READ).is_some(), "cuts {cuts}");
assert!(
!again.holds(Hash(2, 2)),
"cuts {cuts}: only whole records scan"
);
assert_eq!(
again.medium().size(),
whole * 2,
"cuts {cuts}: any torn tail is cut"
);
}
}
#[test]
fn a_refused_read_is_a_counted_miss() {
let pack = Pack::open(Flaky::new(), u64::MAX);
pack.store(Hash(1, 1), &tone(1), &[], None);
pack.medium().reads.store(false, Ordering::Relaxed);
assert!(pack.load(Hash(1, 1), "n", READ).is_none());
assert_eq!(pack.faults(), 1);
}
#[test]
fn a_render_of_nothing_but_hits_flushes_nothing() {
let graph = graph_of(
"flushes",
&[
("osc", "saw(110*t)\n"),
("master", "lowpass(sample(@osc), cutoff=900, q=0.8)*0.5\n"),
],
);
let tiered = Tiered::new(MemoryCache::new(), Pack::open(Flaky::new(), u64::MAX));
let rendered = |tiered: &Tiered<Flaky>| {
let held = sva_engine::render(
&graph,
"master",
sva_engine::RenderConfig::seconds(8_000, 0.05),
Some(tiered),
)
.expect("a render");
tiered.sweep();
held.cache_stats.expect("stats")
};
let flushes = |tiered: &Tiered<Flaky>| tiered.back.medium().flushes.load(Ordering::Relaxed);
let cold = rendered(&tiered);
assert!(cold.stored() > 0);
assert_eq!(
flushes(&tiered),
1,
"what the cold render wrote is flushed once"
);
let warm = rendered(&tiered);
assert_eq!(warm.computed(), 0, "the second render is all hits");
assert_eq!(flushes(&tiered), 1, "and flushes nothing");
tiered.sweep();
assert_eq!(flushes(&tiered), 1, "nor does a bare sweep");
}