/* * JBoss, Home of Professional Open Source. * * Copyright 2012 Red Hat, Inc. and/or its affiliates, and individual * contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xnio.streams; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.xnio.AssertReadWrite.assertReadMessage; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.NoSuchElementException; import org.junit.Before; import org.junit.Test; import org.xnio.Buffers; import org.xnio.ByteBufferSlicePool; import org.xnio.Pooled; /** * Test for {@link BufferPipeInputStream}. * * @author <a href="mailto:flavia.rainone@jboss.com">Flavia Rainone</a> */ public class BufferPipeInputStreamTestCase { private TestInputHandler handler; private BufferPipeInputStream stream; @Before public void before() { handler = new TestInputHandler(); stream = new BufferPipeInputStream(handler); } @Test public void pushEmptyBuffer() throws IOException { assertEquals(0, stream.available()); stream.push(ByteBuffer.allocate(0)); assertEquals(0, stream.available()); final Pooled<ByteBuffer> pooledBuffer = new ByteBufferSlicePool(1, 1).allocate(); pooledBuffer.getResource().flip(); stream.push(pooledBuffer); assertEquals(0, stream.available()); } @Test public void pushAfterFailure() throws IOException { stream.pushException(new IOException("test")); assertCantPush(); } @Test public void readSimpleBuffer() throws IOException { final ByteBuffer buffer = ByteBuffer.allocate(20); buffer.put("abc, def, ghi, jkl".getBytes("UTF-8")).flip(); stream.push(buffer); assertEquals(18, stream.available()); // can't read to empty buffer assertEquals(0, stream.read(new byte[0])); byte[] readBuffer = new byte[20]; assertEquals(18, stream.read(readBuffer)); assertReadMessage(readBuffer, "abc, ", "def, ", "ghi, ", "jkl"); assertEquals(0, stream.available()); assertHandledMessages(false, "abc, def, ghi, jkl"); } @Test public void readPooledByteBuffer() throws IOException { final ByteBufferSlicePool pool = new ByteBufferSlicePool(30, 30); final Pooled<ByteBuffer> pooledBuffer = pool.allocate(); pooledBuffer.getResource().put("abc, def, ghi, jkl".getBytes("UTF-8")).flip(); stream.push(pooledBuffer); assertEquals(18, stream.available()); byte[] readBuffer = new byte[20]; assertEquals(18, stream.read(readBuffer)); assertReadMessage(readBuffer, "abc, ", "def, ", "ghi, ", "jkl"); assertEquals(0, stream.available()); assertHandledMessages(false, "abc, def, ghi, jkl"); } @Test public void readMultipleBuffers() throws IOException { final ByteBufferSlicePool pool = new ByteBufferSlicePool(5, 5); final Pooled<ByteBuffer> pooledBuffer1 = pool.allocate(); final Pooled<ByteBuffer> pooledBuffer2 = pool.allocate(); final ByteBuffer byteBuffer1 = ByteBuffer.allocate(1); final ByteBuffer byteBuffer2 = ByteBuffer.allocate(2); final ByteBuffer byteBuffer3 = ByteBuffer.allocate(3); pooledBuffer1.getResource().put("multi".getBytes("UTF-8")).flip(); byteBuffer1.put("p".getBytes("UTF-8")).flip(); pooledBuffer2.getResource().put("le bu".getBytes("UTF-8")).flip(); byteBuffer2.put("ff".getBytes("UTF-8")).flip(); byteBuffer3.put("ers".getBytes("UTF-8")).flip(); assertEquals(0, stream.available()); stream.push(pooledBuffer1); assertEquals(5, stream.available()); stream.push(byteBuffer1); assertEquals(6, stream.available()); stream.push(pooledBuffer2); assertEquals(11, stream.available()); stream.push(byteBuffer2); assertEquals(13, stream.available()); stream.push(byteBuffer3); assertEquals(16, stream.available()); byte[] readBuffer = new byte[20]; assertEquals(16, stream.read(readBuffer)); assertReadMessage(readBuffer, "multiple buffers"); assertEquals(0, stream.available()); assertHandledMessages(false, "multi", "p", "le bu", "ff", "ers"); } @Test public void readMultipleBuffersMultipleTimes() throws IOException { final ByteBufferSlicePool pool = new ByteBufferSlicePool(5, 5); final Pooled<ByteBuffer> pooledBuffer1 = pool.allocate(); final Pooled<ByteBuffer> pooledBuffer2 = pool.allocate(); final ByteBuffer byteBuffer1 = ByteBuffer.allocate(1); final ByteBuffer byteBuffer2 = ByteBuffer.allocate(2); final ByteBuffer byteBuffer3 = ByteBuffer.allocate(3); byteBuffer1.put("m".getBytes("UTF-8")).flip(); pooledBuffer1.getResource().put("ultip".getBytes("UTF-8")).flip(); pooledBuffer2.getResource().put("le bu".getBytes("UTF-8")).flip(); byteBuffer2.put("ff".getBytes("UTF-8")).flip(); byteBuffer3.put("ers".getBytes("UTF-8")).flip(); assertEquals(0, stream.available()); stream.push(byteBuffer1); assertEquals(1, stream.available()); stream.push(pooledBuffer1); assertEquals(6, stream.available()); stream.push(pooledBuffer2); assertEquals(11, stream.available()); stream.push(byteBuffer2); assertEquals(13, stream.available()); stream.push(byteBuffer3); assertEquals(16, stream.available()); byte[] readBuffer = new byte[3]; assertEquals(3, stream.read(readBuffer)); assertReadMessage(readBuffer, "mul"); assertEquals(13, stream.available()); assertEquals(3, stream.read(readBuffer)); assertReadMessage(readBuffer, "tip"); assertEquals(10, stream.available()); assertEquals(3, stream.read(readBuffer)); assertReadMessage(readBuffer, "le "); assertEquals(7, stream.available()); assertEquals(3, stream.read(readBuffer)); assertReadMessage(readBuffer, "buf"); assertEquals(4, stream.available()); assertEquals(3, stream.read(readBuffer)); assertReadMessage(readBuffer, "fer"); assertEquals(1, stream.available()); assertEquals(1, stream.read(readBuffer)); assertReadMessage(readBuffer, "s"); assertEquals(0, stream.available()); assertHandledMessages(false, "m", "ultip", "le bu", "ff", "ers"); } @Test public void readFailure() throws IOException { final IOException exception = new IOException("test"); stream.pushException(exception); IOException failure = null; try { stream.read(new byte[5]); } catch (IOException e) { failure = e; } assertSame(exception, failure); assertCantPush(); assertHandledMessages(false); } @Test public void messageIsNotTruncatedByFailure() throws IOException { final ByteBuffer buffer = ByteBuffer.allocate(20); buffer.put("truncated message".getBytes("UTF-8")).flip(); stream.push(buffer); assertEquals(17, stream.available()); byte[] readBuffer = new byte[10]; assertEquals(10, stream.read(readBuffer)); assertReadMessage(readBuffer, "truncated "); assertEquals(7, stream.available()); final IOException exception = new IOException("test"); stream.pushException(exception); assertEquals(7, stream.read(readBuffer)); assertReadMessage(readBuffer, "message"); assertEquals(0, stream.available()); IOException failure = null; try { stream.read(readBuffer); } catch (IOException e) { failure = e; } assertSame(exception, failure); assertEquals(0, stream.available()); assertCantPush(); assertHandledMessages(false, "truncated message"); } @Test public void readWaitsForPush() throws Exception { final ByteBuffer buffer = ByteBuffer.allocate(5); buffer.put("read".getBytes("UTF-8")).flip(); assertEquals(0, stream.available()); final ReadTask read = new ReadTask(stream); final Thread readThread = new Thread(read); readThread.start(); readThread.join(100); assertTrue(readThread.isAlive()); stream.push(buffer); readThread.join(); assertEquals(0, stream.available()); assertEquals(4, read.getReadResult()); assertReadMessage(read.getReadBuffer(), "read"); assertHandledMessages(false, "read"); } @Test public void readWaitsForFailure() throws Exception { assertEquals(0, stream.available()); final ReadTask read = new ReadTask(stream); final Thread readThread = new Thread(read); readThread.start(); readThread.join(100); assertTrue(readThread.isAlive()); final IOException failure = new IOException("test"); stream.pushException(failure); readThread.join(); assertEquals(0, stream.available()); assertEquals(0, read.getReadResult()); assertReadMessage(read.getReadBuffer()); assertSame(failure, read.getFailure()); assertCantPush(); assertHandledMessages(false); } @Test public void readWaitsForEof() throws Exception { assertEquals(0, stream.available()); final ReadTask read = new ReadTask(stream); final Thread readThread = new Thread(read); readThread.start(); readThread.join(100); assertTrue(readThread.isAlive()); stream.pushEof(); readThread.join(); assertEquals(0, stream.available()); assertEquals(-1, read.getReadResult()); assertReadMessage(read.getReadBuffer()); assertNull(read.getFailure()); assertCantPush(); assertHandledMessages(false); } @Test public void readWaitsForClose() throws Exception { assertEquals(0, stream.available()); final ReadTask read = new ReadTask(stream); final Thread readThread = new Thread(read); readThread.start(); readThread.join(100); assertTrue(readThread.isAlive()); stream.close(); readThread.join(); assertEquals(0, stream.available()); assertEquals(-1, read.getReadResult()); assertReadMessage(read.getReadBuffer()); assertNull(read.getFailure()); assertCantPush(); assertHandledMessages(true); } @Test public void concurrentReadBuffers() throws Exception { final ByteBuffer buffer = ByteBuffer.allocate(20); buffer.put("abcdefghijklmnopqrst".getBytes("UTF-8")).flip(); stream.push(buffer); assertEquals(20, stream.available()); final ReadTask read1 = new ReadTask(stream, 10); final ReadTask read2 = new ReadTask(stream, 10); final ReadTask read3 = new ReadTask(stream, 10); final PushTask push = new PushTask(stream, "uvwxyz"); final Thread readThread1 = new Thread(read1, "READ1"); final Thread readThread2 = new Thread(read2, "READ2"); final Thread readThread3 = new Thread(read3, "READ3"); final Thread pushThread = new Thread(push, "PUSH"); readThread1.start(); readThread2.start(); readThread3.start(); Thread.sleep(100); pushThread.start(); readThread1.join(); readThread2.join(); readThread3.join(); pushThread.join(); byte[] readBuffer1 = read1.getReadBuffer(); byte[] readBuffer2 = read2.getReadBuffer(); byte[] readBuffer3 = read3.getReadBuffer(); if (readBuffer1[0] == 'a') { if (readBuffer2[0] == 'k') { assertEquals(10, read1.getReadResult()); assertReadMessage(readBuffer1, "abcdefghij"); assertEquals(10, read2.getReadResult()); assertReadMessage(readBuffer2, "klmnopqrst"); assertEquals(6, read3.getReadResult()); assertReadMessage(readBuffer3, "uvwxyz"); } else if (readBuffer2[0] == 'u') { assertEquals(10, read1.getReadResult()); assertReadMessage(readBuffer1, "abcdefghij"); assertEquals(10, read3.getReadResult()); assertReadMessage(readBuffer3, "klmnopqrst"); assertEquals(6, read2.getReadResult()); assertReadMessage(readBuffer2, "uvwxyz"); } else { fail("Unexpected content for readBuffer2: " + Arrays.toString(readBuffer2)); } } else if (readBuffer1[0] == 'k') { if (readBuffer2[0] == 'a') { assertEquals(10, read2.getReadResult()); assertReadMessage(readBuffer2, "abcdefghij"); assertEquals(10, read1.getReadResult()); assertReadMessage(readBuffer1, "klmnopqrst"); assertEquals(6, read3.getReadResult()); assertReadMessage(readBuffer3, "uvwxyz"); } else if (readBuffer2[0] == 'u') { assertEquals(10, read3.getReadResult()); assertReadMessage(readBuffer3, "abcdefghij"); assertEquals(10, read1.getReadResult()); assertReadMessage(readBuffer1, "klmnopqrst"); assertEquals(6, read2.getReadResult()); assertReadMessage(readBuffer2, "uvwxyz"); } else { fail("Unexpected content for readBuffer2: " + Arrays.toString(readBuffer2)); } } else if (readBuffer1[0] == 'u') { if (readBuffer2[0] == 'a') { assertEquals(10, read2.getReadResult()); assertReadMessage(readBuffer2, "abcdefghij"); assertEquals(10, read3.getReadResult()); assertReadMessage(readBuffer3, "klmnopqrst"); assertEquals(6, read1.getReadResult()); assertReadMessage(readBuffer1, "uvwxyz"); } else if (readBuffer2[0] == 'k') { assertEquals(10, read3.getReadResult()); assertReadMessage(readBuffer3, "abcdefghij"); assertEquals(10, read2.getReadResult()); assertReadMessage(readBuffer2, "klmnopqrst"); assertEquals(6, read1.getReadResult()); assertReadMessage(readBuffer1, "uvwxyz"); } else { fail("Unexpected content for readBuffer2: " + Arrays.toString(readBuffer2)); } } else { fail("Unexpected content for readBuffer1: " + Arrays.toString(readBuffer1)); } assertHandledMessages(false, "abcdefghijklmnopqrst", "uvwxyz"); } @Test public void readBytesFromSimpleBuffer() throws IOException { final ByteBuffer buffer = ByteBuffer.allocate(10); buffer.put("abcdef".getBytes("UTF-8")).flip(); stream.push(buffer); assertEquals(6, stream.available()); assertEquals('a', stream.read()); assertEquals(5, stream.available()); assertEquals('b', stream.read()); assertEquals(4, stream.available()); assertEquals('c', stream.read()); assertEquals(3, stream.available()); assertEquals('d', stream.read()); assertEquals(2, stream.available()); assertEquals('e', stream.read()); assertEquals(1, stream.available()); assertEquals('f', stream.read()); assertEquals(0, stream.available()); assertHandledMessages(false, "abcdef"); } @Test public void readBytesFromPooledByteBuffer() throws IOException { final ByteBufferSlicePool pool = new ByteBufferSlicePool(5, 5); final Pooled<ByteBuffer> pooledBuffer = pool.allocate(); pooledBuffer.getResource().put("ghijk".getBytes("UTF-8")).flip(); stream.push(pooledBuffer); assertEquals(5, stream.available()); assertEquals('g', stream.read()); assertEquals(4, stream.available()); assertEquals('h', stream.read()); assertEquals(3, stream.available()); assertEquals('i', stream.read()); assertEquals(2, stream.available()); assertEquals('j', stream.read()); assertEquals(1, stream.available()); assertEquals('k', stream.read()); assertEquals(0, stream.available()); assertHandledMessages(false, "ghijk"); } @Test public void readBytesFromMultipleBuffers() throws IOException { final ByteBufferSlicePool pool = new ByteBufferSlicePool(5, 5); final Pooled<ByteBuffer> pooledBuffer1 = pool.allocate(); final Pooled<ByteBuffer> pooledBuffer2 = pool.allocate(); final ByteBuffer byteBuffer1 = ByteBuffer.allocate(1); final ByteBuffer byteBuffer2 = ByteBuffer.allocate(2); final ByteBuffer byteBuffer3 = ByteBuffer.allocate(3); pooledBuffer1.getResource().put("multi".getBytes("UTF-8")).flip(); byteBuffer1.put("p".getBytes("UTF-8")).flip(); pooledBuffer2.getResource().put("le bu".getBytes("UTF-8")).flip(); byteBuffer2.put("ff".getBytes("UTF-8")).flip(); byteBuffer3.put("ers".getBytes("UTF-8")).flip(); assertEquals(0, stream.available()); stream.push(pooledBuffer1); assertEquals(5, stream.available()); stream.push(byteBuffer1); assertEquals(6, stream.available()); stream.push(pooledBuffer2); assertEquals(11, stream.available()); stream.push(byteBuffer2); assertEquals(13, stream.available()); stream.push(byteBuffer3); assertEquals(16, stream.available()); assertEquals('m', stream.read()); assertEquals(15, stream.available()); assertEquals('u', stream.read()); assertEquals(14, stream.available()); assertEquals('l', stream.read()); assertEquals(13, stream.available()); assertEquals('t', stream.read()); assertEquals(12, stream.available()); assertEquals('i', stream.read()); assertEquals(11, stream.available()); assertEquals('p', stream.read()); assertEquals(10, stream.available()); assertEquals('l', stream.read()); assertEquals(9, stream.available()); assertEquals('e', stream.read()); assertEquals(8, stream.available()); assertEquals(' ', stream.read()); assertEquals(7, stream.available()); assertEquals('b', stream.read()); assertEquals(6, stream.available()); assertEquals('u', stream.read()); assertEquals(5, stream.available()); assertEquals('f', stream.read()); assertEquals(4, stream.available()); assertEquals('f', stream.read()); assertEquals(3, stream.available()); assertEquals('e', stream.read()); assertEquals(2, stream.available()); assertEquals('r', stream.read()); assertEquals(1, stream.available()); assertEquals('s', stream.read()); assertEquals(0, stream.available()); assertHandledMessages(false, "multi", "p", "le bu", "ff", "ers"); } @Test public void readBytesFromMultipleBuffersMultipleTimes() throws IOException { final ByteBufferSlicePool pool = new ByteBufferSlicePool(5, 5); final Pooled<ByteBuffer> pooledBuffer1 = pool.allocate(); final Pooled<ByteBuffer> pooledBuffer2 = pool.allocate(); final ByteBuffer byteBuffer1 = ByteBuffer.allocate(1); final ByteBuffer byteBuffer2 = ByteBuffer.allocate(2); final ByteBuffer byteBuffer3 = ByteBuffer.allocate(3); byteBuffer1.put("m".getBytes("UTF-8")).flip(); pooledBuffer1.getResource().put("ultip".getBytes("UTF-8")).flip(); pooledBuffer2.getResource().put("le bu".getBytes("UTF-8")).flip(); byteBuffer2.put("ff".getBytes("UTF-8")).flip(); byteBuffer3.put("ers".getBytes("UTF-8")).flip(); assertEquals(0, stream.available()); stream.push(byteBuffer1); assertEquals(1, stream.available()); stream.push(pooledBuffer1); assertEquals(6, stream.available()); stream.push(pooledBuffer2); assertEquals(11, stream.available()); stream.push(byteBuffer2); assertEquals(13, stream.available()); stream.push(byteBuffer3); assertEquals(16, stream.available()); assertEquals('m', stream.read()); assertEquals(15, stream.available()); assertEquals('u', stream.read()); assertEquals(14, stream.available()); assertEquals('l', stream.read()); assertEquals(13, stream.available()); assertEquals('t', stream.read()); assertEquals(12, stream.available()); assertEquals('i', stream.read()); assertEquals(11, stream.available()); assertEquals('p', stream.read()); assertEquals(10, stream.available()); assertEquals('l', stream.read()); assertEquals(9, stream.available()); assertEquals('e', stream.read()); assertEquals(8, stream.available()); assertEquals(' ', stream.read()); assertEquals(7, stream.available()); assertEquals('b', stream.read()); assertEquals(6, stream.available()); assertEquals('u', stream.read()); assertEquals(5, stream.available()); assertEquals('f', stream.read()); assertEquals(4, stream.available()); assertEquals('f', stream.read()); assertEquals(3, stream.available()); assertEquals('e', stream.read()); assertEquals(2, stream.available()); assertEquals('r', stream.read()); assertEquals(1, stream.available()); assertEquals('s', stream.read()); assertEquals(0, stream.available()); assertHandledMessages(false, "m", "ultip", "le bu", "ff", "ers"); } @Test public void readByteReceivesFailure() throws IOException { final IOException exception = new IOException("test"); stream.pushException(exception); IOException failure = null; try { stream.read(); } catch (IOException e) { failure = e; } assertSame(exception, failure); assertCantPush(); assertHandledMessages(false); } @Test public void messageBytesAreNotTruncatedByFailure() throws IOException { final ByteBuffer buffer = ByteBuffer.allocate(20); buffer.put("truncated message".getBytes("UTF-8")).flip(); stream.push(buffer); assertEquals(17, stream.available()); assertEquals('t', stream.read()); assertEquals(16, stream.available()); assertEquals('r', stream.read()); assertEquals(15, stream.available()); assertEquals('u', stream.read()); assertEquals(14, stream.available()); assertEquals('n', stream.read()); assertEquals(13, stream.available()); assertEquals('c', stream.read()); assertEquals(12, stream.available()); assertEquals('a', stream.read()); assertEquals(11, stream.available()); assertEquals('t', stream.read()); assertEquals(10, stream.available()); assertEquals('e', stream.read()); assertEquals(9, stream.available()); assertEquals('d', stream.read()); assertEquals(8, stream.available()); assertEquals(' ', stream.read()); assertEquals(7, stream.available()); final IOException exception = new IOException("test"); stream.pushException(exception); assertEquals('m', stream.read()); assertEquals(6, stream.available()); assertEquals('e', stream.read()); assertEquals(5, stream.available()); assertEquals('s', stream.read()); assertEquals(4, stream.available()); assertEquals('s', stream.read()); assertEquals(3, stream.available()); assertEquals('a', stream.read()); assertEquals(2, stream.available()); assertEquals('g', stream.read()); assertEquals(1, stream.available()); assertEquals('e', stream.read()); assertEquals(0, stream.available()); IOException failure = null; try { stream.read(); } catch (IOException e) { failure = e; } assertSame(exception, failure); assertEquals(0, stream.available()); assertCantPush(); assertHandledMessages(false, "truncated message"); } @Test public void readByteWaitsForPush() throws Exception { final ByteBuffer buffer = ByteBuffer.allocate(5); buffer.put("read".getBytes("UTF-8")).flip(); assertEquals(0, stream.available()); final ReadByteTask read1 = new ReadByteTask(stream); final Thread readThread1 = new Thread(read1); final ReadByteTask read2 = new ReadByteTask(stream); final Thread readThread2 = new Thread(read2); final ReadByteTask read3 = new ReadByteTask(stream); final Thread readThread3 = new Thread(read3); final ReadByteTask read4 = new ReadByteTask(stream); final Thread readThread4 = new Thread(read4); readThread1.start(); readThread1.join(100); assertTrue(readThread1.isAlive()); stream.push(buffer); readThread1.join(); assertEquals(3, stream.available()); readThread2.start(); readThread2.join(); assertEquals(2, stream.available()); readThread3.start(); readThread3.join(); assertEquals(1, stream.available()); readThread4.start(); readThread4.join(); assertEquals(0, stream.available()); assertEquals(0, stream.available()); assertEquals('r', read1.getReadResult()); assertEquals('e', read2.getReadResult()); assertEquals('a', read3.getReadResult()); assertEquals('d', read4.getReadResult()); assertHandledMessages(false, "read"); } @Test public void readByteWaitsForFailure() throws Exception { assertEquals(0, stream.available()); final ReadByteTask read = new ReadByteTask(stream); final Thread readThread = new Thread(read); readThread.start(); readThread.join(100); assertTrue(readThread.isAlive()); final IOException failure = new IOException("test"); stream.pushException(failure); readThread.join(); assertEquals(0, stream.available()); assertEquals(0, read.getReadResult()); assertSame(failure, read.getFailure()); assertCantPush(); assertHandledMessages(false); } @Test public void readByteWaitsForEof() throws Exception { assertEquals(0, stream.available()); final ReadByteTask read = new ReadByteTask(stream); final Thread readThread = new Thread(read); readThread.start(); readThread.join(100); assertTrue(readThread.isAlive()); stream.pushEof(); readThread.join(); assertEquals(0, stream.available()); assertEquals(-1, read.getReadResult()); assertNull(read.getFailure()); assertCantPush(); assertHandledMessages(false); } @Test public void readByteWaitsForClose() throws Exception { assertEquals(0, stream.available()); final ReadByteTask read = new ReadByteTask(stream); final Thread readThread = new Thread(read); readThread.start(); readThread.join(100); assertTrue(readThread.isAlive()); stream.close(); readThread.join(); assertEquals(0, stream.available()); assertEquals(-1, read.getReadResult()); assertNull(read.getFailure()); assertCantPush(); assertHandledMessages(true); } @Test public void readBuffersAndBytes() throws IOException { final ByteBuffer buffer1 = ByteBuffer.allocate(10); buffer1.put("abcdefghij".getBytes("UTF-8")).flip(); final ByteBuffer buffer2 = ByteBuffer.allocate(10); buffer2.put("klmnopqrst".getBytes("UTF-8")).flip(); final byte[] readBuffer1 = new byte[3]; final byte[] readBuffer2 = new byte[5]; final byte[] readBuffer3 = new byte[2]; stream.push(buffer1); stream.push(buffer2); assertEquals(20, stream.available()); assertEquals(3, stream.read(readBuffer1)); assertReadMessage(readBuffer1, "abc"); assertEquals(17, stream.available()); assertEquals('d', stream.read()); assertEquals(16, stream.available()); assertEquals('e', stream.read()); assertEquals(15, stream.available()); assertEquals('f', stream.read()); assertEquals(14, stream.available()); assertEquals('g', stream.read()); assertEquals(13, stream.available()); assertEquals(5, stream.read(readBuffer2)); assertReadMessage(readBuffer2, "hijkl"); assertEquals(8, stream.available()); assertEquals('m', stream.read()); assertEquals(7, stream.available()); assertEquals(2, stream.read(readBuffer3)); assertReadMessage(readBuffer3, "no"); assertEquals(5, stream.available()); assertEquals('p', stream.read()); assertEquals(4, stream.available()); assertEquals('q', stream.read()); assertEquals(3, stream.available()); assertEquals('r', stream.read()); assertEquals(2, stream.available()); assertEquals('s', stream.read()); assertEquals(1, stream.available()); assertEquals('t', stream.read()); assertEquals(0, stream.available()); assertHandledMessages(false, "abcdefghij", "klmnopqrst"); } @Test public void concurrentReadBytes() throws Exception { final ByteBuffer buffer = ByteBuffer.allocate(20); buffer.put("ab".getBytes("UTF-8")).flip(); stream.push(buffer); assertEquals(2, stream.available()); final ReadByteTask read1 = new ReadByteTask(stream); final ReadByteTask read2 = new ReadByteTask(stream); final ReadByteTask read3 = new ReadByteTask(stream); final PushTask push = new PushTask(stream, "cde"); final Thread readThread1 = new Thread(read1, "READ1"); final Thread readThread2 = new Thread(read2, "READ2"); final Thread readThread3 = new Thread(read3, "READ3"); final Thread pushThread = new Thread(push, "PUSH"); readThread1.start(); readThread2.start(); readThread3.start(); Thread.sleep(100); pushThread.start(); readThread1.join(); readThread2.join(); readThread3.join(); pushThread.join(); int readByte1 = read1.getReadResult(); int readByte2= read2.getReadResult(); int readByte3 = read3.getReadResult(); assertTrue("Unexpected values for read results: '" + (char) readByte1 + "', '" + (char) readByte2 + "', and '" + (char) readByte3 + "'", (readByte1 == 'a' && readByte2 == 'b' && readByte3 == 'c') || (readByte1 == 'a' && readByte2 == 'c' && readByte3 == 'b') || (readByte1 == 'b' && readByte2 == 'a' && readByte3 == 'c') || (readByte1 == 'b' && readByte2 == 'c' && readByte3 == 'a') || (readByte1 == 'c' && readByte2 == 'a' && readByte3 == 'b') || (readByte1 == 'c' && readByte2 == 'b' && readByte3 == 'a')); // "cde" is not handled because it was not fully read, only the 'c' char was read from "cde" message assertHandledMessages(false, "ab"); } @Test public void skipMessage() throws IOException { final ByteBuffer buffer = ByteBuffer.allocate(3); buffer.put("abc".getBytes("UTF-8")).flip(); stream.push(buffer); assertEquals(3, stream.available()); assertEquals(3, stream.skip(3)); assertEquals(0, stream.available()); assertHandledMessages(false, "abc"); } @Test public void skipPartOfMessage() throws IOException { final ByteBufferSlicePool bufferPool = new ByteBufferSlicePool(6, 6); final Pooled<ByteBuffer> pooledBuffer1 = bufferPool.allocate(); final Pooled<ByteBuffer> pooledBuffer2 = bufferPool.allocate(); pooledBuffer1.getResource().put("skip ".getBytes("UTF-8")).flip(); pooledBuffer2.getResource().put("msg".getBytes("UTF-8")).flip(); assertEquals(0, stream.available()); stream.push(pooledBuffer1); assertEquals(5, stream.available()); stream.push(pooledBuffer2); assertEquals(8, stream.available()); assertEquals(5, stream.skip(4)); assertEquals(3, stream.available()); final byte[] readBuffer = new byte[6]; assertEquals(3, stream.read(readBuffer)); assertReadMessage(readBuffer, "msg"); assertEquals(0, stream.available()); assertHandledMessages(false, "skip ", "msg"); } @Test public void skipWaitsForFailure() throws Exception { assertEquals(0, stream.available()); final SkipTask skip = new SkipTask(stream, 3); final Thread skipThread = new Thread(skip); skipThread.start(); skipThread.join(100); assertTrue(skipThread.isAlive()); final IOException failure = new IOException("test"); stream.pushException(failure); skipThread.join(); assertEquals(0, stream.available()); assertEquals(0, skip.getSkipResult()); assertSame(failure, skip.getFailure()); assertHandledMessages(false); } @Test public void skipWaitsForPush() throws Exception { final ByteBuffer buffer = ByteBuffer.allocate(5); buffer.put("skip".getBytes("UTF-8")).flip(); assertEquals(0, stream.available()); final SkipTask skip = new SkipTask(stream, 10); final Thread skipThread = new Thread(skip); skipThread.start(); skipThread.join(100); assertTrue(skipThread.isAlive()); stream.push(buffer); skipThread.join(); assertEquals(0, stream.available()); assertEquals(4, skip.getSkipResult()); assertHandledMessages(false, "skip"); } @Test public void skipWaitsForEof() throws Exception { assertEquals(0, stream.available()); final SkipTask skip = new SkipTask(stream, 0); final Thread skipThread = new Thread(skip); skipThread.start(); skipThread.join(100); assertTrue(skipThread.isAlive()); stream.pushEof(); skipThread.join(); assertEquals(0, stream.available()); assertEquals(0, skip.getSkipResult()); assertNull(skip.getFailure()); assertCantPush(); assertHandledMessages(false); } @Test public void skipWaitsForClose() throws Exception { assertEquals(0, stream.available()); final SkipTask skip = new SkipTask(stream, 1); final Thread skipThread = new Thread(skip); skipThread.start(); skipThread.join(100); assertTrue(skipThread.isAlive()); stream.close(); skipThread.join(); assertEquals(0, stream.available()); assertEquals(0, skip.getSkipResult()); assertNull(skip.getFailure()); assertCantPush(); assertHandledMessages(true); } @Test public void messageIsTruncatedByClose() throws IOException { final ByteBuffer buffer = ByteBuffer.allocate(20); buffer.put("truncated message".getBytes("UTF-8")).flip(); stream.push(buffer); byte[] readBuffer = new byte[10]; assertEquals(10, stream.read(readBuffer)); assertReadMessage(readBuffer, "truncated "); stream.close(); assertEquals(-1, stream.read(readBuffer)); assertEquals(-1, stream.read()); assertEquals(0, stream.skip(7)); assertCantPush(); assertHandledMessages(true); } @Test public void messageIsNotTruncatedByEof() throws IOException { final ByteBuffer buffer = ByteBuffer.allocate(20); buffer.put("truncated message".getBytes("UTF-8")).flip(); stream.push(buffer); byte[] readBuffer = new byte[10]; assertEquals(10, stream.read(readBuffer)); assertReadMessage(readBuffer, "truncated "); stream.pushEof(); assertEquals(7, stream.read(readBuffer)); assertReadMessage(readBuffer, "message"); assertEquals(-1, stream.read(readBuffer)); // now we can't push anymore buffer.flip(); stream.push(buffer); assertEquals(0, stream.available()); assertEquals(-1, stream.read()); assertEquals(-1, stream.read(readBuffer)); assertEquals(0, stream.skip(5)); assertCantPush(); // push eof is idempotent buffer.flip(); stream.push(buffer); assertEquals(0, stream.available()); assertEquals(-1, stream.read()); assertEquals(-1, stream.read(readBuffer)); assertEquals(0, stream.skip(10)); assertCantPush(); assertHandledMessages(false, "truncated message"); } @Test public void closeEmptyStream() throws IOException { stream.close(); byte[] readBuffer = new byte[10]; assertEquals(-1, stream.read(readBuffer)); final ByteBuffer buffer = ByteBuffer.allocate(20); buffer.put("can't push".getBytes("UTF-8")).flip(); stream.push(buffer); assertEquals(0, stream.available()); assertEquals(-1, stream.read()); assertEquals(-1, stream.read(readBuffer)); assertEquals(0, stream.skip(11)); assertCantPush(); // close is idempotent stream.close(); assertEquals(-1, stream.read()); assertEquals(-1, stream.read(readBuffer)); assertEquals(0, stream.skip(1000)); assertCantPush(); stream.pushException(new IOException("test")); assertHandledMessages(true); } public static class TestInputHandler implements BufferPipeInputStream.InputHandler { private Collection<String> messages = new ArrayList<String>(); private boolean closed = false; @Override public void acknowledge(Pooled<ByteBuffer> pooled) throws IOException { if (closed) { fail("Stream is closed already"); } pooled.getResource().flip(); messages.add(Buffers.getModifiedUtf8(pooled.getResource())); } @Override public void close() throws IOException { closed = true; } public Iterator<String> getHandledMessages() { return messages.iterator(); } public boolean isClosed() { return closed; } } private void assertHandledMessages(boolean closed, String... messages) { final Iterator<String> handledMessages = handler.getHandledMessages(); for (String message: messages) { try { assertEquals(message, handledMessages.next()); } catch (NoSuchElementException e) { fail("Message " + message + " is not handled"); } } assertFalse("There is one or more unexpected handled messages", handledMessages.hasNext()); assertEquals(closed, handler.isClosed()); } private void assertCantPush() throws IOException, UnsupportedEncodingException { // once a failure has been pushed, we can't push anything else assertEquals(0, stream.available()); final ByteBufferSlicePool bufferPool = new ByteBufferSlicePool(5, 5); final Pooled<ByteBuffer> pooledBuffer = bufferPool.allocate(); pooledBuffer.getResource().put("test".getBytes("UTF-8")).flip(); stream.push(pooledBuffer.getResource()); assertEquals(0, stream.available()); stream.push(pooledBuffer); assertEquals(0, stream.available()); // check pooledBuffer resource is freed IllegalStateException expected = null; try { pooledBuffer.getResource(); } catch (IllegalStateException e) { expected = e; } assertNotNull(expected); } private static class PushTask implements Runnable { private final BufferPipeInputStream stream; private final String message; public PushTask(BufferPipeInputStream s, String m) { stream = s; message = m; } public void run() { final ByteBuffer buffer = ByteBuffer.allocate(message.length()); try { buffer.put(message.getBytes("UTF-8")).flip(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); throw new RuntimeException(e); } stream.push(buffer); } } private static class ReadTask implements Runnable { private final BufferPipeInputStream stream; private final int length; private int readResult; private byte[] readBuffer; private IOException failure; public ReadTask(BufferPipeInputStream s) { this(s, -1); } public ReadTask(BufferPipeInputStream s, int l) { stream = s; length = l; } @Override public void run() { readBuffer = length == -1? new byte[50]: new byte[length]; try { readResult = stream.read(readBuffer); } catch (IOException e) { failure = e; } } public int getReadResult() { return readResult; } public byte[] getReadBuffer() { return readBuffer; } public IOException getFailure() { return failure; } } private static class ReadByteTask implements Runnable { private final BufferPipeInputStream stream; private int readResult; private IOException failure; public ReadByteTask(BufferPipeInputStream s) { stream = s; } @Override public void run() { try { readResult = stream.read(); } catch (IOException e) { failure = e; } } public int getReadResult() { return readResult; } public IOException getFailure() { return failure; } } private static class SkipTask implements Runnable { private final BufferPipeInputStream stream; private final int howManyBytes; private long skipResult; private IOException failure; public SkipTask(BufferPipeInputStream s, int l) { stream = s; howManyBytes = l; } @Override public void run() { try { skipResult = stream.skip(howManyBytes); } catch (IOException e) { failure = e; } } public long getSkipResult() { return skipResult; } public IOException getFailure() { return failure; } } }