/**
* Replication Benchmarker
* https://github.com/score-team/replication-benchmarker/
* Copyright (C) 2013 LORIA / Inria / SCORE Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package crdt.tree;
import crdt.CRDTMessage;
import collect.HashTree;
import crdt.PreconditionException;
import collect.Tree;
import collect.Node;
import crdt.Factory;
import static org.junit.Assert.*;
/**
*
* @author score
*/
public class CrdtTreeGeneric<T> {
final static Tree basicTree = new HashTree();
final static Node a = basicTree.add(null, 'a'),
b = basicTree.add(null, 'b'),
c = basicTree.add(null, 'c'),
x = basicTree.add(b, 'x'),
y = basicTree.add(b, 'y'),
z = basicTree.add(c, 'z');
public CRDTMessage populateTreeABC(CRDTUnorderedTree crdttree) throws PreconditionException {
CRDTMessage ABC = crdttree.add(crdttree.getRoot(), 'a');
ABC = ABC.concat(crdttree.add(crdttree.getRoot(), 'b'));
ABC = ABC.concat(crdttree.add(crdttree.getRoot(), 'c'));
return ABC;
}
public CRDTMessage populateTreeXYZ(CRDTUnorderedTree crdttree) throws PreconditionException {
CRDTMessage XYZ = crdttree.add(crdttree.getNode('b'), 'x');
XYZ = XYZ.concat(crdttree.add(crdttree.getNode('b'), 'y'));
XYZ = XYZ.concat(crdttree.add(crdttree.getNode('c'), 'z'));
return XYZ;
}
/**
* Runs all basic test (without return)
*
* @param treeFactory a CRDT tree factory
* @throws Exception if test fails
*/
public void runAllBasic(Factory<CRDTUnorderedTree> treeFactory) throws Exception {
this.testAdd(treeFactory.create());
this.testRemove(treeFactory.create());
this.testApplyRemoteAdd(treeFactory.create(), treeFactory.create());
this.testAddConcurDiffFather(treeFactory.create(), treeFactory.create());
this.testAddConcurSameFather(treeFactory.create(), treeFactory.create());
this.testRmvConcurDiffFather(treeFactory.create(), treeFactory.create());
this.testRmvConcurSameFather(treeFactory.create(), treeFactory.create());
this.testConcuAddRmvDiffFather(treeFactory.create(), treeFactory.create());
}
// Simple Add
public void testAdd(CRDTUnorderedTree crdttree) throws Exception {
//creatTree
populateTreeABC(crdttree);
populateTreeXYZ(crdttree);
//tree to compare
assertEquals(basicTree, crdttree.lookup());
}
// Add + remove
public void testRemove(CRDTUnorderedTree crdttree) throws Exception {
//creatTree
populateTreeABC(crdttree);
populateTreeXYZ(crdttree);
//simple remove
//tree to compare
Tree tree = new HashTree();
tree.add(null, 'a');
tree.add(tree.add(null, 'c'), 'z');
crdttree.remove(crdttree.getNode('b'));
assertEquals(tree, crdttree.lookup());
}
// Add 1 -> 2
public void testApplyRemoteAdd(CRDTUnorderedTree crdttree1, CRDTUnorderedTree crdttree2) throws Exception {
/*
* crdttree1.setReplicaNumber(0);
crdttree2.setReplicaNumber(1);
*/
//TestApply add sequentiel
crdttree2.applyRemote(populateTreeABC(crdttree1));
crdttree2.applyRemote(populateTreeXYZ(crdttree1));
//tree to compare
assertEquals(basicTree, crdttree1.lookup());
assertEquals(crdttree2.lookup(), crdttree1.lookup());
}
// Concurrent insertion under two diffrent father
public void testAddConcurDiffFather(CRDTUnorderedTree crdttree1, CRDTUnorderedTree crdttree2) throws Exception {
crdttree2.applyRemote(populateTreeABC(crdttree1));
//Add concurrently
CRDTMessage m1 = crdttree2.add(crdttree2.getNode('b'), 'x'),//in crdtTree2
m2 = crdttree2.add(crdttree2.getNode('b'), 'y'),//in crdtTree2
m3 = crdttree1.add(crdttree1.getNode('c'), 'z');//in crdtTree1
m1 = m1.concat(m2);
crdttree1.applyRemote(m1);
crdttree2.applyRemote(m3);
//tree to compare
assertEquals(basicTree, crdttree1.lookup());
assertEquals(crdttree2.lookup(), crdttree1.lookup());
}
/*
* Concurrently insertion under same father crdt1 add 'x' and 'y' under
* crdttree.getNode('b') and crdt2 add 'z' under crdttree.getNode('c')
* concurrently
*/
public void testAddConcurSameFather(CRDTUnorderedTree crdttree1, CRDTUnorderedTree crdttree2) throws Exception {
crdttree2.applyRemote(populateTreeABC(crdttree1));
//Add concurrently
CRDTMessage m1 = crdttree2.add(crdttree2.getNode('b'), 'x'),//in crdtTree2
m2 = crdttree1.add(crdttree1.getNode('b'), 'y'),//in crdtTree2
m3 = crdttree2.add(crdttree2.getNode('c'), 'z');//in crdtTree1
m1 = m1.concat(m3);
crdttree1.applyRemote(m1);
crdttree2.applyRemote(m2);
assertEquals(basicTree, crdttree1.lookup());
assertEquals(crdttree2.lookup(), crdttree1.lookup());
}
/*
* delete concurrently under two different father crdttree1 delete
* crdttree.getNode('b') and crdttree2 delete crdttree.getNode('c')
* concurrently result is juste root --> a
*/
public void testRmvConcurDiffFather(CRDTUnorderedTree crdttree1, CRDTUnorderedTree crdttree2) throws Exception {
crdttree2.applyRemote(populateTreeABC(crdttree1));
crdttree2.applyRemote(populateTreeXYZ(crdttree1));
//del concurrently
CRDTMessage m1 = crdttree2.remove(crdttree2.getNode('b'));//in crdtTree2
CRDTMessage m2 = crdttree1.remove(crdttree1.getNode('c'));//in crdtTree1
crdttree1.applyRemote(m1);
crdttree2.applyRemote(m2);
//tree to compare
Tree tree = new HashTree();
tree.add(null, 'a');
assertEquals(tree, crdttree1.lookup());
assertEquals(crdttree2.lookup(), crdttree1.lookup());
}
/*
* delete concurrently the same element under same father both crdt delete
* crdttree.getNode('b') concurrently
*/
public void testRmvConcurSameFather(CRDTUnorderedTree crdttree1, CRDTUnorderedTree crdttree2) throws Exception {
crdttree2.applyRemote(populateTreeABC(crdttree1));
crdttree2.applyRemote(populateTreeXYZ(crdttree1));
//del concurrently
CRDTMessage m1 = crdttree1.remove(crdttree1.getNode('b'));//in crdtTree1
CRDTMessage m2 = crdttree2.remove(crdttree2.getNode('b'));//in crdtTree2
crdttree1.applyRemote(m2);
crdttree2.applyRemote(m1);
//tree to compare
Tree tree = new HashTree();
tree.add(null, 'a');
Node nodeC = tree.add(null, 'c');
tree.add(nodeC, 'z');
assertEquals(tree, crdttree1.lookup());
assertEquals(crdttree2.lookup(), crdttree1.lookup());
}
/*
* Add/delete concurrently under two different father crdt1 remove
* crdttree.getNode('c') when crdt2 add 'm' under crdttree.getNode('b')
*/
public void testConcuAddRmvDiffFather(CRDTUnorderedTree crdttree1, CRDTUnorderedTree crdttree2) throws Exception {
crdttree2.applyRemote(populateTreeABC(crdttree1));
crdttree2.applyRemote(populateTreeXYZ(crdttree1));
//del concurrently
CRDTMessage m1 = crdttree1.remove(crdttree1.getNode('c'));//in crdtTree1
CRDTMessage m2 = crdttree2.add(crdttree2.getNode('b'), 'm');//in crdtTree2
crdttree1.applyRemote(m2);
crdttree2.applyRemote(m1);
//tree to compare
Tree tree = new HashTree();
tree.add(null, 'a');
Node nodeB = tree.add(null, 'b');
tree.add(nodeB, 'x');
tree.add(nodeB, 'y');
tree.add(nodeB, 'm');
assertEquals(tree, crdttree1.lookup());
assertEquals(crdttree2.lookup(), crdttree1.lookup());
}
/*
* Add/delete father crdt2 add 'k' under crdttree.getNode('b') when crdt1
* delete crdttree.getNode('b')
*/
public Tree testConcurAddRmvFather(CRDTUnorderedTree crdttree1, CRDTUnorderedTree crdttree2) throws Exception {
crdttree2.applyRemote(populateTreeABC(crdttree1));
crdttree2.applyRemote(populateTreeXYZ(crdttree1));
//del concurrently
CRDTMessage m1 = crdttree1.remove(crdttree1.getNode('b'));//delete father of y in crdtTree1
CRDTMessage m2 = crdttree2.add(crdttree2.getNode('b', 'x'), 'k');//insertion y in crdtTree2
crdttree1.applyRemote(m2);
crdttree2.applyRemote(m1);
assertEquals(crdttree2.lookup(), crdttree1.lookup());
return (Tree) crdttree1.lookup();
}
/*
* concurrent Addition and delete of the same element crdt1 insert 'x' under
* crdttree.getNode('b') ther delete it, crdt2 add crdttree.getNode('b')
* concurrently
*/
public Tree testConcurAddRmvSameElement(CRDTUnorderedTree crdttree1, CRDTUnorderedTree crdttree2) throws Exception {
crdttree2.applyRemote(populateTreeABC(crdttree1));
CRDTMessage m1 = crdttree1.add(crdttree1.getNode('b'), 'y');
CRDTMessage m2 = crdttree1.add(crdttree1.getNode('c'), 'z');
m1 = m1.concat(m2);
crdttree2.applyRemote(m1);
//del concurrently
CRDTMessage m3 = crdttree1.add(crdttree1.getNode('b'), 'x');
CRDTMessage m4 = crdttree2.add(crdttree2.getNode('b'), 'x');//insertion x under B2
CRDTMessage m5 = crdttree1.remove(crdttree1.getNode('b', 'x'));//delete x
m3 = m3.concat(m5);
crdttree1.applyRemote(m4);
crdttree2.applyRemote(m3);
assertEquals(crdttree2.lookup(), crdttree1.lookup());
return (Tree) crdttree1.lookup();
}
/*
* Creat two path diffrent for same element crdt1 add x under Y and
* concurrently crdt2 add x under A degre(x) from A = 2 and degre(x) from
* crdttree.getNode('b') = 3
*/
public Tree testTwoPath(CRDTUnorderedTree crdttree1, CRDTUnorderedTree crdttree2) throws Exception {
crdttree2.applyRemote(populateTreeABC(crdttree1));
CRDTMessage m1 = crdttree1.add(crdttree1.getNode('b'), 'y');
CRDTMessage m2 = crdttree1.add(crdttree1.getNode('c'), 'z');
m1 = m1.concat(m2);
crdttree2.applyRemote(m1);
//add concurrently
CRDTMessage m3 = crdttree1.add(crdttree1.getNode('b', 'y'), 'x');//insertion x under crdttree.getNode('b')
CRDTMessage m4 = crdttree2.add(crdttree2.getNode('a'), 'x');//insertion x under crdttree.getNode('c')
crdttree1.applyRemote(m4);
crdttree2.applyRemote(m3);
assertEquals(crdttree2.lookup(), crdttree1.lookup());
return (Tree) crdttree1.lookup();
}
/*
* Creat Cycle crdt1 add x under y and concurrently crdt2 add y under x
*/
public Tree testCycle(CRDTUnorderedTree crdttree1, CRDTUnorderedTree crdttree2) throws Exception {
crdttree2.applyRemote(populateTreeABC(crdttree1));
CRDTMessage m1 = crdttree1.add(crdttree1.getNode('b'), 'y');
CRDTMessage m2 = crdttree1.add(crdttree1.getNode('b', 'y'), 'x');
m1 = m1.concat(m2);
CRDTMessage m3 = crdttree2.add(crdttree2.getNode('b'), 'x');
CRDTMessage m4 = crdttree2.add(crdttree2.getNode('b', 'x'), 'y');
m3 = m3.concat(m4);
crdttree2.applyRemote(m1);
crdttree1.applyRemote(m3);
assertEquals(crdttree2.lookup(), crdttree1.lookup());
return (Tree) crdttree1.lookup();
}
/**
* Add/delete father crdt2 add 'k' under crdttree.getNode('b') when crdt1
* delete crdttree.getNode('b') then readd father
*/
public void testAdopt(CRDTUnorderedTree crdttree1, CRDTUnorderedTree crdttree2) throws Exception {
CrdtTreeGeneric test = new CrdtTreeGeneric();
crdttree2.applyRemote(test.populateTreeABC(crdttree1));
crdttree2.applyRemote(test.populateTreeXYZ(crdttree1));
//del concurrently
CRDTMessage m1 = crdttree1.remove(crdttree1.getNode('b'));//delete father of x in crdtTree1
CRDTMessage m2 = crdttree2.add(crdttree2.getNode('b', 'x'), 'k');//insertion k in crdtTree2
m2 = m2.concat(crdttree2.add(crdttree2.getNode('b', 'x', 'k'), 'f'));//insertion z in crdtTree2
crdttree1.applyRemote(m2);
crdttree2.applyRemote(m1);
CRDTMessage m3 = crdttree2.add(crdttree2.getRoot(), 'b');
m3 = m3.concat(crdttree2.add(crdttree2.getNode('b'), 'x')); //reinsertion crdttree.getNode('b') and x in crdtTree2
crdttree1.applyRemote(m3);
Tree tree = new HashTree();
tree.add(null, 'a');
Node b2 = tree.add(null, 'b');
Node c2 = tree.add(null, 'c');
Node x2 = tree.add(b2, 'x');
tree.add(tree.add(x2, 'k'), 'f');
tree.add(c2, 'z');
assertEquals(tree, crdttree1.lookup());
assertEquals(tree, crdttree2.lookup());
}
}