3223285082fd3351cfc957c777a20324432479b8
[honeycomb.git] / infra / data-impl / src / test / java / io / fd / honeycomb / data / impl / ModifiableDataTreeDelegatorTest.java
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package io.fd.honeycomb.data.impl;
18
19 import static org.hamcrest.CoreMatchers.hasItem;
20 import static org.hamcrest.CoreMatchers.hasItems;
21 import static org.hamcrest.CoreMatchers.is;
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertThat;
24 import static org.junit.Assert.assertTrue;
25 import static org.junit.Assert.fail;
26 import static org.mockito.Matchers.any;
27 import static org.mockito.Matchers.eq;
28 import static org.mockito.Mockito.doReturn;
29 import static org.mockito.Mockito.doThrow;
30 import static org.mockito.Mockito.mock;
31 import static org.mockito.Mockito.verify;
32 import static org.mockito.Mockito.when;
33 import static org.mockito.MockitoAnnotations.initMocks;
34
35 import com.google.common.base.Optional;
36 import com.google.common.collect.HashMultimap;
37 import com.google.common.collect.ImmutableMultimap;
38 import com.google.common.collect.Multimap;
39 import com.google.common.util.concurrent.CheckedFuture;
40 import com.google.common.util.concurrent.Futures;
41 import io.fd.honeycomb.data.DataModification;
42 import io.fd.honeycomb.translate.TranslationException;
43 import io.fd.honeycomb.translate.write.DataObjectUpdate;
44 import io.fd.honeycomb.translate.write.WriteContext;
45 import io.fd.honeycomb.translate.write.registry.WriterRegistry;
46 import java.util.AbstractMap;
47 import java.util.Collections;
48 import java.util.HashMap;
49 import java.util.Map;
50 import org.junit.Before;
51 import org.junit.Test;
52 import org.mockito.ArgumentCaptor;
53 import org.mockito.Captor;
54 import org.mockito.Mock;
55 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
56 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
57 import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
58 import org.opendaylight.yangtools.yang.binding.DataObject;
59 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
60 import org.opendaylight.yangtools.yang.common.QName;
61 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
62 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
63 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
64 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
65 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
66
67 public class ModifiableDataTreeDelegatorTest {
68
69     @Mock
70     private WriterRegistry writer;
71     @Mock
72     private BindingNormalizedNodeSerializer serializer;
73     private DataTree dataTree;
74     @Mock
75     private org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification modification;
76     @Mock
77     private DataBroker contextBroker;
78     @Mock
79     private org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction tx;
80
81     @Captor
82     private ArgumentCaptor<WriteContext> writeContextCaptor;
83
84     private ModifiableDataTreeManager configDataTree;
85
86     static final InstanceIdentifier<?> DEFAULT_ID = InstanceIdentifier.create(DataObject.class);
87     static DataObject DEFAULT_DATA_OBJECT = mockDataObject("serialized", DataObject.class);
88
89     @Before
90     public void setUp() throws Exception {
91         initMocks(this);
92         dataTree = ModificationDiffTest.getDataTree();
93         when(contextBroker.newReadWriteTransaction()).thenReturn(tx);
94         when(tx.submit()).thenReturn(Futures.immediateCheckedFuture(null));
95
96         when(serializer.fromYangInstanceIdentifier(any(YangInstanceIdentifier.class))).thenReturn(((InstanceIdentifier) DEFAULT_ID));
97         final Map.Entry<InstanceIdentifier<?>, DataObject> parsed = new AbstractMap.SimpleEntry<>(DEFAULT_ID, DEFAULT_DATA_OBJECT);
98         when(serializer.fromNormalizedNode(any(YangInstanceIdentifier.class), any(NormalizedNode.class))).thenReturn(parsed);
99
100         configDataTree = new ModifiableDataTreeDelegator(serializer, dataTree, ModificationDiffTest.getSchemaCtx(), writer, contextBroker);
101     }
102
103     @Test
104     public void testRead() throws Exception {
105         final ContainerNode topContainer = ModificationDiffTest.getTopContainer("topContainer");
106         ModificationDiffTest.addNodeToTree(dataTree, topContainer, ModificationDiffTest.TOP_CONTAINER_ID);
107         final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read =
108                 configDataTree.read(ModificationDiffTest.TOP_CONTAINER_ID);
109         final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read2 =
110                 configDataTree.newModification().read(ModificationDiffTest.TOP_CONTAINER_ID);
111         final Optional<NormalizedNode<?, ?>> normalizedNodeOptional = read.get();
112         final Optional<NormalizedNode<?, ?>> normalizedNodeOptional2 = read2.get();
113
114         assertEquals(normalizedNodeOptional, normalizedNodeOptional2);
115         assertTrue(normalizedNodeOptional.isPresent());
116         assertEquals(topContainer, normalizedNodeOptional.get());
117         assertEquals(dataTree.takeSnapshot().readNode(ModificationDiffTest.TOP_CONTAINER_ID), normalizedNodeOptional);
118     }
119
120     @Test
121     public void testCommitSuccessful() throws Exception {
122         final MapNode nestedList = ModificationDiffTest.getNestedList("listEntry", "listValue");
123
124         final DataModification dataModification = configDataTree.newModification();
125         dataModification.write(ModificationDiffTest.NESTED_LIST_ID, nestedList);
126         dataModification.validate();
127         dataModification.commit();
128
129         final Multimap<InstanceIdentifier<?>, DataObjectUpdate> map = HashMultimap.create();
130         map.put(DEFAULT_ID, DataObjectUpdate.create(DEFAULT_ID, DEFAULT_DATA_OBJECT, DEFAULT_DATA_OBJECT));
131         verify(writer).update(eq(new WriterRegistry.DataObjectUpdates(map, ImmutableMultimap.of())), any(WriteContext.class));
132         assertEquals(nestedList, dataTree.takeSnapshot().readNode(ModificationDiffTest.NESTED_LIST_ID).get());
133     }
134
135     private static DataObject mockDataObject(final String name, final Class<? extends DataObject> classToMock) {
136         final DataObject dataBefore = mock(classToMock, name);
137         doReturn(classToMock).when(dataBefore).getImplementedInterface();
138         return dataBefore;
139     }
140
141     @Test
142     public void testCommitUndoSuccessful() throws Exception {
143         final MapNode nestedList = ModificationDiffTest.getNestedList("listEntry", "listValue");
144
145         // Fail on update:
146         final WriterRegistry.Reverter reverter = mock(WriterRegistry.Reverter.class);
147         final TranslationException failedOnUpdateException = new TranslationException("update failed");
148         doThrow(new WriterRegistry.BulkUpdateException(Collections.singleton(DEFAULT_ID), reverter, failedOnUpdateException))
149                 .when(writer).update(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class));
150
151         try {
152             // Run the test
153             final DataModification dataModification = configDataTree.newModification();
154             dataModification.write(ModificationDiffTest.NESTED_LIST_ID, nestedList);
155             dataModification.validate();
156             dataModification.commit();
157             fail("WriterRegistry.RevertSuccessException was expected");
158         } catch (WriterRegistry.Reverter.RevertSuccessException e) {
159             verify(writer).update(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class));
160             assertThat(e.getFailedIds(), hasItem(DEFAULT_ID));
161             verify(reverter).revert(any(WriteContext.class));
162         }
163     }
164
165     @Test
166     public void testCommitUndoFailed() throws Exception {
167         final MapNode nestedList = ModificationDiffTest.getNestedList("listEntry", "listValue");
168
169         // Fail on update:
170         final WriterRegistry.Reverter reverter = mock(WriterRegistry.Reverter.class);
171         final TranslationException failedOnUpdateException = new TranslationException("update failed");
172         doThrow(new WriterRegistry.BulkUpdateException(Collections.singleton(DEFAULT_ID), reverter, failedOnUpdateException))
173                 .when(writer).update(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class));
174
175         // Fail on revert:
176         final TranslationException failedOnRevertException = new TranslationException("revert failed");
177         doThrow(new WriterRegistry.Reverter.RevertFailedException(Collections.emptySet(), failedOnRevertException))
178                 .when(reverter).revert(any(WriteContext.class));
179
180         try {
181             // Run the test
182             final DataModification dataModification = configDataTree.newModification();
183             dataModification.write(ModificationDiffTest.NESTED_LIST_ID, nestedList);
184             dataModification.validate();
185             dataModification.commit();
186             fail("WriterRegistry.Reverter.RevertFailedException was expected");
187         } catch (WriterRegistry.Reverter.RevertFailedException e) {
188             verify(writer).update(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class));
189             verify(reverter).revert(any(WriteContext.class));
190             assertEquals(failedOnRevertException, e.getCause());
191         }
192     }
193
194     private abstract static class DataObject1 implements DataObject {}
195     private abstract static class DataObject2 implements DataObject {}
196     private abstract static class DataObject3 implements DataObject {}
197
198     @Test
199     public void testToBindingAware() throws Exception {
200         when(serializer.fromNormalizedNode(any(YangInstanceIdentifier.class), eq(null))).thenReturn(null);
201
202         final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> biNodes = new HashMap<>();
203         // delete
204         final QName nn1 = QName.create("namespace", "nn1");
205         final YangInstanceIdentifier yid1 = mockYid(nn1);
206         final InstanceIdentifier iid1 = mockIid(yid1, DataObject1.class);
207         final NormalizedNode nn1B = mockNormalizedNode(nn1);
208         final DataObject1 do1B = mockDataObject(yid1, iid1, nn1B, DataObject1.class);
209         biNodes.put(yid1, ModificationDiff.NormalizedNodeUpdate.create(yid1, nn1B, null));
210
211         // create
212         final QName nn2 = QName.create("namespace", "nn1");
213         final YangInstanceIdentifier yid2 = mockYid(nn2);
214         final InstanceIdentifier iid2 = mockIid(yid2, DataObject2.class);;
215         final NormalizedNode nn2A = mockNormalizedNode(nn2);
216         final DataObject2 do2A = mockDataObject(yid2, iid2, nn2A, DataObject2.class);
217         biNodes.put(yid2, ModificationDiff.NormalizedNodeUpdate.create(yid2, null, nn2A));
218
219         // update
220         final QName nn3 = QName.create("namespace", "nn1");
221         final YangInstanceIdentifier yid3 = mockYid(nn3);
222         final InstanceIdentifier iid3 = mockIid(yid3, DataObject3.class);
223         final NormalizedNode nn3B = mockNormalizedNode(nn3);
224         final DataObject3 do3B = mockDataObject(yid3, iid3, nn3B, DataObject3.class);
225         final NormalizedNode nn3A = mockNormalizedNode(nn3);
226         final DataObject3 do3A = mockDataObject(yid3, iid3, nn3A, DataObject3.class);;
227         biNodes.put(yid3, ModificationDiff.NormalizedNodeUpdate.create(yid3, nn3B, nn3A));
228
229         final WriterRegistry.DataObjectUpdates dataObjectUpdates =
230                 ModifiableDataTreeDelegator.toBindingAware(biNodes, serializer);
231
232         assertThat(dataObjectUpdates.getDeletes().size(), is(1));
233         assertThat(dataObjectUpdates.getDeletes().keySet(), hasItem(((InstanceIdentifier<?>) iid1)));
234         assertThat(dataObjectUpdates.getDeletes().values(), hasItem(
235                 ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid1, do1B, null))));
236
237         assertThat(dataObjectUpdates.getUpdates().size(), is(2));
238         assertThat(dataObjectUpdates.getUpdates().keySet(), hasItems((InstanceIdentifier<?>) iid2, (InstanceIdentifier<?>) iid3));
239         assertThat(dataObjectUpdates.getUpdates().values(), hasItems(
240                 DataObjectUpdate.create(iid2, null, do2A),
241                 DataObjectUpdate.create(iid3, do3B, do3A)));
242
243         assertThat(dataObjectUpdates.getTypeIntersection().size(), is(3));
244     }
245
246     private <D extends DataObject> D mockDataObject(final YangInstanceIdentifier yid1,
247                                        final InstanceIdentifier iid1,
248                                        final NormalizedNode nn1B,
249                                        final Class<D> type) {
250         final D do1B = mock(type);
251         when(serializer.fromNormalizedNode(yid1, nn1B)).thenReturn(new AbstractMap.SimpleEntry<>(iid1, do1B));
252         return do1B;
253     }
254
255     private NormalizedNode mockNormalizedNode(final QName nn1) {
256         final NormalizedNode nn1B = mock(NormalizedNode.class);
257         when(nn1B.getNodeType()).thenReturn(nn1);
258         return nn1B;
259     }
260
261     private InstanceIdentifier mockIid(final YangInstanceIdentifier yid1,
262                                        final Class<? extends DataObject> type) {
263         final InstanceIdentifier iid1 = InstanceIdentifier.create(type);
264         when(serializer.fromYangInstanceIdentifier(yid1)).thenReturn(iid1);
265         return iid1;
266     }
267
268     private YangInstanceIdentifier mockYid(final QName nn1) {
269         final YangInstanceIdentifier yid1 = mock(YangInstanceIdentifier.class);
270         when(yid1.getLastPathArgument()).thenReturn(new YangInstanceIdentifier.NodeIdentifier(nn1));
271         return yid1;
272     }
273 }