YANG XPath functions - unit tests and bugfix 42/55842/1
authorIgor Foltin <ifoltin@cisco.com>
Fri, 21 Apr 2017 15:06:37 +0000 (17:06 +0200)
committerRobert Varga <nite@hq.sk>
Sat, 22 Apr 2017 11:12:34 +0000 (11:12 +0000)
Add more unit tests for YANG XPath functions
in yang-data-jaxen.

Fix deref function for leafrefs that reference
a leaf-list node.

Change-Id: I7bfe90a3a1a101b95a3b8ea3b1dd42963e5ea264
Signed-off-by: Igor Foltin <ifoltin@cisco.com>
(cherry picked from commit 8539503c13746b5b765c9aa9d249a85462f69d7f)

yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/YangFunctionContext.java
yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/BitIsSetXPathFunctionTest.java [new file with mode: 0644]
yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/DerefXPathFunctionTest.java [new file with mode: 0644]
yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/DerivedFromXPathFunctionTest.java [new file with mode: 0644]
yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/EnumValueXPathFunctionTest.java [new file with mode: 0644]
yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/ReMatchXPathFunctionTest.java [new file with mode: 0644]
yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/YangXPathFunctionsTest.java [deleted file]
yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/bit-is-set-function/foo-invalid.yang [new file with mode: 0644]
yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/deref-function-leafref/foo.yang
yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/derived-from-function/bar-invalid.yang [new file with mode: 0644]
yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/enum-value-function/foo-invalid.yang [new file with mode: 0644]

index a335a5099f4d2e3dbdcb9acb4d4fa82bc5944548..429697d2f73ebbb718f06979e17404ffebb4b2cf 100644 (file)
@@ -29,6 +29,8 @@ import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
@@ -113,19 +115,7 @@ final class YangFunctionContext implements FunctionContext {
         if (correspondingSchemaNode.getType() instanceof LeafrefTypeDefinition) {
             final LeafrefTypeDefinition leafrefType = (LeafrefTypeDefinition) correspondingSchemaNode.getType();
             final RevisionAwareXPath xPath = leafrefType.getPathStatement();
-            if (xPath.isAbsolute()) {
-                final NormalizedNode<?, ?> referencedNode = getNodeReferencedByAbsoluteLeafref(
-                        xPath, currentNodeContext, schemaContext, correspondingSchemaNode);
-                if (referencedNode.getValue().equals(nodeValue)) {
-                    return referencedNode;
-                }
-            } else {
-                final NormalizedNode<?, ?> referencedNode = getNodeReferencedByRelativeLeafref(
-                        xPath, currentNodeContext, schemaContext, correspondingSchemaNode);
-                if (referencedNode.getValue().equals(nodeValue)) {
-                    return referencedNode;
-                }
-            }
+            return getNodeReferencedByLeafref(xPath, currentNodeContext, schemaContext, correspondingSchemaNode, nodeValue);
         }
 
         return null;
@@ -142,8 +132,24 @@ final class YangFunctionContext implements FunctionContext {
             if (possibleNode.isPresent()) {
                 return possibleNode.get();
             }
+        }
+
+        return null;
+    }
+
+    private static NormalizedNode<?, ?> getNodeReferencedByLeafref(final RevisionAwareXPath xPath,
+            final NormalizedNodeContext currentNodeContext, final SchemaContext schemaContext,
+            final TypedSchemaNode correspondingSchemaNode, final Object nodeValue) {
+        final NormalizedNode<?, ?> referencedNode = xPath.isAbsolute() ? getNodeReferencedByAbsoluteLeafref(xPath,
+                currentNodeContext, schemaContext, correspondingSchemaNode) : getNodeReferencedByRelativeLeafref(xPath,
+                currentNodeContext, schemaContext, correspondingSchemaNode);
+
+        if (referencedNode instanceof LeafSetNode) {
+            return getReferencedLeafSetEntryNode((LeafSetNode<?>) referencedNode, nodeValue);
+        }
 
-            return null;
+        if (referencedNode instanceof LeafNode && referencedNode.getValue().equals(nodeValue)) {
+            return referencedNode;
         }
 
         return null;
@@ -163,8 +169,6 @@ final class YangFunctionContext implements FunctionContext {
             if (possibleNode.isPresent()) {
                 return possibleNode.get();
             }
-
-            return null;
         }
 
         return null;
@@ -195,6 +199,17 @@ final class YangFunctionContext implements FunctionContext {
         return null;
     }
 
+    private static LeafSetEntryNode<?> getReferencedLeafSetEntryNode(final LeafSetNode<?> referencedNode,
+            final Object currentNodeValue) {
+        for (final LeafSetEntryNode<?> entryNode : referencedNode.getValue()) {
+            if (currentNodeValue.equals(entryNode.getValue())) {
+                return entryNode;
+            }
+        }
+
+        return null;
+    }
+
     // derived-from(node-set nodes, string identity) function as per https://tools.ietf.org/html/rfc7950#section-10.4.1
     private static final Function DERIVED_FROM_FUNCTION = (context, args) -> {
         if (args == null || args.size() != 1) {
@@ -313,7 +328,8 @@ final class YangFunctionContext implements FunctionContext {
                 }
             }
 
-            throw new IllegalArgumentException("Cannot resolve prefix '%s' from identity '%s'.");
+            throw new IllegalArgumentException(String.format("Cannot resolve prefix '%s' from identity '%s'.",
+                    identityPrefixAndName.get(0), identity));
         }
 
         if (identityPrefixAndName.size() == 1) { // without prefix
@@ -335,8 +351,6 @@ final class YangFunctionContext implements FunctionContext {
                 " identity schema node in the module %s.", identityQName, module));
     }
 
