02adaa5ba59227232f9db48e6606926098dfd2df
[yangtools.git] / codec / yang-data-codec-gson / src / test / java / org / opendaylight / yangtools / yang / data / codec / gson / YT1473Test.java
1 /*
2  * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.yangtools.yang.data.codec.gson;
9
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
12 import static org.mockito.ArgumentMatchers.anyString;
13 import static org.mockito.Mockito.doReturn;
14 import static org.mockito.Mockito.mock;
15 import static org.mockito.Mockito.verify;
16
17 import com.google.common.collect.ImmutableSet;
18 import com.google.gson.stream.JsonWriter;
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;
33
34 class YT1473Test {
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)
45
46     private static final String BAR_NS = "barns"; // namespace for prefix 'bar'
47     private static final QName BAR_STR = QName.create(BAR_NS, "str"); // leaf-list of type 'string'
48     private static final QName BAR_FOO = QName.create(BAR_NS, "foo"); // leaf-list of type 'foo:one' based
49     private static final QName BAR_BAR = QName.create(BAR_NS, "bar"); // leaf-list of type 'instance-identifier'
50     private static final QName BAR_BEE = QName.create(BAR_NS, "bee"); // leaf-list of type 'foo:bts' (bits)
51     private static final QName BAR_TWO = QName.create(BAR_NS, "two"); // identity inheriting 'foo:one'
52
53     private static JSONCodec<YangInstanceIdentifier> CODEC;
54
55     @BeforeAll
56     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 = JSONCodecFactorySupplier.RFC7951.getShared(modelContext).instanceIdentifierCodec(type);
62     }
63
64     @AfterAll
65     static void afterAll() {
66         CODEC = null;
67     }
68
69     @Test
70     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"));
77
78         assertSerdes("/foo:foo[str='str\"\\']", buildYangInstanceIdentifier(FOO_FOO, FOO_STR, "str\"\\"));
79         assertSerdes("/foo:foo[str='str\r\n\t']", buildYangInstanceIdentifier(FOO_FOO, FOO_STR, "str\r\n\t"));
80     }
81
82     @Test
83     @Disabled("YT-1473: string escaping needs to work")
84     void testSerializeEscaped() throws Exception {
85         // Escaping is needed, use double quotes and escape
86         assertSerdes("/bar:str[.=\"str'\\\"\"]", buildYangInstanceIdentifier(BAR_STR, "str'\""));
87         assertSerdes("/bar:str[.=\"str'\\n\"]", buildYangInstanceIdentifier(BAR_STR, "str'\n"));
88         assertSerdes("/bar:str[.=\"str'\\t\"]", buildYangInstanceIdentifier(BAR_STR, "str'\t"));
89         assertSerdes("/bar:str[.=\"str'\r\"]", buildYangInstanceIdentifier(BAR_STR, "str'\r"));
90
91         assertSerdes("/foo:foo[str=\"str'\\\"\\n\"]", buildYangInstanceIdentifier(FOO_FOO, FOO_STR, "str'\"\n"));
92         assertSerdes("/foo:foo[str=\"str'\\t\r\"]", buildYangInstanceIdentifier(FOO_FOO, FOO_STR, "str'\t\r"));
93     }
94
95     @Test
96     @Disabled("YT-1473: QName values need to be recognized and properly encoded via identity codec")
97     void testSerializeIdentityRefSame() throws Exception {
98         assertSerdes("/foo:bar[qname='one']", buildYangInstanceIdentifier(FOO_BAR, FOO_QNAME, FOO_ONE));
99     }
100
101     @Test
102     @Disabled("YT-1473: QName values need to be recognized and properly encoded via identity codec")
103     void testSerializeIdentityRefOther() throws Exception {
104         // No escaping is needed, use double quotes and escape
105         assertSerdes("/foo:bar[qname='bar:two']", buildYangInstanceIdentifier(FOO_BAR, FOO_QNAME, BAR_TWO));
106     }
107
108     @Test
109     @Disabled("YT-1473: Instance-identifier values need to be recognized and properly encoded and escaped")
110     void testSerializeInstanceIdentifierRef() throws Exception {
111         assertSerdes("/foo:baz[id=\"/foo:bar[qname='bar:two']\"]",
112             buildYangInstanceIdentifier(FOO_BAZ, FOO_ID, buildYangInstanceIdentifier(FOO_BAR, FOO_QNAME, BAR_TWO)));
113     }
114
115     @Test
116     @Disabled("YT-1473: QName values need to be recognized and properly encoded via identity codec")
117     void testSerializeIdentityValue() throws Exception {
118         assertSerdes("/bar:foo[.='foo:one']", buildYangInstanceIdentifier(BAR_FOO, FOO_ONE));
119         assertSerdes("/bar:foo[.='two']", buildYangInstanceIdentifier(BAR_FOO, BAR_TWO));
120     }
121
122     @Test
123     @Disabled("YT-1473: Instance-identifier values need to be recognized and properly encoded and escaped")
124     void testSerializeInstanceIdentifierValue() throws Exception {
125         assertSerdes("/bar:bar[.=\"/foo:bar[qname='bar:two']\"]",
126             buildYangInstanceIdentifier(BAR_BAR, buildYangInstanceIdentifier(FOO_BAR, FOO_QNAME, BAR_TWO)));
127         assertSerdes("/bar:bar[.=\"/foo:bar[qname='one']\"]",
128             buildYangInstanceIdentifier(BAR_BAR, buildYangInstanceIdentifier(FOO_BAR, FOO_QNAME, FOO_ONE)));
129     }
130
131     @Test
132     @Disabled("YT-1473: bits values need to be recognized and properly encoded")
133     void testSerializeBits() throws Exception {
134         assertSerdes("/foo:bee[bts='']", buildYangInstanceIdentifier(FOO_BEE, FOO_BTS, ImmutableSet.of()));
135         assertSerdes("/foo:bee[bts='one']", buildYangInstanceIdentifier(FOO_BEE, FOO_BTS, ImmutableSet.of("one")));
136         assertSerdes("/foo:bee[bts='two three']",
137             buildYangInstanceIdentifier(FOO_BEE, FOO_BTS, ImmutableSet.of("two", "three")));
138     }
139
140     @Test
141     @Disabled("YT-1473: bits values need to be recognized and properly encoded")
142     void testSerializeBitsValue() throws Exception {
143         assertSerdes("/bar:bee[.='']", buildYangInstanceIdentifier(BAR_BEE, ImmutableSet.of()));
144         assertSerdes("/bar:bee[.='one']", buildYangInstanceIdentifier(BAR_BEE, ImmutableSet.of("one")));
145         assertSerdes("/bar:bee[.='two three']", buildYangInstanceIdentifier(BAR_BEE, ImmutableSet.of("two", "three")));
146     }
147
148     private static void assertSerdes(final String expected, final YangInstanceIdentifier id) throws Exception {
149         final var writer = mock(JsonWriter.class);
150         final var captor = ArgumentCaptor.forClass(String.class);
151         doReturn(writer).when(writer).value(anyString());
152         CODEC.writeValue(writer, id);
153         verify(writer).value(captor.capture());
154
155         assertEquals(expected, captor.getValue());
156         assertEquals(id, CODEC.parseValue(null, expected));
157     }
158
159     private static YangInstanceIdentifier buildYangInstanceIdentifier(final QName node, final QName key,
160             final Object value) {
161         return YangInstanceIdentifier.create(
162                 new NodeIdentifier(node), NodeIdentifierWithPredicates.of(node, key, value));
163     }
164
165     private static YangInstanceIdentifier buildYangInstanceIdentifier(final QName node, final Object value) {
166         return YangInstanceIdentifier.create(new NodeIdentifier(node), new NodeWithValue<>(node, value));
167     }
168 }