2 * Copyright (c) 2022 PANTHEON.tech, s.r.o. 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.yangtools.yang.data.codec.xml;
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
12 import static org.mockito.Mockito.doReturn;
13 import static org.mockito.Mockito.mock;
14 import static org.mockito.Mockito.verify;
16 import com.google.common.collect.ImmutableSet;
17 import javax.xml.namespace.NamespaceContext;
18 import javax.xml.stream.XMLStreamWriter;
19 import org.junit.jupiter.api.AfterAll;
20 import org.junit.jupiter.api.BeforeAll;
21 import org.junit.jupiter.api.Disabled;
22 import org.junit.jupiter.api.Test;
23 import org.mockito.ArgumentCaptor;
24 import org.opendaylight.yangtools.yang.common.QName;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
29 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
31 import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
32 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
34 public class YT1473Test {
35 public static final String FOO_NS = "foons"; // namespace for prefix 'foo'
36 public static final QName FOO_FOO = QName.create(FOO_NS, "foo"); // list with key 'str'
37 public static final QName FOO_BAR = QName.create(FOO_NS, "bar"); // list with key 'qname'
38 public static final QName FOO_BAZ = QName.create(FOO_NS, "baz"); // list with key 'id'
39 public static final QName FOO_BEE = QName.create(FOO_NS, "bee"); // list with key 'bts'
40 public static final QName FOO_ONE = QName.create(FOO_NS, "one"); // identity
41 public static final QName FOO_STR = QName.create(FOO_NS, "str"); // key of type 'string'
42 public static final QName FOO_QNAME = QName.create(FOO_NS, "qname"); // key of type 'one' based
43 public static final QName FOO_ID = QName.create(FOO_NS, "id"); // key of type 'instance-identifier'
44 public static final QName FOO_BTS = QName.create(FOO_NS, "bts"); // key of type 'bts' (bits)
46 public static final String BAR_NS = "barns"; // namespace for prefix 'bar'
47 public static final QName BAR_TWO = QName.create(BAR_NS, "two"); // identity inheriting 'foo:one'
48 public static final QName BAR_STR = QName.create(BAR_NS, "str"); // leaf-list of type 'string'
49 public static final QName BAR_FOO = QName.create(BAR_NS, "foo"); // leaf-list of type 'foo:one' based
50 public static final QName BAR_BAR = QName.create(BAR_NS, "bar"); // leaf-list of type 'instance-identifier'
51 public static final QName BAR_BEE = QName.create(BAR_NS, "bee"); // leaf-list of type 'foo:bts' (bits)
53 public static XmlCodec<YangInstanceIdentifier> CODEC;
56 public static void beforeAll() {
57 final var modelContext = YangParserTestUtils.parseYangResourceDirectory("/yt1473");
58 final var baz = assertInstanceOf(ListSchemaNode.class, modelContext.getDataChildByName(FOO_BAZ));
59 final var id = assertInstanceOf(LeafSchemaNode.class, baz.getDataChildByName(FOO_ID));
60 final var type = assertInstanceOf(InstanceIdentifierTypeDefinition.class, id.getType());
61 CODEC = XmlCodecFactory.create(modelContext).instanceIdentifierCodec(type);
65 public static void afterAll() {
70 public void testSerializeSimple() throws Exception {
71 // No escaping needed, use single quotes
72 assertSerdes("/bar:str[.='str\"']", buildYangInstanceIdentifier(BAR_STR, "str\""));
73 assertSerdes("/bar:str[.='str\\']", buildYangInstanceIdentifier(BAR_STR, "str\\"));
74 assertSerdes("/bar:str[.='str\r']", buildYangInstanceIdentifier(BAR_STR, "str\r"));
75 assertSerdes("/bar:str[.='str\n']", buildYangInstanceIdentifier(BAR_STR, "str\n"));
76 assertSerdes("/bar:str[.='str\t']", buildYangInstanceIdentifier(BAR_STR, "str\t"));
78 assertSerdes("/foo:foo[foo:str='str\"\\']", buildYangInstanceIdentifier(FOO_FOO, FOO_STR, "str\"\\"));
79 assertSerdes("/foo:foo[foo:str='str\r\n\t']", buildYangInstanceIdentifier(FOO_FOO, FOO_STR, "str\r\n\t"));
83 public void testSerializeEscaped() throws Exception {
84 // Escaping is needed, use double quotes and escape
85 assertSerdes("/bar:str[.=\"str'\\\"\"]", buildYangInstanceIdentifier(BAR_STR, "str'\""));
86 assertSerdes("/bar:str[.=\"str'\\n\"]", buildYangInstanceIdentifier(BAR_STR, "str'\n"));
87 assertSerdes("/bar:str[.=\"str'\\t\"]", buildYangInstanceIdentifier(BAR_STR, "str'\t"));
88 assertSerdes("/bar:str[.=\"str'\r\"]", buildYangInstanceIdentifier(BAR_STR, "str'\r"));
90 assertSerdes("/foo:foo[foo:str=\"str'\\\"\\n\"]", buildYangInstanceIdentifier(FOO_FOO, FOO_STR, "str'\"\n"));
91 assertSerdes("/foo:foo[foo:str=\"str'\\t\r\"]", buildYangInstanceIdentifier(FOO_FOO, FOO_STR, "str'\t\r"));
95 @Disabled("YT-1473: QName values need to be recognized and properly encoded via identity codec")
96 public void testSerializeIdentity() throws Exception {
97 assertSerdes("/foo:bar[foo:qname='foo:one']", buildYangInstanceIdentifier(FOO_BAR, FOO_QNAME, FOO_ONE));
98 assertSerdes("/foo:bar[foo:qname='bar:two']", buildYangInstanceIdentifier(FOO_BAR, FOO_QNAME, BAR_TWO));
102 @Disabled("YT-1473: Instance-identifier values need to be recognized and properly encoded and escaped")
103 public void testSerializeInstanceIdentifierRef() throws Exception {
104 assertSerdes("/foo:baz[foo:id=\"/foo:bar[foo:qname='bar:two']\"]",
105 buildYangInstanceIdentifier(FOO_BAZ, FOO_ID, buildYangInstanceIdentifier(FOO_BAR, FOO_QNAME, BAR_TWO)));
109 @Disabled("YT-1473: QName values need to be recognized and properly encoded via identity codec")
110 public void testSerializeIdentityValue() throws Exception {
111 assertSerdes("/bar:foo[.='foo:one']", buildYangInstanceIdentifier(BAR_FOO, FOO_ONE));
112 assertSerdes("/bar:foo[.='bar:two']", buildYangInstanceIdentifier(BAR_FOO, BAR_TWO));
116 @Disabled("YT-1473: Instance-identifier values need to be recognized and properly encoded and escaped")
117 public void testSerializeInstanceIdentifierValue() throws Exception {
118 assertSerdes("/bar:bar[.=\"/foo:bar[foo:qname='foo:one']\"]",
119 buildYangInstanceIdentifier(BAR_BAR, buildYangInstanceIdentifier(FOO_BAR, FOO_QNAME, FOO_ONE)));
120 assertSerdes("/bar:bar[.=\"/foo:bar[foo:qname='bar:two']\"]",
121 buildYangInstanceIdentifier(BAR_BAR, buildYangInstanceIdentifier(FOO_BAR, FOO_QNAME, BAR_TWO)));
125 @Disabled("YT-1473: bits values need to be recognized and properly encoded and escaped")
126 public void testSerializeBits() throws Exception {
127 assertSerdes("/foo:bee[foo:bts='']", buildYangInstanceIdentifier(FOO_BEE, FOO_BTS, ImmutableSet.of()));
128 assertSerdes("/foo:bee[foo:bts='one']", buildYangInstanceIdentifier(FOO_BEE, FOO_BTS, ImmutableSet.of("one")));
129 assertSerdes("/foo:bee[foo:bts='two three']",
130 buildYangInstanceIdentifier(FOO_BEE, FOO_BTS, ImmutableSet.of("two", "three")));
134 @Disabled("YT-1473: bits values need to be recognized and properly encoded and escaped")
135 public void testSerializeBitsValue() throws Exception {
136 assertSerdes("/bar:bee[.='']", buildYangInstanceIdentifier(BAR_BEE, ImmutableSet.of()));
137 assertSerdes("/bar:bee[.='one']", buildYangInstanceIdentifier(BAR_BEE, ImmutableSet.of("one")));
138 assertSerdes("/bar:bee[.='two three']", buildYangInstanceIdentifier(BAR_BEE, ImmutableSet.of("two", "three")));
141 private static void assertSerdes(final String expected, final YangInstanceIdentifier id) throws Exception {
142 final var writer = mock(XMLStreamWriter.class);
143 final var captor = ArgumentCaptor.forClass(String.class);
144 CODEC.writeValue(writer, id);
145 verify(writer).writeCharacters(captor.capture());
146 assertEquals(expected, captor.getValue());
148 final var context = mock(NamespaceContext.class);
149 doReturn("foons").when(context).getNamespaceURI("foo");
150 doReturn("barns").when(context).getNamespaceURI("bar");
151 assertEquals(id, CODEC.parseValue(context, expected));
154 private static YangInstanceIdentifier buildYangInstanceIdentifier(final QName node, final QName key,
155 final Object value) {
156 return YangInstanceIdentifier.create(
157 new NodeIdentifier(node), NodeIdentifierWithPredicates.of(node, key, value));
160 private static YangInstanceIdentifier buildYangInstanceIdentifier(final QName node, final Object value) {
161 return YangInstanceIdentifier.create(new NodeIdentifier(node), new NodeWithValue<>(node, value));