Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
234 views
in Technique[技术] by (71.8m points)

maven - How to set DOCTYPE in XML file using groovy DOMBuilder

I am trying to create an XML file using a groovy script for running tests using mvn+TestNG, so the XML should have a basic testNG.xml structure. The groovy script will be executed in the Jenkins pipeline. Test methods and test classes are received in JSON format from external service and inserted into the XML for further execution. XML should be saved and then executed by maven. First I have tried to use MarkupBuilder, but there is an issue regarding this class in Jenkins https://issues.jenkins.io/browse/JENKINS-32766 so it's not compiling. So I decided to try DOMBuilder, but I don't know how to set the DOCTYPE parameter for my XML. Moreover, nodes aren't formatted well by DOMBuilder - code is written in one line without line breakers.

What I am trying to get

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name='Recommended tests to run'>
  <test name='tests'>
    <classes>
      <class name='com.epam.automation.api.AverageScoreTest'>
        <methods>
          <include name='averageScoreUpdateInterviewType' />
        </methods>
      </class>
      <class name='com.epam.automation.api.DifferentTypesFilesAttachmentTest'>
        <methods>
          <include name='differentTypesFilesInterviewTest' />
        </methods>
      </class>
      <class name='com.epam.automation.api.smartscheduling.STSRecruitersTest'>
        <methods>
          <include name='suggestedSlotsWithInterviewsDefaultTest' />
        </methods>
      </class>
    </classes>
  </test>
</suite>

Code sample using MarkupBuilder.

import groovy.json.JsonSlurper
    //                    final String url = "http://host.docker.internal:8091/api/agents/hit-auto/plugins/test2code/data/tests-to-run"
    //                    final String response =
    //                            sh(script: "curl -H 'Accept: application/json' -H 'Content-Type:application/json' -w '
' -X GET $url", returnStdout: true).trim()
    final String response = "{
" +
            "    "byType": {
" +
            "        "AUTO": [
" +
            "            "",
" +
            "            "[engine:testng]/[class:com.epam.automation.api.AverageScoreTest]/[method:averageScoreUpdateInterviewType[COMPLETED]]",
" +
            "            "[engine:testng]/[class:com.epam.automation.api.DifferentTypesFilesAttachmentTest]/[method:differentTypesFilesInterviewTest]",
" +
            "            "[engine:testng]/[class:com.epam.automation.api.smartscheduling.STSRecruitersTest]/[method:suggestedSlotsWithInterviewsDefaultTest]"
" +
            "        ]
" +
            "    },
" +
            "    "totalCount": 33
" +
            "}"
    
    JsonSlurper json = new JsonSlurper()
    Map<String,String> parseJson = json.parseText(response) as Map
    
    if (parseJson.totalCount > 0){
        ArrayList tests = parseJson.byType.AUTO
        Map<String,String> classMethodMap = [:]
    
        def patternMethod = ~/(?<=method:)([w]+)/
        def patternClass = ~/(?<=class:)(.*?)(?=])/
    
        for (String item : tests) {
            def matchMethod = (item =~ patternMethod)
            def matchClass = (item =~ patternClass)
            if (matchClass && matchMethod) {
                classMethodMap.put(matchMethod.group(1), matchClass.group(1))
            }
        }
    
        classMethodMap.each{ k, v -> println "${k}:${v}" }
    
        def writer = new StringWriter()
        def location = "C:\Users\Valiantsin_Dorum\Documents\hit-automation\test-suites\recommended_tests.xml"
        def fileXmlOut = new File(location)
    
        if (fileXmlOut.exists()){
            boolean fileSuccessfullyDeleted = new File(location).delete()
            println fileSuccessfullyDeleted
        }
    
        def xml = new MarkupBuilder(writer)
        def helper = new MarkupBuilderHelper(xml)
        helper.xmlDeclaration([version:'1.0', encoding:'UTF-8'])
        helper.yieldUnescaped """<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >"""+"
