332a64597e071ef3658c6450da63b7275f99c77f
[ovsdb.git] / library / impl / src / test / java / org / opendaylight / ovsdb / lib / StalePassiveConnectionServiceTest.java
1 /*
2  * Copyright (c) 2018 Ericsson India Global Services Pvt Ltd. 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 package org.opendaylight.ovsdb.lib;
9
10 import static java.util.concurrent.TimeUnit.MILLISECONDS;
11 import static java.util.concurrent.TimeUnit.SECONDS;
12 import static org.junit.Assert.assertEquals;
13 import static org.junit.Assert.assertTrue;
14 import static org.junit.Assert.fail;
15 import static org.mockito.Mockito.mock;
16 import static org.mockito.Mockito.when;
17
18 import com.google.common.collect.Lists;
19 import com.google.common.util.concurrent.Futures;
20 import com.google.common.util.concurrent.SettableFuture;
21
22 import java.util.HashMap;
23 import java.util.Map;
24 import java.util.concurrent.ExecutionException;
25 import java.util.concurrent.Executors;
26 import java.util.concurrent.ScheduledExecutorService;
27 import java.util.concurrent.TimeUnit;
28 import java.util.concurrent.TimeoutException;
29
30 import org.junit.Test;
31 import org.junit.runner.RunWith;
32 import org.mockito.junit.MockitoJUnitRunner;
33 import org.mockito.stubbing.Answer;
34 import org.opendaylight.ovsdb.lib.impl.StalePassiveConnectionService;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 @RunWith(MockitoJUnitRunner.class)
39 public class StalePassiveConnectionServiceTest {
40
41     private static final Logger LOG = LoggerFactory.getLogger(StalePassiveConnectionService.class);
42     private static final String NOTIFIED = "NOTIFIED";
43     private Map<OvsdbClient, SettableFuture<String>> clientJobRunFutures = new HashMap<>();
44     private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
45     private StalePassiveConnectionService staleConnectionService
46             = new StalePassiveConnectionService((client) -> {
47                 if (clientJobRunFutures.get(client) != null) {
48                     clientJobRunFutures.get(client).set(NOTIFIED);
49                 }
50                 return null;
51             });
52
53     private OvsdbClient firstClient = createClient("127.0.0.1", 8001);
54     private OvsdbClient secondClient = createClient("127.0.0.1", 8002);
55     private OvsdbClient thirdClient = createClient("127.0.0.1", 8003);
56
57     @Test
58     public void testFirstClientAlive() throws Exception {
59         staleConnectionService.handleNewPassiveConnection(secondClient, Lists.newArrayList(firstClient));
60         clientShouldNotBeNotified(secondClient, "Second client should not be processed while first client is active");
61         staleConnectionService.clientDisconnected(firstClient);
62         clientShouldBeNotified(secondClient, "Second client should be notified after first client disconnect");
63     }
64
65     @Test
66     public void testSecondClientDisconnect() throws Exception {
67         staleConnectionService.handleNewPassiveConnection(secondClient, Lists.newArrayList(firstClient));
68         clientShouldNotBeNotified(secondClient, "Second client should not be processed while first client is active");
69         staleConnectionService.clientDisconnected(secondClient);
70         clientShouldNotBeNotified(secondClient, "Second client should not be processed post its disconnect");
71         clientShouldBeClearedFromPendingList(secondClient, "Second client should be cleared from pending state");
72     }
73
74     @Test
75     public void testFirstClientInActive() throws Exception {
76         firstClient.echo();
77         when(firstClient.echo()).thenAnswer(delayedEchoFailedResponse(100, MILLISECONDS));
78         staleConnectionService.handleNewPassiveConnection(secondClient, Lists.newArrayList(firstClient));
79         clientShouldBeNotified(secondClient, "Second client should be notified after first client echo failed");
80         clientShouldBeClearedFromPendingList(secondClient, "Second client should be cleared from pending state");
81     }
82
83     @Test
84     public void testThreeClients() throws Exception {
85         staleConnectionService.handleNewPassiveConnection(secondClient, Lists.newArrayList(firstClient));
86         staleConnectionService.handleNewPassiveConnection(thirdClient, Lists.newArrayList(firstClient, secondClient));
87         clientShouldNotBeNotified(thirdClient, "Third client should not be processed while first client is there");
88         clientShouldNotBeNotified(secondClient, "Second client should not be processed while first client is there");
89
90         //disconnect first client
91         staleConnectionService.clientDisconnected(firstClient);
92         //now second client should be processed
93         clientShouldBeNotified(secondClient, "Second client should be notified after first client disconnected");
94         clientShouldNotBeNotified(thirdClient, "Third client should not be processed while second client is active");
95
96         //disconnect second client
97         staleConnectionService.clientDisconnected(secondClient);
98         //now third client should be processed
99         clientShouldBeNotified(secondClient, "Third client should be notified after second client also disconnected");
100     }
101
102     @Test
103     public void testDelayedFirstClientFailure() throws Exception {
104         /*
105         first client arrived
106         second client arrived
107         first client echo success
108         keep second client on wait list
109
110         third client arrived
111         second client echo success, first client echo failed
112         process second client //catch the moment first clients echo failed it is considered inactive
113
114         second client disconnected
115         process third client
116          */
117         staleConnectionService.handleNewPassiveConnection(secondClient, Lists.newArrayList(firstClient));
118
119         when(firstClient.echo()).thenAnswer(delayedEchoFailedResponse(100, MILLISECONDS));
120         staleConnectionService.handleNewPassiveConnection(thirdClient, Lists.newArrayList(firstClient, secondClient));
121         //now second client should be processed
122         clientShouldBeNotified(secondClient, "Second client should be processed post first client echo failed");
123         clientShouldNotBeNotified(thirdClient, "Third client should not be processed while second client is active");
124         //disconnect second client
125         staleConnectionService.clientDisconnected(secondClient);
126         //now third client should be processed
127         clientShouldBeNotified(thirdClient, "Third client should be processed post second client disconnect also");
128     }
129
130     private void clientShouldBeClearedFromPendingList(OvsdbClient client, String msg) {
131         assertTrue(msg, !staleConnectionService.getPendingClients().containsKey(client));
132     }
133
134     private void clientShouldBeNotified(OvsdbClient client, String msg)
135             throws InterruptedException, ExecutionException, TimeoutException {
136         assertEquals(msg, clientJobRunFutures.get(client).get(1, SECONDS), NOTIFIED);
137         clientShouldBeClearedFromPendingList(client, "client should be cleared from pending state");
138     }
139
140     private void clientShouldNotBeNotified(OvsdbClient client, String msg)
141             throws ExecutionException, InterruptedException {
142         try {
143             clientJobRunFutures.get(client).get(1, SECONDS);
144             fail(msg);
145         } catch (TimeoutException e) {
146             LOG.trace("Expected exception");
147         }
148     }
149
150     private Answer<Object> delayedEchoResponse(int delay, TimeUnit timeUnit) {
151         return (mock) -> Futures.scheduleAsync(() -> Futures.immediateFuture(null),
152                 delay, timeUnit, scheduledExecutorService);
153     }
154
155     private Answer<Object> delayedEchoFailedResponse(int delay, TimeUnit timeUnit) {
156         return (mock) -> Futures.scheduleAsync(() -> Futures.immediateFailedFuture(new RuntimeException("Echo failed")),
157                 delay, timeUnit, scheduledExecutorService);
158     }
159
160     private OvsdbClient createClient(String ip, int port) {
161         OvsdbClient ovsdbClient = mock(OvsdbClient.class);
162         clientJobRunFutures.put(ovsdbClient, SettableFuture.create());
163         when(ovsdbClient.echo()).thenAnswer(delayedEchoResponse(100, MILLISECONDS));
164         OvsdbConnectionInfo connectionInfo = mock(OvsdbConnectionInfo.class);
165         when(connectionInfo.toString()).thenReturn("Host:" + ip + ",Port:" + port);
166         when(ovsdbClient.getConnectionInfo()).thenReturn(connectionInfo);
167         return ovsdbClient;
168     }
169 }