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.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertNotNull;
12 import static org.mockito.ArgumentMatchers.any;
13 import static org.mockito.Mockito.mock;
14 import static org.mockito.Mockito.when;
15 import static org.opendaylight.controller.cluster.databroker.actors.dds.TestUtils.CLIENT_ID;
16 import static org.opendaylight.controller.cluster.databroker.actors.dds.TestUtils.HISTORY_ID;
17 import static org.opendaylight.controller.cluster.databroker.actors.dds.TestUtils.TRANSACTION_ID;
19 import akka.actor.ActorRef;
20 import akka.actor.ActorSelection;
21 import akka.actor.ActorSystem;
22 import akka.testkit.TestProbe;
23 import akka.testkit.javadsl.TestKit;
24 import java.util.List;
26 import org.junit.After;
27 import org.junit.Before;
28 import org.junit.Test;
29 import org.junit.runner.RunWith;
30 import org.mockito.Mock;
31 import org.mockito.junit.MockitoJUnitRunner;
32 import org.opendaylight.controller.cluster.access.client.AbstractClientConnection;
33 import org.opendaylight.controller.cluster.access.client.AccessClientUtil;
34 import org.opendaylight.controller.cluster.access.client.ClientActorContext;
35 import org.opendaylight.controller.cluster.access.client.InternalCommand;
36 import org.opendaylight.controller.cluster.access.commands.AbortLocalTransactionRequest;
37 import org.opendaylight.controller.cluster.access.commands.ConnectClientRequest;
38 import org.opendaylight.controller.cluster.access.commands.ConnectClientSuccess;
39 import org.opendaylight.controller.cluster.access.concepts.Envelope;
40 import org.opendaylight.controller.cluster.access.concepts.FailureEnvelope;
41 import org.opendaylight.controller.cluster.access.concepts.Request;
42 import org.opendaylight.controller.cluster.access.concepts.RequestEnvelope;
43 import org.opendaylight.controller.cluster.access.concepts.RequestFailure;
44 import org.opendaylight.controller.cluster.access.concepts.RequestSuccess;
45 import org.opendaylight.controller.cluster.access.concepts.Response;
46 import org.opendaylight.controller.cluster.access.concepts.SuccessEnvelope;
47 import org.opendaylight.controller.cluster.datastore.messages.PrimaryShardInfo;
48 import org.opendaylight.controller.cluster.datastore.utils.ActorUtils;
49 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
50 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
51 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
52 import scala.concurrent.Promise;
54 @RunWith(MockitoJUnitRunner.StrictStubs.class)
55 public abstract class AbstractClientHandleTest<T extends AbstractClientHandle<AbstractProxyTransaction>> {
56 private static final String PERSISTENCE_ID = "per-1";
57 private static final YangInstanceIdentifier PATH = YangInstanceIdentifier.empty();
60 private DataTree dataTree;
62 private DataTreeSnapshot dataTreeSnapshot;
63 private ActorSystem system;
64 private TestProbe backendProbe;
65 private AbstractClientHistory parent;
66 private AbstractDataStoreClientBehavior client;
70 public void setUp() throws Exception {
71 system = ActorSystem.apply();
72 final TestProbe contextProbe = new TestProbe(system, "context");
73 final TestProbe clientContextProbe = new TestProbe(system, "client-context");
74 backendProbe = new TestProbe(system, "backend");
75 //create handle dependencies
76 final ActorUtils actorUtils = createActorContextMock(system, contextProbe.ref());
77 final ClientActorContext clientContext =
78 AccessClientUtil.createClientActorContext(system, clientContextProbe.ref(), CLIENT_ID, PERSISTENCE_ID);
79 client = new SimpleDataStoreClientBehavior(clientContext, actorUtils, "shard");
80 client.createLocalHistory();
81 parent = new SingleClientHistory(client, HISTORY_ID);
83 client.getConnection(0L);
84 contextProbe.expectMsgClass(ConnectClientRequest.class);
85 final long sequence = 0L;
86 contextProbe.reply(new ConnectClientSuccess(CLIENT_ID, sequence, backendProbe.ref(), List.of(), 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 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 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 assertEquals(TRANSACTION_ID, request.getTarget());
138 public void testEnsureClosed() {
139 doHandleOperation(handle);
140 final Map<Long, AbstractProxyTransaction> transactions = handle.ensureClosed();
141 assertNotNull(transactions);
142 assertEquals(1, transactions.size());
146 public void testEnsureProxy() {
147 final AbstractProxyTransaction expected = mock(AbstractProxyTransaction.class);
148 final AbstractProxyTransaction proxy = handle.ensureProxy(PATH);
149 assertEquals(0, proxy.getIdentifier().getTransactionId());
153 public void testParent() {
154 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 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());