/*
* A CCNx library test.
*
* Copyright (C) 2008, 2009, 2011 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 java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Random;
import java.util.logging.Level;
import junit.framework.Assert;
import org.ccnx.ccn.BasicInterestListener;
import org.ccnx.ccn.CCNBase;
import org.ccnx.ccn.ContentVerifier;
import org.ccnx.ccn.config.SystemConfiguration;
import org.ccnx.ccn.impl.CCNFlowControl;
import org.ccnx.ccn.impl.support.DataUtils;
import org.ccnx.ccn.impl.support.Log;
import org.ccnx.ccn.io.CCNReader;
import org.ccnx.ccn.io.CCNWriter;
import org.ccnx.ccn.io.content.ContentEncodingException;
import org.ccnx.ccn.profiles.SegmentationProfile;
import org.ccnx.ccn.profiles.VersionMissingException;
import org.ccnx.ccn.profiles.VersioningProfile;
import org.ccnx.ccn.protocol.CCNTime;
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.ccnx.ccn.protocol.PublisherPublicKeyDigest;
import org.junit.Test;
/**
* Part of older test infrastructure. Tests a set of library read functionality; some
* of which could be aggregated into other tests.
*/
public class CCNLibraryTest extends LibraryTestBase {
static final String contentString = "This is a very small amount of content";
private class NameSeen {
private ContentName name;
private boolean seen = false;
private NameSeen(ContentName name) {
this.name = name;
}
private boolean setSeen(ContentObject co) {
if (name.isPrefixOf(co.name())) {
seen = true;
return true;
}
return false;
}
}
@Test
public void testEnumerate() {
Log.info(Log.FAC_TEST, "Starting testEnumerate");
Assert.assertNotNull(putHandle);
Assert.assertNotNull(getHandle);
try {
CCNWriter writer = new CCNWriter("/CPOF", putHandle);
ArrayList<NameSeen> testNames = new ArrayList<NameSeen>(3);
testNames.add(new NameSeen(ContentName.fromNative("/CPOF/foo")));
testNames.add(new NameSeen(ContentName.fromNative("/CPOF/bar/lid")));
testNames.add(new NameSeen(ContentName.fromNative("/CPOF/bar/jar")));
for (int i = 0; i < testNames.size(); i++) {
writer.put(testNames.get(i).name, Integer.toString(i).getBytes());
}
CCNReader reader = new CCNReader(getHandle);
ArrayList<ContentObject> availableNames =
reader.enumerate(new Interest("/CPOF"), SystemConfiguration.NO_TIMEOUT);
Iterator<ContentObject> nameIt = availableNames.iterator();
while (nameIt.hasNext()) {
ContentObject theName = nameIt.next();
// Just get by name, to test equivalent to current
// ONC interface.
ContentObject theObject = getHandle.get(theName.name(), 1000);
if (null == theObject) {
Log.info(Log.FAC_TEST, "Missing content: enumerated name: " + theName.name() + " not gettable.");
} else {
Log.info(Log.FAC_TEST, "Retrieved name: " + theName.name());
}
for (NameSeen nt : testNames) {
if (nt.setSeen(theName))
break;
}
}
for (NameSeen nt : testNames) {
if (!nt.seen)
Assert.fail("Didn't see name " + nt.name.toString() + " in enumeration");
}
} catch (Exception e) {
Log.warning(Log.FAC_TEST, "Got an exception in enumerate test: " + e.getClass().getName() + ": " + e.getMessage());
Log.logStackTrace(Log.FAC_TEST, Level.WARNING, e);
Assert.fail("Exception in testEnumerate: " + e.getMessage());
}
Log.info(Log.FAC_TEST, "Completed testEnumerate");
}
@Test
public void testPut() {
Log.info(Log.FAC_TEST, "Starting testPut");
Assert.assertNotNull(putHandle);
Assert.assertNotNull(getHandle);
ContentName name = null;
byte[] content = null;
// SignedInfo.ContentType type = SignedInfo.ContentType.LEAF;
PublisherPublicKeyDigest publisher = null;
content = DataUtils.getBytesFromUTF8String(contentString);
try {
name = ContentName.fromNative("/test/briggs/foo.txt");
} catch (MalformedContentNameStringException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
CCNWriter segmenter = new CCNWriter(name, putHandle);
ContentName result = segmenter.put(name, content, publisher);
Log.info(Log.FAC_TEST, "Resulting ContentName: " + result);
} catch (SignatureException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Log.info(Log.FAC_TEST, "Completed testPut");
}
@Test
public void testRevision() {
Log.info(Log.FAC_TEST, "Starting testRevision");
String key = "/test/key";
byte[] data1 = "data".getBytes();
byte[] data2 = "newdata".getBytes();
ContentName revision1;
ContentName revision2;
try {
ContentName keyName = ContentName.fromNative(key);
CCNWriter segmenter = new CCNWriter(keyName, putHandle);
revision1 = segmenter.newVersion(keyName, data1);
revision2 = segmenter.newVersion(keyName, data2);
long version1 = VersioningProfile.getLastVersionAsLong(revision1);
long version2 = VersioningProfile.getLastVersionAsLong(revision2);
Log.info(Log.FAC_TEST, "Version1: " + version1 + " version2: " + version2);
Assert.assertTrue("Revisions are strange",
version2 > version1);
} catch (Exception e) {
Log.warning(Log.FAC_TEST, "Exception in updating versions: " + e.getClass().getName() + ": " + e.getMessage());
Assert.fail(e.getMessage());
}
Log.info(Log.FAC_TEST, "Completed testRevision");
}
@Test
public void testVersion() throws Exception {
Log.info(Log.FAC_TEST, "Starting testVersion");
String name = "/test/smetters/stuff/versioned_name";
ContentName cn = ContentName.fromNative(name);
String name2 = "/test/smetters/stuff/.directory";
ContentName cn2 = ContentName.fromNative(name2);
String data = "The associated data.";
String newdata = "The new associated data.";
versionTest(cn, data.getBytes(), newdata.getBytes());
versionTest(cn2, data.getBytes(), newdata.getBytes());
Log.info(Log.FAC_TEST, "Completed testRevision");
}
@Test
public void testGetLatestVersion() throws Exception {
Log.info(Log.FAC_TEST, "Starting testGetLatestVersion");
String name = "/test/simon/versioned_name-" + new Random().nextInt(10000);
// include a base object, who's digest can potentially confuse getLatestVersion
ContentName base = ContentName.fromNative(name);
// Don't do repeated get latest versions for now, not working.
final int testCount = 2;
final byte [][] data = new byte[testCount][1];
for (int i=0; i < testCount; ++i) {
data[i][0] = (byte)i;
}
CCNFlowControl f = new CCNFlowControl(base, putHandle);
ContentObject [] cos = new ContentObject[testCount];
cos[0] = ContentObject.buildContentObject(base, data[0]);
for (int i=1; i < testCount; ++i) {
ContentName versionedName = VersioningProfile.addVersion(base);
Thread.sleep(3);
cos[i] = ContentObject.buildContentObject(versionedName, data[i]);
}
f.put(cos[0]);
f.put(cos[1]);
// java lacks nested functions, so use a class here...
class t {
void check(ContentObject o, int i) throws InvalidKeyException, ContentEncodingException,
SignatureException, NoSuchAlgorithmException, InterruptedException {
Log.info(Log.FAC_TEST, "Got content: " + o.name());
Log.info(Log.FAC_TEST, "Original value: " + i + " returned value: " + Byte.toString(o.content()[0]));
Assert.assertTrue(o.verify(putHandle.keyManager()));
Assert.assertTrue(DataUtils.arrayEquals(o.content(), data[i]));
}
/**
* Make sure the data is written to ccnd by reading it
* @throws InterruptedException
* @throws NoSuchAlgorithmException
* @throws SignatureException
* @throws InvalidKeyException
*/
void readAndCheck(ContentName name, int index)
throws ContentEncodingException,
IOException, InvalidKeyException,
SignatureException, NoSuchAlgorithmException, InterruptedException {
Log.info(Log.FAC_TEST, "Getting content: " + name);
check(getHandle.get(name, 2000), index);
}
} t test = new t();
test.readAndCheck(base, 0);
test.readAndCheck(cos[1].name(), 1);
ContentVerifier putVerifier = new ContentObject.SimpleVerifier(putHandle.getDefaultPublisher());
test.check(VersioningProfile.getLatestVersion(base, putHandle.getDefaultPublisher(), 2000, putVerifier, getHandle), 1);
// Beef this up a bit...
for (int i=2; i < testCount; ++i) {
f.put(cos[i]);
Log.info(Log.FAC_TEST, "Wrote content: " + cos[i].name());
test.check(VersioningProfile.getLatestVersion(cos[i-1].name(), putHandle.getDefaultPublisher(), 2000, putVerifier, getHandle), i);
}
Log.info(Log.FAC_TEST, "Completed testGetLatestVersion");
}
@Test
public void testRecall() {
Log.info(Log.FAC_TEST, "Starting testRecall");
String key = "/test/smetters/values/data";
CCNTime time = CCNTime.now();
try {
ContentName keyName = ContentName.fromNative(key);
CCNWriter writer = new CCNWriter(keyName, putHandle);
ContentName name = writer.put(keyName, BigInteger.valueOf(time.getTime()).toByteArray());
Log.info(Log.FAC_TEST, "Put under name: " + name);
ContentObject result = getHandle.get(name, SystemConfiguration.NO_TIMEOUT);
Log.info(Log.FAC_TEST, "Querying for returned name, Got back: " + (result == null ? "0" : "1") + " results.");
if (result == null) {
Log.info(Log.FAC_TEST, "Didn't get back content we just put in.");
Log.info(Log.FAC_TEST, "Put under name: " + keyName);
Log.info(Log.FAC_TEST, "Final name: " + name);
//Assert.fail("Didn't get back content we just put!");
result = getHandle.get(name, SystemConfiguration.NO_TIMEOUT);
Log.info(Log.FAC_TEST, "Recursive querying for returned name, Got back: " + (result == null ? "0" : "1") + " results.");
ContentName parentName = name.parent();
Log.info(Log.FAC_TEST, "Inserted name's parent same as key name? " + parentName.equals(keyName));
} else {
byte [] content = result.content();
Log.info(Log.FAC_TEST, "Got content: " + result.name());
Log.info(Log.FAC_TEST, "Original time: " + time + " returned time: " + new Timestamp(new BigInteger(1, content).longValue()));
Assert.assertNotNull("No content associated with name we just put!", content);
Assert.assertTrue("didn't get back same data",
time.equals(new Timestamp(new BigInteger(1, content).longValue())));
}
result = getHandle.get(keyName, SystemConfiguration.NO_TIMEOUT);
Log.info(Log.FAC_TEST, "Querying for inserted name, Got back: "
+ (result == null ? "0" : "1") + " results.");
if (result == null)
Assert.fail("Didn't get back content we just put!");
if (SegmentationProfile.segmentRoot(result.name()).equals(name) &&
time.equals(new Timestamp(new BigInteger(1, result.content()).longValue()))) {
Log.info(Log.FAC_TEST, "Got back name we inserted.");
} else {
Log.warning(Log.FAC_TEST, "Didn't get back data we just inserted:\n result: " + result.name() +
" (write time: " + result.signedInfo().getTimestamp() +
" content time: " + new Timestamp(new BigInteger(1, result.content()).longValue()) +
")\n orig: " + name +
" (time: " + time + ")");
Assert.fail("Didn't get back data we just inserted - result name: " +
SegmentationProfile.segmentRoot(result.name()) +
", auth: " + result.signedInfo() + " - orig name: " + name + "\n, orig time: " +
time + " time content: " +
new Timestamp(new BigInteger(1, result.content()).longValue()));
}
} catch (Exception e) {
System.out.println("Exception in testing recall: " + e.getClass().getName() + ": " + e.getMessage());
Assert.fail(e.getMessage());
}
Log.info(Log.FAC_TEST, "Completed testRecall");
}
public void versionTest(ContentName docName,
byte [] content1,
byte [] content2) throws Exception {
CCNWriter writer = new CCNWriter(docName, putHandle);
ContentName version1 = writer.newVersion(docName, content1);
Log.info(Log.FAC_TEST, "Inserted first version as: " + version1);
Assert.assertNotNull("New version is null!", version1);
ContentVerifier putVerifier = new ContentObject.SimpleVerifier(putHandle.getDefaultPublisher());
ContentObject latestVersion =
VersioningProfile.getLatestVersion(docName, null, SystemConfiguration.NO_TIMEOUT, putVerifier, getHandle);
Assert.assertTrue(latestVersion.verify(getHandle.keyManager()));
Assert.assertNotNull("Retrieved latest version of " + docName + " got null!", latestVersion);
Log.info(Log.FAC_TEST, "Latest version name: " + latestVersion.name());
ContentName version2 =
writer.newVersion(docName, content2);
Assert.assertNotNull("New version is null!", version2);
Log.info(Log.FAC_TEST, "Inserted second version as: " + version2);
ContentObject newLatestVersion =
VersioningProfile.getLatestVersion(docName, null, SystemConfiguration.NO_TIMEOUT, putVerifier, getHandle);
Assert.assertTrue(newLatestVersion.verify(getHandle.keyManager()));
Assert.assertNotNull("Retrieved new latest version of " + docName + " got null!", newLatestVersion);
Log.info(Log.FAC_TEST, "Latest version name: " + newLatestVersion.name());
Assert.assertTrue("Version is not a version of the parent name!", VersioningProfile.isVersionOf(version1, docName));
Assert.assertTrue("Version is not a version of the parent name!", VersioningProfile.isVersionOf(version2, docName));
Assert.assertTrue("Version numbers don't increase!", VersioningProfile.getLastVersionAsLong(version2) > VersioningProfile.getLastVersionAsLong(version1));
}
@Test
public void testNotFound() throws Exception {
Log.info(Log.FAC_TEST, "Starting testNotFound");
try {
String key = "/some_strange_key_we_should_never_find";
ContentObject result = getHandle.get(ContentName.fromNative(key), 1000);
Assert.assertTrue("found something when there shouldn't have been anything", result == null);
} catch (Exception e) {
Log.info(Log.FAC_TEST, "Exception in testing recall: " + e.getClass().getName() + ": " + e.getMessage());
Assert.fail(e.getMessage());
}
Log.info(Log.FAC_TEST, "Completed testNotFound");
}
class TestListener extends BasicInterestListener {
int _count = 0;
Thread _mainThread;
public TestListener(CCNBase queryProvider,
Interest initialInterest,
Thread mainThread) {
super(queryProvider);
_mainThread = mainThread;
}
public Interest handleContent(ContentObject co, Interest interest) {
byte[] content = null;
try {
if (null != co) {
Log.info(Log.FAC_TEST, "handleContent: got " + co.name());
content = co.content();
String strContent = new String(content);
if (VersioningProfile.hasTerminalVersion(co.name())) {
// We're writing this content using CCNWriter.put. That interface
// does *not* version content for you, at least at the moment.
// TODO We need to decide whether we expect it to. So don't require
// versioning here yet.
Log.info(Log.FAC_TEST, "Got update for " + co.name() + ": " + strContent +
" (revision " + VersioningProfile.getLastVersionAsLong(co.name()) + ")");
} else {
Log.info(Log.FAC_TEST, "Got update for " + co.name() + ": " + strContent);
}
_count++;
switch(_count) {
case 1:
Assert.assertEquals("data1", strContent);
Log.info(Log.FAC_TEST, "Got data1 back!");
_mainThread.interrupt();
break;
case 2:
Assert.assertEquals("data2", strContent);
Log.info(Log.FAC_TEST, "Got data2 back!");
_mainThread.interrupt();
break;
default:
Assert.fail("Somehow got a third update");
}
}
} catch (VersionMissingException vex) {
Assert.fail("No version when expecting one -- though be careful to make sure we should have been.");
}
return null;
}
}
@Test
public void testInterest() {
Log.info(Log.FAC_TEST, "Starting testInterest");
String key = "/test/interest";
final Thread mainThread = Thread.currentThread();
byte[] data1 = "data1".getBytes();
byte[] data2 = "data2".getBytes();
try {
CCNWriter writer = new CCNWriter(key, putHandle);
Interest ik = new Interest(key);
TestListener tl = new TestListener(getHandle, ik, mainThread);
getHandle.expressInterest(ik,
tl);
writer.put(ContentName.fromNative(key), data1);
// wait a little bit before we move on...
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
writer.put(ContentName.fromNative(key), data2);
// wait a little bit before we move on...
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
getHandle.cancelInterest(ik, tl);
} catch (Exception e) {
Log.warning(Log.FAC_TEST, "Exception in testing interests: " + e.getClass().getName() + ": " + e.getMessage());
Assert.fail(e.getMessage());
}
Log.info(Log.FAC_TEST, "Completed testInterest");
}
}