From 36ffaea4d54ad30b4f221c1caf44db18ff2ae8cd Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 28 Jan 2014 21:15:28 +0100 Subject: [PATCH] fixing bug 1729 by adding latch-based synchronization to OT test case --- .../domain/racelog/impl/RaceLogImpl.java | 4 +- .../RaceLogOperationalTransformationTest.java | 79 ++++++++++++++++--- 2 files changed, 70 insertions(+), 13 deletions(-) diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/impl/RaceLogImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/impl/RaceLogImpl.java index ae61d87ef29..0219c4f792e 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/impl/RaceLogImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/impl/RaceLogImpl.java @@ -104,13 +104,13 @@ public class RaceLogImpl extends TrackImpl implements RaceLog { unlockAfterWrite(); } if (isAdded) { - logger.finer(String.format("%s (%s) was added to log.", event, event.getClass().getName())); + logger.finer(String.format("%s (%s) was added to log %s.", event, event.getClass().getName(), getId())); // FIXME with out-of-order delivery would destroy currentPassId; need to check at least the createdAt time // point setCurrentPassId(Math.max(event.getPassId(), this.currentPassId)); notifyListenersAboutReceive(event); } else { - logger.warning(String.format("%s (%s) was not added to log. Ignoring", event, event.getClass().getName())); + logger.warning(String.format("%s (%s) was not added to race log %s. Ignoring", event, event.getClass().getName(), getId())); } return isAdded; } diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceLogOperationalTransformationTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceLogOperationalTransformationTest.java index 60363217b7d..488b666ecae 100755 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceLogOperationalTransformationTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceLogOperationalTransformationTest.java @@ -6,6 +6,7 @@ import java.util.Calendar; import java.util.Collections; import java.util.GregorianCalendar; import java.util.UUID; +import java.util.concurrent.CountDownLatch; import org.junit.Before; import org.junit.Test; @@ -35,6 +36,9 @@ public class RaceLogOperationalTransformationTest { private Peer, RaceLog> server; private OperationalTransformer> transformer; private RaceLogEventAuthor author = new RaceLogEventAuthorImpl("Test Author", 1); + private CountDownLatch client1Latch; + private CountDownLatch client2Latch; + private CountDownLatch serverLatch; @Before public void setUp() { @@ -42,9 +46,45 @@ public class RaceLogOperationalTransformationTest { raceLogClient2 = new RaceLogImpl("Test Race Log Client2"); raceLogServer = new RaceLogImpl("Test Race Log Server"); transformer = new OperationalTransformer<>(); - client1 = new PeerImpl<>(transformer, raceLogClient1, Role.CLIENT); - client2 = new PeerImpl<>(transformer, raceLogClient2, Role.CLIENT); - server = new PeerImpl<>(transformer, raceLogServer, Role.SERVER); + client1Latch = new CountDownLatch(1); + client1 = new PeerImpl, RaceLog>(transformer, raceLogClient1, Role.CLIENT) { + @Override + public void apply(Peer, RaceLog> source, + RaceLogEventWithTransformationSupport operation, int numberOfOperationsSourceHasMergedFromThis) { + try { + client1Latch.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + super.apply(source, operation, numberOfOperationsSourceHasMergedFromThis); + } + }; + client2Latch = new CountDownLatch(1); + client2 = new PeerImpl, RaceLog>(transformer, raceLogClient2, Role.CLIENT) { + @Override + public void apply(Peer, RaceLog> source, + RaceLogEventWithTransformationSupport operation, int numberOfOperationsSourceHasMergedFromThis) { + try { + client2Latch.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + super.apply(source, operation, numberOfOperationsSourceHasMergedFromThis); + } + }; + serverLatch = new CountDownLatch(1); + server = new PeerImpl, RaceLog>(transformer, raceLogServer, Role.SERVER) { + @Override + public void apply(Peer, RaceLog> source, + RaceLogEventWithTransformationSupport operation, int numberOfOperationsSourceHasMergedFromThis) { + try { + serverLatch.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + super.apply(source, operation, numberOfOperationsSourceHasMergedFromThis); + } + }; server.addPeer(client1); client1.addPeer(server); server.addPeer(client2); @@ -58,6 +98,9 @@ public class RaceLogOperationalTransformationTest { RaceLogStartTimeEvent startTimeEvent = new RaceLogStartTimeEventImpl(MillisecondsTimePoint.now(), author, startTime, UUID.randomUUID(), Collections. emptyList(), /* pass ID */ 1, startTime); RaceLogStartTimeEventWithTransformationSupport e = new RaceLogStartTimeEventWithTransformationSupport(startTimeEvent); server.apply(e); + serverLatch.countDown(); + client1Latch.countDown(); + client2Latch.countDown(); waitForEventualConsistency(); raceLogClient1.lockForRead(); try { @@ -93,14 +136,28 @@ public class RaceLogOperationalTransformationTest { public void testConflictingApplyServerWins() { Calendar cServer = new GregorianCalendar(2013, 6, 7, 13, 59, 33); final MillisecondsTimePoint startTimeServer = new MillisecondsTimePoint(cServer.getTime()); - RaceLogStartTimeEvent startTimeEventServer = new RaceLogStartTimeEventImpl(MillisecondsTimePoint.now(), author, startTimeServer, UUID.randomUUID(), Collections. emptyList(), /* pass ID */ 1, startTimeServer); - RaceLogStartTimeEventWithTransformationSupport eServer = new RaceLogStartTimeEventWithTransformationSupport(startTimeEventServer); - server.apply(eServer); - Calendar cClient1 = new GregorianCalendar(2013, 6, 7, 13, 59, 33); - final MillisecondsTimePoint startTimeClient1 = new MillisecondsTimePoint(cClient1.getTime()); - RaceLogStartTimeEvent startTimeEventClient1 = new RaceLogStartTimeEventImpl(MillisecondsTimePoint.now(), author, startTimeClient1, UUID.randomUUID(), Collections. emptyList(), /* pass ID */ 1, startTimeClient1); - RaceLogStartTimeEventWithTransformationSupport eClient1 = new RaceLogStartTimeEventWithTransformationSupport(startTimeEventClient1); - client1.apply(eClient1); + final MillisecondsTimePoint createdAt = MillisecondsTimePoint.now(); + { + RaceLogStartTimeEvent startTimeEventServer = new RaceLogStartTimeEventImpl(createdAt, + author, startTimeServer, UUID.randomUUID(), Collections. emptyList(), /* pass ID */1, + startTimeServer); + RaceLogStartTimeEventWithTransformationSupport eServer = new RaceLogStartTimeEventWithTransformationSupport( + startTimeEventServer); + server.apply(eServer); + } + { + Calendar cClient1 = new GregorianCalendar(2013, 6, 7, 13, 59, 33); + final MillisecondsTimePoint startTimeClient1 = new MillisecondsTimePoint(cClient1.getTime()); + RaceLogStartTimeEvent startTimeEventClient1 = new RaceLogStartTimeEventImpl(createdAt, + author, startTimeClient1, UUID.randomUUID(), Collections. emptyList(), /* pass ID */1, + startTimeClient1); + final RaceLogStartTimeEventWithTransformationSupport eClient1 = new RaceLogStartTimeEventWithTransformationSupport( + startTimeEventClient1); + client1.apply(eClient1); // this call won't block because server calls apply(Peer, O, int) which first waits for the latch before synchronizing + } + serverLatch.countDown(); + client1Latch.countDown(); + client2Latch.countDown(); waitForEventualConsistency(); // now assert that the start time is equal on both clients and the server and equal to that set on the server raceLogClient1.lockForRead();