package com.limegroup.gnutella.dime; import java.io.ByteArrayOutputStream; import java.nio.ByteBuffer; import java.util.HashMap; import org.limewire.util.ByteUtils; import org.limewire.util.StringUtils; import com.limegroup.gnutella.stubs.ReadBufferChannel; import junit.framework.Test; /** * Tests for AsyncDimeRecordReader. */ public final class AsyncDimeRecordReaderTest extends org.limewire.gnutella.tests.LimeTestCase { /** * Constructs a new test instance for responses. */ public AsyncDimeRecordReaderTest(String name) { super(name); } public static Test suite() { return buildTestSuite(AsyncDimeRecordReaderTest.class); } /** * Runs this test individually. */ public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } public void testCreateAsync() throws Exception { byte[] data1 = new byte[] { 0x08, 0, 0, 0 }; // version + mb, me, cf, no type + resreved, option length: 0. byte[] data2 = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; // id, type, data length == 0. ByteBuffer buffer1 = (ByteBuffer)ByteBuffer.wrap(data1).position(data1.length); ByteBuffer buffer2 = ByteBuffer.wrap(data2); ReadBufferChannel channel = new ReadBufferChannel(buffer2, true); AsyncDimeRecordReader reader = new AsyncDimeRecordReader(); assertEquals(0, reader.getAmountProcessed()); assertTrue(buffer2.hasRemaining()); assertFalse(reader.process(channel, buffer1)); assertEquals(data1.length + data2.length, reader.getAmountProcessed()); assertFalse(buffer2.hasRemaining()); DIMERecord record = reader.getRecord(); assertFalse(record.isFirstRecord()); assertFalse(record.isLastRecord()); assertEquals(DIMERecord.TYPE_UNCHANGED, record.getTypeId()); assertEquals(0, record.getType().length); assertEquals("", record.getTypeString()); assertEquals(0, record.getData().length); assertEquals(0, record.getId().length); assertEquals(0, record.getOptions().length); assertEquals("", record.getIdentifier()); assertEquals("", record.getIdentifier()); // test again for coverage. assertEquals(new HashMap(), record.getOptionsMap()); assertEquals(new HashMap(), record.getOptionsMap()); // test again. } public void testCreateAsyncLeaveData() throws Exception { byte[] data1 = new byte[] { 0x08, 0, 0, 0 }; // version + mb, me, cf, no type + resreved, option length: 0. byte[] data2 = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0x1, 0x2, 0x3 }; // id, type, data length == 0, extra data ByteBuffer buffer1 = (ByteBuffer)ByteBuffer.wrap(data1).position(data1.length); ByteBuffer buffer2 = ByteBuffer.wrap(data2); ReadBufferChannel channel = new ReadBufferChannel(buffer2, true); AsyncDimeRecordReader reader = new AsyncDimeRecordReader(); assertEquals(0, reader.getAmountProcessed()); assertTrue(buffer2.hasRemaining()); assertFalse(reader.process(channel, buffer1)); assertEquals(data1.length + data2.length - 3, reader.getAmountProcessed()); assertTrue(buffer2.hasRemaining()); assertEquals(3, buffer2.remaining()); assertEquals(0x1, buffer2.get()); assertEquals(0x2, buffer2.get()); assertEquals(0x3, buffer2.get()); assertFalse(buffer2.hasRemaining()); DIMERecord record = reader.getRecord(); assertFalse(record.isFirstRecord()); assertFalse(record.isLastRecord()); assertEquals(DIMERecord.TYPE_UNCHANGED, record.getTypeId()); assertEquals(0, record.getType().length); assertEquals("", record.getTypeString()); assertEquals(0, record.getData().length); assertEquals(0, record.getId().length); assertEquals(0, record.getOptions().length); assertEquals("", record.getIdentifier()); assertEquals("", record.getIdentifier()); // test again for coverage. assertEquals(new HashMap(), record.getOptionsMap()); assertEquals(new HashMap(), record.getOptionsMap()); // test again. } public void testCreateWithDataAndPadding() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); out.write(0x08); out.write(DIMERecord.TYPE_MEDIA_TYPE); ByteUtils.int2beb(5, out, 2); // options length: 5 ByteUtils.int2beb(6, out, 2); // id length: 6 ByteUtils.int2beb(7, out, 2); // type length: 7 ByteUtils.int2beb(8, out, 4); // data length: 8 out.write(new byte[] { 's', 'a', 'm', 'm', 'y', 0 , 0 , 0 } ); out.write(new byte[] { 'b', 'e', 'r', 'l', 'i', 'n', 0 , 0 } ); out.write(new byte[] { 'h', 'a', 'c', 'k', 'e', 'r', 's', 0 } ); out.write(new byte[] { 'l', 'i', 'm', 'e', 'w', 'i', 'r', 'e' } ); ByteBuffer data = ByteBuffer.wrap(out.toByteArray()); ReadBufferChannel channel = new ReadBufferChannel(data); AsyncDimeRecordReader reader = new AsyncDimeRecordReader(); assertFalse(reader.process(channel, ByteBuffer.allocate(0))); assertEquals(data.capacity(), reader.getAmountProcessed()); DIMERecord record = reader.getRecord(); assertEquals(DIMERecord.TYPE_MEDIA_TYPE, record.getTypeId()); assertEquals(StringUtils.toUTF8Bytes("sammy"), record.getOptions()); assertEquals("berlin", record.getIdentifier()); assertEquals(StringUtils.toUTF8Bytes("berlin"), record.getId()); assertEquals("hackers", record.getTypeString()); assertEquals(StringUtils.toUTF8Bytes("limewire"), record.getData()); assertEquals(44, record.getRecordLength()); } public void testCreateMultiplePasses() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); out.write(0x08); out.write(DIMERecord.TYPE_MEDIA_TYPE); ByteUtils.int2beb(5, out, 2); // options length: 5 ByteUtils.int2beb(6, out, 2); // id length: 6 ByteUtils.int2beb(7, out, 2); // type length: 7 ByteUtils.int2beb(8, out, 4); // data length: 8 out.write(new byte[] { 's', 'a', 'm', 'm', 'y', 0 , 0 , 0 } ); out.write(new byte[] { 'b', 'e', 'r', 'l', 'i', 'n', 0 , 0 } ); out.write(new byte[] { 'h', 'a', 'c', 'k', 'e', 'r', 's', 0 } ); out.write(new byte[] { 'l', 'i', 'm', 'e', 'w', 'i', 'r', 'e' } ); out.write(new byte[] { 0x1, 0x2 } ); // bit of extra. ByteBuffer data = ByteBuffer.wrap(out.toByteArray()); ReadBufferChannel channel = new ReadBufferChannel(data); ByteBuffer zero = ByteBuffer.allocate(0); AsyncDimeRecordReader reader = new AsyncDimeRecordReader(); data.limit(5); assertTrue(reader.process(channel, zero)); assertEquals(5, reader.getAmountProcessed()); assertEquals(5, data.position()); assertNull(reader.getRecord()); data.limit(14); // finish header, a bit into options assertTrue(reader.process(channel, zero)); assertEquals(14, reader.getAmountProcessed()); assertEquals(14, data.position()); assertNull(reader.getRecord()); data.limit(19); // a bit into padding of options assertTrue(reader.process(channel, zero)); assertEquals(19, reader.getAmountProcessed()); assertEquals(19, data.position()); assertNull(reader.getRecord()); data.limit(43); // just before the end of data. assertTrue(reader.process(channel, zero)); assertEquals(43, reader.getAmountProcessed()); assertEquals(43, data.position()); assertNull(reader.getRecord()); data.limit(46); assertFalse(reader.process(channel, zero)); assertEquals(44, data.position()); assertEquals(44, reader.getAmountProcessed()); DIMERecord record = reader.getRecord(); assertEquals(DIMERecord.TYPE_MEDIA_TYPE, record.getTypeId()); assertEquals(StringUtils.toUTF8Bytes("sammy"), record.getOptions()); assertEquals("berlin", record.getIdentifier()); assertEquals(StringUtils.toUTF8Bytes("berlin"), record.getId()); assertEquals("hackers", record.getTypeString()); assertEquals(StringUtils.toUTF8Bytes("limewire"), record.getData()); assertEquals(44, record.getRecordLength()); } public void testDIMEExceptionAmountProcessed() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); out.write(0x08); out.write(DIMERecord.TYPE_MEDIA_TYPE); ByteUtils.int2beb(5, out, 2); // options length: 5 ByteUtils.int2beb(6, out, 2); // id length: 6 ByteUtils.int2beb(7, out, 2); // type length: 7 ByteUtils.int2beb(8, out, 4); // data length: 8 out.write(new byte[] { 's', 'a', 'm', 'm', 'y', 0 , 0 , 0 } ); out.write(new byte[] { 'b', 'e', 'r', 'l', 'i', 'n', 0 , 0 } ); out.write(new byte[] { 'h', 'a', 'c', 'k', 'e', 'r', 's', 0 } ); out.write(new byte[] { 'l', 'i', 'm', 'e', 'w', 'i', 'r', 'e' } ); out.write(new byte[] { 0x1, 0x2 } ); // bit of extra. } public void testInvalidTypes() throws Exception { ByteArrayOutputStream out; ByteBuffer buffer; AsyncDimeRecordReader reader; ReadBufferChannel channel; ByteBuffer zero = ByteBuffer.allocate(0); out = new ByteArrayOutputStream(); out.write( 0x08 ); // version + mb, me, cf (all clear) out.write( 0x00 ); // no type + reserved out.write( new byte[] { 0, 0 } ); // option length: 0 out.write( new byte[] { 0, 0 } ); // id length: 0 ByteUtils.int2beb(3, out, 2); // type length: 3 out.write( new byte[] { 0, 0, 0, 0 } ); // data length: 0 out.write( new byte[] { 's', 'a', 'm', 0 } ); // type + padding buffer = ByteBuffer.wrap(out.toByteArray()); reader = new AsyncDimeRecordReader(); channel = new ReadBufferChannel(buffer); assertFalse(reader.process(channel, zero)); assertEquals(buffer.capacity(), reader.getAmountProcessed()); try { reader.getRecord(); fail("expected exception."); } catch(DIMEException expected) { assertEquals("TYPE_UNCHANGED requires 0 type length", expected.getMessage()); } out = new ByteArrayOutputStream(); out.write( 0x08 ); // version + mb, me, cf (all clear) out.write( 0x03 << 4); // no type + reserved out.write( new byte[] { 0, 0 } ); // option length: 0 out.write( new byte[] { 0, 0 } ); // id length: 0 ByteUtils.int2beb(3, out, 2); // type length: 3 out.write( new byte[] { 0, 0, 0, 0 } ); // data length: 0 out.write( new byte[] { 's', 'a', 'm', 0 } ); // type + padding buffer = ByteBuffer.wrap(out.toByteArray()); reader = new AsyncDimeRecordReader(); channel = new ReadBufferChannel(buffer); assertFalse(reader.process(channel, zero)); assertEquals(buffer.capacity(), reader.getAmountProcessed()); try { reader.getRecord(); fail("expected exception."); } catch(DIMEException expected) { assertEquals("TYPE_UNKNOWN requires 0 type length", expected.getMessage()); } out = new ByteArrayOutputStream(); out.write( 0x08 ); // version + mb, me, cf (all clear) out.write( 0x04 << 4 ); // no type + reserved out.write( new byte[] { 0, 0 } ); // option length: 0 out.write( new byte[] { 0, 0 } ); // id length: 0 ByteUtils.int2beb(3, out, 2); // type length: 3 out.write( new byte[] { 0, 0, 0, 0 } ); // data length: 0 out.write( new byte[] { 's', 'a', 'm', 0 } ); // type + padding buffer = ByteBuffer.wrap(out.toByteArray()); reader = new AsyncDimeRecordReader(); channel = new ReadBufferChannel(buffer); assertFalse(reader.process(channel, zero)); assertEquals(buffer.capacity(), reader.getAmountProcessed()); try { reader.getRecord(); fail("expected exception."); } catch(DIMEException expected) { assertEquals("TYPE_NONE requires 0 type & data length", expected.getMessage()); } out = new ByteArrayOutputStream(); out.write( 0x08 ); // version + mb, me, cf (all clear) out.write( 0x04 << 4 ); // no type + reserved out.write( new byte[] { 0, 0 } ); // option length: 0 out.write( new byte[] { 0, 0 } ); // id length: 0 out.write( new byte[] { 0, 0 } ); // type length: 0 ByteUtils.int2beb(6, out, 4); // data length: 6 out.write( new byte[] { 's', 'a', 'm', 'u', 'e', 'l', 0, 0 } ); //data + padding buffer = ByteBuffer.wrap(out.toByteArray()); reader = new AsyncDimeRecordReader(); channel = new ReadBufferChannel(buffer); assertFalse(reader.process(channel, zero)); assertEquals(buffer.capacity(), reader.getAmountProcessed()); try { reader.getRecord(); fail("expected exception."); } catch(DIMEException expected) { assertEquals("TYPE_NONE requires 0 type & data length", expected.getMessage()); } } public void testBadType() throws Exception { // 0 through 4 are valid, so start at 5. for(int i = 5; i < 0xF; i++) { int type = i << 4; ByteArrayOutputStream out = new ByteArrayOutputStream(); out.write( (byte)0x08 ); //version + mb, me & cf. out.write( (byte)type ); // bad type + reserved out.write( new byte[] { 0, 0 } ); // option length: 0 out.write( new byte[] { 0, 0 } ); // id length: 0 out.write( new byte[] { 0, 0 } ); // type length: 0 out.write( new byte[] { 0, 0, 0, 0 } ); // data length: 0 out.write( new byte[] { 0x1, 0x2, 0x0, 0x0 } ); ByteBuffer buffer = ByteBuffer.wrap(out.toByteArray()); ReadBufferChannel channel = new ReadBufferChannel(buffer); AsyncDimeRecordReader reader = new AsyncDimeRecordReader(); assertFalse(reader.process(channel, ByteBuffer.allocate(0))); try { reader.getRecord(); fail("expected exception, i: " + i); } catch(DIMEException expected) { assertEquals("invalid type: " + i, expected.getMessage()); } } } public void testBadVersion() throws Exception { //test every invalid version possible. for(int i = 0; i < (0xF8 >> 3); i++) { if(i == 1) // valid continue; int version = i << 3; ByteArrayOutputStream out = new ByteArrayOutputStream(); out.write( (byte)version ); // mb, me & cf clear. out.write( 0x00 ); // no type + reserved out.write( new byte[] { 0, 0 } ); // option length: 0 out.write( new byte[] { 0, 0 } ); // id length: 0 ByteUtils.int2beb(2, out, 2); // type length: 2 out.write( new byte[] { 0, 0, 0, 0 } ); // data length: 0 out.write( new byte[] { 0x1, 0x2 } ); // type data ByteBuffer buffer = ByteBuffer.wrap(out.toByteArray()); ReadBufferChannel channel = new ReadBufferChannel(buffer); AsyncDimeRecordReader reader = new AsyncDimeRecordReader(); try { reader.process(channel, ByteBuffer.allocate(0)); fail("expected exception"); } catch(DIMEException expected) { assertEquals("invalid version: " + i, expected.getMessage()); } assertEquals(12, reader.getAmountProcessed()); assertEquals(2, buffer.remaining()); } } public void testBadReserved() throws Exception { //test every invalid reserved value possible. for(int i = 0; i < 0xF; i++) { if(i == 0) // valid continue; ByteArrayOutputStream out = new ByteArrayOutputStream(); out.write( 0x08 ); // mb, me & cf clear. out.write( (byte)i ); // no type + [invalid] reserved out.write( new byte[] { 0, 0 } ); // option length: 0 out.write( new byte[] { 0, 0 } ); // id length: 0 ByteUtils.int2beb(2, out, 2); // type length: 2 out.write( new byte[] { 0, 0, 0, 0 } ); // data length: 0 out.write( new byte[] { 0x1, 0x2 } ); // type data ByteBuffer buffer = ByteBuffer.wrap(out.toByteArray()); ReadBufferChannel channel = new ReadBufferChannel(buffer); AsyncDimeRecordReader reader = new AsyncDimeRecordReader(); try { reader.process(channel, ByteBuffer.allocate(0)); fail("expected exception"); } catch(DIMEException expected) { assertEquals("invalid reserved: " + i, expected.getMessage()); } assertEquals(12, reader.getAmountProcessed()); assertEquals(2, buffer.remaining()); } } public void testDataTooLarge() throws Exception { ByteArrayOutputStream out; ByteBuffer buffer; ReadBufferChannel channel; AsyncDimeRecordReader reader; out = new ByteArrayOutputStream(); out.write( 0x08 ); // mb, me & cf clear. out.write( 0x00 ); // // no type + reserved out.write( new byte[] { 0, 0 } ); // option length: 0 out.write( new byte[] { 0, 0 } ); // id length: 0 out.write( new byte[] { 0, 0 } ); // type length: 0 out.write( new byte[] { (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF } ); // data length: LARGE buffer = ByteBuffer.wrap(out.toByteArray()); channel = new ReadBufferChannel(buffer); reader = new AsyncDimeRecordReader(); try { reader.process(channel, ByteBuffer.allocate(0)); fail("expected exception"); } catch(DIMEException expected) { assertEquals("data too big.", expected.getMessage()); } // Test boundary cases. out = new ByteArrayOutputStream(); out.write( 0x08 ); // mb, me & cf clear. out.write( 0x00 ); // // no type + reserved out.write( new byte[] { 0, 0 } ); // option length: 0 out.write( new byte[] { 0, 0 } ); // id length: 0 out.write( new byte[] { 0, 0 } ); // type length: 0 // one over Integer.MAX_VALUE (in big endian format) out.write( new byte[] { (byte)0x80, (byte)0x00, (byte)0x00, (byte)0x00 } ); buffer = ByteBuffer.wrap(out.toByteArray()); channel = new ReadBufferChannel(buffer); reader = new AsyncDimeRecordReader(); try { reader.process(channel, ByteBuffer.allocate(0)); fail("expected exception"); } catch(DIMEException expected) { assertEquals("data too big.", expected.getMessage()); } // can't really test Integer.MAX_VALUE 'cause it's too big to // store. } }