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.datastore.actors.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;
20 import akka.actor.ActorRef;
21 import akka.actor.ActorSystem;
22 import akka.testkit.TestProbe;
23 import java.util.Optional;
24 import java.util.concurrent.CompletableFuture;
25 import java.util.concurrent.ThreadLocalRandom;
26 import java.util.concurrent.TimeUnit;
27 import org.junit.After;
28 import org.junit.AfterClass;
29 import org.junit.Before;
30 import org.junit.BeforeClass;
31 import org.junit.Test;
32 import org.mockito.ArgumentCaptor;
33 import org.mockito.Mock;
34 import org.mockito.MockitoAnnotations;
35 import org.opendaylight.controller.cluster.access.ABIVersion;
36 import org.opendaylight.controller.cluster.access.concepts.AbstractRequestFailureProxy;
37 import org.opendaylight.controller.cluster.access.concepts.AbstractRequestProxy;
38 import org.opendaylight.controller.cluster.access.concepts.Request;
39 import org.opendaylight.controller.cluster.access.concepts.RequestException;
40 import org.opendaylight.controller.cluster.access.concepts.RequestFailure;
41 import org.opendaylight.controller.cluster.access.concepts.Response;
42 import org.opendaylight.controller.cluster.common.actor.TestTicker;
43 import org.opendaylight.yangtools.concepts.WritableIdentifier;
44 import scala.concurrent.duration.FiniteDuration;
47 * Test suite covering logic contained in {@link SequencedQueue}. It assumes {@link SequencedQueueEntryTest} passes.
49 * @author Robert Varga
51 public class SequencedQueueTest {
52 private static class MockFailure extends RequestFailure<WritableIdentifier, MockFailure> {
53 private static final long serialVersionUID = 1L;
55 MockFailure(final WritableIdentifier target, final long sequence, final long retry, final RequestException cause) {
56 super(target, sequence, retry, cause);
60 protected AbstractRequestFailureProxy<WritableIdentifier, MockFailure> externalizableProxy(final ABIVersion version) {
65 protected MockFailure cloneAsVersion(final ABIVersion version) {
70 private static class MockRequest extends Request<WritableIdentifier, MockRequest> {
71 private static final long serialVersionUID = 1L;
73 MockRequest(final WritableIdentifier target, final long sequence, final ActorRef replyTo) {
74 super(target, sequence, 0, replyTo);
78 MockRequest(final MockRequest request, final long retry) {
79 super(request, retry);
83 public RequestFailure<WritableIdentifier, ?> toRequestFailure(final RequestException cause) {
84 return new MockFailure(getTarget(), getSequence(), getRetry(), cause);
88 protected AbstractRequestProxy<WritableIdentifier, MockRequest> externalizableProxy(final ABIVersion version) {
93 protected MockRequest cloneAsVersion(final ABIVersion version) {
98 protected MockRequest cloneAsRetry(final long retry) {
99 return new MockRequest(this, retry);
104 private ActorRef mockReplyTo;
106 private WritableIdentifier mockIdentifier;
108 private RequestException mockCause;
110 private RequestCallback mockCallback;
112 private ClientActorBehavior mockBehavior;
114 private TestTicker ticker;
115 private BackendInfo mockBackendInfo;
116 private MockRequest mockRequest;
117 private MockRequest mockRequest2;
118 private Response<WritableIdentifier, ?> mockResponse;
119 private Long mockCookie;
121 private static ActorSystem actorSystem;
122 private TestProbe mockActor;
124 private SequencedQueue queue;
127 public static void setupClass() {
128 actorSystem = ActorSystem.apply();
132 public static void teardownClass() {
133 actorSystem.terminate();
137 public void setup() {
138 MockitoAnnotations.initMocks(this);
140 doReturn(mockBehavior).when(mockCallback).complete(any(MockFailure.class));
142 ticker = new TestTicker();
143 ticker.increment(ThreadLocalRandom.current().nextLong());
145 mockActor = TestProbe.apply(actorSystem);
146 mockBackendInfo = new BackendInfo(mockActor.ref(), ABIVersion.current());
147 mockRequest = new MockRequest(mockIdentifier, ThreadLocalRandom.current().nextLong(), mockReplyTo);
148 mockRequest2 = new MockRequest(mockIdentifier, mockRequest.getSequence() + 1, mockReplyTo);
149 mockResponse = mockRequest.toRequestFailure(mockCause);
150 mockCookie = ThreadLocalRandom.current().nextLong();
152 queue = new SequencedQueue(mockCookie, ticker);
156 public void teardown() {
157 actorSystem.stop(mockActor.ref());
161 public void testGetCookie() {
162 assertSame(mockCookie, queue.getCookie());
166 public void testEmptyClose() {
167 assertFalse(queue.hasCompleted());
169 assertTrue(queue.hasCompleted());
172 @Test(expected=IllegalStateException.class)
173 public void testClosedEnqueueRequest() {
177 queue.enqueueRequest(mockRequest, mockCallback);
181 public void testCloseIdempotent() {
187 public void testPoison() {
188 queue.enqueueRequest(mockRequest, mockCallback);
189 queue.poison(mockCause);
191 final ArgumentCaptor<MockFailure> captor = ArgumentCaptor.forClass(MockFailure.class);
192 verify(mockCallback).complete(captor.capture());
193 assertSame(mockCause, captor.getValue().getCause());
196 @Test(expected=IllegalStateException.class)
197 public void testPoisonPerformsClose() {
199 queue.poison(mockCause);
202 queue.enqueueRequest(mockRequest, mockCallback);
206 public void testPoisonIdempotent() {
207 queue.poison(mockCause);
208 queue.poison(mockCause);
212 public void testEnqueueRequestNeedsBackend() {
213 final Optional<FiniteDuration> ret = queue.enqueueRequest(mockRequest, mockCallback);
216 assertFalse(ret.isPresent());
220 public void testExpectProof() {
221 final CompletableFuture<BackendInfo> proof = new CompletableFuture<>();
222 assertTrue(queue.expectProof(proof));
223 assertFalse(queue.expectProof(proof));
226 @Test(expected=NullPointerException.class)
227 public void testSetBackendNull() {
228 final CompletableFuture<BackendInfo> proof = new CompletableFuture<>();
229 assertTrue(queue.expectProof(proof));
230 queue.setBackendInfo(proof, null);
234 public void testSetBackendWithNoResolution() {
235 queue.enqueueRequest(mockRequest, mockCallback);
237 final CompletableFuture<BackendInfo> proof = new CompletableFuture<>();
238 final Optional<FiniteDuration> ret = queue.setBackendInfo(proof, mockBackendInfo);
240 assertFalse(ret.isPresent());
244 public void testSetBackendWithWrongProof() {
245 queue.enqueueRequest(mockRequest, mockCallback);
247 final CompletableFuture<BackendInfo> proof = new CompletableFuture<>();
248 assertTrue(queue.expectProof(proof));
250 final Optional<FiniteDuration> ret = queue.setBackendInfo(new CompletableFuture<>(), mockBackendInfo);
252 assertFalse(ret.isPresent());
256 public void testSetBackendWithNoRequests() {
257 // this utility method covers the entire test
262 public void testSetbackedWithRequestsNoTimer() {
263 queue.enqueueRequest(mockRequest, mockCallback);
265 final CompletableFuture<BackendInfo> proof = new CompletableFuture<>();
266 assertTrue(queue.expectProof(proof));
267 assertFalse(mockActor.msgAvailable());
269 final Optional<FiniteDuration> ret = queue.setBackendInfo(proof, mockBackendInfo);
271 assertTrue(ret.isPresent());
273 assertTransmit(mockRequest);
277 public void testEnqueueRequestNeedsTimer() {
280 final Optional<FiniteDuration> ret = queue.enqueueRequest(mockRequest, mockCallback);
282 assertTrue(ret.isPresent());
283 assertTransmit(mockRequest);
287 public void testEnqueueRequestWithoutTimer() {
291 Optional<FiniteDuration> ret = queue.enqueueRequest(mockRequest, mockCallback);
293 assertTrue(ret.isPresent());
294 assertTransmit(mockRequest);
296 // Second request, no timer fired
297 ret = queue.enqueueRequest(mockRequest2, mockCallback);
299 assertTransmit(mockRequest2);
303 public void testRunTimeoutEmpty() throws NoProgressException {
304 final boolean ret = queue.runTimeout();
309 public void testRunTimeoutWithoutShift() throws NoProgressException {
310 queue.enqueueRequest(mockRequest, mockCallback);
311 final boolean ret = queue.runTimeout();
316 public void testRunTimeoutWithTimeoutLess() throws NoProgressException {
317 queue.enqueueRequest(mockRequest, mockCallback);
319 ticker.increment(SequencedQueue.REQUEST_TIMEOUT_NANOS - 1);
321 final boolean ret = queue.runTimeout();
326 public void testRunTimeoutWithTimeoutExact() throws NoProgressException {
327 queue.enqueueRequest(mockRequest, mockCallback);
329 ticker.increment(SequencedQueue.REQUEST_TIMEOUT_NANOS);
331 final boolean ret = queue.runTimeout();
336 public void testRunTimeoutWithTimeoutMore() throws NoProgressException {
337 queue.enqueueRequest(mockRequest, mockCallback);
339 ticker.increment(SequencedQueue.REQUEST_TIMEOUT_NANOS + 1);
341 final boolean ret = queue.runTimeout();
345 @Test(expected=NoProgressException.class)
346 public void testRunTimeoutWithoutProgressExact() throws NoProgressException {
347 queue.enqueueRequest(mockRequest, mockCallback);
349 ticker.increment(SequencedQueue.NO_PROGRESS_TIMEOUT_NANOS);
355 @Test(expected=NoProgressException.class)
356 public void testRunTimeoutWithoutProgressMore() throws NoProgressException {
357 queue.enqueueRequest(mockRequest, mockCallback);
359 ticker.increment(SequencedQueue.NO_PROGRESS_TIMEOUT_NANOS + 1);
366 public void testRunTimeoutEmptyWithoutProgressExact() throws NoProgressException {
367 ticker.increment(SequencedQueue.NO_PROGRESS_TIMEOUT_NANOS);
370 final boolean ret = queue.runTimeout();
375 public void testRunTimeoutEmptyWithoutProgressMore() throws NoProgressException {
376 ticker.increment(SequencedQueue.NO_PROGRESS_TIMEOUT_NANOS + 1);
379 final boolean ret = queue.runTimeout();
384 public void testCompleteEmpty() {
385 final ClientActorBehavior ret = queue.complete(mockBehavior, mockResponse);
386 assertSame(mockBehavior, ret);
387 verifyNoMoreInteractions(mockCallback);
391 public void testCompleteSingle() {
392 queue.enqueueRequest(mockRequest, mockCallback);
394 ClientActorBehavior ret = queue.complete(mockBehavior, mockResponse);
395 verify(mockCallback).complete(mockResponse);
396 assertSame(mockBehavior, ret);
398 ret = queue.complete(mockBehavior, mockResponse);
399 assertSame(mockBehavior, ret);
400 verifyNoMoreInteractions(mockCallback);
404 public void testCompleteNull() {
405 queue.enqueueRequest(mockRequest, mockCallback);
407 doReturn(null).when(mockCallback).complete(mockResponse);
409 ClientActorBehavior ret = queue.complete(mockBehavior, mockResponse);
410 verify(mockCallback).complete(mockResponse);
415 public void testProgressRecord() throws NoProgressException {
418 queue.enqueueRequest(mockRequest, mockCallback);
420 ticker.increment(10);
421 queue.enqueueRequest(mockRequest2, mockCallback);
422 queue.complete(mockBehavior, mockResponse);
424 ticker.increment(SequencedQueue.NO_PROGRESS_TIMEOUT_NANOS - 11);
425 assertTrue(queue.runTimeout());
428 private void setupBackend() {
429 final CompletableFuture<BackendInfo> proof = new CompletableFuture<>();
430 assertTrue(queue.expectProof(proof));
431 final Optional<FiniteDuration> ret = queue.setBackendInfo(proof, mockBackendInfo);
433 assertFalse(ret.isPresent());
434 assertFalse(mockActor.msgAvailable());
437 private void assertTransmit(final Request<?, ?> expected) {
438 assertTrue(mockActor.msgAvailable());
439 assertRequestEquals(expected, mockActor.receiveOne(FiniteDuration.apply(5, TimeUnit.SECONDS)));
442 private static void assertRequestEquals(final Request<?, ?> expected, final Object o) {
443 final Request<?, ?> actual = (Request<?, ?>) o;
444 assertEquals(expected.getRetry(), actual.getRetry());
445 assertEquals(expected.getSequence(), actual.getSequence());
446 assertEquals(expected.getTarget(), actual.getTarget());