Package rostest :: Module rostest_main

Source Code for Module rostest.rostest_main

  1  # Software License Agreement (BSD License) 
  2  # 
  3  # Copyright (c) 2008, Willow Garage, Inc. 
  4  # All rights reserved. 
  5  # 
  6  # Redistribution and use in source and binary forms, with or without 
  7  # modification, are permitted provided that the following conditions 
  8  # are met: 
  9  # 
 10  #  * Redistributions of source code must retain the above copyright 
 11  #    notice, this list of conditions and the following disclaimer. 
 12  #  * Redistributions in binary form must reproduce the above 
 13  #    copyright notice, this list of conditions and the following 
 14  #    disclaimer in the documentation and/or other materials provided 
 15  #    with the distribution. 
 16  #  * Neither the name of Willow Garage, Inc. nor the names of its 
 17  #    contributors may be used to endorse or promote products derived 
 18  #    from this software without specific prior written permission. 
 19  # 
 20  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 21  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 22  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 23  # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 24  # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 25  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 26  # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 27  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 28  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 29  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 30  # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 31  # POSSIBILITY OF SUCH DAMAGE. 
 32  # 
 33  # Revision $Id$ 
 34   
 35  from __future__ import print_function 
 36   
 37  # NOTE: this has not survived the many refactorings and roslaunch changes well. There are too many ugly globals and bad 
 38  # code organizational choices at this point, but it's not a high priority to cleanup. 
 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   
59 -def configure_logging():
60 import socket 61 logfile_basename = 'rostest-%s-%s.log'%(socket.gethostname(), os.getpid()) 62 logfile_name = rosgraph.roslogging.configure_logging('rostest', filename=logfile_basename) 63 if logfile_name: 64 print("... logging to %s"%logfile_name) 65 return logfile_name
66
67 -def write_bad_filename_failure(test_file, results_file, outname):
68 # similar to rostest-check-results 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
81 -def rostestmain():
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 # make sure all loggers are configured properly 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 # compute some common names we'll be using to generate test names and files 131 test_file = args[0] 132 if options.pkg_dir and options.package: 133 # rosbuild2: the build system knows what package and directory, so let it tell us, 134 # instead of shelling back out to rospack 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 # #1140 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 # really make sure that all of our processes have been killed 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 # print config errors after test has run so that we don't get caught up in .xml results 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 # summary is worthless if textMode is on as we cannot scrape .xml results 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