automerge/rust/automerge/src/automerge/tests.rs

1516 lines
46 KiB
Rust

use itertools::Itertools;
use pretty_assertions::assert_eq;
use super::*;
use crate::op_tree::B;
use crate::transaction::Transactable;
use crate::*;
use std::convert::TryInto;
#[test]
fn insert_op() -> Result<(), AutomergeError> {
let mut doc = Automerge::new();
doc.set_actor(ActorId::random());
let mut tx = doc.transaction();
tx.put(ROOT, "hello", "world")?;
tx.get(ROOT, "hello")?;
tx.commit();
Ok(())
}
#[test]
fn test_set() -> Result<(), AutomergeError> {
let mut doc = Automerge::new();
let mut tx = doc.transaction();
// setting a scalar value shouldn't return an opid as no object was created.
tx.put(ROOT, "a", 1)?;
// setting the same value shouldn't return an opid as there is no change.
tx.put(ROOT, "a", 1)?;
assert_eq!(tx.pending_ops(), 1);
let map = tx.put_object(ROOT, "b", ObjType::Map)?;
// object already exists at b but setting a map again overwrites it so we get an opid.
tx.put(map, "a", 2)?;
tx.put_object(ROOT, "b", ObjType::Map)?;
assert_eq!(tx.pending_ops(), 4);
let map = tx.get(ROOT, "b").unwrap().unwrap().1;
assert_eq!(tx.get(&map, "a")?, None);
tx.commit();
Ok(())
}
#[test]
fn test_list() -> Result<(), AutomergeError> {
let mut doc = Automerge::new();
doc.set_actor(ActorId::random());
let mut tx = doc.transaction();
let list_id = tx.put_object(ROOT, "items", ObjType::List)?;
tx.put(ROOT, "zzz", "zzzval")?;
assert!(tx.get(ROOT, "items")?.unwrap().1 == list_id);
tx.insert(&list_id, 0, "a")?;
tx.insert(&list_id, 0, "b")?;
tx.insert(&list_id, 2, "c")?;
tx.insert(&list_id, 1, "d")?;
assert!(tx.get(&list_id, 0)?.unwrap().0 == "b".into());
assert!(tx.get(&list_id, 1)?.unwrap().0 == "d".into());
assert!(tx.get(&list_id, 2)?.unwrap().0 == "a".into());
assert!(tx.get(&list_id, 3)?.unwrap().0 == "c".into());
assert!(tx.length(&list_id) == 4);
tx.commit();
doc.save();
Ok(())
}
#[test]
fn test_del() -> Result<(), AutomergeError> {
let mut doc = Automerge::new();
doc.set_actor(ActorId::random());
let mut tx = doc.transaction();
tx.put(ROOT, "xxx", "xxx")?;
assert!(tx.get(ROOT, "xxx")?.is_some());
tx.delete(ROOT, "xxx")?;
assert!(tx.get(ROOT, "xxx")?.is_none());
tx.commit();
Ok(())
}
#[test]
fn test_inc() -> Result<(), AutomergeError> {
let mut doc = Automerge::new();
let mut tx = doc.transaction();
tx.put(ROOT, "counter", ScalarValue::counter(10))?;
assert!(tx.get(ROOT, "counter")?.unwrap().0 == Value::counter(10));
tx.increment(ROOT, "counter", 10)?;
assert!(tx.get(ROOT, "counter")?.unwrap().0 == Value::counter(20));
tx.increment(ROOT, "counter", -5)?;
assert!(tx.get(ROOT, "counter")?.unwrap().0 == Value::counter(15));
tx.commit();
Ok(())
}
#[test]
fn test_save_incremental() -> Result<(), AutomergeError> {
let mut doc = Automerge::new();
let mut tx = doc.transaction();
tx.put(ROOT, "foo", 1)?;
tx.commit();
let save1 = doc.save();
let mut tx = doc.transaction();
tx.put(ROOT, "bar", 2)?;
tx.commit();
let save2 = doc.save_incremental();
let mut tx = doc.transaction();
tx.put(ROOT, "baz", 3)?;
tx.commit();
let save3 = doc.save_incremental();
let mut save_a: Vec<u8> = vec![];
save_a.extend(&save1);
save_a.extend(&save2);
save_a.extend(&save3);
assert!(doc.save_incremental().is_empty());
let save_b = doc.save();
assert!(save_b.len() < save_a.len());
let mut doc_a = Automerge::load(&save_a)?;
let mut doc_b = Automerge::load(&save_b)?;
assert!(doc_a.get_all(ROOT, "baz")? == doc_b.get_all(ROOT, "baz")?);
assert!(doc_a.save() == doc_b.save());
Ok(())
}
#[test]
fn test_save_text() -> Result<(), AutomergeError> {
let mut doc = Automerge::new();
let mut tx = doc.transaction();
let text = tx.put_object(ROOT, "text", ObjType::Text)?;
tx.commit();
let heads1 = doc.get_heads();
let mut tx = doc.transaction();
tx.splice_text(&text, 0, 0, "hello world")?;
tx.commit();
let heads2 = doc.get_heads();
let mut tx = doc.transaction();
tx.splice_text(&text, 6, 0, "big bad ")?;
tx.commit();
let heads3 = doc.get_heads();
assert!(&doc.text(&text)? == "hello big bad world");
assert!(&doc.text_at(&text, &heads1)?.is_empty());
assert!(&doc.text_at(&text, &heads2)? == "hello world");
assert!(&doc.text_at(&text, &heads3)? == "hello big bad world");
Ok(())
}
#[test]
fn test_props_vals_at() -> Result<(), AutomergeError> {
let mut doc = Automerge::new();
doc.set_actor("aaaa".try_into().unwrap());
let mut tx = doc.transaction();
tx.put(ROOT, "prop1", "val1")?;
tx.commit();
doc.get_heads();
let heads1 = doc.get_heads();
let mut tx = doc.transaction();
tx.put(ROOT, "prop1", "val2")?;
tx.commit();
doc.get_heads();
let heads2 = doc.get_heads();
let mut tx = doc.transaction();
tx.put(ROOT, "prop2", "val3")?;
tx.commit();
doc.get_heads();
let heads3 = doc.get_heads();
let mut tx = doc.transaction();
tx.delete(ROOT, "prop1")?;
tx.commit();
doc.get_heads();
let heads4 = doc.get_heads();
let mut tx = doc.transaction();
tx.put(ROOT, "prop3", "val4")?;
tx.commit();
doc.get_heads();
let heads5 = doc.get_heads();
assert!(doc.keys_at(ROOT, &heads1).collect_vec() == vec!["prop1".to_owned()]);
assert_eq!(doc.length_at(ROOT, &heads1), 1);
assert!(doc.get_at(ROOT, "prop1", &heads1)?.unwrap().0 == Value::str("val1"));
assert!(doc.get_at(ROOT, "prop2", &heads1)?.is_none());
assert!(doc.get_at(ROOT, "prop3", &heads1)?.is_none());
assert!(doc.keys_at(ROOT, &heads2).collect_vec() == vec!["prop1".to_owned()]);
assert_eq!(doc.length_at(ROOT, &heads2), 1);
assert!(doc.get_at(ROOT, "prop1", &heads2)?.unwrap().0 == Value::str("val2"));
assert!(doc.get_at(ROOT, "prop2", &heads2)?.is_none());
assert!(doc.get_at(ROOT, "prop3", &heads2)?.is_none());
assert!(
doc.keys_at(ROOT, &heads3).collect_vec() == vec!["prop1".to_owned(), "prop2".to_owned()]
);
assert_eq!(doc.length_at(ROOT, &heads3), 2);
assert!(doc.get_at(ROOT, "prop1", &heads3)?.unwrap().0 == Value::str("val2"));
assert!(doc.get_at(ROOT, "prop2", &heads3)?.unwrap().0 == Value::str("val3"));
assert!(doc.get_at(ROOT, "prop3", &heads3)?.is_none());
assert!(doc.keys_at(ROOT, &heads4).collect_vec() == vec!["prop2".to_owned()]);
assert_eq!(doc.length_at(ROOT, &heads4), 1);
assert!(doc.get_at(ROOT, "prop1", &heads4)?.is_none());
assert!(doc.get_at(ROOT, "prop2", &heads4)?.unwrap().0 == Value::str("val3"));
assert!(doc.get_at(ROOT, "prop3", &heads4)?.is_none());
assert!(
doc.keys_at(ROOT, &heads5).collect_vec() == vec!["prop2".to_owned(), "prop3".to_owned()]
);
assert_eq!(doc.length_at(ROOT, &heads5), 2);
assert_eq!(doc.length(ROOT), 2);
assert!(doc.get_at(ROOT, "prop1", &heads5)?.is_none());
assert!(doc.get_at(ROOT, "prop2", &heads5)?.unwrap().0 == Value::str("val3"));
assert!(doc.get_at(ROOT, "prop3", &heads5)?.unwrap().0 == Value::str("val4"));
assert_eq!(doc.keys_at(ROOT, &[]).count(), 0);
assert_eq!(doc.length_at(ROOT, &[]), 0);
assert!(doc.get_at(ROOT, "prop1", &[])?.is_none());
assert!(doc.get_at(ROOT, "prop2", &[])?.is_none());
assert!(doc.get_at(ROOT, "prop3", &[])?.is_none());
Ok(())
}
#[test]
fn test_len_at() -> Result<(), AutomergeError> {
let mut doc = Automerge::new();
doc.set_actor("aaaa".try_into().unwrap());
let mut tx = doc.transaction();
let list = tx.put_object(ROOT, "list", ObjType::List)?;
tx.commit();
let heads1 = doc.get_heads();
let mut tx = doc.transaction();
tx.insert(&list, 0, 10)?;
tx.commit();
let heads2 = doc.get_heads();
let mut tx = doc.transaction();
tx.put(&list, 0, 20)?;
tx.insert(&list, 0, 30)?;
tx.commit();
let heads3 = doc.get_heads();
let mut tx = doc.transaction();
tx.put(&list, 1, 40)?;
tx.insert(&list, 1, 50)?;
tx.commit();
let heads4 = doc.get_heads();
let mut tx = doc.transaction();
tx.delete(&list, 2)?;
tx.commit();
let heads5 = doc.get_heads();
let mut tx = doc.transaction();
tx.delete(&list, 0)?;
tx.commit();
let heads6 = doc.get_heads();
assert!(doc.length_at(&list, &heads1) == 0);
assert!(doc.get_at(&list, 0, &heads1)?.is_none());
assert!(doc.length_at(&list, &heads2) == 1);
assert!(doc.get_at(&list, 0, &heads2)?.unwrap().0 == Value::int(10));
assert!(doc.length_at(&list, &heads3) == 2);
assert!(doc.get_at(&list, 0, &heads3)?.unwrap().0 == Value::int(30));
assert!(doc.get_at(&list, 1, &heads3)?.unwrap().0 == Value::int(20));
assert!(doc.length_at(&list, &heads4) == 3);
assert!(doc.get_at(&list, 0, &heads4)?.unwrap().0 == Value::int(30));
assert!(doc.get_at(&list, 1, &heads4)?.unwrap().0 == Value::int(50));
assert!(doc.get_at(&list, 2, &heads4)?.unwrap().0 == Value::int(40));
assert!(doc.length_at(&list, &heads5) == 2);
assert!(doc.get_at(&list, 0, &heads5)?.unwrap().0 == Value::int(30));
assert!(doc.get_at(&list, 1, &heads5)?.unwrap().0 == Value::int(50));
assert!(doc.length_at(&list, &heads6) == 1);
assert!(doc.length(&list) == 1);
assert!(doc.get_at(&list, 0, &heads6)?.unwrap().0 == Value::int(50));
Ok(())
}
#[test]
fn keys_iter_map() {
let mut doc = Automerge::new();
let mut tx = doc.transaction();
tx.put(ROOT, "a", 3).unwrap();
tx.put(ROOT, "b", 4).unwrap();
tx.put(ROOT, "c", 5).unwrap();
tx.put(ROOT, "d", 6).unwrap();
tx.commit();
let mut tx = doc.transaction();
tx.put(ROOT, "a", 7).unwrap();
tx.commit();
let mut tx = doc.transaction();
tx.put(ROOT, "a", 8).unwrap();
tx.put(ROOT, "d", 9).unwrap();
tx.commit();
assert_eq!(doc.keys(ROOT).count(), 4);
let mut keys = doc.keys(ROOT);
assert_eq!(keys.next(), Some("a".into()));
assert_eq!(keys.next(), Some("b".into()));
assert_eq!(keys.next(), Some("c".into()));
assert_eq!(keys.next(), Some("d".into()));
assert_eq!(keys.next(), None);
let mut keys = doc.keys(ROOT);
assert_eq!(keys.next_back(), Some("d".into()));
assert_eq!(keys.next_back(), Some("c".into()));
assert_eq!(keys.next_back(), Some("b".into()));
assert_eq!(keys.next_back(), Some("a".into()));
assert_eq!(keys.next_back(), None);
let mut keys = doc.keys(ROOT);
assert_eq!(keys.next(), Some("a".into()));
assert_eq!(keys.next_back(), Some("d".into()));
assert_eq!(keys.next_back(), Some("c".into()));
assert_eq!(keys.next_back(), Some("b".into()));
assert_eq!(keys.next_back(), None);
let mut keys = doc.keys(ROOT);
assert_eq!(keys.next_back(), Some("d".into()));
assert_eq!(keys.next(), Some("a".into()));
assert_eq!(keys.next(), Some("b".into()));
assert_eq!(keys.next(), Some("c".into()));
assert_eq!(keys.next(), None);
let keys = doc.keys(ROOT);
assert_eq!(keys.collect::<Vec<_>>(), vec!["a", "b", "c", "d"]);
}
#[test]
fn keys_iter_seq() {
let mut doc = Automerge::new();
let mut tx = doc.transaction();
let list = tx.put_object(ROOT, "list", ObjType::List).unwrap();
tx.insert(&list, 0, 3).unwrap();
tx.insert(&list, 1, 4).unwrap();
tx.insert(&list, 2, 5).unwrap();
tx.insert(&list, 3, 6).unwrap();
tx.commit();
let mut tx = doc.transaction();
tx.put(&list, 0, 7).unwrap();
tx.commit();
let mut tx = doc.transaction();
tx.put(&list, 0, 8).unwrap();
tx.put(&list, 3, 9).unwrap();
tx.commit();
let actor = doc.get_actor();
assert_eq!(doc.keys(&list).count(), 4);
let mut keys = doc.keys(&list);
assert_eq!(keys.next(), Some(format!("2@{}", actor)));
assert_eq!(keys.next(), Some(format!("3@{}", actor)));
assert_eq!(keys.next(), Some(format!("4@{}", actor)));
assert_eq!(keys.next(), Some(format!("5@{}", actor)));
assert_eq!(keys.next(), None);
let mut keys = doc.keys(&list);
assert_eq!(keys.next_back(), Some(format!("5@{}", actor)));
assert_eq!(keys.next_back(), Some(format!("4@{}", actor)));
assert_eq!(keys.next_back(), Some(format!("3@{}", actor)));
assert_eq!(keys.next_back(), Some(format!("2@{}", actor)));
assert_eq!(keys.next_back(), None);
let mut keys = doc.keys(&list);
assert_eq!(keys.next(), Some(format!("2@{}", actor)));
assert_eq!(keys.next_back(), Some(format!("5@{}", actor)));
assert_eq!(keys.next_back(), Some(format!("4@{}", actor)));
assert_eq!(keys.next_back(), Some(format!("3@{}", actor)));
assert_eq!(keys.next_back(), None);
let mut keys = doc.keys(&list);
assert_eq!(keys.next_back(), Some(format!("5@{}", actor)));
assert_eq!(keys.next(), Some(format!("2@{}", actor)));
assert_eq!(keys.next(), Some(format!("3@{}", actor)));
assert_eq!(keys.next(), Some(format!("4@{}", actor)));
assert_eq!(keys.next(), None);
let keys = doc.keys(&list);
assert_eq!(
keys.collect::<Vec<_>>(),
vec![
format!("2@{}", actor),
format!("3@{}", actor),
format!("4@{}", actor),
format!("5@{}", actor)
]
);
}
#[test]
fn range_iter_map() {
let mut doc = Automerge::new();
let mut tx = doc.transaction();
tx.put(ROOT, "a", 3).unwrap();
tx.put(ROOT, "b", 4).unwrap();
tx.put(ROOT, "c", 5).unwrap();
tx.put(ROOT, "d", 6).unwrap();
tx.commit();
let mut tx = doc.transaction();
tx.put(ROOT, "a", 7).unwrap();
tx.commit();
let mut tx = doc.transaction();
tx.put(ROOT, "a", 8).unwrap();
tx.put(ROOT, "d", 9).unwrap();
tx.commit();
let actor = doc.get_actor();
assert_eq!(doc.map_range(ROOT, ..).count(), 4);
let mut range = doc.map_range(ROOT, "b".to_owned().."d".into());
assert_eq!(
range.next(),
Some(("b", 4.into(), ExId::Id(2, actor.clone(), 0)))
);
assert_eq!(
range.next(),
Some(("c", 5.into(), ExId::Id(3, actor.clone(), 0)))
);
assert_eq!(range.next(), None);
let mut range = doc.map_range(ROOT, "b".to_owned()..="d".into());
assert_eq!(
range.next(),
Some(("b", 4.into(), ExId::Id(2, actor.clone(), 0)))
);
assert_eq!(
range.next(),
Some(("c", 5.into(), ExId::Id(3, actor.clone(), 0)))
);
assert_eq!(
range.next(),
Some(("d", 9.into(), ExId::Id(7, actor.clone(), 0)))
);
assert_eq!(range.next(), None);
let mut range = doc.map_range(ROOT, ..="c".to_owned());
assert_eq!(
range.next(),
Some(("a", 8.into(), ExId::Id(6, actor.clone(), 0)))
);
assert_eq!(
range.next(),
Some(("b", 4.into(), ExId::Id(2, actor.clone(), 0)))
);
assert_eq!(
range.next(),
Some(("c", 5.into(), ExId::Id(3, actor.clone(), 0)))
);
assert_eq!(range.next(), None);
let range = doc.map_range(ROOT, "a".to_owned()..);
assert_eq!(
range.collect::<Vec<_>>(),
vec![
("a", 8.into(), ExId::Id(6, actor.clone(), 0)),
("b", 4.into(), ExId::Id(2, actor.clone(), 0)),
("c", 5.into(), ExId::Id(3, actor.clone(), 0)),
("d", 9.into(), ExId::Id(7, actor.clone(), 0)),
]
);
}
#[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();
let list = &doc.put_object(ROOT, "list", ObjType::List).unwrap();
doc.insert(list, 0, 0).unwrap();
doc.insert(list, 0, 1).unwrap(); // both inserts at the same index
assert_eq!(doc.length(list), 2);
assert_eq!(doc.keys(list).count(), 2);
assert_eq!(doc.list_range(list, ..).count(), 2);
}
#[test]
fn get_list_values() -> Result<(), AutomergeError> {
let mut doc1 = Automerge::new();
let mut tx = doc1.transaction();
let list = tx.put_object(ROOT, "list", ObjType::List)?;
// insert elements
tx.insert(&list, 0, "First")?;
tx.insert(&list, 1, "Second")?;
tx.insert(&list, 2, "Third")?;
tx.insert(&list, 3, "Forth")?;
tx.insert(&list, 4, "Fith")?;
tx.insert(&list, 5, "Sixth")?;
tx.insert(&list, 6, "Seventh")?;
tx.insert(&list, 7, "Eights")?;
tx.commit();
let v1 = doc1.get_heads();
let mut doc2 = doc1.fork();
let mut tx = doc1.transaction();
tx.put(&list, 2, "Third V2")?;
tx.commit();
let mut tx = doc2.transaction();
tx.put(&list, 2, "Third V3")?;
tx.commit();
doc1.merge(&mut doc2)?;
assert_eq!(doc1.list_range(&list, ..).count(), 8);
for (i, val1, id) in doc1.list_range(&list, ..) {
let val2 = doc1.get(&list, i)?;
assert_eq!(Some((val1, id)), val2);
}
assert_eq!(doc1.list_range(&list, 3..6).count(), 3);
assert_eq!(doc1.list_range(&list, 3..6).next().unwrap().0, 3);
assert_eq!(doc1.list_range(&list, 3..6).last().unwrap().0, 5);
for (i, val1, id) in doc1.list_range(&list, 3..6) {
let val2 = doc1.get(&list, i)?;
assert_eq!(Some((val1, id)), val2);
}
assert_eq!(doc1.list_range_at(&list, .., &v1).count(), 8);
for (i, val1, id) in doc1.list_range_at(&list, .., &v1) {
let val2 = doc1.get_at(&list, i, &v1)?;
assert_eq!(Some((val1, id)), val2);
}
assert_eq!(doc1.list_range_at(&list, 3..6, &v1).count(), 3);
assert_eq!(doc1.list_range_at(&list, 3..6, &v1).next().unwrap().0, 3);
assert_eq!(doc1.list_range_at(&list, 3..6, &v1).last().unwrap().0, 5);
for (i, val1, id) in doc1.list_range_at(&list, 3..6, &v1) {
let val2 = doc1.get_at(&list, i, &v1)?;
assert_eq!(Some((val1, id)), val2);
}
let range: Vec<_> = doc1
.list_range(&list, ..)
.map(|(_, val, id)| (val, id))
.collect();
let values = doc1.values(&list);
let values: Vec<_> = values.collect();
assert_eq!(range, values);
let range: Vec<_> = doc1
.list_range_at(&list, .., &v1)
.map(|(_, val, id)| (val, id))
.collect();
let values: Vec<_> = doc1.values_at(&list, &v1).collect();
assert_eq!(range, values);
Ok(())
}
#[test]
fn get_range_values() -> Result<(), AutomergeError> {
let mut doc1 = Automerge::new();
let mut tx = doc1.transaction();
tx.put(ROOT, "aa", "aaa")?;
tx.put(ROOT, "bb", "bbb")?;
tx.put(ROOT, "cc", "ccc")?;
tx.put(ROOT, "dd", "ddd")?;
tx.commit();
let v1 = doc1.get_heads();
let mut doc2 = doc1.fork();
let mut tx = doc1.transaction();
tx.put(ROOT, "cc", "ccc V2")?;
tx.commit();
let mut tx = doc2.transaction();
tx.put(ROOT, "cc", "ccc V3")?;
tx.commit();
doc1.merge(&mut doc2)?;
let range = "b".to_string().."d".to_string();
assert_eq!(doc1.map_range(ROOT, range.clone()).count(), 2);
for (key, val1, id) in doc1.map_range(ROOT, range.clone()) {
let val2 = doc1.get(ROOT, key)?;
assert_eq!(Some((val1, id)), val2);
}
assert_eq!(doc1.map_range(ROOT, range.clone()).rev().count(), 2);
for (key, val1, id) in doc1.map_range(ROOT, range.clone()).rev() {
let val2 = doc1.get(ROOT, key)?;
assert_eq!(Some((val1, id)), val2);
}
assert_eq!(doc1.map_range_at(ROOT, range.clone(), &v1).count(), 2);
for (key, val1, id) in doc1.map_range_at(ROOT, range.clone(), &v1) {
let val2 = doc1.get_at(ROOT, key, &v1)?;
assert_eq!(Some((val1, id)), val2);
}
assert_eq!(doc1.map_range_at(ROOT, range.clone(), &v1).rev().count(), 2);
for (key, val1, id) in doc1.map_range_at(ROOT, range, &v1).rev() {
let val2 = doc1.get_at(ROOT, key, &v1)?;
assert_eq!(Some((val1, id)), val2);
}
let range: Vec<_> = doc1
.map_range(ROOT, ..)
.map(|(_, val, id)| (val, id))
.collect();
let values: Vec<_> = doc1.values(ROOT).collect();
assert_eq!(range, values);
let range: Vec<_> = doc1
.map_range_at(ROOT, .., &v1)
.map(|(_, val, id)| (val, id))
.collect();
let values: Vec<_> = doc1.values_at(ROOT, &v1).collect();
assert_eq!(range, values);
Ok(())
}
#[test]
fn range_iter_map_rev() {
let mut doc = Automerge::new();
let mut tx = doc.transaction();
tx.put(ROOT, "a", 3).unwrap();
tx.put(ROOT, "b", 4).unwrap();
tx.put(ROOT, "c", 5).unwrap();
tx.put(ROOT, "d", 6).unwrap();
tx.commit();
let mut tx = doc.transaction();
tx.put(ROOT, "a", 7).unwrap();
tx.commit();
let mut tx = doc.transaction();
tx.put(ROOT, "a", 8).unwrap();
tx.put(ROOT, "d", 9).unwrap();
tx.commit();
let actor = doc.get_actor();
assert_eq!(doc.map_range(ROOT, ..).rev().count(), 4);
let mut range = doc.map_range(ROOT, "b".to_owned().."d".into()).rev();
assert_eq!(
range.next(),
Some(("c", 5.into(), ExId::Id(3, actor.clone(), 0)))
);
assert_eq!(
range.next(),
Some(("b", 4.into(), ExId::Id(2, actor.clone(), 0)))
);
assert_eq!(range.next(), None);
let mut range = doc.map_range(ROOT, "b".to_owned()..="d".into()).rev();
assert_eq!(
range.next(),
Some(("d", 9.into(), ExId::Id(7, actor.clone(), 0)))
);
assert_eq!(
range.next(),
Some(("c", 5.into(), ExId::Id(3, actor.clone(), 0)))
);
assert_eq!(
range.next(),
Some(("b", 4.into(), ExId::Id(2, actor.clone(), 0)))
);
assert_eq!(range.next(), None);
let mut range = doc.map_range(ROOT, ..="c".to_owned()).rev();
assert_eq!(
range.next(),
Some(("c", 5.into(), ExId::Id(3, actor.clone(), 0)))
);
assert_eq!(
range.next(),
Some(("b", 4.into(), ExId::Id(2, actor.clone(), 0)))
);
assert_eq!(
range.next(),
Some(("a", 8.into(), ExId::Id(6, actor.clone(), 0)))
);
assert_eq!(range.next(), None);
let range = doc.map_range(ROOT, "a".to_owned()..).rev();
assert_eq!(
range.collect::<Vec<_>>(),
vec![
("d", 9.into(), ExId::Id(7, actor.clone(), 0)),
("c", 5.into(), ExId::Id(3, actor.clone(), 0)),
("b", 4.into(), ExId::Id(2, actor.clone(), 0)),
("a", 8.into(), ExId::Id(6, actor.clone(), 0)),
]
);
}
#[test]
fn rolling_back_transaction_has_no_effect() {
let mut doc = Automerge::new();
let old_states = doc.states.clone();
let bytes = doc.save();
let tx = doc.transaction();
tx.rollback();
let new_states = doc.states.clone();
assert_eq!(old_states, new_states);
let new_bytes = doc.save();
assert_eq!(bytes, new_bytes);
}
#[test]
fn mutate_old_objects() {
let mut doc = Automerge::new();
let mut tx = doc.transaction();
// create a map
let map1 = tx.put_object(ROOT, "a", ObjType::Map).unwrap();
tx.put(&map1, "b", 1).unwrap();
// overwrite the first map with a new one
let map2 = tx.put_object(ROOT, "a", ObjType::Map).unwrap();
tx.put(&map2, "c", 2).unwrap();
tx.commit();
// we can get the new map by traversing the tree
let map = doc.get(&ROOT, "a").unwrap().unwrap().1;
assert_eq!(doc.get(&map, "b").unwrap(), None);
// and get values from it
assert_eq!(
doc.get(&map, "c").unwrap().map(|s| s.0),
Some(ScalarValue::Int(2).into())
);
// but we can still access the old one if we know the ID!
assert_eq!(doc.get(&map1, "b").unwrap().unwrap().0, Value::int(1));
// and even set new things in it!
let mut tx = doc.transaction();
tx.put(&map1, "c", 3).unwrap();
tx.commit();
assert_eq!(doc.get(&map1, "c").unwrap().unwrap().0, Value::int(3));
}
#[test]
fn delete_nothing_in_map_is_noop() {
let mut doc = Automerge::new();
let mut tx = doc.transaction();
// deleting a missing key in a map should just be a noop
assert!(tx.delete(ROOT, "a",).is_ok());
tx.commit();
let last_change = doc.get_last_local_change();
assert!(last_change.is_none());
let bytes = doc.save();
assert!(Automerge::load(&bytes,).is_ok());
let mut tx = doc.transaction();
tx.put(ROOT, "a", 1).unwrap();
tx.commit();
let last_change = doc.get_last_local_change().unwrap();
assert_eq!(last_change.len(), 1);
let mut tx = doc.transaction();
// a real op
tx.delete(ROOT, "a").unwrap();
// a no-op
tx.delete(ROOT, "a").unwrap();
tx.commit();
let last_change = doc.get_last_local_change().unwrap();
assert_eq!(last_change.len(), 1);
}
#[test]
fn delete_nothing_in_list_returns_error() {
let mut doc = Automerge::new();
let mut tx = doc.transaction();
// deleting an element in a list that does not exist is an error
assert!(tx.delete(ROOT, 0,).is_err());
}
#[test]
fn loaded_doc_changes_have_hash() {
let mut doc = Automerge::new();
let mut tx = doc.transaction();
tx.put(ROOT, "a", 1_u64).unwrap();
tx.commit();
let hash = doc.get_last_local_change().unwrap().hash();
let bytes = doc.save();
let doc = Automerge::load(&bytes).unwrap();
assert_eq!(doc.get_change_by_hash(&hash).unwrap().hash(), hash);
}
#[test]
fn load_change_with_zero_start_op() {
let bytes = &[
133, 111, 74, 131, 202, 50, 52, 158, 2, 96, 163, 163, 83, 255, 255, 255, 50, 50, 50, 50,
50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 255, 255, 245, 53, 1, 0, 0, 0, 0, 0, 0, 4, 233,
245, 239, 255, 1, 0, 0, 0, 133, 111, 74, 131, 163, 96, 0, 0, 2, 10, 202, 144, 125, 19, 48,
89, 133, 49, 10, 10, 67, 91, 111, 10, 74, 131, 96, 0, 163, 131, 255, 255, 255, 255, 255,
255, 255, 255, 255, 1, 153, 0, 0, 246, 255, 255, 255, 157, 157, 157, 157, 157, 157, 157,
157, 157, 157, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 48, 254, 208,
];
let _ = Automerge::load(bytes);
}
#[test]
fn load_broken_list() {
enum Action {
InsertText(usize, char),
DelText(usize),
}
use Action::*;
let actions = [
InsertText(0, 'a'),
InsertText(0, 'b'),
DelText(1),
InsertText(0, 'c'),
DelText(1),
DelText(0),
InsertText(0, 'd'),
InsertText(0, 'e'),
InsertText(1, 'f'),
DelText(2),
DelText(1),
InsertText(0, 'g'),
DelText(1),
DelText(0),
InsertText(0, 'h'),
InsertText(1, 'i'),
DelText(1),
DelText(0),
InsertText(0, 'j'),
InsertText(0, 'k'),
DelText(1),
DelText(0),
InsertText(0, 'l'),
DelText(0),
InsertText(0, 'm'),
InsertText(0, 'n'),
DelText(1),
DelText(0),
InsertText(0, 'o'),
DelText(0),
InsertText(0, 'p'),
InsertText(1, 'q'),
InsertText(1, 'r'),
InsertText(1, 's'),
InsertText(3, 't'),
InsertText(5, 'u'),
InsertText(0, 'v'),
InsertText(3, 'w'),
InsertText(4, 'x'),
InsertText(0, 'y'),
InsertText(6, 'z'),
InsertText(11, '1'),
InsertText(0, '2'),
InsertText(0, '3'),
InsertText(0, '4'),
InsertText(13, '5'),
InsertText(11, '6'),
InsertText(17, '7'),
];
let mut doc = Automerge::new();
let mut tx = doc.transaction();
let list = tx.put_object(ROOT, "list", ObjType::List).unwrap();
for action in actions {
match action {
Action::InsertText(index, c) => {
tx.insert(&list, index, c).unwrap();
}
Action::DelText(index) => {
tx.delete(&list, index).unwrap();
}
}
}
tx.commit();
let bytes = doc.save();
let mut doc2 = Automerge::load(&bytes).unwrap();
let bytes2 = doc2.save();
assert_eq!(doc.text(&list).unwrap(), doc2.text(&list).unwrap());
assert_eq!(doc.queue, doc2.queue);
assert_eq!(doc.history, doc2.history);
assert_eq!(doc.history_index, doc2.history_index);
assert_eq!(doc.states, doc2.states);
assert_eq!(doc.deps, doc2.deps);
assert_eq!(doc.saved, doc2.saved);
assert_eq!(doc.ops, doc2.ops);
assert_eq!(doc.max_op, doc2.max_op);
assert_eq!(bytes, bytes2);
}
#[test]
fn load_broken_list_short() {
// breaks when the B constant in OpSet is 3
enum Action {
InsertText(usize, char),
DelText(usize),
}
use Action::*;
let actions = [
InsertText(0, 'a'),
InsertText(1, 'b'),
DelText(1),
InsertText(1, 'c'),
InsertText(2, 'd'),
InsertText(2, 'e'),
InsertText(0, 'f'),
DelText(4),
InsertText(4, 'g'),
];
let mut doc = Automerge::new();
let mut tx = doc.transaction();
let list = tx.put_object(ROOT, "list", ObjType::List).unwrap();
for action in actions {
match action {
Action::InsertText(index, c) => {
tx.insert(&list, index, c).unwrap();
}
Action::DelText(index) => {
tx.delete(&list, index).unwrap();
}
}
}
tx.commit();
let bytes = doc.save();
let mut doc2 = Automerge::load(&bytes).unwrap();
let bytes2 = doc2.save();
assert_eq!(doc.text(&list).unwrap(), doc2.text(&list).unwrap());
assert_eq!(doc.queue, doc2.queue);
assert_eq!(doc.history, doc2.history);
assert_eq!(doc.history_index, doc2.history_index);
assert_eq!(doc.states, doc2.states);
assert_eq!(doc.deps, doc2.deps);
assert_eq!(doc.saved, doc2.saved);
assert_eq!(doc.ops, doc2.ops);
assert_eq!(doc.max_op, doc2.max_op);
assert_eq!(bytes, bytes2);
}
#[test]
fn compute_list_indexes_correctly_when_list_element_is_split_across_tree_nodes() {
let max = B as u64 * 2;
let actor1 = ActorId::from(b"aaaa");
let mut doc1 = AutoCommit::new().with_actor(actor1.clone());
let actor2 = ActorId::from(b"bbbb");
let mut doc2 = AutoCommit::new().with_actor(actor2.clone());
let list = doc1.put_object(ROOT, "list", ObjType::List).unwrap();
doc1.insert(&list, 0, 0).unwrap();
doc2.load_incremental(&doc1.save_incremental()).unwrap();
for i in 1..=max {
doc1.put(&list, 0, i).unwrap()
}
for i in 1..=max {
doc2.put(&list, 0, i).unwrap()
}
let change1 = doc1.save_incremental();
let change2 = doc2.save_incremental();
doc2.load_incremental(&change1).unwrap();
doc1.load_incremental(&change2).unwrap();
assert_eq!(doc1.length(&list), 1);
assert_eq!(doc2.length(&list), 1);
assert_eq!(
doc1.get_all(&list, 0).unwrap(),
vec![
(max.into(), ExId::Id(max + 2, actor1.clone(), 0)),
(max.into(), ExId::Id(max + 2, actor2.clone(), 1))
]
);
assert_eq!(
doc2.get_all(&list, 0).unwrap(),
vec![
(max.into(), ExId::Id(max + 2, actor1, 0)),
(max.into(), ExId::Id(max + 2, actor2, 1))
]
);
assert!(doc1.get(&list, 1).unwrap().is_none());
assert!(doc2.get(&list, 1).unwrap().is_none());
}
#[test]
fn get_parent_objects() {
let mut doc = AutoCommit::new();
let map = doc.put_object(ROOT, "a", ObjType::Map).unwrap();
let list = doc.put_object(&map, "b", ObjType::List).unwrap();
doc.insert(&list, 0, 2).unwrap();
let text = doc.put_object(&list, 0, ObjType::Text).unwrap();
assert_eq!(
doc.parents(&map).unwrap().next(),
Some((ROOT, Prop::Map("a".into()), true))
);
assert_eq!(
doc.parents(&list).unwrap().next(),
Some((map, Prop::Map("b".into()), true))
);
assert_eq!(
doc.parents(&text).unwrap().next(),
Some((list, Prop::Seq(0), true))
);
}
#[test]
fn get_path_to_object() {
let mut doc = AutoCommit::new();
let map = doc.put_object(ROOT, "a", ObjType::Map).unwrap();
let list = doc.put_object(&map, "b", ObjType::List).unwrap();
doc.insert(&list, 0, 2).unwrap();
let text = doc.put_object(&list, 0, ObjType::Text).unwrap();
assert_eq!(
doc.path_to_object(&map).unwrap(),
vec![(ROOT, Prop::Map("a".into()))]
);
assert_eq!(
doc.path_to_object(&list).unwrap(),
vec![
(ROOT, Prop::Map("a".into())),
(map.clone(), Prop::Map("b".into())),
]
);
assert_eq!(
doc.path_to_object(&text).unwrap(),
vec![
(ROOT, Prop::Map("a".into())),
(map, Prop::Map("b".into())),
(list, Prop::Seq(0)),
]
);
}
#[test]
fn parents_iterator() {
let mut doc = AutoCommit::new();
let map = doc.put_object(ROOT, "a", ObjType::Map).unwrap();
let list = doc.put_object(&map, "b", ObjType::List).unwrap();
doc.insert(&list, 0, 2).unwrap();
let text = doc.put_object(&list, 0, ObjType::Text).unwrap();
let mut parents = doc.parents(text).unwrap();
assert_eq!(parents.next(), Some((list, Prop::Seq(0), true)));
assert_eq!(parents.next(), Some((map, Prop::Map("b".into()), true)));
assert_eq!(parents.next(), Some((ROOT, Prop::Map("a".into()), true)));
assert_eq!(parents.next(), None);
}
#[test]
fn can_insert_a_grapheme_into_text() {
let mut doc = Automerge::new();
let mut tx = doc.transaction();
let text = tx.put_object(ROOT, "text", ObjType::Text).unwrap();
let polar_bear = "🐻‍❄️";
tx.splice_text(&text, 0, 0, polar_bear).unwrap();
tx.commit();
let s = doc.text(&text).unwrap();
assert_eq!(s, polar_bear);
let len = doc.length(&text);
assert_eq!(len, 4); // 4 utf8 chars
}
#[test]
fn long_strings_spliced_into_text_get_segmented_by_utf8_chars() {
let mut doc = Automerge::new();
let mut tx = doc.transaction();
let text = tx.put_object(ROOT, "text", ObjType::Text).unwrap();
let polar_bear = "🐻‍❄️";
let polar_bear_army = polar_bear.repeat(100);
tx.splice_text(&text, 0, 0, &polar_bear_army).unwrap();
tx.commit();
let s = doc.text(&text).unwrap();
assert_eq!(s, polar_bear_army);
let len = doc.length(&text);
assert_eq!(len, polar_bear.chars().count() * 100);
assert_eq!(len, 400);
}
#[test]
fn splice_text_uses_unicode_scalars() {
let mut doc = Automerge::new();
let mut tx = doc.transaction();
let text = tx.put_object(ROOT, "text", ObjType::Text).unwrap();
let polar_bear = "🐻‍❄️";
tx.splice_text(&text, 0, 0, polar_bear).unwrap();
tx.commit();
let s = doc.text(&text).unwrap();
assert_eq!(s, polar_bear);
let len = doc.length(&text);
assert_eq!(len, 4); // 4 chars
}
#[test]
fn observe_counter_change_application_overwrite() {
let mut doc1 = AutoCommit::new();
doc1.set_actor(ActorId::from([1]));
doc1.put(ROOT, "counter", ScalarValue::counter(1)).unwrap();
doc1.commit();
let mut doc2 = doc1.fork();
doc2.set_actor(ActorId::from([2]));
doc2.put(ROOT, "counter", "mystring").unwrap();
doc2.commit();
doc1.increment(ROOT, "counter", 2).unwrap();
doc1.commit();
doc1.increment(ROOT, "counter", 5).unwrap();
doc1.commit();
let mut doc3 = doc1.fork().with_observer(VecOpObserver::default());
doc3.merge(&mut doc2).unwrap();
assert_eq!(
doc3.observer().take_patches(),
vec![Patch::Put {
obj: ExId::Root,
path: vec![],
prop: Prop::Map("counter".into()),
value: (
ScalarValue::Str("mystring".into()).into(),
ExId::Id(2, doc2.get_actor().clone(), 1)
),
conflict: false
}]
);
let mut doc4 = doc2.clone().with_observer(VecOpObserver::default());
doc4.merge(&mut doc1).unwrap();
// no patches as the increments operate on an invisible counter
assert_eq!(doc4.observer().take_patches(), vec![]);
}
#[test]
fn observe_counter_change_application() {
let mut doc = AutoCommit::new();
doc.put(ROOT, "counter", ScalarValue::counter(1)).unwrap();
doc.increment(ROOT, "counter", 2).unwrap();
doc.increment(ROOT, "counter", 5).unwrap();
let changes = doc.get_changes(&[]).unwrap().into_iter().cloned();
let mut new_doc = AutoCommit::new().with_observer(VecOpObserver::default());
new_doc.apply_changes(changes).unwrap();
assert_eq!(
new_doc.observer().take_patches(),
vec![
Patch::Put {
obj: ExId::Root,
path: vec![],
prop: Prop::Map("counter".into()),
value: (
ScalarValue::counter(1).into(),
ExId::Id(1, doc.get_actor().clone(), 0)
),
conflict: false
},
Patch::Increment {
obj: ExId::Root,
path: vec![],
prop: Prop::Map("counter".into()),
value: (2, ExId::Id(2, doc.get_actor().clone(), 0)),
},
Patch::Increment {
obj: ExId::Root,
path: vec![],
prop: Prop::Map("counter".into()),
value: (5, ExId::Id(3, doc.get_actor().clone(), 0)),
}
]
);
}
#[test]
fn get_changes_heads_empty() {
let mut doc = AutoCommit::unobserved();
doc.put(ROOT, "key1", 1).unwrap();
doc.commit();
doc.put(ROOT, "key2", 1).unwrap();
doc.commit();
let heads = doc.get_heads();
assert_eq!(doc.get_changes(&heads).unwrap(), Vec::<&Change>::new());
}