/*
* A CCNx library test.
*
* Copyright (C) 2008-2012 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.io;
import java.io.IOException;
import java.io.InputStream;
import java.security.DigestInputStream;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;
import org.ccnx.ccn.CCNHandle;
import org.ccnx.ccn.config.SystemConfiguration;
import org.ccnx.ccn.impl.support.Log;
import org.ccnx.ccn.io.CCNOutputStream;
import org.ccnx.ccn.io.CCNReader;
import org.ccnx.ccn.io.CCNVersionedInputStream;
import org.ccnx.ccn.profiles.SegmentationProfile;
import org.ccnx.ccn.profiles.VersioningProfile;
import org.ccnx.ccn.protocol.ContentName;
import org.ccnx.ccn.protocol.ContentObject;
import org.ccnx.ccn.protocol.Interest;
import org.ccnx.ccn.test.CCNTestHelper;
import org.ccnx.ccn.test.Flosser;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
/**
* Test for versioned input streams.
*/
public class CCNVersionedInputStreamTest {
static ContentName defaultStreamName;
static ContentName firstVersionName;
static int firstVersionLength;
static int firstVersionMaxSegment;
static byte [] firstVersionDigest;
static ContentName middleVersionName;
static int middleVersionLength;
static int middleVersionMaxSegment;
static byte [] middleVersionDigest;
static ContentName latestVersionName;
static int latestVersionLength;
static int latestVersionMaxSegment;
static byte [] latestVersionDigest;
static CCNHandle outputHandle;
static CCNHandle inputHandle;
static CCNHandle inputHandle2;
static CCNReader reader;
static final int MAX_FILE_SIZE = 1024*1024; // 1 MB
static final int BUF_SIZE = 4096;
static final int MERKLE_TREE_LENGTH = SegmentationProfile.DEFAULT_BLOCKSIZE * CCNOutputStream.BLOCK_BUF_COUNT;
static int [] problematicLengths = {
SegmentationProfile.DEFAULT_BLOCKSIZE,
SegmentationProfile.DEFAULT_BLOCKSIZE/2,
SegmentationProfile.DEFAULT_BLOCKSIZE*2,
((int)(SegmentationProfile.DEFAULT_BLOCKSIZE*1.5)),
((int)(SegmentationProfile.DEFAULT_BLOCKSIZE*2.5)),
MERKLE_TREE_LENGTH + SegmentationProfile.DEFAULT_BLOCKSIZE,
MERKLE_TREE_LENGTH + SegmentationProfile.DEFAULT_BLOCKSIZE/2,
MERKLE_TREE_LENGTH + SegmentationProfile.DEFAULT_BLOCKSIZE*2,
MERKLE_TREE_LENGTH + ((int)(SegmentationProfile.DEFAULT_BLOCKSIZE*1.5)),
MERKLE_TREE_LENGTH + ((int)(SegmentationProfile.DEFAULT_BLOCKSIZE*2.5))};
static byte [][] problematicDigests = new byte[problematicLengths.length][];
static ContentName [] problematicNames = new ContentName[problematicLengths.length];
/**
* Handle naming for the test
*/
static CCNTestHelper testHelper = new CCNTestHelper(CCNVersionedInputStreamTest.class);
@BeforeClass
public static void setUpBeforeClass() throws Exception {
Random randBytes = new Random(); // doesn't need to be secure
outputHandle = CCNHandle.open();
inputHandle = CCNHandle.open();
inputHandle2 = CCNHandle.open();
reader = new CCNReader(inputHandle);
Flosser flosser = new Flosser();
// Write a set of output
defaultStreamName = new ContentName(testHelper.getClassNamespace(), "LongOutput.bin");
firstVersionName = VersioningProfile.addVersion(defaultStreamName);
firstVersionLength = randBytes.nextInt(MAX_FILE_SIZE);
firstVersionMaxSegment = (firstVersionLength == 0) ? 0 :
((firstVersionLength + SegmentationProfile.DEFAULT_BLOCKSIZE - 1) / SegmentationProfile.DEFAULT_BLOCKSIZE) - 1;
firstVersionDigest = writeFile(flosser, firstVersionName, firstVersionLength, randBytes);
middleVersionName = VersioningProfile.addVersion(defaultStreamName);
middleVersionLength = randBytes.nextInt(MAX_FILE_SIZE);
middleVersionMaxSegment = (middleVersionLength == 0) ? 0 :
((middleVersionLength + SegmentationProfile.DEFAULT_BLOCKSIZE - 1) / SegmentationProfile.DEFAULT_BLOCKSIZE) - 1;
middleVersionDigest = writeFile(flosser, middleVersionName, middleVersionLength, randBytes);
latestVersionName = VersioningProfile.addVersion(defaultStreamName);
latestVersionLength = randBytes.nextInt(MAX_FILE_SIZE);
latestVersionMaxSegment = (latestVersionLength == 0) ? 0 :
((latestVersionLength + SegmentationProfile.DEFAULT_BLOCKSIZE - 1) / SegmentationProfile.DEFAULT_BLOCKSIZE) - 1;
latestVersionDigest = writeFile(flosser, latestVersionName, latestVersionLength, randBytes);
for (int i=0; i < problematicLengths.length; ++i) {
problematicNames[i] = VersioningProfile.addVersion(
testHelper.getClassChildName("LengthTest-" + problematicLengths[i]));
problematicDigests[i] = writeFile(flosser, problematicNames[i], problematicLengths[i], randBytes);
}
flosser.stop();
}
@AfterClass
public static void cleanupAfterClass() {
outputHandle.close();
inputHandle.close();
inputHandle2.close();
}
/**
* @param completeName
* @param fileLength
* @param randBytes
* @return
* @throws IOException
* @throws NoSuchAlgorithmException
*/
public static byte [] writeFile(Flosser flosser, ContentName completeName, int fileLength, Random randBytes) throws IOException, NoSuchAlgorithmException {
flosser.handleNamespace(completeName);
CCNOutputStream stockOutputStream = new CCNOutputStream(completeName, outputHandle);
DigestOutputStream digestStreamWrapper = new DigestOutputStream(stockOutputStream, MessageDigest.getInstance("SHA1"));
byte [] bytes = new byte[BUF_SIZE];
int elapsed = 0;
int nextBufSize = 0;
Log.info(Log.FAC_TEST, "Writing file: " + completeName + " bytes: " + fileLength);
final double probFlush = .3;
while (elapsed < fileLength) {
nextBufSize = ((fileLength - elapsed) > BUF_SIZE) ? BUF_SIZE : (fileLength - elapsed);
randBytes.nextBytes(bytes);
digestStreamWrapper.write(bytes, 0, nextBufSize);
elapsed += nextBufSize;
Log.info(completeName + " wrote " + elapsed + " out of " + fileLength + " bytes.");
if (randBytes.nextDouble() < probFlush) {
Log.info(Log.FAC_TEST, "Flushing buffers.");
digestStreamWrapper.flush();
}
}
digestStreamWrapper.close();
Log.info(Log.FAC_TEST, "Finished writing file " + completeName);
return digestStreamWrapper.getMessageDigest().digest();
}
public static byte [] readFile(InputStream inputStream, int fileLength) throws IOException {
DigestInputStream dis = null;
try {
dis = new DigestInputStream(inputStream, MessageDigest.getInstance("SHA1"));
} catch (NoSuchAlgorithmException e) {
Log.severe(Log.FAC_TEST, "No SHA1 available!");
Assert.fail("No SHA1 available!");
}
int elapsed = 0;
int read = 0;
byte [] bytes = new byte[BUF_SIZE];
while (elapsed < fileLength) {
read = dis.read(bytes);
if (read < 0) {
Log.info(Log.FAC_TEST, "EOF read at " + elapsed + " bytes out of " + fileLength);
break;
} else if (read == 0) {
Log.info(Log.FAC_TEST, "0 bytes read at " + elapsed + " bytes out of " + fileLength);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
}
elapsed += read;
Log.info(Log.FAC_TEST, " read " + elapsed + " bytes out of " + fileLength);
}
return dis.getMessageDigest().digest();
}
@Test
public void testCCNVersionedInputStreamContentNameLongPublisherKeyIDCCNLibrary() throws Exception {
Log.info(Log.FAC_TEST, "Starting testCCNVersionedInputStreamContentNameLongPublisherKeyIDCCNLibrary");
// we can make a new handle; as long as we don't use the outputHandle it should work
Log.info(Log.FAC_TEST, "first: "+firstVersionName);
Log.info(Log.FAC_TEST, "middle: "+middleVersionName);
Log.info(Log.FAC_TEST, "latest: "+latestVersionName);
CCNVersionedInputStream vfirst =
new CCNVersionedInputStream(firstVersionName,
((3 > firstVersionMaxSegment) ? firstVersionMaxSegment : 3L), outputHandle.getDefaultPublisher(), inputHandle);
CCNVersionedInputStream vlatest = new CCNVersionedInputStream(defaultStreamName,
((3 > latestVersionMaxSegment) ? latestVersionMaxSegment : 3L), outputHandle.getDefaultPublisher(), inputHandle);
testArgumentRunner(vfirst, vlatest);
Log.info(Log.FAC_TEST, "Completed testCCNVersionedInputStreamContentNameLongPublisherKeyIDCCNLibrary");
}
@Test
public void testCCNVersionedInputStreamContentNamePublisherKeyIDCCNLibrary() throws Exception {
Log.info(Log.FAC_TEST, "Starting testCCNVersionedInputStreamContentNamePublisherKeyIDCCNLibrary");
Log.info(Log.FAC_TEST, "firstVersionName: "+firstVersionName);
Log.info(Log.FAC_TEST, "middle: "+middleVersionName);
Log.info(Log.FAC_TEST, "latest: "+latestVersionName);
Log.info(Log.FAC_TEST, "defaultStreamName: "+defaultStreamName);
// we can make a new handle; as long as we don't use the outputHandle it should work
CCNVersionedInputStream vfirst = new CCNVersionedInputStream(firstVersionName, outputHandle.getDefaultPublisher(), inputHandle);
CCNVersionedInputStream vlatest = new CCNVersionedInputStream(defaultStreamName, outputHandle.getDefaultPublisher(), inputHandle2);
testArgumentRunner(vfirst, vlatest);
Log.info(Log.FAC_TEST, "Completed testCCNVersionedInputStreamContentNamePublisherKeyIDCCNLibrary");
}
@Test
public void testCCNVersionedInputStreamContentName() throws Exception {
Log.info(Log.FAC_TEST, "Starting testCCNVersionedInputStreamContentName");
// we can make a new handle; as long as we don't use the outputHandle it should work
CCNVersionedInputStream vfirst = new CCNVersionedInputStream(firstVersionName);
CCNVersionedInputStream vlatest = new CCNVersionedInputStream(defaultStreamName);
testArgumentRunner(vfirst, vlatest);
Log.info(Log.FAC_TEST, "Completed testCCNVersionedInputStreamContentName");
}
@Test
public void testCCNVersionedInputStreamContentNameCCNLibrary() throws Exception {
Log.info(Log.FAC_TEST, "Starting testCCNVersionedInputStreamContentNameCCNLibrary");
CCNVersionedInputStream vfirst = new CCNVersionedInputStream(firstVersionName, inputHandle);
CCNVersionedInputStream vlatest = new CCNVersionedInputStream(defaultStreamName, inputHandle);
testArgumentRunner(vfirst, vlatest);
Log.info(Log.FAC_TEST, "Completed testCCNVersionedInputStreamContentNameCCNLibrary");
}
protected void testArgumentRunner(CCNVersionedInputStream vfirst,
CCNVersionedInputStream vlatest) throws Exception {
Assert.assertEquals(vfirst.getBaseName(), firstVersionName);
Assert.assertEquals(VersioningProfile.cutTerminalVersion(vfirst.getBaseName()).first(), defaultStreamName);
byte b = (byte)vfirst.read();
if (b != 0x00) {
}
Assert.assertEquals(VersioningProfile.getLastVersionAsTimestamp(firstVersionName),
VersioningProfile.getLastVersionAsTimestamp(vfirst.getBaseName()));
Assert.assertEquals(VersioningProfile.getLastVersionAsTimestamp(firstVersionName),
vfirst.getVersionAsTimestamp());
Log.info(Log.FAC_TEST, "Opened stream on latest version, expected: " + latestVersionName + " got: " +
vlatest.getBaseName());
b = (byte)vlatest.read();
Log.info(Log.FAC_TEST, "Post-read: Opened stream on latest version, expected: " + latestVersionName + " got: " +
vlatest.getBaseName());
Log.info(Log.FAC_TEST, "versions as TS: "+VersioningProfile.getLastVersionAsTimestamp(latestVersionName)+" "+vlatest.getVersion());
Assert.assertEquals(vlatest.getBaseName(), latestVersionName);
Assert.assertEquals(VersioningProfile.cutTerminalVersion(vlatest.getBaseName()).first(), defaultStreamName);
Assert.assertEquals(VersioningProfile.getLastVersionAsLong(latestVersionName),
VersioningProfile.getLastVersionAsLong(vlatest.getBaseName()));
Assert.assertEquals(VersioningProfile.getLastVersionAsTimestamp(latestVersionName),
VersioningProfile.getLastVersionAsTimestamp(vlatest.getBaseName()));
Assert.assertEquals(VersioningProfile.getLastVersionAsTimestamp(latestVersionName),
vlatest.getVersionAsTimestamp());
}
@Test
public void testCCNVersionedInputStreamContentNameInt() throws Exception {
Log.info(Log.FAC_TEST, "Starting testCCNVersionedInputStreamContentNameInt");
// we can make a new handle; as long as we don't use the outputHandle it should work
CCNVersionedInputStream vfirst =
new CCNVersionedInputStream(firstVersionName, Math.min(4L, firstVersionMaxSegment), null);
CCNVersionedInputStream vlatest =
new CCNVersionedInputStream(defaultStreamName, Math.min(4L, latestVersionMaxSegment), null);
testArgumentRunner(vfirst, vlatest);
Log.info(Log.FAC_TEST, "Completed testCCNVersionedInputStreamContentNameInt");
}
@Test
public void testCCNVersionedInputStreamContentObjectCCNLibrary() throws Exception {
Log.info(Log.FAC_TEST, "Starting testCCNVersionedInputStreamContentObjectCCNLibrary");
// we can make a new handle; as long as we don't use the outputHandle it should work
ContentObject firstVersionBlock = inputHandle.get(firstVersionName, SystemConfiguration.getDefaultTimeout());
ContentObject latestVersionBlock = reader.get(Interest.last(defaultStreamName, defaultStreamName.count(), null), SystemConfiguration.getDefaultTimeout());
CCNVersionedInputStream vfirst = new CCNVersionedInputStream(firstVersionBlock, null, inputHandle);
CCNVersionedInputStream vlatest = new CCNVersionedInputStream(latestVersionBlock, null, inputHandle);
testArgumentRunner(vfirst, vlatest);
Log.info(Log.FAC_TEST, "Completed testCCNVersionedInputStreamContentObjectCCNLibrary");
}
@Test
public void testReadByteArray() throws Exception {
Log.info(Log.FAC_TEST, "Starting testReadByteArray");
// Test other forms of read in superclass test.
CCNVersionedInputStream vfirst = new CCNVersionedInputStream(firstVersionName, inputHandle);
byte [] readDigest = readFile(vfirst, firstVersionLength);
Assert.assertArrayEquals(firstVersionDigest, readDigest);
CCNVersionedInputStream vmiddle = new CCNVersionedInputStream(middleVersionName, inputHandle);
readDigest = readFile(vmiddle, middleVersionLength);
Assert.assertArrayEquals(middleVersionDigest, readDigest);
CCNVersionedInputStream vlatest = new CCNVersionedInputStream(defaultStreamName, inputHandle);
readDigest = readFile(vlatest, latestVersionLength);
Assert.assertArrayEquals(latestVersionDigest, readDigest);
Log.info(Log.FAC_TEST, "Completed testReadByteArray");
}
@Test
public void testReadProblematicLengths() throws Exception {
Log.info(Log.FAC_TEST, "Starting testReadProblematicLengths");
CCNVersionedInputStream vstream;
byte [] readDigest;
for (int i=0; i < problematicLengths.length; ++i) {
vstream = new CCNVersionedInputStream(problematicNames[i], inputHandle);
readDigest = readFile(vstream, problematicLengths[i]);
Assert.assertArrayEquals("Stream " + i + " failed to match, length " + problematicLengths[i],
problematicDigests[i], readDigest);
}
Log.info(Log.FAC_TEST, "Completed testReadProblematicLengths");
}
}