/*
Copyright (c) 2002, Dean Hiller
All rights reserved.
*****************************************************************
IF YOU MAKE CHANGES TO THIS CODE AND DO NOT POST THEM, YOU
WILL BE IN VIOLATION OF THE LICENSE I HAVE GIVEN YOU. Contact
me at deanhiller@users.sourceforge.net if you need a different
license.
*****************************************************************
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Also, just to clarify a point in the GNU license, this software
can only be bundled with your software if your software is free.
*/
package org.webpieces.nio.test;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import org.webpieces.util.logging.Logger;
import org.webpieces.nio.api.deprecated.ChannelServiceFactory;
import org.webpieces.nio.api.deprecated.CorruptPacketException;
import org.webpieces.nio.api.libs.BufferHelper;
import org.webpieces.nio.api.libs.FactoryCreator;
import org.webpieces.nio.api.libs.PacketListener;
import org.webpieces.nio.api.libs.PacketProcessor;
import org.webpieces.nio.api.libs.PacketProcessorFactory;
import org.webpieces.nio.api.testutil.CloneByteBuffer;
import biz.xsoftware.mock.CalledMethod;
import biz.xsoftware.mock.MockObject;
import biz.xsoftware.mock.testcase.MockTestCase;
public class TestUnpacketizer extends MockTestCase {
//--------------------------------------------------------------------
// FIELDS/MEMBERS
//--------------------------------------------------------------------
private static final Logger log = LoggerFactory.getLogger(TestUnpacketizer.class);
private static final String PACKET_METHOD = "incomingPacket";
private static final String HALF1 = "01234";
private static final String HALF2 = "56789";
private static final byte[] PACKET_SEPARATOR = new byte[] { Byte.MAX_VALUE, Byte.MAX_VALUE,
Byte.MAX_VALUE, Byte.MAX_VALUE, Byte.MAX_VALUE };
private PacketProcessor unpacketizer;
private BufferHelper helper;
private MockObject listener;
//--------------------------------------------------------------------
// CONSTRUCTORS
//--------------------------------------------------------------------
/**
* FILL IN JAVADOC HERE
* @param arg0
*/
public TestUnpacketizer(String arg0) {
super(arg0);
}
//--------------------------------------------------------------------
// BUSINESS METHODS
//--------------------------------------------------------------------
protected void setUpImpl() {
listener = createMock(PacketListener.class);
listener.setDefaultBehavior("incomingPacket", new CloneByteBuffer());
Map<String, Object> p = new HashMap<String, Object>();
p.put(FactoryCreator.KEY_PACKET_SEPARATOR, PACKET_SEPARATOR);
FactoryCreator creator = FactoryCreator.createFactory(null);
PacketProcessorFactory factory = creator.createPacketProcFactory(p);
unpacketizer = factory.createPacketProcessor("someId");
helper = ChannelServiceFactory.bufferHelper(null);
unpacketizer.setPacketListener((PacketListener)listener);
}
protected void tearDownImpl() {
}
/**
* Test normal behavior by running 2 full normal packets through.
*
*/
public void testNormalBehavior() throws IOException {
ByteBuffer b = ByteBuffer.allocate(100);
for(int i = 0; i < 2; i++) {
b.clear();
String fullString = HALF1+HALF2+i;
helper.putString(b, fullString);
helper.doneFillingBuffer(b);
ByteBuffer outgoing = unpacketizer.processOutgoing(b);
//contract is a rewound buffer that it can read to begin with.
unpacketizer.incomingData(outgoing, null);
CalledMethod method = listener.expect(PACKET_METHOD);
verifyBuffer(method, fullString, 11);
}
}
public void testHalfAPacket() throws IOException {
if (log.isTraceEnabled())
log.log(Level.FINE, "started");
ByteBuffer b = ByteBuffer.allocate(30);
String fullString = HALF1+HALF2;
int size = fullString.getBytes().length;
b.putInt(size);
helper.putString(b, HALF1);
helper.doneFillingBuffer(b);
unpacketizer.incomingData(b, null);
listener.expect(MockObject.NONE);
helper.eraseBuffer(b);
helper.putString(b, HALF2);
b.put(PACKET_SEPARATOR);
helper.doneFillingBuffer(b);
if (log.isTraceEnabled())
log.log(Level.FINE, "FEED NEXT BUFFER********************");
unpacketizer.incomingData(b, null);
CalledMethod method = listener.expect(PACKET_METHOD);
verifyBuffer(method, fullString, size);
if (log.isTraceEnabled())
log.log(Level.FINE, "ended");
}
/**
*
* Throw 2 and 1/2 packets at the unpacketizer and then
* throw last half and first half of next one and then finally
* the very last packet.
*
*/
public void testTwoAndHalfPackets() throws IOException {
if (log.isTraceEnabled())
log.log(Level.FINE, "started");
ByteBuffer b = ByteBuffer.allocate(200);
String fullString = HALF1+HALF2;
int size = fullString.getBytes().length;
b.putInt(size); //2*10 because chars are 2 bytes.
helper.putString(b, fullString);
b.put(PACKET_SEPARATOR);
b.putInt(size);
helper.putString(b, fullString);
b.put(PACKET_SEPARATOR);
b.putInt(size);
helper.putString(b, HALF1);
helper.doneFillingBuffer(b);
String[] methods = new String[2];
methods[0] = PACKET_METHOD;
methods[1] = PACKET_METHOD;
unpacketizer.incomingData(b, null);
CalledMethod[] calledMethods = listener.expect(methods);
//need to verify first 2 packets and then send
//both half of last and half of next to finish off tests.
verifyBuffer(calledMethods[0], fullString, size);
verifyBuffer(calledMethods[1], fullString, size);
helper.eraseBuffer(b);
helper.putString(b, HALF2);
b.put(PACKET_SEPARATOR);
b.putInt(size); //2*10 because chars are 2 bytes.
helper.putString(b, HALF1);
helper.doneFillingBuffer(b);
if (log.isTraceEnabled())
log.log(Level.FINE, "FEED NEXT BUFFER********************");
unpacketizer.incomingData(b, null);
CalledMethod method = listener.expect(PACKET_METHOD);
verifyBuffer(method, fullString, size);
//finish up by feeding last half of last packet
helper.eraseBuffer(b);
helper.putString(b, HALF2);
b.put(PACKET_SEPARATOR);
helper.doneFillingBuffer(b);
unpacketizer.incomingData(b, null);
method = listener.expect(PACKET_METHOD);
verifyBuffer(method, fullString, size);
if (log.isTraceEnabled())
log.log(Level.FINE, "ended");
}
public void testSplitInVeryFirstHeader() throws IOException {
if (log.isTraceEnabled())
log.log(Level.FINE, "started");
String test = "";
for(int i = 0; i < 260; i++) {
test += i%10;
}
int len = test.getBytes().length;
//259 so b1 and b2 contain parts of the size.
//this test must then split b1 and b2.
assertTrue("size should be greater than or equal to 260 for this test", len >= 260);
byte b1 = (byte) (len & 0x000000FF);
byte b2 = (byte) ((len & 0x0000FF00) >> 8);
byte b3 = (byte) ((len & 0x00FF0000) >> 16);
byte b4 = (byte) ((len & 0xFF000000) >> 32);
ByteBuffer b = ByteBuffer.allocate(300);
b.put(b4);
b.put(b3);
b.put(b2);
//leave b1 off and do it later for the split
//b.put(b1);
helper.doneFillingBuffer(b);
unpacketizer.incomingData(b, null);
//Object evt =
listener.expect(MockObject.NONE);
doLastPartOfSplitHeaderVerification(b, b1, test);
if (log.isTraceEnabled())
log.log(Level.FINE, "ended");
}
public void testSplitInSecondHeader() throws IOException {
if (log.isTraceEnabled())
log.log(Level.FINE, "started");
String test = "";
for(int i = 0; i < 260; i++) {
test += i%10;
}
int len = test.getBytes().length;
byte b1 = (byte) (len & 0x000000FF);
byte b2 = (byte) ((len & 0x0000FF00) >> 8);
byte b3 = (byte) ((len & 0x00FF0000) >> 16);
byte b4 = (byte) ((len & 0xFF000000) >> 32);
ByteBuffer b = ByteBuffer.allocate(400);
String fullString = HALF1+HALF2;
int size = fullString.getBytes().length;
b.putInt(size);
helper.putString(b, fullString);
b.put(PACKET_SEPARATOR);
b.put(b4);
b.put(b3);
b.put(b2);
//leave b1 off and do it later for the split
//b.put(b1);
helper.doneFillingBuffer(b);
//contract is a donePut()(flip() is donePut() buffer that it can read to begin with.
unpacketizer.incomingData(b, null);
CalledMethod method = listener.expect(PACKET_METHOD);
verifyBuffer(method, fullString, size);
doLastPartOfSplitHeaderVerification(b, b1, test); //numChars);
}
public void testExceptions() throws IOException {
setNumberOfExpectedWarnings(2);
if (log.isTraceEnabled())
log.log(Level.FINE, "started");
ByteBuffer b = ByteBuffer.allocate(200);
b.putInt(-6); //2*10 because chars are 2 bytes.
helper.putString(b, HALF1+HALF2);
b.putInt(20);
helper.doneFillingBuffer(b);
try {
//feed packet with negative size.....
unpacketizer.incomingData(b, null);
fail("Should have thrown a RuntimeException");
} catch(CorruptPacketException e) {
assertTrue("header should be corrupt", e.isHeaderCorrupt());
}
listener.expect(MockObject.NONE);
if (log.isTraceEnabled())
log.log(Level.FINE, "FEEDING NEXT BAD PACKET************");
helper.eraseBuffer(b);
b.putInt(100000000); //2*10 because chars are 2 bytes.
helper.putString(b, HALF1+HALF2);
b.putInt(20);
helper.doneFillingBuffer(b);
try {
//feed incoming packet with two large a header(ie. size is less than max size of packet)
unpacketizer.incomingData(b, null);
fail("Should have thrown an Exception");
} catch(CorruptPacketException e) {
assertTrue("header should be corrupt", e.isHeaderCorrupt());
}
listener.expect(MockObject.NONE);
if (log.isTraceEnabled())
log.log(Level.FINE, "FEEDING NULL************");
try {
unpacketizer.incomingData(null, null);
fail("Should have thrown a RuntimeException");
} catch(IllegalArgumentException e) {
assertEquals("Message was incorrect", "someId evt cannot be null", e.getMessage());
}
listener.expect(MockObject.NONE);
if (log.isTraceEnabled())
log.log(Level.FINE, "ended");
}
// public void testNoTrailerFailure() {
// String fullString = half1+half2;
// int size = fullString.getBytes().length;
// ByteBuffer b = ByteBuffer.allocate(size+30);
// b.putInt(size);
// helper.putString(b, fullString);
// //look, no packet separator mom!!!!
// //put the string again just for some junk data
// helper.putString(b, "a");
// b.put(packetSeparator); //now put the packet separator
// b.putInt(size);
// helper.putString(b, fullString);
// helper.doneFillingBuffer(b);
//
// //contract is a rewound buffer that it can read to begin with.
// try {
// unpacketizer.notify(b);
// fail("Should have thrown an exception");
// } catch(CorruptPacketException e) {
// assertTrue("trailer should have failed", e.isTrailerCorrupt());
// }
//
// //should still recover and get the good packet behind the bad one....
// CalledMethod method = listener.expect(PACKET_METHOD);
//
// verifyBuffer(method, fullString, size);
// }
//
// public void testFailureWithPacketSeparatorInPacket() {
//
// }
//
// //trailer failure should be thrown back to client rather than throwing listener's
// //failure as the first failure is what is needed usually to debug the problem and
// //maybe causing the second failure!!!
// public void testListenerFailureAfterTrailerFailure() {
//
// }
private void verifyBuffer(CalledMethod method, String expectedValue, int expectedSize) {
Object evt = method.getParameter(0);
assertTrue("Class of evt should be ByteBuffer", evt instanceof ByteBuffer);
ByteBuffer b = (ByteBuffer)evt;
assertEquals("remaining incorrect", expectedSize, b.remaining());
if (log.isTraceEnabled()) {
log.trace("10byteBuf pos="+b.position()+" lim="+b.limit()+" remain="+b.remaining());
}
String s = helper.readString(b, expectedSize);
log.info("s1="+s);
log.info("s2="+expectedValue);
assertEquals("Should have retrieved the string that we put", expectedValue, s);
assertEquals("buffer should be all read", 0, b.remaining());
}
private void doLastPartOfSplitHeaderVerification(ByteBuffer b, byte b1, String test) throws IOException {
helper.eraseBuffer(b);
b.put(b1);
helper.putString(b, test);
b.put(PACKET_SEPARATOR);
helper.doneFillingBuffer(b);
unpacketizer.incomingData(b, null);
CalledMethod method = listener.expect(PACKET_METHOD);
assertTrue("evt should have been an instance of ByteBuffer", method.getAllParams()[0] instanceof ByteBuffer);
b = (ByteBuffer)method.getAllParams()[0];
String s = helper.readString(b, b.remaining());
assertEquals("remaining once read should be 0", 0, b.remaining());
assertEquals("strings should be equal", test, s);
}
}