/*
* TeleStax, Open Source Cloud Communications
* Copyright 2011-2017, Telestax Inc and individual contributors
* by the @authors tag.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.restcomm.media.network.netty;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.after;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import com.google.common.util.concurrent.FutureCallback;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelPromise;
import io.netty.channel.DefaultChannelProgressivePromise;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.util.concurrent.DefaultEventExecutor;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.FailedFuture;
import io.netty.util.concurrent.Future;
/**
* @author Henrique Rosa (henrique.rosa@telestax.com)
*
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest({Bootstrap.class})
public class NettyNetworkManagerTest {
private EventExecutor eventExecutor;
private EventLoopGroup eventLoopGroup;
@After
public void cleanup() {
if (this.eventLoopGroup != null) {
if (!this.eventLoopGroup.isShutdown()) {
this.eventLoopGroup.shutdownGracefully(0L, 0L, TimeUnit.MILLISECONDS);
}
this.eventLoopGroup = null;
}
if (this.eventExecutor != null) {
if (!this.eventExecutor.isShutdown()) {
this.eventExecutor.shutdownGracefully(0L, 0L, TimeUnit.MILLISECONDS);
}
this.eventExecutor = null;
}
}
@Test
public void testCloseSync() throws Exception {
// given
this.eventLoopGroup = new NioEventLoopGroup(1);
final Bootstrap bootstrap = PowerMockito.mock(Bootstrap.class);
PowerMockito.when(bootstrap.group()).thenReturn(eventLoopGroup);
final NettyNetworkManager networkManager = new NettyNetworkManager(bootstrap);
// when
networkManager.close();
// then
assertTrue(eventLoopGroup.isShutdown());
}
@Test(expected = IOException.class)
public void testCloseSyncFailure() throws Exception {
// given
final EventLoopGroup eventLoopGroup = mock(EventLoopGroup.class);
final Bootstrap bootstrap = PowerMockito.mock(Bootstrap.class);
PowerMockito.when(bootstrap.group()).thenReturn(eventLoopGroup);
final NettyNetworkManager networkManager = new NettyNetworkManager(bootstrap);
// when
when(eventLoopGroup.shutdownGracefully(any(Long.class), any(Long.class), any(TimeUnit.class))).thenThrow(new RuntimeException("Testing purposes!"));
networkManager.close();
}
@SuppressWarnings("unchecked")
@Test
public void testCloseAsync() {
// given
this.eventLoopGroup = new NioEventLoopGroup(1);
final Bootstrap bootstrap = PowerMockito.mock(Bootstrap.class);
PowerMockito.when(bootstrap.group()).thenReturn(eventLoopGroup);
final FutureCallback<Void> callback = mock(FutureCallback.class);
final NettyNetworkManager networkManager = new NettyNetworkManager(bootstrap);
// when
networkManager.close(callback);
// then
verify(callback, timeout((NettyNetworkManager.SHUTDOWN_TIMEOUT + 1) * 1000)).onSuccess(null);
assertTrue(eventLoopGroup.isShutdown());
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Test
public void testCloseAsyncFailure() {
// given
final EventLoopGroup eventLoopGroup = mock(EventLoopGroup.class);
final Bootstrap bootstrap = PowerMockito.mock(Bootstrap.class);
PowerMockito.when(bootstrap.group()).thenReturn(eventLoopGroup);
final NettyNetworkManager networkManager = new NettyNetworkManager(bootstrap);
final FutureCallback<Void> callback = mock(FutureCallback.class);
final Exception exception = new RuntimeException("Testing purposes!");
this.eventExecutor = new DefaultEventExecutor();
final Future shutdownFuture = new FailedFuture<>(this.eventExecutor, exception);
// when
when(eventLoopGroup.shutdownGracefully(any(Long.class), any(Long.class), any(TimeUnit.class))).thenReturn(shutdownFuture);
networkManager.close(callback);
// then
verify(callback, timeout(100)).onFailure(exception);
}
@Test
public void testOpenChannelSync() throws Exception {
// given
this.eventLoopGroup = new NioEventLoopGroup(1);
final ChannelHandler channelHandler = mock(ChannelHandler.class);
final Bootstrap bootstrap = new Bootstrap().group(eventLoopGroup).channel(NioDatagramChannel.class).handler(channelHandler);
try (final NettyNetworkManager networkManager = new NettyNetworkManager(bootstrap)) {
// when
final Channel channel = networkManager.openChannel();
// then
assertTrue(channel.isOpen());
assertTrue(channel.isRegistered());
assertFalse(channel.isActive());
}
}
@Test(expected = IOException.class)
public void testOpenChannelSyncFailure() throws Exception {
// given
this.eventLoopGroup = new NioEventLoopGroup(1);
final Bootstrap bootstrap = PowerMockito.mock(Bootstrap.class);
PowerMockito.when(bootstrap.group()).thenReturn(eventLoopGroup);
final Exception exception = new RuntimeException("Testing purposes!");
try (final NettyNetworkManager networkManager = new NettyNetworkManager(bootstrap)) {
// when
when(bootstrap.clone()).thenReturn(bootstrap);
when(bootstrap.register()).thenThrow(exception);
networkManager.openChannel();
}
}
@SuppressWarnings({ "unchecked" })
@Test
public void testOpenChannelAsync() throws Exception {
// given
this.eventLoopGroup = new NioEventLoopGroup(1);
final ChannelHandler channelHandler = mock(ChannelHandler.class);
final Bootstrap bootstrap = new Bootstrap().group(eventLoopGroup).channel(NioDatagramChannel.class).handler(channelHandler);
this.eventExecutor = new DefaultEventExecutor();
final FutureCallback<Channel> callback = mock(FutureCallback.class);
final ArgumentCaptor<Channel> captor = ArgumentCaptor.forClass(Channel.class);
try (final NettyNetworkManager networkManager = new NettyNetworkManager(bootstrap)) {
// when
networkManager.openChannel(callback);
// then
verify(callback, timeout(100)).onSuccess(captor.capture());
assertTrue(captor.getValue().isOpen());
assertTrue(captor.getValue().isRegistered());
assertFalse(captor.getValue().isActive());
}
}
@SuppressWarnings("unchecked")
@Test
public void testOpenChannelAsyncFailure() throws Exception {
// given
this.eventLoopGroup = new NioEventLoopGroup(1);
final Bootstrap bootstrap = PowerMockito.mock(Bootstrap.class);
PowerMockito.when(bootstrap.group()).thenReturn(eventLoopGroup);
this.eventExecutor = new DefaultEventExecutor();
final FutureCallback<Channel> callback = mock(FutureCallback.class);
final Channel channel = mock(Channel.class);
final ChannelPromise promise = new DefaultChannelProgressivePromise(channel, eventExecutor);
final Exception exception = new RuntimeException("Testing purposes!");
try (final NettyNetworkManager networkManager = new NettyNetworkManager(bootstrap)) {
// when
when(bootstrap.clone()).thenReturn(bootstrap);
when(bootstrap.register()).thenReturn(promise);
promise.setFailure(exception);
networkManager.openChannel(callback);
// then
verify(callback, timeout(100)).onFailure(exception);
}
}
}