2 * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.databroker.actors.dds;
10 import static org.mockito.ArgumentMatchers.any;
11 import static org.mockito.Mockito.mock;
12 import static org.mockito.Mockito.when;
13 import static org.opendaylight.controller.cluster.databroker.actors.dds.TestUtils.CLIENT_ID;
14 import static org.opendaylight.controller.cluster.databroker.actors.dds.TestUtils.HISTORY_ID;
15 import static org.opendaylight.controller.cluster.databroker.actors.dds.TestUtils.TRANSACTION_ID;
17 import akka.actor.ActorRef;
18 import akka.actor.ActorSelection;
19 import akka.actor.ActorSystem;
20 import akka.testkit.TestProbe;
21 import akka.testkit.javadsl.TestKit;
22 import java.util.Collection;
23 import java.util.Collections;
24 import org.junit.After;
25 import org.junit.Assert;
26 import org.junit.Before;
27 import org.junit.Test;
28 import org.mockito.Mock;
29 import org.mockito.MockitoAnnotations;
30 import org.opendaylight.controller.cluster.access.client.AbstractClientConnection;
31 import org.opendaylight.controller.cluster.access.client.AccessClientUtil;
32 import org.opendaylight.controller.cluster.access.client.ClientActorContext;
33 import org.opendaylight.controller.cluster.access.client.InternalCommand;
34 import org.opendaylight.controller.cluster.access.commands.AbortLocalTransactionRequest;
35 import org.opendaylight.controller.cluster.access.commands.ConnectClientRequest;
36 import org.opendaylight.controller.cluster.access.commands.ConnectClientSuccess;
37 import org.opendaylight.controller.cluster.access.concepts.Envelope;
38 import org.opendaylight.controller.cluster.access.concepts.FailureEnvelope;
39 import org.opendaylight.controller.cluster.access.concepts.Request;
40 import org.opendaylight.controller.cluster.access.concepts.RequestEnvelope;
41 import org.opendaylight.controller.cluster.access.concepts.RequestFailure;
42 import org.opendaylight.controller.cluster.access.concepts.RequestSuccess;
43 import org.opendaylight.controller.cluster.access.concepts.Response;
44 import org.opendaylight.controller.cluster.access.concepts.SuccessEnvelope;
45 import org.opendaylight.controller.cluster.datastore.messages.PrimaryShardInfo;
46 import org.opendaylight.controller.cluster.datastore.utils.ActorUtils;
47 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
48 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
49 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
50 import scala.concurrent.Promise;
52 public abstract class AbstractClientHandleTest<T extends AbstractClientHandle<AbstractProxyTransaction>> {
54 private static final String PERSISTENCE_ID = "per-1";
55 private static final YangInstanceIdentifier PATH = YangInstanceIdentifier.empty();
58 private DataTree dataTree;
60 private DataTreeSnapshot dataTreeSnapshot;
61 private ActorSystem system;
62 private TestProbe backendProbe;
63 private AbstractClientHistory parent;
64 private AbstractDataStoreClientBehavior client;
68 public void setUp() throws Exception {
69 MockitoAnnotations.initMocks(this);
70 system = ActorSystem.apply();
71 final TestProbe contextProbe = new TestProbe(system, "context");
72 final TestProbe clientContextProbe = new TestProbe(system, "client-context");
73 backendProbe = new TestProbe(system, "backend");
74 //create handle dependencies
75 final ActorUtils actorUtils = createActorContextMock(system, contextProbe.ref());
76 final ClientActorContext clientContext =
77 AccessClientUtil.createClientActorContext(system, clientContextProbe.ref(), CLIENT_ID, PERSISTENCE_ID);
78 client = new SimpleDataStoreClientBehavior(clientContext, actorUtils, "shard");
79 client.createLocalHistory();
80 parent = new SingleClientHistory(client, HISTORY_ID);
82 client.getConnection(0L);
83 contextProbe.expectMsgClass(ConnectClientRequest.class);
84 final long sequence = 0L;
85 contextProbe.reply(new ConnectClientSuccess(CLIENT_ID, sequence, backendProbe.ref(),
86 Collections.emptyList(), dataTree, 3));
87 final InternalCommand<ShardBackendInfo> command = clientContextProbe.expectMsgClass(InternalCommand.class);
88 command.execute(client);
90 when(dataTree.takeSnapshot()).thenReturn(dataTreeSnapshot);
92 handle = createHandle(parent);
95 @SuppressWarnings("checkstyle:hiddenField")
96 protected abstract T createHandle(AbstractClientHistory parent);
99 * Do a operation with handle.
100 * Used for testing, whether closed handle throws exception when the operation is performed.
102 * @param handle handle
104 @SuppressWarnings("checkstyle:hiddenField")
105 protected abstract void doHandleOperation(T handle);
108 public void tearDown() {
109 TestKit.shutdownActorSystem(system);
113 public void testGetIdentifier() {
114 Assert.assertEquals(TRANSACTION_ID, handle.getIdentifier());
118 public void testAbort() throws Exception {
119 doHandleOperation(handle);
121 final Envelope<?> envelope = backendProbe.expectMsgClass(Envelope.class);
122 final AbortLocalTransactionRequest request = (AbortLocalTransactionRequest) envelope.getMessage();
123 Assert.assertEquals(TRANSACTION_ID, request.getTarget());
128 public void testLocalAbort() throws Exception {
129 doHandleOperation(handle);
130 handle.localAbort(new RuntimeException("fail"));
131 final Envelope<?> envelope = backendProbe.expectMsgClass(Envelope.class);
132 final AbortLocalTransactionRequest request = (AbortLocalTransactionRequest) envelope.getMessage();
133 Assert.assertEquals(TRANSACTION_ID, request.getTarget());
138 public void testEnsureClosed() {
139 doHandleOperation(handle);
140 final Collection<AbstractProxyTransaction> transactions = handle.ensureClosed();
141 Assert.assertNotNull(transactions);
142 Assert.assertEquals(1, transactions.size());
146 public void testEnsureProxy() {
147 final AbstractProxyTransaction expected = mock(AbstractProxyTransaction.class);
148 final AbstractProxyTransaction proxy = handle.ensureProxy(PATH);
149 Assert.assertEquals(0, proxy.getIdentifier().getTransactionId());
153 public void testParent() {
154 Assert.assertEquals(parent, handle.parent());
157 protected void checkClosed() throws Exception {
158 TestUtils.assertOperationThrowsException(() -> doHandleOperation(handle), IllegalStateException.class);
162 * Checks, whether backend actor has received request of expected class wrapped in RequestEnvelope.
163 * Then given response wrapped in ResponseEnvelope is sent.
165 * @param expectedRequestClass expected request class
166 * @param response response
167 * @param <R> expected request type
168 * @return request message
170 protected <R extends Request<?, R>> R backendRespondToRequest(final Class<R> expectedRequestClass,
171 final Response<?, ?> response) {
172 final RequestEnvelope envelope = backendProbe.expectMsgClass(RequestEnvelope.class);
173 Assert.assertEquals(expectedRequestClass, envelope.getMessage().getClass());
174 final AbstractClientConnection<ShardBackendInfo> connection = client.getConnection(0L);
175 final long sessionId = envelope.getSessionId();
176 final long txSequence = envelope.getTxSequence();
177 final long executionTime = 0L;
178 if (response instanceof RequestSuccess) {
179 final RequestSuccess<?, ?> success = (RequestSuccess<?, ?>) response;
180 final SuccessEnvelope responseEnvelope = new SuccessEnvelope(success, sessionId, txSequence, executionTime);
181 AccessClientUtil.completeRequest(connection, responseEnvelope);
182 } else if (response instanceof RequestFailure) {
183 final RequestFailure<?, ?> fail = (RequestFailure<?, ?>) response;
184 final FailureEnvelope responseEnvelope = new FailureEnvelope(fail, sessionId, txSequence, executionTime);
185 AccessClientUtil.completeRequest(connection, responseEnvelope);
187 return expectedRequestClass.cast(envelope.getMessage());
190 protected T getHandle() {
194 protected DataTreeSnapshot getDataTreeSnapshot() {
195 return dataTreeSnapshot;
198 private static ActorUtils createActorContextMock(final ActorSystem system, final ActorRef actor) {
199 final ActorUtils mock = mock(ActorUtils.class);
200 final Promise<PrimaryShardInfo> promise = new scala.concurrent.impl.Promise.DefaultPromise<>();
201 final ActorSelection selection = system.actorSelection(actor.path());
202 final PrimaryShardInfo shardInfo = new PrimaryShardInfo(selection, (short) 0);
203 promise.success(shardInfo);
204 when(mock.findPrimaryShardAsync(any())).thenReturn(promise.future());