SharedLog runtime proto
ONOS-1806, ONOS-1807, ONOS-1808
Change-Id: Ic86cb7bdc6b04a81180ab43afa01c1aea4ac18c7
diff --git a/src/test/java/net/onrc/onos/core/util/distributed/sharedlog/SeqNumTest.java b/src/test/java/net/onrc/onos/core/util/distributed/sharedlog/SeqNumTest.java
new file mode 100644
index 0000000..9ae7897
--- /dev/null
+++ b/src/test/java/net/onrc/onos/core/util/distributed/sharedlog/SeqNumTest.java
@@ -0,0 +1,157 @@
+package net.onrc.onos.core.util.distributed.sharedlog;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.*;
+
+import org.junit.Test;
+
+import com.google.common.primitives.UnsignedLongs;
+
+/**
+ * Basic {@link SeqNum} class tests.
+ */
+public class SeqNumTest {
+
+ /**
+ * Tests {@link SeqNum#next()} points to next sequence number,
+ * excluding reserved INITIAL value,
+ * and correctly wraps around ULONG_MAX.
+ */
+ @Test
+ public void testNext() {
+ final SeqNum one = SeqNum.INITIAL.next();
+ assertEquals(1L, one.longValue());
+
+ // succ
+ final SeqNum two = one.next();
+ assertEquals(2L, two.longValue());
+
+ // succ wraps around skipping INITIAL
+ final SeqNum max = SeqNum.valueOf(UnsignedLongs.MAX_VALUE);
+ assertEquals(one, max.next());
+ }
+
+ /**
+ * Tests {@link SeqNum#prev()} points to previous sequence number,
+ * excluding reserved INITIAL value,
+ * and correctly wraps around ULONG_MAX.
+ */
+ @Test
+ public void testPrev() {
+
+ // prev
+ final SeqNum two = SeqNum.valueOf(2);
+ final SeqNum one = two.prev();
+ assertEquals(1L, one.longValue());
+
+ final SeqNum max = SeqNum.INITIAL.prev();
+ assertEquals(UnsignedLongs.MAX_VALUE, max.longValue());
+
+ // prev wraps around skipping INITIAL
+ assertEquals(max, one.prev());
+ }
+
+ /**
+ * Tests that SeqNum equals and hashCode.
+ */
+ @Test
+ public void testEqualsObject() {
+ final SeqNum s = SeqNum.valueOf(42L);
+
+ assertTrue(s.equals(s));
+ assertEquals(s.hashCode(), s.hashCode());
+
+ assertTrue(s.equals(s.next().prev()));
+ assertEquals(s.hashCode(), s.next().prev().hashCode());
+
+ assertFalse(s.equals(s.next()));
+ assertFalse(s.equals(s.prev()));
+ assertFalse(s.equals(null));
+ assertFalse(s.equals(Long.valueOf(42L)));
+ }
+
+ /**
+ * Tests that SeqNum is converted to String as unsigned decimal.
+ */
+ @Test
+ public void testToString() {
+ assertEquals("0", SeqNum.INITIAL.toString());
+ assertEquals("1", SeqNum.valueOf(1).toString());
+ // toString format is unsigned decimal string
+ assertEquals("9223372036854775808",
+ SeqNum.valueOf(Long.MAX_VALUE + 1).toString());
+ }
+
+ /**
+ * Tests that comparison works treating long value as point in a ring.
+ */
+ @Test
+ public void testCompareTo() {
+ final SeqNum zero = SeqNum.INITIAL;
+ final SeqNum one = SeqNum.valueOf(1);
+ final SeqNum two = SeqNum.valueOf(2);
+ final SeqNum oneAlt = zero.next();
+ final SeqNum negOne = one.prev();
+
+ // 0 < 1
+ assertThat(zero.compareTo(one), lessThan(0));
+ // 1 > 0
+ assertThat(one.compareTo(zero), greaterThan(0));
+
+ // 0 == 0
+ assertThat(zero.compareTo(zero), equalTo(0));
+ // 1 == 1
+ assertThat(oneAlt.compareTo(one), equalTo(0));
+ assertThat(one.compareTo(oneAlt), equalTo(0));
+
+ // 2 > 1
+ assertThat(two.compareTo(one), greaterThan(0));
+ // 1 < 2
+ assertThat(one.compareTo(two), lessThan(0));
+
+ // (-1) < 1
+ assertThat(negOne.compareTo(one), lessThan(0));
+ // 1 > (-1)
+ assertThat(one.compareTo(negOne), greaterThan(0));
+
+ // (-1) > (-3)
+ assertThat(negOne.compareTo(negOne.prev().prev()), greaterThan(0));
+ // (-3) < (-1)
+ assertThat(negOne.prev().prev().compareTo(negOne), lessThan(0));
+
+ // (-1) > 0 [0 is always the smallest element]
+ assertThat(negOne.compareTo(zero), greaterThan(0));
+ // 0 < (-1) [0 is always the smallest element]
+ assertThat(zero.compareTo(negOne), lessThan(0));
+
+ /// comparison using shorter arc
+
+ // 0 < SLONG_MAX+1(=HALF) [clockwise arc used]
+ assertThat(zero.compareTo(zero.step(Long.MAX_VALUE).next()), lessThan(0));
+ assertThat(zero.step(Long.MAX_VALUE).next().compareTo(zero), greaterThan(0));
+ /// 0 is always compared clock wise (never wraps)
+ // 0 < SLONG_MAX+1(=HALF)+1 [clockwise arc used]
+ assertThat(zero.compareTo(zero.step(Long.MAX_VALUE).next().next()), lessThan(0));
+ assertThat(zero.step(Long.MAX_VALUE).next().next().compareTo(zero), greaterThan(0));
+
+ // 1 < 1+SLONG_MAX(=HALF-1) [clockwise arc used]
+ assertThat(one.compareTo(one.step(Long.MAX_VALUE)), lessThan(0));
+ assertThat(one.step(Long.MAX_VALUE).compareTo(one), greaterThan(0));
+ // 1 < 1+SLONG_MAX+1(=HALF) [SAME, counter-clockwise arc used]
+ assertThat(one.compareTo(one.step(Long.MAX_VALUE).next()), greaterThan(0));
+ assertThat(one.step(Long.MAX_VALUE).next().compareTo(one), lessThan(0));
+ // 1 < 1+SLONG_MAX+2(=HALF+1) [counter-clockwise arc used]
+ assertThat(one.compareTo(one.step(Long.MAX_VALUE).next().next()), greaterThan(0));
+ assertThat(one.step(Long.MAX_VALUE).next().next().compareTo(one), lessThan(0));
+
+ // (-1) < (-1)+SLONG_MAX(=HALF-1) [clockwise arc used]
+ assertThat(negOne.compareTo(negOne.step(Long.MAX_VALUE)), lessThan(0));
+ assertThat(negOne.step(Long.MAX_VALUE).compareTo(negOne), greaterThan(0));
+ // (-1) < (-1)+SLONG_MAX+1(=HALF) [SAME, clockwise arc used]
+ assertThat(negOne.compareTo(negOne.step(Long.MAX_VALUE).next()), lessThan(0));
+ assertThat(negOne.step(Long.MAX_VALUE).next().compareTo(negOne), greaterThan(0));
+ // (-1) > (-1)+SLONG_MAX+2(=HALF+1) [counter-clockwise arc used]
+ assertThat(negOne.compareTo(negOne.step(Long.MAX_VALUE).next().next()), greaterThan(0));
+ assertThat(negOne.step(Long.MAX_VALUE).next().next().compareTo(negOne), lessThan(0));
+ }
+}
diff --git a/src/test/java/net/onrc/onos/core/util/distributed/sharedlog/example/LogAtomicLongTest.java b/src/test/java/net/onrc/onos/core/util/distributed/sharedlog/example/LogAtomicLongTest.java
new file mode 100644
index 0000000..91fd2be
--- /dev/null
+++ b/src/test/java/net/onrc/onos/core/util/distributed/sharedlog/example/LogAtomicLongTest.java
@@ -0,0 +1,132 @@
+package net.onrc.onos.core.util.distributed.sharedlog.example;
+
+import static org.junit.Assert.*;
+
+import java.util.UUID;
+
+import net.onrc.onos.core.datastore.hazelcast.HZClient;
+import net.onrc.onos.core.util.IntegrationTest;
+import net.onrc.onos.core.util.TestUtils;
+import net.onrc.onos.core.util.distributed.sharedlog.hazelcast.HazelcastRuntime;
+import net.onrc.onos.core.util.distributed.sharedlog.hazelcast.HazelcastSequencerRuntime;
+import net.onrc.onos.core.util.distributed.sharedlog.runtime.LogBasedRuntime;
+import net.onrc.onos.core.util.distributed.sharedlog.runtime.SequencerRuntime;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import com.hazelcast.core.HazelcastInstance;
+
+/**
+ * Unit test to run LogAtomicLong example.
+ */
+public class LogAtomicLongTest {
+
+ static {
+ // configuration to quickly fall back to instance mode for faster test run
+ System.setProperty("net.onrc.onos.core.datastore.hazelcast.client.attemptLimit", "0");
+ }
+
+
+ private LogAtomicLong along;
+
+ /**
+ * Create LogAtomicLong instance.
+ */
+ @Before
+ public void setUp() {
+ final HZClient cl = HZClient.getClient();
+ HazelcastInstance hz = TestUtils.callMethod(cl, "getHZInstance", null);
+
+ SequencerRuntime sequencerRuntime = new HazelcastSequencerRuntime(hz);
+ LogBasedRuntime runtime = new HazelcastRuntime(hz, sequencerRuntime);
+
+ String counterName = UUID.randomUUID().toString();
+ along = new LogAtomicLong(runtime, counterName);
+ }
+
+ /**
+ * Test unconditional write.
+ */
+ @Test
+ public void testSet() {
+ along.set(42);
+ assertEquals(42, along.get());
+ along.set(0);
+ assertEquals(0, along.get());
+ }
+
+ /**
+ * Test conditional write.
+ */
+ @Test
+ public void testCompareAndSet() {
+ along.set(42);
+ assertEquals(42, along.get());
+
+ along.compareAndSet(42, 43);
+ assertEquals(43, along.get());
+
+ // should remain unchanged if expectation not met
+ along.compareAndSet(42, 45);
+ assertEquals(43, along.get());
+ }
+
+ /**
+ * Confirm initial value is 0.
+ */
+ @Test
+ public void testGet() {
+ assertEquals(0, along.get());
+ }
+
+ /**
+ * Confirm another instance with same ID observes the same value.
+ */
+ @Test
+ public void testOtherInstance() {
+ along.set(42);
+ assertEquals(42, along.get());
+
+ final HZClient cl = HZClient.getClient();
+ HazelcastInstance hz = TestUtils.callMethod(cl, "getHZInstance", null);
+
+ SequencerRuntime sequencerRuntime = new HazelcastSequencerRuntime(hz);
+ LogBasedRuntime runtime = new HazelcastRuntime(hz, sequencerRuntime);
+
+ LogAtomicLong anotherInstance = new LogAtomicLong(runtime,
+ along.getObjectID());
+ assertEquals(42, anotherInstance.get());
+ }
+
+ /**
+ * Confirm another instance with same ID initializes using snapshot.
+ */
+ @Category(IntegrationTest.class)
+ @Test
+ public void testOtherInstanceFromSnapshot() {
+ along.set(42);
+ assertEquals(42, along.get());
+
+ final HZClient cl = HZClient.getClient();
+ HazelcastInstance hz = TestUtils.callMethod(cl, "getHZInstance", null);
+
+ SequencerRuntime sequencerRuntime = new HazelcastSequencerRuntime(hz);
+ LogBasedRuntime runtime = new HazelcastRuntime(hz, sequencerRuntime);
+
+ // FIXME SNAPSHOT_INTERVAL should be customized to smaller values
+ // write multiple times to trigger snapshot
+ for (int i = 0; i < HazelcastRuntime.SNAPSHOT_INTERVAL + 2; ++i) {
+ along.set(i);
+ }
+ along.set(99);
+ Thread.yield();
+
+ // this instance might start from latest snap shot then replay
+ LogAtomicLong anotherInstance2 = new LogAtomicLong(runtime,
+ along.getObjectID());
+ assertEquals(99, anotherInstance2.get());
+
+ }
+}