The speech cache: exact, bounded with the least recently used going first, and holding no words.
A line's speech with bytes of audio, each character said for 10 ms.
What one line of three characters and bytes of audio weighs against a cap.
Runs every law. fresh(cap) makes an empty cache over cap bytes; restart reopens it on what it kept.
31pub fn speech_cache<C: SpeechCache>(fresh: &impl Fn(u64) -> C, restart: &impl Fn(C) -> C) { 32 info!("conformance: speech cache"); 33 a_kept_line_is_found_for_its_voice_and_exact_text_only(fresh(1 << 20)); 34 a_found_line_is_the_line_that_was_kept(fresh(1 << 20)); 35 keeping_again_replaces(fresh(1 << 20)); 36 the_least_recently_used_goes_first(fresh(3 * weight(100) + 1)); 37 a_line_over_the_whole_cap_is_not_kept(fresh(weight(100))); 38 what_was_kept_survives_a_restart(fresh, restart); 39}
41pub fn a_kept_line_is_found_for_its_voice_and_exact_text_only<C: SpeechCache>(c: C) { 42 run_ready(c.keep(&voice("v1"), &line("Hello Ada"), &speech("Hello Ada", 50), now())).unwrap(); 43 assert!(run_ready(c.find(&voice("v1"), &line("Hello Ada"), now())).unwrap().is_some()); 44 assert!(run_ready(c.find(&voice("v2"), &line("Hello Ada"), now())).unwrap().is_none(), "another voice is another line"); 45 for other in ["hello Ada", "Hello Ada ", "Hello Ad"] { 46 assert!(run_ready(c.find(&voice("v1"), &line(other), now())).unwrap().is_none(), "{other:?} is not the same text"); 47 } 48} 49 50pub fn a_found_line_is_the_line_that_was_kept<C: SpeechCache>(c: C) { 51 let kept = speech("Hi h\u{e9}", 40); 52 run_ready(c.keep(&voice("v1"), &line("Hi h\u{e9}"), &kept, now())).unwrap(); 53 let found = run_ready(c.find(&voice("v1"), &line("Hi h\u{e9}"), now())).unwrap().unwrap(); 54 assert_eq!(found.audio(), kept.audio()); 55 assert_eq!(found.timing().text(), "Hi h\u{e9}"); 56 let (a, b): (Vec<_>, Vec<_>) = (found.timing().characters().collect(), kept.timing().characters().collect()); 57 assert_eq!(a.len(), b.len()); 58 for ((ca, sa), (cb, sb)) in a.iter().zip(&b) { 59 assert_eq!((ca, sa.start(), sa.end()), (cb, sb.start(), sb.end())); 60 } 61} 62 63pub fn keeping_again_replaces<C: SpeechCache>(c: C) { 64 run_ready(c.keep(&voice("v1"), &line("abc"), &speech("abc", 10), now())).unwrap(); 65 run_ready(c.keep(&voice("v1"), &line("abc"), &speech("abc", 30), now())).unwrap(); 66 let found = run_ready(c.find(&voice("v1"), &line("abc"), now())).unwrap().unwrap(); 67 assert_eq!(found.audio().0.len(), 30); 68 assert_eq!(run_ready(c.size()).unwrap().entries, 1); 69} 70 71pub fn the_least_recently_used_goes_first<C: SpeechCache>(c: C) { 72 let v = voice("v1"); 73 for text in ["aaa", "bbb", "ccc"] { 74 run_ready(c.keep(&v, &line(text), &speech(text, 100), now())).unwrap(); 75 } 76 assert_eq!(run_ready(c.size()).unwrap().entries, 3); 77 // Using `aaa` makes `bbb` the one used longest ago. 78 assert!(run_ready(c.find(&v, &line("aaa"), now())).unwrap().is_some()); 79 run_ready(c.keep(&v, &line("ddd"), &speech("ddd", 100), now())).unwrap(); 80 assert!(run_ready(c.find(&v, &line("bbb"), now())).unwrap().is_none(), "the least recently used was dropped"); 81 for kept in ["aaa", "ccc", "ddd"] { 82 assert!(run_ready(c.find(&v, &line(kept), now())).unwrap().is_some(), "{kept} stays"); 83 } 84 let size = run_ready(c.size()).unwrap(); 85 assert_eq!(size.entries, 3); 86 assert!(size.bytes <= 3 * weight(100) + 1, "the cap holds: {}", size.bytes); 87} 88 89pub fn a_line_over_the_whole_cap_is_not_kept<C: SpeechCache>(c: C) { 90 run_ready(c.keep(&voice("v1"), &line("abc"), &speech("abc", 100), now())).unwrap(); 91 run_ready(c.keep(&voice("v1"), &line("xyz"), &speech("xyz", 5000), now())).unwrap(); 92 assert!(run_ready(c.find(&voice("v1"), &line("xyz"), now())).unwrap().is_none()); 93 assert!(run_ready(c.find(&voice("v1"), &line("abc"), now())).unwrap().is_some(), "and it did not push out what fits"); 94} 95 96pub fn what_was_kept_survives_a_restart<C: SpeechCache>(fresh: &impl Fn(u64) -> C, restart: &impl Fn(C) -> C) { 97 let c = fresh(1 << 20); 98 run_ready(c.keep(&voice("v1"), &line("abc"), &speech("abc", 10), now())).unwrap(); 99 let c = restart(c); 100 assert!(run_ready(c.find(&voice("v1"), &line("abc"), now())).unwrap().is_some()); 101}