|
@@ -0,0 +1,460 @@
|
|
|
+use crate::node::script::NodeScript::*;
|
|
|
+use crate::node::script::NodeTest;
|
|
|
+
|
|
|
+use lib_ot::core::{placeholder_node, NodeData, NodeDataBuilder, NodeOperation, Path};
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn operation_insert_op_transform_test() {
|
|
|
+ let node_1 = NodeDataBuilder::new("text_1").build();
|
|
|
+ let node_2 = NodeDataBuilder::new("text_2").build();
|
|
|
+ let op_1 = NodeOperation::Insert {
|
|
|
+ path: Path(vec![0, 1]),
|
|
|
+ nodes: vec![node_1],
|
|
|
+ };
|
|
|
+
|
|
|
+ let mut insert_2 = NodeOperation::Insert {
|
|
|
+ path: Path(vec![0, 1]),
|
|
|
+ nodes: vec![node_2],
|
|
|
+ };
|
|
|
+
|
|
|
+ // let mut node_tree = NodeTree::new("root");
|
|
|
+ // node_tree.apply_op(insert_1.clone()).unwrap();
|
|
|
+
|
|
|
+ op_1.transform(&mut insert_2);
|
|
|
+ let json = serde_json::to_string(&insert_2).unwrap();
|
|
|
+ assert_eq!(json, r#"{"op":"insert","path":[0,2],"nodes":[{"type":"text_2"}]}"#);
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn operation_insert_one_level_path_test() {
|
|
|
+ let node_data_1 = NodeDataBuilder::new("text_1").build();
|
|
|
+ let node_data_2 = NodeDataBuilder::new("text_2").build();
|
|
|
+ let node_data_3 = NodeDataBuilder::new("text_3").build();
|
|
|
+ let node_3 = node_data_3.clone();
|
|
|
+ // 0: text_1
|
|
|
+ // 1: text_2
|
|
|
+ //
|
|
|
+ // Insert a new operation with rev_id 2 to index 1,but the index was already taken, so
|
|
|
+ // it needs to be transformed.
|
|
|
+ //
|
|
|
+ // 0: text_1
|
|
|
+ // 1: text_2
|
|
|
+ // 2: text_3
|
|
|
+ let scripts = vec![
|
|
|
+ InsertNode {
|
|
|
+ path: 0.into(),
|
|
|
+ node_data: node_data_1.clone(),
|
|
|
+ rev_id: 1,
|
|
|
+ },
|
|
|
+ InsertNode {
|
|
|
+ path: 1.into(),
|
|
|
+ node_data: node_data_2.clone(),
|
|
|
+ rev_id: 2,
|
|
|
+ },
|
|
|
+ InsertNode {
|
|
|
+ path: 1.into(),
|
|
|
+ node_data: node_data_3.clone(),
|
|
|
+ rev_id: 2,
|
|
|
+ },
|
|
|
+ AssertNode {
|
|
|
+ path: 2.into(),
|
|
|
+ expected: Some(node_3.clone()),
|
|
|
+ },
|
|
|
+ ];
|
|
|
+ NodeTest::new().run_scripts(scripts);
|
|
|
+
|
|
|
+ // If the rev_id of the node_data_3 is 3. then the tree will be:
|
|
|
+ // 0: text_1
|
|
|
+ // 1: text_3
|
|
|
+ // 2: text_2
|
|
|
+ let scripts = vec![
|
|
|
+ InsertNode {
|
|
|
+ path: 0.into(),
|
|
|
+ node_data: node_data_1,
|
|
|
+ rev_id: 1,
|
|
|
+ },
|
|
|
+ InsertNode {
|
|
|
+ path: 1.into(),
|
|
|
+ node_data: node_data_2,
|
|
|
+ rev_id: 2,
|
|
|
+ },
|
|
|
+ InsertNode {
|
|
|
+ path: 1.into(),
|
|
|
+ node_data: node_data_3,
|
|
|
+ rev_id: 3,
|
|
|
+ },
|
|
|
+ AssertNode {
|
|
|
+ path: 1.into(),
|
|
|
+ expected: Some(node_3),
|
|
|
+ },
|
|
|
+ ];
|
|
|
+ NodeTest::new().run_scripts(scripts);
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn operation_insert_with_multiple_level_path_test() {
|
|
|
+ let mut test = NodeTest::new();
|
|
|
+ let node_data_1 = NodeDataBuilder::new("text_1")
|
|
|
+ .add_node_data(NodeDataBuilder::new("text_1_1").build())
|
|
|
+ .add_node_data(NodeDataBuilder::new("text_1_2").build())
|
|
|
+ .build();
|
|
|
+
|
|
|
+ let node_data_2 = NodeDataBuilder::new("text_2")
|
|
|
+ .add_node_data(NodeDataBuilder::new("text_2_1").build())
|
|
|
+ .add_node_data(NodeDataBuilder::new("text_2_2").build())
|
|
|
+ .build();
|
|
|
+
|
|
|
+ let node_data_3 = NodeDataBuilder::new("text_3").build();
|
|
|
+ let scripts = vec![
|
|
|
+ InsertNode {
|
|
|
+ path: 0.into(),
|
|
|
+ node_data: node_data_1,
|
|
|
+ rev_id: 1,
|
|
|
+ },
|
|
|
+ InsertNode {
|
|
|
+ path: 1.into(),
|
|
|
+ node_data: node_data_2,
|
|
|
+ rev_id: 2,
|
|
|
+ },
|
|
|
+ InsertNode {
|
|
|
+ path: 1.into(),
|
|
|
+ node_data: node_data_3.clone(),
|
|
|
+ rev_id: 2,
|
|
|
+ },
|
|
|
+ AssertNode {
|
|
|
+ path: 2.into(),
|
|
|
+ expected: Some(node_data_3),
|
|
|
+ },
|
|
|
+ ];
|
|
|
+ test.run_scripts(scripts);
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn operation_insert_node_out_of_bound_test() {
|
|
|
+ let mut test = NodeTest::new();
|
|
|
+ let image_a = NodeData::new("image_a");
|
|
|
+ let image_b = NodeData::new("image_b");
|
|
|
+ let image = NodeDataBuilder::new("image_1")
|
|
|
+ .add_node_data(image_a)
|
|
|
+ .add_node_data(image_b)
|
|
|
+ .build();
|
|
|
+ let text_node = NodeDataBuilder::new("text_1").add_node_data(image).build();
|
|
|
+ let image_c = NodeData::new("image_c");
|
|
|
+
|
|
|
+ let scripts = vec![
|
|
|
+ InsertNode {
|
|
|
+ path: 0.into(),
|
|
|
+ node_data: text_node,
|
|
|
+ rev_id: 1,
|
|
|
+ },
|
|
|
+ // 0:text_1
|
|
|
+ // 0:image_1
|
|
|
+ // 0:image_a
|
|
|
+ // 1:image_b
|
|
|
+ InsertNode {
|
|
|
+ path: vec![0, 0, 3].into(),
|
|
|
+ node_data: image_c.clone(),
|
|
|
+ rev_id: 2,
|
|
|
+ },
|
|
|
+ // 0:text_1
|
|
|
+ // 0:image_1
|
|
|
+ // 0:image_a
|
|
|
+ // 1:image_b
|
|
|
+ // 2:placeholder node
|
|
|
+ // 3:image_c
|
|
|
+ AssertNode {
|
|
|
+ path: vec![0, 0, 2].into(),
|
|
|
+ expected: Some(placeholder_node()),
|
|
|
+ },
|
|
|
+ AssertNode {
|
|
|
+ path: vec![0, 0, 3].into(),
|
|
|
+ expected: Some(image_c),
|
|
|
+ },
|
|
|
+ AssertNode {
|
|
|
+ path: vec![0, 0, 10].into(),
|
|
|
+ expected: None,
|
|
|
+ },
|
|
|
+ ];
|
|
|
+ test.run_scripts(scripts);
|
|
|
+}
|
|
|
+#[test]
|
|
|
+fn operation_insert_node_when_parent_is_not_exist_test1() {
|
|
|
+ let mut test = NodeTest::new();
|
|
|
+ let text_1 = NodeDataBuilder::new("text_1").build();
|
|
|
+ let text_2 = NodeDataBuilder::new("text_2").build();
|
|
|
+ let scripts = vec![
|
|
|
+ InsertNode {
|
|
|
+ path: 0.into(),
|
|
|
+ node_data: text_1,
|
|
|
+ rev_id: 1,
|
|
|
+ },
|
|
|
+ // The node at path 1 is not existing when inserting the text_2 to path 2.
|
|
|
+ InsertNode {
|
|
|
+ path: 2.into(),
|
|
|
+ node_data: text_2.clone(),
|
|
|
+ rev_id: 2,
|
|
|
+ },
|
|
|
+ AssertNode {
|
|
|
+ path: 1.into(),
|
|
|
+ expected: Some(placeholder_node()),
|
|
|
+ },
|
|
|
+ AssertNode {
|
|
|
+ path: 2.into(),
|
|
|
+ expected: Some(text_2),
|
|
|
+ },
|
|
|
+ ];
|
|
|
+ test.run_scripts(scripts);
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn operation_insert_node_when_parent_is_not_exist_test2() {
|
|
|
+ let mut test = NodeTest::new();
|
|
|
+ let text_1 = NodeDataBuilder::new("text_1").build();
|
|
|
+ let text_2 = NodeDataBuilder::new("text_2").build();
|
|
|
+ let scripts = vec![
|
|
|
+ InsertNode {
|
|
|
+ path: 0.into(),
|
|
|
+ node_data: text_1,
|
|
|
+ rev_id: 1,
|
|
|
+ },
|
|
|
+ // The node at path 1 is not existing when inserting the text_2 to path 2.
|
|
|
+ InsertNode {
|
|
|
+ path: 3.into(),
|
|
|
+ node_data: text_2.clone(),
|
|
|
+ rev_id: 2,
|
|
|
+ },
|
|
|
+ AssertNode {
|
|
|
+ path: 1.into(),
|
|
|
+ expected: Some(placeholder_node()),
|
|
|
+ },
|
|
|
+ AssertNode {
|
|
|
+ path: 2.into(),
|
|
|
+ expected: Some(placeholder_node()),
|
|
|
+ },
|
|
|
+ AssertNode {
|
|
|
+ path: 3.into(),
|
|
|
+ expected: Some(text_2),
|
|
|
+ },
|
|
|
+ ];
|
|
|
+ test.run_scripts(scripts);
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn operation_insert_node_when_its_parent_is_not_exist_test3() {
|
|
|
+ let mut test = NodeTest::new();
|
|
|
+ let text_1 = NodeDataBuilder::new("text_1").build();
|
|
|
+ let text_2 = NodeDataBuilder::new("text_2").build();
|
|
|
+
|
|
|
+ let mut placeholder_node = placeholder_node();
|
|
|
+ placeholder_node.children.push(text_2.clone());
|
|
|
+
|
|
|
+ let scripts = vec![
|
|
|
+ InsertNode {
|
|
|
+ path: 0.into(),
|
|
|
+ node_data: text_1,
|
|
|
+ rev_id: 1,
|
|
|
+ },
|
|
|
+ // The node at path 1 is not existing when inserting the text_2 to path 2.
|
|
|
+ InsertNode {
|
|
|
+ path: vec![1, 0].into(),
|
|
|
+ node_data: text_2.clone(),
|
|
|
+ rev_id: 2,
|
|
|
+ },
|
|
|
+ AssertNode {
|
|
|
+ path: 1.into(),
|
|
|
+ expected: Some(placeholder_node),
|
|
|
+ },
|
|
|
+ AssertNode {
|
|
|
+ path: vec![1, 0].into(),
|
|
|
+ expected: Some(text_2),
|
|
|
+ },
|
|
|
+ ];
|
|
|
+ test.run_scripts(scripts);
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn operation_insert_node_to_the_end_when_parent_is_not_exist_test() {
|
|
|
+ let mut test = NodeTest::new();
|
|
|
+ let node_0 = NodeData::new("0");
|
|
|
+ let node_1 = NodeData::new("1");
|
|
|
+ let node_1_1 = NodeData::new("1_1");
|
|
|
+ let text_node = NodeData::new("text");
|
|
|
+ let mut ghost = placeholder_node();
|
|
|
+ ghost.children.push(text_node.clone());
|
|
|
+ // 0:0
|
|
|
+ // 1:1
|
|
|
+ // 0:1_1
|
|
|
+ // 1:ghost
|
|
|
+ // 0:text
|
|
|
+ let scripts = vec![
|
|
|
+ InsertNode {
|
|
|
+ path: 0.into(),
|
|
|
+ node_data: node_0,
|
|
|
+ rev_id: 1,
|
|
|
+ },
|
|
|
+ InsertNode {
|
|
|
+ path: 1.into(),
|
|
|
+ node_data: node_1,
|
|
|
+ rev_id: 2,
|
|
|
+ },
|
|
|
+ InsertNode {
|
|
|
+ path: vec![1, 0].into(),
|
|
|
+ node_data: node_1_1.clone(),
|
|
|
+ rev_id: 3,
|
|
|
+ },
|
|
|
+ InsertNode {
|
|
|
+ path: vec![1, 1, 0].into(),
|
|
|
+ node_data: text_node.clone(),
|
|
|
+ rev_id: 4,
|
|
|
+ },
|
|
|
+ AssertNode {
|
|
|
+ path: vec![1, 0].into(),
|
|
|
+ expected: Some(node_1_1),
|
|
|
+ },
|
|
|
+ AssertNode {
|
|
|
+ path: vec![1, 1].into(),
|
|
|
+ expected: Some(ghost),
|
|
|
+ },
|
|
|
+ AssertNode {
|
|
|
+ path: vec![1, 1, 0].into(),
|
|
|
+ expected: Some(text_node),
|
|
|
+ },
|
|
|
+ ];
|
|
|
+ test.run_scripts(scripts);
|
|
|
+}
|
|
|
+#[test]
|
|
|
+fn operation_insert_node_when_multiple_parent_is_not_exist_test() {
|
|
|
+ let mut test = NodeTest::new();
|
|
|
+ let text_1 = NodeDataBuilder::new("text_1").build();
|
|
|
+ let text_2 = NodeDataBuilder::new("text_2").build();
|
|
|
+
|
|
|
+ let path = vec![1, 0, 0, 0, 0, 0];
|
|
|
+ let mut auto_fill_node = placeholder_node();
|
|
|
+ let mut iter_node: &mut NodeData = &mut auto_fill_node;
|
|
|
+ let insert_path = path.split_at(1).1;
|
|
|
+ for (index, _) in insert_path.iter().enumerate() {
|
|
|
+ if index == insert_path.len() - 1 {
|
|
|
+ iter_node.children.push(text_2.clone());
|
|
|
+ } else {
|
|
|
+ iter_node.children.push(placeholder_node());
|
|
|
+ iter_node = iter_node.children.last_mut().unwrap();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ let scripts = vec![
|
|
|
+ InsertNode {
|
|
|
+ path: 0.into(),
|
|
|
+ node_data: text_1,
|
|
|
+ rev_id: 1,
|
|
|
+ },
|
|
|
+ InsertNode {
|
|
|
+ path: path.clone().into(),
|
|
|
+ node_data: text_2.clone(),
|
|
|
+ rev_id: 2,
|
|
|
+ },
|
|
|
+ AssertNode {
|
|
|
+ path: vec![1].into(),
|
|
|
+ expected: Some(auto_fill_node),
|
|
|
+ },
|
|
|
+ AssertNode {
|
|
|
+ path: path.into(),
|
|
|
+ expected: Some(text_2),
|
|
|
+ },
|
|
|
+ ];
|
|
|
+ test.run_scripts(scripts);
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn operation_insert_node_when_multiple_parent_is_not_exist_test2() {
|
|
|
+ let mut test = NodeTest::new();
|
|
|
+ // 0:ghost
|
|
|
+ // 0:ghost
|
|
|
+ // 1:ghost
|
|
|
+ // 0:text
|
|
|
+ let mut text_node_parent = placeholder_node();
|
|
|
+ let text_node = NodeDataBuilder::new("text").build();
|
|
|
+ text_node_parent.children.push(text_node.clone());
|
|
|
+
|
|
|
+ let mut ghost = placeholder_node();
|
|
|
+ ghost.children.push(placeholder_node());
|
|
|
+ ghost.children.push(text_node_parent.clone());
|
|
|
+
|
|
|
+ let path = vec![1, 1, 0];
|
|
|
+ let scripts = vec![
|
|
|
+ InsertNode {
|
|
|
+ path: path.into(),
|
|
|
+ node_data: text_node.clone(),
|
|
|
+ rev_id: 1,
|
|
|
+ },
|
|
|
+ // 0:ghost
|
|
|
+ // 1:ghost
|
|
|
+ // 0:ghost
|
|
|
+ // 1:ghost
|
|
|
+ // 0:text
|
|
|
+ AssertNode {
|
|
|
+ path: 0.into(),
|
|
|
+ expected: Some(placeholder_node()),
|
|
|
+ },
|
|
|
+ AssertNode {
|
|
|
+ path: 1.into(),
|
|
|
+ expected: Some(ghost),
|
|
|
+ },
|
|
|
+ AssertNumberOfChildrenAtPath {
|
|
|
+ path: Some(1.into()),
|
|
|
+ expected: 2,
|
|
|
+ },
|
|
|
+ AssertNode {
|
|
|
+ path: vec![1, 1].into(),
|
|
|
+ expected: Some(text_node_parent),
|
|
|
+ },
|
|
|
+ AssertNode {
|
|
|
+ path: vec![1, 1, 0].into(),
|
|
|
+ expected: Some(text_node),
|
|
|
+ },
|
|
|
+ ];
|
|
|
+ test.run_scripts(scripts);
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn operation_insert_node_when_multiple_parent_is_not_exist_test3() {
|
|
|
+ let mut test = NodeTest::new();
|
|
|
+ let text_node = NodeDataBuilder::new("text").build();
|
|
|
+ let path = vec![3, 3, 0];
|
|
|
+ let scripts = vec![
|
|
|
+ InsertNode {
|
|
|
+ path: path.clone().into(),
|
|
|
+ node_data: text_node.clone(),
|
|
|
+ rev_id: 1,
|
|
|
+ },
|
|
|
+ // 0:ghost
|
|
|
+ // 1:ghost
|
|
|
+ // 2:ghost
|
|
|
+ // 3:ghost
|
|
|
+ // 0:ghost
|
|
|
+ // 1:ghost
|
|
|
+ // 2:ghost
|
|
|
+ // 3:ghost
|
|
|
+ // 0:text
|
|
|
+ AssertNode {
|
|
|
+ path: 0.into(),
|
|
|
+ expected: Some(placeholder_node()),
|
|
|
+ },
|
|
|
+ AssertNode {
|
|
|
+ path: 1.into(),
|
|
|
+ expected: Some(placeholder_node()),
|
|
|
+ },
|
|
|
+ AssertNode {
|
|
|
+ path: 2.into(),
|
|
|
+ expected: Some(placeholder_node()),
|
|
|
+ },
|
|
|
+ AssertNumberOfChildrenAtPath {
|
|
|
+ path: Some(3.into()),
|
|
|
+ expected: 4,
|
|
|
+ },
|
|
|
+ AssertNode {
|
|
|
+ path: path.into(),
|
|
|
+ expected: Some(text_node),
|
|
|
+ },
|
|
|
+ ];
|
|
|
+ test.run_scripts(scripts);
|
|
|
+}
|