/* * Part of the CCNx Java Library. * * Copyright (C) 2011-2012 Palo Alto Research Center, Inc. * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. * 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., 51 Franklin Street, * Fifth Floor, Boston, MA 02110-1301 USA. */ package org.ccnx.ccn.test.profiles.versioning; import java.io.IOException; import java.util.Arrays; import java.util.HashSet; import java.util.Random; import java.util.TreeSet; import junit.framework.Assert; import org.ccnx.ccn.CCNHandle; import org.ccnx.ccn.impl.CCNFlowControl.SaveType; import org.ccnx.ccn.io.content.CCNStringObject; import org.ccnx.ccn.profiles.VersioningProfile; import org.ccnx.ccn.profiles.versioning.VersionNumber; import org.ccnx.ccn.profiles.versioning.VersioningInterest; import org.ccnx.ccn.protocol.CCNTime; import org.ccnx.ccn.protocol.ContentName; import org.ccnx.ccn.protocol.MalformedContentNameStringException; import org.ccnx.ccn.test.profiles.versioning.VersioningHelper.ReceivedData; import org.ccnx.ccn.test.profiles.versioning.VersioningHelper.TestListener; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * This is really a super-set of the VersioningInterestManager tests. * What we need to test here is that we can express interests for * multiple names and multiple listeners. Then it comes down to * does VersioningInterestManager work (and that is tested * in VersioningInterestManagerTestRepo). */ public class VersioningInterestTestRepo { protected final Random _rnd = new Random(); protected final ContentName prefix; protected CCNHandle recvhandle = null; protected CCNHandle sendhandle = null; protected final static long TIMEOUT=30000; protected final static long SEND_PAUSE = 30; public VersioningInterestTestRepo() throws MalformedContentNameStringException { prefix = ContentName.fromNative(String.format("/repotest/test_%016X", _rnd.nextLong())); } @Before public void setUp() throws Exception { recvhandle = CCNHandle.open(); sendhandle = CCNHandle.open(); } @After public void tearDown() throws Exception { recvhandle.close(); sendhandle.close(); } // ====================================================== /** * Two basenames going to one listener */ @Test public void testTwoNamesOneListener() throws Exception { System.out.println("****** testTwoNamesOneListener starting"); ContentName base1 = new ContentName(prefix, String.format("content_%016X", _rnd.nextLong())); ContentName base2 = new ContentName(prefix, String.format("content_%016X", _rnd.nextLong())); ContentName [] names = new ContentName [] {base1, base2}; TestListener listener = new TestListener(); VersioningInterest vi = new VersioningInterest(recvhandle); vi.expressInterest(base1, listener); vi.expressInterest(base2, listener); // send data CCNTime now = CCNTime.now(); long start = now.getTime(); long stop = start + 1000000L; int count = 100; TreeSet<CCNTime> sent = sendStreamUniform(sendhandle, names, start, stop, count); // now make sure we get them all boolean b = listener.cl.waitForValue(count, TIMEOUT); System.out.println("Received: " + listener.cl.getValue()); Assert.assertTrue(b); HashSet<CCNTime> recv = new HashSet<CCNTime>(); for(ReceivedData data : listener.received) { ContentName name = data.object.name(); CCNTime version = VersioningProfile.getLastVersionAsTimestamp(name); recv.add(version); } // and compare the received versions boolean missing = false; for(CCNTime version : sent) { if( !recv.contains(version) ) { System.out.println("recv missing version " + new VersionNumber(version)); missing = true; } } Assert.assertFalse(missing); vi.close(); System.out.println("****** testTwoNamesOneListener done"); } /** * Three basenames, each with own listener, plus one listener that gets them all */ @Test public void testThreeNamesFourListener() throws Exception { System.out.println("****** testThreeNamesFourListener starting"); ContentName base1 = new ContentName(prefix, String.format("content_%016X", _rnd.nextLong())); ContentName base2 = new ContentName(prefix, String.format("content_%016X", _rnd.nextLong())); ContentName base3 = new ContentName(prefix, String.format("content_%016X", _rnd.nextLong())); ContentName [] names = new ContentName [] {base1, base2, base3}; TestListener listener1 = new TestListener(); TestListener listener2 = new TestListener(); TestListener listener3 = new TestListener(); TestListener listener4 = new TestListener(); VersioningInterest vi = new VersioningInterest(recvhandle); vi.expressInterest(base1, listener1); vi.expressInterest(base2, listener2); vi.expressInterest(base3, listener3); vi.expressInterest(base1, listener4); vi.expressInterest(base2, listener4); vi.expressInterest(base3, listener4); // send data CCNTime now = CCNTime.now(); long start = now.getTime(); long stop = start + 1000000L; int count = 100; TreeSet<CCNTime> sent = sendStreamUniform(sendhandle, names, start, stop, count); // now make sure we get them all boolean b = listener4.cl.waitForValue(count, TIMEOUT); System.out.println("Received: " + listener4.cl.getValue()); Assert.assertTrue(b); HashSet<CCNTime> recv = new HashSet<CCNTime>(); for(ReceivedData data : listener4.received) { ContentName name = data.object.name(); CCNTime version = VersioningProfile.getLastVersionAsTimestamp(name); recv.add(version); } // and compare the received versions boolean missing = false; for(CCNTime version : sent) { if( !recv.contains(version) ) { System.out.println("recv missing version " + new VersionNumber(version)); missing = true; } } Assert.assertFalse(missing); // Make sure other listeners got their shares long sum = listener1.cl.getValue() + listener2.cl.getValue() + listener3.cl.getValue(); System.out.println("Other listeners got " + sum); Assert.assertEquals(count, (int) sum); vi.close(); System.out.println("****** testThreeNamesFourListener done"); } // ======================================================== private TreeSet<CCNTime> sendStreamUniform(CCNHandle handle, ContentName [] names, long start_time, long stop_time, int count) throws Exception { TreeSet<CCNTime> sent = new TreeSet<CCNTime>(); long [] sends = new long[names.length]; for(int i = 0; i < sends.length; i++ ) sends[i] = 0; int width = (int) (stop_time - start_time + 1); for(int i = 0; i < count; i++) { CCNTime version; // pick the name int j = _rnd.nextInt(names.length); ContentName name = names[j]; sends[j]++; // avoid sending duplicate version numbers do { int delta = _rnd.nextInt(width); version = new CCNTime(start_time + delta); } while( !sent.add(version)); // System.out.println("Sending " + i); send(handle, name, version); } System.out.println("sends: " + Arrays.toString(sends)); return sent; } private void send(CCNHandle handle, ContentName name, CCNTime version) throws IOException, InterruptedException { CCNStringObject so = new CCNStringObject(name, "Hello, World " + version, SaveType.LOCALREPOSITORY, handle); int maxtry = 10; int trycount = 0; IOException error = null; do { error = null; try { // do this every time, not just on errors. The FlowController seems // to not like to receive a lot of objects all at once. // With each error, sleep longer Thread.sleep(SEND_PAUSE * (2 * trycount + 1)); boolean b = so.save(version); if( b ) { so.close(); return; } else { throw new IOException("Not saved"); } } catch(IOException e) { error = e; Thread.sleep(SEND_PAUSE * (2 * trycount + 1)); } trycount++; } while( trycount < maxtry && null != error ); if( null != error ) { throw error; } // ContentName versionedName = so.getVersionedName(); // so = new CCNStringObject(name, handle); } }