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.doNothing;
13 import static org.mockito.Mockito.doReturn;
14 import static org.mockito.Mockito.mock;
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.Test;
22 import org.junit.jupiter.api.extension.ExtendWith;
23 import org.mockito.ArgumentCaptor;
24 import org.mockito.junit.jupiter.MockitoExtension;
25 import org.opendaylight.yangtools.yang.common.QName;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
28 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
31 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
33 @ExtendWith(MockitoExtension.class)
35 private static final String FOO_NS = "foons"; // namespace for prefix 'foo'
36 private static final QName FOO_FOO = QName.create(FOO_NS, "foo"); // list with key 'str'
37 private static final QName FOO_BAR = QName.create(FOO_NS, "bar"); // list with key 'qname'
38 private static final QName FOO_BAZ = QName.create(FOO_NS, "baz"); // list with key 'id'
39 private static final QName FOO_BEE = QName.create(FOO_NS, "bee"); // list with key 'bts'
40 private static final QName FOO_ONE = QName.create(FOO_NS, "one"); // identity
41 private static final QName FOO_STR = QName.create(FOO_NS, "str"); // key of type 'string'
42 private static final QName FOO_QNAME = QName.create(FOO_NS, "qname"); // key of type 'one' based
43 private static final QName FOO_ID = QName.create(FOO_NS, "id"); // key of type 'instance-identifier'
44 private static final QName FOO_BTS = QName.create(FOO_NS, "bts"); // key of type 'bts' (bits)
46 private static final String BAR_NS = "barns"; // namespace for prefix 'bar'
47 private static final QName BAR_TWO = QName.create(BAR_NS, "two"); // identity inheriting 'foo:one'
48 private static final QName BAR_STR = QName.create(BAR_NS, "str"); // leaf-list of type 'string'
49 private static final QName BAR_FOO = QName.create(BAR_NS, "foo"); // leaf-list of type 'foo:one' based
50 private static final QName BAR_BAR = QName.create(BAR_NS, "bar"); // leaf-list of type 'instance-identifier'
51 private static final QName BAR_BEE = QName.create(BAR_NS, "bee"); // leaf-list of type 'foo:bts' (bits)
53 private static XmlCodec<YangInstanceIdentifier> CODEC;
56 static void beforeAll() {
57 final var modelContext = YangParserTestUtils.parseYang("""
81 type instance-identifier;
89 type instance-identifier;
124 type instance-identifier;
135 final var baz = assertInstanceOf(ListSchemaNode.class, modelContext.getDataChildByName(FOO_BAZ));
136 final var id = assertInstanceOf(LeafSchemaNode.class, baz.getDataChildByName(FOO_ID));
137 final var type = assertInstanceOf(InstanceIdentifierTypeDefinition.class, id.getType());
138 CODEC = XmlCodecFactory.create(modelContext).instanceIdentifierCodec(type);
142 static void afterAll() {
147 void testSerializeSimple() throws Exception {
148 // No escaping needed, use single quotes
149 assertBar("/b:str[.='str\"']", createIdentifier(BAR_STR, "str\""));
150 assertBar("/b:str[.='str\\']", createIdentifier(BAR_STR, "str\\"));
151 assertBar("/b:str[.='str\r']", createIdentifier(BAR_STR, "str\r"));
152 assertBar("/b:str[.='str\n']", createIdentifier(BAR_STR, "str\n"));
153 assertBar("/b:str[.='str\t']", createIdentifier(BAR_STR, "str\t"));
155 assertFoo("/f:foo[f:str='str\"\\']", createIdentifier(FOO_FOO, FOO_STR, "str\"\\"));
156 assertFoo("/f:foo[f:str='str\r\n\t']", createIdentifier(FOO_FOO, FOO_STR, "str\r\n\t"));
160 void testSerializeEscaped() throws Exception {
161 // Escaping is needed, use double quotes and escape
162 assertBar("/b:str[.=\"str'\\\"\"]", createIdentifier(BAR_STR, "str'\""));
163 assertBar("/b:str[.=\"str'\\n\"]", createIdentifier(BAR_STR, "str'\n"));
164 assertBar("/b:str[.=\"str'\\t\"]", createIdentifier(BAR_STR, "str'\t"));
165 assertBar("/b:str[.=\"str'\r\"]", createIdentifier(BAR_STR, "str'\r"));
167 assertFoo("/f:foo[f:str=\"str'\\\"\\n\"]", createIdentifier(FOO_FOO, FOO_STR, "str'\"\n"));
168 assertFoo("/f:foo[f:str=\"str'\\t\r\"]", createIdentifier(FOO_FOO, FOO_STR, "str'\t\r"));
172 void testSerializeIdentity() throws Exception {
173 assertFoo("/f:bar[f:qname='f:one']", createIdentifier(FOO_BAR, FOO_QNAME, FOO_ONE));
174 assertFooBar("/f:bar[f:qname='b:two']", createIdentifier(FOO_BAR, FOO_QNAME, BAR_TWO));
178 void testSerializeInstanceIdentifierRef() throws Exception {
179 assertFooBar("/f:baz[f:id=\"/f:bar[f:qname='b:two']\"]",
180 createIdentifier(FOO_BAZ, FOO_ID, createIdentifier(FOO_BAR, FOO_QNAME, BAR_TWO)));
184 void testSerializeIdentityValue() throws Exception {
185 assertFooBar("/b:foo[.='f:one']", createIdentifier(BAR_FOO, FOO_ONE));
186 assertBar("/b:foo[.='b:two']", createIdentifier(BAR_FOO, BAR_TWO));
190 void testSerializeInstanceIdentifierValue() throws Exception {
191 assertFooBar("/b:bar[.=\"/f:bar[f:qname='f:one']\"]",
192 createIdentifier(BAR_BAR, createIdentifier(FOO_BAR, FOO_QNAME, FOO_ONE)));
193 assertFooBar("/b:bar[.=\"/f:bar[f:qname='b:two']\"]",
194 createIdentifier(BAR_BAR, createIdentifier(FOO_BAR, FOO_QNAME, BAR_TWO)));
198 void testSerializeBits() throws Exception {
199 assertFoo("/f:bee[f:bts='']", createIdentifier(FOO_BEE, FOO_BTS, ImmutableSet.of()));
200 assertFoo("/f:bee[f:bts='one']", createIdentifier(FOO_BEE, FOO_BTS, ImmutableSet.of("one")));
201 assertFoo("/f:bee[f:bts='two three']", createIdentifier(FOO_BEE, FOO_BTS, ImmutableSet.of("two", "three")));
205 void testSerializeBitsValue() throws Exception {
206 assertBar("/b:bee[.='']", createIdentifier(BAR_BEE, ImmutableSet.of()));
207 assertBar("/b:bee[.='one']", createIdentifier(BAR_BEE, ImmutableSet.of("one")));
208 assertBar("/b:bee[.='two three']", createIdentifier(BAR_BEE, ImmutableSet.of("two", "three")));
211 private static void assertBar(final String expected, final YangInstanceIdentifier id) throws Exception {
212 final var writer = mockWriter();
213 doNothing().when(writer).writeNamespace("b", "barns");
215 final var reader = mock(NamespaceContext.class);
216 doReturn("barns").when(reader).getNamespaceURI("b");
217 assertSerdes(reader, writer, expected, id);
220 private static void assertFoo(final String expected, final YangInstanceIdentifier id) throws Exception {
221 final var writer = mockWriter();
222 doNothing().when(writer).writeNamespace("f", "foons");
224 final var reader = mock(NamespaceContext.class);
225 doReturn("foons").when(reader).getNamespaceURI("f");
227 assertSerdes(reader, writer, expected, id);
230 private static void assertFooBar(final String expected, final YangInstanceIdentifier id) throws Exception {
231 final var writer = mockWriter();
232 doNothing().when(writer).writeNamespace("f", "foons");
233 doNothing().when(writer).writeNamespace("b", "barns");
235 final var reader = mock(NamespaceContext.class);
236 doReturn("foons").when(reader).getNamespaceURI("f");
237 doReturn("barns").when(reader).getNamespaceURI("b");
239 assertSerdes(reader, writer, expected, id);
242 private static void assertSerdes(final NamespaceContext reader, final XMLStreamWriter writer,
243 final String expected, final YangInstanceIdentifier id) throws Exception {
244 assertEquals(id, CODEC.parseValue(reader, expected));
246 final var captor = ArgumentCaptor.forClass(String.class);
247 doNothing().when(writer).writeCharacters(captor.capture());
248 CODEC.writeValue(writer, id);
249 assertEquals(expected, captor.getValue());
252 private static XMLStreamWriter mockWriter() {
253 final var writer = mock(XMLStreamWriter.class);
254 doReturn(null).when(writer).getNamespaceContext();
258 private static YangInstanceIdentifier createIdentifier(final QName node, final QName key, final Object value) {
259 return YangInstanceIdentifier.builder().node(node).nodeWithKey(node, key, value).build();
262 private static YangInstanceIdentifier createIdentifier(final QName node, final Object value) {
263 return YangInstanceIdentifier.builder().node(node).node(new NodeWithValue<>(node, value)).build();