added a second OT test which folds a leaderboard column addition with a leaderboard removal

This commit is contained in:
Axel Uhl
2012-03-28 15:42:33 +02:00
parent 2e603bc6e3
commit ff23e68c9f
14 changed files with 171 additions and 114 deletions
@@ -45,7 +45,7 @@ public class OperationalTransformationTest {
} }
public static class StringInsertTransformer implements Transformer<StringInsertOperation> { public static class StringInsertTransformer implements Transformer<StringInsertOperation> {
@Override @Override
public ClientServerOperationPair<StringInsertOperation> transform(StringInsertOperation clientOp, StringInsertOperation serverOp) { public ClientServerOperationPair<StringInsertOperation> transform(StringInsertOperation clientOp, StringInsertOperation serverOp) {
StringInsertOperation resultClientOp; StringInsertOperation resultClientOp;
StringInsertOperation resultServerOp; StringInsertOperation resultServerOp;
@@ -9,8 +9,8 @@ public interface Transformer<O extends Operation<?>> {
* again.<p> * again.<p>
* *
* If a resulting operation is supposed to not have any effects, return the * If a resulting operation is supposed to not have any effects, return the
* {@link ClientServerOperationPair#getNoOp()} operation, but never <code>null</code> * {@link AbstractRacingEventServiceOperation#getNoOp()} operation, but never <code>null</code>
* as any of the operations in the resulting pair. * as any of the operations in the resulting pair.
*/ */
ClientServerOperationPair<O> transform(O clientOp, O serverOp); ClientServerOperationPair<O> transform(O transformedOp, O unconfirmedOperation);
} }
@@ -48,8 +48,8 @@ public class UnmergedOperationsQueue<O extends Operation<S>, S> {
* *
* @param localOperationNumber starts with 0 * @param localOperationNumber starts with 0
*/ */
public synchronized void updateWithTransformed(int localOperationNumber, O transformedOperation) { public synchronized void updateWithTransformed(int localOperationNumber, O operation) {
unmergedOperations.set(localOperationNumber-numberOfFirstInList, transformedOperation); unmergedOperations.set(localOperationNumber-numberOfFirstInList, operation);
} }
/** /**
@@ -76,7 +76,7 @@ public class UnmergedOperationsQueue<O extends Operation<S>, S> {
StringBuilder result = new StringBuilder(); StringBuilder result = new StringBuilder();
result.append('['); result.append('[');
boolean first = true; boolean first = true;
for (O op : unmergedOperations) { for (Operation<S> op : unmergedOperations) {
if (!first) { if (!first) {
result.append(", "); result.append(", ");
} else { } else {
@@ -17,9 +17,11 @@ import com.sap.sailing.operationaltransformation.Peer.Role;
import com.sap.sailing.operationaltransformation.PeerImpl; import com.sap.sailing.operationaltransformation.PeerImpl;
import com.sap.sailing.server.RacingEventService; import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.server.impl.RacingEventServiceImpl; import com.sap.sailing.server.impl.RacingEventServiceImpl;
import com.sap.sailing.server.operationaltransformation.AddColumnToLeaderboard;
import com.sap.sailing.server.operationaltransformation.AddLeaderboard; import com.sap.sailing.server.operationaltransformation.AddLeaderboard;
import com.sap.sailing.server.operationaltransformation.OperationalTransformer; import com.sap.sailing.server.operationaltransformation.OperationalTransformer;
import com.sap.sailing.server.operationaltransformation.RacingEventServiceOperation; import com.sap.sailing.server.operationaltransformation.RacingEventServiceOperation;
import com.sap.sailing.server.operationaltransformation.RemoveLeaderboard;
public class OperationalTransformationTest { public class OperationalTransformationTest {
private static final String LEADERBOARDNAME = "TESTBOARD"; private static final String LEADERBOARDNAME = "TESTBOARD";
@@ -48,9 +50,24 @@ public class OperationalTransformationTest {
RacingEventServiceOperation addLeaderboardOp = new AddLeaderboard(LEADERBOARDNAME, new int[] { 5 }); RacingEventServiceOperation addLeaderboardOp = new AddLeaderboard(LEADERBOARDNAME, new int[] { 5 });
server.apply(addLeaderboardOp); server.apply(addLeaderboardOp);
server.waitForNotRunning(); server.waitForNotRunning();
replica.waitForNotRunning();
Map<String, Leaderboard> replicaLeaderboards = racingEventServiceReplica.getLeaderboards(); Map<String, Leaderboard> replicaLeaderboards = racingEventServiceReplica.getLeaderboards();
assertEquals(2, replicaLeaderboards.size()); // expected to include the default leaderboard assertEquals(2, replicaLeaderboards.size()); // expected to include the default leaderboard
assertEquals(new HashSet<String>(Arrays.asList(new String[] { DefaultLeaderboardName.DEFAULT_LEADERBOARD_NAME, LEADERBOARDNAME })), assertEquals(new HashSet<String>(Arrays.asList(new String[] { DefaultLeaderboardName.DEFAULT_LEADERBOARD_NAME, LEADERBOARDNAME })),
replicaLeaderboards.keySet()); replicaLeaderboards.keySet());
assertEquals(racingEventServiceServer.getLeaderboards().keySet(), replicaLeaderboards.keySet());
}
@Test
public void testAddColumnToLeaderboardOnClientAndRemoveLeaderboardOnServer() {
RacingEventServiceOperation addLeaderboardColumn = new AddColumnToLeaderboard(
"newColumn", DefaultLeaderboardName.DEFAULT_LEADERBOARD_NAME, /* medalRace */ true);
server.apply(addLeaderboardColumn);
RacingEventServiceOperation removeDefaultLeaderboard = new RemoveLeaderboard(DefaultLeaderboardName.DEFAULT_LEADERBOARD_NAME);
replica.apply(removeDefaultLeaderboard);
server.waitForNotRunning();
replica.waitForNotRunning();
assertEquals(0, racingEventServiceReplica.getLeaderboards().size());
assertEquals(0, racingEventServiceServer.getLeaderboards().size());
} }
} }
@@ -1,25 +1,43 @@
package com.sap.sailing.server.operationaltransformation; package com.sap.sailing.server.operationaltransformation;
public abstract class AbstractLeaderboardColumnOperation extends AbstractRacingEventServiceOperation {
private final String leaderboardName; public abstract class AbstractLeaderboardColumnOperation extends AbstractLeaderboardOperation {
private final String columnName; private final String columnName;
public AbstractLeaderboardColumnOperation(String leaderboardName, String columnName) { public AbstractLeaderboardColumnOperation(String leaderboardName, String columnName) {
super(); super(leaderboardName);
this.leaderboardName = leaderboardName;
this.columnName = columnName; this.columnName = columnName;
} }
public String getLeaderboardName() {
return leaderboardName;
}
public String getColumnName() { public String getColumnName() {
return columnName; return columnName;
} }
protected boolean affectsSameColumn(AbstractLeaderboardColumnOperation other) { protected boolean affectsSameColumn(AbstractLeaderboardColumnOperation other) {
return getLeaderboardName().equals(other.getLeaderboardName()) && getColumnName().equals(other.getColumnName()); return affectsSameLeaderboard(other) && getColumnName().equals(other.getColumnName());
}
@Override
public RacingEventServiceOperation transformServerRemoveColumnFromLeaderboard(
RemoveColumnFromLeaderboard removeColumnFromLeaderboardServerOp) {
if (affectsSameColumn(removeColumnFromLeaderboardServerOp)) {
// skip server's remove and hence only apply the client's remove operation
return AbstractRacingEventServiceOperation.getNoOp();
} else {
return this;
}
}
@Override
public RacingEventServiceOperation transformClientRemoveColumnFromLeaderboard(
RemoveColumnFromLeaderboard removeColumnFromLeaderboardClientOp) {
if (affectsSameColumn(removeColumnFromLeaderboardClientOp)) {
// skip client's remove and hence only apply the server's remove operation
return AbstractRacingEventServiceOperation.getNoOp();
} else {
return this;
}
} }
} }
@@ -0,0 +1,40 @@
package com.sap.sailing.server.operationaltransformation;
public abstract class AbstractLeaderboardOperation extends AbstractRacingEventServiceOperation {
private final String leaderboardName;
public AbstractLeaderboardOperation(String leaderboardName) {
super();
this.leaderboardName = leaderboardName;
}
protected String getLeaderboardName() {
return leaderboardName;
}
protected boolean affectsSameLeaderboard(AbstractLeaderboardOperation other) {
return getLeaderboardName().equals(other.getLeaderboardName());
}
@Override
public RacingEventServiceOperation transformRemoveLeaderboardClientOp(RemoveLeaderboard removeLeaderboard) {
if (affectsSameLeaderboard(removeLeaderboard)) {
return AbstractRacingEventServiceOperation.getNoOp();
} else {
return this;
}
}
@Override
public RacingEventServiceOperation transformRemoveLeaderboardServerOp(RemoveLeaderboard removeLeaderboard) {
if (affectsSameLeaderboard(removeLeaderboard)) {
return AbstractRacingEventServiceOperation.getNoOp();
} else {
return this;
}
}
}
@@ -1,5 +1,8 @@
package com.sap.sailing.server.operationaltransformation; package com.sap.sailing.server.operationaltransformation;
import com.sap.sailing.server.RacingEventService;
/** /**
* Performs identical transformations for all operation types by simply returning the operation passed. Subclasses need * Performs identical transformations for all operation types by simply returning the operation passed. Subclasses need
@@ -9,6 +12,26 @@ package com.sap.sailing.server.operationaltransformation;
* *
*/ */
public abstract class AbstractRacingEventServiceOperation implements RacingEventServiceOperation { public abstract class AbstractRacingEventServiceOperation implements RacingEventServiceOperation {
@Override
public RacingEventServiceOperation transformRemoveLeaderboardClientOp(RemoveLeaderboard removeLeaderboard) {
return removeLeaderboard;
}
@Override
public RacingEventServiceOperation transformRemoveLeaderboardServerOp(RemoveLeaderboard removeLeaderboard) {
return removeLeaderboard;
}
@Override
public RacingEventServiceOperation transformAddLeaderboardClientOp(AddLeaderboard addLeaderboard) {
return addLeaderboard;
}
@Override
public RacingEventServiceOperation transformAddLeaderboardServerOp(AddLeaderboard addLeaderboard) {
return addLeaderboard;
}
@Override @Override
public RacingEventServiceOperation transformClientRenameLeaderboardColumnOp( public RacingEventServiceOperation transformClientRenameLeaderboardColumnOp(
RenameLeaderboardColumn renameLeaderboardColumnClientOp) { RenameLeaderboardColumn renameLeaderboardColumnClientOp) {
@@ -30,4 +53,23 @@ public abstract class AbstractRacingEventServiceOperation implements RacingEvent
public RacingEventServiceOperation transformClientRemoveColumnFromLeaderboard(RemoveColumnFromLeaderboard removeColumnFromLeaderboardClientOp) { public RacingEventServiceOperation transformClientRemoveColumnFromLeaderboard(RemoveColumnFromLeaderboard removeColumnFromLeaderboardClientOp) {
return removeColumnFromLeaderboardClientOp; return removeColumnFromLeaderboardClientOp;
} }
public static RacingEventServiceOperation getNoOp() {
return new AbstractRacingEventServiceOperation() {
@Override
public RacingEventService applyTo(RacingEventService toState) {
return toState;
}
@Override
public RacingEventServiceOperation transformClientOp(RacingEventServiceOperation serverOp) {
return serverOp;
}
@Override
public RacingEventServiceOperation transformServerOp(RacingEventServiceOperation clientOp) {
return clientOp;
}
};
}
} }
@@ -2,32 +2,28 @@ package com.sap.sailing.server.operationaltransformation;
import com.sap.sailing.server.RacingEventService; import com.sap.sailing.server.RacingEventService;
public class AddLeaderboard extends AbstractRacingEventServiceOperation { public class AddLeaderboard extends AbstractLeaderboardOperation {
private final String leaderboardName;
private final int[] discardThresholds; private final int[] discardThresholds;
public AddLeaderboard(String leaderboardName, int[] discardThresholds) { public AddLeaderboard(String leaderboardName, int[] discardThresholds) {
super(); super(leaderboardName);
this.leaderboardName = leaderboardName;
this.discardThresholds = discardThresholds; this.discardThresholds = discardThresholds;
} }
@Override @Override
public RacingEventService applyTo(RacingEventService toState) { public RacingEventService applyTo(RacingEventService toState) {
toState.addLeaderboard(leaderboardName, discardThresholds); toState.addLeaderboard(getLeaderboardName(), discardThresholds);
return toState; return toState;
} }
@Override @Override
public RacingEventServiceOperation transformClientOp(RacingEventServiceOperation serverOp) { public RacingEventServiceOperation transformClientOp(RacingEventServiceOperation serverOp) {
// TODO Auto-generated method stub return serverOp.transformAddLeaderboardClientOp(this);
return null;
} }
@Override @Override
public RacingEventServiceOperation transformServerOp(RacingEventServiceOperation clientOp) { public RacingEventServiceOperation transformServerOp(RacingEventServiceOperation clientOp) {
// TODO Auto-generated method stub return clientOp.transformAddLeaderboardServerOp(this);
return null;
} }
} }
@@ -1,6 +1,5 @@
package com.sap.sailing.server.operationaltransformation; package com.sap.sailing.server.operationaltransformation;
import com.sap.sailing.operationaltransformation.ClientServerOperationPair;
import com.sap.sailing.server.RacingEventService; import com.sap.sailing.server.RacingEventService;
public class MoveLeaderboardColumnDown extends AbstractLeaderboardColumnOperation { public class MoveLeaderboardColumnDown extends AbstractLeaderboardColumnOperation {
@@ -30,26 +29,4 @@ public class MoveLeaderboardColumnDown extends AbstractLeaderboardColumnOperatio
return null; return null;
} }
@Override
public RacingEventServiceOperation transformServerRemoveColumnFromLeaderboard(
RemoveColumnFromLeaderboard removeColumnFromLeaderboardServerOp) {
if (affectsSameColumn(removeColumnFromLeaderboardServerOp)) {
return ClientServerOperationPair.getNoOp();
} else {
return removeColumnFromLeaderboardServerOp;
}
}
@Override
public RacingEventServiceOperation transformClientRemoveColumnFromLeaderboard(
RemoveColumnFromLeaderboard removeColumnFromLeaderboardClientOp) {
if (affectsSameColumn(removeColumnFromLeaderboardClientOp)) {
return ClientServerOperationPair.getNoOp();
} else {
return removeColumnFromLeaderboardClientOp;
}
}
} }
@@ -1,6 +1,5 @@
package com.sap.sailing.server.operationaltransformation; package com.sap.sailing.server.operationaltransformation;
import com.sap.sailing.operationaltransformation.ClientServerOperationPair;
import com.sap.sailing.server.RacingEventService; import com.sap.sailing.server.RacingEventService;
public class MoveLeaderboardColumnUp extends AbstractLeaderboardColumnOperation { public class MoveLeaderboardColumnUp extends AbstractLeaderboardColumnOperation {
@@ -30,26 +29,4 @@ public class MoveLeaderboardColumnUp extends AbstractLeaderboardColumnOperation
return null; return null;
} }
@Override
public RacingEventServiceOperation transformServerRemoveColumnFromLeaderboard(
RemoveColumnFromLeaderboard removeColumnFromLeaderboardServerOp) {
if (affectsSameColumn(removeColumnFromLeaderboardServerOp)) {
return ClientServerOperationPair.getNoOp();
} else {
return removeColumnFromLeaderboardServerOp;
}
}
@Override
public RacingEventServiceOperation transformClientRemoveColumnFromLeaderboard(
RemoveColumnFromLeaderboard removeColumnFromLeaderboardClientOp) {
if (affectsSameColumn(removeColumnFromLeaderboardClientOp)) {
return ClientServerOperationPair.getNoOp();
} else {
return removeColumnFromLeaderboardClientOp;
}
}
} }
@@ -36,4 +36,12 @@ public interface RacingEventServiceOperation extends Operation<RacingEventServic
RacingEventServiceOperation transformClientRenameLeaderboardColumnOp(RenameLeaderboardColumn renameLeaderboardColumnClientOp); RacingEventServiceOperation transformClientRenameLeaderboardColumnOp(RenameLeaderboardColumn renameLeaderboardColumnClientOp);
RacingEventServiceOperation transformServerRenameLeaderboardColumnOp(RenameLeaderboardColumn renameLeaderboardColumnServerOp); RacingEventServiceOperation transformServerRenameLeaderboardColumnOp(RenameLeaderboardColumn renameLeaderboardColumnServerOp);
RacingEventServiceOperation transformAddLeaderboardClientOp(AddLeaderboard addLeaderboard);
RacingEventServiceOperation transformAddLeaderboardServerOp(AddLeaderboard addLeaderboard);
RacingEventServiceOperation transformRemoveLeaderboardClientOp(RemoveLeaderboard removeLeaderboard);
RacingEventServiceOperation transformRemoveLeaderboardServerOp(RemoveLeaderboard removeLeaderboard);
} }
@@ -1,6 +1,5 @@
package com.sap.sailing.server.operationaltransformation; package com.sap.sailing.server.operationaltransformation;
import com.sap.sailing.operationaltransformation.ClientServerOperationPair;
import com.sap.sailing.server.RacingEventService; import com.sap.sailing.server.RacingEventService;
public class RemoveColumnFromLeaderboard extends AbstractLeaderboardColumnOperation { public class RemoveColumnFromLeaderboard extends AbstractLeaderboardColumnOperation {
@@ -26,26 +25,4 @@ public class RemoveColumnFromLeaderboard extends AbstractLeaderboardColumnOperat
return clientOp.transformServerRemoveColumnFromLeaderboard(this); return clientOp.transformServerRemoveColumnFromLeaderboard(this);
} }
@Override
public RacingEventServiceOperation transformServerRemoveColumnFromLeaderboard(
RemoveColumnFromLeaderboard removeColumnFromLeaderboardServerOp) {
if (affectsSameColumn(removeColumnFromLeaderboardServerOp)) {
// skip server's remove and hence only apply the client's remove operation
return ClientServerOperationPair.getNoOp();
} else {
return removeColumnFromLeaderboardServerOp;
}
}
@Override
public RacingEventServiceOperation transformClientRemoveColumnFromLeaderboard(
RemoveColumnFromLeaderboard removeColumnFromLeaderboardClientOp) {
if (affectsSameColumn(removeColumnFromLeaderboardClientOp)) {
// skip client's remove and hence only apply the server's remove operation
return ClientServerOperationPair.getNoOp();
} else {
return removeColumnFromLeaderboardClientOp;
}
}
} }
@@ -0,0 +1,26 @@
package com.sap.sailing.server.operationaltransformation;
import com.sap.sailing.server.RacingEventService;
public class RemoveLeaderboard extends AbstractLeaderboardOperation {
public RemoveLeaderboard(String leaderboardName) {
super(leaderboardName);
}
@Override
public RacingEventServiceOperation transformClientOp(RacingEventServiceOperation serverOp) {
return serverOp.transformRemoveLeaderboardClientOp(this);
}
@Override
public RacingEventServiceOperation transformServerOp(RacingEventServiceOperation clientOp) {
return clientOp.transformRemoveLeaderboardServerOp(this);
}
@Override
public RacingEventService applyTo(RacingEventService toState) {
toState.removeLeaderboard(getLeaderboardName());
return toState;
}
}
@@ -1,6 +1,5 @@
package com.sap.sailing.server.operationaltransformation; package com.sap.sailing.server.operationaltransformation;
import com.sap.sailing.operationaltransformation.ClientServerOperationPair;
import com.sap.sailing.server.RacingEventService; import com.sap.sailing.server.RacingEventService;
public class RenameLeaderboardColumn extends AbstractLeaderboardColumnOperation { public class RenameLeaderboardColumn extends AbstractLeaderboardColumnOperation {
@@ -29,24 +28,4 @@ public class RenameLeaderboardColumn extends AbstractLeaderboardColumnOperation
return clientOp.transformServerRenameLeaderboardColumnOp(this); return clientOp.transformServerRenameLeaderboardColumnOp(this);
} }
@Override
public RacingEventServiceOperation transformServerRemoveColumnFromLeaderboard(
RemoveColumnFromLeaderboard removeColumnFromLeaderboardServerOp) {
if (affectsSameColumn(removeColumnFromLeaderboardServerOp)) {
return ClientServerOperationPair.getNoOp(); // don't need the rename anymore when column is removed
} else {
return removeColumnFromLeaderboardServerOp;
}
}
@Override
public RacingEventServiceOperation transformClientRemoveColumnFromLeaderboard(
RemoveColumnFromLeaderboard removeColumnFromLeaderboardClientOp) {
if (affectsSameColumn(removeColumnFromLeaderboardClientOp)) {
return ClientServerOperationPair.getNoOp(); // don't need the rename anymore when column is removed
} else {
return removeColumnFromLeaderboardClientOp;
}
}
} }