"
        xml.suite(name:'Recommended tests to run'){
            test(name:'tests'){
                classes(){
                    classMethodMap.forEach{method,classes -> /class/(name:classes){
                        methods(){/include/(name:method)
                            }
                        }
                    }
                }
            }
        }
    fileXmlOut << writer.toString()

Code sample using DomBuilder.

Document doc = DOMBuilder.newInstance().createDocument()

Element suite = doc.createElement('suite')
suite.setAttribute('name', 'Recommended suite')
doc.appendChild(suite)

Element test = doc.createElement('test')
test.setAttribute('name', 'Recommended tests')
suite.appendChild(test)

classMethodMap.forEach() { method,testClasses ->
    Element classes = doc.createElement('classes')
    test.appendChild(classes)
    Element testClass = doc.createElement('class')
    testClass.setAttribute('name',testClasses)
    classes.appendChild(testClass)
    Element testMethod = doc.createElement('include')
    testMethod.setAttribute('name',method)
    testClass.appendChild(testMethod)
}

println doc.getDoctype();

DOMSource source = new DOMSource(doc);
FileWriter write = new FileWriter(new File(location));
StreamResult result = new StreamResult(write);

TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.transform(source, result)
question from:https://stackoverflow.com/questions/65625856/how-to-set-doctype-in-xml-file-using-groovy-dombuilder

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Maybe something like:

import groovy.json.*
import groovy.xml.*

final String response = """{
    "byType": {
        "AUTO": [
            "",
            "[engine:testng]/[class:com.epam.automation.api.AverageScoreTest]/[method:averageScoreUpdateInterviewType[COMPLETED]]",
            "[engine:testng]/[class:com.epam.automation.api.DifferentTypesFilesAttachmentTest]/[method:differentTypesFilesInterviewTest]",
            "[engine:testng]/[class:com.epam.automation.api.smartscheduling.STSRecruitersTest]/[method:suggestedSlotsWithInterviewsDefaultTest]"
        ]
    
    },
    "totalCount": 33
}"""

JsonSlurper json = new JsonSlurper()
def parseJson = json.parseText(response)

if (parseJson.totalCount > 0) {
    def result = parseJson.byType.AUTO.findResults {
        def match = it =~ $/.*(?:[class:(.+?)])/(?:[method:(.+)]).*/$
        if (match) {
            [cls: match.group(1), meth: match.group(2)]
        }
    }.groupBy { it.cls }

    StringWriter out = new StringWriter()
    def xmlNodePrinter = new XmlNodePrinter(new PrintWriter(out), "  ")
    

    def xml = new StringWriter().with { sw ->
        sw.write "<?xml version='1.0' encoding='UTF-8'?>
"
        sw.write '<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
'
        new MarkupBuilder(sw).suite(name: 'Recommended tests to run') {
            test(name: 'tests') {
                classes {
                    result.each { entry ->
                        'class'(name: entry.key) {
                            methods {
                                entry.value.each { meth ->
                                    include(name: meth.meth)
                                }
                            }
                        }
                    }
                }
            }
        }
        sw
    }

    println xml.toString()
}

You'll need to write the xml variable to the file

The value of the above is

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name='Recommended tests to run'>
  <test name='tests'>
    <classes>
      <class name='com.epam.automation.api.AverageScoreTest'>
        <methods>
          <include name='averageScoreUpdateInterviewType[COMPLETED]' />
        </methods>
      </class>
      <class name='com.epam.automation.api.DifferentTypesFilesAttachmentTest'>
        <methods>
          <include name='differentTypesFilesInterviewTest' />
        </methods>
      </class>
      <class name='com.epam.automation.api.smartscheduling.STSRecruitersTest'>
        <methods>
          <include name='suggestedSlotsWithInterviewsDefaultTest' />
        </methods>
      </class>
    </classes>
  </test>
</suite>

And it will group multiple methods if they are from the same class


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...