/*
* JBoss, Home of Professional Open Source.
*
* Copyright 2013 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.channels;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.xnio.ChannelListener;
import org.xnio.FileAccess;
import org.xnio.OptionMap;
import org.xnio.Options;
import org.xnio.Xnio;
import org.xnio.XnioIoThread;
import org.xnio.XnioWorker;
import org.xnio.mock.ConnectedStreamChannelMock;
import org.xnio.mock.XnioIoThreadMock;
/**
* Test for {@link EmptySourceStreamChannel}.
*
* @author <a href="mailto:frainone@redhat.com">Flavia Rainone</a>
*/
public class EmptyStreamSourceChannelTestCase {
private EmptyStreamSourceChannel channel;
private XnioIoThreadMock threadMock;
@Before
public void createChannel() throws Exception {
threadMock = new XnioIoThreadMock(null);
threadMock.start();
this.channel = new EmptyStreamSourceChannel(threadMock);
}
@After
public void closeIoThread() {
threadMock.closeIoThread();
}
@SuppressWarnings("deprecation")
@Test
public void getWorkerAndExecutor() throws Exception {
final Xnio xnio = Xnio.getInstance("xnio-mock");
final XnioWorker worker = xnio.createWorker(OptionMap.EMPTY);
final XnioIoThread executor = new XnioIoThreadMock(worker);
this.channel = new EmptyStreamSourceChannel(executor);
assertSame(worker, channel.getWorker());
assertSame(executor, channel.getReadThread());
assertSame(executor, channel.getIoThread());
}
@Test
public void setAndGetOption() throws IOException {
assertNull(channel.getOption(Options.ALLOW_BLOCKING));
assertNull(channel.setOption(Options.ALLOW_BLOCKING, true));
assertNull(channel.getOption(Options.ALLOW_BLOCKING));
assertNull(channel.getOption(Options.FILE_ACCESS));
assertNull(channel.setOption(Options.FILE_ACCESS, FileAccess.READ_ONLY));
assertNull(channel.getOption(Options.FILE_ACCESS));
assertNull(channel.getOption(Options.SECURE));
assertNull(channel.setOption(Options.SECURE, false));
assertNull(channel.getOption(Options.SECURE));
assertNull(channel.getOption(Options.MAX_INBOUND_MESSAGE_SIZE));
assertNull(channel.setOption(Options.MAX_INBOUND_MESSAGE_SIZE, 5000));
assertNull(channel.getOption(Options.MAX_INBOUND_MESSAGE_SIZE));
assertNull(channel.getOption(Options.SEND_BUFFER));
assertNull(channel.setOption(Options.SEND_BUFFER, 100000));
assertNull(channel.getOption(Options.SEND_BUFFER));
assertNull(channel.getOption(Options.WORKER_NAME));
assertNull(channel.setOption(Options.WORKER_NAME, "dummy"));
assertNull(channel.getOption(Options.WORKER_NAME));
assertNull(channel.getOption(Options.WRITE_TIMEOUT));
assertNull(channel.setOption(Options.WRITE_TIMEOUT, 700000));
assertNull(channel.getOption(Options.WRITE_TIMEOUT));
}
@Test
public void supportsOption() throws IOException {
assertFalse(channel.supportsOption(Options.ALLOW_BLOCKING));
assertFalse(channel.supportsOption(Options.FILE_ACCESS));
assertFalse(channel.supportsOption(Options.SECURE));
assertFalse(channel.supportsOption(Options.MAX_INBOUND_MESSAGE_SIZE));
assertFalse(channel.supportsOption(Options.SEND_BUFFER));
assertFalse(channel.supportsOption(Options.WORKER_NAME));
assertFalse(channel.supportsOption(Options.WRITE_TIMEOUT));
}
@Test
public void transferToFileChannel() throws Exception {
final File file = File.createTempFile("test", ".txt");
file.deleteOnExit();
final RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
final FileChannel fileChannel = randomAccessFile.getChannel();
try {
assertEquals(0, channel.transferTo(5, 0, fileChannel));
assertEquals(0, channel.transferTo(0, 0, fileChannel));
assertEquals(0, channel.transferTo(500, 5, fileChannel));
assertEquals(0, channel.transferTo(300, 3, null));
} finally {
fileChannel.close();
randomAccessFile.close();
}
}
@Test
public void transferToStreamSinkChannel() throws Exception {
final ConnectedStreamChannelMock channelMock = new ConnectedStreamChannelMock();
assertEquals(-1, channel.transferTo(50, ByteBuffer.allocate(60), channelMock));
}
@Test
public void simpleRead() throws Exception {
final ReadListener listener = new ReadListener();
channel.getReadSetter().set(listener);
channel.resumeReads();
assertTrue(channel.isReadResumed());
listener.waitInvocation();
assertEquals(-1, listener.getReadToBufferResult());
assertEquals(-1, listener.getReadToBufferArrayResult());
assertEquals(-1, listener.getReadToBufferArrayWithOffsetResult());
listener.clearListenerInvocationData();
// show resume reads is idempotent
assertFalse(listener.isInvoked());
channel.resumeReads();
channel.suspendReads();
assertFalse(channel.isReadResumed());
// suspend reads is idempotent as well
channel.suspendReads();
assertFalse(channel.isReadResumed());
}
@Test
public void resumeReadAfterEmptied() throws Exception {
final ReadListener listener = new ReadListener();
channel.getReadSetter().set(listener);
channel.resumeReads();
assertTrue(channel.isReadResumed());
listener.waitInvocation();
channel.suspendReads();
assertFalse(channel.isReadResumed());
listener.clearListenerInvocationData();
channel.resumeReads();
assertTrue(channel.isReadResumed());
assertFalse(listener.isInvoked());
}
@Test
public void wrappedReadListener() throws Exception {
final ReadListener listener = new ReadListener();
final WrappedReadListener wrappedListener = new WrappedReadListener(listener);
channel.getReadSetter().set(wrappedListener);
channel.resumeReads();
assertTrue(channel.isReadResumed());
listener.waitInvocation();
assertEquals(-1, listener.getReadToBufferResult());
assertEquals(-1, listener.getReadToBufferArrayResult());
assertEquals(-1, listener.getReadToBufferArrayWithOffsetResult());
listener.clearListenerInvocationData();
// show resume reads is idempotent
assertFalse(listener.isInvoked());
channel.resumeReads();
channel.suspendReads();
assertFalse(channel.isReadResumed());
// suspend reads is idempotent as well
channel.suspendReads();
assertFalse(channel.isReadResumed());
}
@Test
public void listenerSuspendReadsOnChannel() throws Exception {
final SuspendReadListener listener = new SuspendReadListener();
channel.getReadSetter().set(listener);
channel.resumeReads();
listener.waitInvocation();
assertFalse(channel.isReadResumed());
}
@Test
public void resumeReadsWithoutListener() {
channel.resumeReads();
}
@Test
public void awaitReadable() throws IOException {
// should return immediately no matter what
channel.awaitReadable();
channel.awaitReadable(500, TimeUnit.DAYS);
channel.resumeReads();
channel.awaitReadable();
channel.awaitReadable(1, TimeUnit.SECONDS);
channel.suspendReads();
channel.awaitReadable();
channel.awaitReadable(30, TimeUnit.MILLISECONDS);
}
@Test
public void shutdownReads() throws Exception {
final EmptyListener listener = new EmptyListener();
channel.getCloseSetter().set(listener);
channel.resumeReads();
channel.shutdownReads();
listener.waitInvocation();
assertFalse(channel.isReadResumed());
assertFalse(channel.isOpen());
// shutdownReads is idempotent
listener.clearInvocationData();
channel.shutdownReads();
assertFalse(listener.isInvoked());
assertFalse(channel.isReadResumed());
assertFalse(channel.isOpen());
}
@Test
public void close() throws Exception {
final EmptyListener listener = new EmptyListener();
channel.getCloseSetter().set(listener);
channel.close();
listener.waitInvocation();
assertFalse(channel.isReadResumed());
assertFalse(channel.isOpen());
// close is idempotent
listener.clearInvocationData();
channel.close();
assertFalse(listener.isInvoked());
assertFalse(channel.isReadResumed());
assertFalse(channel.isOpen());
assertEquals(-1, channel.read(ByteBuffer.allocate(15)));
final File file = File.createTempFile("test", ".txt");
file.deleteOnExit();
final RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
final FileChannel fileChannel = randomAccessFile.getChannel();
try {
assertEquals(0, channel.transferTo(0, 10, fileChannel));
} finally {
fileChannel.close();
randomAccessFile.close();
}
}
@Test
public void listenerClosesChannel() throws Exception {
}
private static class ReadListener implements ChannelListener<StreamSourceChannel> {
private CountDownLatch countDownLatch = new CountDownLatch(1);
private boolean listenerInvoked = false;
private int readToBufferResult = 0;
private long readToBufferArrayResult = 0;
private long readToBufferArrayWithOffsetResult = 0;
@Override
public void handleEvent(StreamSourceChannel channel) {
listenerInvoked = true;
countDownLatch.countDown();
final ByteBuffer[] buffer = new ByteBuffer[] {ByteBuffer.allocate(10)};
try {
readToBufferResult = channel.read(buffer[0]);
readToBufferArrayResult = channel.read(buffer);
readToBufferArrayWithOffsetResult = channel.read(buffer, 0, 1);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
public void waitInvocation() throws InterruptedException {
countDownLatch.await();
}
public boolean isInvoked() {
return listenerInvoked;
}
public int getReadToBufferResult() {
return readToBufferResult;
}
public long getReadToBufferArrayResult() {
return readToBufferArrayResult;
}
public long getReadToBufferArrayWithOffsetResult() {
return readToBufferArrayWithOffsetResult;
}
public void clearListenerInvocationData() {
listenerInvoked = false;
countDownLatch = new CountDownLatch(1);
}
}
private static class SuspendReadListener implements ChannelListener<StreamSourceChannel> {
private final CountDownLatch countDownLatch = new CountDownLatch(1);
@Override
public void handleEvent(StreamSourceChannel channel) {
countDownLatch.countDown();
channel.suspendReads();
}
public void waitInvocation() throws InterruptedException {
countDownLatch.await();
}
}
private static class WrappedReadListener implements ChannelListener<StreamSourceChannel> {
private final ChannelListener<StreamSourceChannel> listener;
public WrappedReadListener(ChannelListener<StreamSourceChannel> listener) {
this.listener = listener;
}
@Override
public void handleEvent(StreamSourceChannel channel) {
channel.getReadSetter().set(listener);
}
}
private static class EmptyListener implements ChannelListener<StreamSourceChannel> {
private CountDownLatch countDownLatch = new CountDownLatch(1);
private boolean invoked = false;
@Override
public void handleEvent(StreamSourceChannel channel) {
countDownLatch.countDown();
invoked = true;
}
public void waitInvocation() throws InterruptedException {
countDownLatch.await();
}
public boolean isInvoked() {
return invoked;
}
public void clearInvocationData() {
invoked = false;
countDownLatch = new CountDownLatch(1);
}
}
}