roslaunch: #1985 proof of concept for roslaunch parse testing as unit test

This commit is contained in:
Ken Conley 2009-12-15 06:46:22 +00:00
parent fc2b05d571
commit a4d04d262a
5 changed files with 179 additions and 22 deletions

View File

@ -0,0 +1,84 @@
#!/usr/bin/env python
# Software License Agreement (BSD License)
#
# Copyright (c) 2009, 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: catunit 3804 2009-02-11 02:16:00Z rob_wheeler $
from __future__ import with_statement
import roslib; roslib.load_manifest('rostest')
import os
import sys
import roslib.packages
def usage():
print >> sys.stderr, """Usage:
\troslaunch-check file.launch
"""
print sys.argv
sys.exit(os.EX_USAGE)
def check_roslaunch(f):
pass
## run check and output test result file
if __name__ == '__main__':
if len(sys.argv) < 2:
usage()
roslaunch_file = sys.argv[1]
_, package = roslib.packages.get_dir_pkg(roslaunch_file)
import roslaunch.rlutil
import rostest.rostestutil
pkg_dir, pkg = roslib.packages.get_dir_pkg(roslaunch_file)
outname = os.path.basename(roslaunch_file).replace('.', '_')
test_file = rostest.rostestutil.xmlResultsFile(pkg, outname, is_rostest=False)
error_msg = roslaunch.rlutil.check_roslaunch(roslaunch_file)
test_name = roslaunch_file
print "...writing test results to", test_file
if error_msg:
print>> sys.stderr, "FAILURE:\n%s"%error_msg
if not os.path.exists(os.path.dirname(test_file)):
os.makedirs(os.path.dirname(test_file))
with open(test_file, 'w') as f:
message = "roslaunch file %s failed to parse:\n %s"%(roslaunch_file, error_msg)
f.write(rostest.rostestutil.test_failure_junit_xml(test_name, message))
f.close()
print "wrote test file to [%s]"%test_file
else:
print "passed"
with open(test_file, 'w') as f:
f.write(rostest.rostestutil.test_success_junit_xml(test_name))

View File

@ -177,3 +177,31 @@ def xmlResultsFile(test_pkg, test_name, is_rostest=False):
else:
return os.path.join(test_dir, 'TEST-%s.xml'%test_name)
def test_failure_junit_xml(test_name, message):
"""
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
"""
return """<?xml version="1.0" encoding="UTF-8"?>
<testsuite tests="1" failures="1" time="1" errors="0" name="%s">
<testcase name="test_ran" status="run" time="1" classname="Results">
<failure message="%s" type=""/>
</testcase>
</testsuite>"""%(test_name, message)
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 """<?xml version="1.0" encoding="UTF-8"?>
<testsuite tests="1" failures="0" time="1" errors="0" name="%s">
<testcase name="test_ran" status="run" time="1" classname="Results">
</testcase>
</testsuite>"""%(test_name)

View File

@ -298,15 +298,22 @@ class ROSLaunchConfig(object):
## Base routine for creating a ROSLaunchConfig from a set of \a
## roslaunch_files and or launch XML strings and initializing it. This
## config will have a core definition and also set the master to run
## on \a port.
## @param roslaunch_files [str]
## @param port int: roscore/master port
## @param roslaunch_strs [str]: roslaunch XML strings to load
## @return ROSLaunchConfig initialized rosconfig instance
def load_config_default(roslaunch_files, port, roslaunch_strs=None, loader=None, verbose=True):
"""
Base routine for creating a ROSLaunchConfig from a set of
roslaunch_files and or launch XML strings and initializing
it. This config will have a core definition and also set the
master to run on port.
@param roslaunch_files: files to load
@type roslaunch_files: [str]
@param port roscore/master port
@type port: int
@param roslaunch_strs: roslaunch XML strings to load
@type roslaunch_strs: [str]
@return: initialized rosconfig instance
@rtype: L{ROSLaunchConfig}
"""
logger = logging.getLogger('roslaunch.config')
# This is the main roslaunch server process. Load up the

View File

@ -153,3 +153,51 @@ def get_or_generate_uuid(options_runid, options_wait_for_master):
val = roslaunch.core.generate_run_id()
return val
def check_roslaunch(f):
"""
Check roslaunch file for errors, returning error message if check fails. This routine
is mainly to support rostest's roslaunch_check.
@param f: roslaunch file name
@type f: str
@return: error message or None
@rtype: str
"""
try:
import roslaunch.config
config = roslaunch.config.load_config_default([f], 11311, verbose=False)
except roslaunch.RLException, e:
return str(e)
errors = []
# check for missing deps
import roslaunch.depends
base_pkg, file_deps, missing = roslaunch.depends.roslaunch_deps([f])
for pkg, miss in missing.iteritems():
if miss:
errors.append("Missing manifest dependencies: %s/manifest.xml: %s"%(pkg, ', '.join(miss)))
# load all node defs
nodes = []
for filename, rldeps in file_deps.iteritems():
nodes.extend(rldeps.nodes)
# check for missing packages
import roslib.packages
for pkg, node_type in nodes:
try:
roslib.packages.get_pkg_dir(pkg, required=True)
except:
errors.append("cannot find package [%s] for node [%s]"%(pkg, node_type))
# check for missing nodes
for pkg, node_type in nodes:
try:
if not roslib.packages.find_node(pkg, node_type):
errors.append("cannot find node [%s] in package [%s]"%(node_type, pkg))
except Exception, e:
errors.append("unable to find node [%s/%s]: %s"%(pkg, node_type, str(e)))
if errors:
return '\n'.join(errors)

View File

@ -55,6 +55,10 @@ def bin_roslaunch_check(ctx):
if not is_executable(roslaunch):
return "%s is lacking executable permissions"%roslaunch
# this is very similar to roslib.packages.find_node. However, we
# cannot use that implementation as it returns the first found
# path. For the sake of this test, we have to find all potential
# candidates.
def _find_node(pkg, node_type):
try:
dir = roslib.packages.get_pkg_dir(pkg)
@ -100,20 +104,6 @@ def roslaunch_duplicate_node_check(ctx):
warnings.append("node [%s] in package [%s]\n"%(node_type, pkg))
return warnings
def roslaunch_machine_credentials_check(ctx):
"""
Do basic SSH checks for machine, this does not do the full NxN test
"""
nodes = []
for filename, rldeps in ctx.launch_file_deps.iteritems():
nodes.extend(rldeps.nodes)
warnings = []
for pkg, node_type in nodes:
paths = _find_node(pkg, node_type)
if len(paths) > 1:
warnings.append("node [%s] in package [%s]\n"%(node_type, pkg))
return warnings
def pycrypto_check(ctx):
try:
import Crypto