2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.restconf.nb.rfc8040.utils.parser;
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertThrows;
12 import static org.junit.Assert.assertTrue;
14 import com.google.common.collect.ImmutableMap;
15 import java.io.FileNotFoundException;
16 import java.util.Iterator;
17 import java.util.List;
19 import java.util.Map.Entry;
21 import org.junit.AfterClass;
22 import org.junit.BeforeClass;
23 import org.junit.Test;
24 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
25 import org.opendaylight.restconf.nb.rfc8040.TestRestconfUtils;
26 import org.opendaylight.yangtools.yang.common.ErrorTag;
27 import org.opendaylight.yangtools.yang.common.ErrorType;
28 import org.opendaylight.yangtools.yang.common.QName;
29 import org.opendaylight.yangtools.yang.common.Revision;
30 import org.opendaylight.yangtools.yang.common.Uint16;
31 import org.opendaylight.yangtools.yang.common.Uint8;
32 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
33 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
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.YangInstanceIdentifier.NodeWithValue;
37 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
38 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
39 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
42 * Unit tests for {@link YangInstanceIdentifierDeserializer}.
44 public class YangInstanceIdentifierDeserializerTest {
46 private static EffectiveModelContext SCHEMA_CONTEXT;
49 public static void beforeClass() throws FileNotFoundException {
51 YangParserTestUtils.parseYangFiles(TestRestconfUtils.loadFiles("/restconf/parser/deserializer"));
55 public static void afterClass() {
56 SCHEMA_CONTEXT = null;
60 * Test of deserialization <code>String</code> URI with container to
61 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
64 public void deserializeContainerTest() {
65 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA");
66 assertEquals(1, result.size());
67 assertEquals(NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")), result.get(0));
71 * Test of deserialization <code>String</code> URI with container containing leaf to
72 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
75 public void deserializeContainerWithLeafTest() {
76 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA/leaf-A");
77 assertEquals(2, result.size());
78 assertEquals(NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")), result.get(0));
79 assertEquals(NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "leaf-A")), result.get(1));
83 * Test of deserialization <code>String</code> URI with container containing list with leaf list to
84 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
87 public void deserializeContainerWithListWithLeafListTest() {
88 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
89 "deserializer-test:contA/list-A=100/leaf-list-AA=instance");
90 assertEquals(5, result.size());
93 assertEquals(NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")), result.get(0));
95 final QName list = QName.create("deserializer:test", "2016-06-06", "list-A");
96 assertEquals(NodeIdentifier.create(list), result.get(1));
97 assertEquals(NodeIdentifierWithPredicates.of(list, QName.create(list, "list-key"), Uint8.valueOf(100)),
100 final QName leafList = QName.create("deserializer:test", "2016-06-06", "leaf-list-AA");
101 assertEquals(NodeIdentifier.create(leafList), result.get(3));
102 assertEquals(new NodeWithValue<>(leafList, "instance"), result.get(4));
106 * Test of deserialization <code>String</code> URI with container containing list with Action to
107 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
110 public void deserializeContainerWithListWithActionTest() {
111 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
112 "example-actions:interfaces/interface=eth0/reset");
113 assertEquals(4, result.size());
115 assertEquals(NodeIdentifier.create(
116 QName.create("https://example.com/ns/example-actions", "2016-07-07", "interfaces")), result.get(0));
118 final QName list = QName.create("https://example.com/ns/example-actions", "2016-07-07", "interface");
119 assertEquals(NodeIdentifier.create(list), result.get(1));
120 assertEquals(NodeIdentifierWithPredicates.of(list, QName.create(list, "name"), "eth0"), result.get(2));
122 assertEquals(NodeIdentifier.create(
123 QName.create("https://example.com/ns/example-actions", "2016-07-07", "reset")), result.get(3));
127 * Test of deserialization <code>String</code> URI containing list with no keys to
128 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
131 public void deserializeListWithNoKeysTest() {
132 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:list-no-key");
133 assertEquals(2, result.size());
134 final QName list = QName.create("deserializer:test", "2016-06-06", "list-no-key");
135 assertEquals(NodeIdentifier.create(list), result.get(0));
136 assertEquals(NodeIdentifier.create(list), result.get(1));
140 * Test of deserialization <code>String</code> URI containing list with one key to
141 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
144 public void deserializeListWithOneKeyTest() {
145 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
146 "deserializer-test:list-one-key=value");
147 assertEquals(2, result.size());
148 final QName list = QName.create("deserializer:test", "2016-06-06", "list-one-key");
149 assertEquals(NodeIdentifier.create(list), result.get(0));
150 assertEquals(NodeIdentifierWithPredicates.of(list, QName.create(list, "name"), "value"), result.get(1));
154 * Test of deserialization <code>String</code> URI containing list with multiple keys to
155 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
158 public void deserializeListWithMultipleKeysTest() {
159 final QName list = QName.create("deserializer:test", "2016-06-06", "list-multiple-keys");
160 final Map<QName, Object> values = ImmutableMap.of(
161 QName.create(list, "name"), "value",
162 QName.create(list, "number"), Uint8.valueOf(100),
163 QName.create(list, "enabled"), false);
165 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
166 "deserializer-test:list-multiple-keys=value,100,false");
167 assertEquals(2, result.size());
168 assertEquals(NodeIdentifier.create(list), result.get(0));
169 assertEquals(NodeIdentifierWithPredicates.of(list, values), result.get(1));
173 * Test of deserialization <code>String</code> URI containing leaf list to
174 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
177 public void deserializeLeafListTest() {
178 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
179 "deserializer-test:leaf-list-0=true");
180 assertEquals(2, result.size());
182 final QName leafList = QName.create("deserializer:test", "2016-06-06", "leaf-list-0");
183 assertEquals(new NodeIdentifier(leafList), result.get(0));
184 assertEquals(new NodeWithValue<>(leafList, true), result.get(1));
188 * Test when empty <code>String</code> is supplied as an input. Test is expected to return empty result.
191 public void deserializeEmptyDataTest() {
192 assertEquals(List.of(), YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, ""));
196 * Test of deserialization <code>String</code> URI with identifiers separated by multiple slashes to
197 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
200 public void deserializeMultipleSlashesTest() {
201 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
202 "deserializer-test:contA////list-A=40//list-key");
203 assertEquals(4, result.size());
206 assertEquals(NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")), result.get(0));
208 final QName list = QName.create("deserializer:test", "2016-06-06", "list-A");
209 assertEquals(NodeIdentifier.create(list), result.get(1));
210 assertEquals(NodeIdentifierWithPredicates.of(list, QName.create(list, "list-key"), Uint8.valueOf(40)),
213 assertEquals(new NodeIdentifier(QName.create("deserializer:test", "2016-06-06", "list-key")), result.get(3));
217 * Negative test when supplied <code>SchemaContext</code> is null. Test is expected to fail with
218 * <code>NullPointerException</code>.
221 public void deserializeNullSchemaContextNegativeTest() {
222 assertThrows(NullPointerException.class,
223 () -> YangInstanceIdentifierDeserializer.create(null, "deserializer-test:contA"));
227 * Negative test when supplied <code>String</code> data to deserialize is null. Test is expected to fail with
228 * <code>NullPointerException</code>.
231 public void nullDataNegativeNegativeTest() {
232 assertThrows(NullPointerException.class,
233 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, null));
237 * Negative test when identifier is not followed by slash or equals. Test is expected to fail with
238 * <code>RestconfDocumentedException</code>.
241 public void deserializeBadCharMissingSlashOrEqualNegativeTest() {
242 assertThrows(RestconfDocumentedException.class,
243 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:cont*leaf-A"));
247 * Negative test of validating identifier when there is a slash after container without next identifier. Test
248 * is expected to fail with <code>RestconfDocumentedException</code>.
251 public void validArgIdentifierContainerEndsWithSlashNegativeTest() {
252 assertThrows(RestconfDocumentedException.class,
253 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA/"));
257 * Negative test of validating identifier when there are multiple slashes after container without next identifier.
258 * Test is expected to fail with <code>RestconfDocumentedException</code>.
261 public void validArgIdentifierContainerEndsWithMultipleSlashesNegativeTest() {
262 assertThrows(RestconfDocumentedException.class,
263 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA///"));
267 * Negative test of validating identifier when there is a slash after list key values without next identifier. Test
268 * is expected to fail with <code>RestconfDocumentedException</code>.
271 public void validArgIdentifierListEndsWithSlashLNegativeTest() {
272 assertThrows(RestconfDocumentedException.class,
273 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:list-one-key=value/"));
277 * Negative test of validating identifier when there are multiple slashes after list key values without next
278 * identifier. Test is expected to fail with <code>RestconfDocumentedException</code>.
281 public void validArgIdentifierListEndsWithSlashesNegativeTest() {
282 assertThrows(RestconfDocumentedException.class,
283 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:list-one-key=value//"));
287 * Negative test of creating <code>QName</code> when identifier is empty (example: '/'). Test is expected to fail
288 * with <code>RestconfDocumentedException</code>.
291 public void prepareQnameEmptyIdentifierNegativeTest() {
292 assertThrows(RestconfDocumentedException.class,
293 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "/"));
297 * Negative test of creating <code>QName</code> when in identifier there is another sign than colon or equals.
298 * Test is expected to fail with <code>RestconfDocumentedException</code>.
301 public void prepareQnameBuildPathNegativeTest() {
302 assertThrows(RestconfDocumentedException.class,
303 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test*contA"));
307 * Negative test of creating <code>QName</code> when it is not possible to find module for specified prefix. Test is
308 * expected to fail with <code>RestconfDocumentedException</code>.
311 public void prepareQnameNotExistingPrefixNegativeTest() {
312 assertThrows(RestconfDocumentedException.class,
313 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "not-existing:contA"));
317 * Negative test of creating <code>QName</code> when after prefix and colon there is not parsable identifier as
318 * local name. Test is expected to fail with <code>RestconfDocumentedException</code>.
321 public void prepareQnameNotValidPrefixAndLocalNameNegativeTest() {
322 assertThrows(RestconfDocumentedException.class, () ->
323 YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:*not-parsable-identifier"));
327 * Negative test of creating <code>QName</code> when data ends after prefix and colon. Test is expected to fail
328 * with <code>StringIndexOutOfBoundsException</code>.
331 public void prepareQnameErrorParsingNegativeTest() {
332 assertThrows(StringIndexOutOfBoundsException.class,
333 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:"));
337 * Negative test of creating <code>QName</code> when after identifier and colon there is node name of unknown
338 * node in current container. Test is expected to fail with <code>RestconfDocumentedException</code> and error
339 * type, error tag and error status code are compared to expected values.
342 public void prepareQnameNotValidContainerNameNegativeTest() {
343 RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class,
344 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:contA/leafB"));
345 assertEquals(ErrorType.PROTOCOL, ex.getErrors().get(0).getErrorType());
346 assertEquals(ErrorTag.DATA_MISSING, ex.getErrors().get(0).getErrorTag());
350 * Negative test of creating <code>QName</code> when after identifier and equals there is node name of unknown
351 * node in current list. Test is expected to fail with <code>RestconfDocumentedException</code> and error
352 * type, error tag and error status code are compared to expected values.
355 public void prepareQnameNotValidListNameNegativeTest() {
356 RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class,
357 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
358 "deserializer-test:list-no-key/disabled=false"));
359 assertEquals(ErrorType.PROTOCOL, ex.getErrors().get(0).getErrorType());
360 assertEquals(ErrorTag.DATA_MISSING, ex.getErrors().get(0).getErrorTag());
364 * Negative test of getting next identifier when current node is keyed entry. Test is expected to
365 * fail with <code>RestconfDocumentedException</code>.
368 public void prepareIdentifierNotKeyedEntryNegativeTest() {
369 assertThrows(RestconfDocumentedException.class,
370 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:list-one-key"));
374 * Negative test when there is a comma also after the last key. Test is expected to fail with
375 * <code>RestconfDocumentedException</code>.
378 public void deserializeKeysEndsWithComaNegativeTest() {
379 assertThrows(RestconfDocumentedException.class, () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
380 "deserializer-test:list-multiple-keys=value,100,false,"));
384 * Positive when not all keys of list are encoded. The missing keys should be considered to has empty
385 * <code>String</code> values. Also value of next leaf must not be considered to be missing key value.
388 public void notAllListKeysEncodedPositiveTest() {
389 final QName list = QName.create("deserializer:test", "2016-06-06", "list-multiple-keys");
390 final Map<QName, Object> values = ImmutableMap.of(
391 QName.create(list, "name"), ":foo",
392 QName.create(list, "number"), "",
393 QName.create(list, "enabled"), "");
395 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
396 "deserializer-test:list-multiple-keys=%3Afoo,,/string-value");
397 assertEquals(3, result.size());
399 assertEquals(NodeIdentifier.create(list), result.get(0));
400 assertEquals(NodeIdentifierWithPredicates.of(list, values), result.get(1));
402 assertEquals(new NodeIdentifier(QName.create("deserializer:test", "2016-06-06", "string-value")),
407 * Negative test when not all keys of list are encoded and it is not possible to consider missing keys to be empty.
408 * Test is expected to fail with <code>RestconfDocumentedException</code> and error type, error tag and error
409 * status code are compared to expected values.
412 public void notAllListKeysEncodedNegativeTest() {
413 RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class,
414 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
415 "deserializer-test:list-multiple-keys=%3Afoo/string-value"));
416 assertEquals(ErrorType.PROTOCOL, ex.getErrors().get(0).getErrorType());
417 assertEquals(ErrorTag.MISSING_ATTRIBUTE, ex.getErrors().get(0).getErrorTag());
421 * Test URI with list where key value starts with, ends with or contains percent encoded characters.The encoded
422 * value should be complete also with not percent-encoded parts.
425 public void percentEncodedKeyEndsWithNoPercentEncodedChars() {
426 final String URI = "deserializer-test:list-multiple-keys=%3Afoo,1,true";
427 final YangInstanceIdentifier result = YangInstanceIdentifier.create(
428 YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, URI));
430 final Iterator<Entry<QName, Object>> resultListKeys =
431 ((NodeIdentifierWithPredicates)result.getLastPathArgument()).entrySet().iterator();
433 assertEquals(":foo", resultListKeys.next().getValue());
434 assertEquals(Uint8.ONE, resultListKeys.next().getValue());
435 assertEquals(true, resultListKeys.next().getValue());
439 * Positive test when all keys of list can be considered to be empty <code>String</code>.
442 public void deserializeAllKeysEmptyTest() {
443 final QName list = QName.create("deserializer:test", "2016-06-06", "list-multiple-keys");
444 final Map<QName, Object> values = ImmutableMap.of(
445 QName.create(list, "name"), "",
446 QName.create(list, "number"), "",
447 QName.create(list, "enabled"), "");
449 final var result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
450 "deserializer-test:list-multiple-keys=,,");
451 assertEquals(2, result.size());
452 assertEquals(NodeIdentifier.create(list), result.get(0));
453 assertEquals(NodeIdentifierWithPredicates.of(list, values), result.get(1));
457 * Negative test of deserialization when for leaf list there is no specified instance value.
458 * <code>RestconfDocumentedException</code> is expected and error type, error tag and error status code are
459 * compared to expected values.
462 public void leafListMissingKeyNegativeTest() {
463 RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class,
464 () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT, "deserializer-test:leaf-list-0="));
465 assertEquals(ErrorType.PROTOCOL, ex.getErrors().get(0).getErrorType());
466 assertEquals(ErrorTag.MISSING_ATTRIBUTE, ex.getErrors().get(0).getErrorTag());
470 * Positive test of deserialization when parts of input URI <code>String</code> are defined in another module.
473 public void deserializePartInOtherModuleTest() {
474 final List<PathArgument> result = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
475 "deserializer-test-included:augmented-list=100/augmented-leaf");
477 assertEquals(4, result.size());
479 final QName list = QName.create("deserializer:test:included", "2016-06-06", "augmented-list");
480 final QName child = QName.create("deserializer:test", "2016-06-06", "augmented-leaf");
483 assertEquals(NodeIdentifier.create(list), result.get(0));
484 assertEquals(NodeIdentifierWithPredicates.of(list, QName.create(list, "list-key"), Uint16.valueOf(100)),
488 assertEquals(new AugmentationIdentifier(Set.of(child)), result.get(2));
489 assertEquals(NodeIdentifier.create(child), result.get(3));
493 * Deserialization of path that contains list entry with key which value is described by leaflef to identityref.
496 public void deserializePathWithIdentityrefKeyValueTest() {
497 final var pathArgs = YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
498 "refs/list-with-identityref=deserializer-test%3Aderived-identity/foo");
499 assertEquals(4, pathArgs.size());
501 assertEquals("refs", pathArgs.get(0).getNodeType().getLocalName());
502 assertEquals("list-with-identityref", pathArgs.get(1).getNodeType().getLocalName());
504 final PathArgument listEntryArg = pathArgs.get(2);
505 assertTrue(listEntryArg instanceof NodeIdentifierWithPredicates);
506 assertEquals("list-with-identityref", listEntryArg.getNodeType().getLocalName());
507 final Set<QName> keys = ((NodeIdentifierWithPredicates) listEntryArg).keySet();
508 assertEquals(1, keys.size());
509 assertEquals("id", keys.iterator().next().getLocalName());
510 final Object keyValue = ((NodeIdentifierWithPredicates) listEntryArg).values().iterator().next();
511 assertEquals(QName.create("deserializer:test", "derived-identity", Revision.of("2016-06-06")), keyValue);
513 assertEquals("foo", pathArgs.get(3).getNodeType().getLocalName());
517 * Identityref key value is not encoded correctly - ':' character must be encoded as '%3A'.
520 public void deserializePathWithInvalidIdentityrefKeyValueTest() {
521 assertThrows(RestconfDocumentedException.class, () -> YangInstanceIdentifierDeserializer.create(SCHEMA_CONTEXT,
522 "refs/list-with-identityref=deserializer-test:derived-identity/foo"));