Enforce case uniqueness 12/28712/14
authorRobert Varga <rovarga@cisco.com>
Thu, 22 Oct 2015 20:41:47 +0000 (22:41 +0200)
committerGerrit Code Review <gerrit@opendaylight.org>
Thu, 7 Jan 2016 09:33:29 +0000 (09:33 +0000)
Validate case leaf exclusion. RFC6020 requires that at most once case
inside a choice is present at any time. This means we must not allow
leaves which belong to different case statements exist at the same point
in time.

Change-Id: If8235ffc4aff87fd96d5071158b7e794ef8c3c5e
Signed-off-by: Robert Varga <rovarga@cisco.com>
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractNodeContainerModificationStrategy.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/CaseEnforcer.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ChoiceModificationStrategy.java
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/CaseExclusionTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ConfigStatementValidationTest.java
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/RetestModel.java
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Retest_ConfigStatementValidationTest.java
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/TestModel.java
yang/yang-data-impl/src/test/resources/case-exclusion-test.yang [new file with mode: 0644]

index 3b0b586b12c9d5d2107667392b2a0fea6b7c246f..b7f2f6003c385d69429c83d69c8e07e28ec60667 100644 (file)
@@ -8,6 +8,7 @@
 package org.opendaylight.yangtools.yang.data.impl.schema.tree;
 
 import static com.google.common.base.Preconditions.checkArgument;
+
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import java.util.Collection;
@@ -213,6 +214,10 @@ abstract class AbstractNodeContainerModificationStrategy extends SchemaAwareAppl
         }
     }
 
+    protected boolean verifyChildrenStructure() {
+        return verifyChildrenStructure;
+    }
+
     @SuppressWarnings("rawtypes")
     protected abstract NormalizedNodeContainerBuilder createBuilder(NormalizedNode<?, ?> original);
 }
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/CaseEnforcer.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/CaseEnforcer.java
new file mode 100644 (file)
index 0000000..62a5f83
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.impl.schema.tree;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+final class CaseEnforcer implements Immutable {
+    private final Map<NodeIdentifier, DataSchemaNode> children;
+
+    private CaseEnforcer(final Map<NodeIdentifier, DataSchemaNode> children) {
+        this.children = Preconditions.checkNotNull(children);
+    }
+
+    static CaseEnforcer forTree(final ChoiceCaseNode schema, final TreeType treeType) {
+        final Builder<NodeIdentifier, DataSchemaNode> builder = ImmutableMap.builder();
+        if (SchemaAwareApplyOperation.belongsToTree(treeType, schema)) {
+            for (DataSchemaNode child : schema.getChildNodes()) {
+                if (SchemaAwareApplyOperation.belongsToTree(treeType, child)) {
+                    builder.put(NodeIdentifier.create(child.getQName()), child);
+                }
+            }
+        }
+
+        final Map<NodeIdentifier, DataSchemaNode> children = builder.build();
+        return children.isEmpty() ? null : new CaseEnforcer(children);
+    }
+
+    Set<Entry<NodeIdentifier, DataSchemaNode>> getChildEntries() {
+        return children.entrySet();
+    }
+
+    Set<NodeIdentifier> getChildIdentifiers() {
+        return children.keySet();
+    }
+}
index 47f1e66b4e33527fbf556c7d388b7538c6753e43..3a0d14a72d6429e00aa1d688e47f1a15af70cbf3 100644 (file)
@@ -8,14 +8,27 @@
 package org.opendaylight.yangtools.yang.data.impl.schema.tree;
 
 import static com.google.common.base.Preconditions.checkArgument;
+
 import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicates;
+import com.google.common.base.Verify;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import java.util.Collection;
 import java.util.Map;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import java.util.Map.Entry;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.Version;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableChoiceNodeBuilder;
 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
