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.common.Uint8;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
31 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
32 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
33 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
34 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
35 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
36 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
37 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
40 * Unit tests for {@link YangInstanceIdentifierDeserializer}.
42 public class YangInstanceIdentifierDeserializerTest {
45 public ExpectedException thrown = ExpectedException.none();
48 private SchemaContext schemaContext;
51 public void init() throws FileNotFoundException {
53 YangParserTestUtils.parseYangFiles(TestRestconfUtils.loadFiles("/restconf/parser/deserializer"));
57 * Test of deserialization <code>String</code> URI with container to
58 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
61 public void deserializeContainerTest() {
62 final Iterable<PathArgument> result = YangInstanceIdentifierDeserializer
63 .create(this.schemaContext, "deserializer-test:contA");
65 assertEquals("Result does not contains expected number of path arguments", 1, Iterables.size(result));
66 assertEquals("Not expected path argument",
67 NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")),
68 result.iterator().next());
72 * Test of deserialization <code>String</code> URI with container containing leaf to
73 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
76 public void deserializeContainerWithLeafTest() {
77 final Iterable<PathArgument> result = YangInstanceIdentifierDeserializer
78 .create(this.schemaContext, "deserializer-test:contA/leaf-A");
80 assertEquals("Result does not contains expected number of path arguments", 2, Iterables.size(result));
82 final Iterator<PathArgument> iterator = result.iterator();
83 assertEquals("Not expected path argument",
84 NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")),
86 assertEquals("Not expected path argument",
87 NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "leaf-A")),
92 * Test of deserialization <code>String</code> URI with container containing list with leaf list to
93 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
96 public void deserializeContainerWithListWithLeafListTest() {
97 final Iterable<PathArgument> result = YangInstanceIdentifierDeserializer
98 .create(this.schemaContext, "deserializer-test:contA/list-A=100/leaf-list-AA=instance");
100 assertEquals("Result does not contains expected number of path arguments", 5, Iterables.size(result));
102 final Iterator<PathArgument> iterator = result.iterator();
105 assertEquals("Not expected path argument",
106 NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")),
110 final QName list = QName.create("deserializer:test", "2016-06-06", "list-A");
111 assertEquals("Not expected path argument", NodeIdentifier.create(list), iterator.next());
112 assertEquals("Not expected path argument", NodeIdentifierWithPredicates.of(
113 list, QName.create(list, "list-key"), 100).toString(), iterator.next().toString());
116 final QName leafList = QName.create("deserializer:test", "2016-06-06", "leaf-list-AA");
117 assertEquals("Not expected path argument", NodeIdentifier.create(leafList), iterator.next());
118 assertEquals("Not expected path argument", new NodeWithValue<>(leafList, "instance"), iterator.next());
122 * Test of deserialization <code>String</code> URI with container containing list with Action to
123 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
126 public void deserializeContainerWithListWithActionTest() {
127 final Iterable<PathArgument> result = YangInstanceIdentifierDeserializer.create(this.schemaContext,
128 "example-actions:interfaces/interface=eth0/reset");
129 assertEquals("Result does not contains expected number of path arguments", 4, Iterables.size(result));
131 final Iterator<PathArgument> iterator = result.iterator();
134 assertEquals("Not expected path argument",
135 NodeIdentifier.create(QName.create("https://example.com/ns/example-actions", "2016-07-07", "interfaces")),
139 final QName list = QName.create("https://example.com/ns/example-actions", "2016-07-07", "interface");
140 assertEquals("Not expected path argument", NodeIdentifier.create(list), iterator.next());
141 assertEquals("Not expected path argument",
142 NodeIdentifierWithPredicates.of(list, QName.create(list, "name"), "eth0"), iterator.next());
145 final QName action = QName.create("https://example.com/ns/example-actions", "2016-07-07", "reset");
146 assertEquals("Not expected path argument", NodeIdentifier.create(action),
151 * Test of deserialization <code>String</code> URI containing list with no keys to
152 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
155 public void deserializeListWithNoKeysTest() {
156 final Iterable<PathArgument> result = YangInstanceIdentifierDeserializer.create(this.schemaContext,
157 "deserializer-test:list-no-key");
159 assertEquals("Result does not contains expected number of path arguments", 2, Iterables.size(result));
161 final Iterator<PathArgument> iterator = result.iterator();
162 final QName list = QName.create("deserializer:test", "2016-06-06", "list-no-key");
164 assertEquals("Not expected path argument", NodeIdentifier.create(list), iterator.next());
165 assertEquals("Not expected path argument", NodeIdentifier.create(list), iterator.next());
169 * Test of deserialization <code>String</code> URI containing list with one key to
170 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
173 public void deserializeListWithOneKeyTest() {
174 final Iterable<PathArgument> result = YangInstanceIdentifierDeserializer.create(this.schemaContext,
175 "deserializer-test:list-one-key=value");
177 assertEquals("Result does not contains expected number of path arguments", 2, Iterables.size(result));
179 final Iterator<PathArgument> iterator = result.iterator();
180 final QName list = QName.create("deserializer:test", "2016-06-06", "list-one-key");
182 assertEquals("Not expected path argument", NodeIdentifier.create(list), iterator.next());
183 assertEquals("Not expected path argument",
184 NodeIdentifierWithPredicates.of(list, QName.create(list, "name"), "value"), iterator.next());
188 * Test of deserialization <code>String</code> URI containing list with multiple keys to
189 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
192 public void deserializeListWithMultipleKeysTest() {
193 final QName list = QName.create("deserializer:test", "2016-06-06", "list-multiple-keys");
194 final Map<QName, Object> values = new LinkedHashMap<>();
195 values.put(QName.create(list, "name"), "value");
196 values.put(QName.create(list, "number"), 100);
197 values.put(QName.create(list, "enabled"), false);
199 final Iterable<PathArgument> result = YangInstanceIdentifierDeserializer.create(this.schemaContext,
200 "deserializer-test:list-multiple-keys=value,100,false");
202 assertEquals("Result does not contains expected number of path arguments", 2, Iterables.size(result));
204 final Iterator<PathArgument> iterator = result.iterator();
206 assertEquals("Not expected path argument", NodeIdentifier.create(list), iterator.next());
207 assertEquals("Not expected path argument", NodeIdentifierWithPredicates.of(list, values).toString(),
208 iterator.next().toString());
212 * Test of deserialization <code>String</code> URI containing leaf list to
213 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
216 public void deserializeLeafListTest() {
217 final Iterable<PathArgument> result = YangInstanceIdentifierDeserializer.create(this.schemaContext,
218 "deserializer-test:leaf-list-0=true");
220 assertEquals("Result does not contains expected number of path arguments", 2, Iterables.size(result));
222 final Iterator<PathArgument> iterator = result.iterator();
223 final QName leafList = QName.create("deserializer:test", "2016-06-06", "leaf-list-0");
225 assertEquals("Not expected path argument", new NodeIdentifier(leafList), iterator.next());
226 assertEquals("Not expected path argument",
227 new NodeWithValue<>(leafList, true).toString(), iterator.next().toString());
231 * Test when empty <code>String</code> is supplied as an input. Test is expected to return empty result.
234 public void deserializeEmptyDataTest() {
235 final Iterable<PathArgument> result = YangInstanceIdentifierDeserializer.create(this.schemaContext, "");
236 assertTrue("Empty result expected", Iterables.isEmpty(result));
240 * Test of deserialization <code>String</code> URI with identifiers separated by multiple slashes to
241 * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
244 public void deserializeMultipleSlashesTest() {
245 final Iterable<PathArgument> result = YangInstanceIdentifierDeserializer
246 .create(this.schemaContext, "deserializer-test:contA////list-A=40//list-key");
248 assertEquals("Result does not contains expected number of path arguments", 4, Iterables.size(result));
250 final Iterator<PathArgument> iterator = result.iterator();
253 assertEquals("Not expected path argument",
254 NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")),
258 final QName list = QName.create("deserializer:test", "2016-06-06", "list-A");
259 assertEquals("Not expected path argument", NodeIdentifier.create(list), iterator.next());
260 assertEquals("Not expected path argument",
261 NodeIdentifierWithPredicates.of(list, QName.create(list, "list-key"), 40).toString(),
262 iterator.next().toString());
265 assertEquals("Not expected path argument",
266 new NodeIdentifier(QName.create("deserializer:test", "2016-06-06", "list-key")), iterator.next());
270 * Negative test when supplied <code>SchemaContext</code> is null. Test is expected to fail with
271 * <code>NullPointerException</code>.
274 public void deserializeNullSchemaContextNegativeTest() {
275 this.thrown.expect(NullPointerException.class);
276 YangInstanceIdentifierDeserializer.create(null, "deserializer-test:contA");
280 * Negative test when supplied <code>String</code> data to deserialize is null. Test is expected to fail with
281 * <code>NullPointerException</code>.
284 public void nullDataNegativeNegativeTest() {
285 this.thrown.expect(NullPointerException.class);
286 YangInstanceIdentifierDeserializer.create(this.schemaContext, null);
290 * Negative test when identifier is not followed by slash or equals. Test is expected to fail with
291 * <code>RestconfDocumentedException</code>.
294 public void deserializeBadCharMissingSlashOrEqualNegativeTest() {
295 this.thrown.expect(RestconfDocumentedException.class);
296 YangInstanceIdentifierDeserializer.create(this.schemaContext, "deserializer-test:cont*leaf-A");
300 * Negative test of validating identifier when there is a slash after container without next identifier. Test
301 * is expected to fail with <code>RestconfDocumentedException</code>.
304 public void validArgIdentifierContainerEndsWithSlashNegativeTest() {
305 this.thrown.expect(RestconfDocumentedException.class);
306 YangInstanceIdentifierDeserializer.create(this.schemaContext, "deserializer-test:contA/");
310 * Negative test of validating identifier when there are multiple slashes after container without next identifier.
311 * Test is expected to fail with <code>RestconfDocumentedException</code>.
314 public void validArgIdentifierContainerEndsWithMultipleSlashesNegativeTest() {
315 this.thrown.expect(RestconfDocumentedException.class);
316 YangInstanceIdentifierDeserializer.create(this.schemaContext, "deserializer-test:contA///");
320 * Negative test of validating identifier when there is a slash after list key values without next identifier. Test
321 * is expected to fail with <code>RestconfDocumentedException</code>.
324 public void validArgIdentifierListEndsWithSlashLNegativeTest() {
325 this.thrown.expect(RestconfDocumentedException.class);
326 YangInstanceIdentifierDeserializer.create(this.schemaContext, "deserializer-test:list-one-key=value/");
330 * Negative test of validating identifier when there are multiple slashes after list key values without next
331 * identifier. Test is expected to fail with <code>RestconfDocumentedException</code>.
334 public void validArgIdentifierListEndsWithSlashesNegativeTest() {
335 this.thrown.expect(RestconfDocumentedException.class);
336 YangInstanceIdentifierDeserializer.create(this.schemaContext, "deserializer-test:list-one-key=value//");
340 * Negative test of creating <code>QName</code> when identifier is empty (example: '/'). Test is expected to fail
341 * with <code>RestconfDocumentedException</code>.
344 public void prepareQnameEmptyIdentifierNegativeTest() {
345 this.thrown.expect(RestconfDocumentedException.class);
346 YangInstanceIdentifierDeserializer.create(this.schemaContext, "/");
350 * Negative test of creating <code>QName</code> when in identifier there is another sign than colon or equals.
351 * Test is expected to fail with <code>RestconfDocumentedException</code>.
354 public void prepareQnameBuildPathNegativeTest() {
355 this.thrown.expect(RestconfDocumentedException.class);
356 YangInstanceIdentifierDeserializer.create(this.schemaContext, "deserializer-test*contA");
360 * Negative test of creating <code>QName</code> when it is not possible to find module for specified prefix. Test is
361 * expected to fail with <code>RestconfDocumentedException</code>.
364 public void prepareQnameNotExistingPrefixNegativeTest() {
365 this.thrown.expect(RestconfDocumentedException.class);
366 YangInstanceIdentifierDeserializer.create(this.schemaContext, "not-existing:contA");
370 * Negative test of creating <code>QName</code> when after prefix and colon there is not parsable identifier as
371 * local name. Test is expected to fail with <code>RestconfDocumentedException</code>.
374 public void prepareQnameNotValidPrefixAndLocalNameNegativeTest() {
375 this.thrown.expect(RestconfDocumentedException.class);
376 YangInstanceIdentifierDeserializer.create(this.schemaContext, "deserializer-test:*not-parsable-identifier");
380 * Negative test of creating <code>QName</code> when data ends after prefix and colon. Test is expected to fail
381 * with <code>StringIndexOutOfBoundsException</code>.
384 public void prepareQnameErrorParsingNegativeTest() {
385 this.thrown.expect(StringIndexOutOfBoundsException.class);
386 YangInstanceIdentifierDeserializer.create(this.schemaContext, "deserializer-test:");
390 * Negative test of creating <code>QName</code> when after identifier and colon there is node name of unknown
391 * node in current container. Test is expected to fail with <code>RestconfDocumentedException</code> and error
392 * type, error tag and error status code are compared to expected values.
395 public void prepareQnameNotValidContainerNameNegativeTest() {
397 YangInstanceIdentifierDeserializer.create(this.schemaContext, "deserializer-test:contA/leafB");
398 fail("Test should fail due to unknown child node in container");
399 } catch (final RestconfDocumentedException e) {
400 assertEquals("Not expected error type",
401 RestconfError.ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
402 assertEquals("Not expected error tag",
403 RestconfError.ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
408 * Negative test of creating <code>QName</code> when after identifier and equals there is node name of unknown
409 * node in current list. Test is expected to fail with <code>RestconfDocumentedException</code> and error
410 * type, error tag and error status code are compared to expected values.
413 public void prepareQnameNotValidListNameNegativeTest() {
415 YangInstanceIdentifierDeserializer
416 .create(this.schemaContext, "deserializer-test:list-no-key/disabled=false");
417 fail("Test should fail due to unknown child node in list");
418 } catch (final RestconfDocumentedException e) {
419 assertEquals("Not expected error type",
420 RestconfError.ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
421 assertEquals("Not expected error tag",
422 RestconfError.ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag());
427 * Negative test of getting next identifier when current node is keyed entry. Test is expected to
428 * fail with <code>RestconfDocumentedException</code>.
431 public void prepareIdentifierNotKeyedEntryNegativeTest() {
432 this.thrown.expect(RestconfDocumentedException.class);
433 YangInstanceIdentifierDeserializer.create(this.schemaContext, "deserializer-test:list-one-key");
437 * Negative test when there is a comma also after the last key. Test is expected to fail with
438 * <code>RestconfDocumentedException</code>.
441 public void deserializeKeysEndsWithComaNegativeTest() {
442 this.thrown.expect(RestconfDocumentedException.class);
443 YangInstanceIdentifierDeserializer.create(this.schemaContext,
444 "deserializer-test:list-multiple-keys=value,100,false,");
448 * Positive when not all keys of list are encoded. The missing keys should be considered to has empty
449 * <code>String</code> values. Also value of next leaf must not be considered to be missing key value.
452 public void notAllListKeysEncodedPositiveTest() {
453 final QName list = QName.create("deserializer:test", "2016-06-06", "list-multiple-keys");
454 final Map<QName, Object> values = new LinkedHashMap<>();
455 values.put(QName.create(list, "name"), ":foo");
456 values.put(QName.create(list, "number"), "");
457 values.put(QName.create(list, "enabled"), "");
459 final Iterable<PathArgument> result = YangInstanceIdentifierDeserializer.create(
460 this.schemaContext, "deserializer-test:list-multiple-keys=%3Afoo,,/string-value");
462 assertEquals("Result does not contains expected number of path arguments", 3, Iterables.size(result));
464 final Iterator<PathArgument> iterator = result.iterator();
467 assertEquals("Not expected path argument", NodeIdentifier.create(list), iterator.next());
468 assertEquals("Not expected path argument", NodeIdentifierWithPredicates.of(list, values).toString(),
469 iterator.next().toString());
472 assertEquals("Not expected path argument",
473 new NodeIdentifier(QName.create("deserializer:test", "2016-06-06", "string-value")), iterator.next());
477 * Negative test when not all keys of list are encoded and it is not possible to consider missing keys to be empty.
478 * Test is expected to fail with <code>RestconfDocumentedException</code> and error type, error tag and error
479 * status code are compared to expected values.
482 public void notAllListKeysEncodedNegativeTest() {
484 YangInstanceIdentifierDeserializer.create(
485 this.schemaContext, "deserializer-test:list-multiple-keys=%3Afoo/string-value");
486 fail("Test should fail due to missing list key values");
487 } catch (final RestconfDocumentedException e) {
488 assertEquals("Not expected error type",
489 RestconfError.ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
490 assertEquals("Not expected error tag",
491 RestconfError.ErrorTag.MISSING_ATTRIBUTE, e.getErrors().get(0).getErrorTag());
492 assertEquals("Not expected error status code",
493 400, e.getErrors().get(0).getErrorTag().getStatusCode());
498 * Test URI with list where key value starts with, ends with or contains percent encoded characters.The encoded
499 * value should be complete also with not percent-encoded parts.
502 public void percentEncodedKeyEndsWithNoPercentEncodedChars() {
503 final String URI = "deserializer-test:list-multiple-keys=%3Afoo,1,true";
504 final YangInstanceIdentifier result = YangInstanceIdentifier.create(
505 YangInstanceIdentifierDeserializer.create(this.schemaContext, URI));
507 final Iterator<Entry<QName, Object>> resultListKeys =
508 ((NodeIdentifierWithPredicates)result.getLastPathArgument()).entrySet().iterator();
510 assertEquals(":foo", resultListKeys.next().getValue());
511 assertEquals(Uint8.ONE, resultListKeys.next().getValue());
512 assertEquals(true, resultListKeys.next().getValue());
516 * Positive test when all keys of list can be considered to be empty <code>String</code>.
519 public void deserializeAllKeysEmptyTest() {
520 final QName list = QName.create("deserializer:test", "2016-06-06", "list-multiple-keys");
521 final Map<QName, Object> values = new LinkedHashMap<>();
522 values.put(QName.create(list, "name"), "");
523 values.put(QName.create(list, "number"), "");
524 values.put(QName.create(list, "enabled"), "");
526 final Iterable<PathArgument> result = YangInstanceIdentifierDeserializer
527 .create(this.schemaContext, "deserializer-test:list-multiple-keys=,,");
529 assertEquals("Result does not contains expected number of path arguments", 2, Iterables.size(result));
531 final Iterator<PathArgument> iterator = result.iterator();
533 assertEquals("Not expected path argument", NodeIdentifier.create(list), iterator.next());
534 assertEquals("Not expected path argument", NodeIdentifierWithPredicates.of(list, values).toString(),
535 iterator.next().toString());
539 * Negative test of deserialization when for leaf list there is no specified instance value.
540 * <code>RestconfDocumentedException</code> is expected and error type, error tag and error status code are
541 * compared to expected values.
544 public void leafListMissingKeyNegativeTest() {
546 YangInstanceIdentifierDeserializer.create(this.schemaContext, "deserializer-test:leaf-list-0=");
547 fail("Test should fail due to missing instance value");
548 } catch (final RestconfDocumentedException e) {
549 assertEquals("Not expected error type",
550 RestconfError.ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
551 assertEquals("Not expected error tag",
552 RestconfError.ErrorTag.MISSING_ATTRIBUTE, e.getErrors().get(0).getErrorTag());
553 assertEquals("Not expected error status code",
554 400, e.getErrors().get(0).getErrorTag().getStatusCode());
559 * Positive test of deserialization when parts of input URI <code>String</code> are defined in another module.
562 public void deserializePartInOtherModuleTest() {
563 final Iterable<PathArgument> result = YangInstanceIdentifierDeserializer.create(this.schemaContext,
564 "deserializer-test-included:augmented-list=100/augmented-leaf");
566 assertEquals("Result does not contains expected number of path arguments", 4, Iterables.size(result));
568 final Iterator<PathArgument> iterator = result.iterator();
569 final QName list = QName.create("deserializer:test:included", "2016-06-06", "augmented-list");
570 final QName child = QName.create("deserializer:test", "2016-06-06", "augmented-leaf");
573 assertEquals("Not expected path argument",
574 NodeIdentifier.create(list),
577 assertEquals("Not expected path argument",
578 NodeIdentifierWithPredicates.of(list, QName.create(list, "list-key"), 100).toString(),
579 iterator.next().toString());
582 assertEquals("Not expected path argument", new AugmentationIdentifier(Sets.newHashSet(child)), iterator.next());
583 assertEquals("Not expected path argument", NodeIdentifier.create(child), iterator.next());