1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 from __future__ import print_function
36
37
38
39
40 import os
41 import sys
42 import time
43 import unittest
44 import logging
45
46 import roslaunch
47 import rospkg
48 from rospkg.environment import ROS_TEST_RESULTS_DIR
49 import rosgraph.roslogging
50
51 from rostest.rostestutil import createXMLRunner, printRostestSummary, \
52 xmlResultsFile, rostest_name_from_path
53 from rostest.rostest_parent import ROSTestLaunchParent
54
55 import rostest.runner
56
57 _NAME = 'rostest'
58
66
68
69 results_file_dir = os.path.dirname(results_file)
70 if not os.path.isdir(results_file_dir):
71 os.makedirs(results_file_dir)
72 with open(results_file, 'w') as f:
73 d = {'test': outname, 'test_file': test_file }
74 f.write("""<?xml version="1.0" encoding="UTF-8"?>
75 <testsuite tests="1" failures="1" time="1" errors="0" name="%(test)s">
76 <testcase name="test_ran" status="run" time="1" classname="Results">
77 <failure message="rostest file [%(test_file)s] does not exist" type=""/>
78 </testcase>
79 </testsuite>"""%d)
80
82 import roslaunch.rlutil
83
84 from optparse import OptionParser
85 parser = OptionParser(usage="usage: %prog [options] [package] <filename>", prog=_NAME)
86 parser.add_option("-t", "--text",
87 action="store_true", dest="text_mode", default=False,
88 help="Run with stdout output instead of XML output")
89 parser.add_option("--pkgdir", metavar="PKG_DIR",
90 dest="pkg_dir", default=None,
91 help="package dir")
92 parser.add_option("--package", metavar="PACKAGE",
93 dest="package", default=None,
94 help="package")
95 parser.add_option("--results-filename", metavar="RESULTS_FILENAME",
96 dest="results_filename", default=None,
97 help="results_filename")
98 parser.add_option("--results-base-dir", metavar="RESULTS_BASE_DIR",
99 help="The base directory of the test results. The test result file is " +
100 "created in a subfolder name PKG_DIR.")
101 parser.add_option("-r", "--reuse-master", action="store_true",
102 help="Connect to an existing ROS master instead of spawning a new ROS master on a custom port")
103 parser.add_option("-c", "--clear", action="store_true",
104 help="Clear all parameters when connecting to an existing ROS master (only works with --reuse-master)")
105 (options, args) = parser.parse_args()
106
107 if options.clear and not options.reuse_master:
108 print("The --clear option is only valid with --reuse-master", file=sys.stderr)
109 sys.exit(1)
110
111 try:
112 args = roslaunch.rlutil.resolve_launch_arguments(args)
113 except roslaunch.core.RLException as e:
114 print(str(e), file=sys.stderr)
115 sys.exit(1)
116
117
118 logfile_name = configure_logging()
119 logger = logging.getLogger('rostest')
120 import roslaunch.core
121 roslaunch.core.add_printlog_handler(logger.info)
122 roslaunch.core.add_printerrlog_handler(logger.error)
123
124 logger.info('rostest starting with options %s, args %s'%(options, args))
125 if len(args) == 0:
126 parser.error("You must supply a test file argument to rostest.")
127 if len(args) != 1:
128 parser.error("rostest only accepts a single test file")
129
130
131 test_file = args[0]
132 if options.pkg_dir and options.package:
133
134
135 pkg_dir, pkg = options.pkg_dir, options.package
136 else:
137 pkg = rospkg.get_package_name(test_file)
138 r = rospkg.RosPack()
139 pkg_dir = r.get_path(pkg)
140
141 if options.results_filename:
142 outname = options.results_filename
143 if '.' in outname:
144 outname = outname[:outname.rfind('.')]
145 else:
146 outname = rostest_name_from_path(pkg_dir, test_file)
147
148 env = None
149 if options.results_base_dir:
150 env = {ROS_TEST_RESULTS_DIR: options.results_base_dir}
151
152
153 if not os.path.isfile(test_file):
154 results_file = xmlResultsFile(pkg, outname, True, env=env)
155 write_bad_filename_failure(test_file, results_file, outname)
156 parser.error("test file is invalid. Generated failure case result file in %s"%results_file)
157
158 try:
159 testCase = rostest.runner.createUnitTest(pkg, test_file, options.reuse_master, options.clear, options.results_base_dir)
160 suite = unittest.TestLoader().loadTestsFromTestCase(testCase)
161
162 if options.text_mode:
163 rostest.runner.setTextMode(True)
164 result = unittest.TextTestRunner(verbosity=2).run(suite)
165 else:
166 is_rostest = True
167 results_file = xmlResultsFile(pkg, outname, is_rostest, env=env)
168 xml_runner = createXMLRunner(pkg, outname, \
169 results_file=results_file, \
170 is_rostest=is_rostest)
171 result = xml_runner.run(suite)
172 finally:
173
174 test_parents = rostest.runner.getRostestParents()
175 for r in test_parents:
176 logger.info("finally rostest parent tearDown [%s]", r)
177 r.tearDown()
178 del test_parents[:]
179 from roslaunch.pmon import pmon_shutdown
180 logger.info("calling pmon_shutdown")
181 pmon_shutdown()
182 logger.info("... done calling pmon_shutdown")
183
184
185 config = rostest.runner.getConfig()
186 if config:
187 if config.config_errors:
188 print("\n[ROSTEST WARNINGS]"+'-'*62+'\n', file=sys.stderr)
189 for err in config.config_errors:
190 print(" * %s"%err, file=sys.stderr)
191 print('')
192
193
194 subtest_results = rostest.runner.getResults()
195 if not options.text_mode:
196 printRostestSummary(result, subtest_results)
197 else:
198 print("WARNING: overall test result is not accurate when --text is enabled")
199
200 if logfile_name:
201 print("rostest log file is in %s"%logfile_name)
202
203 if not result.wasSuccessful():
204 sys.exit(1)
205 elif subtest_results.num_errors or subtest_results.num_failures:
206 sys.exit(2)
207
208 if __name__ == '__main__':
209 rostestmain()
210