package voldemort.protocol;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import junit.framework.TestCase;
import org.junit.Test;
import voldemort.ServerTestUtils;
import voldemort.TestUtils;
import voldemort.VoldemortException;
import voldemort.client.protocol.RequestFormat;
import voldemort.client.protocol.RequestFormatFactory;
import voldemort.client.protocol.RequestFormatType;
import voldemort.server.RequestRoutingType;
import voldemort.server.StoreRepository;
import voldemort.server.protocol.RequestHandler;
import voldemort.store.memory.InMemoryStorageEngine;
import voldemort.utils.ByteArray;
import voldemort.versioning.ObsoleteVersionException;
import voldemort.versioning.VectorClock;
import voldemort.versioning.Version;
import voldemort.versioning.Versioned;
public abstract class AbstractRequestFormatTest extends TestCase {
private final String storeName;
private final RequestFormat clientWireFormat;
private final RequestHandler serverWireFormat;
private final InMemoryStorageEngine<ByteArray, byte[], byte[]> store;
private final RequestFormatType type;
public AbstractRequestFormatTest(RequestFormatType type) {
this.type = type;
this.storeName = "test";
this.store = new InMemoryStorageEngine<ByteArray, byte[], byte[]>(storeName);
StoreRepository repository = new StoreRepository();
repository.addLocalStore(store);
repository.addRoutedStore(store);
this.clientWireFormat = new RequestFormatFactory().getRequestFormat(type);
this.serverWireFormat = ServerTestUtils.getSocketRequestHandlerFactory(repository)
.getRequestHandler(type);
}
@Test
public void testNullKeys() throws Exception {
try {
testGetRequest(null, null, null, null, false);
fail("Null key allowed.");
} catch(IllegalArgumentException e) {
// this is good
}
try {
testGetAllRequest(new ByteArray[] { null }, null, null, null, new boolean[] { false });
fail("Null key allowed.");
} catch(IllegalArgumentException e) {
// this is good
}
try {
testPutRequest(null, null, null, null, null, true);
fail("Null key allowed.");
} catch(IllegalArgumentException e) {
// this is good
}
try {
testDeleteRequest(null, null, null, false);
fail("Null key allowed.");
} catch(IllegalArgumentException e) {
// this is good
}
}
@Test
public void testGetRequests() throws Exception {
testGetRequest(TestUtils.toByteArray("hello"), null, null, null, false);
testGetRequest(TestUtils.toByteArray("hello"), "".getBytes(), null, new VectorClock(), true);
testGetRequest(TestUtils.toByteArray("hello"),
"abc".getBytes(),
null,
TestUtils.getClock(1, 2, 2, 3),
true);
testGetRequest(TestUtils.toByteArray("hello"),
"abcasdf".getBytes(),
null,
TestUtils.getClock(1, 3, 4, 5),
true);
}
public void testGetRequest(ByteArray key,
byte[] value,
byte[] transforms,
VectorClock version,
boolean isPresent) throws Exception {
try {
if(isPresent) {
testPutRequest(key, value, null, version, null, false);
}
ByteArrayOutputStream getRequest = new ByteArrayOutputStream();
this.clientWireFormat.writeGetRequest(new DataOutputStream(getRequest),
storeName,
key,
transforms,
RequestRoutingType.NORMAL);
ByteArrayOutputStream getResponse = handleRequest(getRequest);
testIsCompleteGetResponse(getResponse);
List<Versioned<byte[]>> values = this.clientWireFormat.readGetResponse(inputStream(getResponse));
if(isPresent) {
assertEquals(1, values.size());
Versioned<byte[]> v = values.get(0);
assertEquals(version, v.getVersion());
assertTrue(Arrays.equals(v.getValue(), value));
} else {
assertEquals(0, values.size());
}
} finally {
this.store.deleteAll();
}
}
// @Test
public void testGetVersionRequest() throws Exception {
testGetVersionRequest(TestUtils.toByteArray("hello"), null, null, false);
testGetVersionRequest(TestUtils.toByteArray("hello"),
"".getBytes(),
new VectorClock(),
true);
testGetVersionRequest(TestUtils.toByteArray("hello"),
"abc".getBytes(),
TestUtils.getClock(1, 2, 2, 3),
true);
testGetVersionRequest(TestUtils.toByteArray("hello"),
"abcasdf".getBytes(),
TestUtils.getClock(1, 3, 4, 5),
true);
}
public void testGetVersionRequest(ByteArray key,
byte[] value,
VectorClock version,
boolean isPresent) throws Exception {
try {
if(isPresent) {
testPutRequest(key, value, null, version, null, false);
}
ByteArrayOutputStream getVersionRequest = new ByteArrayOutputStream();
this.clientWireFormat.writeGetVersionRequest(new DataOutputStream(getVersionRequest),
storeName,
key,
RequestRoutingType.NORMAL);
ByteArrayOutputStream getVersionResponse = handleRequest(getVersionRequest);
testIsCompleteGetVersionResponse(getVersionResponse);
List<Version> values = this.clientWireFormat.readGetVersionResponse(inputStream(getVersionResponse));
if(isPresent) {
assertEquals(1, values.size());
VectorClock returnValue = (VectorClock) values.get(0);
assertEquals(version, returnValue);
assertEquals(version.getTimestamp(), returnValue.getTimestamp());
} else {
assertEquals(0, values.size());
}
} finally {
this.store.deleteAll();
}
}
@Test
public void testGetAllRequests() throws Exception {
testGetAllRequest(new ByteArray[] {},
new byte[][] {},
null,
new VectorClock[] {},
new boolean[] {});
testGetAllRequest(new ByteArray[] { new ByteArray() },
new byte[][] { new byte[] {} },
null,
new VectorClock[] { new VectorClock() },
new boolean[] { true });
testGetAllRequest(new ByteArray[] { TestUtils.toByteArray("hello") },
new byte[][] { "world".getBytes() },
null,
new VectorClock[] { new VectorClock() },
new boolean[] { true });
testGetAllRequest(new ByteArray[] { TestUtils.toByteArray("hello"),
TestUtils.toByteArray("holly") },
new byte[][] { "world".getBytes(), "cow".getBytes() },
null,
new VectorClock[] { TestUtils.getClock(1, 1), TestUtils.getClock(1, 2) },
new boolean[] { true, false });
}
public void testGetAllRequest(ByteArray[] keys,
byte[][] values,
Map<ByteArray, byte[]> transforms,
VectorClock[] versions,
boolean[] isFound) throws Exception {
try {
for(int i = 0; i < keys.length; i++) {
if(isFound[i])
testPutRequest(keys[i], values[i], null, versions[i], null, false);
}
ByteArrayOutputStream getAllRequest = new ByteArrayOutputStream();
this.clientWireFormat.writeGetAllRequest(new DataOutputStream(getAllRequest),
storeName,
Arrays.asList(keys),
transforms,
RequestRoutingType.NORMAL);
ByteArrayOutputStream getAllResponse = handleRequest(getAllRequest);
testIsCompleteGetAllResponse(getAllResponse);
Map<ByteArray, List<Versioned<byte[]>>> found = this.clientWireFormat.readGetAllResponse(inputStream(getAllResponse));
for(int i = 0; i < keys.length; i++) {
if(isFound[i]) {
assertTrue(keys[i] + " is not in the found set.", found.containsKey(keys[i]));
assertEquals(1, found.get(keys[i]).size());
Versioned<byte[]> versioned = found.get(keys[i]).get(0);
assertEquals(versions[i], versioned.getVersion());
assertTrue(Arrays.equals(values[i], versioned.getValue()));
} else {
assertTrue(keys[i] + " is in the found set but should not be.",
!found.containsKey(keys[i]));
}
}
} finally {
this.store.deleteAll();
}
}
@Test
public void testPutRequests() throws Exception {
testPutRequest(new ByteArray(), new byte[0], null, new VectorClock(), null, true);
testPutRequest(TestUtils.toByteArray("hello"),
"world".getBytes(),
null,
new VectorClock(),
null,
false);
testPutRequest(TestUtils.toByteArray("hello"),
"world".getBytes(),
null,
new VectorClock(),
ObsoleteVersionException.class,
true);
}
public void testPutRequest(ByteArray key,
byte[] value,
byte[] transforms,
VectorClock version,
Class<? extends VoldemortException> exception,
boolean deleteFinally) throws Exception {
try {
ByteArrayOutputStream putRequest = new ByteArrayOutputStream();
this.clientWireFormat.writePutRequest(new DataOutputStream(putRequest),
storeName,
key,
value,
transforms,
version,
RequestRoutingType.NORMAL);
ByteArrayOutputStream putResponse = handleRequest(putRequest);
testIsCompletePutResponse(putResponse);
this.clientWireFormat.readPutResponse(inputStream(putResponse));
TestUtils.assertContains(this.store, key, value);
} catch(IllegalArgumentException e) {
throw e;
} catch(Exception e) {
assertEquals("Unexpected exception " + e.getClass().getName(), e.getClass(), exception);
} finally {
if(deleteFinally) {
this.store.deleteAll();
}
}
}
@Test
public void testDeleteRequests() throws Exception {
// test pre-existing are deleted
testDeleteRequest(new ByteArray(),
new VectorClock(),
new Versioned<byte[]>("hello".getBytes()),
true);
testDeleteRequest(TestUtils.toByteArray("hello"),
new VectorClock(),
new Versioned<byte[]>("world".getBytes()),
true);
// test non-existant aren't deleted
testDeleteRequest(TestUtils.toByteArray("hello"), new VectorClock(), null, false);
}
public void testDeleteRequest(ByteArray key,
VectorClock version,
Versioned<byte[]> existingValue,
boolean isDeleted) throws Exception {
try {
if(existingValue != null) {
testPutRequest(key,
existingValue.getValue(),
null,
(VectorClock) existingValue.getVersion(),
null,
false);
}
ByteArrayOutputStream delRequest = new ByteArrayOutputStream();
this.clientWireFormat.writeDeleteRequest(new DataOutputStream(delRequest),
storeName,
key,
version,
RequestRoutingType.NORMAL);
ByteArrayOutputStream delResponse = handleRequest(delRequest);
testIsCompleteDeleteResponse(delResponse);
boolean wasDeleted = this.clientWireFormat.readDeleteResponse(inputStream(delResponse));
assertEquals(isDeleted, wasDeleted);
} finally {
this.store.deleteAll();
}
}
private ByteArrayOutputStream handleRequest(ByteArrayOutputStream input) throws Exception {
testIsCompleteRequest(input);
ByteArrayOutputStream response = new ByteArrayOutputStream();
this.serverWireFormat.handleRequest(inputStream(input), new DataOutputStream(response));
return response;
}
public DataInputStream inputStream(ByteArrayOutputStream output) {
return new DataInputStream(new ByteArrayInputStream(output.toByteArray()));
}
public void testIsCompleteGetResponse(ByteArrayOutputStream response) {
ByteBuffer buffer = ByteBuffer.wrap(response.toByteArray());
int entryPosition = buffer.position();
int limit = buffer.limit();
for(int i = 0; i < limit; i++) {
positionBuffer(buffer, entryPosition, i);
boolean isCompleteResponse = this.clientWireFormat.isCompleteGetResponse(buffer);
assertFalse(" Partial response should be inComplete", isCompleteResponse);
}
positionBuffer(buffer, entryPosition, limit);
boolean isCompleteResponse = this.clientWireFormat.isCompleteGetResponse(buffer);
assertTrue(" Full response should be complete", isCompleteResponse);
positionBuffer(buffer, entryPosition, limit);
}
public void testIsCompleteGetAllResponse(ByteArrayOutputStream response) {
ByteBuffer buffer = ByteBuffer.wrap(response.toByteArray());
int entryPosition = buffer.position();
int limit = buffer.limit();
for(int i = 0; i < limit; i++) {
positionBuffer(buffer, entryPosition, i);
boolean isCompleteResponse = this.clientWireFormat.isCompleteGetAllResponse(buffer);
assertFalse(" Partial response should be inComplete", isCompleteResponse);
}
positionBuffer(buffer, entryPosition, limit);
boolean isCompleteResponse = this.clientWireFormat.isCompleteGetAllResponse(buffer);
assertTrue(" Full response should be complete", isCompleteResponse);
positionBuffer(buffer, entryPosition, limit);
}
public void testIsCompletePutResponse(ByteArrayOutputStream response) {
ByteBuffer buffer = ByteBuffer.wrap(response.toByteArray());
int entryPosition = buffer.position();
int limit = buffer.limit();
for(int i = 0; i < limit; i++) {
positionBuffer(buffer, entryPosition, i);
boolean isCompleteResponse = this.clientWireFormat.isCompletePutResponse(buffer);
assertFalse(" Partial response should be inComplete", isCompleteResponse);
}
positionBuffer(buffer, entryPosition, limit);
boolean isCompleteResponse = this.clientWireFormat.isCompletePutResponse(buffer);
assertTrue(" Full response should be complete", isCompleteResponse);
positionBuffer(buffer, entryPosition, limit);
}
public void testIsCompleteGetVersionResponse(ByteArrayOutputStream response) {
ByteBuffer buffer = ByteBuffer.wrap(response.toByteArray());
int entryPosition = buffer.position();
int limit = buffer.limit();
for(int i = 0; i < limit; i++) {
positionBuffer(buffer, entryPosition, i);
boolean isCompleteResponse = this.clientWireFormat.isCompleteGetVersionResponse(buffer);
assertFalse(" Partial response should be inComplete", isCompleteResponse);
}
positionBuffer(buffer, entryPosition, limit);
boolean isCompleteResponse = this.clientWireFormat.isCompleteGetVersionResponse(buffer);
assertTrue(" Full response should be complete", isCompleteResponse);
positionBuffer(buffer, entryPosition, limit);
}
public void testIsCompleteDeleteResponse(ByteArrayOutputStream response) {
ByteBuffer buffer = ByteBuffer.wrap(response.toByteArray());
int entryPosition = buffer.position();
int limit = buffer.limit();
for(int i = 0; i < limit; i++) {
positionBuffer(buffer, entryPosition, i);
boolean isCompleteResponse = this.clientWireFormat.isCompleteDeleteResponse(buffer);
assertFalse(" Partial response should be inComplete", isCompleteResponse);
}
positionBuffer(buffer, entryPosition, limit);
boolean isCompleteResponse = this.clientWireFormat.isCompleteDeleteResponse(buffer);
assertTrue(" Full response should be complete", isCompleteResponse);
positionBuffer(buffer, entryPosition, limit);
}
public void testIsCompleteRequest(ByteArrayOutputStream request) {
ByteBuffer buffer = ByteBuffer.wrap(request.toByteArray());
int entryPosition = buffer.position();
int limit = buffer.limit();
for(int i = 0; i < limit; i++) {
positionBuffer(buffer, entryPosition, i);
boolean isCompleteRequest = this.serverWireFormat.isCompleteRequest(buffer);
assertFalse(" Partial requests should be inComplete", isCompleteRequest);
}
positionBuffer(buffer, entryPosition, limit);
boolean isCompleteRequest = this.serverWireFormat.isCompleteRequest(buffer);
assertTrue(" Full request should be complete", isCompleteRequest);
positionBuffer(buffer, entryPosition, limit);
}
private void positionBuffer(ByteBuffer buffer, int position, int limit) {
buffer.position(position);
buffer.limit(limit);
}
}