ROS系统python代码测试之rostest
ROS系统中提供了测试框架,可以实现python/c++代码的单元测试,python和C++通过不同的方式实现,
之后的两篇文档分别详细介绍各自的实现步骤,以及测试结果和覆盖率的获取。
ROS系统中python代码测试介绍
关于测试代码的写法细节请参考官方wiki文档,http://wiki.ros.org/unittest,本文主要说明使用中的坑。
ROS中python代码的测试可以有两种实现方式一是节点级的集成测试,可以测试节点创建和消息收发的整个过程;二是代码级的单元测试,在测试用例中导入被测代码进行测试。
python代码测试中可能遇到的问题及优化修改
1、创建启动测试的roslaunch文件
rostest相关的roslaunch请参考 http://wiki.ros.org/roslaunch/XML/test
<launch>
<node name="nodename" pkg="pkgname" type="被测文件的py"/>
<test test-name="testnodename" pkg="pkgname" time-limit="500.0" type="测试文件py" args="--cov"/>
</launch>
关注点1:
time-limit这个标签限制了用例运行的最长时间,如果用例耗时超过这个时间,那么用例会自动以超时失败结束,默认值是60s。如果用例较多运行时间长的话,需要
设置合理的值;
关注点2:
args 这个标签可以向测试程序中传入参数,--cov的作用是测试完成后生成覆盖率文件,下面详细介绍。
2、测试结果文件获取
参见上一篇介绍环境变量了文章,通过ROS_TEST_RESULTS_DIR这个变量可以修改测试结果输出的位置。
3、覆盖率统计
我使用的ros版本是indigo,这个版本中rostest对覆盖率统计的代码需要进一步优化。
优化点1:rostest.rosrun(package_name, test_name, test_case_class, sysargs=None)
根据wiki中对rostest.rosrun的描述,该函数应该有第五个参数coverage_packages,该参数表示待测试package list.
优化后的函数rostest.rosrun(package_name, test_name, test_case_class, sysargs=None, coverage_packages=None)
优化点2:rostest覆盖率统计完善
覆盖率统计需要首先a安装python代码覆盖率工具coverge,参考http://coverage.readthedocs.org/en/latest/
修改rostest.rosrun代码,使代码能够输出xml_report,为什么要输出xml报告呢,因为The report is compatible with Cobertura reports.
这一点很关键,在jenkins持续集成环境中需要这一步骤的工作。jenkins中的Cobertura插件可以解析xml_report文件,然后将python代码的详细覆盖率信息显示在用例的测试结果中。
/opt/ros/indigo/lib/python2.7/dist-packages/rostest/__init__.py 中函数的修改
def rosrun(package, test_name, test, sysargs=None, coverage_packages=None):
"""
Run a rostest/unittest-based integration test. @param package: name of package that test is in
@type package: str
@param test_name: name of test that is being run
@type test_name: str
@param test: test class
@type test: unittest.TestCase
@param sysargs: command-line args. If not specified, this defaults to sys.argv. rostest
will look for the --text and --gtest_output parameters
@type sysargs: list
"""
if sysargs is None:
# lazy-init sys args
import sys
sysargs = sys.argv #parse sysargs
result_file = None
for arg in sysargs:
if arg.startswith(XML_OUTPUT_FLAG):
result_file = arg[len(XML_OUTPUT_FLAG):]
text_mode = '--text' in sysargs
coverage_mode = '--cov' in sysargs
if coverage_mode:
_start_coverage(coverage_packages) import unittest
import rospy coverresult = os.getenv('ROS_TEST_RESULTS_DIR') + '/coverage/' suite = unittest.TestLoader().loadTestsFromTestCase(test)
if text_mode:
result = unittest.TextTestRunner(verbosity=2).run(suite)
else:
result = rosunit.create_xml_runner(package, test_name, result_file).run(suite)
if coverage_mode:
_stop_coverage(coverage_packages, coverresult)
rosunit.print_unittest_summary(result) # shutdown any node resources in case test forgets to
rospy.signal_shutdown('test complete')
if not result.wasSuccessful():
import sys
sys.exit(1)
def _stop_coverage(packages, html=None):
"""
@param packages: list of packages to generate coverage reports for
@type packages: [str]
@param html: (optional) if not None, directory to generate html report to
@type html: str
"""
if _cov is None:
return
import sys, os
try:
_cov.stop()
# accumulate results
_cov.save() # - update our own .coverage-modules file list for
# coverage-html tool. The reason we read and rewrite instead
# of append is that this does a uniqueness check to keep the
# file from growing unbounded
if os.path.exists('.coverage-modules'):
with open('.coverage-modules','r') as f:
all_packages = set([x for x in f.read().split('\n') if x.strip()] + packages)
else:
all_packages = set(packages)
with open('.coverage-modules','w') as f:
f.write('\n'.join(all_packages)+'\n') try:
# list of all modules for html report
all_mods = [] # iterate over packages to generate per-package console reports
for package in packages:
pkg = __import__(package)
m = [v for v in sys.modules.values() if v and v.__name__.startswith(package)]
all_mods.extend(m) # generate overall report and per module analysis
_cov.report(m, show_missing=0)
for mod in m:
res = _cov.analysis(mod)
print("\n%s:\nMissing lines: %s"%(res[0], res[3])) if html: print("="*80+"\ngenerating html coverage report to %s\n"%html+"="*80)
_cov.html_report(all_mods, directory=html)
_cov.xml_report(all_mods, outfile=html + 'cover.xml')
except ImportError as e:
print("WARNING: cannot import '%s', will not generate coverage report"%package, file=sys.stderr)
except ImportError as e:
print("""WARNING: cannot import python-coverage, coverage tests will not run.
To install coverage, run 'easy_install coverage'""", file=sys.stderr)
/opt/ros/indigo/lib/python2.7/dist-packages/rosunit/pyunit.py 文件修改
def unitrun(package, test_name, test, sysargs=None, coverage_packages=None):
"""
Wrapper routine from running python unitttests with
JUnit-compatible XML output. This is meant for unittests that do
not not need a running ROS graph (i.e. offline tests only). This enables JUnit-compatible test reporting so that
test results can be reported to higher-level tools. WARNING: unitrun() will trigger a sys.exit() on test failure in
order to properly exit with an error code. This routine is meant
to be used as a main() routine, not as a library. @param package: name of ROS package that is running the test
@type package: str
@param coverage_packages: list of Python package to compute coverage results for. Defaults to package
@type coverage_packages: [str]
@param sysargs: (optional) alternate sys.argv
@type sysargs: [str]
"""
if sysargs is None:
# lazy-init sys args
import sys
sysargs = sys.argv import unittest if coverage_packages is None:
coverage_packages = [package] #parse sysargs
result_file = None
for arg in sysargs:
if arg.startswith(XML_OUTPUT_FLAG):
result_file = arg[len(XML_OUTPUT_FLAG):]
text_mode = '--text' in sysargs coverage_mode = '--cov' in sysargs or '--covhtml' in sysargs
if coverage_mode:
start_coverage(coverage_packages) # create and run unittest suite with our xmllrunner wrapper
suite = unittest.TestLoader().loadTestsFromTestCase(test)
if text_mode:
result = unittest.TextTestRunner(verbosity=2).run(suite)
else:
result = create_xml_runner(package, test_name, result_file).run(suite)
if coverage_mode:
#cov_html_dir = 'covhtml_test' if '--covhtml' in sysargs else None
cov_html_dir = os.getenv('ROS_TEST_RESULTS_DIR') + '/coverage/'
stop_coverage(coverage_packages, html=cov_html_dir) # test over, summarize results and exit appropriately
print_unittest_summary(result) if not result.wasSuccessful():
import sys
sys.exit(1)
def stop_coverage(packages, html=None):
"""
@param packages: list of packages to generate coverage reports for
@type packages: [str]
@param html: (optional) if not None, directory to generate html report to
@type html: str
"""
if _cov is None:
return
import sys, os
try:
_cov.stop()
# accumulate results
_cov.save() # - update our own .coverage-modules file list for
# coverage-html tool. The reason we read and rewrite instead
# of append is that this does a uniqueness check to keep the
# file from growing unbounded
if os.path.exists('.coverage-modules'):
with open('.coverage-modules','r') as f:
all_packages = set([x for x in f.read().split('\n') if x.strip()] + packages)
else:
all_packages = set(packages)
with open('.coverage-modules','w') as f:
f.write('\n'.join(all_packages)+'\n') try:
# list of all modules for html report
all_mods = [] # iterate over packages to generate per-package console reports
for package in packages:
pkg = __import__(package)
m = [v for v in sys.modules.values() if v and v.__name__.startswith(package)]
all_mods.extend(m) # generate overall report and per module analysis
_cov.report(m, show_missing=0)
for mod in m:
res = _cov.analysis(mod)
print("\n%s:\nMissing lines: %s"%(res[0], res[3])) if html: print("="*80+"\ngenerating html coverage report to %s\n"%html+"="*80)
_cov.html_report(all_mods, directory=html)
_cov.xml_report(all_mods, outfile=html + 'cover.xml')
except ImportError as e:
print("WARNING: cannot import '%s', will not generate coverage report"%package, file=sys.stderr)
except ImportError as e:
print("""WARNING: cannot import python-coverage, coverage tests will not run.
To install coverage, run 'easy_install coverage'""", file=sys.stderr)
4、测试代码例子
if __name__ == '__main__':
for arg in sys.argv:
print arg
testfiles = []
testfiles.append('被测试的python package')
testfiles.append('被测试的python package')
rostest.rosrun(PKG, NAME, 测试类名, sys.argv, testfiles)
ROS系统python代码测试之rostest的更多相关文章
- ROS系统C++代码测试之gtest
1. 安装gtestsudo apt-get install libgtest-dev 2.修改CMakeLists.txtfind_package(GTest REQUIRED)uncommend ...
- ros 使用python代码启动launch文件
在开发中我们经常会遇到使用python代码启动launch文件这样的问题.一般的做法是使用subprocess调用roslaunch.但是这种方法使用起来并不方便.要涉及到自己去控制进程的状态.由于r ...
- 光伏电池测控系统python代码
'''硬件keithley万用表和程控电源visa是VXIplug&play系统联盟制定的一套标准.python实现VISA,形成pyviva模块'''###IV测试系统的部分程序代码 fro ...
- 树莓派(Raspbian系统)中使用pyinstaller封装Python代码为可执行程序
一.前言 将做好的Python软件运行在树莓派上时,不想公开源码,就需要对文件进行封装(或称打包),本文主要介绍使用pyinstaller封装Python代码为可执行程序. Python是一个脚本语言 ...
- 基于深度学习的人脸性别识别系统(含UI界面,Python代码)
摘要:人脸性别识别是人脸识别领域的一个热门方向,本文详细介绍基于深度学习的人脸性别识别系统,在介绍算法原理的同时,给出Python的实现代码以及PyQt的UI界面.在界面中可以选择人脸图片.视频进行检 ...
- (二)ROS系统架构及概念 ROS Architecture and Concepts 以Kinetic为主更新 附课件PPT
第2章 ROS系统架构及概念 ROS Architecture and Concepts PPT说明: 正文用白色,命令或代码用黄色,右下角为对应中文译著页码. 这一章需要掌握ROS文件系统,运行图级 ...
- SLAM+语音机器人DIY系列:(二)ROS入门——2.ROS系统整体架构
摘要 ROS机器人操作系统在机器人应用领域很流行,依托代码开源和模块间协作等特性,给机器人开发者带来了很大的方便.我们的机器人“miiboo”中的大部分程序也采用ROS进行开发,所以本文就重点对ROS ...
- 【转载】ROS系统整体架构
目录 1.从文件系统级理解 2.从计算图级理解 3.从开源社区级理解 由于ROS系统的组织架构比较复杂,简单从一个方面来说明很难说清楚.按照ROS官方的说法,我们可以从3个方面来理解ROS系统整体架构 ...
- ROS-2 : ROS系统层级结构
一.ROS文件系统层级 ROS的文件和文件夹按如下层级来组织:
随机推荐
- Java Gradle入门指南之内建与定制任务类(buildSrc、Groovy等)
上一篇随笔介绍了Gradle的安装与任务管理,这篇着重介绍Gradle的内建任务(in-built tasks)与自定义任务(custom tasks),借助Gradle提供的众多内建任务类型 ...
- Spring AOP 动态代理 缓存
Spring AOP应用:xml配置及注解实现. 动态代理:jdk.cglib.javassist 缓存应用:高速缓存提供程序ehcache,页面缓存,session缓存 项目地址:https://g ...
- log4j 实现只输入我们指定包的日志
#all logger output level is 'ERROR' and output position is stdout #so only write our project's DEBUG ...
- Servlet/JSP-06 Session
一. 概述 Session 指客户端(浏览器)与服务器端之间保持状态的解决方案,有时候也用来指这种解决方案的存储结构. 当服务器端程序要为客户端的请求创建一个 Session 时,会首先检查这个请求里 ...
- C++/CLI——读书笔记《Visual C++/CLI从入门到精通》 第Ⅰ部分
=================================版权声明================================= 版权声明:本文为博主原创文章 未经许可不得转载 请通过右 ...
- SQL Server 2008 R2——T-SQL 存储过程 返回表
==================================声明================================== 本文原创,转载在正文中显要的注明作者和出处,并保证文章的完 ...
- 解决"is marked as crashed and should be repaired"方法
初次遇到这个问题是在服务器上放置mysql的磁盘空间满了(数据库目录和网站目录一定要做一定的分离,不要放在一个磁盘空间了) 当请求写入数据库时,php会提示 **** is marked as cra ...
- iOS OC和Swift进行互相调用
有时候 ,我们会涉及到双向混合编程,特别是OC和swift的互相引用. swift调用oc的方法: 1.桥接文件,一般是swift工程,在创建一个oc文件时,系统自动添加(不用改名,直接默认即可) 2 ...
- JQuery中的extend函数
1.jQuery.fn.extend(object) 扩展 jQuery 元素集来提供新的方法(通常用来制作插件). 例如:增加两个插件方法. jQuery.fn.extend({ check: fu ...
- java 生产者消费者问题 并发问题的解决
引言 生产者和消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一个存储空间,如下图所示,生产者向空间里存放数据,而消费者取用数据,如果不加以协调可能会出现以下情况: 生产者消费者图 ...