package com.sap.runlet.operationaltransformation.test;
import java.util.Random;
import java.util.UUID;
import junit.framework.TestCase;
import com.sap.runlet.operationaltransformation.AbstractOperation;
import com.sap.runlet.operationaltransformation.ClientServerOperationPair;
import com.sap.runlet.operationaltransformation.Peer;
import com.sap.runlet.operationaltransformation.PeerImpl;
import com.sap.runlet.operationaltransformation.Transformer;
import com.sap.runlet.operationaltransformation.Peer.Role;
import com.sap.runlet.operationaltransformation.test.util.Base64;
public class OperationalTransformationTests extends TestCase {
private Peer<StringInsertOperation, StringState> server;
private Peer<StringInsertOperation, StringState> client1, client2;
public static class StringInsertOperation extends AbstractOperation<StringState> {
private int pos;
private String s;
public StringInsertOperation(int pos, String s) {
this.pos = pos;
this.s = s;
}
public StringInsertOperation clone() {
return (StringInsertOperation) super.clone();
}
public int getPos() {
return pos;
}
public String getS() {
return s;
}
public String toString() {
return "insert("+getPos()+", \""+getS()+"\")";
}
@Override
public StringState applyTo(StringState toState) {
return toState.apply(this);
}
}
public static class StringInsertTransformer implements Transformer<StringInsertOperation> {
@Override
public ClientServerOperationPair<StringInsertOperation> transform(StringInsertOperation clientOp, StringInsertOperation serverOp) {
StringInsertOperation resultClientOp;
StringInsertOperation resultServerOp;
if (clientOp.getPos() >= serverOp.getPos()) {
resultServerOp = serverOp;
resultClientOp = new StringInsertOperation(clientOp.getPos()+serverOp.getS().length(), clientOp.getS());
} else {
resultClientOp = clientOp;
resultServerOp = new StringInsertOperation(serverOp.getPos()+clientOp.getS().length(), serverOp.getS());
}
return new ClientServerOperationPair<StringInsertOperation>(resultClientOp, resultServerOp);
}
}
public static class StringState {
private String state;
private UUID id;
public String getState() {
return state;
}
public boolean equals(Object o) {
return o instanceof StringState &&
this.getState().equals(((StringState) o).getState()) &&
this.id.equals(((StringState) o).id);
}
public int hashCode() {
return getState().hashCode() ^ id.hashCode();
}
public StringState(String state) {
super();
id = UUID.randomUUID();
this.state = state;
}
public StringState apply(StringInsertOperation operation) {
String s = this.getState().substring(0, operation.getPos()) +
operation.getS()+this.getState().substring(operation.getPos());
StringState result = new StringState(s);
return result;
}
private UUID getId() {
return id;
}
protected void setId(UUID id) {
this.id = id;
}
public String toString() {
return "\""+getState()+"\" @ "+getId();
}
}
public static class SimpleClient extends PeerImpl<StringInsertOperation, StringState> {
public SimpleClient(String name, StringState initialState) {
super(name, new StringInsertTransformer(), initialState, Role.SERVER);
}
}
public void setUp() {
server = new PeerImpl<StringInsertOperation, StringState>(
"Server", new StringInsertTransformer(), new StringState(""), Role.SERVER);
client1 = new PeerImpl<StringInsertOperation, StringState>("Client1", new StringInsertTransformer(), server);
client2 = new PeerImpl<StringInsertOperation, StringState>("Client2", new StringInsertTransformer(), server);
}
public void testBasicTransformation() {
client1.apply(new StringInsertOperation(0, "abc"));
client2.apply(new StringInsertOperation(0, "def"));
client1.waitForNotRunning();
client2.waitForNotRunning();
server.waitForNotRunning();
assertEquals(server.getCurrentState().getState(), client1.getCurrentState().getState());
assertEquals(server.getCurrentState().getState(), client2.getCurrentState().getState());
assertEquals(6, server.getCurrentState().getState().length());
assertTrue(server.getCurrentState().getState().equals("abcdef") || server.getCurrentState().getState().equals("defabc"));
}
public void testTwoMassInserts() {
final int COUNT = 100;
for (int i = 0; i < COUNT; i++) {
client1.apply(new StringInsertOperation(0, "abc"));
client2.apply(new StringInsertOperation(0, "def"));
}
client1.waitForNotRunning();
client2.waitForNotRunning();
server.waitForNotRunning();
assertEquals(server.getCurrentState().getState(), client1.getCurrentState().getState());
assertEquals(server.getCurrentState().getState(), client2.getCurrentState().getState());
assertEquals(6*COUNT, server.getCurrentState().getState().length());
}
/**
* Randomizes the number of changes applied to each client per iteration,
* the insert position and the string to be inserted.
*/
public void testRandomInserts() {
final int COUNT = 100;
Random r = new Random();
int totalLength = 0;
for (int i = 0; i < COUNT; i++) {
for (int j = r.nextInt(10); j > 0; j--) {
byte[] b = new byte[r.nextInt(10)];
r.nextBytes(b);
String s = Base64.encode(b);
client1.apply(new StringInsertOperation(
r.nextInt(client1.getCurrentState().getState().length()+1), s));
totalLength += s.length();
}
for (int j = r.nextInt(10); j > 0; j--) {
byte[] b = new byte[r.nextInt(10)];
r.nextBytes(b);
String s = Base64.encode(b);
client2.apply(new StringInsertOperation(
r.nextInt(client2.getCurrentState().getState().length()+1), s));
totalLength += s.length();
}
}
client1.waitForNotRunning();
client2.waitForNotRunning();
server.waitForNotRunning();
assertEquals(server.getCurrentState().getState(), client1.getCurrentState().getState());
assertEquals(server.getCurrentState().getState(), client2.getCurrentState().getState());
assertEquals(totalLength, server.getCurrentState().getState().length());
}
public void testTwoMassInsertsWithServerReplica() {
Peer<StringInsertOperation, StringState> server2 = new PeerImpl<StringInsertOperation, StringState>("Server2",
new StringInsertTransformer(), server);
final int COUNT = 100;
for (int i = 0; i < COUNT; i++) {
client1.apply(new StringInsertOperation(0, "abc"));
client2.apply(new StringInsertOperation(0, "def"));
}
client1.waitForNotRunning();
client2.waitForNotRunning();
server.waitForNotRunning();
server2.waitForNotRunning();
assertEquals(server.getCurrentState().getState(), client1.getCurrentState().getState());
assertEquals(server.getCurrentState().getState(), client2.getCurrentState().getState());
assertEquals(server.getCurrentState().getState(), server2.getCurrentState().getState());
assertEquals(6*COUNT, server2.getCurrentState().getState().length());
}
}