roslaunch: #1985 proof of concept for roslaunch parse testing as unit test
This commit is contained in:
parent
fc2b05d571
commit
a4d04d262a
|
@ -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))
|
||||
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue