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.mockito.ArgumentMatchers.any;
15 import static org.mockito.Mockito.doAnswer;
16 import static org.mockito.Mockito.doNothing;
17 import static org.mockito.Mockito.doReturn;
18 import static org.mockito.Mockito.mock;
19 import static org.mockito.Mockito.verify;
21 import com.google.common.util.concurrent.FluentFuture;
22 import com.google.common.util.concurrent.Futures;
23 import com.google.common.util.concurrent.SettableFuture;
24 import java.util.Optional;
25 import java.util.concurrent.ExecutionException;
26 import java.util.function.Function;
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.mdsal.common.api.CommitInfo;
33 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
34 import org.opendaylight.mdsal.common.api.TransactionCommitFailedException;
35 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadOperations;
36 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
37 import org.opendaylight.mdsal.dom.api.DOMDataTreeTransaction;
38 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteOperations;
39 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
40 import org.opendaylight.mdsal.dom.api.DOMTransactionChainListener;
41 import org.opendaylight.yangtools.util.concurrent.FluentFutures;
42 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
43 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
45 @RunWith(MockitoJUnitRunner.StrictStubs.class)
46 public class PingPongTransactionChainTest {
48 public Function<DOMTransactionChainListener, DOMTransactionChain> delegateFactory;
50 public DOMTransactionChainListener listener;
52 public DOMTransactionChain chain;
54 public DOMDataTreeReadWriteTransaction rwTx;
56 public DOMTransactionChainListener pingPongListener;
57 public PingPongTransactionChain pingPong;
60 public void before() {
61 // Slightly complicated bootstrap
62 doAnswer(invocation -> {
63 pingPongListener = invocation.getArgument(0);
65 }).when(delegateFactory).apply(any());
66 pingPong = new PingPongTransactionChain(delegateFactory, listener);
67 verify(delegateFactory).apply(any());
69 doReturn(rwTx).when(chain).newReadWriteTransaction();
73 public void testIdleClose() {
74 doNothing().when(chain).close();
76 verify(chain).close();
78 doNothing().when(listener).onTransactionChainSuccessful(pingPong);
79 pingPongListener.onTransactionChainSuccessful(chain);
80 verify(listener).onTransactionChainSuccessful(pingPong);
84 public void testIdleFailure() {
85 final var cause = new Throwable();
86 doNothing().when(listener).onTransactionChainFailed(pingPong, null, cause);
87 pingPongListener.onTransactionChainFailed(chain, rwTx, cause);
88 verify(listener).onTransactionChainFailed(pingPong, null, cause);
92 public void testReadOnly() {
93 final var tx = pingPong.newReadOnlyTransaction();
94 assertGetIdentifier(tx);
95 assertReadOperations(tx);
96 assertCommit(tx::close);
100 public void testReadWrite() {
101 final var tx = pingPong.newReadWriteTransaction();
102 assertGetIdentifier(tx);
103 assertReadOperations(tx);
104 assertWriteOperations(tx);
105 assertCommit(tx::commit);
109 public void testWriteOnly() {
110 final var tx = pingPong.newWriteOnlyTransaction();
111 assertGetIdentifier(tx);
112 assertWriteOperations(tx);
113 assertCommit(tx::commit);
116 private void assertGetIdentifier(final DOMDataTreeTransaction tx) {
117 final var id = mock(Object.class);
118 doReturn(id).when(rwTx).getIdentifier();
119 assertSame(id, tx.getIdentifier());
122 private void assertReadOperations(final DOMDataTreeReadOperations tx) {
123 doReturn(FluentFutures.immediateTrueFluentFuture()).when(rwTx).exists(
124 LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty());
125 final var exists = tx.exists(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty());
126 verify(rwTx).exists(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty());
127 assertEquals(Boolean.TRUE, assertDone(exists));
129 doReturn(FluentFutures.immediateFluentFuture(Optional.empty())).when(rwTx).read(
130 LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty());
131 final var read = tx.read(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty());
132 verify(rwTx).read(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty());
133 assertEquals(Optional.empty(), assertDone(read));
136 private void assertWriteOperations(final DOMDataTreeWriteOperations tx) {
137 doNothing().when(rwTx).delete(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty());
138 tx.delete(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty());
139 verify(rwTx).delete(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty());
141 final var data = mock(NormalizedNode.class);
142 doNothing().when(rwTx).merge(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty(), data);
143 tx.merge(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty(), data);
144 verify(rwTx).merge(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty(), data);
146 doNothing().when(rwTx).put(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty(), data);
147 tx.put(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty(), data);
148 verify(rwTx).put(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.empty(), data);
151 private void assertCommit(final Runnable commitMethod) {
152 doReturn(CommitInfo.emptyFluentFuture()).when(rwTx).commit();
154 verify(rwTx).commit();
158 public void testCommitFailure() {
159 assertCommitFailure(() -> { });
163 public void testCommitFailureAfterClose() {
164 assertCommitFailure(() -> {
165 doNothing().when(chain).close();
167 verify(chain).close();
171 private void assertCommitFailure(final Runnable asyncAction) {
172 final var tx = pingPong.newWriteOnlyTransaction();
174 final var rwTxFuture = SettableFuture.<CommitInfo>create();
175 doReturn(FluentFuture.from(rwTxFuture)).when(rwTx).commit();
177 final var txFuture = tx.commit();
178 verify(rwTx).commit();
179 assertFalse(txFuture.isDone());
183 final var cause = new TransactionCommitFailedException("cause");
184 rwTxFuture.setException(cause);
185 assertSame(cause, assertThrows(ExecutionException.class, () -> Futures.getDone(txFuture)).getCause());
189 public void testSimpleCancelFalse() {
190 assertSimpleCancel(false);
194 public void testSimpleCancelTrue() {
195 assertSimpleCancel(true);
198 private void assertSimpleCancel(final boolean result) {
199 final var tx = pingPong.newWriteOnlyTransaction();
201 doReturn(result).when(rwTx).cancel();
202 assertEquals(result, tx.cancel());
203 verify(rwTx).cancel();
206 private static <T> T assertDone(final FluentFuture<T> future) {
208 return Futures.getDone(future);
209 } catch (ExecutionException e) {
210 throw new AssertionError(e);