YANG XPath functions - unit tests and bugfix
[yangtools.git] / yang / yang-data-jaxen / src / test / java / org / opendaylight / yangtools / yang / data / jaxen / DerivedFromXPathFunctionTest.java
1 /*
2  * Copyright (c) 2017 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.yangtools.yang.data.jaxen;
10
11 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertFalse;
13 import static org.junit.Assert.assertNotNull;
14 import static org.junit.Assert.assertTrue;
15 import static org.junit.Assert.fail;
16 import static org.mockito.Mockito.mock;
17
18 import com.google.common.collect.BiMap;
19 import com.google.common.collect.HashBiMap;
20 import com.google.common.collect.ImmutableList;
21 import com.google.common.collect.ImmutableMap;
22 import com.google.common.collect.Maps;
23 import java.net.URI;
24 import java.text.ParseException;
25 import org.jaxen.Context;
26 import org.jaxen.Function;
27 import org.jaxen.FunctionCallException;
28 import org.junit.BeforeClass;
29 import org.junit.Test;
30 import org.opendaylight.yangtools.yang.common.QName;
31 import org.opendaylight.yangtools.yang.common.QNameModule;
32 import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
33 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
34 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
35 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
36 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
37 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
38 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
39 import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathDocument;
40 import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathSchemaContext;
41 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
42 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
43 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
44
45 public class DerivedFromXPathFunctionTest {
46
47     private static JaxenSchemaContextFactory jaxenSchemaContextFactory;
48
49     private static QNameModule barModule;
50     private static QName myContainer; 
51     private static QName myList;
52     private static QName keyLeaf;
53     private static QName idrefLeaf;
54     private static QName idC2Identity;
55
56     @BeforeClass
57     public static void setup() throws ParseException {
58         jaxenSchemaContextFactory = new JaxenSchemaContextFactory();
59
60         barModule = QNameModule.create(URI.create("bar-ns"),
61                 SimpleDateFormatUtil.getRevisionFormat().parse("2017-04-03"));
62         myContainer = QName.create(barModule, "my-container");
63         myList = QName.create(barModule, "my-list");
64         keyLeaf = QName.create(barModule, "key-leaf");
65         idrefLeaf = QName.create(barModule, "idref-leaf");
66         idC2Identity = QName.create(barModule, "id-c2");
67     }
68
69     @Test
70     public void testDerivedFromFunction() throws Exception {
71         // also includes test for derived-from-or-self function
72         final SchemaContext schemaContext = YangParserTestUtils.parseYangSources(ImmutableList.of(
73                 "/yang-xpath-functions-test/derived-from-function/foo.yang",
74                 "/yang-xpath-functions-test/derived-from-function/bar.yang"));
75         assertNotNull(schemaContext);
76
77         final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
78         final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode(idC2Identity));
79
80         final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
81         converterBiMap.put("bar-prefix", barModule);
82
83         final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
84                 (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
85
86         final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
87                 buildPathToIdrefLeafNode());
88
89         final Function derivedFromFunction = normalizedNodeContextSupport.getFunctionContext()
90                 .getFunction(null, null, "derived-from");
91
92         assertTrue(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-a3"));
93         assertTrue(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-a4"));
94         assertTrue(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-b2"));
95         assertTrue(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "bar-prefix:id-b3"));
96         assertTrue(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "id-b4"));
97
98         assertFalse(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-a1"));
99         assertFalse(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-a2"));
100         assertFalse(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-b1"));
101         assertFalse(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-c1"));
102         assertFalse(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "bar-prefix:id-c2"));
103
104         final Function derivedFromOrSelfFunction = normalizedNodeContextSupport.getFunctionContext()
105                 .getFunction(null, null, "derived-from-or-self");
106         assertTrue(getDerivedFromResult(derivedFromOrSelfFunction, normalizedNodeContext, "bar-prefix:id-c2"));
107     }
108
109     @Test
110     public void testInvalidTypeOfCorrespondingSchemaNode() throws Exception {
111         final SchemaContext schemaContext = YangParserTestUtils.parseYangSource(
112                 "/yang-xpath-functions-test/derived-from-function/bar-invalid.yang");
113         assertNotNull(schemaContext);
114
115         final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
116         final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode(idC2Identity));
117
118         final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
119         converterBiMap.put("bar-prefix", barModule);
120
121         final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
122                 (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
123
124         final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
125                 buildPathToIdrefLeafNode());
126
127         final Function derivedFromFunction = normalizedNodeContextSupport.getFunctionContext()
128                 .getFunction(null, null, "derived-from");
129
130         assertFalse(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "some-identity"));
131     }
132
133     @Test
134     public void testInvalidNormalizedNodeValueType() throws Exception {
135         final SchemaContext schemaContext = YangParserTestUtils.parseYangSources(ImmutableList.of(
136                 "/yang-xpath-functions-test/derived-from-function/foo.yang",
137                 "/yang-xpath-functions-test/derived-from-function/bar.yang"));
138         assertNotNull(schemaContext);
139
140         final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
141         final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode("should be QName"));
142
143         final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
144         converterBiMap.put("bar-prefix", barModule);
145
146         final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
147                 (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
148
149         final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
150                 buildPathToIdrefLeafNode());
151
152         final Function derivedFromFunction = normalizedNodeContextSupport.getFunctionContext()
153                 .getFunction(null, null, "derived-from");
154
155         assertFalse(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-a3"));
156     }
157
158     @Test
159     public void shouldFailOnUnknownPrefixOfIdentity() throws Exception {
160         final SchemaContext schemaContext = YangParserTestUtils.parseYangSources(ImmutableList.of(
161                 "/yang-xpath-functions-test/derived-from-function/foo.yang",
162                 "/yang-xpath-functions-test/derived-from-function/bar.yang"));
163         assertNotNull(schemaContext);
164
165         final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
166         final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode(idC2Identity));
167
168         final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
169         converterBiMap.put("bar-prefix", barModule);
170
171         final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
172                 (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
173
174         final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
175                 buildPathToIdrefLeafNode());
176
177         final Function derivedFromFunction = normalizedNodeContextSupport.getFunctionContext()
178                 .getFunction(null, null, "derived-from");
179
180         try {
181             getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "unknown-prefix:id-a3");
182             fail("Function call should have failed on unresolved prefix of the identity argument.");
183         } catch (IllegalArgumentException ex) {
184             assertEquals("Cannot resolve prefix 'unknown-prefix' from identity 'unknown-prefix:id-a3'.", ex.getMessage());
185         }
186     }
187
188     @Test
189     public void shouldFailOnMalformedIdentityArgument() throws Exception {
190         final SchemaContext schemaContext = YangParserTestUtils.parseYangSources(ImmutableList.of(
191                 "/yang-xpath-functions-test/derived-from-function/foo.yang",
192                 "/yang-xpath-functions-test/derived-from-function/bar.yang"));
193         assertNotNull(schemaContext);
194
195         final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
196         final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode(idC2Identity));
197
198         final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
199         converterBiMap.put("bar-prefix", barModule);
200
201         final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
202                 (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
203
204         final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
205                 buildPathToIdrefLeafNode());
206
207         final Function derivedFromFunction = normalizedNodeContextSupport.getFunctionContext()
208                 .getFunction(null, null, "derived-from");
209
210         try {
211             getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo:bar:id-a3");
212             fail("Function call should have failed on malformed identity argument.");
213         } catch (IllegalArgumentException ex) {
214             assertEquals("Malformed identity argument: foo:bar:id-a3.", ex.getMessage());
215         }
216     }
217
218     @Test
219     public void shouldFailOnUnknownIdentityArgument() throws Exception {
220         final SchemaContext schemaContext = YangParserTestUtils.parseYangSources(ImmutableList.of(
221                 "/yang-xpath-functions-test/derived-from-function/foo.yang",
222                 "/yang-xpath-functions-test/derived-from-function/bar.yang"));
223         assertNotNull(schemaContext);
224
225         final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
226         final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode(idC2Identity));
227
228         final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
229         converterBiMap.put("bar-prefix", barModule);
230
231         final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
232                 (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
233
234         final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
235                 buildPathToIdrefLeafNode());
236
237         final Function derivedFromFunction = normalizedNodeContextSupport.getFunctionContext()
238                 .getFunction(null, null, "derived-from");
239
240         try {
241             getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-a333");
242             fail("Function call should have failed on unknown identity argument.");
243         } catch (IllegalArgumentException ex) {
244             assertTrue(ex.getMessage().startsWith(
245                     "Identity (foo-ns?revision=2017-04-03)id-a333 does not have a corresponding identity schema " +
246                     "node in the module"));
247         }
248     }
249
250     @Test
251     public void shouldFailOnInvalidNumberOfArguments() throws Exception {
252         final YangFunctionContext yangFunctionContext = YangFunctionContext.getInstance();
253         final Function derivedFromFunction = yangFunctionContext.getFunction(null, null, "derived-from");
254
255         final Context mockedContext = mock(Context.class);
256
257         try {
258             derivedFromFunction.call(mockedContext, ImmutableList.of("some-identity", "should not be here"));
259             fail("Function call should have failed on invalid number of arguments.");
260         } catch (final FunctionCallException ex) {
261             assertEquals("derived-from() takes two arguments: node-set nodes, string identity.", ex.getMessage());
262         }
263     }
264
265     @Test
266     public void shouldFailOnInvalidTypeOfArgument() throws Exception {
267         final YangFunctionContext yangFunctionContext = YangFunctionContext.getInstance();
268         final Function bitIsSetFunction = yangFunctionContext.getFunction(null, null, "derived-from");
269
270         final Context mockedContext = mock(Context.class);
271
272         try {
273             bitIsSetFunction.call(mockedContext, ImmutableList.of(100));
274             fail("Function call should have failed on invalid type of the identity argument.");
275         } catch (final FunctionCallException ex) {
276             assertEquals("Argument 'identity' of derived-from() function should be a String.", ex.getMessage());
277         }
278     }
279
280     private static boolean getDerivedFromResult(final Function derivedFromFunction, final NormalizedNodeContext nnCtx,
281             final String identityArg) throws Exception {
282         return (boolean) derivedFromFunction.call(nnCtx, ImmutableList.of(identityArg));
283     }
284
285     private static ContainerNode buildMyContainerNode(final Object idrefLeafValue) {
286         final LeafNode<?> idrefLeafNode = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(idrefLeaf))
287                 .withValue(idrefLeafValue).build();
288
289         final MapNode myListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(myList))
290                 .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
291                         new NodeIdentifierWithPredicates(myList, keyLeaf, "key-value"))
292                         .withChild(idrefLeafNode).build()).build();
293
294         final ContainerNode myContainerNode = Builders.containerBuilder().withNodeIdentifier(
295                 new NodeIdentifier(myContainer)).withChild(myListNode).build();
296         return myContainerNode;
297     }
298
299     private static YangInstanceIdentifier buildPathToIdrefLeafNode() {
300         final ImmutableMap.Builder<QName, Object> builder = ImmutableMap.builder();
301         final ImmutableMap<QName, Object> keys = builder.put(keyLeaf, "key-value").build();
302
303         final YangInstanceIdentifier path = YangInstanceIdentifier.of(myList)
304                 .node(new NodeIdentifierWithPredicates(myList, keys)).node(idrefLeaf);
305         return path;
306     }
307 }