From e6fa5b9a935f57068d169517b51362b14045c14e Mon Sep 17 00:00:00 2001 From: Ken Conley Date: Wed, 27 Oct 2010 02:02:57 +0000 Subject: [PATCH] porting more of rostest code to rosunit, including rostest-check-results, which is now check_test_ran.py --- tools/rosunit/scripts/check_test_ran.py | 98 +++++++++++++++++++++++++ tools/rosunit/src/rosunit/__init__.py | 89 ++++++++++++++++++++++ tools/rosunit/src/rosunit/junitxml.py | 43 +++++++++++ 3 files changed, 230 insertions(+) create mode 100755 tools/rosunit/scripts/check_test_ran.py diff --git a/tools/rosunit/scripts/check_test_ran.py b/tools/rosunit/scripts/check_test_ran.py new file mode 100755 index 00000000..94068dfa --- /dev/null +++ b/tools/rosunit/scripts/check_test_ran.py @@ -0,0 +1,98 @@ +#!/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$ + +""" +Writes a test failure out to test file if it doesn't exist. +""" + +from __future__ import with_statement +PKG='rosunit' +NAME="check_test_ran.py" +import roslib; roslib.load_manifest(PKG) +import os +import sys + +import roslib.packages +import rosunit + +def usage(): + print >> sys.stderr, """Usage: +\t%s test-file.xml +or +\t%s --rostest pkg-name test-file.xml +"""%(NAME, NAME) + print sys.argv + sys.exit(os.EX_USAGE) + +def check_main(): + if len(sys.argv) < 2: + usage() + if '--rostest' in sys.argv[1:]: + if len(sys.argv) != 4: + usage() + test_pkg, test_file = [a for a in sys.argv[1:] if a != '--rostest'] + # this logic derives the output filename that rostest uses + + pkg_dir, _ = roslib.packages.get_dir_pkg(test_file) + + # compute test name for friendlier reporting + outname = rosunit.rostest_name_from_path(pkg_dir, test_file) + + test_file = rosunit.xml_results_file(test_pkg, outname, is_rostest=True) + else: + if len(sys.argv) != 2: + usage() + test_file = sys.argv[1] + + print "Checking for test results in %s"%test_file + + if not os.path.exists(test_file): + if not os.path.exists(os.path.dirname(test_file)): + os.makedirs(os.path.dirname(test_file)) + + print "Cannot find results, writing failure results to", test_file + + with open(test_file, 'w') as f: + test_name = os.path.basename(test_file) + d = {'test': test_name, 'test_file': test_file } + f.write(""" + + + + +"""%d) + +if __name__ == '__main__': + check_main() diff --git a/tools/rosunit/src/rosunit/__init__.py b/tools/rosunit/src/rosunit/__init__.py index e69de29b..7c8c9b83 100644 --- a/tools/rosunit/src/rosunit/__init__.py +++ b/tools/rosunit/src/rosunit/__init__.py @@ -0,0 +1,89 @@ +# 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$ + +""" +Library and tools support unit testing in ROS. +""" + +# NOTE: while this makes some forward references to conventions used +# in rostest, it does not use rostest itself. + + +def xml_results_file(test_pkg, test_name, is_rostest=False): + """ + @param test_pkg: name of test's package + @type test_pkg: str + @param test_name str: name of test + @type test_name: str + @param is_rostest: True if the results file is for a rostest-generated unit instance + @type is_rostest: bool + @return: name of xml results file for specified test + @rtype: str + """ + test_dir = os.path.join(roslib.rosenv.get_test_results_dir(), test_pkg) + if not os.path.exists(test_dir): + try: + roslib.rosenv.makedirs_with_parent_perms(test_dir) + except OSError: + raise IOError("cannot create test results directory [%s]. Please check permissions."%(test_dir)) + + # #576: strip out chars that would bork the filename + # this is fairly primitive, but for now just trying to catch some common cases + for c in ' "\'&$!`/\\': + if c in test_name: + test_name = test_name.replace(c, '_') + if is_rostest: + return os.path.join(test_dir, 'TEST-rostest__%s.xml'%test_name) + else: + return os.path.join(test_dir, 'TEST-%s.xml'%test_name) + +def rostest_name_from_path(pkg_dir, test_file): + """ + Derive name of rostest based on file name/path. rostest follows a + certain convention defined above. + + @return: name of test + @rtype: str + """ + test_file_abs = os.path.abspath(test_file) + if test_file_abs.startswith(pkg_dir): + # compute package-relative path + test_file = test_file_abs[len(pkg_dir):] + if test_file[0] == os.sep: + test_file = test_file[1:] + outname = test_file.replace(os.sep, '_') + if '.' in outname: + outname = outname[:outname.rfind('.')] + return outname + diff --git a/tools/rosunit/src/rosunit/junitxml.py b/tools/rosunit/src/rosunit/junitxml.py index 11d85467..6025d91d 100644 --- a/tools/rosunit/src/rosunit/junitxml.py +++ b/tools/rosunit/src/rosunit/junitxml.py @@ -356,3 +356,46 @@ def read_all(filter=[]): result = read(file, os.path.basename(subdir)) root_result.accumulate(result) return root_result + + +def test_failure_junit_xml(test_name, message, stdout=None): + """ + Generate JUnit XML file for a unary test suite where the test failed + + @param test_name: Name of test that failed + @type test_name: str + @param message: failure message + @type message: str + @param stdout: stdout data to include in report + @type stdout: str + """ + if not stdout: + return """ + + + + +"""%(test_name, message) + else: + return """ + + + + + +"""%(test_name, message, stdout) + +def test_success_junit_xml(test_name): + """ + Generate JUnit XML file for a unary test suite where the test succeeded. + + @param test_name: Name of test that passed + @type test_name: str + """ + return """ + + + +"""%(test_name)