From 87edd5c3bb219df62aa3bd30b1ed49cc25fd7c47 Mon Sep 17 00:00:00 2001 From: Peter Kajsa Date: Wed, 11 May 2016 11:04:39 +0200 Subject: [PATCH] Bug 5446: Yangtools UnionStringCodec is not consistent with BinaryStringCodec Yangtools union codec serializes byte array via invoking of toString() method on byte[] which leads to undesirable results. Change-Id: Ib5c8efb1b28cf7f8e51d9791dd4a30078ebbd6bd Signed-off-by: Peter Kajsa --- .../yang/data/codec/gson/Bug5446Test.java | 94 +++++++++ .../src/test/resources/bug5446/json/foo.json | 5 + .../src/test/resources/bug5446/yang/foo.yang | 33 +++ .../data/impl/codec/UnionStringCodec.java | 8 +- .../transform/dom/serializer/Bug5446Test.java | 192 ++++++++++++++++++ .../src/test/resources/bug5446/xml/foo.xml | 6 + .../src/test/resources/bug5446/yang/foo.yang | 33 +++ 7 files changed, 370 insertions(+), 1 deletion(-) create mode 100644 yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/Bug5446Test.java create mode 100644 yang/yang-data-codec-gson/src/test/resources/bug5446/json/foo.json create mode 100644 yang/yang-data-codec-gson/src/test/resources/bug5446/yang/foo.yang create mode 100644 yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/dom/serializer/Bug5446Test.java create mode 100644 yang/yang-data-impl/src/test/resources/bug5446/xml/foo.xml create mode 100644 yang/yang-data-impl/src/test/resources/bug5446/yang/foo.yang diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/Bug5446Test.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/Bug5446Test.java new file mode 100644 index 0000000000..c75b17b55f --- /dev/null +++ b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/Bug5446Test.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. 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 com.google.common.io.BaseEncoding; +import com.google.gson.JsonElement; +import com.google.gson.JsonIOException; +import com.google.gson.JsonParser; +import com.google.gson.JsonSyntaxException; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.net.URI; +import java.net.URISyntaxException; +import org.junit.BeforeClass; +import org.junit.Test; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.QNameModule; +import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter; +import org.opendaylight.yangtools.yang.data.codec.gson.retest.RetestUtils; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; + +public class Bug5446Test { + + private static QNameModule fooModuleQName; + private static QName rootQName; + private static QName ipAddressQName; + private static SchemaContext schemaContext; + + @BeforeClass + public static void init() throws Exception { + fooModuleQName = QNameModule.create(new URI("foo"), SimpleDateFormatUtil.getRevisionFormat() + .parse("2015-11-05")); + rootQName = QName.create(fooModuleQName, "root"); + ipAddressQName = QName.create(fooModuleQName, "ip-address"); + + schemaContext = RetestUtils.parseYangSources(new File(Bug5446Test.class.getResource("/bug5446/yang/foo.yang") + .toURI())); + } + + @Test + public void test() throws IOException, JsonIOException, JsonSyntaxException, URISyntaxException { + final DataContainerChild rootNode = createRootNode(); + + final Writer writer = new StringWriter(); + final String jsonOutput = normalizedNodeToJsonStreamTransformation(writer, rootNode); + + final JsonParser parser = new JsonParser(); + final JsonElement serializedJson = parser.parse(jsonOutput); + final JsonElement expextedJson = parser.parse(new FileReader(new File(getClass().getResource( + "/bug5446/json/foo.json").toURI()))); + + assertEquals(expextedJson, serializedJson); + } + + private static String normalizedNodeToJsonStreamTransformation(final Writer writer, + final NormalizedNode inputStructure) throws IOException { + + final NormalizedNodeStreamWriter jsonStream = JSONNormalizedNodeStreamWriter.createExclusiveWriter( + JSONCodecFactory.create(schemaContext), SchemaPath.ROOT, null, + JsonWriterFactory.createJsonWriter(writer, 2)); + final NormalizedNodeWriter nodeWriter = NormalizedNodeWriter.forStreamWriter(jsonStream); + nodeWriter.write(inputStructure); + + nodeWriter.close(); + return writer.toString(); + } + + private ContainerNode createRootNode() { + LeafNode ipAddress = ImmutableNodes.leafNode(ipAddressQName, BaseEncoding.base64().decode("fwAAAQ==")); + return ImmutableContainerNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(rootQName)) + .withChild(ipAddress).build(); + } +} diff --git a/yang/yang-data-codec-gson/src/test/resources/bug5446/json/foo.json b/yang/yang-data-codec-gson/src/test/resources/bug5446/json/foo.json new file mode 100644 index 0000000000..a62eb740e1 --- /dev/null +++ b/yang/yang-data-codec-gson/src/test/resources/bug5446/json/foo.json @@ -0,0 +1,5 @@ +{ + "foo:root":{ + "ip-address":"fwAAAQ==" + } +} \ No newline at end of file diff --git a/yang/yang-data-codec-gson/src/test/resources/bug5446/yang/foo.yang b/yang/yang-data-codec-gson/src/test/resources/bug5446/yang/foo.yang new file mode 100644 index 0000000000..b5f78ca029 --- /dev/null +++ b/yang/yang-data-codec-gson/src/test/resources/bug5446/yang/foo.yang @@ -0,0 +1,33 @@ +module foo { + yang-version 1; + namespace "foo"; + prefix "foo"; + + revision "2015-11-05" { + } + + typedef ipv4-address-binary { + type binary { + length "4"; + } + } + + typedef ipv6-address-binary { + type binary { + length "16"; + } + } + + typedef ip-address-binary { + type union { + type ipv4-address-binary; + type ipv6-address-binary; + } + } + + container root { + leaf ip-address { + type ip-address-binary; + } + } +} diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/UnionStringCodec.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/UnionStringCodec.java index a117ceef56..6d47ab94ab 100644 --- a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/UnionStringCodec.java +++ b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/UnionStringCodec.java @@ -9,6 +9,8 @@ package org.opendaylight.yangtools.yang.data.impl.codec; import com.google.common.base.Optional; +import com.google.common.io.BaseEncoding; +import java.util.Objects; import org.opendaylight.yangtools.yang.data.api.codec.UnionCodec; import org.opendaylight.yangtools.yang.model.api.TypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition; @@ -29,7 +31,11 @@ final class UnionStringCodec extends TypeDefinitionAwareCodec> root = docNode.getChild(new NodeIdentifier(rootQName)); + assertTrue(root.orNull() instanceof ContainerNode); + + Optional> child = ((ContainerNode) root.orNull()) + .getChild(new NodeIdentifier(ipAddressQName)); + assertTrue(child.orNull() instanceof LeafNode); + LeafNode ipAdress = (LeafNode) child.get(); + + Object value = ipAdress.getValue(); + assertTrue(value instanceof byte[]); + assertEquals("fwAAAQ==", BaseEncoding.base64().encode((byte[]) value)); + + DOMResult serializationResult = writeNormalizedNode(docNode, schemaContext); + assertNotNull(serializationResult); + + XMLUnit.setIgnoreWhitespace(true); + XMLUnit.setIgnoreComments(true); + XMLUnit.setIgnoreAttributeOrder(true); + XMLUnit.setNormalize(true); + + String expectedXMLString = toString(doc.getDocumentElement().getElementsByTagName("root").item(0)); + String serializationResultXMLString = toString(serializationResult.getNode()); + + assertXMLEqual(expectedXMLString, serializationResultXMLString); + } + + private ContainerNode createDocNode() { + LeafNode ipAddress = ImmutableNodes.leafNode(ipAddressQName, BaseEncoding.base64().decode("fwAAAQ==")); + ContainerNode root = ImmutableContainerNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(rootQName)) + .withChild(ipAddress).build(); + return ImmutableContainerNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(rootQName)).withChild(root) + .build(); + } + + private static DOMResult writeNormalizedNode(final ContainerNode normalized, final SchemaContext context) + throws IOException, XMLStreamException { + final Document doc = XmlDocumentUtils.getDocument(); + final DOMResult result = new DOMResult(doc); + NormalizedNodeWriter normalizedNodeWriter = null; + NormalizedNodeStreamWriter normalizedNodeStreamWriter = null; + XMLStreamWriter writer = null; + try { + writer = XML_FACTORY.createXMLStreamWriter(result); + normalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(writer, context); + normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(normalizedNodeStreamWriter); + + for (NormalizedNode child : normalized.getValue()) { + normalizedNodeWriter.write(child); + } + + normalizedNodeWriter.flush(); + } finally { + if (normalizedNodeWriter != null) { + normalizedNodeWriter.close(); + } + if (normalizedNodeStreamWriter != null) { + normalizedNodeStreamWriter.close(); + } + if (writer != null) { + writer.close(); + } + } + + return result; + } + + private static Document loadDocument(final String xmlPath) throws IOException, SAXException { + final InputStream resourceAsStream = Bug5446Test.class.getResourceAsStream(xmlPath); + final Document currentConfigElement = readXmlToDocument(resourceAsStream); + Preconditions.checkNotNull(currentConfigElement); + return currentConfigElement; + } + + private static Document readXmlToDocument(final InputStream xmlContent) throws IOException, SAXException { + final DocumentBuilder dBuilder; + try { + dBuilder = BUILDERFACTORY.newDocumentBuilder(); + } catch (final ParserConfigurationException e) { + throw new RuntimeException("Failed to parse XML document", e); + } + final Document doc = dBuilder.parse(xmlContent); + + doc.getDocumentElement().normalize(); + return doc; + } + + private static String toString(final Node xml) { + try { + final Transformer transformer = TransformerFactory.newInstance().newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); + + final StreamResult result = new StreamResult(new StringWriter()); + final DOMSource source = new DOMSource(xml); + transformer.transform(source, result); + + return result.getWriter().toString(); + } catch (IllegalArgumentException | TransformerFactoryConfigurationError | TransformerException e) { + throw new RuntimeException("Unable to serialize xml element " + xml, e); + } + } +} diff --git a/yang/yang-data-impl/src/test/resources/bug5446/xml/foo.xml b/yang/yang-data-impl/src/test/resources/bug5446/xml/foo.xml new file mode 100644 index 0000000000..b9b8abf95c --- /dev/null +++ b/yang/yang-data-impl/src/test/resources/bug5446/xml/foo.xml @@ -0,0 +1,6 @@ + + + + fwAAAQ== + + \ No newline at end of file diff --git a/yang/yang-data-impl/src/test/resources/bug5446/yang/foo.yang b/yang/yang-data-impl/src/test/resources/bug5446/yang/foo.yang new file mode 100644 index 0000000000..b5f78ca029 --- /dev/null +++ b/yang/yang-data-impl/src/test/resources/bug5446/yang/foo.yang @@ -0,0 +1,33 @@ +module foo { + yang-version 1; + namespace "foo"; + prefix "foo"; + + revision "2015-11-05" { + } + + typedef ipv4-address-binary { + type binary { + length "4"; + } + } + + typedef ipv6-address-binary { + type binary { + length "16"; + } + } + + typedef ip-address-binary { + type union { + type ipv4-address-binary; + type ipv6-address-binary; + } + } + + container root { + leaf ip-address { + type ip-address-binary; + } + } +} -- 2.36.6