/*
* A CCNx library test.
*
* Copyright (C) 2008-2013 Palo Alto Research Center, Inc.
*
* This work is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License version 2 as published by the
* Free Software Foundation.
* This work 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 General Public License
* for more details. You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
package org.ccnx.ccn.test;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Random;
import java.util.concurrent.Semaphore;
import java.util.logging.Level;
import org.ccnx.ccn.CCNContentHandler;
import org.ccnx.ccn.CCNHandle;
import org.ccnx.ccn.CCNInterestHandler;
import org.ccnx.ccn.config.ConfigurationException;
import org.ccnx.ccn.config.SystemConfiguration;
import org.ccnx.ccn.impl.CCNFlowControl;
import org.ccnx.ccn.impl.support.Log;
import org.ccnx.ccn.io.CCNWriter;
import org.ccnx.ccn.protocol.ContentName;
import org.ccnx.ccn.protocol.ContentObject;
import org.ccnx.ccn.protocol.Interest;
import org.ccnx.ccn.protocol.MalformedContentNameStringException;
import org.junit.AfterClass;
import org.junit.BeforeClass;
/**
*
* A base class for the old style of library tests.
* Defines a few common parameters, and a test-running framework which passes data
* between different threads or objects, via ccnd.
* New tests should probably not use this without some additional cleanup.
*
*/
public class LibraryTestBase extends CCNTestBase {
protected static boolean exit = false;
protected static Throwable error = null; // for errors from other threads
public static int count = 55;
//public static int count = 5;
public static Random rand = new Random();
public static final int WAIT_DELAY = 200000;
protected static final String BASE_NAME = "/test/BaseLibraryTest/";
protected static ContentName PARENT_NAME;
protected static final boolean DO_TAP = true;
protected HashSet<Integer> _resultSet = new HashSet<Integer>();
protected static ArrayList<Integer> usedIds = new ArrayList<Integer>();
static {
try {
PARENT_NAME = ContentName.fromNative(BASE_NAME);
} catch (MalformedContentNameStringException e) {}
}
@BeforeClass
public static void setUpBeforeClass() throws Exception {
CCNTestBase.setUpBeforeClass();
// Let default logging level be set centrally so it can be overridden by property
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
CCNTestBase.tearDownAfterClass();
}
public void genericGetPut(Thread putter, Thread getter) throws Throwable {
try {
putter.start();
Thread.sleep(20);
Date start = new Date();
getter.start();
putter.join(WAIT_DELAY);
getter.join(WAIT_DELAY);
boolean good = true;
exit = true;
if (getter.getState() != Thread.State.TERMINATED) {
getter.interrupt();
Log.warning(Log.FAC_TEST, "Get Thread has not finished!");
good = false;
}
if (putter.getState() != Thread.State.TERMINATED) {
putter.interrupt();
Log.warning(Log.FAC_TEST, "Put Thread has not finished!");
good = false;
}
if (null != error) {
Log.warning(Log.FAC_TEST, "Error in test thread: " + error.getClass().toString());
throw error;
}
if (!good) {
fail();
}
Log.info(Log.FAC_TEST, "Get/Put test in " + (new Date().getTime() - start.getTime()) + " ms");
} catch (InterruptedException e) {
Log.logStackTrace(Level.WARNING, e);
fail("InterruptedException");
}
}
/**
* Subclassible object processing operations, to make it possible to easily
* implement tests based on this one.
* @author smetters
*
*/
public void checkGetResults(ContentObject getResults) {
Log.info(Log.FAC_TEST, "Got result: " + getResults.name());
}
public void checkPutResults(ContentName putResult) {
Log.info(Log.FAC_TEST, "Put data: " + putResult);
}
/**
* Expects this method to call checkGetResults on each set of content returned...
* @param baseName
* @param count
* @param handle
* @return
* @throws InterruptedException
* @throws IOException
* @throws SignatureException
* @throws InvalidKeyException
* @throws InterruptedException
*/
public void getResults(ContentName baseName, int count, CCNHandle handle) throws IOException, InvalidKeyException, SignatureException, InterruptedException {
Random rand = new Random();
// boolean done = false;
Log.info(Log.FAC_TEST, "getResults: getting children of " + baseName);
for (int i = 0; i < count; i++) {
// while (!done) {
Thread.sleep(rand.nextInt(50));
Log.info(Log.FAC_TEST, "getResults getting " + baseName + " subitem " + i);
ContentObject contents = handle.get(new ContentName(baseName, Integer.toString(i)), SystemConfiguration.NO_TIMEOUT);
try {
int val = Integer.parseInt(new String(contents.content()));
if (_resultSet.contains(val)) {
Log.info(Log.FAC_TEST, "Got " + val + " again.");
} else {
Log.info(Log.FAC_TEST, "Got " + val);
}
_resultSet.add(val);
} catch (NumberFormatException nfe) {
Log.info(Log.FAC_TEST, "BaseLibraryTest: unexpected content - not integer. Name: " + contents.toString());
}
//assertEquals(i, Integer.parseInt(new String(contents.get(0).content())));
checkGetResults(contents);
if (_resultSet.size() == count) {
Log.info(Log.FAC_TEST, "We have everything!");
// done = true;
}
}
return;
}
/**
* Responsible for calling checkPutResults on each put. (Could return them all in
* a batch then check...)
* @throws InterruptedException
* @throws IOException
* @throws MalformedContentNameStringException
* @throws SignatureException
* @throws InvalidKeyException
*/
public void doPuts(ContentName baseName, int count, CCNHandle handle)
throws InterruptedException, SignatureException, MalformedContentNameStringException, IOException, InvalidKeyException {
CCNWriter writer = new CCNWriter(baseName, handle);
Random rand = new Random();
for (int i = 0; i < count; i++) {
Thread.sleep(rand.nextInt(50));
ContentName putResult = writer.put(new ContentName(baseName, Integer.toString(i)), new Integer(i).toString().getBytes());
Log.info(Log.FAC_TEST, "Put " + i + " done");
checkPutResults(putResult);
}
writer.close();
}
public int getUniqueId() {
int id;
do {
id = rand.nextInt(1000);
} while (usedIds.indexOf(id) != -1);
usedIds.add(id);
return id;
}
public class GetThread implements Runnable {
protected CCNHandle handle = null;
int count = 0;
int id = 0;
public GetThread(int n, int id) throws ConfigurationException, IOException {
handle = CCNHandle.open();
count = n;
this.id = id;
if (DO_TAP) {
try {
((CCNHandle)handle).getNetworkManager().setTap(SystemConfiguration.DEBUG_DATA_DIRECTORY + "/LibraryTestDebug_" + Integer.toString(id) + "_get");
} catch (IOException ie) {
}
}
}
public void run() {
try {
Log.info(Log.FAC_TEST, "Get thread started");
getResults(new ContentName(PARENT_NAME, Integer.toString(id)), count, handle);
handle.close();
Log.info(Log.FAC_TEST, "Get thread finished");
} catch (Throwable ex) {
error = ex;
Log.warning(Log.FAC_TEST, "Exception in run: " + ex.getClass().getName() + " message: " + ex.getMessage());
Log.logStackTrace(Log.FAC_TEST, Level.WARNING, ex);
}
}
}
public class PutThread implements Runnable {
protected CCNHandle handle = null;
int count = 0;
int id = 0;
public PutThread(int n, int id) throws ConfigurationException, IOException {
handle = CCNHandle.open();
count = n;
this.id = id;
if (DO_TAP) {
try {
((CCNHandle)handle).getNetworkManager().setTap(SystemConfiguration.DEBUG_DATA_DIRECTORY + "/LibraryTestDebug_" + Integer.toString(id) + "_put");
} catch (IOException ie) {
}
}
}
public void run() {
try {
Log.info(Log.FAC_TEST, "Put thread started");
doPuts(new ContentName(PARENT_NAME, Integer.toString(id)), count, handle);
handle.close();
Log.info(Log.FAC_TEST, "Put thread finished");
//cf.shutdown();
} catch (Throwable ex) {
error = ex;
Log.warning(Log.FAC_TEST, "Exception in run: " + ex.getClass().getName() + " message: " + ex.getMessage());
Log.logStackTrace(Log.FAC_TEST, Level.WARNING, ex);
}
}
}
public class GetServer implements Runnable, CCNContentHandler {
protected CCNHandle handle = null;
int count = 0;
int next = 0;
Semaphore sema = new Semaphore(0);
HashSet<Integer> accumulatedResults = new HashSet<Integer>();
int id;
public GetServer(int n, int id) throws ConfigurationException, IOException {
handle = CCNHandle.open();
count = n;
this.id = id;
if (DO_TAP) {
try {
((CCNHandle)handle).getNetworkManager().setTap(SystemConfiguration.DEBUG_DATA_DIRECTORY + "/LibraryTestDebug_" + Integer.toString(id) + "_get");
} catch (IOException ie) {
}
}
}
public void run() {
try {
Log.info(Log.FAC_TEST, "GetServer started");
Interest interest = new Interest(new ContentName(PARENT_NAME, Integer.toString(id)));
// Register interest
handle.expressInterest(interest, this);
// Block on semaphore until enough data has been received
boolean interrupted = false;
do {
try {
sema.acquire();
} catch (InterruptedException ie) { interrupted = true; }
} while (interrupted);
handle.cancelInterest(interest, this);
handle.close();
} catch (Throwable ex) {
error = ex;
Log.warning(Log.FAC_TEST, "Exception in run: " + ex.getClass().getName() + " message: " + ex.getMessage());
Log.logStackTrace(Log.FAC_TEST, Level.WARNING, ex);
}
}
public synchronized Interest handleContent(ContentObject contentObject, Interest interest) {
Interest newInterest = null;
try {
int val = Integer.parseInt(new String(contentObject.content()));
if (!accumulatedResults.contains(val)) {
accumulatedResults.add(val);
Log.info(Log.FAC_TEST, "Got " + val);
}
newInterest = Interest.next(contentObject.fullName(), contentObject.name().count() - 2, null);
} catch (NumberFormatException nfe) {
Log.info(Log.FAC_TEST, "Unexpected content, " + contentObject.name() + " is not an integer!");
}
checkGetResults(contentObject);
if (accumulatedResults.size() >= count) {
Log.info(Log.FAC_TEST, "GetServer got all content: " + accumulatedResults.size() + ". Releasing semaphore.");
sema.release();
}
return newInterest;
}
}
public class PutServer implements Runnable, CCNInterestHandler {
protected CCNHandle handle = null;
int count = 0;
int next = 0;
Semaphore sema = new Semaphore(0);
ContentName name = null;
HashSet<Integer> accumulatedResults = new HashSet<Integer>();
int id;
CCNFlowControl cf = null;
CCNWriter writer = null;
public PutServer(int n, int id) throws ConfigurationException, IOException {
handle = CCNHandle.open();
count = n;
this.id = id;
if (DO_TAP) {
try {
((CCNHandle)handle).getNetworkManager().setTap(SystemConfiguration.DEBUG_DATA_DIRECTORY + "/LibraryTestDebug_" + Integer.toString(id) + "_put");
} catch (IOException ie) {
}
}
}
public void run() {
try {
Log.info(Log.FAC_TEST, "PutServer started");
// Register filter
name = new ContentName(PARENT_NAME, Integer.toString(id));
writer = new CCNWriter(name, handle);
handle.registerFilter(name, this);
// Block on semaphore until enough data has been received
sema.acquire();
handle.unregisterFilter(name, this);
Log.info(Log.FAC_TEST, "PutServer finished.");
handle.close();
} catch (Throwable ex) {
error = ex;
Log.warning(Log.FAC_TEST, "Exception in run: " + ex.getClass().getName() + " message: " + ex.getMessage());
Log.logStackTrace(Log.FAC_TEST, Level.WARNING, ex);
}
}
public synchronized boolean handleInterest(Interest interest) {
boolean result = false;
try {
try {
int val = Integer.parseInt(new String(interest.name().component(interest.name().count()-1)));
Log.info(Log.FAC_TEST, "Got interest in " + val);
if (!accumulatedResults.contains(val)) {
ContentName putResult = writer.put(new ContentName(name, Integer.toString(val)), Integer.toString(next).getBytes());
result = true;
Log.info(Log.FAC_TEST, "Put " + val + " done");
checkPutResults(putResult);
next++;
accumulatedResults.add(val);
}
} catch (NumberFormatException nfe) {
Log.info(Log.FAC_TEST, "Unexpected interest, " + interest.name() + " does not end in an integer!");
}
if (accumulatedResults.size() >= count) {
sema.release();
}
} catch (Throwable e) {
error = e;
Log.warning(Log.FAC_TEST, "Exception in run: " + e.getClass().getName() + " message: " + e.getMessage());
Log.logStackTrace(Log.FAC_TEST, Level.WARNING, e);
}
return result;
}
}
}