gossip-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ecapri...@apache.org
Subject [1/2] incubator-gossip git commit: GOSSIP-66 Implement Crdt 2P-Set
Date Mon, 17 Jul 2017 22:58:10 GMT
Repository: incubator-gossip
Updated Branches:
  refs/heads/master 6ef0eb788 -> 95cce48a8


GOSSIP-66 Implement Crdt 2P-Set


Project: http://git-wip-us.apache.org/repos/asf/incubator-gossip/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-gossip/commit/89af0ac1
Tree: http://git-wip-us.apache.org/repos/asf/incubator-gossip/tree/89af0ac1
Diff: http://git-wip-us.apache.org/repos/asf/incubator-gossip/diff/89af0ac1

Branch: refs/heads/master
Commit: 89af0ac11289e7448a00382a0a93c460d9bfce5c
Parents: f71460a
Author: Maxim Rusak <mak-rusak@yandex.ru>
Authored: Fri Jun 30 10:15:26 2017 +0300
Committer: Maxim Rusak <mak-rusak@yandex.ru>
Committed: Fri Jun 30 10:16:22 2017 +0300

----------------------------------------------------------------------
 .../java/org/apache/gossip/crdt/CrdtModule.java |   8 ++
 .../org/apache/gossip/crdt/TwoPhaseSet.java     | 115 ++++++++++++++++
 .../gossip/crdt/AbstractCRDTStringSetTest.java  | 133 ------------------
 .../gossip/crdt/AddRemoveStringSetTest.java     | 137 +++++++++++++++++++
 .../java/org/apache/gossip/crdt/LwwSetTest.java |   2 +-
 .../apache/gossip/crdt/MaxChangeSetTest.java    |   2 +-
 .../java/org/apache/gossip/crdt/OrSetTest.java  |   2 +-
 .../org/apache/gossip/crdt/TwoPhaseSetTest.java | 101 ++++++++++++++
 .../test/java/org/apache/gossip/DataTest.java   |   6 +
 .../gossip/protocol/json/JacksonTest.java       |  12 +-
 10 files changed, 379 insertions(+), 139 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-gossip/blob/89af0ac1/gossip-base/src/main/java/org/apache/gossip/crdt/CrdtModule.java
----------------------------------------------------------------------
diff --git a/gossip-base/src/main/java/org/apache/gossip/crdt/CrdtModule.java b/gossip-base/src/main/java/org/apache/gossip/crdt/CrdtModule.java
index 7ec96e7..ab5cefa 100644
--- a/gossip-base/src/main/java/org/apache/gossip/crdt/CrdtModule.java
+++ b/gossip-base/src/main/java/org/apache/gossip/crdt/CrdtModule.java
@@ -54,6 +54,13 @@ abstract class MaxChangeSetMixin<E> {
   @JsonProperty("data") abstract Map<E, Integer> getStruct();
 }
 
