Ram Gandikota / Mbed OS ABCD
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers unity_to_junit.py Source File

unity_to_junit.py

00001 # -----------------------------------------------------------------------
00002 # Copyright (c) 2016 ARM Limited. All rights reserved.
00003 # SPDX-License-Identifier: Apache-2.0
00004 # Licensed under the Apache License, Version 2.0 (the License); you may
00005 # not use this file except in compliance with the License.
00006 # You may obtain a copy of the License at
00007 #
00008 # http://www.apache.org/licenses/LICENSE-2.0
00009 #
00010 # Unless required by applicable law or agreed to in writing, software
00011 # distributed under the License is distributed on an AS IS BASIS, WITHOUT
00012 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013 # See the License for the specific language governing permissions and
00014 # limitations under the License.
00015 # -----------------------------------------------------------------------
00016 
00017 import re
00018 import sys
00019 from tabulate import tabulate
00020 
00021 def extract_tags(logLines):
00022 
00023     # These are the Unity tag names we are going to look for.
00024     tagNames = [
00025         "UnityTest",
00026         "UnityIgnoredTest",
00027         "UnityResult",
00028     ]
00029 
00030     # All tags will end up here.
00031     tags = []
00032 
00033     for tagName in tagNames:
00034 
00035         # Fetch start and end locations for the start and end markers.
00036 
00037         startMatches = re.finditer(re.escape("<***" + tagName + "***>"), logLines)
00038         endMatches = re.finditer(re.escape("</***" + tagName + "***>"), logLines)
00039 
00040         startOffsets = [match.end() for match in startMatches]
00041         endOffsets = [match.start() - 1 for match in endMatches]
00042 
00043         # If the amount of start and end markers isn't identical, this is an error.
00044         if len(startOffsets) != len(endOffsets):
00045             raise Exception("For tag '" + tagName + "', start and end tags do not match!")
00046 
00047         # Append all found tags to the tags list.
00048         for tagOffsets in zip(startOffsets, endOffsets):
00049             tagContent = logLines[tagOffsets[0]: tagOffsets[1] + 1]
00050             tags.append((tagOffsets[0], tagName, tagContent))
00051 
00052     # Sort the tags list by the offset.
00053     tags.sort(key=lambda tag_: tag_[0])
00054 
00055     # Throw away the offset (once sorted, it is no longer needed).
00056     tags = [tag[1:] for tag in tags]
00057 
00058     # At this point we are left with list of tags, each one a pair, consisting of
00059     # (group name, test name, status).
00060     return tags
00061 
00062 def get_test_group_and_name(printableName):
00063     match = re.match(r"TEST\((.*), (.*)\)", printableName)
00064     if match is not None:
00065         return match.group(1), match.group(2)
00066     else:
00067         raise Exception("Erorr parsing test group and name")
00068 
00069 def get_ignored_test_group_and_name(printableName):
00070     match = re.match(r"IGNORE_TEST\((.*), (.*)\)", printableName)
00071     if match is not None:
00072         return match.group(1), match.group(2)
00073     else:
00074         raise Exception("Erorr parsing test group and name")
00075 
00076 def collect_tests(tags):
00077 
00078     tests = []
00079 
00080     # Build the list of tests, with status for each.
00081     curTest = ""
00082     resultHandled = False
00083     for tag in tags:
00084 
00085         tagName = tag[0]
00086         tagValue = tag[1]
00087 
00088         if tagName == "UnityTest":
00089             curTest = get_test_group_and_name(tagValue)
00090             resultHandled = False
00091         elif tagName == "UnityResult":
00092             if not resultHandled:
00093                 tests.append((curTest, tagValue))
00094                 resultHandled = True
00095         elif tagName == "UnityIgnoredTest":
00096             curTest = get_ignored_test_group_and_name(tagValue)
00097             tests.append((curTest, "IGNORE"))
00098         else:
00099             raise Exception("Unknown tag '" + tagName + "' encountered")
00100 
00101     return tests
00102 
00103 def generate_junit_xml_output(packageName, tests, xmlOutFile):
00104 
00105     testsCount = len(tests)
00106 
00107     with open(xmlOutFile, "wt") as f:
00108         print >> f, '<testsuite tests="' + str(testsCount) + '">'
00109         for test in tests:
00110             print >> f, '    <testcase classname="' + packageName + "." + test[0][0] + '" name="' + test[0][1] + '">'
00111             if test[1] == "FAIL":
00112                 print >> f, '        <failure/>'
00113             if test[1] == "IGNORE":
00114                 print >> f, '        <skipped/>'
00115             print >> f, '    </testcase>'
00116         print >> f, '</testsuite>'
00117 
00118 def generate_text_output(packageName, tests, textOutFile):
00119 
00120     testsCount = len(tests)
00121 
00122     failingTests = 0
00123     passedTests = 0
00124     ignoredTests = 0
00125 
00126     testsTable = []
00127     for test in tests:
00128         if test[1] == "FAIL":
00129             failingTests += 1
00130         if test[1] == "PASS":
00131             passedTests += 1
00132         if test[1] == "IGNORE":
00133             ignoredTests += 1
00134 
00135         testsTable.append([test[0][0], test[0][1], test[1]])
00136 
00137     resultsTableHeader = ["TOTAL", "PASS", "FAIL", "IGNORE"]
00138     resultsTable = [[str(testsCount), str(passedTests), str(failingTests), str(ignoredTests)]]
00139 
00140     with open(textOutFile, "wt") as f:
00141 
00142         print >> f, "==== " + packageName + " ===="
00143         print >> f, tabulate(testsTable)
00144         print >> f, tabulate(resultsTable, headers=resultsTableHeader)
00145 
00146         if testsCount == 0 or failingTests > 0:
00147             finalStatus = "FAIL"
00148         else:
00149             finalStatus = "PASS"
00150         print >> f
00151         print >> f, "Final status: " + finalStatus + "."
00152 
00153 def unity_to_junit(packageName, inputFile, xmlOutFile, textOutFile):
00154 
00155     with open(inputFile, "rt") as f:
00156         logLines = f.read()
00157 
00158     tags = extract_tags(logLines)
00159     tests = collect_tests(tags)
00160 
00161     generate_junit_xml_output(packageName, tests, xmlOutFile)
00162     generate_text_output(packageName, tests, textOutFile)
00163 
00164 
00165 if __name__ == "__main__":
00166 
00167     if len(sys.argv) != 5:
00168         print "Usage: <package-name> <input-file> <xml-out-file> <text-out-file>"
00169         sys.exit(1)
00170 
00171     unity_to_junit(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4])
00172