mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2026-05-26 20:49:55 +00:00
feat(stitched): Implement a simple REPL for testing stitched ordering
This commit is contained in:
@@ -1,101 +1,12 @@
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
use super::{algorithm::*, *};
|
||||
use crate::memory_backend::MemoryStitcherBackend;
|
||||
|
||||
mod parser;
|
||||
|
||||
/// A stitcher backend which holds a stitched ordering in RAM.
|
||||
#[derive(Default)]
|
||||
struct TestStitcherBackend<'id> {
|
||||
items: Vec<(u64, StitchedItem<'id>)>,
|
||||
counter: AtomicU64,
|
||||
}
|
||||
|
||||
impl<'id> TestStitcherBackend<'id> {
|
||||
fn next_id(&self) -> u64 { self.counter.fetch_add(1, Ordering::Relaxed) }
|
||||
|
||||
fn extend(&mut self, results: OrderUpdates<'id, <Self as StitcherBackend>::Key>) {
|
||||
for update in results.gap_updates {
|
||||
let Some(gap_index) = self.items.iter().position(|(key, _)| *key == update.key)
|
||||
else {
|
||||
panic!("bad update key {}", update.key);
|
||||
};
|
||||
|
||||
let insertion_index = if update.gap.is_empty() {
|
||||
self.items.remove(gap_index);
|
||||
gap_index
|
||||
} else {
|
||||
match self.items.get_mut(gap_index) {
|
||||
| Some((_, StitchedItem::Gap(gap))) => {
|
||||
*gap = update.gap;
|
||||
},
|
||||
| Some((key, other)) => {
|
||||
panic!("expected item with key {key} to be a gap, it was {other:?}");
|
||||
},
|
||||
| None => unreachable!("we just checked that this index is valid"),
|
||||
}
|
||||
gap_index.checked_add(1).expect(
|
||||
"should never allocate usize::MAX ids. what kind of test are you running",
|
||||
)
|
||||
};
|
||||
|
||||
let to_insert: Vec<_> = update
|
||||
.inserted_items
|
||||
.into_iter()
|
||||
.map(|item| (self.next_id(), item))
|
||||
.collect();
|
||||
self.items
|
||||
.splice(insertion_index..insertion_index, to_insert.into_iter())
|
||||
.for_each(drop);
|
||||
}
|
||||
|
||||
let new_items: Vec<_> = results
|
||||
.new_items
|
||||
.into_iter()
|
||||
.map(|item| (self.next_id(), item))
|
||||
.collect();
|
||||
self.items.extend(new_items);
|
||||
}
|
||||
|
||||
fn iter(&self) -> impl Iterator<Item = &StitchedItem<'id>> {
|
||||
self.items.iter().map(|(_, item)| item)
|
||||
}
|
||||
}
|
||||
|
||||
impl StitcherBackend for TestStitcherBackend<'_> {
|
||||
type Key = u64;
|
||||
|
||||
fn find_matching_gaps<'a>(
|
||||
&'a self,
|
||||
events: impl Iterator<Item = &'a str>,
|
||||
) -> impl Iterator<Item = (Self::Key, Gap)> {
|
||||
// nobody cares about test suite performance right
|
||||
let mut gaps = vec![];
|
||||
|
||||
for event in events {
|
||||
for (key, item) in &self.items {
|
||||
if let StitchedItem::Gap(gap) = item
|
||||
&& gap.contains(event)
|
||||
{
|
||||
gaps.push((*key, gap.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gaps.into_iter()
|
||||
}
|
||||
|
||||
fn event_exists<'a>(&'a self, event: &'a str) -> bool {
|
||||
self.items
|
||||
.iter()
|
||||
.any(|item| matches!(item.1, StitchedItem::Event(id) if event == id))
|
||||
}
|
||||
}
|
||||
|
||||
fn run_testcase(testcase: parser::TestCase<'_>) {
|
||||
let mut backend = TestStitcherBackend::default();
|
||||
let mut backend = MemoryStitcherBackend::default();
|
||||
|
||||
for (index, phase) in testcase.into_iter().enumerate() {
|
||||
let stitcher = Stitcher::new(&backend);
|
||||
@@ -107,7 +18,7 @@ fn run_testcase(testcase: parser::TestCase<'_>) {
|
||||
for update in &updates.gap_updates {
|
||||
println!("update to gap {}:", update.key);
|
||||
println!(" new gap contents: {:?}", update.gap);
|
||||
println!(" new items: {:?}", update.inserted_items);
|
||||
println!(" inserted items: {:?}", update.inserted_items);
|
||||
}
|
||||
|
||||
println!("expected new items: {:?}", &phase.order.new_items);
|
||||
@@ -134,9 +45,9 @@ fn run_testcase(testcase: parser::TestCase<'_>) {
|
||||
}
|
||||
|
||||
backend.extend(updates);
|
||||
println!("extended ordering: {:?}", backend.items);
|
||||
println!("extended ordering: {:?}", backend);
|
||||
|
||||
for (expected, actual) in phase.order.iter().zip_eq(backend.iter()) {
|
||||
for (expected, ref actual) in phase.order.iter().zip_eq(backend.iter()) {
|
||||
assert_eq!(
|
||||
expected, actual,
|
||||
"bad item in order, expected {expected:?} but got {actual:?}",
|
||||
|
||||
Reference in New Issue
Block a user