package io.fd.honeycomb.v3po.data.impl; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.util.Map; import org.junit.Test; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; import org.opendaylight.yangtools.yang.data.api.schema.MapNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateTip; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; import org.opendaylight.yangtools.yang.data.api.schema.tree.TipProducingDataTree; import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType; import org.opendaylight.yangtools.yang.data.impl.schema.Builders; import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor; import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline; import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl; public class ModificationDiffTest { private static final QName TOP_CONTAINER_QNAME = QName.create("urn:opendaylight:params:xml:ns:yang:test:diff", "2015-01-05", "top-container"); private static final QName STRING_LEAF_QNAME = QName.create(TOP_CONTAINER_QNAME, "string"); private static final QName NAME_LEAF_QNAME = QName.create(TOP_CONTAINER_QNAME, "name"); private static final QName TEXT_LEAF_QNAME = QName.create(TOP_CONTAINER_QNAME, "text"); private static final QName NESTED_LIST_QNAME = QName.create(TOP_CONTAINER_QNAME, "nested-list"); private static final QName DEEP_LIST_QNAME = QName.create(TOP_CONTAINER_QNAME, "deep-list"); private YangInstanceIdentifier topContainerId = YangInstanceIdentifier.of(TOP_CONTAINER_QNAME);; @Test public void testInitialWrite() throws Exception { final TipProducingDataTree dataTree = getDataTree(); final DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); final DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); final NormalizedNode topContainer = getTopContainer("string1"); final YangInstanceIdentifier topContainerId = YangInstanceIdentifier.of(TOP_CONTAINER_QNAME); dataTreeModification.write(topContainerId, topContainer); dataTreeModification.ready(); dataTree.validate(dataTreeModification); final DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification); final ModifiableDataTreeDelegator.ModificationDiff modificationDiff = ModifiableDataTreeDelegator.ModificationDiff .recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, prepare.getRootNode()); assertTrue(modificationDiff.getModificationsBefore().isEmpty()); assertAfter(topContainer, topContainerId, modificationDiff); } @Test public void testUpdateWrite() throws Exception { final TipProducingDataTree dataTree = getDataTree(); final NormalizedNode topContainerBefore = addTopContainer(dataTree); final DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); final DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); final NormalizedNode topContainerAfter = getTopContainer("string2"); dataTreeModification.write(topContainerId, topContainerAfter); dataTreeModification.ready(); dataTree.validate(dataTreeModification); final DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification); final ModifiableDataTreeDelegator.ModificationDiff modificationDiff = ModifiableDataTreeDelegator.ModificationDiff .recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, prepare.getRootNode()); assertBefore(topContainerBefore, topContainerId, modificationDiff); assertAfter(topContainerAfter, topContainerId, modificationDiff); } @Test public void testUpdateMerge() throws Exception { final TipProducingDataTree dataTree = getDataTree(); final NormalizedNode topContainerBefore = addTopContainer(dataTree); final DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); final DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); final NormalizedNode topContainerAfter = getTopContainer("string2"); dataTreeModification.merge(topContainerId, topContainerAfter); dataTreeModification.ready(); dataTree.validate(dataTreeModification); final DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification); final ModifiableDataTreeDelegator.ModificationDiff modificationDiff = ModifiableDataTreeDelegator.ModificationDiff .recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, prepare.getRootNode()); assertBefore(topContainerBefore, topContainerId, modificationDiff); assertAfter(topContainerAfter, topContainerId, modificationDiff); } @Test public void testUpdateDelete() throws Exception { final TipProducingDataTree dataTree = getDataTree(); final NormalizedNode topContainerBefore = addTopContainer(dataTree); final DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); final DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); dataTreeModification.delete(topContainerId); dataTreeModification.ready(); dataTree.validate(dataTreeModification); final DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification); final ModifiableDataTreeDelegator.ModificationDiff modificationDiff = ModifiableDataTreeDelegator.ModificationDiff .recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, prepare.getRootNode()); assertBefore(topContainerBefore, topContainerId, modificationDiff); assertTrue(modificationDiff.getModificationsAfter().isEmpty()); } @Test public void testWriteAndUpdateInnerList() throws Exception { final TipProducingDataTree dataTree = getDataTree(); addTopContainer(dataTree); DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); final YangInstanceIdentifier listId = YangInstanceIdentifier.create( new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME), new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME)); final MapNode mapNode = getNestedList("name1", "text"); dataTreeModification.write(listId, mapNode); dataTreeModification.ready(); dataTree.validate(dataTreeModification); DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification); ModifiableDataTreeDelegator.ModificationDiff modificationDiff = ModifiableDataTreeDelegator.ModificationDiff .recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, prepare.getRootNode()); assertTrue(modificationDiff.getModificationsBefore().isEmpty()); assertAfter(mapNode, listId, modificationDiff); // Commit so that update can be tested next dataTree.commit(prepare); YangInstanceIdentifier listItemId = listId.node( new YangInstanceIdentifier.NodeIdentifierWithPredicates(NESTED_LIST_QNAME, NAME_LEAF_QNAME, "name1")); MapEntryNode mapEntryNode = getNestedList("name1", "text-update").getValue().iterator().next(); dataTreeSnapshot = dataTree.takeSnapshot(); dataTreeModification = dataTreeSnapshot.newModification(); dataTreeModification.write(listItemId, mapEntryNode); dataTreeModification.ready(); dataTree.validate(dataTreeModification); prepare = dataTree.prepare(dataTreeModification); modificationDiff = ModifiableDataTreeDelegator.ModificationDiff .recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, prepare.getRootNode()); assertBefore(mapNode.getValue().iterator().next(), listItemId, modificationDiff); assertAfter(mapEntryNode, listItemId, modificationDiff); } @Test public void testWriteTopContainerAndInnerList() throws Exception { final TipProducingDataTree dataTree = getDataTree(); DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); final ContainerNode topContainer = getTopContainer("string1"); dataTreeModification.write(topContainerId, topContainer); final YangInstanceIdentifier listId = YangInstanceIdentifier.create( new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME), new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME)); final MapNode mapNode = getNestedList("name1", "text"); dataTreeModification.write(listId, mapNode); dataTreeModification.ready(); dataTree.validate(dataTreeModification); final DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification); final ModifiableDataTreeDelegator.ModificationDiff modificationDiff = ModifiableDataTreeDelegator.ModificationDiff .recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, prepare.getRootNode()); assertTrue(modificationDiff.getModificationsBefore().isEmpty()); // TODO HONEYCOMB-94 2 after modifications should appear, for top-container and nested-list entry assertAfter(Builders.containerBuilder(topContainer) .withChild(mapNode) .build(), topContainerId, modificationDiff); } @Test public void testWriteDeepList() throws Exception { final TipProducingDataTree dataTree = getDataTree(); addTopContainer(dataTree); DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); YangInstanceIdentifier listId = YangInstanceIdentifier.create( new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME), new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME)); MapNode mapNode = getNestedList("name1", "text"); dataTreeModification.write(listId, mapNode); dataTreeModification.ready(); dataTree.validate(dataTreeModification); DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification); dataTree.commit(prepare); dataTreeSnapshot = dataTree.takeSnapshot(); dataTreeModification = dataTreeSnapshot.newModification(); final YangInstanceIdentifier.NodeIdentifierWithPredicates nestedListNodeId = new YangInstanceIdentifier.NodeIdentifierWithPredicates(NESTED_LIST_QNAME, NAME_LEAF_QNAME, "name1"); listId = YangInstanceIdentifier.create( new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME), new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME), nestedListNodeId); final YangInstanceIdentifier deepListId = listId.node(new YangInstanceIdentifier.NodeIdentifier(DEEP_LIST_QNAME)); final YangInstanceIdentifier deepListEntryId = deepListId.node( new YangInstanceIdentifier.NodeIdentifierWithPredicates(DEEP_LIST_QNAME, NAME_LEAF_QNAME,"name1")); final MapEntryNode deepListEntry = getDeepList("name1").getValue().iterator().next(); // Merge parent list, just to see no modifications on it dataTreeModification.merge( listId, Builders.mapEntryBuilder().withNodeIdentifier(nestedListNodeId).withChild(ImmutableNodes.leafNode(NAME_LEAF_QNAME, "name1")).build()); dataTreeModification.merge( deepListId, Builders.mapBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(DEEP_LIST_QNAME)).build()); dataTreeModification.merge( deepListEntryId, deepListEntry); dataTreeModification.ready(); dataTree.validate(dataTreeModification); prepare = dataTree.prepare(dataTreeModification); dataTree.commit(prepare); final ModifiableDataTreeDelegator.ModificationDiff modificationDiff = ModifiableDataTreeDelegator.ModificationDiff .recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, prepare.getRootNode()); assertTrue(modificationDiff.getModificationsBefore().isEmpty()); assertAfter(getDeepList("name1"), deepListId, modificationDiff); } @Test public void testDeleteInnerListItem() throws Exception { final TipProducingDataTree dataTree = getDataTree(); addTopContainer(dataTree); DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); final YangInstanceIdentifier listId = YangInstanceIdentifier.create( new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME), new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME)); final MapNode mapNode = getNestedList("name1", "text"); dataTreeModification.write(listId, mapNode); dataTreeModification.ready(); dataTree.validate(dataTreeModification); DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification); // Commit so that update can be tested next dataTree.commit(prepare); YangInstanceIdentifier listItemId = listId.node( new YangInstanceIdentifier.NodeIdentifierWithPredicates(NESTED_LIST_QNAME, NAME_LEAF_QNAME, "name1")); dataTreeSnapshot = dataTree.takeSnapshot(); dataTreeModification = dataTreeSnapshot.newModification(); dataTreeModification.delete(listItemId); dataTreeModification.ready(); dataTree.validate(dataTreeModification); prepare = dataTree.prepare(dataTreeModification); final ModifiableDataTreeDelegator.ModificationDiff modificationDiff = ModifiableDataTreeDelegator.ModificationDiff .recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, prepare.getRootNode()); assertBefore(mapNode.getValue().iterator().next(), listItemId, modificationDiff); assertTrue(modificationDiff.getModificationsAfter().isEmpty()); } private NormalizedNode addTopContainer(final TipProducingDataTree dataTree) throws DataValidationFailedException { DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); final NormalizedNode topContainerBefore = getTopContainer("string1"); dataTreeModification.write(topContainerId, topContainerBefore); dataTreeModification.ready(); dataTree.validate(dataTreeModification); DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification); dataTree.commit(prepare); return topContainerBefore; } private void assertAfter(final NormalizedNode topContainer, final YangInstanceIdentifier topContainerId, final ModifiableDataTreeDelegator.ModificationDiff modificationDiff) { assertModification(topContainer, topContainerId, modificationDiff.getModificationsAfter()); } private void assertModification(final NormalizedNode topContainer, final YangInstanceIdentifier topContainerId, final Map> modificationMap) { assertEquals(1, modificationMap.keySet().size()); assertEquals(topContainerId, modificationMap.keySet().iterator().next()); assertEquals(topContainer, modificationMap.values().iterator().next()); } private void assertBefore(final NormalizedNode topContainerBefore, final YangInstanceIdentifier topContainerId, final ModifiableDataTreeDelegator.ModificationDiff modificationDiff) { assertModification(topContainerBefore, topContainerId, modificationDiff.getModificationsBefore()); } private TipProducingDataTree getDataTree() throws ReactorException { final TipProducingDataTree dataTree = InMemoryDataTreeFactory.getInstance().create(TreeType.CONFIGURATION); dataTree.setSchemaContext(getSchemaCtx()); return dataTree; } private ContainerNode getTopContainer(final String stringValue) { return Builders.containerBuilder() .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME)) .withChild(ImmutableNodes.leafNode(STRING_LEAF_QNAME, stringValue)) .build(); } private MapNode getNestedList(final String listItemName, final String text) { return Builders.mapBuilder() .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME)) .withChild( Builders.mapEntryBuilder() .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifierWithPredicates(NESTED_LIST_QNAME, NAME_LEAF_QNAME, listItemName)) .withChild(ImmutableNodes.leafNode(NAME_LEAF_QNAME, listItemName)) .withChild(ImmutableNodes.leafNode(TEXT_LEAF_QNAME, text)) .build() ) .build(); } private MapNode getDeepList(final String listItemName) { return Builders.mapBuilder() .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(DEEP_LIST_QNAME)) .withChild( Builders.mapEntryBuilder() .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifierWithPredicates(DEEP_LIST_QNAME, NAME_LEAF_QNAME, listItemName)) .withChild(ImmutableNodes.leafNode(NAME_LEAF_QNAME, listItemName)) .build() ) .build(); } private SchemaContext getSchemaCtx() throws ReactorException { final CrossSourceStatementReactor.BuildAction buildAction = YangInferencePipeline.RFC6020_REACTOR.newBuild(); buildAction.addSource(new YangStatementSourceImpl(getClass().getResourceAsStream("/test-diff.yang"))); return buildAction.buildEffective(); } }