/* * 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.assertTrue; import static org.xnio.AssertReadWrite.assertWrittenMessage; import java.io.IOException; import java.nio.channels.ClosedChannelException; import java.util.concurrent.TimeUnit; import org.junit.Test; import org.xnio.channels.WriteTimeoutException; import org.xnio.mock.ConnectedStreamChannelMock; /** * Test for {@link ChannelOutputStream}. * * @author <a href="mailto:flavia.rainone@jboss.com">Flavia Rainone</a> */ public class ChannelOutputStreamTestCase extends AbstractChannelStreamTest<ChannelOutputStream> { @Test public void illegalConstructorArguments() { // try with null sinkChannel IllegalArgumentException expected = null; try { new ChannelOutputStream(null); } catch (IllegalArgumentException e) { expected = e; } assertNotNull(expected); expected = null; try { new ChannelOutputStream(null, 10, TimeUnit.SECONDS); } catch (IllegalArgumentException e) { expected = e; } assertNotNull(expected); // try with null timeout unit final ConnectedStreamChannelMock sinkChannel = new ConnectedStreamChannelMock(); expected = null; try { new ChannelOutputStream(sinkChannel, 5, null); } catch (IllegalArgumentException e) { expected = e; } assertNotNull(expected); // try with negative timeout expected = null; try { new ChannelOutputStream(sinkChannel, -1, TimeUnit.MICROSECONDS); } catch (IllegalArgumentException e) { expected = e; } assertNotNull(expected); expected = null; try { new ChannelOutputStream(sinkChannel, -60, TimeUnit.SECONDS); } catch (IllegalArgumentException e) { expected = e; } assertNotNull(expected); } @Test public void writeBytesAndByteArrays() throws IOException { final ConnectedStreamChannelMock channelMock = new ConnectedStreamChannelMock(); final ChannelOutputStream stream = new ChannelOutputStream(channelMock); channelMock.setReadData("data"); channelMock.enableWrite(true); assertWrittenMessage(channelMock); stream.write('d'); assertWrittenMessage(channelMock, "d"); stream.write('a'); assertWrittenMessage(channelMock, "da"); stream.write('t'); assertWrittenMessage(channelMock, "dat"); stream.write('a'); assertWrittenMessage(channelMock, "data"); stream.write(" more data".getBytes("UTF-8"), 1, 9); assertWrittenMessage(channelMock, "data", "more data"); } @Test public void writeByteArraysAndBytes() throws IOException { final ConnectedStreamChannelMock channelMock = new ConnectedStreamChannelMock(); final ChannelOutputStream stream = new ChannelOutputStream(channelMock); channelMock.enableWrite(true); stream.write("data".getBytes("UTF-8")); assertWrittenMessage(channelMock, "data"); stream.write('m'); assertWrittenMessage(channelMock, "data", "m"); stream.write('o'); assertWrittenMessage(channelMock, "data", "mo"); stream.write('r'); assertWrittenMessage(channelMock, "data", "mor"); stream.write('e'); assertWrittenMessage(channelMock, "data", "more"); stream.write(' '); assertWrittenMessage(channelMock, "data", "more "); stream.write('d'); assertWrittenMessage(channelMock, "data", "more d"); stream.write('a'); assertWrittenMessage(channelMock, "data", "more da"); stream.write('t'); assertWrittenMessage(channelMock, "data", "more dat"); stream.write('a'); assertWrittenMessage(channelMock, "data", "more data"); } @Test public void writeBlocks() throws Exception { final ConnectedStreamChannelMock channelMock = new ConnectedStreamChannelMock(); channelMock.enableWrite(false); final ChannelOutputStream stream = new ChannelOutputStream(channelMock); // create and start write thread final WriteByteTask writeByteTask = new WriteByteTask(stream, (byte) 'w'); final Thread writeByteThread = new Thread(writeByteTask); writeByteThread.start(); // thread cant complete writeByteThread.join(200); assertTrue(writeByteThread.isAlive()); // enable write, now thread can complete with a result channelMock.enableWrite(true); writeByteThread.join(); assertWrittenMessage(channelMock, "w"); } @Test public void writeByteArrayBlocks1() throws Exception { final ConnectedStreamChannelMock channelMock = new ConnectedStreamChannelMock(); channelMock.enableWrite(false); final ChannelOutputStream stream = new ChannelOutputStream(channelMock); // create and start write thread final WriteBytesTask writeBytesTask = new WriteBytesTask(stream, "stream write blocks until channel write is enabled".getBytes("UTF-8")); final Thread writeBytesThread = new Thread(writeBytesTask); writeBytesThread.start(); // thread cant complete writeBytesThread.join(200); assertTrue(writeBytesThread.isAlive()); // enable write, now thread can complete with a result channelMock.enableWrite(true); writeBytesThread.join(); assertWrittenMessage(channelMock, "stream write blocks until channel write is enabled"); } @Test public void writeByteArrayBlocks2() throws Exception { final ConnectedStreamChannelMock channelMock = new ConnectedStreamChannelMock(); channelMock.enableWrite(false); final ChannelOutputStream stream = new ChannelOutputStream(channelMock); // create and start write thread final WriteBytesTask writeBytesTask = new WriteBytesTask(stream, "write blocks".getBytes("UTF-8")); final Thread writeBytesThread = new Thread(writeBytesTask); writeBytesThread.start(); // thread cant complete writeBytesThread.join(200); assertTrue(writeBytesThread.isAlive()); // enable write, now thread can complete with a result channelMock.enableWrite(true); writeBytesThread.join(); assertWrittenMessage(channelMock, "write blocks"); } @Test public void flushBlocks() throws Exception { final ConnectedStreamChannelMock channelMock = new ConnectedStreamChannelMock(); channelMock.enableWrite(true); channelMock.enableFlush(false); final ChannelOutputStream stream = new ChannelOutputStream(channelMock); stream.write("flush".getBytes("UTF-8")); assertWrittenMessage(channelMock, "flush"); assertFalse(channelMock.isFlushed()); // try to flush final FlushTask flushTask = new FlushTask(stream); final Thread flushThread = new Thread(flushTask); flushThread.start(); flushThread.join(200); assertTrue(flushThread.isAlive()); assertFalse(channelMock.isFlushed()); channelMock.enableFlush(true); flushThread.join(); assertTrue(channelMock.isFlushed()); assertWrittenMessage(channelMock, "flush"); } @Test public void writeBytesAndByteArraysWithTimeout() throws IOException { final ConnectedStreamChannelMock channelMock = new ConnectedStreamChannelMock(); channelMock.enableWrite(false); final ChannelOutputStream stream = new ChannelOutputStream(channelMock, 0, TimeUnit.MICROSECONDS); assertEquals(0, stream.getWriteTimeout(TimeUnit.MICROSECONDS)); assertEquals(0, stream.getWriteTimeout(TimeUnit.MILLISECONDS)); stream.setWriteTimeout(100, TimeUnit.MILLISECONDS); assertEquals(100, stream.getWriteTimeout(TimeUnit.MILLISECONDS)); // start write test WriteTimeoutException expectedException = null; try { stream.write('a'); } catch (WriteTimeoutException e) { expectedException = e; } assertNotNull(expectedException); channelMock.setReadData("data"); channelMock.enableWrite(true); assertWrittenMessage(channelMock); stream.write('d'); stream.write('a'); stream.write('t'); stream.write('a'); assertWrittenMessage(channelMock, "data"); channelMock.enableWrite(false); expectedException = null; try { stream.write('a'); } catch (WriteTimeoutException e) { expectedException = e; } assertNotNull(expectedException); expectedException = null; try { stream.write("abc".getBytes("UTF-8")); } catch (WriteTimeoutException e) { expectedException = e; } assertNotNull(expectedException); channelMock.enableWrite(true); stream.write("#more data".getBytes("UTF-8"), 1, 9); assertWrittenMessage(channelMock, "data", "more data"); } @Test public void writeByteArraysAndBytesWithTimeout() throws IOException { final ConnectedStreamChannelMock channelMock = new ConnectedStreamChannelMock(); channelMock.enableWrite(false); // try using 10 microseconds, timeout value is not rounded up to 1 millisecond final ChannelOutputStream stream = new ChannelOutputStream(channelMock, 10, TimeUnit.MICROSECONDS); assertEquals(10, stream.getWriteTimeout(TimeUnit.MICROSECONDS)); assertEquals(0, stream.getWriteTimeout(TimeUnit.MILLISECONDS)); stream.setWriteTimeout(200, TimeUnit.MILLISECONDS); assertEquals(200, stream.getWriteTimeout(TimeUnit.MILLISECONDS)); // start write test WriteTimeoutException expectedException = null; try { stream.write("abc".getBytes("UTF-8")); } catch (WriteTimeoutException e) { expectedException = e; } assertNotNull(expectedException); channelMock.setReadData("data"); channelMock.enableWrite(true); final byte[] bytes = "data".getBytes("UTF-8"); stream.write(bytes, 0, -3); assertWrittenMessage(channelMock); stream.write(bytes); assertWrittenMessage(channelMock, "data"); channelMock.enableWrite(false); expectedException = null; try { stream.write('a'); } catch (WriteTimeoutException e) { expectedException = e; } assertNotNull(expectedException); expectedException = null; try { stream.write("abc".getBytes("UTF-8")); } catch (WriteTimeoutException e) { expectedException = e; } assertNotNull(expectedException); channelMock.enableWrite(true); stream.write('m'); assertWrittenMessage(channelMock, "data", "m"); stream.write('o'); assertWrittenMessage(channelMock, "data", "mo"); stream.write('r'); assertWrittenMessage(channelMock, "data", "mor"); stream.write('e'); assertWrittenMessage(channelMock, "data", "more"); } @Test public void writeThrowsException() throws IOException { final ConnectedStreamChannelMock channelMock = new ConnectedStreamChannelMock(); channelMock.close(); // channel mock will always throw ClosedChannelException final ChannelOutputStream stream = new ChannelOutputStream(channelMock); // try to write, test twice to make sure that buffer is kept consistent ClosedChannelException expected = null; try { stream.write('a'); } catch (ClosedChannelException e) { expected = e; } assertNotNull(expected); expected = null; try { stream.write('a'); } catch (ClosedChannelException e) { expected = e; } assertNotNull(expected); // try to write bytes, test twice to make sure that buffer is kept consistent expected = null; try { stream.write("abc".getBytes("UTF-8")); } catch (ClosedChannelException e) { expected = e; } assertNotNull(expected); expected = null; try { stream.write("abc".getBytes("UTF-8")); } catch (ClosedChannelException e) { expected = e; } assertNotNull(expected); } @Test public void close() throws IOException { final ConnectedStreamChannelMock channelMock = new ConnectedStreamChannelMock(); final ChannelOutputStream stream = new ChannelOutputStream(channelMock); channelMock.enableWrite(true); // close! stream.close(); IOException expected = null; try { stream.write('a'); } catch (IOException e) { expected = e; } assertNotNull(expected); expected = null; try { stream.write("abc".getBytes("UTF-8")); } catch (IOException e) { expected = e; } assertNotNull(expected); // close is idempotent stream.close(); expected = null; try { stream.write('a'); } catch (IOException e) { expected = e; } assertNotNull(expected); expected = null; try { stream.write("abc".getBytes("UTF-8")); } catch (IOException e) { expected = e; } assertNotNull(expected); } @Test public void closeStreamWithTimeout() throws IOException { final ConnectedStreamChannelMock channelMock = new ConnectedStreamChannelMock(); final ChannelOutputStream stream = new ChannelOutputStream(channelMock, 15, TimeUnit.MILLISECONDS); channelMock.enableWrite(true); // close! stream.close(); IOException expected = null; try { stream.write('a'); } catch (IOException e) { expected = e; } assertNotNull(expected); expected = null; try { stream.write("abc".getBytes("UTF-8")); } catch (IOException e) { expected = e; } assertNotNull(expected); // close is idempotent stream.close(); expected = null; try { stream.write('a'); } catch (IOException e) { expected = e; } assertNotNull(expected); expected = null; try { stream.write("abc".getBytes("UTF-8")); } catch (IOException e) { expected = e; } assertNotNull(expected); } @Override protected long getOperationTimeout(ChannelOutputStream stream, TimeUnit timeUnit) { return stream.getWriteTimeout(timeUnit); } @Override protected void setOperationTimeout(ChannelOutputStream stream, int timeout, TimeUnit timeUnit) { stream.setWriteTimeout(timeout, timeUnit); } @Override protected ChannelOutputStream createChannelStream(long timeout, TimeUnit timeUnit) { final ConnectedStreamChannelMock channelMock = new ConnectedStreamChannelMock(); return new ChannelOutputStream(channelMock, timeout, timeUnit); } private class WriteByteTask implements Runnable { private final ChannelOutputStream stream; private final byte writeByte; public WriteByteTask(ChannelOutputStream s, byte b) { stream = s; writeByte = b; } @Override public void run() { try { stream.write(writeByte); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(e); } } } private class WriteBytesTask implements Runnable { private final ChannelOutputStream stream; private final byte[] bytes; public WriteBytesTask(ChannelOutputStream s, byte[] b) { stream = s; bytes = b; } @Override public void run() { try { stream.write(bytes); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(e); } } } private class FlushTask implements Runnable { private final ChannelOutputStream stream; public FlushTask(ChannelOutputStream s) { stream = s; } @Override public void run() { try { stream.flush(); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(e); } } } }