2014-11-12 05:14:42 +01:00
|
|
|
/*
|
|
|
|
* Copyright 2014 The Netty Project
|
|
|
|
|
|
|
|
* The Netty Project licenses this file to you 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 io.netty.channel;
|
|
|
|
|
2019-02-15 22:13:17 +01:00
|
|
|
import java.io.IOException;
|
|
|
|
import java.net.InetSocketAddress;
|
2014-11-12 05:14:42 +01:00
|
|
|
import java.net.SocketAddress;
|
2019-02-15 22:13:17 +01:00
|
|
|
import java.nio.channels.ClosedChannelException;
|
2014-11-12 05:14:42 +01:00
|
|
|
|
2019-02-15 22:13:17 +01:00
|
|
|
import io.netty.util.NetUtil;
|
2014-11-12 05:14:42 +01:00
|
|
|
import org.junit.Test;
|
|
|
|
|
2016-02-02 00:07:28 +01:00
|
|
|
import static org.junit.Assert.*;
|
2017-01-09 22:04:18 +01:00
|
|
|
import static org.mockito.Mockito.*;
|
2016-01-14 15:22:57 +01:00
|
|
|
|
2014-11-12 05:14:42 +01:00
|
|
|
public class AbstractChannelTest {
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void ensureInitialRegistrationFiresActive() throws Throwable {
|
2017-01-09 22:04:18 +01:00
|
|
|
EventLoop eventLoop = mock(EventLoop.class);
|
2014-11-12 05:14:42 +01:00
|
|
|
// This allows us to have a single-threaded test
|
2017-01-09 22:04:18 +01:00
|
|
|
when(eventLoop.inEventLoop()).thenReturn(true);
|
2019-01-17 09:17:51 +01:00
|
|
|
when(eventLoop.unsafe()).thenReturn(mock(EventLoop.Unsafe.class));
|
2014-11-12 05:14:42 +01:00
|
|
|
|
2019-01-14 20:11:13 +01:00
|
|
|
TestChannel channel = new TestChannel(eventLoop);
|
2019-03-28 10:28:27 +01:00
|
|
|
ChannelHandler handler = mock(ChannelHandler.class);
|
2014-11-12 05:14:42 +01:00
|
|
|
channel.pipeline().addLast(handler);
|
|
|
|
|
2019-01-14 20:11:13 +01:00
|
|
|
registerChannel(channel);
|
2014-11-12 05:14:42 +01:00
|
|
|
|
2017-01-09 22:04:18 +01:00
|
|
|
verify(handler).handlerAdded(any(ChannelHandlerContext.class));
|
|
|
|
verify(handler).channelRegistered(any(ChannelHandlerContext.class));
|
|
|
|
verify(handler).channelActive(any(ChannelHandlerContext.class));
|
2014-11-12 05:14:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void ensureSubsequentRegistrationDoesNotFireActive() throws Throwable {
|
2017-01-09 22:04:18 +01:00
|
|
|
final EventLoop eventLoop = mock(EventLoop.class);
|
2016-01-14 15:22:57 +01:00
|
|
|
// This allows us to have a single-threaded test
|
2017-01-09 22:04:18 +01:00
|
|
|
when(eventLoop.inEventLoop()).thenReturn(true);
|
2019-01-17 09:17:51 +01:00
|
|
|
when(eventLoop.unsafe()).thenReturn(mock(EventLoop.Unsafe.class));
|
2017-01-09 22:04:18 +01:00
|
|
|
|
2019-01-25 10:51:05 +01:00
|
|
|
doAnswer(invocationOnMock -> {
|
|
|
|
((Runnable) invocationOnMock.getArgument(0)).run();
|
|
|
|
return null;
|
2017-01-09 22:04:18 +01:00
|
|
|
}).when(eventLoop).execute(any(Runnable.class));
|
2016-01-14 15:22:57 +01:00
|
|
|
|
2019-01-14 20:11:13 +01:00
|
|
|
final TestChannel channel = new TestChannel(eventLoop);
|
2019-03-28 10:28:27 +01:00
|
|
|
ChannelHandler handler = mock(ChannelHandler.class);
|
2017-01-09 22:04:18 +01:00
|
|
|
|
2016-01-14 15:22:57 +01:00
|
|
|
channel.pipeline().addLast(handler);
|
|
|
|
|
2019-01-14 20:11:13 +01:00
|
|
|
registerChannel(channel);
|
2016-01-14 15:22:57 +01:00
|
|
|
channel.unsafe().deregister(new DefaultChannelPromise(channel));
|
|
|
|
|
2019-01-14 20:11:13 +01:00
|
|
|
registerChannel(channel);
|
2016-01-14 15:22:57 +01:00
|
|
|
|
2017-01-09 22:04:18 +01:00
|
|
|
verify(handler).handlerAdded(any(ChannelHandlerContext.class));
|
|
|
|
|
|
|
|
// Should register twice
|
|
|
|
verify(handler, times(2)) .channelRegistered(any(ChannelHandlerContext.class));
|
|
|
|
verify(handler).channelActive(any(ChannelHandlerContext.class));
|
|
|
|
verify(handler).channelUnregistered(any(ChannelHandlerContext.class));
|
2016-01-14 15:22:57 +01:00
|
|
|
}
|
2014-11-12 05:14:42 +01:00
|
|
|
|
2016-02-02 00:07:28 +01:00
|
|
|
@Test
|
|
|
|
public void ensureDefaultChannelId() {
|
2019-01-14 20:11:13 +01:00
|
|
|
final EventLoop eventLoop = mock(EventLoop.class);
|
|
|
|
TestChannel channel = new TestChannel(eventLoop);
|
2016-02-02 00:07:28 +01:00
|
|
|
final ChannelId channelId = channel.id();
|
2017-01-09 22:04:18 +01:00
|
|
|
assertTrue(channelId instanceof DefaultChannelId);
|
2016-02-02 00:07:28 +01:00
|
|
|
}
|
|
|
|
|
2019-02-15 22:13:17 +01:00
|
|
|
@Test
|
|
|
|
public void testClosedChannelExceptionCarryIOException() throws Exception {
|
|
|
|
final IOException ioException = new IOException();
|
|
|
|
final EventLoop eventLoop = mock(EventLoop.class);
|
|
|
|
// This allows us to have a single-threaded test
|
|
|
|
when(eventLoop.inEventLoop()).thenReturn(true);
|
|
|
|
when(eventLoop.unsafe()).thenReturn(mock(EventLoop.Unsafe.class));
|
|
|
|
|
|
|
|
doAnswer(invocationOnMock -> {
|
|
|
|
((Runnable) invocationOnMock.getArgument(0)).run();
|
|
|
|
return null;
|
|
|
|
}).when(eventLoop).execute(any(Runnable.class));
|
|
|
|
|
|
|
|
final Channel channel = new TestChannel(eventLoop) {
|
|
|
|
private boolean open = true;
|
|
|
|
private boolean active;
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected AbstractUnsafe newUnsafe() {
|
|
|
|
return new AbstractUnsafe() {
|
|
|
|
@Override
|
|
|
|
public void connect(
|
|
|
|
SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
|
|
|
|
active = true;
|
|
|
|
promise.setSuccess();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void doClose() {
|
|
|
|
active = false;
|
|
|
|
open = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void doWrite(ChannelOutboundBuffer in) throws Exception {
|
|
|
|
throw ioException;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean isOpen() {
|
|
|
|
return open;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean isActive() {
|
|
|
|
return active;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
try {
|
|
|
|
registerChannel(channel);
|
|
|
|
channel.connect(new InetSocketAddress(NetUtil.LOCALHOST, 8888)).sync();
|
|
|
|
assertSame(ioException, channel.writeAndFlush("").await().cause());
|
|
|
|
|
|
|
|
assertClosedChannelException(channel.writeAndFlush(""), ioException);
|
|
|
|
assertClosedChannelException(channel.write(""), ioException);
|
|
|
|
assertClosedChannelException(channel.bind(new InetSocketAddress(NetUtil.LOCALHOST, 8888)), ioException);
|
|
|
|
} finally {
|
|
|
|
channel.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void assertClosedChannelException(ChannelFuture future, IOException expected)
|
|
|
|
throws InterruptedException {
|
|
|
|
Throwable cause = future.await().cause();
|
|
|
|
assertTrue(cause instanceof ClosedChannelException);
|
|
|
|
assertSame(expected, cause.getCause());
|
|
|
|
}
|
|
|
|
|
2019-01-14 20:11:13 +01:00
|
|
|
private static void registerChannel(Channel channel) throws Exception {
|
2014-11-12 05:14:42 +01:00
|
|
|
DefaultChannelPromise future = new DefaultChannelPromise(channel);
|
2019-01-14 20:11:13 +01:00
|
|
|
channel.register(future);
|
2014-11-12 05:14:42 +01:00
|
|
|
future.sync(); // Cause any exceptions to be thrown
|
|
|
|
}
|
|
|
|
|
|
|
|
private static class TestChannel extends AbstractChannel {
|
2015-11-25 19:42:50 +01:00
|
|
|
private static final ChannelMetadata TEST_METADATA = new ChannelMetadata(false);
|
2014-11-12 05:14:42 +01:00
|
|
|
|
2019-02-15 22:13:17 +01:00
|
|
|
private final ChannelConfig config = new DefaultChannelConfig(this);
|
2014-11-12 05:14:42 +01:00
|
|
|
|
2019-01-24 16:24:19 +01:00
|
|
|
TestChannel(EventLoop eventLoop) {
|
2019-01-14 20:11:13 +01:00
|
|
|
super(null, eventLoop);
|
2014-11-12 05:14:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public ChannelConfig config() {
|
2019-02-15 22:13:17 +01:00
|
|
|
return config;
|
2014-11-12 05:14:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean isOpen() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean isActive() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public ChannelMetadata metadata() {
|
2015-11-25 19:42:50 +01:00
|
|
|
return TEST_METADATA;
|
2014-11-12 05:14:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected AbstractUnsafe newUnsafe() {
|
2019-02-15 22:13:17 +01:00
|
|
|
return new AbstractUnsafe() {
|
|
|
|
@Override
|
|
|
|
public void connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
|
|
|
|
promise.setFailure(new UnsupportedOperationException());
|
|
|
|
}
|
|
|
|
};
|
2014-11-12 05:14:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected SocketAddress localAddress0() {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected SocketAddress remoteAddress0() {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2019-02-15 22:13:17 +01:00
|
|
|
protected void doBind(SocketAddress localAddress) { }
|
2014-11-12 05:14:42 +01:00
|
|
|
|
|
|
|
@Override
|
2019-02-15 22:13:17 +01:00
|
|
|
protected void doDisconnect() { }
|
2014-11-12 05:14:42 +01:00
|
|
|
|
|
|
|
@Override
|
2019-02-15 22:13:17 +01:00
|
|
|
protected void doClose() { }
|
2014-11-12 05:14:42 +01:00
|
|
|
|
|
|
|
@Override
|
2019-02-15 22:13:17 +01:00
|
|
|
protected void doBeginRead() { }
|
2014-11-12 05:14:42 +01:00
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void doWrite(ChannelOutboundBuffer in) throws Exception { }
|
|
|
|
}
|
|
|
|
}
|