2015859a110efde3d0afd7931d2ecad6dff3eb06
[openflowplugin.git] / openflowplugin-impl / src / test / java / org / opendaylight / openflowplugin / impl / connection / listener / SystemNotificationsListenerImplTest.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.openflowplugin.impl.connection.listener;
10
11 import static org.mockito.ArgumentMatchers.any;
12
13 import com.google.common.util.concurrent.Futures;
14 import com.google.common.util.concurrent.ListenableFuture;
15 import com.google.common.util.concurrent.SettableFuture;
16 import java.net.InetSocketAddress;
17 import java.util.concurrent.SynchronousQueue;
18 import java.util.concurrent.TimeUnit;
19 import org.junit.After;
20 import org.junit.Before;
21 import org.junit.Test;
22 import org.junit.runner.RunWith;
23 import org.mockito.Mock;
24 import org.mockito.Mockito;
25 import org.mockito.junit.MockitoJUnitRunner;
26 import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter;
27 import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
28 import org.opendaylight.openflowplugin.impl.connection.ConnectionContextImpl;
29 import org.opendaylight.openflowplugin.impl.util.ThreadPoolLoggingExecutor;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.EchoInput;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.EchoOutput;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.EchoOutputBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FeaturesReply;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.system.rev130927.DisconnectEvent;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.system.rev130927.DisconnectEventBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.system.rev130927.SwitchIdleEvent;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.system.rev130927.SwitchIdleEventBuilder;
39 import org.opendaylight.yangtools.yang.common.RpcResult;
40 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
41 import org.opendaylight.yangtools.yang.common.Uint8;
42
43 /**
44  * Testing basic bahavior of {@link SystemNotificationsListenerImpl}.
45  */
46 @RunWith(MockitoJUnitRunner.class)
47 public class SystemNotificationsListenerImplTest {
48
49     private static final int SAFE_TIMEOUT = 1000;
50     private static final int ECHO_REPLY_TIMEOUT = 2000;
51
52     @Mock
53     private ConnectionAdapter connectionAdapter;
54     @Mock
55     private FeaturesReply features;
56
57     private ConnectionContext connectionContext;
58     private ConnectionContextImpl connectionContextGolem;
59     private SystemNotificationsListenerImpl systemNotificationsListener;
60
61     private static final NodeId NODE_ID =
62             new NodeId("OFP:TEST");
63
64     private final ThreadPoolLoggingExecutor threadPool = new ThreadPoolLoggingExecutor(
65             0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<>(), "opfpool");
66
67     @Before
68     public void setUp() {
69         connectionContextGolem = new ConnectionContextImpl(connectionAdapter);
70         connectionContextGolem.changeStateToWorking();
71         connectionContextGolem.setNodeId(NODE_ID);
72         connectionContextGolem.setFeatures(features);
73         connectionContext = Mockito.spy(connectionContextGolem);
74
75         Mockito.when(connectionAdapter.getRemoteAddress()).thenReturn(
76                 InetSocketAddress.createUnresolved("unit-odl.example.org", 4242));
77
78         Mockito.when(features.getAuxiliaryId()).thenReturn(Uint8.ZERO);
79
80         systemNotificationsListener =
81                 new SystemNotificationsListenerImpl(connectionContext, ECHO_REPLY_TIMEOUT, threadPool);
82     }
83
84     @After
85     public void tearDown() {
86         Mockito.verifyNoMoreInteractions(connectionContext);
87     }
88
89     /**
90      * Successful scenario - connection is on and closes without errors.
91      */
92     @Test
93     public void testOnDisconnectEvent1() {
94
95         DisconnectEvent disconnectNotification = new DisconnectEventBuilder().setInfo("testing disconnect").build();
96         systemNotificationsListener.onDisconnectEvent(disconnectNotification);
97
98         verifyCommonInvocationsSubSet();
99         Mockito.verify(connectionContext).onConnectionClosed();
100         Mockito.verify(connectionContext).getConnectionAdapter();
101         Mockito.verify(connectionContext, Mockito.atLeastOnce()).getSafeNodeIdForLOG();
102     }
103
104     /**
105      * Broken scenario - connection is on but fails to close.
106      */
107     @Test
108     public void testOnDisconnectEvent2() {
109
110         DisconnectEvent disconnectNotification = new DisconnectEventBuilder().setInfo("testing disconnect").build();
111         systemNotificationsListener.onDisconnectEvent(disconnectNotification);
112
113         verifyCommonInvocationsSubSet();
114         Mockito.verify(connectionContext).onConnectionClosed();
115         Mockito.verify(connectionContext).getConnectionAdapter();
116         Mockito.verify(connectionContext, Mockito.atLeastOnce()).getSafeNodeIdForLOG();
117     }
118
119     /**
120      * Successful scenario - connection is already down.
121      */
122     @Test
123     public void testOnDisconnectEvent3() {
124         connectionContextGolem.changeStateToTimeouting();
125
126         DisconnectEvent disconnectNotification = new DisconnectEventBuilder().setInfo("testing disconnect").build();
127         systemNotificationsListener.onDisconnectEvent(disconnectNotification);
128
129         verifyCommonInvocationsSubSet();
130         Mockito.verify(connectionContext).onConnectionClosed();
131         Mockito.verify(connectionContext).getConnectionAdapter();
132         Mockito.verify(connectionContext, Mockito.atLeastOnce()).getSafeNodeIdForLOG();
133     }
134
135     /**
136      * Broken scenario - connection is on but throws error on close.
137      */
138     @Test
139     public void testOnDisconnectEvent4() {
140         Mockito.when(connectionContext.getConnectionState()).thenReturn(ConnectionContext.CONNECTION_STATE.RIP);
141
142         DisconnectEvent disconnectNotification = new DisconnectEventBuilder().setInfo("testing disconnect").build();
143         systemNotificationsListener.onDisconnectEvent(disconnectNotification);
144
145         verifyCommonInvocationsSubSet();
146         Mockito.verify(connectionContext).onConnectionClosed();
147         Mockito.verify(connectionContext).getConnectionAdapter();
148         Mockito.verify(connectionContext, Mockito.atLeastOnce()).getSafeNodeIdForLOG();
149     }
150
151     /**
152      * First encounter of idle event, echo received successfully.
153      */
154     @Test
155     public void testOnSwitchIdleEvent1() throws Exception {
156         final ListenableFuture<RpcResult<EchoOutput>> echoReply =
157                 Futures.immediateFuture(RpcResultBuilder.success(new EchoOutputBuilder().setXid(0L).build()).build());
158
159         Mockito.when(connectionAdapter.echo(any(EchoInput.class))).thenReturn(echoReply);
160
161         SwitchIdleEvent notification = new SwitchIdleEventBuilder().setInfo("wake up, device sleeps").build();
162         systemNotificationsListener.onSwitchIdleEvent(notification);
163
164         // make sure that the idle notification processing thread started
165         Thread.sleep(SAFE_TIMEOUT);
166
167         verifyCommonInvocations();
168         Mockito.verify(connectionAdapter, Mockito.timeout(SAFE_TIMEOUT)).echo(any(EchoInput.class));
169         Mockito.verify(connectionAdapter, Mockito.never()).disconnect();
170         Mockito.verify(connectionContext).changeStateToTimeouting();
171         Mockito.verify(connectionContext).changeStateToWorking();
172     }
173
174     /**
175      * First encounter of idle event, echo not receive.
176      */
177     @Test
178     public void testOnSwitchIdleEvent2() throws Exception {
179         final SettableFuture<RpcResult<EchoOutput>> echoReply = SettableFuture.create();
180         Mockito.when(connectionAdapter.echo(any(EchoInput.class))).thenReturn(echoReply);
181         Mockito.when(connectionAdapter.isAlive()).thenReturn(true);
182         Mockito.when(connectionAdapter.disconnect())
183                 .thenReturn(Futures.immediateFailedFuture(new Exception("unit exception")));
184
185         SwitchIdleEvent notification = new SwitchIdleEventBuilder().setInfo("wake up, device sleeps").build();
186         systemNotificationsListener.onSwitchIdleEvent(notification);
187
188         Thread.sleep(SystemNotificationsListenerImpl.MAX_ECHO_REPLY_TIMEOUT + SAFE_TIMEOUT);
189
190         verifyCommonInvocations();
191         Mockito.verify(connectionAdapter, Mockito.timeout(SAFE_TIMEOUT)).echo(any(EchoInput.class));
192         Mockito.verify(connectionAdapter).disconnect();
193         Mockito.verify(connectionContext).changeStateToTimeouting();
194         Mockito.verify(connectionContext).closeConnection(true);
195         Mockito.verify(connectionContext, Mockito.atLeastOnce()).getSafeNodeIdForLOG();
196
197     }
198
199     private void verifyCommonInvocations() {
200         verifyCommonInvocationsSubSet();
201         Mockito.verify(connectionContext, Mockito.timeout(SAFE_TIMEOUT).atLeastOnce()).getFeatures();
202         Mockito.verify(connectionContext, Mockito.timeout(SAFE_TIMEOUT).atLeastOnce()).getConnectionAdapter();
203     }
204
205     private void verifyCommonInvocationsSubSet() {
206         Mockito.verify(connectionContext, Mockito.timeout(SAFE_TIMEOUT).atLeastOnce()).getConnectionState();
207     }
208 }