/* * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.openflowplugin.impl.connection.listener; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.SettableFuture; import java.net.InetSocketAddress; import java.util.concurrent.Future; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.TimeUnit; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Matchers; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.runners.MockitoJUnitRunner; import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter; import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext; import org.opendaylight.openflowplugin.impl.connection.ConnectionContextImpl; import org.opendaylight.openflowplugin.openflow.md.core.ThreadPoolLoggingExecutor; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.EchoInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.EchoOutput; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.EchoOutputBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FeaturesReply; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.system.rev130927.DisconnectEvent; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.system.rev130927.DisconnectEventBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.system.rev130927.SwitchIdleEvent; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.system.rev130927.SwitchIdleEventBuilder; import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.common.RpcResultBuilder; /** * Testing basic bahavior of {@link SystemNotificationsListenerImpl} */ @RunWith(MockitoJUnitRunner.class) public class SystemNotificationsListenerImplTest { private static final int SAFE_TIMEOUT = 1000; private final static int ECHO_REPLY_TIMEOUT = 2000; @Mock private ConnectionAdapter connectionAdapter; @Mock private FeaturesReply features; private ConnectionContext connectionContext; private ConnectionContextImpl connectionContextGolem; private SystemNotificationsListenerImpl systemNotificationsListener; private static final NodeId nodeId = new NodeId("OFP:TEST"); private final ThreadPoolLoggingExecutor threadPool = new ThreadPoolLoggingExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<>(), "opfpool"); @Before public void setUp() { connectionContextGolem = new ConnectionContextImpl(connectionAdapter); connectionContextGolem.changeStateToWorking(); connectionContextGolem.setNodeId(nodeId); connectionContext = Mockito.spy(connectionContextGolem); Mockito.when(connectionAdapter.getRemoteAddress()).thenReturn( InetSocketAddress.createUnresolved("unit-odl.example.org", 4242)); Mockito.when(features.getAuxiliaryId()).thenReturn((short) 0); Mockito.when(connectionContext.getConnectionAdapter()).thenReturn(connectionAdapter); Mockito.when(connectionContext.getFeatures()).thenReturn(features); systemNotificationsListener = new SystemNotificationsListenerImpl(connectionContext, ECHO_REPLY_TIMEOUT, threadPool); } @After public void tearDown() throws Exception { Mockito.verifyNoMoreInteractions(connectionContext); } /** * successful scenario - connection is on and closes without errors * * @throws Exception */ @Test public void testOnDisconnectEvent1() throws Exception { Mockito.when(connectionAdapter.isAlive()).thenReturn(true); Mockito.when(connectionAdapter.disconnect()).thenReturn(Futures.immediateFuture(Boolean.TRUE)); DisconnectEvent disconnectNotification = new DisconnectEventBuilder().setInfo("testing disconnect").build(); systemNotificationsListener.onDisconnectEvent(disconnectNotification); verifyCommonInvocationsSubSet(); Mockito.verify(connectionContext).onConnectionClosed(); Mockito.verify(connectionContext).getConnectionAdapter(); Mockito.verify(connectionContext, Mockito.atLeastOnce()).getSafeNodeIdForLOG(); } /** * broken scenario - connection is on but fails to close * * @throws Exception */ @Test public void testOnDisconnectEvent2() throws Exception { Mockito.when(connectionAdapter.isAlive()).thenReturn(true); Mockito.when(connectionAdapter.disconnect()).thenReturn(Futures.immediateFuture(Boolean.FALSE)); DisconnectEvent disconnectNotification = new DisconnectEventBuilder().setInfo("testing disconnect").build(); systemNotificationsListener.onDisconnectEvent(disconnectNotification); verifyCommonInvocationsSubSet(); Mockito.verify(connectionContext).onConnectionClosed(); Mockito.verify(connectionContext).getConnectionAdapter(); Mockito.verify(connectionContext, Mockito.atLeastOnce()).getSafeNodeIdForLOG(); } /** * successful scenario - connection is already down * * @throws Exception */ @Test public void testOnDisconnectEvent3() throws Exception { connectionContextGolem.changeStateToTimeouting(); Mockito.when(connectionAdapter.isAlive()).thenReturn(true); Mockito.when(connectionAdapter.disconnect()).thenReturn(Futures.<Boolean>immediateFailedFuture(new Exception("unit exception"))); DisconnectEvent disconnectNotification = new DisconnectEventBuilder().setInfo("testing disconnect").build(); systemNotificationsListener.onDisconnectEvent(disconnectNotification); verifyCommonInvocationsSubSet(); Mockito.verify(connectionContext).onConnectionClosed(); Mockito.verify(connectionContext).getConnectionAdapter(); Mockito.verify(connectionContext, Mockito.atLeastOnce()).getSafeNodeIdForLOG(); } /** * broken scenario - connection is on but throws error on close * * @throws Exception */ @Test public void testOnDisconnectEvent4() throws Exception { Mockito.when(connectionContext.getConnectionState()).thenReturn(ConnectionContext.CONNECTION_STATE.RIP); Mockito.when(connectionAdapter.isAlive()).thenReturn(false); DisconnectEvent disconnectNotification = new DisconnectEventBuilder().setInfo("testing disconnect").build(); systemNotificationsListener.onDisconnectEvent(disconnectNotification); verifyCommonInvocationsSubSet(); Mockito.verify(connectionContext).onConnectionClosed(); Mockito.verify(connectionContext).getConnectionAdapter(); Mockito.verify(connectionContext, Mockito.atLeastOnce()).getSafeNodeIdForLOG(); } /** * first encounter of idle event, echo received successfully * * @throws Exception */ @Test public void testOnSwitchIdleEvent1() throws Exception { final Future<RpcResult<EchoOutput>> echoReply = Futures.immediateFuture(RpcResultBuilder.success(new EchoOutputBuilder().build()).build()); Mockito.when(connectionAdapter.echo(Matchers.any(EchoInput.class))).thenReturn(echoReply); SwitchIdleEvent notification = new SwitchIdleEventBuilder().setInfo("wake up, device sleeps").build(); systemNotificationsListener.onSwitchIdleEvent(notification); // make sure that the idle notification processing thread started Thread.sleep(SAFE_TIMEOUT); verifyCommonInvocations(); Mockito.verify(connectionAdapter, Mockito.timeout(SAFE_TIMEOUT)).echo(Matchers.any(EchoInput.class)); Mockito.verify(connectionAdapter, Mockito.never()).disconnect(); Mockito.verify(connectionContext).changeStateToTimeouting(); Mockito.verify(connectionContext).changeStateToWorking(); } /** * first encounter of idle event, echo not receive * * @throws Exception */ @Test public void testOnSwitchIdleEvent2() throws Exception { final SettableFuture<RpcResult<EchoOutput>> echoReply = SettableFuture.create(); Mockito.when(connectionAdapter.echo(Matchers.any(EchoInput.class))).thenReturn(echoReply); Mockito.when(connectionAdapter.isAlive()).thenReturn(true); Mockito.when(connectionAdapter.disconnect()).thenReturn(Futures.<Boolean>immediateFailedFuture(new Exception("unit exception"))); SwitchIdleEvent notification = new SwitchIdleEventBuilder().setInfo("wake up, device sleeps").build(); systemNotificationsListener.onSwitchIdleEvent(notification); Thread.sleep(SystemNotificationsListenerImpl.MAX_ECHO_REPLY_TIMEOUT + SAFE_TIMEOUT); verifyCommonInvocations(); Mockito.verify(connectionAdapter, Mockito.timeout(SAFE_TIMEOUT)).echo(Matchers.any(EchoInput.class)); Mockito.verify(connectionAdapter).disconnect(); Mockito.verify(connectionContext).changeStateToTimeouting(); Mockito.verify(connectionContext).closeConnection(true); Mockito.verify(connectionContext, Mockito.atLeastOnce()).getSafeNodeIdForLOG(); } private void verifyCommonInvocations() { verifyCommonInvocationsSubSet(); Mockito.verify(connectionContext, Mockito.timeout(SAFE_TIMEOUT).atLeastOnce()).getConnectionAdapter(); } private void verifyCommonInvocationsSubSet() { Mockito.verify(connectionContext, Mockito.timeout(SAFE_TIMEOUT).atLeastOnce()).getConnectionState(); Mockito.verify(connectionContext, Mockito.timeout(SAFE_TIMEOUT).atLeastOnce()).getFeatures(); } }