/*
* ALMA - Atacama Large Millimiter Array
* (c) European Southern Observatory, 2002
* Copyright by ESO (in the framework of the ALMA collaboration)
* and Cosylab 2002, All rights reserved
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
package alma.acs.util.stringqueue.test;
import java.util.Random;
import java.util.Vector;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import alma.acs.util.stringqueue.QueueEntry;
import alma.acs.util.stringqueue.TimestampedStringQueue;
import junit.framework.TestCase;
/**
* A class to test <code>TimestampedStringQueue</code>.
*
* @author acaproni
*
*/
public class TimestampedStringQueueTest extends TestCase {
/**
* A runnable to push strings in the queue
*
* @author acaproni
*
*/
private class StringPusher implements Runnable {
/**
* The string to push in the queue
*/
private final Vector<String> strsToPush=new Vector<String>();
private final CountDownLatch latch;
private final String name;
/**
* Constructor
*
* @param numOfStringsToPush
*/
public StringPusher(int numOfStringsToPush, CountDownLatch latch, String name) {
if (numOfStringsToPush<=0) {
throw new IllegalArgumentException("numOfStringsToPush must be greater the 0");
}
populateStringsVector(strsToPush,numOfStringsToPush);
this.latch=latch;
this.name=name;
}
@Override
public void run() {
System.out.println(name+" started. Pushing "+strsToPush.size()+" strings in the queue");
for (int t=0; t<strsToPush.size(); t++) {
try {
cache.push(strsToPush.elementAt(t));
} catch (Throwable th) {
th.printStackTrace();
}
}
strsToPush.clear();
latch.countDown();
System.out.println(name+" terminated");
}
}
/**
* A runnable to get strings out the queue
*
* @author acaproni
*
*/
private class StringPopper implements Runnable {
/**
* The string read from the queue
*/
private final Vector<String> poppedStrings= new Vector<String>();
private final int numOfStrsToGet;
private final CountDownLatch latch;
private final String name;
/**
* Constructor
*
* @param numOfStrsToGet
*/
public StringPopper(int numOfStrsToGet, CountDownLatch latch, String name) {
if (numOfStrsToGet<=0) {
throw new IllegalArgumentException("numOfStringsToPush must be greater the 0");
}
this.numOfStrsToGet=numOfStrsToGet;
this.latch=latch;
this.name=name;
}
@Override
public void run() {
System.out.println(name+" started");
int read=0;
while (read<numOfStrsToGet) {
String str=null;
try {
str=cache.pop();
} catch (Throwable t) {
t.printStackTrace();
}
if (str==null) {
continue;
}
poppedStrings.add(str);
read++;
}
latch.countDown();
System.out.println(name+" terminated");
}
}
// The size of each file of the cache
private static final int CACHE_SIZE=30000;
// The cache to test
private TimestampedStringQueue cache;
public TimestampedStringQueueTest() {
super("TimestampedStringQueueTest");
}
/* (non-Javadoc)
* @see junit.framework.TestCase#setUp()
*/
@Override
protected void setUp() throws Exception {
super.setUp();
// Build the cache
cache = new TimestampedStringQueue(CACHE_SIZE,"TIMESTAMP=\"");
assertNotNull(cache);
assertEquals(0, cache.getActiveFilesSize());
assertEquals(0, cache.size());
}
/* (non-Javadoc)
* @see junit.framework.TestCase#tearDown()
*/
@Override
protected void tearDown() throws Exception {
cache.close(true);
super.tearDown();
}
/**
* Generate the strings to put in the cache.
* <P>
* The number of strings to put in the vector depends on the passed
* parameter. The sum of the length of all the strings in the
* vector is equal or greater to the passed parameter.
* In this way it is possible to check if the cache creates/deletes a
* file in the right moment.
*
* @param size The length of all the strings in the vector
* @return A vector of strings
*
*/
private Vector<String> generateStrings(int size) {
Vector<String> strings = new Vector<String>();
int currentSz=0;
long t=0;
while (currentSz<size) {
String str = "TimeStamp=\"2005-12-02T13:45:02.761\" "+(t++);
currentSz+=str.length();
strings.add(str);
}
return strings;
}
/**
* Generate a vector of size strings.
*
* @param strings
* @param size The number of strings to put in the vector
*/
private void populateStringsVector(Vector<String> strings, int size) {
long t=0;
while (strings.size()<size) {
String str = "A string to test with threads, TimeStamp=\"2005-12-02T13:45:02.761\" "+(t++);
strings.add(str);
}
}
/**
* Test the normal behavior of the cache by pushing and getting
* strings into the cache without involving the creation of more
* then on file.
*
* @throws Exception
*/
public void testPushPopSingleFile() throws Exception {
Vector<String> items = generateStrings(CACHE_SIZE/2);
// Push the string in the cache
for (String str: items) {
cache.push(str);
}
assertEquals(items.size(), cache.size());
assertEquals(1, cache.getActiveFilesSize());
for (int t=0; t<items.size(); t++) {
String str = cache.pop();
// str is null if the cache is empty
assertNotNull(str);
assertEquals(items.get(t), str);
}
// No more items and files in cache
assertEquals(0, cache.size());
assertEquals(1, cache.getActiveFilesSize());
System.out.println("testPushPopSingleFile done");
}
/**
* Test the normal behavior of the cache by pushing and getting
* strings into the cache involving the creation of several files.
*
* @throws Exception
*/
public void testPushPopSeveralFiles() throws Exception {
// Create a vector of strings the trigger the cache to create 4 files
Vector<String> items = generateStrings(3*CACHE_SIZE+CACHE_SIZE/2);
// Push the string in the cache
for (String str: items) {
cache.push(str);
}
assertEquals(items.size(), cache.size());
assertEquals(4, cache.getActiveFilesSize());
for (int t=0; t<items.size(); t++) {
String str = cache.pop();
// str is null if the cache is empty
assertNotNull(str);
assertEquals(items.get(t), str);
}
// No more items and files in cache
assertEquals(0, cache.size());
assertEquals(1, cache.getActiveFilesSize());
System.out.println("testPushPopSeveralFiles done");
}
/**
* Check the conversion in QueueEntry between the key, start and end position
* and their representations as an hexadecimal string.
*/
public void testCacheEntryTranslation() throws Exception {
// Check for 0, 0, 1 (end must be greater then 0)
QueueEntry zeroCE = new QueueEntry(0,0,1);
String zeroStr = zeroCE.toHexadecimal();
assertEquals(QueueEntry.ENTRY_LENGTH,zeroStr.length());
QueueEntry check = new QueueEntry(zeroStr);
assertEquals(zeroCE.key, check.key);
assertEquals(zeroCE.start, check.start);
assertEquals(zeroCE.end, check.end);
// Check for to values
QueueEntry topCE = new QueueEntry(Integer.MAX_VALUE,Long.MAX_VALUE-1,Long.MAX_VALUE);
String topStr = topCE.toHexadecimal();
assertEquals(QueueEntry.ENTRY_LENGTH,topStr.length());
check= new QueueEntry(topStr);
assertEquals(topCE.key, check.key);
assertEquals(topCE.start, check.start);
assertEquals(topCE.end, check.end);
// Check for some random values
Random rnd = new Random(System.currentTimeMillis());
for (int t=0; t<1000; t++) {
long start=0;
long end=0;
while (start>=end) {
start = Math.abs(rnd.nextLong());
end = Math.abs(rnd.nextLong());
}
QueueEntry test = new QueueEntry(Math.abs(rnd.nextInt()),start,end);
String testStr = test.toHexadecimal();
assertEquals(QueueEntry.ENTRY_LENGTH,testStr.length());
check= new QueueEntry(testStr);
assertEquals(test.key, check.key);
assertEquals(test.start, check.start);
assertEquals(test.end, check.end);
}
System.out.println("testCacheEntryTranslation done");
}
/**
* Check if getting an entry from an empty queue returns <code>null</code>
* after the timout elapses
*/
public void testEmptyTimeout() throws Exception {
// Set the timeout to 1 sec.
cache.setTimeout(1000);
long now = System.currentTimeMillis();
String str = cache.pop();
long after = System.currentTimeMillis();
assertTrue("The queue returned too early! No timeout? ", after-now>=1000);
assertNull("The queue shall return NULL when empty!",str);
}
/**
* Test if the cache works as expected by running 5 threads in parallel
* to push .strings asynchronously.
*/
public void testAsyncPush() throws Exception {
System.out.println("testAsyncPush started");
cache.start();
int numOfPushers=5;
CountDownLatch latch = new CountDownLatch(numOfPushers);
// Instantiate 5 threads to push 1000 items each
StringPusher[] pushers = new StringPusher[numOfPushers];
for (int t=0; t<pushers.length; t++) {
pushers[t]=new StringPusher(1000, latch,"StringPusher-"+t);
}
// Create and run all the threads
System.out.println("testAsyncPush creating threads");
Vector<Thread> threads = new Vector<Thread>();
for (int t=0; t<pushers.length; t++) {
threads.add(new Thread(pushers[t]));
}
System.out.println("testAsyncPush starting threads");
for (int t=0; t<threads.size(); t++) {
threads.elementAt(t).start();
}
System.out.println("testAsyncPush waiting until all threads terminate");
while (!latch.await(1,TimeUnit.MINUTES)) {
System.out.println("Strings in cache: "+cache.size()+", files="+cache.getActiveFilesSize());
}
System.out.println("testAsyncPush pusher threads terminated");
assertEquals("The cahce does not contain the expected number of strings!",5000, cache.size());
System.out.println("testAsyncPush done");
}
/**
* Test if the cache works as expected by running 5 threads in parallel
* to pop strings asynchronously.
*/
public void testAsyncPop() throws Exception {
System.out.println("testAsyncPop started");
cache.start();
int numOfPoppers=5;
CountDownLatch latch = new CountDownLatch(numOfPoppers);
// Piush the strings in the cache
Vector<String> strsToPush = new Vector<String>();
populateStringsVector(strsToPush, 5000);
for (String str: strsToPush) {
cache.push(str);
}
assertEquals(5000, cache.size());
// Instantiate the threads to read 1000 items each
StringPopper[] poppers = new StringPopper[numOfPoppers];
for (int t=0; t<poppers.length; t++) {
poppers[t]=new StringPopper(1000, latch,"StringPopper-"+t);
}
// Create and run all the threads
System.out.println("testAsyncPop creating threads");
Vector<Thread> threads = new Vector<Thread>();
for (int t=0; t<poppers.length; t++) {
threads.add(new Thread(poppers[t]));
}
System.out.println("testAsyncPop starting threads");
for (int t=0; t<threads.size(); t++) {
threads.elementAt(t).start();
}
System.out.println("testAsyncPop waiting until all threads terminate");
// Wait the thread to terminate with a timeout of 3 minutes
while (!latch.await(1,TimeUnit.MINUTES)) {
// This is an error!!!!
System.out.println("Not all the threads to get strings from the cache terminated!");
System.out.println("Strings in cache: "+cache.size()+", files="+cache.getActiveFilesSize());
}
// Here all the strings should have been read from the cache!
assertTrue(cache.size()==0);
System.out.println("testAsyncPop done");
}
/**
* Test if the cache works as expected by running 13 threads in parallel
* to push and pop strings asynchronously
*/
public void testAsyncPushPop() throws Exception {
System.out.println("testAsyncPushPop started");
cache.start();
int numOfPushers=5;
int numOfPoppers=8;
CountDownLatch latch = new CountDownLatch(numOfPushers+numOfPoppers);
// Instantiate 5 threads to push 1000 items each
StringPusher[] pushers = new StringPusher[numOfPushers];
for (int t=0; t<pushers.length; t++) {
pushers[t]=new StringPusher(1000, latch,"StringPusher-"+t);
}
// Instantiate 8 threads to read 625 items each
StringPopper[] poppers = new StringPopper[numOfPoppers];
for (int t=0; t<poppers.length; t++) {
poppers[t]=new StringPopper(625, latch,"StringPopper-"+t);
}
// Create and run all the threads
System.out.println("testAsyncPushPop creating threads");
Vector<Thread> threads = new Vector<Thread>();
for (int t=0; t<poppers.length; t++) {
threads.add(new Thread(poppers[t]));
}
for (int t=0; t<pushers.length; t++) {
threads.add(new Thread(pushers[t]));
}
System.out.println("testAsyncPushPop starting threads");
for (int t=0; t<threads.size(); t++) {
threads.elementAt(t).start();
}
System.out.println("testAsyncPushPop waiting until all threads terminate");
while (!latch.await(3,TimeUnit.MINUTES)) {
// This is an error!!!!
System.out.println("Not all the threads terminated!");
System.out.println("Strings in cache: "+cache.size()+", files="+cache.getActiveFilesSize());
}
assertTrue(cache.size()==0);
System.out.println("testAsyncPushPop done");
}
}