add undo test

This commit is contained in:
appflowy 2021-08-06 08:40:45 +08:00
parent 760c191758
commit 3d837c51f4
4 changed files with 89 additions and 45 deletions

View File

@ -1,5 +1,5 @@
use crate::{
client::{History, RevId, Revision, UndoResult},
client::{History, RevId, UndoResult},
core::{
Attribute,
Attributes,
@ -21,14 +21,17 @@ pub struct Document {
impl Document {
pub fn new() -> Self {
let mut delta = Delta::new();
delta.insert("\n", Attributes::Empty);
Document {
data: Delta::new(),
data: delta,
history: History::new(),
rev_id_counter: 1,
}
}
pub fn edit(&mut self, index: usize, text: &str) {
pub fn edit(&mut self, index: usize, text: &str) -> Result<(), OTError> {
if self.data.target_len < index {
log::error!(
"{} out of bounds. should 0..{}",
@ -44,16 +47,21 @@ impl Document {
let insert = OpBuilder::insert(text).attributes(attributes).build();
let interval = Interval::new(index, index);
self.update_with_op(insert, interval);
self.update_with_op(insert, interval)
}
pub fn format(&mut self, interval: Interval, attribute: Attribute, enable: bool) {
pub fn format(
&mut self,
interval: Interval,
attribute: Attribute,
enable: bool,
) -> Result<(), OTError> {
let attributes = match enable {
true => AttrsBuilder::new().add_attribute(attribute).build(),
false => AttrsBuilder::new().remove_attribute(attribute).build(),
};
self.update_with_attribute(attributes, interval);
self.update_with_attribute(attributes, interval)
}
pub fn can_undo(&self) -> bool { self.history.can_undo() }
@ -89,9 +97,9 @@ impl Document {
}
}
pub fn delete(&mut self, interval: Interval) {
pub fn delete(&mut self, interval: Interval) -> Result<(), OTError> {
let delete = OpBuilder::delete(interval.size() as u64).build();
self.update_with_op(delete, interval);
self.update_with_op(delete, interval)
}
pub fn to_json(&self) -> String { self.data.to_json() }
@ -100,7 +108,7 @@ impl Document {
pub fn set_data(&mut self, data: Delta) { self.data = data; }
fn update_with_op(&mut self, op: Operation, interval: Interval) {
fn update_with_op(&mut self, op: Operation, interval: Interval) -> Result<(), OTError> {
let mut new_delta = Delta::default();
let (prefix, interval, suffix) = split_length_with_interval(self.data.target_len, interval);
@ -127,18 +135,20 @@ impl Document {
});
}
let new_data = self.data.compose(&new_delta).unwrap();
let undo_delta = new_data.invert_delta(&self.data);
let new_data = self.data.compose(&new_delta)?;
let undo_delta = new_delta.invert_delta(&self.data);
self.rev_id_counter += 1;
if !undo_delta.is_empty() {
self.history.add_undo(undo_delta);
}
self.history.record(undo_delta);
self.data = new_data;
Ok(())
}
pub fn update_with_attribute(&mut self, mut attributes: Attributes, interval: Interval) {
pub fn update_with_attribute(
&mut self,
mut attributes: Attributes,
interval: Interval,
) -> Result<(), OTError> {
let old_attributes = self.data.get_attributes(interval);
log::debug!(
"merge attributes: {:?}, with old: {:?}",
@ -165,23 +175,12 @@ impl Document {
interval
);
self.update_with_op(retain, interval);
self.update_with_op(retain, interval)
}
fn next_rev_id(&self) -> RevId { RevId(self.rev_id_counter) }
}
pub fn transform(left: &mut Document, right: &mut Document) {
let (a_prime, b_prime) = left.data.transform(&right.data).unwrap();
log::trace!("a:{:?},b:{:?}", a_prime, b_prime);
let data_left = left.data.compose(&b_prime).unwrap();
let data_right = right.data.compose(&a_prime).unwrap();
left.set_data(data_left);
right.set_data(data_right);
}
fn split_length_with_interval(length: usize, interval: Interval) -> (Interval, Interval, Interval) {
let original_interval = Interval::new(0, length);
let prefix = original_interval.prefix(interval);

View File

@ -1,4 +1,4 @@
use crate::core::{Delta, Interval, OpBuilder, Operation};
use crate::core::Delta;
const MAX_UNDOS: usize = 20;
@ -37,6 +37,7 @@ pub struct History {
cur_undo: usize,
undos: Vec<Delta>,
redos: Vec<Delta>,
capacity: usize,
}
impl History {
@ -45,6 +46,7 @@ impl History {
cur_undo: 1,
undos: Vec::new(),
redos: Vec::new(),
capacity: 20,
}
}
@ -56,6 +58,19 @@ impl History {
pub fn add_redo(&mut self, delta: Delta) { self.redos.push(delta); }
pub fn record(&mut self, delta: Delta) {
if delta.ops.is_empty() {
return;
}
self.redos.clear();
self.add_undo(delta);
if self.undos.len() > self.capacity {
self.undos.remove(0);
}
}
pub fn undo(&mut self) -> Option<Delta> {
if !self.can_undo() {
return None;

View File

@ -1,8 +1,5 @@
use derive_more::Display;
use flowy_ot::{
client::{transform, Document},
core::*,
};
use flowy_ot::{client::Document, core::*};
use rand::{prelude::*, Rng as WrappedRng};
use std::sync::Once;
@ -66,30 +63,41 @@ impl OpTester {
match op {
TestOp::Insert(delta_i, s, index) => {
let document = &mut self.documents[*delta_i];
document.edit(*index, s);
document.edit(*index, s).unwrap();
},
TestOp::Delete(delta_i, interval) => {
let document = &mut self.documents[*delta_i];
document.delete(*interval);
document.delete(*interval).unwrap();
},
TestOp::InsertBold(delta_i, s, interval) => {
let document = &mut self.documents[*delta_i];
document.edit(interval.start, s);
document.format(*interval, Attribute::Bold, true);
document.edit(interval.start, s).unwrap();
document.format(*interval, Attribute::Bold, true).unwrap();
},
TestOp::Bold(delta_i, interval, enable) => {
let document = &mut self.documents[*delta_i];
document.format(*interval, Attribute::Bold, *enable);
document
.format(*interval, Attribute::Bold, *enable)
.unwrap();
},
TestOp::Italic(delta_i, interval, enable) => {
let document = &mut self.documents[*delta_i];
document.format(*interval, Attribute::Italic, *enable);
document
.format(*interval, Attribute::Italic, *enable)
.unwrap();
},
TestOp::Transform(delta_a_i, delta_b_i) => {
transform(
&mut self.documents[*delta_a_i],
&mut self.documents[*delta_b_i],
);
let (a_prime, b_prime) = self.documents[*delta_a_i]
.data()
.transform(&self.documents[*delta_b_i].data())
.unwrap();
log::trace!("a:{:?},b:{:?}", a_prime, b_prime);
let data_left = self.documents[*delta_a_i].data().compose(&b_prime).unwrap();
let data_right = self.documents[*delta_b_i].data().compose(&a_prime).unwrap();
self.documents[*delta_a_i].set_data(data_left);
self.documents[*delta_b_i].set_data(data_right);
},
TestOp::Invert(delta_a_i, delta_b_i) => {
let delta_a = &self.documents[*delta_a_i].data();
@ -114,10 +122,10 @@ impl OpTester {
self.documents[*delta_a_i].set_data(new_delta_after_undo);
},
TestOp::Undo(delta_i) => {
self.documents[*delta_i].undo();
self.documents[*delta_i].undo().unwrap();
},
TestOp::Redo(delta_i) => {
self.documents[*delta_i].redo();
self.documents[*delta_i].redo().unwrap();
},
TestOp::AssertOpsJson(delta_i, expected) => {
let delta_i_json = self.documents[*delta_i].to_json();

View File

@ -0,0 +1,22 @@
pub mod helper;
use crate::helper::{TestOp::*, *};
#[test]
fn delta_undo_insert_text() {
let ops = vec![Insert(0, "123", 0), Undo(0), AssertOpsJson(0, r#"[]"#)];
OpTester::new().run_script(ops);
}
#[test]
fn delta_undo_insert_text2() {
let ops = vec![
Insert(0, "123", 0),
Insert(0, "456", 0),
Undo(0),
AssertOpsJson(0, r#"[{"insert":"123\n"}]"#),
Undo(0),
AssertOpsJson(0, r#"[{"insert":"\n"}]"#),
];
OpTester::new().run_script(ops);
}