mirror of
https://github.com/loro-dev/loro.git
synced 2025-02-02 11:06:14 +00:00
feat: compare frontiers causal order (#257)
This commit is contained in:
parent
0998342001
commit
680041f3a9
10 changed files with 189 additions and 29 deletions
|
@ -24,6 +24,7 @@ use crate::{
|
||||||
handler::TextHandler,
|
handler::TextHandler,
|
||||||
handler::TreeHandler,
|
handler::TreeHandler,
|
||||||
id::PeerID,
|
id::PeerID,
|
||||||
|
oplog::dag::FrontiersNotIncluded,
|
||||||
version::Frontiers,
|
version::Frontiers,
|
||||||
InternalString, LoroError, VersionVector,
|
InternalString, LoroError, VersionVector,
|
||||||
};
|
};
|
||||||
|
@ -574,8 +575,20 @@ impl LoroDoc {
|
||||||
/// - Ordering::Equal means versions equal
|
/// - Ordering::Equal means versions equal
|
||||||
/// - Ordering::Greater means self's version is greater than target
|
/// - Ordering::Greater means self's version is greater than target
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn cmp_frontiers(&self, other: &Frontiers) -> Ordering {
|
pub fn cmp_with_frontiers(&self, other: &Frontiers) -> Ordering {
|
||||||
self.oplog().lock().unwrap().cmp_frontiers(other)
|
self.oplog().lock().unwrap().cmp_with_frontiers(other)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compare two [Frontiers] causally.
|
||||||
|
///
|
||||||
|
/// If one of the [Frontiers] are not included, it will return [FrontiersNotIncluded].
|
||||||
|
#[inline]
|
||||||
|
pub fn cmp_frontiers(
|
||||||
|
&self,
|
||||||
|
a: &Frontiers,
|
||||||
|
b: &Frontiers,
|
||||||
|
) -> Result<Option<Ordering>, FrontiersNotIncluded> {
|
||||||
|
self.oplog().lock().unwrap().cmp_frontiers(a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn subscribe_root(&self, callback: Subscriber) -> SubID {
|
pub fn subscribe_root(&self, callback: Subscriber) -> SubID {
|
||||||
|
@ -721,11 +734,13 @@ impl LoroDoc {
|
||||||
Arc::downgrade(&self.state)
|
Arc::downgrade(&self.state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn len_ops(&self) -> usize {
|
pub fn len_ops(&self) -> usize {
|
||||||
let oplog = self.oplog.lock().unwrap();
|
let oplog = self.oplog.lock().unwrap();
|
||||||
oplog.vv().iter().map(|(_, ops)| *ops).sum::<i32>() as usize
|
oplog.vv().iter().map(|(_, ops)| *ops).sum::<i32>() as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn len_changes(&self) -> usize {
|
pub fn len_changes(&self) -> usize {
|
||||||
let oplog = self.oplog.lock().unwrap();
|
let oplog = self.oplog.lock().unwrap();
|
||||||
oplog.len_changes()
|
oplog.len_changes()
|
||||||
|
@ -750,6 +765,7 @@ impl LoroDoc {
|
||||||
self.renew_txn_if_auto_commit();
|
self.renew_txn_if_auto_commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn log_estimated_size(&self) {
|
pub fn log_estimated_size(&self) {
|
||||||
let state = self.state.try_lock().unwrap();
|
let state = self.state.try_lock().unwrap();
|
||||||
state.log_estimated_size();
|
state.log_estimated_size();
|
||||||
|
|
|
@ -26,6 +26,7 @@ use crate::version::{Frontiers, ImVersionVector, VersionVector};
|
||||||
use crate::LoroError;
|
use crate::LoroError;
|
||||||
|
|
||||||
type ClientChanges = FxHashMap<PeerID, Vec<Change>>;
|
type ClientChanges = FxHashMap<PeerID, Vec<Change>>;
|
||||||
|
pub use self::dag::FrontiersNotIncluded;
|
||||||
use self::pending_changes::PendingChanges;
|
use self::pending_changes::PendingChanges;
|
||||||
|
|
||||||
use super::arena::SharedArena;
|
use super::arena::SharedArena;
|
||||||
|
@ -432,8 +433,20 @@ impl OpLog {
|
||||||
/// - Ordering::Less means self is less than target or parallel
|
/// - Ordering::Less means self is less than target or parallel
|
||||||
/// - Ordering::Equal means versions equal
|
/// - Ordering::Equal means versions equal
|
||||||
/// - Ordering::Greater means self's version is greater than target
|
/// - Ordering::Greater means self's version is greater than target
|
||||||
pub fn cmp_frontiers(&self, other: &Frontiers) -> Ordering {
|
pub fn cmp_with_frontiers(&self, other: &Frontiers) -> Ordering {
|
||||||
self.dag.cmp_frontiers(other)
|
self.dag.cmp_with_frontiers(other)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compare two [Frontiers] causally.
|
||||||
|
///
|
||||||
|
/// If one of the [Frontiers] are not included, it will return [FrontiersNotIncluded].
|
||||||
|
#[inline]
|
||||||
|
pub fn cmp_frontiers(
|
||||||
|
&self,
|
||||||
|
a: &Frontiers,
|
||||||
|
b: &Frontiers,
|
||||||
|
) -> Result<Option<Ordering>, FrontiersNotIncluded> {
|
||||||
|
self.dag.cmp_frontiers(a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_min_lamport_at(&self, id: ID) -> Lamport {
|
pub(crate) fn get_min_lamport_at(&self, id: ID) -> Lamport {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
use std::fmt::{Display, Write};
|
||||||
|
|
||||||
use crate::change::Lamport;
|
use crate::change::Lamport;
|
||||||
use crate::dag::{Dag, DagNode};
|
use crate::dag::{Dag, DagNode};
|
||||||
|
@ -251,7 +252,7 @@ impl AppDag {
|
||||||
/// - Ordering::Less means self is less than target or parallel
|
/// - Ordering::Less means self is less than target or parallel
|
||||||
/// - Ordering::Equal means versions equal
|
/// - Ordering::Equal means versions equal
|
||||||
/// - Ordering::Greater means self's version is greater than target
|
/// - Ordering::Greater means self's version is greater than target
|
||||||
pub fn cmp_frontiers(&self, other: &Frontiers) -> Ordering {
|
pub fn cmp_with_frontiers(&self, other: &Frontiers) -> Ordering {
|
||||||
if &self.frontiers == other {
|
if &self.frontiers == other {
|
||||||
Ordering::Equal
|
Ordering::Equal
|
||||||
} else if other.iter().all(|id| self.vv.includes_id(*id)) {
|
} else if other.iter().all(|id| self.vv.includes_id(*id)) {
|
||||||
|
@ -260,4 +261,26 @@ impl AppDag {
|
||||||
Ordering::Less
|
Ordering::Less
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PERF
|
||||||
|
/// Compare two [Frontiers] causally.
|
||||||
|
///
|
||||||
|
/// If one of the [Frontiers] are not included, it will return [FrontiersNotIncluded].
|
||||||
|
pub fn cmp_frontiers(
|
||||||
|
&self,
|
||||||
|
a: &Frontiers,
|
||||||
|
b: &Frontiers,
|
||||||
|
) -> Result<Option<Ordering>, FrontiersNotIncluded> {
|
||||||
|
let a = self.frontiers_to_vv(a).ok_or(FrontiersNotIncluded)?;
|
||||||
|
let b = self.frontiers_to_vv(b).ok_or(FrontiersNotIncluded)?;
|
||||||
|
Ok(a.partial_cmp(&b))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub struct FrontiersNotIncluded;
|
||||||
|
impl Display for FrontiersNotIncluded {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str("The given Frontiers are not included by the doc")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,6 +167,12 @@ impl From<&[ID]> for Frontiers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> From<[ID; N]> for Frontiers {
|
||||||
|
fn from(value: [ID; N]) -> Self {
|
||||||
|
Self(value.as_slice().into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<ID> for Frontiers {
|
impl From<ID> for Frontiers {
|
||||||
fn from(value: ID) -> Self {
|
fn from(value: ID) -> Self {
|
||||||
Self([value].into())
|
Self([value].into())
|
||||||
|
|
|
@ -99,6 +99,8 @@ extern "C" {
|
||||||
pub type JsTextStyles;
|
pub type JsTextStyles;
|
||||||
#[wasm_bindgen(typescript_type = "Delta<string>[]")]
|
#[wasm_bindgen(typescript_type = "Delta<string>[]")]
|
||||||
pub type JsDelta;
|
pub type JsDelta;
|
||||||
|
#[wasm_bindgen(typescript_type = "-1 | 1 | 0 | undefined")]
|
||||||
|
pub type JsPartialOrd;
|
||||||
}
|
}
|
||||||
|
|
||||||
mod observer {
|
mod observer {
|
||||||
|
@ -625,17 +627,47 @@ impl Loro {
|
||||||
///
|
///
|
||||||
/// Frontiers cannot be compared without the history of the OpLog.
|
/// Frontiers cannot be compared without the history of the OpLog.
|
||||||
///
|
///
|
||||||
#[inline]
|
#[wasm_bindgen(js_name = "cmpWithFrontiers")]
|
||||||
#[wasm_bindgen(js_name = "cmpFrontiers")]
|
pub fn cmp_with_frontiers(&self, frontiers: Vec<JsID>) -> JsResult<i32> {
|
||||||
pub fn cmp_frontiers(&self, frontiers: Vec<JsID>) -> JsResult<i32> {
|
|
||||||
let frontiers = ids_to_frontiers(frontiers)?;
|
let frontiers = ids_to_frontiers(frontiers)?;
|
||||||
Ok(match self.0.cmp_frontiers(&frontiers) {
|
Ok(match self.0.cmp_with_frontiers(&frontiers) {
|
||||||
Ordering::Less => -1,
|
Ordering::Less => -1,
|
||||||
Ordering::Greater => 1,
|
Ordering::Greater => 1,
|
||||||
Ordering::Equal => 0,
|
Ordering::Equal => 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Compare the ordering of two Frontiers.
|
||||||
|
///
|
||||||
|
/// It's assumed that both Frontiers are included by the doc. Otherwise, an error will be thrown.
|
||||||
|
///
|
||||||
|
/// Return value:
|
||||||
|
///
|
||||||
|
/// - -1: a < b
|
||||||
|
/// - 0: a == b
|
||||||
|
/// - 1: a > b
|
||||||
|
/// - undefined: a ∥ b: a and b are concurrent
|
||||||
|
#[wasm_bindgen(js_name = "cmpFrontiers")]
|
||||||
|
pub fn cmp_frontiers(&self, a: Vec<JsID>, b: Vec<JsID>) -> JsResult<JsPartialOrd> {
|
||||||
|
let a = ids_to_frontiers(a)?;
|
||||||
|
let b = ids_to_frontiers(b)?;
|
||||||
|
let c = self
|
||||||
|
.0
|
||||||
|
.cmp_frontiers(&a, &b)
|
||||||
|
.map_err(|e| JsError::new(&e.to_string()))?;
|
||||||
|
if let Some(c) = c {
|
||||||
|
let v: JsValue = match c {
|
||||||
|
Ordering::Less => -1,
|
||||||
|
Ordering::Greater => 1,
|
||||||
|
Ordering::Equal => 0,
|
||||||
|
}
|
||||||
|
.into();
|
||||||
|
Ok(v.into())
|
||||||
|
} else {
|
||||||
|
Ok(JsValue::UNDEFINED.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Export the snapshot of current version, it's include all content of
|
/// Export the snapshot of current version, it's include all content of
|
||||||
/// operations and states
|
/// operations and states
|
||||||
#[wasm_bindgen(js_name = "exportSnapshot")]
|
#[wasm_bindgen(js_name = "exportSnapshot")]
|
||||||
|
|
|
@ -21,6 +21,7 @@ pub use loro_internal::container::richtext::ExpandType;
|
||||||
pub use loro_internal::container::{ContainerID, ContainerType};
|
pub use loro_internal::container::{ContainerID, ContainerType};
|
||||||
pub use loro_internal::obs::SubID;
|
pub use loro_internal::obs::SubID;
|
||||||
pub use loro_internal::obs::Subscriber;
|
pub use loro_internal::obs::Subscriber;
|
||||||
|
pub use loro_internal::oplog::FrontiersNotIncluded;
|
||||||
pub use loro_internal::version::{Frontiers, VersionVector};
|
pub use loro_internal::version::{Frontiers, VersionVector};
|
||||||
pub use loro_internal::DiffEvent;
|
pub use loro_internal::DiffEvent;
|
||||||
pub use loro_internal::{loro_value, to_value};
|
pub use loro_internal::{loro_value, to_value};
|
||||||
|
@ -78,8 +79,16 @@ impl LoroDoc {
|
||||||
self.doc.checkout(frontiers)
|
self.doc.checkout(frontiers)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cmp_frontiers(&self, other: &Frontiers) -> Ordering {
|
pub fn cmp_with_frontiers(&self, other: &Frontiers) -> Ordering {
|
||||||
self.doc.cmp_frontiers(other)
|
self.doc.cmp_with_frontiers(other)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cmp_frontiers(
|
||||||
|
&self,
|
||||||
|
a: &Frontiers,
|
||||||
|
b: &Frontiers,
|
||||||
|
) -> Result<Option<Ordering>, FrontiersNotIncluded> {
|
||||||
|
self.doc.cmp_frontiers(a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Force the document enter the detached mode.
|
/// Force the document enter the detached mode.
|
||||||
|
|
|
@ -1,7 +1,46 @@
|
||||||
use std::sync::Arc;
|
use std::{cmp::Ordering, sync::Arc};
|
||||||
|
|
||||||
use loro::LoroDoc;
|
use loro::{FrontiersNotIncluded, LoroDoc};
|
||||||
use loro_internal::{delta::DeltaItem, handler::TextDelta, DiffEvent, LoroResult};
|
use loro_internal::{delta::DeltaItem, handler::TextDelta, id::ID, DiffEvent, LoroResult};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cmp_frontiers() {
|
||||||
|
let doc1 = LoroDoc::new();
|
||||||
|
doc1.set_peer_id(1).unwrap();
|
||||||
|
doc1.get_text("text").insert(0, "012345").unwrap();
|
||||||
|
let doc2 = LoroDoc::new();
|
||||||
|
doc2.set_peer_id(2).unwrap();
|
||||||
|
doc2.import(&doc1.export_snapshot()).unwrap();
|
||||||
|
doc2.get_text("text").insert(0, "6789").unwrap();
|
||||||
|
doc1.import(&doc2.export_snapshot()).unwrap();
|
||||||
|
doc1.get_text("text").insert(0, "0123").unwrap();
|
||||||
|
doc1.commit();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
doc1.cmp_frontiers(&[].into(), &[ID::new(2, 5)].into()),
|
||||||
|
Err(FrontiersNotIncluded)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
doc1.cmp_frontiers(&[ID::new(1, 2)].into(), &[ID::new(2, 3)].into()),
|
||||||
|
Ok(Some(Ordering::Less))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
doc1.cmp_frontiers(&[ID::new(1, 5)].into(), &[ID::new(2, 3)].into()),
|
||||||
|
Ok(Some(Ordering::Less))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
doc1.cmp_frontiers(&[ID::new(1, 6)].into(), &[ID::new(2, 3)].into()),
|
||||||
|
Ok(Some(Ordering::Greater))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
doc1.cmp_frontiers(&[].into(), &[].into()),
|
||||||
|
Ok(Some(Ordering::Equal))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
doc1.cmp_frontiers(&[ID::new(1, 6)].into(), &[ID::new(1, 6)].into()),
|
||||||
|
Ok(Some(Ordering::Equal))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn get_change_at_lamport() {
|
fn get_change_at_lamport() {
|
||||||
|
|
|
@ -251,31 +251,31 @@ it("get change with given lamport", () => {
|
||||||
doc1.getText("text").insert(0, "01234");
|
doc1.getText("text").insert(0, "01234");
|
||||||
doc1.commit();
|
doc1.commit();
|
||||||
{
|
{
|
||||||
const change = doc1.getChangeAtLamport("1", 1);
|
const change = doc1.getChangeAtLamport("1", 1)!;
|
||||||
expect(change.lamport).toBe(0);
|
expect(change.lamport).toBe(0);
|
||||||
expect(change.peer).toBe("1");
|
expect(change.peer).toBe("1");
|
||||||
expect(change.length).toBe(5);
|
expect(change.length).toBe(5);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const change = doc1.getChangeAtLamport("1", 7);
|
const change = doc1.getChangeAtLamport("1", 7)!;
|
||||||
expect(change.lamport).toBe(0);
|
expect(change.lamport).toBe(0);
|
||||||
expect(change.peer).toBe("1");
|
expect(change.peer).toBe("1");
|
||||||
expect(change.length).toBe(5);
|
expect(change.length).toBe(5);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const change = doc1.getChangeAtLamport("1", 10);
|
const change = doc1.getChangeAtLamport("1", 10)!;
|
||||||
expect(change.lamport).toBe(10);
|
expect(change.lamport).toBe(10);
|
||||||
expect(change.peer).toBe("1");
|
expect(change.peer).toBe("1");
|
||||||
expect(change.length).toBe(5);
|
expect(change.length).toBe(5);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const change = doc1.getChangeAtLamport("1", 13);
|
const change = doc1.getChangeAtLamport("1", 13)!;
|
||||||
expect(change.lamport).toBe(10);
|
expect(change.lamport).toBe(10);
|
||||||
expect(change.peer).toBe("1");
|
expect(change.peer).toBe("1");
|
||||||
expect(change.length).toBe(5);
|
expect(change.length).toBe(5);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const change = doc1.getChangeAtLamport("1", 20);
|
const change = doc1.getChangeAtLamport("1", 20)!;
|
||||||
expect(change.lamport).toBe(10);
|
expect(change.lamport).toBe(10);
|
||||||
expect(change.peer).toBe("1");
|
expect(change.peer).toBe("1");
|
||||||
expect(change.length).toBe(5);
|
expect(change.length).toBe(5);
|
||||||
|
|
|
@ -57,17 +57,17 @@ describe("Checkout", () => {
|
||||||
const v0 = doc.frontiers();
|
const v0 = doc.frontiers();
|
||||||
const docB = new Loro();
|
const docB = new Loro();
|
||||||
docB.import(doc.exportFrom());
|
docB.import(doc.exportFrom());
|
||||||
expect(docB.cmpFrontiers(v0)).toBe(0);
|
expect(docB.cmpWithFrontiers(v0)).toBe(0);
|
||||||
text.insert(1, "0");
|
text.insert(1, "0");
|
||||||
doc.commit();
|
doc.commit();
|
||||||
expect(docB.cmpFrontiers(doc.frontiers())).toBe(-1);
|
expect(docB.cmpWithFrontiers(doc.frontiers())).toBe(-1);
|
||||||
const textB = docB.getText("text");
|
const textB = docB.getText("text");
|
||||||
textB.insert(0, "0");
|
textB.insert(0, "0");
|
||||||
docB.commit();
|
docB.commit();
|
||||||
expect(docB.cmpFrontiers(doc.frontiers())).toBe(-1);
|
expect(docB.cmpWithFrontiers(doc.frontiers())).toBe(-1);
|
||||||
docB.import(doc.exportFrom());
|
docB.import(doc.exportFrom());
|
||||||
expect(docB.cmpFrontiers(doc.frontiers())).toBe(1);
|
expect(docB.cmpWithFrontiers(doc.frontiers())).toBe(1);
|
||||||
doc.import(docB.exportFrom());
|
doc.import(docB.exportFrom());
|
||||||
expect(docB.cmpFrontiers(doc.frontiers())).toBe(0);
|
expect(docB.cmpWithFrontiers(doc.frontiers())).toBe(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,26 +4,48 @@ import { Loro, OpId, VersionVector } from "../src";
|
||||||
describe("Frontiers", () => {
|
describe("Frontiers", () => {
|
||||||
it("two clients", () => {
|
it("two clients", () => {
|
||||||
const doc = new Loro();
|
const doc = new Loro();
|
||||||
|
doc.setPeerId(0);
|
||||||
const text = doc.getText("text");
|
const text = doc.getText("text");
|
||||||
text.insert(0, "0");
|
text.insert(0, "0");
|
||||||
doc.commit();
|
doc.commit();
|
||||||
|
|
||||||
const v0 = doc.frontiers();
|
const v0 = doc.frontiers();
|
||||||
const docB = new Loro();
|
const docB = new Loro();
|
||||||
|
docB.setPeerId(1);
|
||||||
docB.import(doc.exportFrom());
|
docB.import(doc.exportFrom());
|
||||||
expect(docB.cmpFrontiers(v0)).toBe(0);
|
expect(docB.cmpWithFrontiers(v0)).toBe(0);
|
||||||
text.insert(1, "0");
|
text.insert(1, "0");
|
||||||
doc.commit();
|
doc.commit();
|
||||||
expect(docB.cmpFrontiers(doc.frontiers())).toBe(-1);
|
expect(docB.cmpWithFrontiers(doc.frontiers())).toBe(-1);
|
||||||
const textB = docB.getText("text");
|
const textB = docB.getText("text");
|
||||||
textB.insert(0, "0");
|
textB.insert(0, "0");
|
||||||
docB.commit();
|
docB.commit();
|
||||||
expect(docB.cmpFrontiers(doc.frontiers())).toBe(-1);
|
expect(docB.cmpWithFrontiers(doc.frontiers())).toBe(-1);
|
||||||
docB.import(doc.exportFrom());
|
docB.import(doc.exportFrom());
|
||||||
expect(docB.cmpFrontiers(doc.frontiers())).toBe(1);
|
expect(docB.cmpWithFrontiers(doc.frontiers())).toBe(1);
|
||||||
doc.import(docB.exportFrom());
|
doc.import(docB.exportFrom());
|
||||||
expect(docB.cmpFrontiers(doc.frontiers())).toBe(0);
|
expect(docB.cmpWithFrontiers(doc.frontiers())).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("cmp frontiers", () => {
|
||||||
|
const doc1 = new Loro();
|
||||||
|
doc1.setPeerId(1);
|
||||||
|
const doc2 = new Loro();
|
||||||
|
doc2.setPeerId(2n);
|
||||||
|
|
||||||
|
doc1.getText("text").insert(0, "01234");
|
||||||
|
doc2.import(doc1.exportFrom());
|
||||||
|
doc2.getText("text").insert(0, "56789");
|
||||||
|
doc1.import(doc2.exportFrom());
|
||||||
|
doc1.getText("text").insert(0, "01234");
|
||||||
|
doc1.commit();
|
||||||
|
|
||||||
|
expect(() => { doc1.cmpFrontiers([{ peer: "1", counter: 1 }], [{ peer: "2", counter: 10 }]) }).toThrow();
|
||||||
|
expect(doc1.cmpFrontiers([], [{ peer: "1", counter: 1 }])).toBe(-1)
|
||||||
|
expect(doc1.cmpFrontiers([], [])).toBe(0)
|
||||||
|
expect(doc1.cmpFrontiers([{ peer: "1", counter: 4 }], [{ peer: "2", counter: 3 }])).toBe(-1)
|
||||||
|
expect(doc1.cmpFrontiers([{ peer: "1", counter: 5 }], [{ peer: "2", counter: 3 }])).toBe(1)
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
it('peer id repr should be consistent', () => {
|
it('peer id repr should be consistent', () => {
|
||||||
|
|
Loading…
Reference in a new issue