Add tests and fixes for double ended map range iterator
This commit is contained in:
parent
7d5eaa0b7f
commit
28a61f2dcd
4 changed files with 351 additions and 1 deletions
automerge/src
|
@ -476,6 +476,325 @@ fn range_iter_map() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn map_range_back_and_forth_single() {
|
||||
let mut doc = AutoCommit::new();
|
||||
let actor = doc.get_actor().clone();
|
||||
|
||||
doc.put(ROOT, "1", "a").unwrap();
|
||||
doc.put(ROOT, "2", "b").unwrap();
|
||||
doc.put(ROOT, "3", "c").unwrap();
|
||||
|
||||
let mut range_all = doc.map_range(ROOT, ..);
|
||||
assert_eq!(
|
||||
range_all.next(),
|
||||
Some(("1", "a".into(), ExId::Id(1, actor.clone(), 0)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next_back(),
|
||||
Some(("3", "c".into(), ExId::Id(3, actor.clone(), 0)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next_back(),
|
||||
Some(("2", "b".into(), ExId::Id(2, actor.clone(), 0)))
|
||||
);
|
||||
assert_eq!(range_all.next_back(), None);
|
||||
assert_eq!(range_all.next(), None);
|
||||
|
||||
let mut range_all = doc.map_range(ROOT, ..);
|
||||
assert_eq!(
|
||||
range_all.next(),
|
||||
Some(("1", "a".into(), ExId::Id(1, actor.clone(), 0)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next_back(),
|
||||
Some(("3", "c".into(), ExId::Id(3, actor.clone(), 0)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next(),
|
||||
Some(("2", Value::str("b"), ExId::Id(2, actor.clone(), 0)))
|
||||
);
|
||||
assert_eq!(range_all.next_back(), None);
|
||||
assert_eq!(range_all.next(), None);
|
||||
|
||||
let mut range_all = doc.map_range(ROOT, ..);
|
||||
assert_eq!(
|
||||
range_all.next(),
|
||||
Some(("1", "a".into(), ExId::Id(1, actor.clone(), 0)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next(),
|
||||
Some(("2", "b".into(), ExId::Id(2, actor.clone(), 0)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next(),
|
||||
Some(("3", "c".into(), ExId::Id(3, actor.clone(), 0)))
|
||||
);
|
||||
assert_eq!(range_all.next_back(), None);
|
||||
assert_eq!(range_all.next(), None);
|
||||
|
||||
let mut range_all = doc.map_range(ROOT, ..);
|
||||
assert_eq!(
|
||||
range_all.next_back(),
|
||||
Some(("3", "c".into(), ExId::Id(3, actor.clone(), 0)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next_back(),
|
||||
Some(("2", "b".into(), ExId::Id(2, actor.clone(), 0)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next_back(),
|
||||
Some(("1", "a".into(), ExId::Id(1, actor, 0)))
|
||||
);
|
||||
assert_eq!(range_all.next_back(), None);
|
||||
assert_eq!(range_all.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn map_range_back_and_forth_double() {
|
||||
let mut doc1 = AutoCommit::new();
|
||||
doc1.set_actor(ActorId::from([0]));
|
||||
|
||||
doc1.put(ROOT, "1", "a").unwrap();
|
||||
doc1.put(ROOT, "2", "b").unwrap();
|
||||
doc1.put(ROOT, "3", "c").unwrap();
|
||||
|
||||
// actor 2 should win in all conflicts here
|
||||
let mut doc2 = AutoCommit::new();
|
||||
doc1.set_actor(ActorId::from([1]));
|
||||
let actor2 = doc2.get_actor().clone();
|
||||
doc2.put(ROOT, "1", "aa").unwrap();
|
||||
doc2.put(ROOT, "2", "bb").unwrap();
|
||||
doc2.put(ROOT, "3", "cc").unwrap();
|
||||
|
||||
doc1.merge(&mut doc2).unwrap();
|
||||
|
||||
let mut range_all = doc1.map_range(ROOT, ..);
|
||||
assert_eq!(
|
||||
range_all.next(),
|
||||
Some(("1", "aa".into(), ExId::Id(1, actor2.clone(), 1)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next_back(),
|
||||
Some(("3", "cc".into(), ExId::Id(3, actor2.clone(), 1)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next_back(),
|
||||
Some(("2", "bb".into(), ExId::Id(2, actor2.clone(), 1)))
|
||||
);
|
||||
assert_eq!(range_all.next_back(), None);
|
||||
assert_eq!(range_all.next(), None);
|
||||
|
||||
let mut range_all = doc1.map_range(ROOT, ..);
|
||||
assert_eq!(
|
||||
range_all.next(),
|
||||
Some(("1", "aa".into(), ExId::Id(1, actor2.clone(), 1)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next_back(),
|
||||
Some(("3", "cc".into(), ExId::Id(3, actor2.clone(), 1)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next(),
|
||||
Some(("2", "bb".into(), ExId::Id(2, actor2.clone(), 1)))
|
||||
);
|
||||
assert_eq!(range_all.next_back(), None);
|
||||
assert_eq!(range_all.next(), None);
|
||||
|
||||
let mut range_all = doc1.map_range(ROOT, ..);
|
||||
assert_eq!(
|
||||
range_all.next(),
|
||||
Some(("1", "aa".into(), ExId::Id(1, actor2.clone(), 1)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next(),
|
||||
Some(("2", "bb".into(), ExId::Id(2, actor2.clone(), 1)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next(),
|
||||
Some(("3", "cc".into(), ExId::Id(3, actor2.clone(), 1)))
|
||||
);
|
||||
assert_eq!(range_all.next_back(), None);
|
||||
assert_eq!(range_all.next(), None);
|
||||
|
||||
let mut range_all = doc1.map_range(ROOT, ..);
|
||||
assert_eq!(
|
||||
range_all.next_back(),
|
||||
Some(("3", "cc".into(), ExId::Id(3, actor2.clone(), 1)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next_back(),
|
||||
Some(("2", "bb".into(), ExId::Id(2, actor2.clone(), 1)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next_back(),
|
||||
Some(("1", "aa".into(), ExId::Id(1, actor2, 1)))
|
||||
);
|
||||
assert_eq!(range_all.next_back(), None);
|
||||
assert_eq!(range_all.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn map_range_at_back_and_forth_single() {
|
||||
let mut doc = AutoCommit::new();
|
||||
let actor = doc.get_actor().clone();
|
||||
|
||||
doc.put(ROOT, "1", "a").unwrap();
|
||||
doc.put(ROOT, "2", "b").unwrap();
|
||||
doc.put(ROOT, "3", "c").unwrap();
|
||||
|
||||
let heads = doc.get_heads();
|
||||
|
||||
let mut range_all = doc.map_range_at(ROOT, .., &heads);
|
||||
assert_eq!(
|
||||
range_all.next(),
|
||||
Some(("1", "a".into(), ExId::Id(1, actor.clone(), 0)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next_back(),
|
||||
Some(("3", "c".into(), ExId::Id(3, actor.clone(), 0)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next_back(),
|
||||
Some(("2", "b".into(), ExId::Id(2, actor.clone(), 0)))
|
||||
);
|
||||
assert_eq!(range_all.next_back(), None);
|
||||
assert_eq!(range_all.next(), None);
|
||||
|
||||
let mut range_all = doc.map_range_at(ROOT, .., &heads);
|
||||
assert_eq!(
|
||||
range_all.next(),
|
||||
Some(("1", "a".into(), ExId::Id(1, actor.clone(), 0)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next_back(),
|
||||
Some(("3", "c".into(), ExId::Id(3, actor.clone(), 0)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next(),
|
||||
Some(("2", Value::str("b"), ExId::Id(2, actor.clone(), 0)))
|
||||
);
|
||||
assert_eq!(range_all.next_back(), None);
|
||||
assert_eq!(range_all.next(), None);
|
||||
|
||||
let mut range_all = doc.map_range_at(ROOT, .., &heads);
|
||||
assert_eq!(
|
||||
range_all.next(),
|
||||
Some(("1", "a".into(), ExId::Id(1, actor.clone(), 0)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next(),
|
||||
Some(("2", "b".into(), ExId::Id(2, actor.clone(), 0)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next(),
|
||||
Some(("3", "c".into(), ExId::Id(3, actor.clone(), 0)))
|
||||
);
|
||||
assert_eq!(range_all.next_back(), None);
|
||||
assert_eq!(range_all.next(), None);
|
||||
|
||||
let mut range_all = doc.map_range_at(ROOT, .., &heads);
|
||||
assert_eq!(
|
||||
range_all.next_back(),
|
||||
Some(("3", "c".into(), ExId::Id(3, actor.clone(), 0)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next_back(),
|
||||
Some(("2", "b".into(), ExId::Id(2, actor.clone(), 0)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next_back(),
|
||||
Some(("1", "a".into(), ExId::Id(1, actor, 0)))
|
||||
);
|
||||
assert_eq!(range_all.next_back(), None);
|
||||
assert_eq!(range_all.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn map_range_at_back_and_forth_double() {
|
||||
let mut doc1 = AutoCommit::new();
|
||||
doc1.set_actor(ActorId::from([0]));
|
||||
|
||||
doc1.put(ROOT, "1", "a").unwrap();
|
||||
doc1.put(ROOT, "2", "b").unwrap();
|
||||
doc1.put(ROOT, "3", "c").unwrap();
|
||||
|
||||
// actor 2 should win in all conflicts here
|
||||
let mut doc2 = AutoCommit::new();
|
||||
doc1.set_actor(ActorId::from([1]));
|
||||
let actor2 = doc2.get_actor().clone();
|
||||
doc2.put(ROOT, "1", "aa").unwrap();
|
||||
doc2.put(ROOT, "2", "bb").unwrap();
|
||||
doc2.put(ROOT, "3", "cc").unwrap();
|
||||
|
||||
doc1.merge(&mut doc2).unwrap();
|
||||
let heads = doc1.get_heads();
|
||||
|
||||
let mut range_all = doc1.map_range_at(ROOT, .., &heads);
|
||||
assert_eq!(
|
||||
range_all.next(),
|
||||
Some(("1", "aa".into(), ExId::Id(1, actor2.clone(), 1)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next_back(),
|
||||
Some(("3", "cc".into(), ExId::Id(3, actor2.clone(), 1)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next_back(),
|
||||
Some(("2", "bb".into(), ExId::Id(2, actor2.clone(), 1)))
|
||||
);
|
||||
assert_eq!(range_all.next_back(), None);
|
||||
assert_eq!(range_all.next(), None);
|
||||
|
||||
let mut range_all = doc1.map_range_at(ROOT, .., &heads);
|
||||
assert_eq!(
|
||||
range_all.next(),
|
||||
Some(("1", "aa".into(), ExId::Id(1, actor2.clone(), 1)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next_back(),
|
||||
Some(("3", "cc".into(), ExId::Id(3, actor2.clone(), 1)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next(),
|
||||
Some(("2", "bb".into(), ExId::Id(2, actor2.clone(), 1)))
|
||||
);
|
||||
assert_eq!(range_all.next_back(), None);
|
||||
assert_eq!(range_all.next(), None);
|
||||
|
||||
let mut range_all = doc1.map_range_at(ROOT, .., &heads);
|
||||
assert_eq!(
|
||||
range_all.next(),
|
||||
Some(("1", "aa".into(), ExId::Id(1, actor2.clone(), 1)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next(),
|
||||
Some(("2", "bb".into(), ExId::Id(2, actor2.clone(), 1)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next(),
|
||||
Some(("3", "cc".into(), ExId::Id(3, actor2.clone(), 1)))
|
||||
);
|
||||
assert_eq!(range_all.next_back(), None);
|
||||
assert_eq!(range_all.next(), None);
|
||||
|
||||
let mut range_all = doc1.map_range_at(ROOT, .., &heads);
|
||||
assert_eq!(
|
||||
range_all.next_back(),
|
||||
Some(("3", "cc".into(), ExId::Id(3, actor2.clone(), 1)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next_back(),
|
||||
Some(("2", "bb".into(), ExId::Id(2, actor2.clone(), 1)))
|
||||
);
|
||||
assert_eq!(
|
||||
range_all.next_back(),
|
||||
Some(("1", "aa".into(), ExId::Id(1, actor2, 1)))
|
||||
);
|
||||
assert_eq!(range_all.next_back(), None);
|
||||
assert_eq!(range_all.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_at_index() {
|
||||
let mut doc = AutoCommit::new();
|
||||
|
|
|
@ -98,7 +98,7 @@ pub(crate) enum QueryResult {
|
|||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub(crate) struct Index {
|
||||
/// The map of visible elements to the number of operations targetting them.
|
||||
/// The map of visible keys to the number of visible operations for that key.
|
||||
pub(crate) visible: HashMap<Key, usize, FxBuildHasher>,
|
||||
/// Set of opids found in this node and below.
|
||||
pub(crate) ops: HashSet<OpId, FxBuildHasher>,
|
||||
|
|
|
@ -74,6 +74,7 @@ impl<'a, R: RangeBounds<String>> DoubleEndedIterator for MapRange<'a, R> {
|
|||
for i in (self.index..self.index_back).rev() {
|
||||
let op = self.root_child.get(i)?;
|
||||
self.index_back -= 1;
|
||||
|
||||
if Some(op.key) != self.last_key_back && op.visible() {
|
||||
self.last_key_back = Some(op.key);
|
||||
let prop = match op.key {
|
||||
|
@ -85,6 +86,21 @@ impl<'a, R: RangeBounds<String>> DoubleEndedIterator for MapRange<'a, R> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we're now overlapping the index and index_back so try and take the result from the next query
|
||||
if let Some((prop, a, b)) = self.next_result.take() {
|
||||
let last_prop = match self.last_key_back {
|
||||
None => None,
|
||||
Some(Key::Map(u)) => Some(self.meta.props.get(u).as_str()),
|
||||
Some(Key::Seq(_)) => None,
|
||||
};
|
||||
|
||||
// we can only use this result if we haven't ended in the prop's state (to account for
|
||||
// conflicts).
|
||||
if Some(prop) != last_prop {
|
||||
return Some((prop, a, b));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,6 +99,21 @@ impl<'a, R: RangeBounds<String>> DoubleEndedIterator for MapRangeAt<'a, R> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we're now overlapping the index and index_back so try and take the result from the next query
|
||||
if let Some((prop, a, b)) = self.next_result.take() {
|
||||
let last_prop = match self.last_key_back {
|
||||
None => None,
|
||||
Some(Key::Map(u)) => Some(self.meta.props.get(u).as_str()),
|
||||
Some(Key::Seq(_)) => None,
|
||||
};
|
||||
|
||||
// we can only use this result if we haven't ended in the prop's state (to account for
|
||||
// conflicts).
|
||||
if Some(prop) != last_prop {
|
||||
return Some((prop, a, b));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue