/*
* 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.protocol;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.util.Arrays;
import java.util.Date;
import java.util.Random;
import junit.framework.Assert;
import org.ccnx.ccn.impl.support.DataUtils;
import org.ccnx.ccn.impl.support.Log;
import org.ccnx.ccn.protocol.CCNTime;
import org.junit.BeforeClass;
import org.junit.Test;
/**
* Test CCN quantized time wrapper.
*/
public class CCNTimeTest {
static int NUM_RUNS = 10;
static Random random = new Random();
static Timestamp early, middle, late;
static Date dearly, dmiddle, dlate;
/**
* @throws java.lang.Exception
*/
@BeforeClass
public static void setUpBeforeClass() throws Exception {
dearly = new Date();
early = new Timestamp(System.currentTimeMillis());
Thread.sleep(10);
dmiddle = new Date();
middle = new Timestamp(System.currentTimeMillis());
Thread.sleep(10);
dlate = new Date();
late = new Timestamp(System.currentTimeMillis());
}
/**
* Test method for CCNTime#setTime(long).
*/
@Test
public void testSetTime() {
Log.info(Log.FAC_TEST, "Starting testSetTime");
for (int i=0; i < NUM_RUNS; ++i) {
Timestamp e2 = new Timestamp(early.getTime());
e2.setNanos(early.getNanos());
Assert.assertEquals(early, e2);
CCNTime c2 = new CCNTime(e2);
Assert.assertTrue(timestampEquals(e2, c2));
Assert.assertTrue(c2.equals(e2));
long newTime = e2.getTime() + random.nextInt(1000 * 60 * 60 * 24 * 7); // up to a week
e2.setTime(newTime);
c2.setTime(newTime);
Assert.assertTrue(timestampEquals(e2, c2));
Assert.assertTrue(c2.equals(e2));
testTimestamp(c2, e2);
}
Log.info(Log.FAC_TEST, "Completed testSetTime");
}
/**
* Test method for CCNTime#setNanos(int).
*/
@Test
public void testSetNanos() {
Log.info(Log.FAC_TEST, "Starting testSetNanos");
for (int i=0; i < NUM_RUNS; ++i) {
Timestamp e2 = new Timestamp(early.getTime());
e2.setNanos(early.getNanos());
Assert.assertEquals(early, e2);
CCNTime c2 = new CCNTime(e2);
Assert.assertTrue(timestampEquals(e2, c2));
Assert.assertTrue(c2.equals(e2));
// 999877929 is the highest value that doesn't quantize over the Timestamp limit of 999999999
int newNanos = random.nextInt(CCNTime.NANOS_MAX);
e2.setNanos(newNanos);
c2.setNanos(newNanos);
Assert.assertTrue(timestampEquals(e2, c2));
Assert.assertTrue(c2.equals(e2));
testTimestamp(c2, e2);
}
Log.info(Log.FAC_TEST, "Completed testSetNanos");
}
/**
* Test method for CCNTime#CCNTime(long).
*/
@Test
public void testCCNTimeLong() {
Log.info(Log.FAC_TEST, "Starting testCCNTimeLong");
for (int i=0; i < NUM_RUNS; ++i) {
long msec = System.currentTimeMillis();
CCNTime t1 = new CCNTime(msec);
Timestamp ts1 = new Timestamp(msec);
testTimestamp(t1, ts1);
}
Log.info(Log.FAC_TEST, "Completed testCCNTimeLong");
}
/**
* Test method for CCNTime#CCNTime(java.sql.Timestamp).
*/
@Test
public void testCCNTimeTimestamp() {
Log.info(Log.FAC_TEST, "Starting testCCNTimeTimestamp");
for (int i=0; i < NUM_RUNS; ++i) {
Timestamp ts2 = new Timestamp(System.currentTimeMillis());
ts2.setNanos(random.nextInt(10000000));
CCNTime t2 = new CCNTime(ts2);
testTimestamp(t2, ts2);
}
Log.info(Log.FAC_TEST, "Completed testCCNTimeTimestamp");
}
/**
* Test method for CCNTime#CCNTime(java.util.Date).
*/
@Test
public void testCCNTimeDate() {
Log.info(Log.FAC_TEST, "Starting testCCNTimeDate");
for (int i=0; i < NUM_RUNS; ++i) {
Date now = new Date();
CCNTime timeNow = new CCNTime(now);
Timestamp tsn = new Timestamp(now.getTime());
testTimestamp(timeNow, tsn);
}
Log.info(Log.FAC_TEST, "Completed testCCNTimeDate");
}
/**
* Test method for CCNTime#CCNTime().
*/
@Test
public void testCCNTime() {
Log.info(Log.FAC_TEST, "Starting testCCNTime");
for (int i=0; i < NUM_RUNS; ++i) {
CCNTime now = new CCNTime();
Timestamp tn = new Timestamp(now.getTime());
tn.setNanos(now.getNanos());
testTimestamp(now, tn);
}
// Sample problem case
long msec = 1252088415990L;
CCNTime m = new CCNTime(msec);
Timestamp tm = new Timestamp(msec);
Timestamp tn = new Timestamp(m.getTime());
tn.setNanos(m.getNanos());
Timestamp rm = roundTimestamp(tm);
testTimestamp(m, tm);
testTimestamp(m, rm);
testTimestamp(m, tn);
Log.info(Log.FAC_TEST, "Completed testCCNTime");
}
/**
* Test method for CCNTime#compareTo(java.util.Date).
*/
@Test
public void testCompareToDate() {
Log.info(Log.FAC_TEST, "Starting testCompareToDate");
CCNTime ce = new CCNTime(dearly);
CCNTime cm = new CCNTime(dmiddle);
CCNTime cl = new CCNTime(dlate);
Assert.assertTrue(ce.compareTo(dmiddle) < 0);
Assert.assertTrue(ce.compareTo(dlate) < 0);
Assert.assertTrue(cm.compareTo(dearly) > 0);
Assert.assertTrue(cm.compareTo(dlate) < 0);
Assert.assertTrue(cl.compareTo(dearly) > 0);
Assert.assertTrue(cl.compareTo(dmiddle) > 0);
// Can't compare the other way. Date.compareTo returning 1 for all targets, whether CCNTime or Timestamp. Only works on other Dates
Log.info(Log.FAC_TEST, "Completed testCompareToDate");
}
/**
* Test method for CCNTime#compareTo(java.sql.Timestamp).
*/
@Test
public void testCompareToTimestamp() {
Log.info(Log.FAC_TEST, "Starting testCompareToTimestamp");
CCNTime ce = new CCNTime(early);
CCNTime cm = new CCNTime(middle);
CCNTime cl = new CCNTime(late);
Assert.assertTrue(ce.compareTo(middle) < 0);
Assert.assertTrue(ce.compareTo(late) < 0);
Assert.assertTrue(cm.compareTo(early) > 0);
Assert.assertTrue(cm.compareTo(late) < 0);
Assert.assertTrue(cl.compareTo(early) > 0);
Assert.assertTrue(cl.compareTo(middle) > 0);
Assert.assertTrue(early.compareTo(cm) < 0);
Assert.assertTrue(early.compareTo(cl) < 0);
Assert.assertTrue(middle.compareTo(ce) > 0);
Assert.assertTrue(middle.compareTo(cl) < 0);
Assert.assertTrue(late.compareTo(ce) > 0);
Assert.assertTrue(late.compareTo(cm) > 0);
Log.info(Log.FAC_TEST, "Completed testCompareToTimestamp");
}
/**
* Test method for CCNTime#before(java.sql.Timestamp) and CCNTime#after(java.sql.Timestamp).
*/
@Test
public void testBeforeAfterTimestamp() {
Log.info(Log.FAC_TEST, "Starting testBeforeAfterTimestamp");
CCNTime ce = new CCNTime(early);
CCNTime cm = new CCNTime(middle);
CCNTime cl = new CCNTime(late);
Assert.assertTrue(ce.before(middle));
Assert.assertTrue(ce.before(late));
Assert.assertTrue(cm.after(early));
Assert.assertTrue(cm.before(late));
Assert.assertTrue(cl.after(early));
Assert.assertTrue(cl.after(middle));
Assert.assertTrue(early.before(cm));
Assert.assertTrue(early.before(cl));
Assert.assertTrue(middle.after(ce));
Assert.assertTrue(middle.before(cl));
Assert.assertTrue(late.after(ce));
Assert.assertTrue(late.after(cm));
Log.info(Log.FAC_TEST, "Completed testBeforeAfterTimestamp");
}
/**
* Test method for CCNTime#before(java.util.Date) and CCNTime#after(java.util.Date).
*/
@Test
public void testBeforeAfterDate() {
Log.info(Log.FAC_TEST, "Starting testBeforeAfterDate");
CCNTime ce = new CCNTime(dearly);
CCNTime cm = new CCNTime(dmiddle);
CCNTime cl = new CCNTime(dlate);
Assert.assertTrue(ce.before(dmiddle));
Assert.assertTrue(ce.before(dlate));
Assert.assertTrue(cm.after(dearly));
Assert.assertTrue(cm.before(dlate));
Assert.assertTrue(cl.after(dearly));
Assert.assertTrue(cl.after(dmiddle));
// Date's comparisons not happy with timestamps
Log.info(Log.FAC_TEST, "Completed testBeforeAfterDate");
}
/**
* Test method for CCNTime#now().
*/
@Test
public void testNow() {
Log.info(Log.FAC_TEST, "Starting testNow");
for (int i=0; i < NUM_RUNS; ++i) {
CCNTime now = CCNTime.now();
Timestamp tn = new Timestamp(now.getTime());
tn.setNanos(now.getNanos());
testTimestamp(now, tn);
}
Log.info(Log.FAC_TEST, "Completed testNow");
}
@Test
public void testBinaryArray() throws Exception {
Log.info(Log.FAC_TEST, "Starting testBinaryArray");
long v0 = 0x7FFFFF;
byte [] b0 = new byte [] {(byte) 0x7F, (byte) 0xFF, (byte) 0xFF};
CCNTime t0 = CCNTime.fromBinaryTimeAsLong(v0);
byte [] x0 = t0.toBinaryTime();
Assert.assertTrue(java.util.Arrays.equals(b0, x0));
long v1 = 0x80FFFF;
byte [] b1 = new byte [] {(byte) 0x80, (byte) 0xFF, (byte) 0xFF};
CCNTime t1 = CCNTime.fromBinaryTimeAsLong(v1);
byte [] x1 = t1.toBinaryTime();
Assert.assertTrue(java.util.Arrays.equals(b1, x1));
Log.info(Log.FAC_TEST, "Completed testBinaryArray");
}
public void testTimestamp(CCNTime ccnTime, Timestamp compareTS) {
Timestamp rounded = roundTimestamp(compareTS);
Assert.assertTrue(timestampEquals(compareTS, rounded));
Assert.assertTrue(timestampEquals(rounded, ccnTime));
Assert.assertEquals(rounded, ccnTime);
Assert.assertTrue(timestampEquals(compareTS, ccnTime));
long ts12 = timestampToBinaryTime12AsLong(compareTS);
long r12 = timestampToBinaryTime12AsLong(rounded);
long c12 = timestampToBinaryTime12AsLong(ccnTime);
long c12t = ccnTime.toBinaryTimeAsLong();
Assert.assertEquals(ts12, r12);
Assert.assertEquals(ts12, c12);
Assert.assertEquals(ts12, c12t);
byte [] tsb12 = timestampToBinaryTime12(compareTS);
byte [] rb12 = timestampToBinaryTime12(rounded);
byte [] cb12 = timestampToBinaryTime12(ccnTime);
byte [] cb12t = ccnTime.toBinaryTime();
Assert.assertTrue(Arrays.equals(tsb12, rb12));
Assert.assertTrue(Arrays.equals(tsb12, cb12));
Assert.assertTrue(Arrays.equals(tsb12, cb12t));
CCNTime cfb = new CCNTime(cb12t);
Assert.assertEquals(cfb, ccnTime);
CCNTime cfl = CCNTime.fromBinaryTimeAsLong(c12);
Assert.assertEquals(cfl, ccnTime);
}
/**
* Old static quantized time interface. Move here as "ground truth", as we
* know it is compatible with the C side; use it to test against.
*/
/**
* Converts a timestamp into a fixed point representation, with 12 bits in the fractional
* component, and adds this to the ContentName as a version field. The timestamp is rounded
* to the nearest value in the fixed point representation.
* <p>
* This allows versions to be recorded as a timestamp with a 1/4096 second accuracy.
*/
public static byte [] timestampToBinaryTime12(Timestamp timestamp) {
long timeVal = timestampToBinaryTime12AsLong(timestamp);
return BigInteger.valueOf(timeVal).toByteArray();
}
public static long timestampToBinaryTime12AsLong(Timestamp timestamp) {
long binaryTime12AsLong = (timestamp.getTime() / 1000) * 4096L + (timestamp.getNanos() * 4096L + 500000000L) / 1000000000L;
return binaryTime12AsLong;
}
public static Timestamp binaryTime12ToTimestamp(byte [] binaryTime12) {
if ((null == binaryTime12) || (binaryTime12.length == 0)) {
throw new IllegalArgumentException("Invalid binary time!");
} else if (binaryTime12.length > 6) {
throw new IllegalArgumentException("Time unacceptably far in the future, can't decode: " + DataUtils.printHexBytes(binaryTime12));
}
long time = new BigInteger(binaryTime12).longValue();
Timestamp ts = binaryTime12ToTimestamp(time);
return ts;
}
public static Timestamp binaryTime12ToTimestamp(long binaryTime12AsLong) {
Timestamp ts = new Timestamp((binaryTime12AsLong / 4096L) * 1000L);
ts.setNanos((int)(((binaryTime12AsLong % 4096L) * 1000000000L) / 4096L));
return ts;
}
/**
* Compare timestamps taking into account the resolution lost in the conversion above.
*/
public static boolean timestampEquals(Timestamp t1, Timestamp t2) {
long timeVal1 = CCNTimeTest.timestampToBinaryTime12AsLong(t1);
long timeVal2 = CCNTimeTest.timestampToBinaryTime12AsLong(t2);
return (timeVal1 == timeVal2);
}
/**
* Rounding function for timestamps.
*/
public static Timestamp roundTimestamp(Timestamp origTimestamp) {
long binaryTime12AsLong = timestampToBinaryTime12AsLong(origTimestamp);
Timestamp ts = binaryTime12ToTimestamp(binaryTime12AsLong);
return ts;
}
}