CONTROLLER-1641: Handle commit cohorts async
[controller.git] / opendaylight / md-sal / sal-distributed-datastore / src / test / java / org / opendaylight / controller / cluster / datastore / SimpleShardDataTreeCohortTest.java
1 /*
2  * Copyright (c) 2015 Brocade Communications Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.controller.cluster.datastore;
9
10 import static org.junit.Assert.assertSame;
11 import static org.mockito.Matchers.any;
12 import static org.mockito.Mockito.doAnswer;
13 import static org.mockito.Mockito.doReturn;
14 import static org.mockito.Mockito.mock;
15 import static org.mockito.Mockito.never;
16 import static org.mockito.Mockito.verify;
17 import static org.mockito.Mockito.verifyNoMoreInteractions;
18
19 import com.google.common.primitives.UnsignedLong;
20 import com.google.common.util.concurrent.FutureCallback;
21 import java.util.Optional;
22 import java.util.concurrent.CompletableFuture;
23 import java.util.concurrent.Future;
24 import org.junit.Before;
25 import org.junit.Test;
26 import org.mockito.Mock;
27 import org.mockito.MockitoAnnotations;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
29 import org.opendaylight.yangtools.yang.data.api.schema.tree.ConflictingModificationAppliedException;
30 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
31 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateTip;
32 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
33 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
34
35 /**
36  * Unit tests for SimpleShardDataTreeCohort.
37  *
38  * @author Thomas Pantelis
39  */
40 public class SimpleShardDataTreeCohortTest extends AbstractTest {
41     @Mock
42     private ShardDataTree mockShardDataTree;
43
44     @Mock
45     private DataTreeModification mockModification;
46
47     @Mock
48     private CompositeDataTreeCohort mockUserCohorts;
49
50     @Mock
51     private FutureCallback<DataTreeCandidate> mockPreCallback;
52
53     private SimpleShardDataTreeCohort cohort;
54
55     @Before
56     public void setup() throws Exception {
57         MockitoAnnotations.initMocks(this);
58
59         doReturn(Optional.empty()).when(mockUserCohorts).commit();
60         doReturn(Optional.empty()).when(mockUserCohorts).abort();
61
62         cohort = new SimpleShardDataTreeCohort(mockShardDataTree, mockModification, nextTransactionId(),
63             mockUserCohorts);
64     }
65
66     @Test
67     public void testCanCommitSuccess() throws Exception {
68         canCommitSuccess();
69     }
70
71     private void canCommitSuccess() {
72         doAnswer(invocation -> {
73             invocation.getArgumentAt(0, SimpleShardDataTreeCohort.class).successfulCanCommit();
74             return null;
75         }).when(mockShardDataTree).startCanCommit(cohort);
76
77         @SuppressWarnings("unchecked")
78         final FutureCallback<Void> callback = mock(FutureCallback.class);
79         cohort.canCommit(callback);
80
81         verify(callback).onSuccess(null);
82         verifyNoMoreInteractions(callback);
83     }
84
85     private void testValidatationPropagates(final Exception cause) throws DataValidationFailedException {
86         doAnswer(invocation -> {
87             invocation.getArgumentAt(0, SimpleShardDataTreeCohort.class).failedCanCommit(cause);
88             return null;
89         }).when(mockShardDataTree).startCanCommit(cohort);
90
91         @SuppressWarnings("unchecked")
92         final FutureCallback<Void> callback = mock(FutureCallback.class);
93         cohort.canCommit(callback);
94
95         verify(callback).onFailure(cause);
96         verifyNoMoreInteractions(callback);
97     }
98
99     @Test
100     public void testCanCommitWithConflictingModEx() throws DataValidationFailedException {
101         testValidatationPropagates(new ConflictingModificationAppliedException(YangInstanceIdentifier.EMPTY, "mock"));
102     }
103
104     @Test
105     public void testCanCommitWithDataValidationEx() throws DataValidationFailedException {
106         testValidatationPropagates(new DataValidationFailedException(YangInstanceIdentifier.EMPTY, "mock"));
107     }
108
109     @Test
110     public void testCanCommitWithIllegalArgumentEx() throws DataValidationFailedException {
111         testValidatationPropagates(new IllegalArgumentException("mock"));
112     }
113
114     private DataTreeCandidateTip preCommitSuccess() {
115         final DataTreeCandidateTip mockCandidate = mock(DataTreeCandidateTip.class);
116         doAnswer(invocation -> {
117             invocation.getArgumentAt(0, SimpleShardDataTreeCohort.class).successfulPreCommit(mockCandidate);
118             return null;
119         }).when(mockShardDataTree).startPreCommit(cohort);
120
121         @SuppressWarnings("unchecked")
122         final FutureCallback<DataTreeCandidate> callback = mock(FutureCallback.class);
123         cohort.preCommit(callback);
124
125         verify(callback).onSuccess(mockCandidate);
126         verifyNoMoreInteractions(callback);
127
128         assertSame("getCandidate", mockCandidate, cohort.getCandidate());
129
130         return mockCandidate;
131     }
132
133     @Test
134     public void testPreCommitAndCommitSuccess() throws Exception {
135         canCommitSuccess();
136         final DataTreeCandidateTip candidate = preCommitSuccess();
137
138         doAnswer(invocation -> {
139             invocation.getArgumentAt(0, SimpleShardDataTreeCohort.class).successfulCommit(UnsignedLong.valueOf(0),
140                 () -> { });
141             return null;
142         }).when(mockShardDataTree).startCommit(cohort, candidate);
143
144         @SuppressWarnings("unchecked")
145         final FutureCallback<UnsignedLong> mockCommitCallback = mock(FutureCallback.class);
146         cohort.commit(mockCommitCallback);
147
148         verify(mockCommitCallback).onSuccess(any(UnsignedLong.class));
149         verifyNoMoreInteractions(mockCommitCallback);
150
151         verify(mockUserCohorts).commit();
152     }
153
154     @Test
155     public void testPreCommitWithIllegalArgumentEx() throws Exception {
156         canCommitSuccess();
157
158         final Exception cause = new IllegalArgumentException("mock");
159         doAnswer(invocation -> {
160             invocation.getArgumentAt(0, SimpleShardDataTreeCohort.class).failedPreCommit(cause);
161             return null;
162         }).when(mockShardDataTree).startPreCommit(cohort);
163
164         @SuppressWarnings("unchecked")
165         final FutureCallback<DataTreeCandidate> callback = mock(FutureCallback.class);
166         cohort.preCommit(callback);
167
168         verify(callback).onFailure(cause);
169         verifyNoMoreInteractions(callback);
170
171         verify(mockUserCohorts).abort();
172     }
173
174     @Test
175     public void testPreCommitWithReportedFailure() throws Exception {
176         canCommitSuccess();
177
178         final Exception cause = new IllegalArgumentException("mock");
179         cohort.reportFailure(cause);
180
181         @SuppressWarnings("unchecked")
182         final FutureCallback<DataTreeCandidate> callback = mock(FutureCallback.class);
183         cohort.preCommit(callback);
184
185         verify(callback).onFailure(cause);
186         verifyNoMoreInteractions(callback);
187
188         verify(mockShardDataTree, never()).startPreCommit(cohort);
189     }
190
191     @Test
192     public void testCommitWithIllegalArgumentEx() {
193         canCommitSuccess();
194         final DataTreeCandidateTip candidate = preCommitSuccess();
195
196         final Exception cause = new IllegalArgumentException("mock");
197         doAnswer(invocation -> {
198             invocation.getArgumentAt(0, SimpleShardDataTreeCohort.class).failedCommit(cause);
199             return null;
200         }).when(mockShardDataTree).startCommit(cohort, candidate);
201
202         @SuppressWarnings("unchecked")
203         final FutureCallback<UnsignedLong> callback = mock(FutureCallback.class);
204         cohort.commit(callback);
205
206         verify(callback).onFailure(cause);
207         verifyNoMoreInteractions(callback);
208
209         verify(mockUserCohorts).abort();
210     }
211
212     private static Future<?> abort(final ShardDataTreeCohort cohort) {
213         final CompletableFuture<Void> f = new CompletableFuture<>();
214         cohort.abort(new FutureCallback<Void>() {
215             @Override
216             public void onSuccess(final Void result) {
217                 f.complete(null);
218             }
219
220             @Override
221             public void onFailure(final Throwable failure) {
222                 f.completeExceptionally(failure);
223             }
224         });
225
226         return f;
227     }
228
229     @Test
230     public void testAbort() throws Exception {
231         doReturn(Boolean.TRUE).when(mockShardDataTree).startAbort(cohort);
232
233         abort(cohort).get();
234         verify(mockShardDataTree).startAbort(cohort);
235     }
236
237     @Test
238     public void testAbortWithCohorts() throws Exception {
239         doReturn(true).when(mockShardDataTree).startAbort(cohort);
240
241         doReturn(Optional.of(CompletableFuture.completedFuture(null))).when(mockUserCohorts).abort();
242
243         final Future<?> abortFuture = abort(cohort);
244
245         abortFuture.get();
246         verify(mockShardDataTree).startAbort(cohort);
247     }
248 }