All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.gradle.internal.xml.SimpleXmlWriterSpec.groovy Maven / Gradle / Ivy

There is a newer version: 8.6
Show newest version
/*
 * Copyright 2012 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.gradle.internal.xml

import org.gradle.util.TextUtil
import spock.lang.Specification

import javax.xml.parsers.DocumentBuilderFactory

class SimpleXmlWriterSpec extends Specification {

    private sw = new ByteArrayOutputStream()
    private writer = new SimpleXmlWriter(sw)

    String getXml() {
        def text = sw.toString("UTF-8")
        def document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new ByteArrayInputStream(sw.toByteArray()))
        assert document
        return text
    }

    def "writes basic XML"() {
        when:
        writer.startElement("root").attribute("items", "9")
        writer.startElement("item").endElement()
        writer.startElement("item").attribute("size", "10m")
        writer.characters("some chars")
        writer.characters(" and some other".toCharArray())
        writer.characters("x  chars.x".toCharArray(), 2, 7)
        writer.startElement("foo").characters(" ")
        writer.endElement()
        writer.endElement()
        writer.endElement()

        then:
        xml == 'some chars and some other chars. '
    }

    def "escapes reserved characters in text content"() {
        when:
        writer.startElement("root")
        writer.characters("chars with interesting stuff: < < > ' \" ]]> \r\n \t")
        writer.endElement()

        then:
        xml.contains('chars with interesting stuff: &lt; < > \' " ]]> \r\n \t')
    }

    def "escapes reserved characters in attribute values"() {
        when:
        writer.startElement("root")
        writer.startElement("item").attribute("description", "encoded: \t < < > ' \n\r\"  ")
        writer.endElement()
        writer.endElement()

        then:
        xml.contains('')

        and:
        def item = new XmlSlurper().parseText(xml).item
        [email protected]() == "encoded: \t < < > ' \n\r\"  "
    }

    def "surrogates in attributes"() {
        when:
        writer.startElement("root")
        writer.attribute("test", "丈, 😃, and नि")
        writer.endElement()

        then:
        xml == ""
    }

    def "surrogates in content"() {
        when:
        writer.startElement("root")
        writer.startElement("a")
        def v = "丈, 😃, and नि"
        writer.characters(v)
        writer.endElement()
        writer.startElement("b")
        writer.characters(v.toCharArray())
        writer.endElement()
        writer.endElement()

        then:
        xml == "丈, 😃, and नि丈, 😃, and नि"
    }

    def "surrogates in comment"() {
        when:
        writer.startElement("root")
        writer.comment("丈, 😃, and नि")
        writer.endElement()

        then:
        xml == ""
    }

    def "surrogates in CDATA"() {
        when:
        writer.startElement("root")
        writer.startCDATA()
        writer.characters("丈, 😃, and नि")
        writer.endCDATA()
        writer.startCDATA()
        writer.characters("x丈, नि, 😃".toCharArray())
        writer.endCDATA()
        writer.endElement()

        then:
        xml == "😃😃"
    }

    def "writes CDATA"() {
        when:
        writer.startElement("root")
        writer.startElement("stuff")

        writer.startCDATA()
        writer.characters('x hey x'.toCharArray(), 2, 4)
        writer.characters('joe'.toCharArray())
        writer.characters("!")
        writer.endCDATA()

        writer.endElement()

        writer.startCDATA()
        writer.characters('encodes: ]]> ')
        writer.characters('does not encode: ]] ')
        writer.characters('html allowed: <> &')
        writer.endCDATA()

        writer.endElement()

        then:
        xml.contains(' does not encode: ]] html allowed: <> &]]>')
    }

    def "encodes CDATA when token on the border"() {
        when:
        //the end token is on the border of both char arrays
        writer.startElement('root')
        writer.startCDATA()
        writer.characters('stuff ]]')
        writer.characters('> more stuff')
        writer.endCDATA()
        writer.endElement()

        then:
        xml.contains(' more stuff]]>')
    }

    def "does not encode CDATA when token separated in different CDATAs"() {
        when:
        //the end token is on the border of both char arrays

        writer.startElement('root')

        writer.startCDATA();
        writer.characters('stuff ]]')
        writer.endCDATA();

        writer.startCDATA()
        writer.characters('> more stuff')
        writer.endCDATA();

        writer.endElement()

        then:
        xml.contains(' more stuff]]>')
    }

    def "encodes non-ASCII characters"() {
        when:
        writer.startElement("\u0200").attribute("\u0201", "\u0202")
        writer.characters("\u0203")
        writer.startCDATA().characters("\u0204").endCDATA()
        writer.endElement()

        then:
        xml.contains('<\u0200 \u0201="\u0202">\u0203')
    }

    def "escapes restricted characters in text content"() {
        when:
        writer.startElement("root")
        writer.attribute("name", "\u0084\u009f")
        writer.characters("\u0084\u009f")
        writer.startCDATA().characters("\u0084\u009f").endCDATA()
        writer.endElement()

        then:
        xml.contains('„ŸŸ')
    }

    def "replaces illegal characters in text content"() {
        given:

        when:
        writer.startElement("root")
        writer.characters(chars)
        writer.startElement("broken").attribute("name", chars)
        writer.startCDATA().characters(chars).endCDATA()
        writer.endElement()
        writer.endElement()

        then:
        xml.contains('?')

        where:
        chars << ["\u0000", "\ud800", "\udfff", "\ufffe"]
    }

    def "is a Writer implementation that escapes characters"() {
        when:
        writer.startElement("root")
        writer.write("some ")
        writer.write(" and ".toCharArray())
        writer.write("x some x".toCharArray(), 2, 4)
        writer.write(' ')
        writer.startCDATA()
        writer.write("cdata")
        writer.endCDATA()
        writer.endElement()

        then:
        xml.contains("some <chars> and some ")
    }

    def "cannot end element when stack is empty"() {
        writer.startElement("root")
        writer.endElement()

        when:
        writer.endElement()

        then:
        IllegalStateException e = thrown()
        e.message == 'Cannot end element, as there are no started elements.'
    }

    def "cannot write characters when stack is empty"() {
        when:
        writer.characters("text")

        then:
        IllegalStateException e = thrown()
        e.message == 'Cannot write text, as there are no started elements.'

        given:

        when:
        writer.startElement("root")
        writer.endElement()
        writer.characters("text")

        then:
        e = thrown()
        e.message == 'Cannot write text, as there are no started elements.'
    }

    def "cannot end element when CDATA node is open"() {
        writer.startElement("root")
        writer.startCDATA()

        when:
        writer.endElement()

        then:
        IllegalStateException e = thrown()
        e.message == 'Cannot end element, as current CDATA node has not been closed.'
    }

    def "cannot start element when CDATA node is open"() {
        writer.startElement("root")
        writer.startCDATA()

        when:
        writer.startElement("nested")

        then:
        IllegalStateException e = thrown()
        e.message == 'Cannot start element, as current CDATA node has not been closed.'
    }

    def "cannot start CDATA node when CDATA node is open"() {
        writer.startElement("root")
        writer.startCDATA()

        when:
        writer.startCDATA()

        then:
        IllegalStateException e = thrown()
        e.message == 'Cannot start CDATA node, as current CDATA node has not been closed.'
    }

    def "cannot end CDATA node when not in a CDATA node"() {
        writer.startElement("root")

        when:
        writer.endCDATA()

        then:
        IllegalStateException e = thrown()
        e.message == 'Cannot end CDATA node, as not currently in a CDATA node.'
    }

    def "closes tags"() {
        when:
        writer.startElement("root")
        action.call(writer)
        writer.endElement()

        then:
        sw.toString().contains("") //is closed with '>'

        where:
        action << [{ it.startElement("foo"); it.endElement() },
                { it.startCDATA(); it.endCDATA() },
                { it.characters("bar") },
                { it.write("close") }]
    }

    def "closes attributed tags"() {
        when:
        writer.startElement("root")
        writer.attribute("foo", '115')
        action.call(writer)
        writer.endElement()

        then:
        sw.toString().contains('') //is closed with '>'

        where:
        action << [{ it.startElement("foo"); it.endElement() },
                { it.startCDATA(); it.endCDATA() },
                { it.characters("bar") },
                { it.write("close") }]
    }

    def "outputs empty element when element has no content"() {
        when:
        writer.startElement("root")
        writer.startElement("empty").endElement()
        writer.startElement("empty").attribute("property", "value").endElement()
        writer.endElement()

        then:
        xml.contains('')
    }

    def "writes indented XML when enabled"() {
        sw.reset()
        def writer = new SimpleXmlWriter(sw, "    ")

        when:
        writer.startElement("root").attribute("items", "9")
        writer.startElement("item").endElement()
        writer.startElement("item").characters("some text").endElement()
        writer.startElement("item")
        writer.startElement("nested-1")
        writer.startElement("nested-2").characters(" ").endElement()
        writer.endElement()
        writer.endElement()
        writer.startElement("item")
        writer.startElement("thing").characters("some text").endElement()
        writer.startElement("thing").startCDATA().characters("some text").endCDATA().endElement()
        writer.endElement()
        writer.startElement("mixed")
        writer.characters("text")
        writer.startElement("mixed-1").endElement()
        writer.characters("text")
        writer.startElement("mixed-2").characters("123").endElement()
        writer.startElement("mixed-3").startElement("empty").endElement().endElement()
        writer.characters("text")
        writer.endElement()
        writer.endElement()

        then:
        xml == TextUtil.toPlatformLineSeparators('''

    
    some text
    
        
             
        
    
    
        some text
        
    
    text
        text
        123
        
            
        text

''')
    }

    def "allows valid tag names"() {
        when:
        writer.startElement(name)

        then:
        notThrown(IllegalArgumentException)

        where:
        name << ["name", "NAME", "with-dashes", "with.dots", "with123digits", ":", "_", "\u037f\u0300", "ns:foo"]
    }

    def "validates tag names"() {
        when:
        writer.startElement(name)

        then:
        def ex = thrown(IllegalArgumentException)
        ex.message == "Invalid element name: '$name'"

        where:
        name << ["tag with space", "", "-invalid-start-char", "  ", "912", "\u00d7", "ns:foo:bar"]
    }

    def "allows valid attribute names"() {
        when:
        writer.startElement("foo").attribute(name, "foo")

        then:
        notThrown(IllegalArgumentException)

        where:
        name << ["name", "NAME", "with-dashes", "with.dots", "with123digits", ":", "_", "\u037f\u0300", "ns:foo"]
    }

    def "validates attribute names"() {
        when:
        writer.startElement("foo").attribute(name, "foo")

        then:
        def ex = thrown(IllegalArgumentException)
        ex.message == "Invalid attribute name: '$name'"

        where:
        name << ["attribute with space", "", "-invalid-start-char", "  ", "912", "\u00d7", "ns:foo:bar"]
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy