BUG-3579: device disconnection cleanup
[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 com.google.common.util.concurrent.Futures;
12 import com.google.common.util.concurrent.SettableFuture;
13 import java.net.InetSocketAddress;
14 import org.junit.After;
15 import org.junit.Before;
16 import org.junit.Test;
17 import org.junit.runner.RunWith;
18 import org.mockito.Matchers;
19 import org.mockito.Mock;
20 import org.mockito.Mockito;
21 import org.mockito.runners.MockitoJUnitRunner;
22 import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
23 import org.opendaylight.openflowplugin.impl.connection.ConnectionContextImpl;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.EchoInput;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.EchoOutput;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.EchoOutputBuilder;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FeaturesReply;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.system.rev130927.DisconnectEvent;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.system.rev130927.DisconnectEventBuilder;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.system.rev130927.SwitchIdleEvent;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.system.rev130927.SwitchIdleEventBuilder;
32 import org.opendaylight.yangtools.yang.common.RpcResult;
33 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
34
35 /**
36  * Testing basic bahavior of {@link SystemNotificationsListenerImpl}
37  */
38 @RunWith(MockitoJUnitRunner.class)
39 public class SystemNotificationsListenerImplTest {
40
41     public static final int SAFE_TIMEOUT = 1000;
42     @Mock
43     private org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter connectionAdapter;
44     @Mock
45     private FeaturesReply features;
46     private ConnectionContext connectionContext;
47
48     private SystemNotificationsListenerImpl systemNotificationsListener;
49     private ConnectionContextImpl connectionContextGolem;
50
51     @Before
52     public void setUp() {
53         connectionContextGolem = new ConnectionContextImpl(connectionAdapter);
54         connectionContextGolem.changeStateToWorking();
55
56         Mockito.when(connectionAdapter.getRemoteAddress()).thenReturn(
57                 InetSocketAddress.createUnresolved("unit-odl.example.org", 4242));
58         connectionContext = Mockito.spy(connectionContextGolem);
59         Mockito.when(features.getAuxiliaryId()).thenReturn((short) 0);
60
61         Mockito.when(connectionContext.getConnectionAdapter()).thenReturn(connectionAdapter);
62         Mockito.when(connectionContext.getFeatures()).thenReturn(features);
63
64         systemNotificationsListener = new SystemNotificationsListenerImpl(connectionContext);
65     }
66
67     @After
68     public void tearDown() throws Exception {
69         Mockito.verifyNoMoreInteractions(connectionContext);
70     }
71
72     /**
73      * successful scenario - connection is on and closes without errors
74      *
75      * @throws Exception
76      */
77     @Test
78     public void testOnDisconnectEvent1() throws Exception {
79         Mockito.when(connectionAdapter.isAlive()).thenReturn(true);
80         Mockito.when(connectionAdapter.disconnect()).thenReturn(Futures.immediateFuture(Boolean.TRUE));
81
82         DisconnectEvent disconnectNotification = new DisconnectEventBuilder().setInfo("testing disconnect").build();
83         systemNotificationsListener.onDisconnectEvent(disconnectNotification);
84
85         verifyCommonInvocationsSubSet();
86         Mockito.verify(connectionContext).onConnectionClosed();
87     }
88
89     /**
90      * broken scenario - connection is on but fails to close
91      *
92      * @throws Exception
93      */
94     @Test
95     public void testOnDisconnectEvent2() throws Exception {
96         Mockito.when(connectionAdapter.isAlive()).thenReturn(true);
97         Mockito.when(connectionAdapter.disconnect()).thenReturn(Futures.immediateFuture(Boolean.FALSE));
98
99         DisconnectEvent disconnectNotification = new DisconnectEventBuilder().setInfo("testing disconnect").build();
100         systemNotificationsListener.onDisconnectEvent(disconnectNotification);
101
102         verifyCommonInvocationsSubSet();
103         Mockito.verify(connectionContext).onConnectionClosed();
104     }
105
106     /**
107      * successful scenario - connection is already down
108      *
109      * @throws Exception
110      */
111     @Test
112     public void testOnDisconnectEvent3() throws Exception {
113         connectionContextGolem.changeStateToTimeouting();
114
115         Mockito.when(connectionAdapter.isAlive()).thenReturn(true);
116         Mockito.when(connectionAdapter.disconnect()).thenReturn(Futures.<Boolean>immediateFailedFuture(new Exception("unit exception")));
117
118         DisconnectEvent disconnectNotification = new DisconnectEventBuilder().setInfo("testing disconnect").build();
119         systemNotificationsListener.onDisconnectEvent(disconnectNotification);
120
121         verifyCommonInvocationsSubSet();
122         Mockito.verify(connectionContext).onConnectionClosed();
123     }
124
125     /**
126      * broken scenario - connection is on but throws error on close
127      *
128      * @throws Exception
129      */
130     @Test
131     public void testOnDisconnectEvent4() throws Exception {
132 //    TODO: solve    connectionContextGolem.chansetConnectionState(ConnectionContext.CONNECTION_STATE.RIP);
133
134         Mockito.when(connectionAdapter.isAlive()).thenReturn(false);
135
136         DisconnectEvent disconnectNotification = new DisconnectEventBuilder().setInfo("testing disconnect").build();
137         systemNotificationsListener.onDisconnectEvent(disconnectNotification);
138
139         verifyCommonInvocationsSubSet();
140         Mockito.verify(connectionContext).onConnectionClosed();
141     }
142
143     /**
144      * first encounter of idle event, echo received successfully
145      *
146      * @throws Exception
147      */
148     @Test
149     public void testOnSwitchIdleEvent1() throws Exception {
150         final SettableFuture<RpcResult<EchoOutput>> echoReply = SettableFuture.create();
151         Mockito.when(connectionAdapter.echo(Matchers.any(EchoInput.class))).thenReturn(echoReply);
152
153         SwitchIdleEvent notification = new SwitchIdleEventBuilder().setInfo("wake up, device sleeps").build();
154         systemNotificationsListener.onSwitchIdleEvent(notification);
155
156         // make sure that the idle notification processing thread started
157         Thread.sleep(SAFE_TIMEOUT);
158         EchoOutput echoReplyVal = new EchoOutputBuilder().build();
159         echoReply.set(RpcResultBuilder.success(echoReplyVal).build());
160
161         verifyCommonInvocations();
162         Mockito.verify(connectionAdapter, Mockito.timeout(SAFE_TIMEOUT)).echo(Matchers.any(EchoInput.class));
163         Mockito.verify(connectionAdapter, Mockito.never()).disconnect();
164         Mockito.verify(connectionContext).changeStateToTimeouting();
165         Mockito.verify(connectionContext).changeStateToWorking();
166     }
167
168     /**
169      * first encounter of idle event, echo not receive
170      *
171      * @throws Exception
172      */
173     @Test
174     public void testOnSwitchIdleEvent2() throws Exception {
175         final SettableFuture<RpcResult<EchoOutput>> echoReply = SettableFuture.create();
176         Mockito.when(connectionAdapter.echo(Matchers.any(EchoInput.class))).thenReturn(echoReply);
177         Mockito.when(connectionAdapter.isAlive()).thenReturn(true);
178         Mockito.when(connectionAdapter.disconnect()).thenReturn(Futures.<Boolean>immediateFailedFuture(new Exception("unit exception")));
179
180         SwitchIdleEvent notification = new SwitchIdleEventBuilder().setInfo("wake up, device sleeps").build();
181         systemNotificationsListener.onSwitchIdleEvent(notification);
182
183         Thread.sleep(SystemNotificationsListenerImpl.MAX_ECHO_REPLY_TIMEOUT + SAFE_TIMEOUT);
184
185         verifyCommonInvocations();
186         Mockito.verify(connectionAdapter, Mockito.timeout(SAFE_TIMEOUT)).echo(Matchers.any(EchoInput.class));
187         Mockito.verify(connectionAdapter).disconnect();
188         Mockito.verify(connectionContext).changeStateToTimeouting();
189         Mockito.verify(connectionContext).closeConnection(true);
190     }
191
192     private void verifyCommonInvocations() {
193         verifyCommonInvocationsSubSet();
194         Mockito.verify(connectionContext, Mockito.timeout(SAFE_TIMEOUT).atLeastOnce()).getConnectionAdapter();
195     }
196
197     private void verifyCommonInvocationsSubSet() {
198         Mockito.verify(connectionContext, Mockito.timeout(SAFE_TIMEOUT).atLeastOnce()).getConnectionState();
199         Mockito.verify(connectionContext, Mockito.timeout(SAFE_TIMEOUT).atLeastOnce()).getFeatures();
200     }
201 }