2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.controller.cluster.access.client;
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertFalse;
12 import static org.junit.Assert.assertNotNull;
13 import static org.junit.Assert.assertNull;
14 import static org.junit.Assert.assertSame;
15 import static org.junit.Assert.assertTrue;
16 import static org.mockito.ArgumentMatchers.any;
17 import static org.mockito.Mockito.doNothing;
18 import static org.mockito.Mockito.doReturn;
19 import static org.mockito.Mockito.mock;
20 import static org.mockito.Mockito.verify;
21 import static org.mockito.Mockito.verifyNoMoreInteractions;
23 import akka.actor.ActorRef;
24 import akka.actor.ActorSystem;
25 import akka.testkit.TestProbe;
26 import com.google.common.testing.FakeTicker;
27 import java.util.OptionalLong;
28 import java.util.concurrent.ThreadLocalRandom;
29 import java.util.concurrent.TimeUnit;
30 import java.util.function.Consumer;
31 import org.junit.After;
32 import org.junit.AfterClass;
33 import org.junit.Before;
34 import org.junit.BeforeClass;
35 import org.junit.Test;
36 import org.junit.runner.RunWith;
37 import org.mockito.ArgumentCaptor;
38 import org.mockito.Mock;
39 import org.mockito.junit.MockitoJUnitRunner;
40 import org.opendaylight.controller.cluster.access.ABIVersion;
41 import org.opendaylight.controller.cluster.access.concepts.FailureEnvelope;
42 import org.opendaylight.controller.cluster.access.concepts.Request;
43 import org.opendaylight.controller.cluster.access.concepts.RequestEnvelope;
44 import org.opendaylight.controller.cluster.access.concepts.RequestException;
45 import org.opendaylight.controller.cluster.access.concepts.RequestFailure;
46 import org.opendaylight.controller.cluster.access.concepts.Response;
47 import org.opendaylight.controller.cluster.messaging.MessageSlicer;
48 import org.opendaylight.yangtools.concepts.WritableIdentifier;
49 import scala.concurrent.duration.FiniteDuration;
52 * Test suite covering logic contained in {@link ConnectingClientConnection}. It assumes {@link ConnectionEntryTest}
55 @RunWith(MockitoJUnitRunner.class)
56 public class ConnectingClientConnectionTest {
57 private static class MockFailure extends RequestFailure<WritableIdentifier, MockFailure> {
58 private static final long serialVersionUID = 1L;
60 MockFailure(final WritableIdentifier target, final RequestException cause) {
61 super(target, 0, cause);
65 protected SerialForm<WritableIdentifier, MockFailure> externalizableProxy(final ABIVersion version) {
70 protected MockFailure cloneAsVersion(final ABIVersion version) {
75 private static class MockRequest extends Request<WritableIdentifier, MockRequest> {
76 private static final long serialVersionUID = 1L;
78 MockRequest(final WritableIdentifier target, final ActorRef replyTo) {
79 super(target, 0, replyTo);
83 public RequestFailure<WritableIdentifier, ?> toRequestFailure(final RequestException cause) {
84 return new MockFailure(getTarget(), cause);
88 protected Request.SerialForm<WritableIdentifier, MockRequest> externalizableProxy(final ABIVersion version) {
93 protected MockRequest cloneAsVersion(final ABIVersion version) {
99 private ActorRef mockReplyTo;
101 private WritableIdentifier mockIdentifier;
103 private RequestException mockCause;
105 private Consumer<Response<?, ?>> mockCallback;
107 private ClientActorBehavior<?> mockBehavior;
109 private ClientActorContext mockContext;
111 private FakeTicker ticker;
112 private BackendInfo mockBackendInfo;
113 private MockRequest mockRequest;
114 private MockRequest mockRequest2;
115 private RequestFailure<WritableIdentifier, ?> mockResponse;
116 private FailureEnvelope mockResponseEnvelope;
117 private Long mockCookie;
119 private static ActorSystem actorSystem;
120 private TestProbe mockActor;
122 private AbstractClientConnection<?> queue;
125 public static void setupClass() {
126 actorSystem = ActorSystem.apply();
130 public static void teardownClass() {
131 actorSystem.terminate();
135 public void setup() {
136 doNothing().when(mockCallback).accept(any(MockFailure.class));
138 ticker = new FakeTicker();
139 ticker.advance(ThreadLocalRandom.current().nextLong());
140 doReturn(ticker).when(mockContext).ticker();
142 final ClientActorConfig mockConfig = AccessClientUtil.newMockClientActorConfig();
143 doReturn(mockConfig).when(mockContext).config();
145 doReturn(mock(MessageSlicer.class)).when(mockContext).messageSlicer();
147 mockActor = TestProbe.apply(actorSystem);
148 mockBackendInfo = new BackendInfo(mockActor.ref(), "test", 0, ABIVersion.current(), 5);
149 mockRequest = new MockRequest(mockIdentifier, mockReplyTo);
150 mockRequest2 = new MockRequest(mockIdentifier, mockReplyTo);
151 mockResponse = mockRequest.toRequestFailure(mockCause);
152 mockResponseEnvelope = new FailureEnvelope(mockResponse, 0, 0, 0);
153 mockCookie = ThreadLocalRandom.current().nextLong();
155 queue = new ConnectingClientConnection<>(mockContext, mockCookie, mockBackendInfo.getName());
159 public void teardown() {
160 actorSystem.stop(mockActor.ref());
164 public void testCookie() {
165 assertEquals(mockCookie, queue.cookie());
169 public void testPoison() {
170 queue.sendRequest(mockRequest, mockCallback);
171 queue.poison(mockCause);
173 final ArgumentCaptor<MockFailure> captor = ArgumentCaptor.forClass(MockFailure.class);
174 verify(mockCallback).accept(captor.capture());
175 assertSame(mockCause, captor.getValue().getCause());
178 @Test(expected = IllegalStateException.class)
179 public void testPoisonPerformsClose() {
181 queue.poison(mockCause);
184 queue.sendRequest(mockRequest, mockCallback);
188 public void testPoisonIdempotent() {
189 queue.poison(mockCause);
190 queue.poison(mockCause);
194 public void testSendRequestNeedsBackend() {
195 queue.sendRequest(mockRequest, mockCallback);
196 final OptionalLong ret = queue.checkTimeout(ticker.read());
198 assertTrue(ret.isPresent());
202 public void testSetBackendWithNoRequests() {
203 // this utility method covers the entire test
208 public void testSendRequestNeedsTimer() {
211 queue.sendRequest(mockRequest, mockCallback);
212 final OptionalLong ret = queue.checkTimeout(ticker.read());
214 assertTrue(ret.isPresent());
215 assertTransmit(mockRequest, 0);
219 public void testRunTimeoutEmpty() {
220 OptionalLong ret = queue.checkTimeout(ticker.read());
222 assertFalse(ret.isPresent());
226 public void testRunTimeoutWithoutShift() {
227 queue.sendRequest(mockRequest, mockCallback);
228 OptionalLong ret = queue.checkTimeout(ticker.read());
230 assertTrue(ret.isPresent());
234 public void testRunTimeoutWithTimeoutLess() {
235 queue.sendRequest(mockRequest, mockCallback);
237 ticker.advance(AbstractClientConnection.DEFAULT_BACKEND_ALIVE_TIMEOUT_NANOS - 1);
239 OptionalLong ret = queue.checkTimeout(ticker.read());
241 assertTrue(ret.isPresent());
245 public void testRunTimeoutWithTimeoutExact() {
248 queue.sendRequest(mockRequest, mockCallback);
250 ticker.advance(AbstractClientConnection.DEFAULT_BACKEND_ALIVE_TIMEOUT_NANOS);
252 OptionalLong ret = queue.checkTimeout(ticker.read());
257 public void testRunTimeoutWithTimeoutMore() {
260 queue.sendRequest(mockRequest, mockCallback);
262 ticker.advance(AbstractClientConnection.DEFAULT_BACKEND_ALIVE_TIMEOUT_NANOS + 1);
264 assertNull(queue.checkTimeout(ticker.read()));
267 @SuppressWarnings({ "rawtypes", "unchecked" })
268 public void testRunTimeoutWithoutProgressExact() {
269 queue.sendRequest(mockRequest, mockCallback);
271 ticker.advance(AbstractClientConnection.DEFAULT_NO_PROGRESS_TIMEOUT_NANOS);
274 queue.runTimer((ClientActorBehavior) mockBehavior);
275 assertNotNull(queue.poisoned());
278 @SuppressWarnings({ "rawtypes", "unchecked" })
279 public void testRunTimeoutWithoutProgressMore() {
280 queue.sendRequest(mockRequest, mockCallback);
282 ticker.advance(AbstractClientConnection.DEFAULT_NO_PROGRESS_TIMEOUT_NANOS + 1);
285 queue.runTimer((ClientActorBehavior) mockBehavior);
286 assertNotNull(queue.poisoned());
290 public void testRunTimeoutEmptyWithoutProgressExact() {
291 ticker.advance(AbstractClientConnection.DEFAULT_NO_PROGRESS_TIMEOUT_NANOS);
294 assertEquals(OptionalLong.empty(), queue.checkTimeout(ticker.read()));
298 public void testRunTimeoutEmptyWithoutProgressMore() {
299 ticker.advance(AbstractClientConnection.DEFAULT_NO_PROGRESS_TIMEOUT_NANOS + 1);
302 assertEquals(OptionalLong.empty(), queue.checkTimeout(ticker.read()));
306 public void testCompleteEmpty() {
307 queue.receiveResponse(mockResponseEnvelope);
308 verifyNoMoreInteractions(mockCallback);
312 public void testCompleteSingle() {
315 queue.sendRequest(mockRequest, mockCallback);
317 queue.receiveResponse(mockResponseEnvelope);
318 verify(mockCallback).accept(mockResponse);
320 queue.receiveResponse(mockResponseEnvelope);
321 verifyNoMoreInteractions(mockCallback);
325 public void testCompleteNull() {
328 queue.sendRequest(mockRequest, mockCallback);
330 doNothing().when(mockCallback).accept(mockResponse);
332 queue.receiveResponse(mockResponseEnvelope);
333 verify(mockCallback).accept(mockResponse);
337 public void testProgressRecord() {
340 queue.sendRequest(mockRequest, mockCallback);
343 queue.sendRequest(mockRequest2, mockCallback);
344 queue.receiveResponse(mockResponseEnvelope);
346 ticker.advance(AbstractClientConnection.DEFAULT_NO_PROGRESS_TIMEOUT_NANOS - 11);
348 assertNull(queue.checkTimeout(ticker.read()));
351 private void setupBackend() {
352 final ConnectingClientConnection<BackendInfo> connectingConn =
353 new ConnectingClientConnection<>(mockContext, mockCookie, "test");
354 final ConnectedClientConnection<BackendInfo> connectedConn =
355 new ConnectedClientConnection<>(connectingConn, mockBackendInfo);
356 queue.setForwarder(new SimpleReconnectForwarder(connectedConn));
357 queue = connectedConn;
360 private void assertTransmit(final Request<?, ?> expected, final long sequence) {
361 assertTrue(mockActor.msgAvailable());
362 assertRequestEquals(expected, sequence, mockActor.receiveOne(FiniteDuration.apply(5, TimeUnit.SECONDS)));
365 private static void assertRequestEquals(final Request<?, ?> expected, final long sequence, final Object obj) {
366 assertTrue(obj instanceof RequestEnvelope);
368 final RequestEnvelope actual = (RequestEnvelope) obj;
369 assertEquals(0, actual.getSessionId());
370 assertEquals(sequence, actual.getTxSequence());
371 assertSame(expected, actual.getMessage());