/**
* Copyright 2013-2015 Seagate Technology LLC.
*
* 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
* https://mozilla.org/MP:/2.0/.
*
* This program is distributed in the hope that it will be useful,
* but is provided AS-IS, WITHOUT ANY WARRANTY; including without
* the implied warranty of MERCHANTABILITY, NON-INFRINGEMENT or
* FITNESS FOR A PARTICULAR PURPOSE. See the Mozilla Public
* License for more details.
*
* See www.openkinetic.org for more project information
*/
package com.seagate.kinetic.simulator.client.sanity;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertTrue;
import static com.seagate.kinetic.KineticTestHelpers.toByteArray;
import java.io.StringWriter;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import kinetic.client.AsyncKineticException;
import kinetic.client.CallbackHandler;
import kinetic.client.CallbackResult;
import kinetic.client.Entry;
import kinetic.client.EntryMetadata;
import kinetic.client.KineticException;
import org.testng.Assert;
import org.testng.annotations.Test;
import com.seagate.kinetic.IntegrationTestCase;
import com.seagate.kinetic.proto.Kinetic.Command.Algorithm;
@Test(groups = {"simulator"})
public class KineticSanityTest extends IntegrationTestCase {
Logger logger = Logger.getLogger(KineticSanityTest.class.getName());
@Test(dataProvider = "transportProtocolOptions")
public void sanityTest(String clientName) throws Exception {
/**
* start playing with the APIs.
*/
Random random = new Random();
byte[] key = new byte[20];
random.nextBytes(key);
byte[] value = toByteArray("world");
byte[] initVersion = new byte[20];
random.nextBytes(initVersion);
EntryMetadata entryMetadata = new EntryMetadata();
entryMetadata.setAlgorithm(Algorithm.SHA2.toString());
Entry versioned = new Entry(key, value, entryMetadata);
/**
* put key/value and validate.
*/
Entry dbVersioned = getClient(clientName).put(versioned, initVersion);
Entry vFromDb = getClient(clientName).get(key);
assertTrue(Arrays.equals(vFromDb.getValue(), value));
/**
* get metadata only
*/
EntryMetadata metadata = getClient(clientName).getMetadata(key);
assertTrue(Arrays.equals(metadata.getVersion(), initVersion));
assertTrue(Algorithm.SHA2.toString().equals(
metadata.getAlgorithm().toString()));
byte[] newWorld = toByteArray("new world");
// set value to "new world" for the entry.
dbVersioned.setValue(newWorld);
EntryMetadata entryMetadata1 = new EntryMetadata();
entryMetadata1.setVersion(initVersion);
dbVersioned.setEntryMetadata(entryMetadata1);
/**
* update entry with new version in the persistent store
*/
Entry dbVersioned2 = getClient(clientName).put(dbVersioned, toByteArray("2"));
assertTrue(Arrays.equals(dbVersioned2.getEntryMetadata().getVersion(),
toByteArray("2")));
// read from db
Entry v2FromDb = getClient(clientName).get(key);
// validate value was put successfully
assertTrue(Arrays.equals(v2FromDb.getValue(), newWorld));
/**
* get metadata only
*/
EntryMetadata metadata2 = getClient(clientName).getMetadata(key);
assertTrue(Arrays.equals(metadata2.getVersion(), toByteArray("2")));
logger.info("put/get twice successfully, deleting entry ...");
// delete entry
getClient(clientName).delete(dbVersioned2);
// read and validate deleted
Entry versionedGetFromDb = getClient(clientName).get(key);
assertTrue(versionedGetFromDb == null);
logger.info("sanity test for put/get/delete passed");
}
@Test(dataProvider = "transportProtocolOptions")
public void noopTest(String clientName) throws Exception {
/**
* noop test
*/
long time = getClient(clientName).noop();
/**
* noop returns successfully with round-trip time in milliseconds.
* Otherwise, an exception is thrown.
*/
assertTrue(time >= 0);
}
@Test(dataProvider = "transportProtocolOptions")
public void maxValueSizeTest(String clientName) throws Exception {
byte[] value = new byte[(1024 * 1024) + 1];
EntryMetadata entryMetadata = new EntryMetadata();
entryMetadata.setAlgorithm(Algorithm.SHA1.toString());
Entry entry = new Entry("maxValueSizeTest".getBytes(), value,
entryMetadata);
try {
getClient(clientName).putForced(entry);
throw new RuntimeException("expect exception is not thrown");
} catch (KineticException e) {
e.printStackTrace();
// expected exception was thrown
logger.info("received expected exception: " + e.getMessage());
}
}
@Test(dataProvider = "transportProtocolOptions")
public void putForcedTest(String clientName) throws Exception {
/**
* start playing with the APIs.
*/
Random random = new Random();
byte[] key = new byte[20];
random.nextBytes(key);
byte[] value = toByteArray("initValue");
byte[] initVersion = new byte[20];
random.nextBytes(initVersion);
EntryMetadata entryMetadata = new EntryMetadata();
entryMetadata.setAlgorithm(Algorithm.SHA1.toString());
Entry versioned = new Entry(key, value, entryMetadata);
/**
* put key/value and validate.
*/
Entry dbVersioned = getClient(clientName).put(versioned, initVersion);
Entry vFromDb = getClient(clientName).get(key);
assertTrue(Arrays.equals(vFromDb.getValue(), value));
// do forced put
byte[] forcedPutValue = toByteArray("ForcedPutValue");
byte[] forcedPutVersion = new byte[20];
random.nextBytes(forcedPutVersion);
EntryMetadata entryMetadata2 = new EntryMetadata();
entryMetadata2.setAlgorithm(Algorithm.SHA1.toString());
// Entry versioned2 = new Entry(key, value, entryMetadata2);
entryMetadata2.setVersion(forcedPutVersion);
dbVersioned.setEntryMetadata(entryMetadata2);
dbVersioned.setValue(forcedPutValue);
/**
* forced put
*/
Entry dbVersioned2 = getClient(clientName).putForced(dbVersioned);
assertTrue(Arrays.equals(dbVersioned2.getEntryMetadata().getVersion(),
forcedPutVersion));
// read from db
Entry v2FromDb = getClient(clientName).get(key);
// validate value was put successfully
assertTrue(Arrays.equals(v2FromDb.getValue(), forcedPutValue));
logger.info("put/get twice successfully, deleting entry ...");
// delete entry
boolean deleted = getClient(clientName).deleteForced(key);
assertTrue(deleted == true);
boolean deleted2 = getClient(clientName).deleteForced(key);
assertTrue(deleted2 == true);
Entry entry3 = getClient(clientName).get(key);
assertTrue(entry3 == null);
logger.info("forced put/delete passed");
}
@Test(dataProvider = "transportProtocolOptions")
public void getAsyncTest(String clientName) throws Exception {
int max = 10;
List<Entry> masterList = new ArrayList<Entry>(max);
List<String> dataList = new ArrayList<String>(max);
// for put reply
final List<Entry> putList = new ArrayList<Entry>(max);
final List<Entry> getList = new ArrayList<Entry>(max);
long start = System.nanoTime();
// get signal
final CountDownLatch getSignal = new CountDownLatch(max);
// put signal
final CountDownLatch putSignal = new CountDownLatch(max);
// init and put data entries
for (int i = 0; i < max; i++) {
byte[] data = ByteBuffer.allocate(8).putLong(start + i).array();
dataList.add(new String(data));
logger.info("adding data index=" + i);
// construct/put data to db
EntryMetadata entryMetadata = new EntryMetadata();
Entry versioned = new Entry(data, data, entryMetadata);
EntryMetadata masterMetadata = new EntryMetadata();
masterMetadata.setVersion(data);
Entry masterEntry = new Entry(data, data, masterMetadata);
masterList.add(masterEntry);
getClient(clientName).putAsync(versioned, data, new CallbackHandler<Entry>() {
@Override
public void onSuccess(CallbackResult<Entry> result) {
// add to list
logger.fine(" putAsync received: "
+ result.getResponseMessage());
putList.add(result.getResult());
putSignal.countDown();
}
@Override
public void onError(AsyncKineticException exception) {
// TODO Auto-generated method stub
exception.printStackTrace();
}
});
}
putSignal.await(10, TimeUnit.SECONDS);
assertEquals(max, putList.size());
// The following validation assumes messages are in serial order per I/O
// socket.
for (int i = 0; i < max; i++) {
assertTrue(dataList.contains(new String(putList.get(i).getKey())));
assertTrue(dataList.contains(new String(putList.get(i).getValue())));
assertTrue(dataList.contains(new String(putList.get(i)
.getEntryMetadata().getVersion())));
}
// getAsync
for (int i = 0; i < max; i++) {
final byte[] key = putList.get(i).getKey();
this.getClient(clientName).getAsync(key, new CallbackHandler<Entry>() {
@Override
public void onSuccess(CallbackResult<Entry> result) {
logger.fine(" getAsync received: "
+ result.getResponseMessage());
getList.add(result.getResult());
getSignal.countDown();
}
@Override
public void onError(AsyncKineticException exception) {
logger.log(Level.WARNING, exception.getMessage(), exception);
}
});
}
getSignal.await(10, TimeUnit.SECONDS);
assertEquals(max, getList.size());
// The following validation assumes messages are in serial order per I/O
// socket.
for (int i = 0; i < max; i++) {
assertTrue(dataList.contains(new String(putList.get(i).getKey())));
assertTrue(dataList.contains(new String(putList.get(i).getValue())));
assertTrue(dataList.contains(new String(putList.get(i)
.getEntryMetadata().getVersion())));
}
// clean up
for (int i = 0; i < max; i++) {
boolean deleted = getClient(clientName).delete(putList.get(i));
assertTrue(deleted == true);
}
// verify clean up
for (int i = 0; i < max; i++) {
Entry v = getClient(clientName).get(putList.get(i).getKey());
assertTrue(v == null);
}
logger.info("getAsyncTest passed ...");
}
@Test(dataProvider = "transportProtocolOptions")
public void getNextTest(String clientName) throws Exception {
List<byte[]> dataList = new ArrayList<byte[]>(10);
List<Entry> versionedList = new ArrayList<Entry>(10);
long start = System.nanoTime();
int max = 10;
// init and put data entries
for (int i = 0; i < max; i++) {
byte[] data = ByteBuffer.allocate(8).putLong(start + i).array();
// add to cache
dataList.add(data);
logger.info("adding data index=" + i);
// construct/put data to db
EntryMetadata entryMetadata = new EntryMetadata();
Entry versioned = new Entry(data, data, entryMetadata);
Entry dbVersioned = getClient(clientName).put(versioned, data);
logger.info("added data index=" + i);
versionedList.add(dbVersioned);
}
// verify getNext
for (int i = 0; i < (max - 1); i++) {
Entry nextVersioned = getClient(clientName).getNext(versionedList.get(i)
.getKey());
byte[] nextKey = nextVersioned.getKey();
assertTrue(Arrays
.equals(nextKey, versionedList.get(i + 1).getKey()));
}
try {
Entry nextVersioned = getClient(clientName).getNext(versionedList.get(max - 1)
.getKey());
assertTrue(nextVersioned == null);
} catch (Exception e) {
Assert.fail("caught exception: " + e.getMessage());
}
// clean up
for (int i = 0; i < max; i++) {
boolean deleted = getClient(clientName).delete(versionedList.get(i));
assertTrue(deleted == true);
}
// verify clean up
for (int i = 0; i < max; i++) {
Entry v = getClient(clientName).get(versionedList.get(i).getKey());
assertTrue(v == null);
}
logger.info("getNextTest passed ...");
}
@Test(dataProvider = "transportProtocolOptions")
public void getPreviousTest(String clientName) throws Exception {
List<byte[]> dataList = new ArrayList<byte[]>(10);
List<Entry> versionedList = new ArrayList<Entry>(10);
long start = System.nanoTime();
int max = 10;
// init and put entry
for (int i = 0; i < max; i++) {
byte[] data = ByteBuffer.allocate(8).putLong(start + i).array();
// add to cache
dataList.add(data);
logger.info("adding data index=" + i);
// construct/put data to db
EntryMetadata entryMetadata = new EntryMetadata();
Entry versioned = new Entry(data, data, entryMetadata);
Entry dbVersioned = getClient(clientName).put(versioned, data);
logger.info("added data index=" + i);
versionedList.add(dbVersioned);
}
// verify get previous
for (int i = 0; i < max - 1; i++) {
Entry previousVersioned = getClient(clientName).getPrevious(versionedList.get(
max - i - 1).getKey());
byte[] previousKey = previousVersioned.getKey();
assertTrue(Arrays.equals(previousKey, versionedList
.get(max - i - 2).getKey()));
}
try {
Entry previousVersioned = getClient(clientName).getPrevious(versionedList.get(
0).getKey());
assertTrue(previousVersioned == null);
} catch (Exception e) {
// must not throw exception
Assert.fail("caught exception: " + e.getClass().getName());
}
// clean up
for (int i = 0; i < max; i++) {
boolean deleted = getClient(clientName).delete(versionedList.get(i));
assertTrue(deleted == true);
}
// verify clean up
for (int i = 0; i < max; i++) {
Entry v = getClient(clientName).get(versionedList.get(i).getKey());
assertTrue(v == null);
}
logger.info("getPreviousTest passed ...");
}
@Test
public void permuteTest() {
// create a list from 1 to 20 in order
List<Integer> l = new LinkedList<Integer>();
for (int i = 0; i < 20; i++) {
l.add(i + 1);
}
logger.info(toString("Original List: ", l));
// Permute that list
@SuppressWarnings("unchecked")
List<Integer> pl = permute(l);
// Write it out...
logger.info(toString("Permuted List: ", pl));
// sort it back...
ArrayList<Integer> al = new ArrayList<Integer>();
for (int i = 0; i < 20; i++) {
al.add(0);
}
for (int x : pl) {
if (0 != al.set(x - 1, x))
Assert.fail("over wrote entry " + x);
}
logger.info(toString("Stored List: ", al));
// make sure they are equal
assertTrue(Arrays.equals(l.toArray(), al.toArray()));
}
// The following randomly permutes of a list.
// this algorithm is from Knuth volume 2, algorithm 3.4.2.P
@SuppressWarnings({ "unchecked", "rawtypes" })
private List permute(List<?> l) {
Random r = new Random();
List f = new ArrayList(l);
for (int j = f.size() - 1; j > 1; j--) {
int k = (int) (r.nextFloat() * j);
f.set(k, f.set(j, f.get(k)));
}
return f;
}
// helper to print out the members of a list
private String toString(String s1, List<?> l) {
StringWriter s = new StringWriter();
s.write(s1);
for (Object x : l) {
s.write(" " + x);
}
return s.toString();
}
// TODO modify
// @Test
// public void jimsGetNextTest() throws Exception {
//
// // Create an ordered list of 120 keys. This will be used to check
// // the ordering of the results.
// List<Entry> ordered = new LinkedList<Entry>();
// for (byte i = 0; i < 20; i++) {
// ordered.add(new Entry(new byte[] { 0, i }, null, null));
// for (byte j = 0; j < 5; j++) {
// ordered.add(new Entry(new byte[] { 0, i, j }, null, null));
// }
// }
//
// for (Entry v : ordered) {
// getClient().delete(v);
// }
//
// @SuppressWarnings("unchecked")
// List<Entry> permuted = permute(ordered);
//
// for (Entry v : permuted) {
// getClient().put(v);
// }
//
// // check various values of n.
// Iterable<byte[]> read = getClient().getKeyRange(new byte[] { 0 }, true,
// new byte[] { 1 }, true);
//
// int pos = 0;
// for (byte[] key : read) {
// Arrays.equals(ordered.get(pos++).getKey(), key);
// }
//
// for (Entry v : ordered) {
// assertTrue(getClient().delete(v));
// }
// }
@Test(dataProvider = "transportProtocolOptions")
public void getKeyRangeTest(String clientName) throws Exception {
List<byte[]> dataList = new ArrayList<byte[]>(10);
List<Entry> versionedList = new ArrayList<Entry>(10);
long start = System.nanoTime();
int max = 10;
// init and put data entries
for (int i = 0; i < max; i++) {
byte[] data = ByteBuffer.allocate(8).putLong(start + i).array();
// add to cache
dataList.add(data);
logger.info("adding data index=" + i);
// construct/put data to db
EntryMetadata entryMetadata = new EntryMetadata();
Entry versioned = new Entry(data, data, entryMetadata);
Entry dbVersioned = getClient(clientName).put(versioned, data);
logger.info("added data index=" + i + ", key="
+ ByteBuffer.wrap(data).getLong());
versionedList.add(dbVersioned);
}
// verify getRange
int startIndex = 3;
boolean startInclusive = true;
int endIndex = 7;
boolean endInclusive = false;
int expectReturnSize = endIndex - startIndex;
List<byte[]> rangeKeys = getClient(clientName).getKeyRange(
versionedList.get(startIndex).getKey(), startInclusive,
versionedList.get(endIndex).getKey(), endInclusive,
expectReturnSize);
logger.info("get key range finished");
int pos = 0;
for (byte[] key : rangeKeys) {
assertTrue(Arrays.equals(key, versionedList.get(startIndex + pos)
.getKey()));
pos++;
}
assertTrue(pos == expectReturnSize);
// clean up
for (int i = 0; i < max; i++) {
boolean deleted = getClient(clientName).delete(versionedList.get(i));
assertTrue(deleted == true);
}
// verify clean up
for (int i = 0; i < max; i++) {
Entry v = getClient(clientName).get(versionedList.get(i).getKey());
assertTrue(v == null);
}
logger.info("getRangeTest passed ...");
}
}