SquirrelJME/SquirrelJME

View on GitHub
nanocoat/ctest-to-junit.py

Summary

Maintainability
A
0 mins
Test Coverage
# ---------------------------------------------------------------------------
# SquirrelJME
#     Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
# ---------------------------------------------------------------------------
# SquirrelJME is under the GNU General Public License v3, or later.
# For more information see license.txt.
# ---------------------------------------------------------------------------
# DESCRIPTION: Converts CTest Result XML Output into Ant/Gradle JUnit's XML

import sys
import os
import xml.etree.ElementTree as Xml
import datetime

# Convert banner
print("Converting CTest from stdin...")

# Globals
squirreljmeGlobalTestTime = None

# Resultant Variables for runs
testName = None
squirreljmeTestResult = None
squirreljmeNumSkipped = None
squirreljmeNumFailed = None
squirreljmeExecTime = None
squirreljmeWantValue = None
squirreljmeStdErr = None

# Parse the XML Tree
for event, tree in Xml.iterparse(sys.stdin, events=("start", "end")):
    tag = tree.tag
    text = tree.text
    attrib = tree.attrib

    # Debugging
    # print('DEBUG: %s %s `%s` %s' % (event, tag, text, attrib))

    # Start of a new tag
    if event == "start":
        # Test time (UNIX Time -> 2021-03-05T18:23:27.788)
        if tag == "StartTestTime":
            squirreljmeGlobalTestTime = datetime.datetime \
                .utcfromtimestamp(int(text)).strftime("%Y-%m-%dT%H:%M:%S")

        # Defining new test
        elif tag == "Test" and "Status" in attrib:
            # Failed test?
            status = attrib["Status"]
            if status == "failed":
                squirreljmeNumSkipped = 0
                squirreljmeNumFailed = 1

            # Skipped test
            elif status == "notrun":
                squirreljmeNumSkipped = 1
                squirreljmeNumFailed = 0

            # Passed test
            elif status == "passed":
                squirreljmeNumSkipped = 0
                squirreljmeNumFailed = 0

        # Name of test
        elif tag == "Name":
            testName = text

        # Time test took to execute (CTest ?? -> JUnit ms)
        elif (tag == "NamedMeasurement" and "name" in attrib and
              attrib["name"] == "Execution Time"):
            squirreljmeWantValue = "execTime"

        # Test output
        elif tag == "Measurement" and len(attrib) == 0:
            squirreljmeWantValue = "stdErr"

        # Reading a value
        elif tag == "Value":
            # Execution time?
            if "execTime" == squirreljmeWantValue:
                squirreljmeExecTime = max(float(1), float(text) * 1000)

            # Standard error?
            elif "stdErr" == squirreljmeWantValue:
                squirreljmeStdErr = text

            # Always clear out what we want, so it is not used
            squirreljmeWantValue = None

    # End of a test tag, dump the XML, or end of entire file
    elif event == "end" and tag == 'Test' and "Status" in attrib:
        # Make sure the name is always invalid, if missed try again
        if testName is None:
            testName = "InvalidTestName"

        # Force values to be set?
        if squirreljmeExecTime is None:
            squirreljmeExecTime = 0

        # Create build directory if missing
        if not os.path.isdir("build"):
            os.mkdir("build")
        if not os.path.isdir("build/junit"):
            os.mkdir("build/junit")

        # Open output file
        with open("build/junit/TEST-" + testName + ".xml", "wt") as file:
            # XML Header
            file.write('<?xml version="1.0" encoding="UTF-8"?>')
            file.write('\n')

            # Test Suite Information
            file.write('<testsuite name="%s" tests="1" skipped="%d" '
                       'failures="%d" errors="%d" timestamp="%s" '
                       'hostname="CTest" time="%g">' %
                       (testName, squirreljmeNumSkipped,
                        squirreljmeNumFailed, squirreljmeNumFailed,
                        squirreljmeGlobalTestTime, squirreljmeExecTime))
            file.write('\n')

            # Test Case (mostly the same)
            file.write('<testcase name="%s" '
                       'classname="%s" time="%g">' %
                       (testName, testName,
                        squirreljmeExecTime))
            file.write('\n')

            # Failure, needed for cases
            if squirreljmeNumFailed > 0:
                file.write('<failure type="%s"><![CDATA[%s]]></failure>' %
                           (testName, squirreljmeStdErr))
                file.write('\n')

            # Standard output
            file.write('<system-out><![CDATA[]]></system-out>')
            file.write('\n')

            # Standard error
            file.write(
                '<system-err><![CDATA[%s]]></system-err>' % squirreljmeStdErr)
            file.write('\n')

            # End tags
            file.write('</testcase>')
            file.write('</testsuite>')
            file.write('\n')

        # Reset all test state
        testName = None
        squirreljmeTestResult = None
        squirreljmeNumSkipped = None
        squirreljmeNumFailed = None
        squirreljmeExecTime = None
        squirreljmeWantValue = None
        squirreljmeStdErr = None