/**
* Copyright (c) 2016, All Contributors (see CONTRIBUTORS file)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package com.eventsourcing.hlc;
import com.eventsourcing.layout.Layout;
import com.eventsourcing.layout.ObjectDeserializer;
import com.eventsourcing.layout.ObjectSerializer;
import com.eventsourcing.layout.Property;
import com.eventsourcing.layout.binary.BinarySerialization;
import com.google.common.util.concurrent.AbstractService;
import lombok.SneakyThrows;
import org.apache.commons.net.ntp.TimeStamp;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.nio.ByteBuffer;
import java.util.Date;
import java.util.List;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
public class HybridTimestampTest {
class TestPhysicalTimeProvider extends AbstractService implements PhysicalTimeProvider {
private long physicalTime = 0;
@Override
public long getPhysicalTime() {
return physicalTime;
}
public void setPhysicalTime(long physicalTime) {
this.physicalTime = physicalTime;
}
@Override
protected void doStart() {
notifyStarted();
}
@Override
protected void doStop() {
notifyStopped();
}
}
;
private TestPhysicalTimeProvider physicalTimeProvider;
@BeforeClass
public void setup() {
physicalTimeProvider = new TestPhysicalTimeProvider();
}
@Test
public void initialTimestamp() {
HybridTimestamp timestamp = new HybridTimestamp(physicalTimeProvider);
TimeStamp ntpTime = new TimeStamp(timestamp.getLogicalTime());
assertEquals(ntpTime.getDate(), new Date(0));
}
@Test
public void testTimestamp() {
HybridTimestamp timestamp = new HybridTimestamp(physicalTimeProvider);
timestamp.update();
timestamp.update();
assertTrue(timestamp.getLogicalCounter() > 0);
HybridTimestamp timestamp1 = new HybridTimestamp(physicalTimeProvider, timestamp.getLogicalTime(),
timestamp.getLogicalCounter());
assertEquals(timestamp1.getLogicalCounter(), timestamp.getLogicalCounter());
}
@Test @SneakyThrows
public void layout() {
Layout<HybridTimestamp> layout = Layout.forClass(HybridTimestamp.class);
List<Property<HybridTimestamp>> properties = layout.getProperties();
assertEquals(properties.size(), 2);
assertTrue(properties.stream().anyMatch(p -> p.getName().contentEquals("logicalTime")));
assertTrue(properties.stream().anyMatch(p -> p.getName().contentEquals("logicalCounter")));
HybridTimestamp timestamp = new HybridTimestamp(physicalTimeProvider);
timestamp.update();
BinarySerialization serialization = BinarySerialization.getInstance();
ObjectSerializer<HybridTimestamp> serializer = serialization.getSerializer(HybridTimestamp.class);
ObjectDeserializer<HybridTimestamp> deserializer = serialization.getDeserializer(HybridTimestamp.class);
ByteBuffer buffer = serializer.serialize(timestamp);
buffer.rewind();
HybridTimestamp timestamp1 = deserializer.deserialize(buffer);
assertEquals(timestamp1.compareTo(timestamp), 0);
}
@Test
public void test() {
long ts, ts_;
HybridTimestamp timestamp = new HybridTimestamp(physicalTimeProvider, 0, 0);
ts = (long) 1 << 32 | 0;
physicalTimeProvider.setPhysicalTime(ts);
timestamp.update();
assertEquals(ts, timestamp.getLogicalTime());
assertEquals(0, timestamp.getLogicalCounter());
// clock didn't move
ts = (long) 1 << 32 | 0;
physicalTimeProvider.setPhysicalTime(ts);
timestamp.update();
assertEquals(ts, timestamp.getLogicalTime());
assertEquals(1, timestamp.getLogicalCounter());
// clock moved back
ts = (long) 0 << 32 | 1;
physicalTimeProvider.setPhysicalTime(ts);
ts_ = timestamp.getLogicalTime();
timestamp.update();
assertEquals(ts_, timestamp.getLogicalTime());
assertEquals(2, timestamp.getLogicalCounter());
// clock moved ahead
ts = (long) 2 << 32 | 0;
physicalTimeProvider.setPhysicalTime(ts);
timestamp.update();
assertEquals(ts, timestamp.getLogicalTime());
assertEquals(0, timestamp.getLogicalCounter());
// event happens, but wall ahead
ts = (long) 3 << 32 | 0;
physicalTimeProvider.setPhysicalTime(ts);
timestamp.update((long) 1 << 32 | 2, 3);
assertEquals(ts, timestamp.getLogicalTime());
assertEquals(0, timestamp.getLogicalCounter());
// event happens, wall ahead but unchanged
ts = (long) 3 << 32 | 0;
physicalTimeProvider.setPhysicalTime(ts);
timestamp.update((long) 1 << 32 | 2, 3);
assertEquals(ts, timestamp.getLogicalTime());
assertEquals(1, timestamp.getLogicalCounter());
// event happens at wall, which is still unchanged
ts = (long) 3 << 32 | 0;
physicalTimeProvider.setPhysicalTime(ts);
timestamp.update((long) 3 << 32 | 0, 1);
assertEquals(ts, timestamp.getLogicalTime());
assertEquals(2, timestamp.getLogicalCounter());
// event with larger logical, wall unchaged
ts = (long) 3 << 32 | 0;
physicalTimeProvider.setPhysicalTime(ts);
timestamp.update((long) 3 << 32 | 0, 99);
assertEquals(ts, timestamp.getLogicalTime());
assertEquals(100, timestamp.getLogicalCounter());
// event with larger wall, our wall behind
ts = (long) 3 << 32 | 5;
physicalTimeProvider.setPhysicalTime(ts);
timestamp.update((long) 4 << 32 | 4, 100);
assertEquals((long) 4 << 32 | 4, timestamp.getLogicalTime());
assertEquals(101, timestamp.getLogicalCounter());
// event behind wall, but ahead of previous state
ts = (long) 5 << 32 | 0;
physicalTimeProvider.setPhysicalTime(ts);
timestamp.update((long) 4 << 32 | 5, 0);
assertEquals(ts, timestamp.getLogicalTime());
assertEquals(0, timestamp.getLogicalCounter());
ts = (long) 4 << 32 | 9;
physicalTimeProvider.setPhysicalTime(ts);
timestamp.update((long) 5 << 32 | 0, 99);
assertEquals((long) 5 << 32 | 0, timestamp.getLogicalTime());
assertEquals(100, timestamp.getLogicalCounter());
// event at state, lower logical than state
ts = (long) 0 << 32 | 0;
physicalTimeProvider.setPhysicalTime(ts);
timestamp.update((long) 5 << 32 | 0, 50);
assertEquals((long) 5 << 32 | 0, timestamp.getLogicalTime());
assertEquals(101, timestamp.getLogicalCounter());
// another update API
timestamp.update(timestamp);
assertEquals((long) 5 << 32 | 0, timestamp.getLogicalTime());
assertEquals(102, timestamp.getLogicalCounter());
}
}