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.assertTrue;
12 import static org.junit.Assert.fail;
14 import com.google.common.collect.Iterables;
15 import com.google.common.collect.Sets;
16 import java.io.FileNotFoundException;
17 import java.util.Iterator;
18 import java.util.LinkedHashMap;
20 import java.util.Map.Entry;
21 import org.junit.Before;
22 import org.junit.Rule;
23 import org.junit.Test;
24 import org.junit.rules.ExpectedException;
25 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
26 import org.opendaylight.restconf.common.errors.RestconfError;
27 import org.opendaylight.restconf.nb.rfc8040.TestRestconfUtils;
28 import org.opendaylight.yangtools.yang.common.QName;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
31 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
32 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
33 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
34 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
35 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
36 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
39 * Unit tests for {@link YangInstanceIdentifierDeserializer}.
41 public class YangInstanceIdentifierDeserializerTest {
44 public ExpectedException thrown = ExpectedException.none();
47 private SchemaContext schemaContext;
50 public void init() throws FileNotFoundException {
52 YangParserTestUtils.parseYangFiles(TestRestconfUtils.loadFiles("/restconf/parser/deserializer"));
56 * Test of deserialization <code>String</code> URI with container to
57 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
60 public void deserializeContainerTest() {
61 final Iterable<PathArgument> result = YangInstanceIdentifierDeserializer
62 .create(this.schemaContext, "deserializer-test:contA");
64 assertEquals("Result does not contains expected number of path arguments", 1, Iterables.size(result));
65 assertEquals("Not expected path argument",
66 NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")),
67 result.iterator().next());
71 * Test of deserialization <code>String</code> URI with container containing leaf to
72 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
75 public void deserializeContainerWithLeafTest() {
76 final Iterable<PathArgument> result = YangInstanceIdentifierDeserializer
77 .create(this.schemaContext, "deserializer-test:contA/leaf-A");
79 assertEquals("Result does not contains expected number of path arguments", 2, Iterables.size(result));
81 final Iterator<PathArgument> iterator = result.iterator();
82 assertEquals("Not expected path argument",
83 NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")),
85 assertEquals("Not expected path argument",
86 NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "leaf-A")),
91 * Test of deserialization <code>String</code> URI with container containing list with leaf list to
92 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
95 public void deserializeContainerWithListWithLeafListTest() {
96 final Iterable<PathArgument> result = YangInstanceIdentifierDeserializer
97 .create(this.schemaContext, "deserializer-test:contA/list-A=100/leaf-list-AA=instance");
99 assertEquals("Result does not contains expected number of path arguments", 5, Iterables.size(result));
101 final Iterator<PathArgument> iterator = result.iterator();
104 assertEquals("Not expected path argument",
105 NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")),
109 final QName list = QName.create("deserializer:test", "2016-06-06", "list-A");
110 assertEquals("Not expected path argument", NodeIdentifier.create(list), iterator.next());
111 assertEquals("Not expected path argument", NodeIdentifierWithPredicates.of(
112 list, QName.create(list, "list-key"), 100).toString(), iterator.next().toString());
115 final QName leafList = QName.create("deserializer:test", "2016-06-06", "leaf-list-AA");
116 assertEquals("Not expected path argument", NodeIdentifier.create(leafList), iterator.next());
117 assertEquals("Not expected path argument", new NodeWithValue<>(leafList, "instance"), iterator.next());
121 * Test of deserialization <code>String</code> URI with container containing list with Action to
122 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
125 public void deserializeContainerWithListWithActionTest() {
126 final Iterable<PathArgument> result = YangInstanceIdentifierDeserializer.create(this.schemaContext,
127 "example-actions:interfaces/interface=eth0/reset");
128 assertEquals("Result does not contains expected number of path arguments", 4, Iterables.size(result));
130 final Iterator<PathArgument> iterator = result.iterator();
133 assertEquals("Not expected path argument",
134 NodeIdentifier.create(QName.create("https://example.com/ns/example-actions", "2016-07-07", "interfaces")),
138 final QName list = QName.create("https://example.com/ns/example-actions", "2016-07-07", "interface");
139 assertEquals("Not expected path argument", NodeIdentifier.create(list), iterator.next());
140 assertEquals("Not expected path argument",
141 NodeIdentifierWithPredicates.of(list, QName.create(list, "name"), "eth0"), iterator.next());
144 final QName action = QName.create("https://example.com/ns/example-actions", "2016-07-07", "reset");
145 assertEquals("Not expected path argument", NodeIdentifier.create(action),
150 * Test of deserialization <code>String</code> URI containing list with no keys to
151 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
154 public void deserializeListWithNoKeysTest() {
155 final Iterable<PathArgument> result = YangInstanceIdentifierDeserializer.create(this.schemaContext,
156 "deserializer-test:list-no-key");
158 assertEquals("Result does not contains expected number of path arguments", 2, Iterables.size(result));
160 final Iterator<PathArgument> iterator = result.iterator();
161 final QName list = QName.create("deserializer:test", "2016-06-06", "list-no-key");
163 assertEquals("Not expected path argument", NodeIdentifier.create(list), iterator.next());
164 assertEquals("Not expected path argument", NodeIdentifier.create(list), iterator.next());
168 * Test of deserialization <code>String</code> URI containing list with one key to
169 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
172 public void deserializeListWithOneKeyTest() {
173 final Iterable<PathArgument> result = YangInstanceIdentifierDeserializer.create(this.schemaContext,
174 "deserializer-test:list-one-key=value");
176 assertEquals("Result does not contains expected number of path arguments", 2, Iterables.size(result));
178 final Iterator<PathArgument> iterator = result.iterator();
179 final QName list = QName.create("deserializer:test", "2016-06-06", "list-one-key");
181 assertEquals("Not expected path argument", NodeIdentifier.create(list), iterator.next());
182 assertEquals("Not expected path argument",
183 NodeIdentifierWithPredicates.of(list, QName.create(list, "name"), "value"), iterator.next());
187 * Test of deserialization <code>String</code> URI containing list with multiple keys to
188 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
191 public void deserializeListWithMultipleKeysTest() {
192 final QName list = QName.create("deserializer:test", "2016-06-06", "list-multiple-keys");
193 final Map<QName, Object> values = new LinkedHashMap<>();
194 values.put(QName.create(list, "name"), "value");
195 values.put(QName.create(list, "number"), 100);
196 values.put(QName.create(list, "enabled"), false);
198 final Iterable<PathArgument> result = YangInstanceIdentifierDeserializer.create(this.schemaContext,
199 "deserializer-test:list-multiple-keys=value,100,false");
201 assertEquals("Result does not contains expected number of path arguments", 2, Iterables.size(result));
203 final Iterator<PathArgument> iterator = result.iterator();
205 assertEquals("Not expected path argument", NodeIdentifier.create(list), iterator.next());
206 assertEquals("Not expected path argument", NodeIdentifierWithPredicates.of(list, values).toString(),
207 iterator.next().toString());
211 * Test of deserialization <code>String</code> URI containing leaf list to
212 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
215 public void deserializeLeafListTest() {
216 final Iterable<PathArgument> result = YangInstanceIdentifierDeserializer.create(this.schemaContext,
217 "deserializer-test:leaf-list-0=true");
219 assertEquals("Result does not contains expected number of path arguments", 2, Iterables.size(result));
221 final Iterator<PathArgument> iterator = result.iterator();
222 final QName leafList = QName.create("deserializer:test", "2016-06-06", "leaf-list-0");
224 assertEquals("Not expected path argument", new NodeIdentifier(leafList), iterator.next());
225 assertEquals("Not expected path argument",
226 new NodeWithValue<>(leafList, true).toString(), iterator.next().toString());
230 * Test when empty <code>String</code> is supplied as an input. Test is expected to return empty result.
233 public void deserializeEmptyDataTest() {
234 final Iterable<PathArgument> result = YangInstanceIdentifierDeserializer.create(this.schemaContext, "");
235 assertTrue("Empty result expected", Iterables.isEmpty(result));
239 * Test of deserialization <code>String</code> URI with identifiers separated by multiple slashes to
240 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
243 public void deserializeMultipleSlashesTest() {
244 final Iterable<PathArgument> result = YangInstanceIdentifierDeserializer
245 .create(this.schemaContext, "deserializer-test:contA////list-A=40//list-key");
247 assertEquals("Result does not contains expected number of path arguments", 4, Iterables.size(result));
249 final Iterator<PathArgument> iterator = result.iterator();
252 assertEquals("Not expected path argument",
253 NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")),
257 final QName list = QName.create("deserializer:test", "2016-06-06", "list-A");
258 assertEquals("Not expected path argument", NodeIdentifier.create(list), iterator.next());
259 assertEquals("Not expected path argument",
260 NodeIdentifierWithPredicates.of(list, QName.create(list, "list-key"), 40).toString(),
261 iterator.next().toString());
264 assertEquals("Not expected path argument",
265 new NodeIdentifier(QName.create("deserializer:test", "2016-06-06", "list-key")), iterator.next());
269 * Negative test when supplied <code>SchemaContext</code> is null. Test is expected to fail with
270 * <code>NullPointerException</code>.
273 public void deserializeNullSchemaContextNegativeTest() {
274 this.thrown.expect(NullPointerException.class);
275 YangInstanceIdentifierDeserializer.create(null, "deserializer-test:contA");
279 * Negative test when supplied <code>String</code> data to deserialize is null. Test is expected to fail with
280 * <code>NullPointerException</code>.
283 public void nullDataNegativeNegativeTest() {
284 this.thrown.expect(NullPointerException.class);
285 YangInstanceIdentifierDeserializer.create(this.schemaContext, null);
289 * Negative test when identifier is not followed by slash or equals. Test is expected to fail with
290 * <code>RestconfDocumentedException</code>.
293 public void deserializeBadCharMissingSlashOrEqualNegativeTest() {
294 this.thrown.expect(RestconfDocumentedException.class);
295 YangInstanceIdentifierDeserializer.create(this.schemaContext, "deserializer-test:cont*leaf-A");
299 * Negative test of validating identifier when there is a slash after container without next identifier. Test
300 * is expected to fail with <code>RestconfDocumentedException</code>.
303 public void validArgIdentifierContainerEndsWithSlashNegativeTest() {
304 this.thrown.expect(RestconfDocumentedException.class);
305 YangInstanceIdentifierDeserializer.create(this.schemaContext, "deserializer-test:contA/");
309 * Negative test of validating identifier when there are multiple slashes after container without next identifier.
310 * Test is expected to fail with <code>RestconfDocumentedException</code>.
313 public void validArgIdentifierContainerEndsWithMultipleSlashesNegativeTest() {
314 this.thrown.expect(RestconfDocumentedException.class);
315 YangInstanceIdentifierDeserializer.create(this.schemaContext, "deserializer-test:contA///");
319 * Negative test of validating identifier when there is a slash after list key values without next identifier. Test
320 * is expected to fail with <code>RestconfDocumentedException</code>.
323 public void validArgIdentifierListEndsWithSlashLNegativeTest() {
324 this.thrown.expect(RestconfDocumentedException.class);
325 YangInstanceIdentifierDeserializer.create(this.schemaContext, "deserializer-test:list-one-key=value/");
329 * Negative test of validating identifier when there are multiple slashes after list key values without next
330 * identifier. Test is expected to fail with <code>RestconfDocumentedException</code>.
333 public void validArgIdentifierListEndsWithSlashesNegativeTest() {
334 this.thrown.expect(RestconfDocumentedException.class);
335 YangInstanceIdentifierDeserializer.create(this.schemaContext, "deserializer-test:list-one-key=value//");
339 * Negative test of creating <code>QName</code> when identifier is empty (example: '/'). Test is expected to fail
340 * with <code>RestconfDocumentedException</code>.
343 public void prepareQnameEmptyIdentifierNegativeTest() {
344 this.thrown.expect(RestconfDocumentedException.class);
345 YangInstanceIdentifierDeserializer.create(this.schemaContext, "/");
349 * Negative test of creating <code>QName</code> when in identifier there is another sign than colon or equals.
350 * Test is expected to fail with <code>RestconfDocumentedException</code>.
353 public void prepareQnameBuildPathNegativeTest() {
354 this.thrown.expect(RestconfDocumentedException.class);
355 YangInstanceIdentifierDeserializer.create(this.schemaContext, "deserializer-test*contA");
359 * Negative test of creating <code>QName</code> when it is not possible to find module for specified prefix. Test is
360 * expected to fail with <code>RestconfDocumentedException</code>.
363 public void prepareQnameNotExistingPrefixNegativeTest() {
364 this.thrown.expect(RestconfDocumentedException.class);
365 YangInstanceIdentifierDeserializer.create(this.schemaContext, "not-existing:contA");
369 * Negative test of creating <code>QName</code> when after prefix and colon there is not parsable identifier as
370 * local name. Test is expected to fail with <code>RestconfDocumentedException</code>.
373 public void prepareQnameNotValidPrefixAndLocalNameNegativeTest() {
374 this.thrown.expect(RestconfDocumentedException.class);
375 YangInstanceIdentifierDeserializer.create(this.schemaContext, "deserializer-test:*not-parsable-identifier");
379 * Negative test of creating <code>QName</code> when data ends after prefix and colon. Test is expected to fail
380 * with <code>StringIndexOutOfBoundsException</code>.
383 public void prepareQnameErrorParsingNegativeTest() {
384 this.thrown.expect(StringIndexOutOfBoundsException.class);
385 YangInstanceIdentifierDeserializer.create(this.schemaContext, "deserializer-test:");
389 * Negative test of creating <code>QName</code> when after identifier and colon there is node name of unknown
390 * node in current container. Test is expected to fail with <code>RestconfDocumentedException</code> and error
391 * type, error tag and error status code are compared to expected values.
394 public void prepareQnameNotValidContainerNameNegativeTest() {
396 YangInstanceIdentifierDeserializer.create(this.schemaContext, "deserializer-test:contA/leafB");
397 fail("Test should fail due to unknown child node in container");
398 } catch (final RestconfDocumentedException e) {
399 assertEquals("Not expected error type",
400 RestconfError.ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
401 assertEquals("Not expected error tag",
402 RestconfError.ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
403 assertEquals("Not expected error status code",
404 404, e.getErrors().get(0).getErrorTag().getStatusCode());
409 * Negative test of creating <code>QName</code> when after identifier and equals there is node name of unknown
410 * node in current list. Test is expected to fail with <code>RestconfDocumentedException</code> and error
411 * type, error tag and error status code are compared to expected values.
414 public void prepareQnameNotValidListNameNegativeTest() {
416 YangInstanceIdentifierDeserializer
417 .create(this.schemaContext, "deserializer-test:list-no-key/disabled=false");
418 fail("Test should fail due to unknown child node in list");
419 } catch (final RestconfDocumentedException e) {
420 assertEquals("Not expected error type",
421 RestconfError.ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
422 assertEquals("Not expected error tag",
423 RestconfError.ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
424 assertEquals("Not expected error status code",
425 404, e.getErrors().get(0).getErrorTag().getStatusCode());
430 * Negative test of getting next identifier when current node is keyed entry. Test is expected to
431 * fail with <code>RestconfDocumentedException</code>.
434 public void prepareIdentifierNotKeyedEntryNegativeTest() {
435 this.thrown.expect(RestconfDocumentedException.class);
436 YangInstanceIdentifierDeserializer.create(this.schemaContext, "deserializer-test:list-one-key");
440 * Negative test when there is a comma also after the last key. Test is expected to fail with
441 * <code>RestconfDocumentedException</code>.
444 public void deserializeKeysEndsWithComaNegativeTest() {
445 this.thrown.expect(RestconfDocumentedException.class);
446 YangInstanceIdentifierDeserializer.create(this.schemaContext,
447 "deserializer-test:list-multiple-keys=value,100,false,");
451 * Positive when not all keys of list are encoded. The missing keys should be considered to has empty
452 * <code>String</code> values. Also value of next leaf must not be considered to be missing key value.
455 public void notAllListKeysEncodedPositiveTest() {
456 final QName list = QName.create("deserializer:test", "2016-06-06", "list-multiple-keys");
457 final Map<QName, Object> values = new LinkedHashMap<>();
458 values.put(QName.create(list, "name"), ":foo");
459 values.put(QName.create(list, "number"), "");
460 values.put(QName.create(list, "enabled"), "");
462 final Iterable<PathArgument> result = YangInstanceIdentifierDeserializer.create(
463 this.schemaContext, "deserializer-test:list-multiple-keys=%3Afoo,,/string-value");
465 assertEquals("Result does not contains expected number of path arguments", 3, Iterables.size(result));
467 final Iterator<PathArgument> iterator = result.iterator();
470 assertEquals("Not expected path argument", NodeIdentifier.create(list), iterator.next());
471 assertEquals("Not expected path argument", NodeIdentifierWithPredicates.of(list, values).toString(),
472 iterator.next().toString());
475 assertEquals("Not expected path argument",
476 new NodeIdentifier(QName.create("deserializer:test", "2016-06-06", "string-value")), iterator.next());
480 * Negative test when not all keys of list are encoded and it is not possible to consider missing keys to be empty.
481 * Test is expected to fail with <code>RestconfDocumentedException</code> and error type, error tag and error
482 * status code are compared to expected values.
485 public void notAllListKeysEncodedNegativeTest() {
487 YangInstanceIdentifierDeserializer.create(
488 this.schemaContext, "deserializer-test:list-multiple-keys=%3Afoo/string-value");
489 fail("Test should fail due to missing list key values");
490 } catch (final RestconfDocumentedException e) {
491 assertEquals("Not expected error type",
492 RestconfError.ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
493 assertEquals("Not expected error tag",
494 RestconfError.ErrorTag.MISSING_ATTRIBUTE, e.getErrors().get(0).getErrorTag());
495 assertEquals("Not expected error status code",
496 400, e.getErrors().get(0).getErrorTag().getStatusCode());
501 * Test URI with list where key value starts with, ends with or contains percent encoded characters.The encoded
502 * value should be complete also with not percent-encoded parts.
505 public void percentEncodedKeyEndsWithNoPercentEncodedChars() {
506 final String URI = "deserializer-test:list-multiple-keys=%3Afoo,1,true";
507 final YangInstanceIdentifier result = YangInstanceIdentifier.create(
508 YangInstanceIdentifierDeserializer.create(this.schemaContext, URI));
510 final Iterator<Entry<QName, Object>> resultListKeys =
511 ((NodeIdentifierWithPredicates)result.getLastPathArgument()).entrySet().iterator();
513 assertEquals(":foo", resultListKeys.next().getValue());
514 assertEquals(new Short("1"), resultListKeys.next().getValue());
515 assertEquals(true, resultListKeys.next().getValue());
519 * Positive test when all keys of list can be considered to be empty <code>String</code>.
522 public void deserializeAllKeysEmptyTest() {
523 final QName list = QName.create("deserializer:test", "2016-06-06", "list-multiple-keys");
524 final Map<QName, Object> values = new LinkedHashMap<>();
525 values.put(QName.create(list, "name"), "");
526 values.put(QName.create(list, "number"), "");
527 values.put(QName.create(list, "enabled"), "");
529 final Iterable<PathArgument> result = YangInstanceIdentifierDeserializer
530 .create(this.schemaContext, "deserializer-test:list-multiple-keys=,,");
532 assertEquals("Result does not contains expected number of path arguments", 2, Iterables.size(result));
534 final Iterator<PathArgument> iterator = result.iterator();
536 assertEquals("Not expected path argument", NodeIdentifier.create(list), iterator.next());
537 assertEquals("Not expected path argument", NodeIdentifierWithPredicates.of(list, values).toString(),
538 iterator.next().toString());
542 * Negative test of deserialization when for leaf list there is no specified instance value.
543 * <code>RestconfDocumentedException</code> is expected and error type, error tag and error status code are
544 * compared to expected values.
547 public void leafListMissingKeyNegativeTest() {
549 YangInstanceIdentifierDeserializer.create(this.schemaContext, "deserializer-test:leaf-list-0=");
550 fail("Test should fail due to missing instance value");
551 } catch (final RestconfDocumentedException e) {
552 assertEquals("Not expected error type",
553 RestconfError.ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
554 assertEquals("Not expected error tag",
555 RestconfError.ErrorTag.MISSING_ATTRIBUTE, e.getErrors().get(0).getErrorTag());
556 assertEquals("Not expected error status code",
557 400, e.getErrors().get(0).getErrorTag().getStatusCode());
562 * Positive test of deserialization when parts of input URI <code>String</code> are defined in another module.
565 public void deserializePartInOtherModuleTest() {
566 final Iterable<PathArgument> result = YangInstanceIdentifierDeserializer.create(this.schemaContext,
567 "deserializer-test-included:augmented-list=100/augmented-leaf");
569 assertEquals("Result does not contains expected number of path arguments", 4, Iterables.size(result));
571 final Iterator<PathArgument> iterator = result.iterator();
572 final QName list = QName.create("deserializer:test:included", "2016-06-06", "augmented-list");
573 final QName child = QName.create("deserializer:test", "2016-06-06", "augmented-leaf");
576 assertEquals("Not expected path argument",
577 NodeIdentifier.create(list),
580 assertEquals("Not expected path argument",
581 NodeIdentifierWithPredicates.of(list, QName.create(list, "list-key"), 100).toString(),
582 iterator.next().toString());
585 assertEquals("Not expected path argument", new AugmentationIdentifier(Sets.newHashSet(child)), iterator.next());
586 assertEquals("Not expected path argument", NodeIdentifier.create(child), iterator.next());