From: Robert Varga Date: Fri, 6 Jan 2023 14:24:24 +0000 (+0100) Subject: Tests for YangInstanceIdentifier key value serialization X-Git-Tag: v10.0.3~8 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=6fca49a9a8daff89d5304c15d3e5c734dfa3da11;p=yangtools.git Tests for YangInstanceIdentifier key value serialization There are a number of corner cases which we do not handle. This patch adds @Ignored tests which hightlight basic serialization problems here. JIRA: YANGTOOLS-1473 Change-Id: I6226cc8ebe48acc03a62309efec2ab205549e0fb Signed-off-by: Ruslan Kashapov Signed-off-by: Robert Varga --- diff --git a/codec/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/YT1473Test.java b/codec/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/YT1473Test.java new file mode 100644 index 0000000000..7434a3209b --- /dev/null +++ b/codec/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/YT1473Test.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.yangtools.yang.data.codec.gson; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.verify; + +import com.google.gson.stream.JsonWriter; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue; +import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; +import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition; +import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils; + +@ExtendWith(MockitoExtension.class) +public class YT1473Test { + + private static final String FOO_NS = "foons"; // namespace for prefix 'foo' + private static final QName FOO_FOO = QName.create(FOO_NS, "foo"); // list with key 'str' + private static final QName FOO_BAR = QName.create(FOO_NS, "bar"); // list with key 'qname' + private static final QName FOO_BAZ = QName.create(FOO_NS, "baz"); // list with key 'id' + private static final QName FOO_ONE = QName.create(FOO_NS, "one"); // identity + private static final QName FOO_STR = QName.create(FOO_NS, "str"); // key of type 'string' + private static final QName FOO_QNAME = QName.create(FOO_NS, "qname"); // key of type 'one' based + private static final QName FOO_ID = QName.create(FOO_NS, "id"); // key of type 'instance-identifier' + + private static final String BAR_NS = "barns"; // namespace for prefix 'bar' + private static final QName BAR_FOO = QName.create(BAR_NS, "foo"); // leaf of type 'foo:one' based + private static final QName BAR_BAR = QName.create(BAR_NS, "bar"); // leaf of type 'instance-identifier' + private static final QName BAR_TWO = QName.create(BAR_NS, "two"); // identity inheriting 'foo:one' + + private static JSONCodec CODEC; + + @Mock + private JsonWriter writer; + @Captor + private ArgumentCaptor captor; + + @BeforeAll + public static void beforeAll() { + final var modelContext = YangParserTestUtils.parseYangResourceDirectory("/yt1473"); + final var baz = modelContext.getDataChildByName(FOO_BAZ); + assertTrue(baz instanceof ListSchemaNode); + final var id = ((ListSchemaNode) baz).getDataChildByName(FOO_ID); + assertTrue(id instanceof LeafSchemaNode); + final var type = ((LeafSchemaNode) id).getType(); + assertTrue(type instanceof InstanceIdentifierTypeDefinition); + CODEC = JSONCodecFactorySupplier.RFC7951.getShared(modelContext) + .instanceIdentifierCodec((InstanceIdentifierTypeDefinition) type); + } + + @AfterAll + public static void afterAll() { + CODEC = null; + } + + @Test + public void testSerializeSimple() throws Exception { + // No escaping needed, use single quotes + assertEquals("/foo:foo[str='str\"']", write(buildYangInstanceIdentifier(FOO_FOO, FOO_STR, "str\""))); + } + + @Test + @Disabled("YT-1473: string escaping needs to work") + public void testSerializeEscaped() throws Exception { + // Escaping is needed, use double quotes and escape + assertEquals("/foo:foo[str=\"str'\\\"\"]", write(buildYangInstanceIdentifier(FOO_FOO, FOO_STR, "str'\""))); + } + + @Test + @Disabled("YT-1473: QName values need to be recognized and properly encoded via identity codec") + public void testSerializeIdentityRefSame() throws Exception { + // TODO: an improvement is to use just 'one' as the namespace is the same as the leaf (see RFC7951 section 6.8) + assertEquals("/foo:bar[foo:qname='foo:one']", write(buildYangInstanceIdentifier(FOO_BAR, FOO_QNAME, FOO_ONE))); + } + + @Test + @Disabled("YT-1473: QName values need to be recognized and properly encoded via identity codec") + public void testSerializeIdentityRefOther() throws Exception { + // No escaping is needed, use double quotes and escape + assertEquals("/foo:bar[qname='bar:two']", write(buildYangInstanceIdentifier(FOO_BAR, FOO_QNAME, BAR_TWO))); + } + + @Test + @Disabled("YT-1473: Instance-identifier values need to be recognized and properly encoded and escaped") + public void testSerializeInstanceIdentifierRef() throws Exception { + assertEquals("/foo:baz[id=\"/foo:bar[qname='bar:two']\"]", write( + buildYangInstanceIdentifier(FOO_BAZ, FOO_ID, buildYangInstanceIdentifier(FOO_BAR, FOO_QNAME, BAR_TWO))) + ); + } + + @Test + @Disabled("YT-1473: QName values need to be recognized and properly encoded via identity codec") + public void testSerializeIdentityValue() throws Exception { + assertEquals("/bar:foo[.='foo:one']", write(buildYangInstanceIdentifier(BAR_FOO, FOO_ONE))); + } + + @Test + @Disabled("YT-1473: Instance-identifier values need to be recognized and properly encoded and escaped") + public void testSerializeInstanceIdentifierValue() throws Exception { + assertEquals("/bar:bar[.=\"/foo:bar/bar[qname='bar:two'\"]']", + write(buildYangInstanceIdentifier(BAR_BAR, buildYangInstanceIdentifier(FOO_BAR, FOO_QNAME, BAR_TWO)))); + } + + private static YangInstanceIdentifier buildYangInstanceIdentifier(final QName node, final QName key, + final Object value) { + return YangInstanceIdentifier.create( + new NodeIdentifier(node), NodeIdentifierWithPredicates.of(node, key, value)); + } + + private static YangInstanceIdentifier buildYangInstanceIdentifier(final QName nodeQName, final Object value) { + return YangInstanceIdentifier.create(new NodeWithValue<>(nodeQName, value)); + } + + private String write(final YangInstanceIdentifier yangInstanceIdentifier) throws Exception { + doReturn(writer).when(writer).value(anyString()); + CODEC.writeValue(writer, yangInstanceIdentifier); + verify(writer).value(captor.capture()); + return captor.getValue(); + } +} diff --git a/codec/yang-data-codec-gson/src/test/resources/yt1473/bar.yang b/codec/yang-data-codec-gson/src/test/resources/yt1473/bar.yang new file mode 100644 index 0000000000..dceeacac54 --- /dev/null +++ b/codec/yang-data-codec-gson/src/test/resources/yt1473/bar.yang @@ -0,0 +1,20 @@ +module bar { + namespace barns; + prefix bar; + + import foo { prefix foo; } + + identity two { + base foo:one; + } + + leaf foo { + type identityref { + base foo:one; + } + } + + leaf bar { + type instance-identifier; + } +} diff --git a/codec/yang-data-codec-gson/src/test/resources/yt1473/foo.yang b/codec/yang-data-codec-gson/src/test/resources/yt1473/foo.yang new file mode 100644 index 0000000000..9690484b8b --- /dev/null +++ b/codec/yang-data-codec-gson/src/test/resources/yt1473/foo.yang @@ -0,0 +1,29 @@ +module foo { + namespace foons; + prefix foo; + + identity one; + + list foo { + key str; + leaf str { + type string; + } + } + + list bar { + key qname; + leaf qname { + type identityref { + base one; + } + } + } + + list baz { + key id; + leaf id { + type instance-identifier; + } + } +} diff --git a/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/YT1473Test.java b/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/YT1473Test.java new file mode 100644 index 0000000000..163f9c0324 --- /dev/null +++ b/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/YT1473Test.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.yangtools.yang.data.codec.xml; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.verify; + +import javax.xml.stream.XMLStreamWriter; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue; +import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; +import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition; +import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils; + +@ExtendWith(MockitoExtension.class) +public class YT1473Test { + + private static final String FOO_NS = "foons"; // namespace for prefix 'foo' + private static final QName FOO_FOO = QName.create(FOO_NS, "foo"); // list with key 'str' + private static final QName FOO_BAR = QName.create(FOO_NS, "bar"); // list with key 'qname' + private static final QName FOO_BAZ = QName.create(FOO_NS, "baz"); // list with key 'id' + private static final QName FOO_ONE = QName.create(FOO_NS, "one"); // identity + private static final QName FOO_STR = QName.create(FOO_NS, "str"); // key of type 'string' + private static final QName FOO_QNAME = QName.create(FOO_NS, "qname"); // key of type 'one' based + private static final QName FOO_ID = QName.create(FOO_NS, "id"); // key of type 'instance-identifier' + + private static final String BAR_NS = "barns"; // namespace for prefix 'bar' + private static final QName BAR_FOO = QName.create(BAR_NS, "foo"); // leaf of type 'foo:one' based + private static final QName BAR_BAR = QName.create(BAR_NS, "bar"); // leaf of type 'instance-identifier' + private static final QName BAR_TWO = QName.create(BAR_NS, "two"); // identity inheriting 'foo:one' + + private static XmlCodec CODEC; + + @Mock + private XMLStreamWriter writer; + @Captor + private ArgumentCaptor captor; + + @BeforeAll + public static void beforeAll() { + final var modelContext = YangParserTestUtils.parseYangResourceDirectory("/yt1473"); + final var baz = modelContext.getDataChildByName(FOO_BAZ); + assertTrue(baz instanceof ListSchemaNode); + final var id = ((ListSchemaNode) baz).getDataChildByName(FOO_ID); + assertTrue(id instanceof LeafSchemaNode); + final var type = ((LeafSchemaNode) id).getType(); + assertTrue(type instanceof InstanceIdentifierTypeDefinition); + CODEC = (XmlStringInstanceIdentifierCodec) XmlCodecFactory.create(modelContext) + .instanceIdentifierCodec((InstanceIdentifierTypeDefinition) type); + } + + @AfterAll + public static void afterAll() { + CODEC = null; + } + + @Test + public void testSerializeSimple() throws Exception { + // No escaping needed, use single quotes + assertEquals("/foo:foo[foo:str='str\"']", write(buildYangInstanceIdentifier(FOO_FOO, FOO_STR, "str\""))); + } + + @Test + @Disabled("YT-1473: string escaping needs to work") + public void testSerializeEscaped() throws Exception { + // Escaping is needed, use double quotes and escape + assertEquals("/foo:foo[foo:str=\"str'\\\"\"]", write(buildYangInstanceIdentifier(FOO_FOO, FOO_STR, "str'\""))); + } + + @Test + @Disabled("YT-1473: QName values need to be recognized and properly encoded via identity codec") + public void testSerializeIdentityRefSame() throws Exception { + // TODO: an improvement is to use just 'one' as the namespace is the same as the leaf (see RFC7951 section 6.8) + assertEquals("/foo:bar[qname='one']", write(buildYangInstanceIdentifier(FOO_BAR, FOO_QNAME, FOO_ONE))); + } + + @Test + @Disabled("YT-1473: QName values need to be recognized and properly encoded via identity codec") + public void testSerializeIdentityRefOther() throws Exception { + // No escaping is needed, use double quotes and escape + assertEquals("/foo:bar[qname='bar:two']", write(buildYangInstanceIdentifier(FOO_BAR, FOO_QNAME, BAR_TWO))); + } + + @Test + @Disabled("YT-1473: Instance-identifier values need to be recognized and properly encoded and escaped") + public void testSerializeInstanceIdentifierRef() throws Exception { + assertEquals("/foo:baz[id=\"/foo:bar[qname='bar:two']\"]", write( + buildYangInstanceIdentifier(FOO_BAZ, FOO_ID, buildYangInstanceIdentifier(FOO_BAR, FOO_QNAME, BAR_TWO))) + ); + } + + @Test + @Disabled("YT-1473: QName values need to be recognized and properly encoded via identity codec") + public void testSerializeIdentityValue() throws Exception { + assertEquals("/bar:foo[.='foo:one']", write(buildYangInstanceIdentifier(BAR_FOO, FOO_ONE))); + } + + @Test + @Disabled("YT-1473: Instance-identifier values need to be recognized and properly encoded and escaped") + public void testSerializeInstanceIdentifierValue() throws Exception { + assertEquals("/bar:bar[.=\"/foo:bar/bar[qname='bar:two'\"]']", + write(buildYangInstanceIdentifier(BAR_BAR, buildYangInstanceIdentifier(FOO_BAR, FOO_QNAME, BAR_TWO)))); + } + + private static YangInstanceIdentifier buildYangInstanceIdentifier(final QName node, final QName key, + final Object value) { + return YangInstanceIdentifier.create( + new NodeIdentifier(node), NodeIdentifierWithPredicates.of(node, key, value)); + } + + private static YangInstanceIdentifier buildYangInstanceIdentifier(final QName node, final Object value) { + return YangInstanceIdentifier.create(new NodeWithValue<>(node, value)); + } + + private String write(final YangInstanceIdentifier yangInstanceIdentifier) throws Exception { + CODEC.writeValue(writer, yangInstanceIdentifier); + verify(writer).writeCharacters(captor.capture()); + return captor.getValue(); + } +} diff --git a/codec/yang-data-codec-xml/src/test/resources/yt1473/bar.yang b/codec/yang-data-codec-xml/src/test/resources/yt1473/bar.yang new file mode 100644 index 0000000000..dceeacac54 --- /dev/null +++ b/codec/yang-data-codec-xml/src/test/resources/yt1473/bar.yang @@ -0,0 +1,20 @@ +module bar { + namespace barns; + prefix bar; + + import foo { prefix foo; } + + identity two { + base foo:one; + } + + leaf foo { + type identityref { + base foo:one; + } + } + + leaf bar { + type instance-identifier; + } +} diff --git a/codec/yang-data-codec-xml/src/test/resources/yt1473/foo.yang b/codec/yang-data-codec-xml/src/test/resources/yt1473/foo.yang new file mode 100644 index 0000000000..9690484b8b --- /dev/null +++ b/codec/yang-data-codec-xml/src/test/resources/yt1473/foo.yang @@ -0,0 +1,29 @@ +module foo { + namespace foons; + prefix foo; + + identity one; + + list foo { + key str; + leaf str { + type string; + } + } + + list bar { + key qname; + leaf qname { + type identityref { + base one; + } + } + } + + list baz { + key id; + leaf id { + type instance-identifier; + } + } +}