-
-
     // enum-value(node-set nodes) function as per https://tools.ietf.org/html/rfc7950#section-10.5.1
     private static final Function ENUM_VALUE_FUNCTION = (context, args) -> {
         if (!args.isEmpty()) {
@@ -350,16 +364,18 @@ final class YangFunctionContext implements FunctionContext {
         final TypedSchemaNode correspondingSchemaNode = getCorrespondingTypedSchemaNode(schemaContext,
             currentNodeContext);
 
-        if (!(correspondingSchemaNode.getType() instanceof EnumTypeDefinition)) {
+        final TypeDefinition<?> nodeType = correspondingSchemaNode.getType();
+        if (!(nodeType instanceof EnumTypeDefinition)) {
             return DOUBLE_NAN;
         }
 
-        if (!(currentNodeContext.getNode().getValue() instanceof String)) {
+        final Object nodeValue = currentNodeContext.getNode().getValue();
+        if (!(nodeValue instanceof String)) {
             return DOUBLE_NAN;
         }
 
-        final EnumTypeDefinition enumerationType = (EnumTypeDefinition) correspondingSchemaNode.getType();
-        final String enumName = (String) currentNodeContext.getNode().getValue();
+        final EnumTypeDefinition enumerationType = (EnumTypeDefinition) nodeType;
+        final String enumName = (String) nodeValue;
 
         return getEnumValue(enumerationType, enumName);
     };
diff --git a/yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/BitIsSetXPathFunctionTest.java b/yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/BitIsSetXPathFunctionTest.java
new file mode 100644 (file)
index 0000000..b654b37
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2017 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.jaxen;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import java.net.URI;
+import java.text.ParseException;
+import java.util.Set;
+import org.jaxen.Context;
+import org.jaxen.Function;
+import org.jaxen.FunctionCallException;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathDocument;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathSchemaContext;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class BitIsSetXPathFunctionTest {
+
+    private static JaxenSchemaContextFactory jaxenSchemaContextFactory;
+
+    private static QNameModule fooModule;
+    private static QName myContainer; 
+    private static QName myList;
+    private static QName flags;
+    private static QName ordinaryLeaf;
+
+    @BeforeClass
+    public static void setup() throws ParseException {
+        jaxenSchemaContextFactory = new JaxenSchemaContextFactory();
+
+        fooModule = QNameModule.create(URI.create("foo-ns"),
+                SimpleDateFormatUtil.getRevisionFormat().parse("2017-04-03"));
+        myContainer = QName.create(fooModule, "my-container");
+        myList = QName.create(fooModule, "my-list");
+        flags = QName.create(fooModule, "flags");
+        ordinaryLeaf = QName.create(fooModule, "ordinary-leaf");
+    }
+
+    @Test
+    public void testBitIsSetFunction() throws Exception {
+        final Set<String> setOfBits = ImmutableSet.of("UP", "PROMISCUOUS");
+
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangSource(
+                "/yang-xpath-functions-test/bit-is-set-function/foo.yang");
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode(setOfBits));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("foo-prefix", fooModule);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
+                buildPathToFlagsLeafNode(setOfBits));
+
+        final Function bitIsSetFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "bit-is-set");
+        boolean bitIsSetResult = (boolean) bitIsSetFunction.call(normalizedNodeContext, ImmutableList.of("UP"));
+        assertTrue(bitIsSetResult);
+        bitIsSetResult = (boolean) bitIsSetFunction.call(normalizedNodeContext, ImmutableList.of("PROMISCUOUS"));
+        assertTrue(bitIsSetResult);
+        bitIsSetResult = (boolean) bitIsSetFunction.call(normalizedNodeContext, ImmutableList.of("DISABLED"));
+        assertFalse(bitIsSetResult);
+    }
+
+    @Test
+    public void testInvalidTypeOfCorrespondingSchemaNode() throws Exception {
+        final Set<String> setOfBits = ImmutableSet.of("UP", "PROMISCUOUS");
+
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangSource(
+                "/yang-xpath-functions-test/bit-is-set-function/foo-invalid.yang");
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode(setOfBits));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("foo-prefix", fooModule);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
+                buildPathToFlagsLeafNode(setOfBits));
+
+        final Function bitIsSetFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "bit-is-set");
+        boolean bitIsSetResult = (boolean) bitIsSetFunction.call(normalizedNodeContext, ImmutableList.of("UP"));
+        assertFalse(bitIsSetResult);
+    }
+
+    @Test
+    public void testInvalidNormalizedNodeValueType() throws Exception {
+        final String invalidNodeValueType = "value of invalid type";
+
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangSource(
+                "/yang-xpath-functions-test/bit-is-set-function/foo.yang");
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode(invalidNodeValueType));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("foo-prefix", fooModule);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
+                buildPathToFlagsLeafNode(invalidNodeValueType));
+
+        final Function bitIsSetFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "bit-is-set");
+        boolean bitIsSetResult = (boolean) bitIsSetFunction.call(normalizedNodeContext, ImmutableList.of("UP"));
+        assertFalse(bitIsSetResult);
+    }
+
+    @Test
+    public void shouldFailOnUnknownBitArgument() throws Exception {
+        final Set<String> setOfBits = ImmutableSet.of("UP", "PROMISCUOUS");
+
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangSource(
+                "/yang-xpath-functions-test/bit-is-set-function/foo.yang");
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode(setOfBits));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("foo-prefix", fooModule);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
+                buildPathToFlagsLeafNode(setOfBits));
+
+        final Function bitIsSetFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "bit-is-set");
+        try {
+            bitIsSetFunction.call(normalizedNodeContext, ImmutableList.of("UNKNOWN"));
+            fail("Function call should have failed on unknown bit-name argument");
+        } catch (final IllegalStateException ex) {
+            assertTrue(ex.getMessage().startsWith("Bit UNKNOWN does not belong to bits"));
+        }
+    }
+
+    @Test
+    public void shouldFailOnInvalidNumberOfArguments() throws Exception {
+        final YangFunctionContext yangFunctionContext = YangFunctionContext.getInstance();
+        final Function bitIsSetFunction = yangFunctionContext.getFunction(null, null, "bit-is-set");
+
+        final Context mockedContext = mock(Context.class);
+
+        try {
+            bitIsSetFunction.call(mockedContext, ImmutableList.of("bit-a", "bit-b"));
+            fail("Function call should have failed on invalid number of arguments.");
+        } catch (final FunctionCallException ex) {
+            assertEquals("bit-is-set() takes two arguments: node-set nodes, string bit-name", ex.getMessage());
+        }
+    }
+
+    @Test
+    public void shouldFailOnInvalidTypeOfArgument() throws Exception {
+        final YangFunctionContext yangFunctionContext = YangFunctionContext.getInstance();
+        final Function bitIsSetFunction = yangFunctionContext.getFunction(null, null, "bit-is-set");
+
+        final Context mockedContext = mock(Context.class);
+
+        try {
+            bitIsSetFunction.call(mockedContext, ImmutableList.of(100));
+            fail("Function call should have failed on invalid type of the bit-name argument.");
+        } catch (final FunctionCallException ex) {
+            assertEquals("Argument bit-name of bit-is-set() function should be a String", ex.getMessage());
+        }
+    }
+
+    private static ContainerNode buildMyContainerNode(final Object keyLeafValue) {
+        final LeafNode<?> ordinaryLeafNode = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(ordinaryLeaf))
+                .withValue("test-value").build();
+
+        final MapNode myListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(myList))
+                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+                        new NodeIdentifierWithPredicates(myList, flags, keyLeafValue))
+                        .withChild(ordinaryLeafNode).build()).build();
+
+        final ContainerNode myContainerNode = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(myContainer)).withChild(myListNode).build();
+
+        return myContainerNode;
+    }
+
+    private static YangInstanceIdentifier buildPathToFlagsLeafNode(final Object keyLeafValue) {
+        final ImmutableMap.Builder<QName, Object> builder = ImmutableMap.builder();
+        final ImmutableMap<QName, Object> keys = builder.put(flags, keyLeafValue).build();
+
+        final YangInstanceIdentifier path = YangInstanceIdentifier.of(myList)
+                .node(new NodeIdentifierWithPredicates(myList, keys)).node(flags);
+        return path;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/DerefXPathFunctionTest.java b/yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/DerefXPathFunctionTest.java
new file mode 100644 (file)
index 0000000..539d9fc
--- /dev/null
@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) 2017 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.jaxen;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import java.net.URI;
+import java.text.ParseException;
+import java.util.Map;
+import org.jaxen.Function;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+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.xpath.XPathDocument;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathSchemaContext;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class DerefXPathFunctionTest {
+
+    private static JaxenSchemaContextFactory jaxenSchemaContextFactory;
+
+    private static QNameModule fooModule;
+    private static QName myContainer;
+    private static QName myInnerContainer;
+    private static QName myList;
+    private static QName keyLeafA;
+    private static QName keyLeafB;
+    private static QName iidLeaf;
+    private static QName referencedLeaf;
+    private static QName referencedLeafList;
+    private static QName absLeafrefLeaf;
+    private static QName relLeafrefLeaf;
+    private static QName leafListLeafrefLeaf;
+    private static QName ordinaryLeafA;
+    private static QName ordinaryLeafB;
+
+    @BeforeClass
+    public static void setup() throws ParseException {
+        jaxenSchemaContextFactory = new JaxenSchemaContextFactory();
+
+        fooModule = QNameModule.create(URI.create("foo-ns"),
+                SimpleDateFormatUtil.getRevisionFormat().parse("2017-04-03"));
+        myContainer = QName.create(fooModule, "my-container");
+        myInnerContainer = QName.create(fooModule, "my-inner-container");
+        myList = QName.create(fooModule, "my-list");
+        keyLeafA = QName.create(fooModule, "key-leaf-a");
+        keyLeafB = QName.create(fooModule, "key-leaf-b");
+        iidLeaf = QName.create(fooModule, "iid-leaf");
+        referencedLeaf = QName.create(fooModule, "referenced-leaf");
+        referencedLeafList = QName.create(fooModule, "referenced-leaf-list");
+        absLeafrefLeaf = QName.create(fooModule, "abs-leafref-leaf");
+        relLeafrefLeaf = QName.create(fooModule, "rel-leafref-leaf");
+        leafListLeafrefLeaf = QName.create(fooModule, "leaf-list-leafref-leaf");
+        ordinaryLeafA = QName.create(fooModule, "ordinary-leaf-a");
+        ordinaryLeafB = QName.create(fooModule, "ordinary-leaf-b");
+    }
+
+    @Test
+    public void testDerefFunctionForInstanceIdentifier() throws Exception {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangSource(
+                "/yang-xpath-functions-test/deref-function-iid/foo.yang");
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
+
+        final LeafNode<?> referencedLeafNode = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(referencedLeaf))
+                .withValue("referenced-leaf-node-value").build();
+
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNodeForIIdTest(referencedLeafNode));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("foo-prefix", fooModule);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
+                buildPathToIIdLeafNode());
+
+        final Function derefFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "deref");
+        final Object derefResult = derefFunction.call(normalizedNodeContext, ImmutableList.of());
+        assertNotNull(derefResult);
+        assertTrue(derefResult instanceof NormalizedNode<?, ?>);
+        assertSame(referencedLeafNode, derefResult);
+    }
+
+    @Test
+    public void testDerefFunctionForLeafref() throws Exception {
+        // tests absolute and relative leafref that references a leaf node
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangSource(
+                "/yang-xpath-functions-test/deref-function-leafref/foo.yang");
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
+
+        final LeafNode<?> referencedLeafNode = Builders.leafBuilder().withNodeIdentifier(
+                new NodeIdentifier(referencedLeaf)).withValue("referenced-leaf-node-value").build();
+
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNodeForLeafrefTest(
+                referencedLeafNode));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("foo-prefix", fooModule);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final YangInstanceIdentifier absLeafrefPath = YangInstanceIdentifier.of(myInnerContainer).node(absLeafrefLeaf);
+        NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(absLeafrefPath);
+
+        final Function derefFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "deref");
+        Object derefResult = derefFunction.call(normalizedNodeContext, ImmutableList.of());
+        assertNotNull(derefResult);
+        assertTrue(derefResult instanceof NormalizedNode<?, ?>);
+        assertSame(referencedLeafNode, derefResult);
+
+        final YangInstanceIdentifier relLeafrefPath = YangInstanceIdentifier.of(myInnerContainer).node(relLeafrefLeaf);
+        normalizedNodeContext = normalizedNodeContextSupport.createContext(relLeafrefPath);
+
+        derefResult = derefFunction.call(normalizedNodeContext, ImmutableList.of());
+        assertNotNull(derefResult);
+        assertTrue(derefResult instanceof NormalizedNode<?, ?>);
+        assertSame(referencedLeafNode, derefResult);
+    }
+
+    @Test
+    public void testDerefFunctionForLeafref2() throws Exception {
+        // tests leafref that references a leaf-list node
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangSource(
+                "/yang-xpath-functions-test/deref-function-leafref/foo.yang");
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
+
+        final LeafSetNode<?> referencedLeafListNode = Builders.leafSetBuilder().withNodeIdentifier(
+                new NodeIdentifier(referencedLeafList))
+                .withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(
+                        new NodeWithValue<>(referencedLeafList, "referenced-node-entry-value-a"))
+                        .withValue("referenced-node-entry-value-a").build())
+                .withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(
+                        new NodeWithValue<>(referencedLeafList, "referenced-node-entry-value-b"))
+                        .withValue("referenced-node-entry-value-b").build())
+                .withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(
+                        new NodeWithValue<>(referencedLeafList, "referenced-node-entry-value-c"))
+                        .withValue("referenced-node-entry-value-c").build())
+                .build();
+
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNodeForLeafrefTest(
+                referencedLeafListNode));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("foo-prefix", fooModule);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final YangInstanceIdentifier leafListLeafrefPath = YangInstanceIdentifier.of(myInnerContainer)
+                .node(leafListLeafrefLeaf);
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport
+                .createContext(leafListLeafrefPath);
+
+        final Function derefFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "deref");
+        Object derefResult = derefFunction.call(normalizedNodeContext, ImmutableList.of());
+        assertNotNull(derefResult);
+        assertTrue(derefResult instanceof NormalizedNode<?, ?>);
+
+        final LeafSetEntryNode<?> referencedLeafListNodeEntry = referencedLeafListNode.getChild(
+                new NodeWithValue<>(referencedLeafList, "referenced-node-entry-value-b")).get();
+        assertSame(referencedLeafListNodeEntry, derefResult);
+    }
+
+    private static ContainerNode buildMyContainerNodeForIIdTest(final LeafNode<?> referencedLeafNode) {
+        final Map<QName, Object> keyValues = ImmutableMap.of(keyLeafA, "key-value-a", keyLeafB, "key-value-b");
+        final YangInstanceIdentifier iidPath = YangInstanceIdentifier.of(myContainer).node(myList)
+                .node(new NodeIdentifierWithPredicates(myList, keyValues)).node(referencedLeaf);
+
+        final LeafNode<?> iidLeafNode = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(iidLeaf))
+                .withValue(iidPath).build();
+
+        final MapNode myListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(myList))
+                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+                        new NodeIdentifierWithPredicates(myList, keyValues))
+                        .withChild(iidLeafNode)
+                        .withChild(referencedLeafNode).build())
+                .build();
+
+        final ContainerNode myContainerNode = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(myContainer)).withChild(myListNode).build();
+        return myContainerNode;
+    }
+
+    private static YangInstanceIdentifier buildPathToIIdLeafNode() {
+        final Map<QName, Object> keyValues = ImmutableMap.of(keyLeafA, "key-value-a", keyLeafB, "key-value-b");
+        final YangInstanceIdentifier path = YangInstanceIdentifier.of(myList)
+                .node(new NodeIdentifierWithPredicates(myList, keyValues)).node(iidLeaf);
+        return path;
+    }
+
+    // variant for a leafref that references a leaf
+    private static ContainerNode buildMyContainerNodeForLeafrefTest(final LeafNode<?> referencedLeafNode) {
+        final Map<QName, Object> keyValues = ImmutableMap.of(keyLeafA, "value-a", keyLeafB, "value-b");
+
+        final LeafNode<?> absLeafrefNode = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(absLeafrefLeaf))
+                .withValue("referenced-leaf-node-value").build();
+        final LeafNode<?> relLeafrefNode = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(relLeafrefLeaf))
+                .withValue("referenced-leaf-node-value").build();
+        final LeafNode<?> ordinaryLeafANode = Builders.leafBuilder().withNodeIdentifier(
+                new NodeIdentifier(ordinaryLeafA)).withValue("value-a").build();
+        final LeafNode<?> ordinaryLeafBNode = Builders.leafBuilder().withNodeIdentifier(
+                new NodeIdentifier(ordinaryLeafB)).withValue("value-b").build();
+
+        final MapNode myListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(myList))
+                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+                        new NodeIdentifierWithPredicates(myList, keyValues))
+                        .withChild(referencedLeafNode).build())
+                .build();
+
+        final ContainerNode myInnerContainerNode = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(myInnerContainer))
+                .withChild(absLeafrefNode)
+                .withChild(relLeafrefNode)
+                .withChild(ordinaryLeafANode)
+                .withChild(ordinaryLeafBNode).build();
+
+        final ContainerNode myContainerNode = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(myContainer))
+                .withChild(myListNode)
+                .withChild(myInnerContainerNode).build();
+        return myContainerNode;
+    }
+
+    // variant for a leafref that references a leaf-list
+    private static ContainerNode buildMyContainerNodeForLeafrefTest(final LeafSetNode<?> referencedLeafListNode) {
+        final LeafNode<?> leafListLeafrefNode = Builders.leafBuilder().withNodeIdentifier(
+                new NodeIdentifier(leafListLeafrefLeaf)).withValue("referenced-node-entry-value-b").build();
+
+        final LeafNode<?> ordinaryLeafANode = Builders.leafBuilder().withNodeIdentifier(
+                new NodeIdentifier(ordinaryLeafA)).withValue("value-a").build();
+        final LeafNode<?> ordinaryLeafBNode = Builders.leafBuilder().withNodeIdentifier(
+                new NodeIdentifier(ordinaryLeafB)).withValue("value-b").build();
+
+        final ContainerNode myInnerContainerNode = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(myInnerContainer))
+                .withChild(leafListLeafrefNode)
+                .withChild(ordinaryLeafANode)
+                .withChild(ordinaryLeafBNode).build();
+
+        final Map<QName, Object> keyValues = ImmutableMap.of(keyLeafA, "value-a", keyLeafB, "value-b");
+
+        final MapNode myListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(myList))
+                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+                        new NodeIdentifierWithPredicates(myList, keyValues))
+                        .withChild(referencedLeafListNode).build())
+                .build();
+
+        final ContainerNode myContainerNode = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(myContainer))
+                .withChild(myListNode)
+                .withChild(myInnerContainerNode).build();
+        return myContainerNode;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/DerivedFromXPathFunctionTest.java b/yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/DerivedFromXPathFunctionTest.java
new file mode 100644 (file)
index 0000000..c08dd24
--- /dev/null
@@ -0,0 +1,307 @@
+/*
+ * Copyright (c) 2017 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.jaxen;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import java.net.URI;
+import java.text.ParseException;
+import org.jaxen.Context;
+import org.jaxen.Function;
+import org.jaxen.FunctionCallException;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathDocument;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathSchemaContext;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class DerivedFromXPathFunctionTest {
+
+    private static JaxenSchemaContextFactory jaxenSchemaContextFactory;
+
+    private static QNameModule barModule;
+    private static QName myContainer; 
+    private static QName myList;
+    private static QName keyLeaf;
+    private static QName idrefLeaf;
+    private static QName idC2Identity;
+
+    @BeforeClass
+    public static void setup() throws ParseException {
+        jaxenSchemaContextFactory = new JaxenSchemaContextFactory();
+
+        barModule = QNameModule.create(URI.create("bar-ns"),
+                SimpleDateFormatUtil.getRevisionFormat().parse("2017-04-03"));
+        myContainer = QName.create(barModule, "my-container");
+        myList = QName.create(barModule, "my-list");
+        keyLeaf = QName.create(barModule, "key-leaf");
+        idrefLeaf = QName.create(barModule, "idref-leaf");
+        idC2Identity = QName.create(barModule, "id-c2");
+    }
+
+    @Test
+    public void testDerivedFromFunction() throws Exception {
+        // also includes test for derived-from-or-self function
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangSources(ImmutableList.of(
+                "/yang-xpath-functions-test/derived-from-function/foo.yang",
+                "/yang-xpath-functions-test/derived-from-function/bar.yang"));
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode(idC2Identity));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("bar-prefix", barModule);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
+                buildPathToIdrefLeafNode());
+
+        final Function derivedFromFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "derived-from");
+
+        assertTrue(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-a3"));
+        assertTrue(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-a4"));
+        assertTrue(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-b2"));
+        assertTrue(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "bar-prefix:id-b3"));
+        assertTrue(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "id-b4"));
+
+        assertFalse(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-a1"));
+        assertFalse(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-a2"));
+        assertFalse(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-b1"));
+        assertFalse(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-c1"));
+        assertFalse(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "bar-prefix:id-c2"));
+
+        final Function derivedFromOrSelfFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "derived-from-or-self");
+        assertTrue(getDerivedFromResult(derivedFromOrSelfFunction, normalizedNodeContext, "bar-prefix:id-c2"));
+    }
+
+    @Test
+    public void testInvalidTypeOfCorrespondingSchemaNode() throws Exception {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangSource(
+                "/yang-xpath-functions-test/derived-from-function/bar-invalid.yang");
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode(idC2Identity));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("bar-prefix", barModule);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
+                buildPathToIdrefLeafNode());
+
+        final Function derivedFromFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "derived-from");
+
+        assertFalse(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "some-identity"));
+    }
+
+    @Test
+    public void testInvalidNormalizedNodeValueType() throws Exception {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangSources(ImmutableList.of(
+                "/yang-xpath-functions-test/derived-from-function/foo.yang",
+                "/yang-xpath-functions-test/derived-from-function/bar.yang"));
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode("should be QName"));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("bar-prefix", barModule);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
+                buildPathToIdrefLeafNode());
+
+        final Function derivedFromFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "derived-from");
+
+        assertFalse(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-a3"));
+    }
+
+    @Test
+    public void shouldFailOnUnknownPrefixOfIdentity() throws Exception {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangSources(ImmutableList.of(
+                "/yang-xpath-functions-test/derived-from-function/foo.yang",
+                "/yang-xpath-functions-test/derived-from-function/bar.yang"));
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode(idC2Identity));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("bar-prefix", barModule);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
+                buildPathToIdrefLeafNode());
+
+        final Function derivedFromFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "derived-from");
+
+        try {
+            getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "unknown-prefix:id-a3");
+            fail("Function call should have failed on unresolved prefix of the identity argument.");
+        } catch (IllegalArgumentException ex) {
+            assertEquals("Cannot resolve prefix 'unknown-prefix' from identity 'unknown-prefix:id-a3'.", ex.getMessage());
+        }
+    }
+
+    @Test
+    public void shouldFailOnMalformedIdentityArgument() throws Exception {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangSources(ImmutableList.of(
+                "/yang-xpath-functions-test/derived-from-function/foo.yang",
+                "/yang-xpath-functions-test/derived-from-function/bar.yang"));
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode(idC2Identity));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("bar-prefix", barModule);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
+                buildPathToIdrefLeafNode());
+
+        final Function derivedFromFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "derived-from");
+
+        try {
+            getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo:bar:id-a3");
+            fail("Function call should have failed on malformed identity argument.");
+        } catch (IllegalArgumentException ex) {
+            assertEquals("Malformed identity argument: foo:bar:id-a3.", ex.getMessage());
+        }
+    }
+
+    @Test
+    public void shouldFailOnUnknownIdentityArgument() throws Exception {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangSources(ImmutableList.of(
+                "/yang-xpath-functions-test/derived-from-function/foo.yang",
+                "/yang-xpath-functions-test/derived-from-function/bar.yang"));
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode(idC2Identity));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("bar-prefix", barModule);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
+                buildPathToIdrefLeafNode());
+
+        final Function derivedFromFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "derived-from");
+
+        try {
+            getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-a333");
+            fail("Function call should have failed on unknown identity argument.");
+        } catch (IllegalArgumentException ex) {
+            assertTrue(ex.getMessage().startsWith(
+                    "Identity (foo-ns?revision=2017-04-03)id-a333 does not have a corresponding identity schema " +
+                    "node in the module"));
+        }
+    }
+
+    @Test
+    public void shouldFailOnInvalidNumberOfArguments() throws Exception {
+        final YangFunctionContext yangFunctionContext = YangFunctionContext.getInstance();
+        final Function derivedFromFunction = yangFunctionContext.getFunction(null, null, "derived-from");
+
+        final Context mockedContext = mock(Context.class);
+
+        try {
+            derivedFromFunction.call(mockedContext, ImmutableList.of("some-identity", "should not be here"));
+            fail("Function call should have failed on invalid number of arguments.");
+        } catch (final FunctionCallException ex) {
+            assertEquals("derived-from() takes two arguments: node-set nodes, string identity.", ex.getMessage());
+        }
+    }
+
+    @Test
+    public void shouldFailOnInvalidTypeOfArgument() throws Exception {
+        final YangFunctionContext yangFunctionContext = YangFunctionContext.getInstance();
+        final Function bitIsSetFunction = yangFunctionContext.getFunction(null, null, "derived-from");
+
+        final Context mockedContext = mock(Context.class);
+
+        try {
+            bitIsSetFunction.call(mockedContext, ImmutableList.of(100));
+            fail("Function call should have failed on invalid type of the identity argument.");
+        } catch (final FunctionCallException ex) {
+            assertEquals("Argument 'identity' of derived-from() function should be a String.", ex.getMessage());
+        }
+    }
+
+    private static boolean getDerivedFromResult(final Function derivedFromFunction, final NormalizedNodeContext nnCtx,
+            final String identityArg) throws Exception {
+        return (boolean) derivedFromFunction.call(nnCtx, ImmutableList.of(identityArg));
+    }
+
+    private static ContainerNode buildMyContainerNode(final Object idrefLeafValue) {
+        final LeafNode<?> idrefLeafNode = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(idrefLeaf))
+                .withValue(idrefLeafValue).build();
+
+        final MapNode myListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(myList))
+                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+                        new NodeIdentifierWithPredicates(myList, keyLeaf, "key-value"))
+                        .withChild(idrefLeafNode).build()).build();
+
+        final ContainerNode myContainerNode = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(myContainer)).withChild(myListNode).build();
+        return myContainerNode;
+    }
+
+    private static YangInstanceIdentifier buildPathToIdrefLeafNode() {
+        final ImmutableMap.Builder<QName, Object> builder = ImmutableMap.builder();
+        final ImmutableMap<QName, Object> keys = builder.put(keyLeaf, "key-value").build();
+
+        final YangInstanceIdentifier path = YangInstanceIdentifier.of(myList)
+                .node(new NodeIdentifierWithPredicates(myList, keys)).node(idrefLeaf);
+        return path;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/EnumValueXPathFunctionTest.java b/yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/EnumValueXPathFunctionTest.java
new file mode 100644 (file)
index 0000000..0e42f87
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2017 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.jaxen;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import java.net.URI;
+import java.text.ParseException;
+import org.jaxen.Context;
+import org.jaxen.Function;
+import org.jaxen.FunctionCallException;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathDocument;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathSchemaContext;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class EnumValueXPathFunctionTest {
+
+    private static JaxenSchemaContextFactory jaxenSchemaContextFactory;
+
+    private static QNameModule fooModule;
+    private static QName myContainer;
+    private static QName alarm;
+    private static QName severity;
+    private static QName ordinaryLeaf;
+
+    @BeforeClass
+    public static void setup() throws ParseException {
+        jaxenSchemaContextFactory = new JaxenSchemaContextFactory();
+
+        fooModule = QNameModule.create(URI.create("foo-ns"),
+                SimpleDateFormatUtil.getRevisionFormat().parse("2017-04-03"));
+        myContainer = QName.create(fooModule, "my-container");
+        alarm = QName.create(fooModule, "alarm");
+        severity = QName.create(fooModule, "severity");
+        ordinaryLeaf = QName.create(fooModule, "ordinary-leaf");
+    }
+
+    @Test
+    public void testEnumValueFunction() throws Exception {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangSource(
+                "/yang-xpath-functions-test/enum-value-function/foo.yang");
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode("major"));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("foo-prefix", fooModule);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
+                buildPathToSeverityLeafNode("major"));
+
+        final Function enumValueFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "enum-value");
+        final int enumValueResult = (int) enumValueFunction.call(normalizedNodeContext, ImmutableList.of());
+        assertEquals(5, enumValueResult);
+    }
+
+    @Test
+    public void testInvalidTypeOfCorrespondingSchemaNode() throws Exception {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangSource(
+                "/yang-xpath-functions-test/enum-value-function/foo-invalid.yang");
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode("major"));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("foo-prefix", fooModule);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
+                buildPathToSeverityLeafNode("major"));
+
+        final Function enumValueFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "enum-value");
+        final Double enumValueResult = (Double) enumValueFunction.call(normalizedNodeContext, ImmutableList.of());
+        assertEquals(Double.NaN, enumValueResult, 0.001);
+    }
+
+    @Test
+    public void testInvalidNormalizedNodeValueType() throws Exception {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangSource(
+                "/yang-xpath-functions-test/enum-value-function/foo.yang");
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode(100));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("foo-prefix", fooModule);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
+                buildPathToSeverityLeafNode(100));
+
+        final Function enumValueFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "enum-value");
+        final Double enumValueResult = (Double) enumValueFunction.call(normalizedNodeContext, ImmutableList.of());
+        assertEquals(Double.NaN, enumValueResult, 0.001);
+    }
+
+    @Test
+    public void shouldFailOnUnknownEnumNodeValue() throws Exception {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangSource(
+                "/yang-xpath-functions-test/enum-value-function/foo.yang");
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode("unknown"));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("foo-prefix", fooModule);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
+                buildPathToSeverityLeafNode("unknown"));
+
+        final Function enumValueFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "enum-value");
+        try {
+            enumValueFunction.call(normalizedNodeContext, ImmutableList.of());
+            fail("Function call should have failed on unknown enum node value");
+        } catch (final IllegalStateException ex) {
+            assertTrue(ex.getMessage().startsWith("Enum unknown does not belong to enumeration"));
+        }
+    }
+
+    @Test
+    public void shouldFailOnInvalidNumberOfArguments() throws Exception {
+        final YangFunctionContext yangFunctionContext = YangFunctionContext.getInstance();
+        final Function enumValueFunction = yangFunctionContext.getFunction(null, null, "enum-value");
+
+        final Context mockedContext = mock(Context.class);
+
+        try {
+            enumValueFunction.call(mockedContext, ImmutableList.of("should not be here"));
+            fail("Function call should have failed on invalid number of arguments.");
+        } catch (final FunctionCallException ex) {
+            assertEquals("enum-value() takes one argument: node-set nodes.", ex.getMessage());
+        }
+    }
+
+    private static ContainerNode buildMyContainerNode(final Object keyLeafValue) {
+        final LeafNode<?> ordinaryLeafNode = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(ordinaryLeaf))
+                .withValue("test-value").build();
+
+        final MapNode alarmListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(alarm))
+                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+                        new NodeIdentifierWithPredicates(alarm, severity, keyLeafValue))
+                        .withChild(ordinaryLeafNode).build()).build();
+
+        final ContainerNode myContainerNode = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(myContainer)).withChild(alarmListNode).build();
+        return myContainerNode;
+    }
+
+    private static YangInstanceIdentifier buildPathToSeverityLeafNode(final Object keyLeafValue) {
+        final ImmutableMap.Builder<QName, Object> builder = ImmutableMap.builder();
+        final ImmutableMap<QName, Object> keys = builder.put(severity, keyLeafValue).build();
+
+        final YangInstanceIdentifier path = YangInstanceIdentifier.of(alarm)
+                .node(new NodeIdentifierWithPredicates(alarm, keys)).node(severity);
+        return path;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/ReMatchXPathFunctionTest.java b/yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/ReMatchXPathFunctionTest.java
new file mode 100644 (file)
index 0000000..27dc726
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2017 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.jaxen;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+import com.google.common.collect.ImmutableList;
+import org.jaxen.Context;
+import org.jaxen.Function;
+import org.jaxen.FunctionCallException;
+import org.junit.Test;
+
+public class ReMatchXPathFunctionTest {
+
+    @Test
+    public void testRematchFunction() throws Exception {
+        // re-match() uses regex processing from yang-parser-impl which has been thoroughly tested within
+        // the Bug5410Test unit test class, so here is just a basic test
+        final YangFunctionContext yangFunctionContext = YangFunctionContext.getInstance();
+        final Function rematchFunction = yangFunctionContext.getFunction(null, null, "re-match");
+
+        final Context mockedContext = mock(Context.class);
+
+        boolean rematchResult = (boolean) rematchFunction.call(mockedContext, ImmutableList.of("abbc", "[abc]{1,4}"));
+        assertTrue(rematchResult);
+        rematchResult = (boolean) rematchFunction.call(mockedContext, ImmutableList.of("abbcc", "[abc]{1,4}"));
+        assertFalse(rematchResult);
+    }
+
+    @Test
+    public void shouldFailOnInvalidNumberOfArguments() throws Exception {
+        final YangFunctionContext yangFunctionContext = YangFunctionContext.getInstance();
+        final Function rematchFunction = yangFunctionContext.getFunction(null, null, "re-match");
+
+        final Context mockedContext = mock(Context.class);
+
+        try {
+            rematchFunction.call(mockedContext, ImmutableList.of("abbc", "[abc]{1,4}", "should not be here"));
+            fail("Function call should have failed on invalid number of arguments.");
+        } catch (final FunctionCallException ex) {
+            assertEquals("re-match() takes two arguments: string subject, string pattern.", ex.getMessage());
+        }
+    }
+
+    @Test
+    public void shouldFailOnInvalidTypeOfArgument() throws Exception {
+        final YangFunctionContext yangFunctionContext = YangFunctionContext.getInstance();
+        final Function rematchFunction = yangFunctionContext.getFunction(null, null, "re-match");
+
+        final Context mockedContext = mock(Context.class);
+
+        try {
+            rematchFunction.call(mockedContext, ImmutableList.of(100, "[abc]{1,4}"));
+            fail("Function call should have failed on invalid type of the subject argument.");
+        } catch (final FunctionCallException ex) {
+            assertEquals("First argument of re-match() should be a String.", ex.getMessage());
+        }
+
+        try {
+            rematchFunction.call(mockedContext, ImmutableList.of("abbc", 100));
+            fail("Function call should have failed on invalid type of the pattern argument.");
+        } catch (final FunctionCallException ex) {
+            assertEquals("Second argument of re-match() should be a String.", ex.getMessage());
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/YangXPathFunctionsTest.java b/yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/YangXPathFunctionsTest.java
deleted file mode 100644 (file)
index a79ab08..0000000
+++ /dev/null
@@ -1,371 +0,0 @@
-/*
- * Copyright (c) 2017 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.jaxen;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-
-import com.google.common.collect.BiMap;
-import com.google.common.collect.HashBiMap;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Maps;
-import java.net.URI;
-import java.util.Map;
-import java.util.Set;
-import org.jaxen.Context;
-import org.jaxen.Function;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.QNameModule;
-import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
-import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
-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.xpath.XPathDocument;
-import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathSchemaContext;
-import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
-
-public class YangXPathFunctionsTest {
-
-    private static JaxenSchemaContextFactory jaxenSchemaContextFactory;
-
-    @BeforeClass
-    public static void setup() {
-        jaxenSchemaContextFactory = new JaxenSchemaContextFactory();
-    }
-
-    @Test
-    public void testRematchFunction() throws Exception {
-        // re-match() uses regex processing from yang-parser-impl which has been thoroughly tested within
-        // the Bug5410Test unit test class, so here is just a basic test
-        final YangFunctionContext yangFunctionContext = YangFunctionContext.getInstance();
-        final Function rematchFunction = yangFunctionContext.getFunction(null, null, "re-match");
-
-        final Context mockedContext = mock(Context.class);
-
-        boolean rematchResult = (boolean) rematchFunction.call(mockedContext, ImmutableList.of("abbc", "[abc]{1,4}"));
-        assertTrue(rematchResult);
-        rematchResult = (boolean) rematchFunction.call(mockedContext, ImmutableList.of("abbcc", "[abc]{1,4}"));
-        assertFalse(rematchResult);
-    }
-
-    @Test
-    public void testDerefFunctionForInstanceIdentifier() throws Exception {
-        final QNameModule fooModule = QNameModule.create(URI.create("foo-ns"),
-                SimpleDateFormatUtil.getRevisionFormat().parse("2017-04-03"));
-        final QName myContainer = QName.create(fooModule, "my-container");
-        final QName myList = QName.create(fooModule, "my-list");
-        final QName keyLeafA = QName.create(fooModule, "key-leaf-a");
-        final QName keyLeafB = QName.create(fooModule, "key-leaf-b");
-        final QName iidLeaf = QName.create(fooModule, "iid-leaf");
-        final QName referencedLeaf = QName.create(fooModule, "referenced-leaf");
-
-        final Map<QName, Object> keyValues = ImmutableMap.of(keyLeafA, "key-value-a", keyLeafB, "key-value-b");
-        final YangInstanceIdentifier iidPath = YangInstanceIdentifier.of(myContainer).node(myList)
-                .node(new NodeIdentifierWithPredicates(myList, keyValues)).node(referencedLeaf);
-
-        final LeafNode<?> iidLeafNode = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(iidLeaf))
-                .withValue(iidPath).build();
-        final LeafNode<?> referencedLeafNode = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(referencedLeaf))
-                .withValue("referenced-leaf-node-value").build();
-
-        final MapNode myListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(myList))
-                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
-                        new NodeIdentifierWithPredicates(myList, keyValues))
-                        .withChild(iidLeafNode)
-                        .withChild(referencedLeafNode).build())
-                .build();
-
-        final ContainerNode myContainerNode = Builders.containerBuilder().withNodeIdentifier(
-                new NodeIdentifier(myContainer)).withChild(myListNode).build();
-
-        final SchemaContext schemaContext = YangParserTestUtils.parseYangSource(
-                "/yang-xpath-functions-test/deref-function-iid/foo.yang");
-        assertNotNull(schemaContext);
-
-        final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
-        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(myContainerNode);
-
-        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
-        converterBiMap.put("foo-prefix", fooModule);
-
-        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
-                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
-
-        final YangInstanceIdentifier path = YangInstanceIdentifier.of(myList)
-                .node(new NodeIdentifierWithPredicates(myList, keyValues)).node(iidLeaf);
-        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(path);
-
-        final Function derefFunction = normalizedNodeContextSupport.getFunctionContext()
-                .getFunction(null, null, "deref");
-        final Object derefResult = derefFunction.call(normalizedNodeContext, ImmutableList.of());
-        assertNotNull(derefResult);
-        assertTrue(derefResult instanceof NormalizedNode<?, ?>);
-        assertSame(referencedLeafNode, derefResult);
-    }
-
-    @Test
-    public void testDerefFunctionForLeafref() throws Exception {
-        final QNameModule fooModule = QNameModule.create(URI.create("foo-ns"),
-                SimpleDateFormatUtil.getRevisionFormat().parse("2017-04-03"));
-        final QName myContainer = QName.create(fooModule, "my-container");
-        final QName myInnerContainer = QName.create(fooModule, "my-inner-container");
-        final QName myList = QName.create(fooModule, "my-list");
-        final QName keyLeafA = QName.create(fooModule, "key-leaf-a");
-        final QName keyLeafB = QName.create(fooModule, "key-leaf-b");
-        final QName absLeafrefLeaf = QName.create(fooModule, "abs-leafref-leaf");
-        final QName relLeafrefLeaf = QName.create(fooModule, "rel-leafref-leaf");
-        final QName ordinaryLeafA = QName.create(fooModule, "ordinary-leaf-a");
-        final QName ordinaryLeafB = QName.create(fooModule, "ordinary-leaf-b");
-        final QName referencedLeaf = QName.create(fooModule, "referenced-leaf");
-
-        final Map<QName, Object> keyValues = ImmutableMap.of(keyLeafA, "value-a", keyLeafB, "value-b");
-
-        final LeafNode<?> absLeafrefNode = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(absLeafrefLeaf))
-                .withValue("referenced-leaf-node-value").build();
-        final LeafNode<?> relLeafrefNode = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(relLeafrefLeaf))
-                .withValue("referenced-leaf-node-value").build();
-        final LeafNode<?> ordinaryLeafANode = Builders.leafBuilder().withNodeIdentifier(
-                new NodeIdentifier(ordinaryLeafA)).withValue("value-a").build();
-        final LeafNode<?> ordinaryLeafBNode = Builders.leafBuilder().withNodeIdentifier(
-                new NodeIdentifier(ordinaryLeafB)).withValue("value-b").build();
-
-        final LeafNode<?> referencedLeafNode = Builders.leafBuilder().withNodeIdentifier(
-                new NodeIdentifier(referencedLeaf)).withValue("referenced-leaf-node-value").build();
-
-        final MapNode myListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(myList))
-                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
-                        new NodeIdentifierWithPredicates(myList, keyValues))
-                        .withChild(referencedLeafNode).build())
-                .build();
-
-        final ContainerNode myInnerContainerNode = Builders.containerBuilder().withNodeIdentifier(
-                new NodeIdentifier(myInnerContainer))
-                .withChild(absLeafrefNode)
-                .withChild(relLeafrefNode)
-                .withChild(ordinaryLeafANode)
-                .withChild(ordinaryLeafBNode).build();
-
-        final ContainerNode myContainerNode = Builders.containerBuilder().withNodeIdentifier(
-                new NodeIdentifier(myContainer))
-                .withChild(myListNode)
-                .withChild(myInnerContainerNode).build();
-
-        final SchemaContext schemaContext = YangParserTestUtils.parseYangSource(
-                "/yang-xpath-functions-test/deref-function-leafref/foo.yang");
-        assertNotNull(schemaContext);
-
-        final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
-        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(myContainerNode);
-
-        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
-        converterBiMap.put("foo-prefix", fooModule);
-
-        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
-                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
-
-        final YangInstanceIdentifier absLeafrefPath = YangInstanceIdentifier.of(myInnerContainer).node(absLeafrefLeaf);
-        NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(absLeafrefPath);
-
-        final Function derefFunction = normalizedNodeContextSupport.getFunctionContext()
-                .getFunction(null, null, "deref");
-        Object derefResult = derefFunction.call(normalizedNodeContext, ImmutableList.of());
-        assertNotNull(derefResult);
-        assertTrue(derefResult instanceof NormalizedNode<?, ?>);
-        assertSame(referencedLeafNode, derefResult);
-
-        final YangInstanceIdentifier relLeafrefPath = YangInstanceIdentifier.of(myInnerContainer).node(relLeafrefLeaf);
-        normalizedNodeContext = normalizedNodeContextSupport.createContext(relLeafrefPath);
-
-        derefResult = derefFunction.call(normalizedNodeContext, ImmutableList.of());
-        assertNotNull(derefResult);
-        assertTrue(derefResult instanceof NormalizedNode<?, ?>);
-        assertSame(referencedLeafNode, derefResult);
-    }
-
-    @Test
-    public void testDerivedFromFunction() throws Exception {
-        // also includes test for derived-from-or-self function
-        final QNameModule barModule = QNameModule.create(URI.create("bar-ns"),
-                SimpleDateFormatUtil.getRevisionFormat().parse("2017-04-03"));
-        final QName myContainer = QName.create(barModule, "my-container");
-        final QName myList = QName.create(barModule, "my-list");
-        final QName keyLeaf = QName.create(barModule, "key-leaf");
-        final QName idrefLeaf = QName.create(barModule, "idref-leaf");
-        final QName idC2Identity = QName.create(barModule, "id-c2");
-
-        final LeafNode<?> idrefLeafNode = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(idrefLeaf))
-                .withValue(idC2Identity).build();
-
-        final MapNode myListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(myList))
-                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
-                        new NodeIdentifierWithPredicates(myList, keyLeaf, "key-value"))
-                        .withChild(idrefLeafNode).build()).build();
-
-        final ContainerNode myContainerNode = Builders.containerBuilder().withNodeIdentifier(
-                new NodeIdentifier(myContainer)).withChild(myListNode).build();
-
-        final SchemaContext schemaContext = YangParserTestUtils.parseYangSources(
-                "/yang-xpath-functions-test/derived-from-function");
-        assertNotNull(schemaContext);
-
-        final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
-        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(myContainerNode);
-
-        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
-        converterBiMap.put("bar-prefix", barModule);
-
-        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
-                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
-
-        final ImmutableMap.Builder<QName, Object> builder = ImmutableMap.builder();
-        final ImmutableMap<QName, Object> keys = builder.put(keyLeaf, "key-value").build();
-
-        final YangInstanceIdentifier path = YangInstanceIdentifier.of(myList)
-                .node(new NodeIdentifierWithPredicates(myList, keys)).node(idrefLeaf);
-        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(path);
-
-        final Function derivedFromFunction = normalizedNodeContextSupport.getFunctionContext()
-                .getFunction(null, null, "derived-from");
-
-        assertTrue(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-a3"));
-        assertTrue(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-a4"));
-        assertTrue(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-b2"));
-        assertTrue(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "bar-prefix:id-b3"));
-        assertTrue(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "id-b4"));
-
-        assertFalse(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-a1"));
-        assertFalse(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-a2"));
-        assertFalse(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-b1"));
-        assertFalse(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-c1"));
-        assertFalse(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "bar-prefix:id-c2"));
-
-        final Function derivedFromOrSelfFunction = normalizedNodeContextSupport.getFunctionContext()
-                .getFunction(null, null, "derived-from-or-self");
-        assertTrue(getDerivedFromResult(derivedFromOrSelfFunction, normalizedNodeContext, "bar-prefix:id-c2"));
-    }
-
-
-
-    @Test
-    public void testEnumValueFunction() throws Exception {
-        final QNameModule fooModule = QNameModule.create(URI.create("foo-ns"),
-                SimpleDateFormatUtil.getRevisionFormat().parse("2017-04-03"));
-        final QName myContainer = QName.create(fooModule, "my-container");
-        final QName alarm = QName.create(fooModule, "alarm");
-        final QName severity = QName.create(fooModule, "severity");
-        final QName ordinaryLeaf = QName.create(fooModule, "ordinary-leaf");
-
-        final LeafNode<?> ordinaryLeafNode = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(ordinaryLeaf))
-                .withValue("test-value").build();
-
-        final MapNode alarmListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(alarm))
-                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
-                        new NodeIdentifierWithPredicates(alarm, severity, "major"))
-                        .withChild(ordinaryLeafNode).build()).build();
-
-        final ContainerNode myContainerNode = Builders.containerBuilder().withNodeIdentifier(
-                new NodeIdentifier(myContainer)).withChild(alarmListNode).build();
-
-        final SchemaContext schemaContext = YangParserTestUtils.parseYangSource(
-                "/yang-xpath-functions-test/enum-value-function/foo.yang");
-        assertNotNull(schemaContext);
-
-        final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
-        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(myContainerNode);
-
-        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
-        converterBiMap.put("foo-prefix", fooModule);
-
-        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
-                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
-
-        final ImmutableMap.Builder<QName, Object> builder = ImmutableMap.builder();
-        final ImmutableMap<QName, Object> keys = builder.put(severity, "major").build();
-
-        final YangInstanceIdentifier path = YangInstanceIdentifier.of(alarm)
-                .node(new NodeIdentifierWithPredicates(alarm, keys)).node(severity);
-        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(path);
-
-        final Function enumValueFunction = normalizedNodeContextSupport.getFunctionContext()
-                .getFunction(null, null, "enum-value");
-        final int enumValueResult = (int) enumValueFunction.call(normalizedNodeContext, ImmutableList.of());
-        assertEquals(5, enumValueResult);
-    }
-
-    @Test
-    public void testBitIsSetFunction() throws Exception {
-        final QNameModule fooModule = QNameModule.create(URI.create("foo-ns"),
-                SimpleDateFormatUtil.getRevisionFormat().parse("2017-04-03"));
-        final QName myContainer = QName.create(fooModule, "my-container");
-        final QName myList = QName.create(fooModule, "my-list");
-        final QName flags = QName.create(fooModule, "flags");
-        final QName ordinaryLeaf = QName.create(fooModule, "ordinary-leaf");
-
-        final LeafNode<?> ordinaryLeafNode = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(ordinaryLeaf))
-                .withValue("test-value").build();
-
-        final Set<String> setOfBits = ImmutableSet.of("UP", "PROMISCUOUS");
-
-        final MapNode myListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(myList))
-                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
-                        new NodeIdentifierWithPredicates(myList, flags, setOfBits))
-                        .withChild(ordinaryLeafNode).build()).build();
-
-        final ContainerNode myContainerNode = Builders.containerBuilder().withNodeIdentifier(
-                new NodeIdentifier(myContainer)).withChild(myListNode).build();
-
-        final SchemaContext schemaContext = YangParserTestUtils.parseYangSource(
-                "/yang-xpath-functions-test/bit-is-set-function/foo.yang");
-        assertNotNull(schemaContext);
-
-        final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
-        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(myContainerNode);
-
-        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
-        converterBiMap.put("foo-prefix", fooModule);
-
-        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
-                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
-
-        final ImmutableMap.Builder<QName, Object> builder = ImmutableMap.builder();
-        final ImmutableMap<QName, Object> keys = builder.put(flags, setOfBits).build();
-
-        final YangInstanceIdentifier path = YangInstanceIdentifier.of(myList)
-                .node(new NodeIdentifierWithPredicates(myList, keys)).node(flags);
-        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(path);
-
-        final Function bitIsSetFunction = normalizedNodeContextSupport.getFunctionContext()
-                .getFunction(null, null, "bit-is-set");
-        boolean bitIsSetResult = (boolean) bitIsSetFunction.call(normalizedNodeContext, ImmutableList.of("UP"));
-        assertTrue(bitIsSetResult);
-        bitIsSetResult = (boolean) bitIsSetFunction.call(normalizedNodeContext, ImmutableList.of("PROMISCUOUS"));
-        assertTrue(bitIsSetResult);
-        bitIsSetResult = (boolean) bitIsSetFunction.call(normalizedNodeContext, ImmutableList.of("DISABLED"));
-        assertFalse(bitIsSetResult);
-    }
-
-    private static boolean getDerivedFromResult(final Function derivedFromFunction, final NormalizedNodeContext nnCtx,
-            final String identityArg) throws Exception {
-        return (boolean) derivedFromFunction.call(nnCtx, ImmutableList.of(identityArg));
-    }
-}
diff --git a/yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/bit-is-set-function/foo-invalid.yang b/yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/bit-is-set-function/foo-invalid.yang
new file mode 100644 (file)
index 0000000..13e09e2
--- /dev/null
@@ -0,0 +1,21 @@
+module foo-invalid {
+    namespace foo-ns;
+    prefix foo-prefix;
+    yang-version 1.1;
+
+    revision 2017-04-03;
+
+    container my-container {
+        list my-list {
+            key flags;
+
+            leaf flags {
+                type int32;
+            }
+
+            leaf ordinary-leaf {
+                type string;
+            }
+        }
+    }
+}
\ No newline at end of file
index a5c771af0db26da75ce2231bf99b8f4380d2a8da..4b12f546a69d64019a674f1afc72847c197e124a 100644 (file)
@@ -20,6 +20,10 @@ module foo {
             leaf referenced-leaf {
                 type string;
             }
+
+            leaf referenced-leaf-list {
+                type string;
+            }
         }
 
         container my-inner-container {
@@ -32,8 +36,15 @@ module foo {
 
             leaf rel-leafref-leaf {
                 type leafref {
-                    path "../../my-list[key-leaf-a=current()/../ordinary-leaf-a]" +
-                        "[key-leaf-b=current()/../ordinary-leaf-b]/referenced-leaf";
+                    path "../../foo-prefix:my-list[foo-prefix:key-leaf-a=current()/../ordinary-leaf-a]" +
+                        "[key-leaf-b=current()/../ordinary-leaf-b]/foo-prefix:referenced-leaf";
+                }
+            }
+
+            leaf leaf-list-leafref-leaf {
+                type leafref {
+                    path "/my-container/my-list[key-leaf-a=current()/../ordinary-leaf-a]" +
+                        "[key-leaf-b=current()/../ordinary-leaf-b]/referenced-leaf-list";
                 }
             }
 
diff --git a/yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/derived-from-function/bar-invalid.yang b/yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/derived-from-function/bar-invalid.yang
new file mode 100644 (file)
index 0000000..c43fd47
--- /dev/null
@@ -0,0 +1,21 @@
+module bar-invalid {
+    namespace bar-ns;
+    prefix bar-prefix;
+    yang-version 1.1;
+
+    revision 2017-04-03;
+
+    container my-container {
+        list my-list {
+            key key-leaf;
+
+            leaf key-leaf {
+                type string;
+            }
+
+            leaf idref-leaf {
+                type int32;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/enum-value-function/foo-invalid.yang b/yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/enum-value-function/foo-invalid.yang
new file mode 100644 (file)
index 0000000..aa238fe
--- /dev/null
@@ -0,0 +1,21 @@
+module foo-invalid {
+    namespace foo-ns;
+    prefix foo-prefix;
+    yang-version 1.1;
+
+    revision 2017-04-03;
+
+    container my-container {
+        list alarm {
+            key severity;
+
+            leaf ordinary-leaf {
+                type string;
+            }
+
+            leaf severity {
+                type int32;
+            }
+        }
+    }
+}
\ No newline at end of file