2 * Copyright (c) 2016 Cisco and/or its affiliates.
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:
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package io.fd.honeycomb.data.impl;
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;
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;
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;
67 public class ModifiableDataTreeDelegatorTest {
70 private WriterRegistry writer;
72 private BindingNormalizedNodeSerializer serializer;
73 private DataTree dataTree;
75 private org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification modification;
77 private DataBroker contextBroker;
79 private org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction tx;
82 private ArgumentCaptor<WriteContext> writeContextCaptor;
84 private ModifiableDataTreeManager configDataTree;
86 static final InstanceIdentifier<?> DEFAULT_ID = InstanceIdentifier.create(DataObject.class);
87 static DataObject DEFAULT_DATA_OBJECT = mockDataObject("serialized", DataObject.class);
90 public void setUp() throws Exception {
92 dataTree = ModificationDiffTest.getDataTree();
93 when(contextBroker.newReadWriteTransaction()).thenReturn(tx);
94 when(tx.submit()).thenReturn(Futures.immediateCheckedFuture(null));
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);
100 configDataTree = new ModifiableDataTreeDelegator(serializer, dataTree, ModificationDiffTest.getSchemaCtx(), writer, contextBroker);
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();
114 assertEquals(normalizedNodeOptional, normalizedNodeOptional2);
115 assertTrue(normalizedNodeOptional.isPresent());
116 assertEquals(topContainer, normalizedNodeOptional.get());
117 assertEquals(dataTree.takeSnapshot().readNode(ModificationDiffTest.TOP_CONTAINER_ID), normalizedNodeOptional);
121 public void testCommitSuccessful() throws Exception {
122 final MapNode nestedList = ModificationDiffTest.getNestedList("listEntry", "listValue");
124 final DataModification dataModification = configDataTree.newModification();
125 dataModification.write(ModificationDiffTest.NESTED_LIST_ID, nestedList);
126 dataModification.validate();
127 dataModification.commit();
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());
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();
142 public void testCommitUndoSuccessful() throws Exception {
143 final MapNode nestedList = ModificationDiffTest.getNestedList("listEntry", "listValue");
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));
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));
166 public void testCommitUndoFailed() throws Exception {
167 final MapNode nestedList = ModificationDiffTest.getNestedList("listEntry", "listValue");
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));
176 final TranslationException failedOnRevertException = new TranslationException("revert failed");
177 doThrow(new WriterRegistry.Reverter.RevertFailedException(Collections.emptySet(), failedOnRevertException))
178 .when(reverter).revert(any(WriteContext.class));
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());
194 private abstract static class DataObject1 implements DataObject {}
195 private abstract static class DataObject2 implements DataObject {}
196 private abstract static class DataObject3 implements DataObject {}
199 public void testToBindingAware() throws Exception {
200 when(serializer.fromNormalizedNode(any(YangInstanceIdentifier.class), eq(null))).thenReturn(null);
202 final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> biNodes = new HashMap<>();
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));
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));
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));
229 final WriterRegistry.DataObjectUpdates dataObjectUpdates =
230 ModifiableDataTreeDelegator.toBindingAware(biNodes, serializer);
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))));
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)));
243 assertThat(dataObjectUpdates.getTypeIntersection().size(), is(3));
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));
255 private NormalizedNode mockNormalizedNode(final QName nn1) {
256 final NormalizedNode nn1B = mock(NormalizedNode.class);
257 when(nn1B.getNodeType()).thenReturn(nn1);
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);
268 private YangInstanceIdentifier mockYid(final QName nn1) {
269 final YangInstanceIdentifier yid1 = mock(YangInstanceIdentifier.class);
270 when(yid1.getLastPathArgument()).thenReturn(new YangInstanceIdentifier.NodeIdentifier(nn1));