@@ -23,27 +36,38 @@ import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 
 final class ChoiceModificationStrategy extends AbstractNodeContainerModificationStrategy {
-    private final Map<YangInstanceIdentifier.PathArgument, ModificationApplyOperation> childNodes;
+    private final Map<PathArgument, ModificationApplyOperation> childNodes;
+    // FIXME: enforce leaves not coming from two case statements at the same time
+    private final Map<CaseEnforcer, Collection<CaseEnforcer>> exclusions;
+    private final Map<PathArgument, CaseEnforcer> caseEnforcers;
 
     ChoiceModificationStrategy(final ChoiceSchemaNode schemaNode, final TreeType treeType) {
         super(ChoiceNode.class, treeType);
-        final ImmutableMap.Builder<YangInstanceIdentifier.PathArgument, ModificationApplyOperation> child = ImmutableMap.builder();
 
+        final Builder<PathArgument, ModificationApplyOperation> childBuilder = ImmutableMap.builder();
+        final Builder<PathArgument, CaseEnforcer> enforcerBuilder = ImmutableMap.builder();
         for (final ChoiceCaseNode caze : schemaNode.getCases()) {
-            if (SchemaAwareApplyOperation.belongsToTree(treeType,caze)) {
-                for (final DataSchemaNode cazeChild : caze.getChildNodes()) {
-                    if (SchemaAwareApplyOperation.belongsToTree(treeType,cazeChild)) {
-                        final ModificationApplyOperation childNode = SchemaAwareApplyOperation.from(cazeChild, treeType);
-                        child.put(NodeIdentifier.create(cazeChild.getQName()), childNode);
-                    }
+            final CaseEnforcer enforcer = CaseEnforcer.forTree(caze, treeType);
+            if (enforcer != null) {
+                for (final Entry<NodeIdentifier, DataSchemaNode> e : enforcer.getChildEntries()) {
+                    childBuilder.put(e.getKey(), SchemaAwareApplyOperation.from(e.getValue(), treeType));
+                    enforcerBuilder.put(e.getKey(), enforcer);
                 }
             }
         }
-        childNodes = child.build();
+        childNodes = childBuilder.build();
+        caseEnforcers = enforcerBuilder.build();
+
+        final Builder<CaseEnforcer, Collection<CaseEnforcer>> exclusionsBuilder = ImmutableMap.builder();
+        for (CaseEnforcer e : caseEnforcers.values()) {
+            exclusionsBuilder.put(e, ImmutableList.copyOf(
+                Collections2.filter(caseEnforcers.values(), Predicates.not(Predicates.equalTo(e)))));
+        }
+        exclusions = exclusionsBuilder.build();
     }
 
     @Override
-    public Optional<ModificationApplyOperation> getChild(final YangInstanceIdentifier.PathArgument child) {
+    public Optional<ModificationApplyOperation> getChild(final PathArgument child) {
         return Optional.fromNullable(childNodes.get(child));
     }
 
@@ -53,4 +77,58 @@ final class ChoiceModificationStrategy extends AbstractNodeContainerModification
         checkArgument(original instanceof ChoiceNode);
         return ImmutableChoiceNodeBuilder.create((ChoiceNode) original);
     }
+
+    @Override
+    void verifyStructure(final NormalizedNode<?, ?> writtenValue, final boolean verifyChildren) {
+        if(verifyChildrenStructure() && verifyChildren) {
+            enforceCases(writtenValue);
+        }
+        super.verifyStructure(writtenValue, verifyChildren);
+    }
+
+    private void enforceCases(final TreeNode tree) {
+        enforceCases(tree.getData());
+    }
+
+    private void enforceCases(final NormalizedNode<?, ?> normalizedNode) {
+        Verify.verify(normalizedNode instanceof ChoiceNode);
+        final Collection<DataContainerChild<?, ?>> children = ((ChoiceNode) normalizedNode).getValue();
+        if (!children.isEmpty()) {
+            final DataContainerChild<?, ?> firstChild = children.iterator().next();
+            final CaseEnforcer enforcer = caseEnforcers.get(firstChild.getIdentifier());
+            Verify.verifyNotNull(enforcer, "Case enforcer cannot be null. Most probably, child node %s of choice node %s does not belong in current tree type.", firstChild.getIdentifier(), normalizedNode.getIdentifier());
+
+            // Make sure no leaves from other cases are present
+            for (CaseEnforcer other : exclusions.get(enforcer)) {
+                for (NodeIdentifier id : other.getChildIdentifiers()) {
+                    final Optional<NormalizedNode<?, ?>> maybeChild = NormalizedNodes.getDirectChild(normalizedNode, id);
+                    Preconditions.checkArgument(!maybeChild.isPresent(),
+                        "Child %s (from case %s) implies non-presence of child %s (from case %s), which is %s",
+                        firstChild.getIdentifier(), enforcer, id, other, maybeChild.orNull());
+                }
+            }
+        }
+    }
+
+    @Override
+    protected TreeNode applyMerge(final ModifiedNode modification, final TreeNode currentMeta, final Version version) {
+        final TreeNode ret = super.applyMerge(modification, currentMeta, version);
+        enforceCases(ret);
+        return ret;
+    }
+
+    @Override
+    protected TreeNode applyWrite(final ModifiedNode modification, final Optional<TreeNode> currentMeta,
+            final Version version) {
+        final TreeNode ret = super.applyWrite(modification, currentMeta, version);
+        enforceCases(ret);
+        return ret;
+    }
+
+    @Override
+    protected TreeNode applyTouch(final ModifiedNode modification, final TreeNode currentMeta, final Version version) {
+        final TreeNode ret = super.applyTouch(modification, currentMeta, version);
+        enforceCases(ret);
+        return ret;
+    }
 }
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/CaseExclusionTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/CaseExclusionTest.java
new file mode 100644 (file)
index 0000000..cec5bbb
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.impl.schema.tree;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.leafNode;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+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.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+
+public class CaseExclusionTest {
+
+    private SchemaContext schemaContext;
+
+    @Before
+    public void prepare() throws ReactorException {
+        schemaContext = RetestModel.createTestContext("/case-exclusion-test.yang");
+        assertNotNull("Schema context must not be null.", schemaContext);
+    }
+
+    private InMemoryDataTree initDataTree() {
+        InMemoryDataTree inMemoryDataTree = (InMemoryDataTree) InMemoryDataTreeFactory.getInstance().create(
+                TreeType.CONFIGURATION);
+        inMemoryDataTree.setSchemaContext(schemaContext);
+        return inMemoryDataTree;
+    }
+
+    @Test
+    public void testCorrectCaseWrite() throws DataValidationFailedException {
+        final InMemoryDataTree inMemoryDataTree = initDataTree();
+        final NodeIdentifier choice1Id = new NodeIdentifier(QName.create(TestModel.TEST_QNAME, "choice1"));
+
+        final ContainerNode container = Builders
+                .containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
+                .withChild(
+                        Builders.choiceBuilder().withNodeIdentifier(choice1Id)
+                                .withChild(leafNode(QName.create(TestModel.TEST_QNAME, "case1-leaf1"), "leaf-value"))
+                                .build()).build();
+        final InMemoryDataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.write(TestModel.TEST_PATH, container);
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCaseExclusion() throws DataValidationFailedException {
+        final InMemoryDataTree inMemoryDataTree = initDataTree();
+        final NodeIdentifier choice1Id = new NodeIdentifier(QName.create(TestModel.TEST_QNAME, "choice1"));
+
+        final ContainerNode container = Builders
+                .containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
+                .withChild(
+                        Builders.choiceBuilder()
+                                .withNodeIdentifier(choice1Id)
+                                .withChild(leafNode(QName.create(TestModel.TEST_QNAME, "case1-leaf1"), "leaf-value"))
+                                .withChild(
+                                        ImmutableNodes.containerNode(QName.create(TestModel.TEST_QNAME, "case2-cont")))
+                                .build()).build();
+        try {
+            final InMemoryDataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+            modificationTree.write(TestModel.TEST_PATH, container);
+            modificationTree.ready();
+
+            inMemoryDataTree.validate(modificationTree);
+            final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+            inMemoryDataTree.commit(prepare);
+        } catch (IllegalArgumentException e) {
+            assertTrue(e.getMessage().contains("implies non-presence of child"));
+            throw e;
+        }
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCaseExclusionOnChoiceWrite() throws DataValidationFailedException {
+        final InMemoryDataTree inMemoryDataTree = initDataTree();
+        // Container write
+        final ContainerNode container = Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME)).build();
+
+        final InMemoryDataTreeModification modificationTree1 = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree1.write(TestModel.TEST_PATH, container);
+        modificationTree1.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        // Choice write
+        final NodeIdentifier choice1Id = new NodeIdentifier(QName.create(TestModel.TEST_QNAME, "choice1"));
+        final ChoiceNode choice = Builders.choiceBuilder().withNodeIdentifier(choice1Id)
+                .withChild(leafNode(QName.create(TestModel.TEST_QNAME, "case1-leaf1"), "leaf-value"))
+                .withChild(ImmutableNodes.containerNode(QName.create(TestModel.TEST_QNAME, "case2-cont"))).build();
+
+        try {
+            final InMemoryDataTreeModification modificationTree2 = inMemoryDataTree.takeSnapshot().newModification();
+            modificationTree2.write(TestModel.TEST_PATH.node(choice1Id), choice);
+            modificationTree2.ready();
+
+            inMemoryDataTree.validate(modificationTree2);
+
+            final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+            inMemoryDataTree.commit(prepare2);
+        } catch (IllegalArgumentException e) {
+            assertTrue(e.getMessage().contains("implies non-presence of child"));
+            throw e;
+        }
+    }
+}
index 072f45ea62b88d0d9aee5e6cc0f5d56b59b20f65..f35ad36e1a136a76da145cac9dd0a6210945e075 100644 (file)
@@ -11,6 +11,8 @@ import static org.junit.Assert.assertNotNull;
 import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.leafNode;
 import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntryBuilder;
 import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapNodeBuilder;
+
+import com.google.common.base.VerifyException;
 import org.junit.Before;
 import org.junit.Test;
 import org.opendaylight.yangtools.yang.common.QName;
@@ -148,7 +150,7 @@ public class ConfigStatementValidationTest {
         inMemoryDataTree.commit(prepare);
     }
 
-    @Test(expected=SchemaValidationFailedException.class)
+    @Test(expected=VerifyException.class)
     public void testOnDataCaseLeafFail() throws DataValidationFailedException {
         final InMemoryDataTree inMemoryDataTree =
                 (InMemoryDataTree) InMemoryDataTreeFactory.getInstance().create(TreeType.CONFIGURATION);
index 756abb078505c4acbae7c2645a8ade6611adc392..9f31295f6dffe52fe6518c7d82640a459ef70fa7 100644 (file)
@@ -40,4 +40,9 @@ public class RetestModel {
     public static SchemaContext createTestContext() throws ReactorException {
         return RetestUtils.parseYangStreams(Arrays.asList(getDatastoreTestInputStream()));
     }
+
+    public static SchemaContext createTestContext(String resourcePath) throws ReactorException {
+        InputStream yangStream = RetestModel.class.getResourceAsStream(resourcePath);
+        return RetestUtils.parseYangStreams(Arrays.asList(yangStream));
+    }
 }
index 6bb03a55fa39cb0e604d8ac642032dba55c02f3f..383857ceb74037d11eab7a61c23e000f90d69b30 100644 (file)
@@ -13,6 +13,7 @@ import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.le
 import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntryBuilder;
 import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapNodeBuilder;
 
+import com.google.common.base.VerifyException;
 import org.junit.Before;
 import org.junit.Test;
 import org.opendaylight.yangtools.yang.common.QName;
@@ -38,20 +39,20 @@ public class Retest_ConfigStatementValidationTest {
     private static final Short ONE_ID = 1;
     private static final Short TWO_ID = 2;
 
-    private static final YangInstanceIdentifier OUTER_LIST_1_PATH = YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH)
-            .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, ONE_ID) //
+    private static final YangInstanceIdentifier OUTER_LIST_1_PATH = YangInstanceIdentifier
+            .builder(TestModel.OUTER_LIST_PATH).nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, ONE_ID) //
             .build();
 
-    private static final YangInstanceIdentifier OUTER_LIST_2_PATH = YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH)
-            .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, TWO_ID) //
+    private static final YangInstanceIdentifier OUTER_LIST_2_PATH = YangInstanceIdentifier
+            .builder(TestModel.OUTER_LIST_PATH).nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, TWO_ID) //
             .build();
 
