2 * Copyright (c) 2021 PANTHEON.tech, 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.mdsal.dom.spi;
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertFalse;
12 import static org.junit.Assert.assertSame;
13 import static org.junit.Assert.assertThrows;
14 import static org.junit.Assert.assertTrue;
15 import static org.mockito.ArgumentMatchers.any;
16 import static org.mockito.Mockito.doAnswer;
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;
22 import com.google.common.util.concurrent.FluentFuture;
23 import com.google.common.util.concurrent.Futures;
24 import com.google.common.util.concurrent.SettableFuture;
25 import java.util.Optional;
26 import java.util.concurrent.ExecutionException;
27 import java.util.function.Function;
28 import org.junit.Before;
29 import org.junit.Test;
30 import org.junit.runner.RunWith;
31 import org.mockito.Mock;
32 import org.mockito.junit.MockitoJUnitRunner;
33 import org.opendaylight.mdsal.common.api.CommitInfo;
34 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
35 import org.opendaylight.mdsal.common.api.TransactionCommitFailedException;
36 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadOperations;
37 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
38 import org.opendaylight.mdsal.dom.api.DOMDataTreeTransaction;
39 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteOperations;
40 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
41 import org.opendaylight.mdsal.dom.api.DOMTransactionChainListener;
42 import org.opendaylight.yangtools.util.concurrent.FluentFutures;
43 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
44 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
46 @RunWith(MockitoJUnitRunner.StrictStubs.class)
47 public class PingPongTransactionChainTest {
49 public Function<DOMTransactionChainListener, DOMTransactionChain> delegateFactory;
51 public DOMTransactionChainListener listener;
53 public DOMTransactionChain chain;
55 public DOMDataTreeReadWriteTransaction rwTx;
57 public DOMTransactionChainListener pingPongListener;
58 public PingPongTransactionChain pingPong;
61 public void before() {
62 // Slightly complicated bootstrap
63 doAnswer(invocation -> {
64 pingPongListener = invocation.getArgument(0);
66 }).when(delegateFactory).apply(any());
67 pingPong = new PingPongTransactionChain(delegateFactory, listener);
68 verify(delegateFactory).apply(any());
70 doReturn(rwTx).when(chain).newReadWriteTransaction();
74 public void testIdleClose() {
75 doNothing().when(chain).close();
77 verify(chain).close();
79 doNothing().when(listener).onTransactionChainSuccessful(pingPong);
80 pingPongListener.onTransactionChainSuccessful(chain);
81 verify(listener).onTransactionChainSuccessful(pingPong);
85 public void testIdleFailure() {
86 final var cause = new Throwable();
87 doNothing().when(listener).onTransactionChainFailed(pingPong, null, cause);
88 pingPongListener.onTransactionChainFailed(chain, rwTx, cause);
89 verify(listener).onTransactionChainFailed(pingPong, null, cause);
93 public void testReadOnly() {
94 final var tx = pingPong.newReadOnlyTransaction();
95 assertGetIdentifier(tx);
96 assertReadOperations(tx);
97 assertCommit(tx::close);
101 public void testReadWrite() {
102 final var tx = pingPong.newReadWriteTransaction();
103 assertGetIdentifier(tx);
104 assertReadOperations(tx);
105 assertWriteOperations(tx);
106 assertCommit(tx::commit);
110 public void testWriteOnly() {
111 final var tx = pingPong.newWriteOnlyTransaction();
112 assertGetIdentifier(tx);
113 assertWriteOperations(tx);
114 assertCommit(tx::commit);
117 private void assertGetIdentifier(final DOMDataTreeTransaction tx) {
118 final var id = mock(Object.class);
119 doReturn(id).when(rwTx).getIdentifier();
120 assertSame(id, tx.getIdentifier());
123 private void assertReadOperations(final DOMDataTreeReadOperations tx) {
124 doReturn(FluentFutures.immediateTrueFluentFuture()).when(rwTx).exists(
125 LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty());
126 final var exists = tx.exists(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty());
127 verify(rwTx).exists(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty());
128 assertEquals(Boolean.TRUE, assertDone(exists));
130 doReturn(FluentFutures.immediateFluentFuture(Optional.empty())).when(rwTx).read(
131 LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty());
132 final var read = tx.read(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty());
133 verify(rwTx).read(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty());
134 assertEquals(Optional.empty(), assertDone(read));
137 private void assertWriteOperations(final DOMDataTreeWriteOperations tx) {
138 doNothing().when(rwTx).delete(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty());
139 tx.delete(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty());
140 verify(rwTx).delete(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty());
142 final var data = mock(NormalizedNode.class);
143 doNothing().when(rwTx).merge(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty(), data);
144 tx.merge(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty(), data);
145 verify(rwTx).merge(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty(), data);
147 doNothing().when(rwTx).put(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty(), data);
148 tx.put(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty(), data);
149 verify(rwTx).put(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty(), data);
152 private void assertCommit(final Runnable commitMethod) {
153 doReturn(CommitInfo.emptyFluentFuture()).when(rwTx).commit();
155 verify(rwTx).commit();
159 public void testCommitFailure() {
160 assertCommitFailure(() -> { });
164 public void testCommitFailureAfterClose() {
165 assertCommitFailure(() -> {
166 doNothing().when(chain).close();
168 verify(chain).close();
172 private void assertCommitFailure(final Runnable asyncAction) {
173 final var tx = pingPong.newWriteOnlyTransaction();
175 final var rwTxFuture = SettableFuture.<CommitInfo>create();
176 doReturn(FluentFuture.from(rwTxFuture)).when(rwTx).commit();
178 final var txFuture = tx.commit();
179 verify(rwTx).commit();
180 assertFalse(txFuture.isDone());
184 final var cause = new TransactionCommitFailedException("cause");
185 rwTxFuture.setException(cause);
186 assertSame(cause, assertThrows(ExecutionException.class, () -> Futures.getDone(txFuture)).getCause());
190 public void testSimpleCancelFalse() {
191 assertSimpleCancel(false);
195 public void testSimpleCancelTrue() {
196 assertSimpleCancel(true);
199 private void assertSimpleCancel(final boolean result) {
200 final var tx = pingPong.newWriteOnlyTransaction();
202 doNothing().when(chain).close();
203 doReturn(result).when(rwTx).cancel();
204 doReturn("mock").when(rwTx).toString();
206 // FIXME: it seems we are doing the wrong, we should see 'result' returned here
207 assertTrue(tx.cancel());
209 verify(rwTx).cancel();
210 verify(chain).close();
213 private static <T> T assertDone(final FluentFuture<T> future) {
215 return Futures.getDone(future);
216 } catch (ExecutionException e) {
217 throw new AssertionError(e);