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.Matchers.any;
17 import static org.mockito.Mockito.doReturn;
18 import static org.mockito.Mockito.verify;
19 import static org.mockito.Mockito.verifyNoMoreInteractions;
21 import akka.actor.ActorRef;
22 import akka.actor.ActorSystem;
23 import akka.testkit.TestProbe;
24 import java.util.Optional;
25 import java.util.concurrent.CompletableFuture;
26 import java.util.concurrent.ThreadLocalRandom;
27 import java.util.concurrent.TimeUnit;
28 import org.junit.After;
29 import org.junit.AfterClass;
30 import org.junit.Before;
31 import org.junit.BeforeClass;
32 import org.junit.Test;
33 import org.mockito.ArgumentCaptor;
34 import org.mockito.Mock;
35 import org.mockito.MockitoAnnotations;
36 import org.opendaylight.controller.cluster.access.ABIVersion;
37 import org.opendaylight.controller.cluster.access.concepts.AbstractRequestFailureProxy;
38 import org.opendaylight.controller.cluster.access.concepts.AbstractRequestProxy;
39 import org.opendaylight.controller.cluster.access.concepts.FailureEnvelope;
40 import org.opendaylight.controller.cluster.access.concepts.Request;
41 import org.opendaylight.controller.cluster.access.concepts.RequestEnvelope;
42 import org.opendaylight.controller.cluster.access.concepts.RequestException;
43 import org.opendaylight.controller.cluster.access.concepts.RequestFailure;
44 import org.opendaylight.controller.cluster.common.actor.TestTicker;
45 import org.opendaylight.yangtools.concepts.WritableIdentifier;
46 import scala.concurrent.duration.FiniteDuration;
49 * Test suite covering logic contained in {@link SequencedQueue}. It assumes {@link SequencedQueueEntryTest} passes.
51 * @author Robert Varga
53 public class SequencedQueueTest {
54 private static class MockFailure extends RequestFailure<WritableIdentifier, MockFailure> {
55 private static final long serialVersionUID = 1L;
57 MockFailure(final WritableIdentifier target, final RequestException cause) {
58 super(target, 0, cause);
62 protected AbstractRequestFailureProxy<WritableIdentifier, MockFailure> externalizableProxy(
63 final ABIVersion version) {
68 protected MockFailure cloneAsVersion(final ABIVersion version) {
73 private static class MockRequest extends Request<WritableIdentifier, MockRequest> {
74 private static final long serialVersionUID = 1L;
76 MockRequest(final WritableIdentifier target, final ActorRef replyTo) {
77 super(target, 0, replyTo);
81 public RequestFailure<WritableIdentifier, ?> toRequestFailure(final RequestException cause) {
82 return new MockFailure(getTarget(), cause);
86 protected AbstractRequestProxy<WritableIdentifier, MockRequest> externalizableProxy(final ABIVersion version) {
91 protected MockRequest cloneAsVersion(final ABIVersion version) {
97 private ActorRef mockReplyTo;
99 private WritableIdentifier mockIdentifier;
101 private RequestException mockCause;
103 private RequestCallback mockCallback;
105 private ClientActorBehavior mockBehavior;
107 private TestTicker ticker;
108 private BackendInfo mockBackendInfo;
109 private MockRequest mockRequest;
110 private MockRequest mockRequest2;
111 private RequestFailure<WritableIdentifier, ?> mockResponse;
112 private FailureEnvelope mockResponseEnvelope;
113 private Long mockCookie;
115 private static ActorSystem actorSystem;
116 private TestProbe mockActor;
118 private SequencedQueue queue;
121 public static void setupClass() {
122 actorSystem = ActorSystem.apply();
126 public static void teardownClass() {
127 actorSystem.terminate();
131 public void setup() {
132 MockitoAnnotations.initMocks(this);
134 doReturn(mockBehavior).when(mockCallback).complete(any(MockFailure.class));
136 ticker = new TestTicker();
137 ticker.increment(ThreadLocalRandom.current().nextLong());
139 mockActor = TestProbe.apply(actorSystem);
140 mockBackendInfo = new BackendInfo(mockActor.ref(), 0, ABIVersion.current(), 5);
141 mockRequest = new MockRequest(mockIdentifier, mockReplyTo);
142 mockRequest2 = new MockRequest(mockIdentifier, mockReplyTo);
143 mockResponse = mockRequest.toRequestFailure(mockCause);
144 mockResponseEnvelope = new FailureEnvelope(mockResponse, 0, 0);
145 mockCookie = ThreadLocalRandom.current().nextLong();
147 queue = new SequencedQueue(mockCookie, ticker);
151 public void teardown() {
152 actorSystem.stop(mockActor.ref());
156 public void testGetCookie() {
157 assertSame(mockCookie, queue.getCookie());
161 public void testEmptyClose() {
162 assertFalse(queue.hasCompleted());
164 assertTrue(queue.hasCompleted());
167 @Test(expected = IllegalStateException.class)
168 public void testClosedEnqueueRequest() {
172 queue.enqueueRequest(mockRequest, mockCallback);
176 public void testCloseIdempotent() {
182 public void testPoison() {
183 queue.enqueueRequest(mockRequest, mockCallback);
184 queue.poison(mockCause);
186 final ArgumentCaptor<MockFailure> captor = ArgumentCaptor.forClass(MockFailure.class);
187 verify(mockCallback).complete(captor.capture());
188 assertSame(mockCause, captor.getValue().getCause());
191 @Test(expected = IllegalStateException.class)
192 public void testPoisonPerformsClose() {
194 queue.poison(mockCause);
197 queue.enqueueRequest(mockRequest, mockCallback);
201 public void testPoisonIdempotent() {
202 queue.poison(mockCause);
203 queue.poison(mockCause);
207 public void testEnqueueRequestNeedsBackend() {
208 final Optional<FiniteDuration> ret = queue.enqueueRequest(mockRequest, mockCallback);
211 assertFalse(ret.isPresent());
215 public void testExpectProof() {
216 final CompletableFuture<BackendInfo> proof = new CompletableFuture<>();
217 assertTrue(queue.expectProof(proof));
218 assertFalse(queue.expectProof(proof));
221 @Test(expected = NullPointerException.class)
222 public void testSetBackendNull() {
223 final CompletableFuture<BackendInfo> proof = new CompletableFuture<>();
224 assertTrue(queue.expectProof(proof));
225 queue.setBackendInfo(proof, null);
229 public void testSetBackendWithNoResolution() {
230 queue.enqueueRequest(mockRequest, mockCallback);
232 final CompletableFuture<BackendInfo> proof = new CompletableFuture<>();
233 final Optional<FiniteDuration> ret = queue.setBackendInfo(proof, mockBackendInfo);
235 assertFalse(ret.isPresent());
239 public void testSetBackendWithWrongProof() {
240 queue.enqueueRequest(mockRequest, mockCallback);
242 final CompletableFuture<BackendInfo> proof = new CompletableFuture<>();
243 assertTrue(queue.expectProof(proof));
245 final Optional<FiniteDuration> ret = queue.setBackendInfo(new CompletableFuture<>(), mockBackendInfo);
247 assertFalse(ret.isPresent());
251 public void testSetBackendWithNoRequests() {
252 // this utility method covers the entire test
257 public void testSetBackendWithRequestsNoTimer() {
258 queue.enqueueRequest(mockRequest, mockCallback);
260 final CompletableFuture<BackendInfo> proof = new CompletableFuture<>();
261 assertTrue(queue.expectProof(proof));
262 assertFalse(mockActor.msgAvailable());
264 final Optional<FiniteDuration> ret = queue.setBackendInfo(proof, mockBackendInfo);
266 assertTrue(ret.isPresent());
268 assertTransmit(mockRequest, 0);
272 public void testEnqueueRequestNeedsTimer() {
275 final Optional<FiniteDuration> ret = queue.enqueueRequest(mockRequest, mockCallback);
277 assertTrue(ret.isPresent());
278 assertTransmit(mockRequest, 0);
282 public void testEnqueueRequestWithoutTimer() {
286 Optional<FiniteDuration> ret = queue.enqueueRequest(mockRequest, mockCallback);
288 assertTrue(ret.isPresent());
289 assertTransmit(mockRequest, 0);
291 // Second request, no timer fired
292 ret = queue.enqueueRequest(mockRequest2, mockCallback);
294 assertTransmit(mockRequest2, 1);
298 public void testRunTimeoutEmpty() throws NoProgressException {
299 final boolean ret = queue.runTimeout();
304 public void testRunTimeoutWithoutShift() throws NoProgressException {
305 queue.enqueueRequest(mockRequest, mockCallback);
306 final boolean ret = queue.runTimeout();
311 public void testRunTimeoutWithTimeoutLess() throws NoProgressException {
312 queue.enqueueRequest(mockRequest, mockCallback);
314 ticker.increment(SequencedQueue.REQUEST_TIMEOUT_NANOS - 1);
316 final boolean ret = queue.runTimeout();
321 public void testRunTimeoutWithTimeoutExact() throws NoProgressException {
324 queue.enqueueRequest(mockRequest, mockCallback);
326 ticker.increment(SequencedQueue.REQUEST_TIMEOUT_NANOS);
328 final boolean ret = queue.runTimeout();
333 public void testRunTimeoutWithTimeoutMore() throws NoProgressException {
336 queue.enqueueRequest(mockRequest, mockCallback);
338 ticker.increment(SequencedQueue.REQUEST_TIMEOUT_NANOS + 1);
340 final boolean ret = queue.runTimeout();
344 @Test(expected = NoProgressException.class)
345 public void testRunTimeoutWithoutProgressExact() throws NoProgressException {
346 queue.enqueueRequest(mockRequest, mockCallback);
348 ticker.increment(SequencedQueue.NO_PROGRESS_TIMEOUT_NANOS);
354 @Test(expected = NoProgressException.class)
355 public void testRunTimeoutWithoutProgressMore() throws NoProgressException {
356 queue.enqueueRequest(mockRequest, mockCallback);
358 ticker.increment(SequencedQueue.NO_PROGRESS_TIMEOUT_NANOS + 1);
365 public void testRunTimeoutEmptyWithoutProgressExact() throws NoProgressException {
366 ticker.increment(SequencedQueue.NO_PROGRESS_TIMEOUT_NANOS);
369 final boolean ret = queue.runTimeout();
374 public void testRunTimeoutEmptyWithoutProgressMore() throws NoProgressException {
375 ticker.increment(SequencedQueue.NO_PROGRESS_TIMEOUT_NANOS + 1);
378 final boolean ret = queue.runTimeout();
383 public void testCompleteEmpty() {
384 final ClientActorBehavior ret = queue.complete(mockBehavior, mockResponseEnvelope);
385 assertSame(mockBehavior, ret);
386 verifyNoMoreInteractions(mockCallback);
390 public void testCompleteSingle() {
393 queue.enqueueRequest(mockRequest, mockCallback);
395 ClientActorBehavior ret = queue.complete(mockBehavior, mockResponseEnvelope);
396 verify(mockCallback).complete(mockResponse);
397 assertSame(mockBehavior, ret);
399 ret = queue.complete(mockBehavior, mockResponseEnvelope);
400 assertSame(mockBehavior, ret);
401 verifyNoMoreInteractions(mockCallback);
405 public void testCompleteNull() {
408 queue.enqueueRequest(mockRequest, mockCallback);
410 doReturn(null).when(mockCallback).complete(mockResponse);
412 ClientActorBehavior ret = queue.complete(mockBehavior, mockResponseEnvelope);
413 verify(mockCallback).complete(mockResponse);
418 public void testProgressRecord() throws NoProgressException {
421 queue.enqueueRequest(mockRequest, mockCallback);
423 ticker.increment(10);
424 queue.enqueueRequest(mockRequest2, mockCallback);
425 queue.complete(mockBehavior, mockResponseEnvelope);
427 ticker.increment(SequencedQueue.NO_PROGRESS_TIMEOUT_NANOS - 11);
428 assertTrue(queue.runTimeout());
431 private void setupBackend() {
432 final CompletableFuture<BackendInfo> proof = new CompletableFuture<>();
433 assertTrue(queue.expectProof(proof));
434 final Optional<FiniteDuration> ret = queue.setBackendInfo(proof, mockBackendInfo);
436 assertFalse(ret.isPresent());
437 assertFalse(mockActor.msgAvailable());
440 private void assertTransmit(final Request<?, ?> expected, final long sequence) {
441 assertTrue(mockActor.msgAvailable());
442 assertRequestEquals(expected, sequence, mockActor.receiveOne(FiniteDuration.apply(5, TimeUnit.SECONDS)));
445 private static void assertRequestEquals(final Request<?, ?> expected, final long sequence, final Object obj) {
446 assertTrue(obj instanceof RequestEnvelope);
448 final RequestEnvelope actual = (RequestEnvelope) obj;
449 assertEquals(0, actual.getSessionId());
450 assertEquals(sequence, actual.getTxSequence());
451 assertSame(expected, actual.getMessage());