-    private static final MapEntryNode INNER_FOO_ENTRY_NODE =
-            ImmutableNodes.mapEntry(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, "foo");
+    private static final MapEntryNode INNER_FOO_ENTRY_NODE = ImmutableNodes.mapEntry(TestModel.INNER_LIST_QNAME,
+            TestModel.NAME_QNAME, "foo");
 
-    private static final MapEntryNode INNER_BAR_ENTRY_NODE =
-            ImmutableNodes.mapEntryBuilder(QName.create(TestModel.TEST_QNAME, "inner-list2"), TestModel.NAME_QNAME, "foo")
-                    .withChild(ImmutableNodes.leafNode(TestModel.VALUE_QNAME, "value")).build();
+    private static final MapEntryNode INNER_BAR_ENTRY_NODE = ImmutableNodes
+            .mapEntryBuilder(QName.create(TestModel.TEST_QNAME, "inner-list2"), TestModel.NAME_QNAME, "foo")
+            .withChild(ImmutableNodes.leafNode(TestModel.VALUE_QNAME, "value")).build();
 
     private static final MapEntryNode FOO_NODE = mapEntryBuilder(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, ONE_ID) //
             .withChild(mapNodeBuilder(TestModel.INNER_LIST_QNAME).withChild(INNER_FOO_ENTRY_NODE) //
@@ -66,21 +67,15 @@ public class Retest_ConfigStatementValidationTest {
     private SchemaContext schemaContext;
 
     private static ContainerNode createFooTestContainerNode() {
-        return ImmutableContainerNodeBuilder
-                .create()
+        return ImmutableContainerNodeBuilder.create()
                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
-                .withChild(
-                        mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
-                                .withChild(FOO_NODE).build()).build();
+                .withChild(mapNodeBuilder(TestModel.OUTER_LIST_QNAME).withChild(FOO_NODE).build()).build();
     }
 
     private static ContainerNode createBarTestContainerNode() {
-        return ImmutableContainerNodeBuilder
-                .create()
+        return ImmutableContainerNodeBuilder.create()
                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
-                .withChild(
-                        mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
-                                .withChild(BAR_NODE).build()).build();
+                .withChild(mapNodeBuilder(TestModel.OUTER_LIST_QNAME).withChild(BAR_NODE).build()).build();
     }
 
     @Before
@@ -89,14 +84,15 @@ public class Retest_ConfigStatementValidationTest {
         assertNotNull("Schema context must not be null.", schemaContext);
     }
 
-    @Test(expected=SchemaValidationFailedException.class)
+    @Test(expected = SchemaValidationFailedException.class)
     public void testOnPathFail() throws DataValidationFailedException {
-        final InMemoryDataTree inMemoryDataTree =
-                (InMemoryDataTree) InMemoryDataTreeFactory.getInstance().create(TreeType.CONFIGURATION);
+        final InMemoryDataTree inMemoryDataTree = (InMemoryDataTree) InMemoryDataTreeFactory.getInstance().create(
+                TreeType.CONFIGURATION);
         inMemoryDataTree.setSchemaContext(schemaContext);
         final InMemoryDataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
-        final YangInstanceIdentifier ii = OUTER_LIST_1_PATH.node(new YangInstanceIdentifier.NodeIdentifier(TestModel.INNER_LIST_QNAME))
-                .node(INNER_FOO_ENTRY_NODE.getIdentifier());
+        final YangInstanceIdentifier ii = OUTER_LIST_1_PATH.node(
+                new YangInstanceIdentifier.NodeIdentifier(TestModel.INNER_LIST_QNAME)).node(
+                INNER_FOO_ENTRY_NODE.getIdentifier());
         modificationTree.write(ii, INNER_FOO_ENTRY_NODE);
 
         inMemoryDataTree.validate(modificationTree);
@@ -104,10 +100,10 @@ public class Retest_ConfigStatementValidationTest {
         inMemoryDataTree.commit(prepare);
     }
 
-    @Test(expected=SchemaValidationFailedException.class)
+    @Test(expected = SchemaValidationFailedException.class)
     public void testOnDataFail() throws DataValidationFailedException {
-        final InMemoryDataTree inMemoryDataTree =
-                (InMemoryDataTree) InMemoryDataTreeFactory.getInstance().create(TreeType.CONFIGURATION);
+        final InMemoryDataTree inMemoryDataTree = (InMemoryDataTree) InMemoryDataTreeFactory.getInstance().create(
+                TreeType.CONFIGURATION);
         inMemoryDataTree.setSchemaContext(schemaContext);
         final InMemoryDataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
         modificationTree.write(TestModel.TEST_PATH, createFooTestContainerNode());
@@ -117,10 +113,10 @@ public class Retest_ConfigStatementValidationTest {
         inMemoryDataTree.commit(prepare);
     }
 
-    @Test(expected=SchemaValidationFailedException.class)
+    @Test(expected = SchemaValidationFailedException.class)
     public void testOnDataLeafFail() throws DataValidationFailedException {
-        final InMemoryDataTree inMemoryDataTree =
-                (InMemoryDataTree) InMemoryDataTreeFactory.getInstance().create(TreeType.CONFIGURATION);
+        final InMemoryDataTree inMemoryDataTree = (InMemoryDataTree) InMemoryDataTreeFactory.getInstance().create(
+                TreeType.CONFIGURATION);
         inMemoryDataTree.setSchemaContext(schemaContext);
         final InMemoryDataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
         modificationTree.write(TestModel.TEST_PATH, createBarTestContainerNode());
@@ -130,13 +126,13 @@ public class Retest_ConfigStatementValidationTest {
         inMemoryDataTree.commit(prepare);
     }
 
-    @Test(expected=SchemaValidationFailedException.class)
+    @Test(expected = SchemaValidationFailedException.class)
     public void testOnPathCaseLeafFail() throws DataValidationFailedException {
-        final InMemoryDataTree inMemoryDataTree =
-                (InMemoryDataTree) InMemoryDataTreeFactory.getInstance().create(TreeType.CONFIGURATION);
+        final InMemoryDataTree inMemoryDataTree = (InMemoryDataTree) InMemoryDataTreeFactory.getInstance().create(
+                TreeType.CONFIGURATION);
         inMemoryDataTree.setSchemaContext(schemaContext);
-        final YangInstanceIdentifier.NodeIdentifier choice1Id = new YangInstanceIdentifier.NodeIdentifier(
-                QName.create(TestModel.TEST_QNAME, "choice1"));
+        final YangInstanceIdentifier.NodeIdentifier choice1Id = new YangInstanceIdentifier.NodeIdentifier(QName.create(
+                TestModel.TEST_QNAME, "choice1"));
         final YangInstanceIdentifier.NodeIdentifier case2ContId = new YangInstanceIdentifier.NodeIdentifier(
                 QName.create(TestModel.TEST_QNAME, "case2-cont"));
         final YangInstanceIdentifier ii = TestModel.TEST_PATH.node(choice1Id).node(case2ContId);
@@ -151,13 +147,13 @@ public class Retest_ConfigStatementValidationTest {
         inMemoryDataTree.commit(prepare);
     }
 
-    @Test(expected=SchemaValidationFailedException.class)
+    @Test(expected = VerifyException.class)
     public void testOnDataCaseLeafFail() throws DataValidationFailedException {
-        final InMemoryDataTree inMemoryDataTree =
-                (InMemoryDataTree) InMemoryDataTreeFactory.getInstance().create(TreeType.CONFIGURATION);
+        final InMemoryDataTree inMemoryDataTree = (InMemoryDataTree) InMemoryDataTreeFactory.getInstance().create(
+                TreeType.CONFIGURATION);
         inMemoryDataTree.setSchemaContext(schemaContext);
-        final YangInstanceIdentifier.NodeIdentifier choice1Id = new YangInstanceIdentifier.NodeIdentifier(
-                QName.create(TestModel.TEST_QNAME, "choice1"));
+        final YangInstanceIdentifier.NodeIdentifier choice1Id = new YangInstanceIdentifier.NodeIdentifier(QName.create(
+                TestModel.TEST_QNAME, "choice1"));
         final YangInstanceIdentifier ii = TestModel.TEST_PATH.node(choice1Id);
         final ChoiceNode choice1 = Builders.choiceBuilder().withNodeIdentifier(choice1Id)
                 .withChild(leafNode(QName.create(TestModel.TEST_QNAME, "case1-leaf1"), "leaf-value")).build();
index b21dfc03f30c99beb4f418c99974cd303094b3b8..9dd298b14cb7f8090731f7fd47b966b899918572 100644 (file)
@@ -43,9 +43,14 @@ public final class TestModel {
     }
 
     public static SchemaContext createTestContext() {
+        return createTestContext(DATASTORE_TEST_YANG);
+    }
+
+    public static SchemaContext createTestContext(String resourcePath) {
         YangParserImpl parser = new YangParserImpl();
         try {
-            return parser.parseSources(Collections.singleton(Resources.asByteSource(TestModel.class.getResource(DATASTORE_TEST_YANG))));
+            return parser.parseSources(Collections.singleton(Resources.asByteSource(TestModel.class
+                    .getResource(resourcePath))));
         } catch (IOException | YangSyntaxErrorException e) {
             throw new IllegalStateException("Failed to create context", e);
         }
diff --git a/yang/yang-data-impl/src/test/resources/case-exclusion-test.yang b/yang/yang-data-impl/src/test/resources/case-exclusion-test.yang
new file mode 100644 (file)
index 0000000..e006ad2
--- /dev/null
@@ -0,0 +1,70 @@
+module case-exclusion-test {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test";
+    prefix "store-test";
+
+    revision "2014-03-13" {
+        description "Initial revision.";
+    }
+
+    container test {
+        presence true;
+        config true;
+        choice choice1 {
+            case case1 {
+                leaf case1-leaf1 {
+                    type string;
+                }
+            }
+            case case2 {
+                container case2-cont {
+                    leaf case2-leaf1 {
+                        type string;
+                    }
+                }
+            }
+        }
+
+        list outer-list {
+
+            key id;
+            leaf id {
+                type uint16;
+            }
+            choice outer-choice {
+                case one {
+                    leaf one {
+                        type string;
+                    }
+                }
+                case two-three {
+                    leaf two {
+                        type string;
+                    }
+                    leaf three {
+                        type string;
+                    }
+                }
+            }
+            list inner-list {
+                key name;
+                leaf name {
+                    type string;
+                }
+                leaf value {
+                    type string;
+                }
+            }
+
+            list inner-list2 {
+                key name;
+                leaf name {
+                    type string;
+                }
+                leaf value {
+                    type string;
+                }
+            }
+        }
+    }
+}