+abstract class TwoPhaseSetMixin<E> {
+  @JsonCreator
+  TwoPhaseSetMixin(@JsonProperty("added") Set<E> added, @JsonProperty("removed") Set<E>
removed) { }
+  @JsonProperty("added") abstract Set<E> getAdded();
+  @JsonProperty("removed") abstract Set<E> getRemoved();
+}
+
 abstract class GrowOnlySetMixin<E>{
   @JsonCreator
   GrowOnlySetMixin(@JsonProperty("elements") Set<E> elements){ }
@@ -93,6 +100,7 @@ public class CrdtModule extends SimpleModule {
     context.setMixInAnnotations(LwwSet.class, LWWSetMixin.class);
     context.setMixInAnnotations(LwwSet.Timestamps.class, LWWSetTimestampsMixin.class);
     context.setMixInAnnotations(MaxChangeSet.class, MaxChangeSetMixin.class);
+    context.setMixInAnnotations(TwoPhaseSet.class, TwoPhaseSetMixin.class);
   }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-gossip/blob/89af0ac1/gossip-base/src/main/java/org/apache/gossip/crdt/TwoPhaseSet.java
----------------------------------------------------------------------
diff --git a/gossip-base/src/main/java/org/apache/gossip/crdt/TwoPhaseSet.java b/gossip-base/src/main/java/org/apache/gossip/crdt/TwoPhaseSet.java
new file mode 100644
index 0000000..a1f44a9
--- /dev/null
+++ b/gossip-base/src/main/java/org/apache/gossip/crdt/TwoPhaseSet.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.gossip.crdt;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.function.BiFunction;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/*
+  Two-Phase CrdtSet.
+  You can add element only once and remove only once.
+  You cannot remove element which is not present.
+
+  Read more: https://github.com/aphyr/meangirls#2p-set
+  You can view examples of usage in tests:
+  TwoPhaseSetTest - unit tests
+  DataTest - integration test with 2 nodes, TwoPhaseSet was serialized/deserialized, sent
between nodes, merged
+*/
+
+public class TwoPhaseSet<ElementType> implements CrdtAddRemoveSet<ElementType, Set<ElementType>,
TwoPhaseSet<ElementType>> {
+  private final Set<ElementType> added;
+  private final Set<ElementType> removed;
+
+  public TwoPhaseSet(){
+    added = new HashSet<>();
+    removed = new HashSet<>();
+  }
+
+  @SafeVarargs
+  public TwoPhaseSet(ElementType... elements){
+    this(new HashSet<>(Arrays.asList(elements)));
+  }
+
+  public TwoPhaseSet(Set<ElementType> set){
+    this();
+    for (ElementType e : set){
+      added.add(e);
+    }
+  }
+
+  public TwoPhaseSet(TwoPhaseSet<ElementType> first, TwoPhaseSet<ElementType>
second){
+    BiFunction<Set<ElementType>, Set<ElementType>, Set<ElementType>>
mergeSets = (f, s) ->
+        Stream.concat(f.stream(), s.stream()).collect(Collectors.toSet());
+
+    added = mergeSets.apply(first.added, second.added);
+    removed = mergeSets.apply(first.removed, second.removed);
+  }
+
+  TwoPhaseSet(Set<ElementType> added, Set<ElementType> removed){
+    this.added = added;
+    this.removed = removed;
+  }
+
+  Set<ElementType> getAdded(){
+    return added;
+  }
+
+  Set<ElementType> getRemoved(){
+    return removed;
+  }
+
+  public TwoPhaseSet<ElementType> add(ElementType e){
+    if (removed.contains(e) || added.contains(e)){
+      return this;
+    }
+    return this.merge(new TwoPhaseSet<>(e));
+  }
+
+  public TwoPhaseSet<ElementType> remove(ElementType e){
+    if (removed.contains(e) || !added.contains(e)){
+      return this;
+    }
+    Set<ElementType> eSet = new HashSet<>(Collections.singletonList(e));
+    return this.merge(new TwoPhaseSet<>(eSet, eSet));
+  }
+
+  @Override
+  public TwoPhaseSet<ElementType> merge(TwoPhaseSet<ElementType> other){
+    return new TwoPhaseSet<>(this, other);
+  }
+
+  @Override
+  public Set<ElementType> value(){
+    return added.stream().filter(e -> !removed.contains(e)).collect(Collectors.toSet());
+  }
+
+  @Override
+  public TwoPhaseSet<ElementType> optimize(){
+    return new TwoPhaseSet<>(value(), removed);
+  }
+
+  @Override
+  public boolean equals(Object obj){
+    return this == obj || (obj != null && getClass() == obj.getClass() &&
value().equals(((TwoPhaseSet) obj).value()));
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-gossip/blob/89af0ac1/gossip-base/src/test/java/org/apache/gossip/crdt/AbstractCRDTStringSetTest.java
----------------------------------------------------------------------
diff --git a/gossip-base/src/test/java/org/apache/gossip/crdt/AbstractCRDTStringSetTest.java
b/gossip-base/src/test/java/org/apache/gossip/crdt/AbstractCRDTStringSetTest.java
deleted file mode 100644
index d4db4ce..0000000
--- a/gossip-base/src/test/java/org/apache/gossip/crdt/AbstractCRDTStringSetTest.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unle<F4>ss required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.gossip.crdt;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.Ignore;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-/*
-  Abstract test suit to test CrdtSets with Add and Remove operations.
-  It compares them with simple sets, validates add, remove, equals, value, etc. operations
-  To use it you should:
-  1. subclass this and implement constructors
-  2. implement CrdtAddRemoveSet in your CrdtSet
-  3. make your CrdtSet immutable
-*/
-
-@Ignore
-public abstract class AbstractCRDTStringSetTest<SetType extends CrdtAddRemoveSet<String,
Set<String>, SetType>> {
-  abstract SetType construct(Set<String> set);
-
-  abstract SetType construct();
-
-  private Set<String> sampleSet;
-
-  @Before
-  public void setup(){
-    sampleSet = new HashSet<>();
-    sampleSet.add("4");
-    sampleSet.add("5");
-    sampleSet.add("12");
-  }
-
-  @Test
-  public void abstractSetConstructorTest(){
-    Assert.assertEquals(construct(sampleSet).value(), sampleSet);
-  }
-
-  @Test
-  public void abstractStressWithSetTest(){
-    Set<String> hashSet = new HashSet<>();
-    SetType set = construct();
-    for (int it = 0; it < 40; it++){
-      SetType newSet;
-      if (it % 5 == 1){
-        //deleting existing
-        String forDelete = hashSet.stream().skip((long) (hashSet.size() * Math.random())).findFirst().get();
-        newSet = set.remove(forDelete);
-        Assert.assertEquals(set.value(), hashSet); // check old version is immutable
-        hashSet.remove(forDelete);
-      } else {
-        //adding
-        String forAdd = String.valueOf((int) (10000 * Math.random()));
-        newSet = set.add(forAdd);
-        Assert.assertEquals(set.value(), hashSet); // check old version is immutable
-        hashSet.add(forAdd);
-      }
-      set = newSet;
-      Assert.assertEquals(set.value(), hashSet);
-    }
-  }
-
-  @Test
-  public void abstractEqualsTest(){
-    SetType set = construct(sampleSet);
-    Assert.assertFalse(set.equals(sampleSet));
-    SetType newSet = set.add("25");
-    sampleSet.add("25");
-    Assert.assertFalse(newSet.equals(set));
-    Assert.assertEquals(construct(sampleSet), newSet);
-  }
-
-  @Test
-  public void abstractRemoveMissingTest(){
-    SetType set = construct(sampleSet);
-    set = set.add("25");
-    set = set.remove("25");
-    Assert.assertEquals(set.value(), sampleSet);
-    set = set.remove("25");
-    set = set.add("25");
-    sampleSet.add("25");
-    Assert.assertEquals(set.value(), sampleSet);
-  }
-
-  @Test
-  public void abstractStressMergeTest(){
-    // in one-process context, add, remove and merge operations of lww are equal to operations
of Set
-    // we've already checked it. Now just check merge
-    Set<String> hashSet1 = new HashSet<>(), hashSet2 = new HashSet<>();
-    SetType set1 = construct(), set2 = construct();
-
-    for (int it = 0; it < 100; it++){
-      String forAdd = String.valueOf((int) (10000 * Math.random()));
-      if (it % 2 == 0){
-        hashSet1.add(forAdd);
-        set1 = set1.add(forAdd);
-      } else {
-        hashSet2.add(forAdd);
-        set2 = set2.add(forAdd);
-      }
-    }
-    Assert.assertEquals(set1.value(), hashSet1);
-    Assert.assertEquals(set2.value(), hashSet2);
-    Set<String> mergedSet = Stream.concat(hashSet1.stream(), hashSet2.stream()).collect(Collectors.toSet());
-    Assert.assertEquals(set1.merge(set2).value(), mergedSet);
-  }
-
-  @Test
-  public void abstractOptimizeTest(){
-    Assert.assertEquals(construct(sampleSet).value(), sampleSet);
-    Assert.assertEquals(construct(sampleSet).optimize().value(), sampleSet);
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-gossip/blob/89af0ac1/gossip-base/src/test/java/org/apache/gossip/crdt/AddRemoveStringSetTest.java
----------------------------------------------------------------------
diff --git a/gossip-base/src/test/java/org/apache/gossip/crdt/AddRemoveStringSetTest.java
b/gossip-base/src/test/java/org/apache/gossip/crdt/AddRemoveStringSetTest.java
new file mode 100644
index 0000000..6dac9df
--- /dev/null
+++ b/gossip-base/src/test/java/org/apache/gossip/crdt/AddRemoveStringSetTest.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unle<F4>ss required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.gossip.crdt;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/*
+  Abstract test suit to test CrdtSets with Add and Remove operations.
+  You can use this suite only if your set supports multiple additions/deletions
+    and has behavior similar to Set in single-threaded environment.
+  It compares them with simple sets, validates add, remove, equals, value, etc. operations
+  To use it you should:
+  1. subclass this and implement constructors
+  2. implement CrdtAddRemoveSet in your CrdtSet
+  3. make your CrdtSet immutable
+*/
+
+@Ignore
+public abstract class AddRemoveStringSetTest<SetType extends CrdtAddRemoveSet<String,
Set<String>, SetType>> {
+
+  abstract SetType construct(Set<String> set);
+
+  abstract SetType construct();
+
+  private Set<String> sampleSet;
+
+  @Before
+  public void setup(){
+    sampleSet = new HashSet<>();
+    sampleSet.add("4");
+    sampleSet.add("5");
+    sampleSet.add("12");
+  }
+
+  @Test
+  public void abstractSetConstructorTest(){
+    Assert.assertEquals(construct(sampleSet).value(), sampleSet);
+  }
+
+  @Test
+  public void abstractStressWithSetTest(){
+    Set<String> hashSet = new HashSet<>();
+    SetType set = construct();
+    for (int it = 0; it < 40; it++){
+      SetType newSet;
+      if (it % 5 == 1){
+        //deleting existing
+        String forDelete = hashSet.stream().skip((long) (hashSet.size() * Math.random())).findFirst().get();
+        newSet = set.remove(forDelete);
+        Assert.assertEquals(set.value(), hashSet); // check old version is immutable
+        hashSet.remove(forDelete);
+      } else {
+        //adding
+        String forAdd = String.valueOf((int) (10000 * Math.random()));
+        newSet = set.add(forAdd);
+        Assert.assertEquals(set.value(), hashSet); // check old version is immutable
+        hashSet.add(forAdd);
+      }
+      set = newSet;
+      Assert.assertEquals(set.value(), hashSet);
+    }
+  }
+
+  @Test
+  public void abstractEqualsTest(){
+    SetType set = construct(sampleSet);
+    Assert.assertFalse(set.equals(sampleSet));
+    SetType newSet = set.add("25");
+    sampleSet.add("25");
+    Assert.assertFalse(newSet.equals(set));
+    Assert.assertEquals(construct(sampleSet), newSet);
+  }
+
+  @Test
+  public void abstractRemoveMissingTest(){
+    SetType set = construct(sampleSet);
+    set = set.add("25");
+    set = set.remove("25");
+    Assert.assertEquals(set.value(), sampleSet);
+    set = set.remove("25");
+    set = set.add("25");
+    sampleSet.add("25");
+    Assert.assertEquals(set.value(), sampleSet);
+  }
+
+  @Test
+  public void abstractStressMergeTest(){
+    // in one-process context, add, remove and merge operations of lww are equal to operations
of Set
+    // we've already checked it. Now just check merge
+    Set<String> hashSet1 = new HashSet<>(), hashSet2 = new HashSet<>();
+    SetType set1 = construct(), set2 = construct();
+
+    for (int it = 0; it < 100; it++){
+      String forAdd = String.valueOf((int) (10000 * Math.random()));
+      if (it % 2 == 0){
+        hashSet1.add(forAdd);
+        set1 = set1.add(forAdd);
+      } else {
+        hashSet2.add(forAdd);
+        set2 = set2.add(forAdd);
+      }
+    }
+    Assert.assertEquals(set1.value(), hashSet1);
+    Assert.assertEquals(set2.value(), hashSet2);
+    Set<String> mergedSet = Stream.concat(hashSet1.stream(), hashSet2.stream()).collect(Collectors.toSet());
+    Assert.assertEquals(set1.merge(set2).value(), mergedSet);
+  }
+
+  @Test
+  public void abstractOptimizeTest(){
+    Assert.assertEquals(construct(sampleSet).value(), sampleSet);
+    Assert.assertEquals(construct(sampleSet).optimize().value(), sampleSet);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-gossip/blob/89af0ac1/gossip-base/src/test/java/org/apache/gossip/crdt/LwwSetTest.java
----------------------------------------------------------------------
diff --git a/gossip-base/src/test/java/org/apache/gossip/crdt/LwwSetTest.java b/gossip-base/src/test/java/org/apache/gossip/crdt/LwwSetTest.java
index 8200b15..c4da83d 100644
--- a/gossip-base/src/test/java/org/apache/gossip/crdt/LwwSetTest.java
+++ b/gossip-base/src/test/java/org/apache/gossip/crdt/LwwSetTest.java
@@ -27,7 +27,7 @@ import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 
-public class LwwSetTest extends AbstractCRDTStringSetTest<LwwSet<String>> {
+public class LwwSetTest extends AddRemoveStringSetTest<LwwSet<String>> {
   static private Clock clock = new SystemClock();
 
   LwwSet<String> construct(Set<String> set){

http://git-wip-us.apache.org/repos/asf/incubator-gossip/blob/89af0ac1/gossip-base/src/test/java/org/apache/gossip/crdt/MaxChangeSetTest.java
----------------------------------------------------------------------
diff --git a/gossip-base/src/test/java/org/apache/gossip/crdt/MaxChangeSetTest.java b/gossip-base/src/test/java/org/apache/gossip/crdt/MaxChangeSetTest.java
index 2ba3f09..3828747 100644
--- a/gossip-base/src/test/java/org/apache/gossip/crdt/MaxChangeSetTest.java
+++ b/gossip-base/src/test/java/org/apache/gossip/crdt/MaxChangeSetTest.java
@@ -25,7 +25,7 @@ import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 
-public class MaxChangeSetTest extends AbstractCRDTStringSetTest<MaxChangeSet<String>>
{
+public class MaxChangeSetTest extends AddRemoveStringSetTest<MaxChangeSet<String>>
{
   MaxChangeSet<String> construct(Set<String> set){
     return new MaxChangeSet<>(set);
   }

http://git-wip-us.apache.org/repos/asf/incubator-gossip/blob/89af0ac1/gossip-base/src/test/java/org/apache/gossip/crdt/OrSetTest.java
----------------------------------------------------------------------
diff --git a/gossip-base/src/test/java/org/apache/gossip/crdt/OrSetTest.java b/gossip-base/src/test/java/org/apache/gossip/crdt/OrSetTest.java
index bdaada9..8b21360 100644
--- a/gossip-base/src/test/java/org/apache/gossip/crdt/OrSetTest.java
+++ b/gossip-base/src/test/java/org/apache/gossip/crdt/OrSetTest.java
@@ -25,7 +25,7 @@ import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeSet;
 
-public class OrSetTest extends AbstractCRDTStringSetTest<OrSet<String>> {
+public class OrSetTest extends AddRemoveStringSetTest<OrSet<String>> {
   OrSet<String> construct(){
     return new OrSet<>();
   }

http://git-wip-us.apache.org/repos/asf/incubator-gossip/blob/89af0ac1/gossip-base/src/test/java/org/apache/gossip/crdt/TwoPhaseSetTest.java
----------------------------------------------------------------------
diff --git a/gossip-base/src/test/java/org/apache/gossip/crdt/TwoPhaseSetTest.java b/gossip-base/src/test/java/org/apache/gossip/crdt/TwoPhaseSetTest.java
new file mode 100644
index 0000000..3af1920
--- /dev/null
+++ b/gossip-base/src/test/java/org/apache/gossip/crdt/TwoPhaseSetTest.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.gossip.crdt;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.function.BiConsumer;
+
+public class TwoPhaseSetTest {
+
+  private Set<String> sampleSet;
+
+  @Before
+  public void setup(){
+    sampleSet = new HashSet<>();
+    sampleSet.add("a");
+    sampleSet.add("b");
+    sampleSet.add("d");
+  }
+
+  @Test
+  public void setConstructorTest(){
+    Assert.assertEquals(new TwoPhaseSet<>(sampleSet).value(), sampleSet);
+  }
+
+  @Test
+  public void valueTest(){
+    Set<Character> added = new HashSet<>();
+    added.add('a');
+    added.add('b');
+    Set<Character> removed = new HashSet<>();
+    removed.add('b');
+    Assert.assertEquals(new TwoPhaseSet<>(added, removed), new TwoPhaseSet<>('a'));
+  }
+
+  @Test
+  public void optimizeTest(){
+    TwoPhaseSet<String> set = new TwoPhaseSet<>(sampleSet);
+    set = set.remove("b");
+    Assert.assertEquals(set.optimize(), set);
+    // check that optimize in this case actually works
+    Assert.assertTrue(set.optimize().getAdded().size() < set.getAdded().size());
+  }
+
+  @Test
+  public void immutabilityTest(){
+    TwoPhaseSet<String> set = new TwoPhaseSet<>(sampleSet);
+    TwoPhaseSet<String> newSet = set.remove("b");
+    Assert.assertNotEquals(set, newSet);
+    Assert.assertEquals(set, new TwoPhaseSet<>(sampleSet));
+  }
+
+  @Test
+  public void removeMissingAddExistingLimitsTest(){
+    BiConsumer<TwoPhaseSet<?>, TwoPhaseSet<?>> checkInternals = (f, s)
-> {
+      Assert.assertEquals(s, f);
+      Assert.assertEquals(s.getRemoved(), f.getRemoved());
+      Assert.assertEquals(s.getAdded(), f.getAdded());
+    };
+    TwoPhaseSet<String> set = new TwoPhaseSet<>(sampleSet);
+    // remove missing
+    checkInternals.accept(set, set.remove("e"));
+    // add existing
+    checkInternals.accept(set, set.add("a"));
+    // limits
+    TwoPhaseSet<String> newSet = set.remove("a"); // allow this remove
+    Assert.assertEquals(newSet.add("a"), new TwoPhaseSet<>("b", "d")); // discard this
add, "a" was added and removed
+  }
+
+  @Test
+  public void mergeTest(){
+    TwoPhaseSet<String> f = new TwoPhaseSet<>(sampleSet);
+    TwoPhaseSet<String> s = new TwoPhaseSet<>("a", "c");
+    s = s.remove("a");
+    TwoPhaseSet<String> res = f.merge(s);
+    Assert.assertEquals(res, new TwoPhaseSet<>(f, s)); // check two-sets constructor
+
+    // "a" was both added and deleted in second set => it's deleted in result
+    // "b" and "d" comes from first set and "c" comes from second
+    Assert.assertEquals(res, new TwoPhaseSet<>("b", "c", "d"));
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-gossip/blob/89af0ac1/gossip-itest/src/test/java/org/apache/gossip/DataTest.java
----------------------------------------------------------------------
diff --git a/gossip-itest/src/test/java/org/apache/gossip/DataTest.java b/gossip-itest/src/test/java/org/apache/gossip/DataTest.java
index df078aa..c16174f 100644
--- a/gossip-itest/src/test/java/org/apache/gossip/DataTest.java
+++ b/gossip-itest/src/test/java/org/apache/gossip/DataTest.java
@@ -25,6 +25,7 @@ import org.apache.gossip.crdt.LwwSet;
 import org.apache.gossip.crdt.MaxChangeSet;
 import org.apache.gossip.crdt.OrSet;
 import org.apache.gossip.crdt.PNCounter;
+import org.apache.gossip.crdt.TwoPhaseSet;
 import org.apache.gossip.manager.GossipManager;
 import org.apache.gossip.manager.GossipManagerBuilder;
 import org.apache.gossip.model.PerNodeDataMessage;
@@ -148,6 +149,11 @@ public class DataTest {
   }
 
   @Test
+  public void TwoPhaseSetTest(){
+    crdtSetTest("crtps", TwoPhaseSet::new);
+  }
+
+  @Test
   public void GrowOnlyCounterTest(){
     Consumer<Long> assertCountUpdated = count -> {
       for (GossipManager client : clients){

http://git-wip-us.apache.org/repos/asf/incubator-gossip/blob/89af0ac1/gossip-protocol-jackson/src/test/java/org/apache/gossip/protocol/json/JacksonTest.java
----------------------------------------------------------------------
diff --git a/gossip-protocol-jackson/src/test/java/org/apache/gossip/protocol/json/JacksonTest.java
b/gossip-protocol-jackson/src/test/java/org/apache/gossip/protocol/json/JacksonTest.java
index d391fa1..2a5239c 100644
--- a/gossip-protocol-jackson/src/test/java/org/apache/gossip/protocol/json/JacksonTest.java
+++ b/gossip-protocol-jackson/src/test/java/org/apache/gossip/protocol/json/JacksonTest.java
@@ -25,6 +25,7 @@ import org.apache.gossip.Member;
 import org.apache.gossip.crdt.LwwSet;
 import org.apache.gossip.crdt.MaxChangeSet;
 import org.apache.gossip.crdt.OrSet;
+import org.apache.gossip.crdt.TwoPhaseSet;
 import org.apache.gossip.manager.GossipManager;
 import org.apache.gossip.manager.GossipManagerBuilder;
 import org.apache.gossip.protocol.ProtocolManager;
@@ -98,17 +99,22 @@ public class JacksonTest {
 
   @Test
   public void jacksonOrSetTest(){
-    jacksonCrdtSeDeTest(new OrSet<>("1", "2", "3"), OrSet.class);
+    jacksonCrdtSeDeTest(new OrSet<>("1", "2", "3").remove("2"), OrSet.class);
   }
 
   @Test
   public void jacksonLWWSetTest(){
-    jacksonCrdtSeDeTest(new LwwSet<>("1", "2", "3"), LwwSet.class);
+    jacksonCrdtSeDeTest(new LwwSet<>("1", "2", "3").remove("2"), LwwSet.class);
   }
 
   @Test
   public void jacksonMaxChangeSetTest(){
-    jacksonCrdtSeDeTest(new MaxChangeSet<>("1", "2", "3"), MaxChangeSet.class);
+    jacksonCrdtSeDeTest(new MaxChangeSet<>("1", "2", "3").remove("2"), MaxChangeSet.class);
+  }
+
+  @Test
+  public void jacksonTwoPhaseSetTest(){
+    jacksonCrdtSeDeTest(new TwoPhaseSet<>("1", "2", "3").remove("2"), TwoPhaseSet.class);
   }
 
   @Test


Mime
View raw message