diff --git a/tools/rosunit/src/rosunit/junitxml.py b/tools/rosunit/src/rosunit/junitxml.py index 1c0e6af1..7ae3b4a8 100644 --- a/tools/rosunit/src/rosunit/junitxml.py +++ b/tools/rosunit/src/rosunit/junitxml.py @@ -37,6 +37,8 @@ Library for reading and manipulating Ant JUnit XML result files. """ +from __future__ import print_function + import os import sys import cStringIO @@ -180,6 +182,7 @@ class Result(object): self.num_errors += r.num_errors self.num_failures += r.num_failures self.num_tests += r.num_tests + self.time += r.time self.test_case_results.extend(r.test_case_results) if r.system_out: self.system_out += '\n'+r.system_out @@ -240,7 +243,7 @@ def _load_suite_results(test_suite_name, test_suite, result): elif not classname.startswith(result.name): classname = "%s.%s"%(result.name,classname) - time = node.getAttribute('time') or 0.0 + time = float(node.getAttribute('time')) or 0.0 tc_result = TestCaseResult("%s/%s"%(test_suite_name,name)) tc_result.classname = classname tc_result.time = time @@ -310,14 +313,14 @@ def read(test_file, test_name): try: xml_str = _read_file_safe_xml(test_file) if not xml_str.strip(): - print "WARN: test result file is empty [%s]"%(test_file) + print("WARN: test result file is empty [%s]"%(test_file)) return Result(test_name, 0, 0, 0) test_suites = parseString(xml_str).getElementsByTagName('testsuite') except Exception as e: - print "WARN: cannot read test result file [%s]: %s"%(test_file, str(e)) + print("WARN: cannot read test result file [%s]: %s"%(test_file, str(e))) return Result(test_name, 0, 0, 0) if not test_suites: - print "WARN: test result file [%s] contains no results"%(test_file) + print("WARN: test result file [%s] contains no results"%(test_file)) return Result(test_name, 0, 0, 0) results = Result(test_name, 0, 0, 0) @@ -328,7 +331,7 @@ def read(test_file, test_name): err, fail, tests = [string.atoi(val) for val in vals] result = Result(test_name, err, fail, tests) - result.time = test_suite.getAttribute('time') or 0.0 + result.time = float(test_suite.getAttribute('time')) or 0.0 # Create a prefix based on the test result filename. The idea is to # disambiguate the case when tests of the same name are provided in @@ -447,5 +450,5 @@ def print_summary(junit_results, runner_name='ROSUNIT'): else: buff.write(" * FAILURES: 0\n") - print buff.getvalue() + print(buff.getvalue()) diff --git a/tools/rosunit/test/__init__.py b/tools/rosunit/test/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tools/rosunit/test/test_junitxml.py b/tools/rosunit/test/test_junitxml.py new file mode 100644 index 00000000..e8712546 --- /dev/null +++ b/tools/rosunit/test/test_junitxml.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python +# Software License Agreement (BSD License) +# +# Copyright (c) 2008, Willow Garage, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# * Neither the name of Willow Garage, Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# Revision $Id: $ + +import os +import io +import sys +import unittest +import tempfile +import shutil + +junitxml = None + +## Basic test of xmlresult functionality of reading gtest xml files and +## summarizing their results into a new file. +class MockResult(): + def __init__(self, directory, filename, suites = [], noSuitesRoot = False): + self.filename = os.path.join(directory, filename) + self.suites = suites + # whether to suppress root node + self.noSuitesRoot = noSuitesRoot + +class MockSuite(): + def __init__(self, cases, name, tests = 0, errors = 0, fail = 0, time = 1): + self.cases = cases + self.tests = tests + self.time = time + self.fail = fail + self.errors = errors + self.name = name + + +class MockCase(): + def __init__(self, name, errorList = [], classname="", time = 1): + self.classname = classname + self.name = name + self.time = time + self.errorList = errorList + +class MockErrorType(Exception): + def __init__(self, value, etype = ''): + self.value = value + self.__name__ = value + self.type = etype + +def _writeMockResultFile(result): + "writes a test result as a gtest compatible test runner would do" + with open(result.filename, 'w') as f: + f.write(u"""""") + if len(result.suites) > 1 or result.noSuitesRoot == False: + f.write(u"""\n""") + for suite in result.suites: + f.write('\n') + for case in suite.cases: + f.write('\n') + for error in case.errorList: + f.write('\n') + f.write('\n') + f.write('\n') + if len(result.suites) > 1 or result.noSuitesRoot == False: + f.write(u"""\n""") + + +class XmlResultTestRead(unittest.TestCase): + + def setUp(self): + # lazy-import to get coverage + global junitxml + if junitxml is None: + import rosunit.junitxml + junitxml = rosunit.junitxml + + self.directory = tempfile.mkdtemp() + + # setting up mock results as dict so results can be checked individually + self.mockresults={ + "empty": MockResult(self.directory, "empty.xml", []), + "emptysuite": MockResult(self.directory, "emptysuite.xml", [MockSuite([], "emptySuite", 0, 0, 0, 0)]), + "succ1": MockResult(self.directory, "succ1.xml", [MockSuite([MockCase("succCase")],"succ1suite", 1, 0, 0, 1)]), + "err1": MockResult(self.directory, "err1.xml", [MockSuite([MockCase("errCase")],"err1suite", 1, 1, 0, 1)]), + "fail1": MockResult(self.directory, "fail1.xml", [MockSuite([MockCase("failCase")],"fail1suite", 1, 0, 1, 1)]), + "noroot": MockResult(self.directory, "succ1.xml", [MockSuite([MockCase("succCase")],"succ1suite", 1, 0, 0, 1)], noSuitesRoot = True), + "multicase": MockResult(self.directory, + "multicase.xml", + [MockSuite([MockCase("succCase"), + MockCase("errCase"), + MockCase("failCase")], + "succ1suite", 3, 1, 1, time = 3)]), + "multisuite": MockResult(self.directory, + "multisuite.xml", + [MockSuite([MockCase("succCase")],"succ1suite", 1, 0, 0, 1), + MockSuite([MockCase("errCase")],"err1suite", 1, 1, 0, 1), + MockSuite([MockCase("failCase")],"fail1suite", 1, 0, 1, 1)]) + } + + + for name, result in self.mockresults.items(): + _writeMockResultFile(result) + + def tearDown(self): + shutil.rmtree(self.directory) + #pass + + def testReadNoSuites(self): + result = junitxml.read(self.mockresults["empty"].filename, "fooname") + self.assert_(result is not None) + self.assertEquals(0.0, result.time) + self.assertEquals(0, result.num_tests) + self.assertEquals(0, result.num_errors) + self.assertEquals(0, result.num_failures) + + def testReadEmptySuite(self): + result = junitxml.read(self.mockresults["emptysuite"].filename, "fooname") + self.assert_(result is not None) + self.assertEquals(0.0, result.time) + self.assertEquals(0, result.num_tests) + self.assertEquals(0, result.num_errors) + self.assertEquals(0, result.num_failures) + + def testReadSuccess(self): + result = junitxml.read(self.mockresults["succ1"].filename, "fooname") + self.assert_(result is not None) + self.assertEquals(1.0, result.time) + self.assertEquals(1, result.num_tests) + self.assertEquals(0, result.num_errors) + self.assertEquals(0, result.num_failures) + + def testReadError(self): + result = junitxml.read(self.mockresults["err1"].filename, "fooname") + self.assert_(result is not None) + self.assertEquals(1.0, result.time) + self.assertEquals(1, result.num_tests) + self.assertEquals(1, result.num_errors) + self.assertEquals(0, result.num_failures) + + def testReadFail(self): + result = junitxml.read(self.mockresults["fail1"].filename, "fooname") + self.assert_(result is not None) + self.assertEquals(1.0, result.time) + self.assertEquals(1, result.num_tests) + self.assertEquals(0, result.num_errors) + self.assertEquals(1, result.num_failures) + + def testReadMulticase(self): + result = junitxml.read(self.mockresults["multicase"].filename, "fooname") + self.assert_(result is not None) + self.assertEquals(3.0, result.time) + self.assertEquals(3, result.num_tests) + self.assertEquals(1, result.num_errors) + self.assertEquals(1, result.num_failures) + + def testReadMultisuite(self): + result = junitxml.read(self.mockresults["multisuite"].filename, "fooname") + self.assert_(result is not None) + self.assertEquals(3.0, result.time) + self.assertEquals(3, result.num_tests) + self.assertEquals(1, result.num_errors) + self.assertEquals(1, result.num_failures)