$search
00001 #!/usr/bin/env python 00002 # Software License Agreement (BSD License) 00003 # 00004 # Copyright (c) 2009, Willow Garage, Inc. 00005 # All rights reserved. 00006 # 00007 # Redistribution and use in source and binary forms, with or without 00008 # modification, are permitted provided that the following conditions 00009 # are met: 00010 # 00011 # * Redistributions of source code must retain the above copyright 00012 # notice, this list of conditions and the following disclaimer. 00013 # * Redistributions in binary form must reproduce the above 00014 # copyright notice, this list of conditions and the following 00015 # disclaimer in the documentation and/or other materials provided 00016 # with the distribution. 00017 # * Neither the name of the Willow Garage nor the names of its 00018 # contributors may be used to endorse or promote products derived 00019 # from this software without specific prior written permission. 00020 # 00021 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 00022 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00023 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 00024 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 00025 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 00026 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 00027 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 00028 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 00029 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00030 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 00031 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00032 # POSSIBILITY OF SUCH DAMAGE. 00033 # 00034 00035 ##\author Kevin Watts 00036 00037 ##\brief Tests receipt of /diagnostics_agg from diagnostic aggregator 00038 00039 from __future__ import with_statement 00040 PKG = 'diagnostic_aggregator' 00041 00042 import roslib; roslib.load_manifest(PKG) 00043 00044 import unittest 00045 import rospy, rostest 00046 from time import sleep 00047 import sys 00048 from optparse import OptionParser 00049 import threading 00050 import types 00051 00052 from diagnostic_msgs.msg import DiagnosticArray 00053 00054 prefix = "" 00055 00056 ##\brief Removes name chaff (ex: 'tilt_hokuyo_node: Frequency' to 'Frequency') 00057 def fix_sub_name(name, remove_prefixes): 00058 last = str(name) 00059 for start_name in remove_prefixes: 00060 if last.startswith(start_name): 00061 last = last[len(start_name):] 00062 if last.startswith(':'): 00063 last = last[1:] 00064 while last.startswith(' '): 00065 last = last[1:] 00066 00067 return last 00068 00069 def combine_name_prefix(my_prefix, name, remove_prefixes): 00070 fixed = fix_sub_name(name.replace('/', ''), remove_prefixes) 00071 return '/'.join([prefix, my_prefix, fixed]) 00072 00073 def header_name(my_prefix): 00074 return '/'.join([prefix, my_prefix]) 00075 00076 def _get_params_list(params): 00077 out = [] 00078 if type(params) in (list, tuple): 00079 for p in params: 00080 out.append(str(p)) 00081 return out 00082 return [ str(params) ] 00083 00084 def name_to_full_generic(name, my_prefix, value, header=False): 00085 remove_prefixes = [] 00086 if value.has_key('remove_prefix'): 00087 remove_prefixes = _get_params_list(value['remove_prefix']) 00088 00089 if value.has_key('find_and_remove_prefix'): 00090 for rp in _get_params_list(value['find_and_remove_prefix']): 00091 remove_prefixes.extend(_get_params_list(value['find_and_remove_prefix'])) 00092 for sw in _get_params_list(value['find_and_remove_prefix']): 00093 if name.startswith(sw): 00094 if header: 00095 return header_name(my_prefix) 00096 return combine_name_prefix(my_prefix, name, remove_prefixes) 00097 00098 if value.has_key('startswith'): 00099 for sw in _get_params_list(value['startswith']): 00100 if name.startswith(sw): 00101 if header: 00102 return header_name(my_prefix) 00103 return combine_name_prefix(my_prefix, name, remove_prefixes) 00104 00105 if value.has_key('contains'): 00106 for con in _get_params_list(value['contains']): 00107 if name.find(con) >= 0: 00108 if header: 00109 return header_name(my_prefix) 00110 return combine_name_prefix(my_prefix, name, remove_prefixes) 00111 00112 if value.has_key('name'): 00113 for nm in _get_params_list(value['name']): 00114 if name == nm: 00115 if header: 00116 return header_name(my_prefix) 00117 return combine_name_prefix(my_prefix, name, remove_prefixes) 00118 00119 if value.has_key('expected'): 00120 for nm in _get_params_list(value['expected']): 00121 if name == nm: 00122 if header: 00123 return header_name(my_prefix) 00124 return combine_name_prefix(my_prefix, name, remove_prefixes) 00125 00126 00127 return None 00128 00129 def name_to_agg_name(name, params): 00130 for key, value in params.iteritems(): 00131 if not value.has_key('path') or not value.has_key('type'): 00132 return None 00133 my_prefix = value['path'] 00134 if value['type'] == 'GenericAnalyzer' or value['type'] == 'diagnostic_aggregator/GenericAnalyzer': 00135 generic_name = name_to_full_generic(name, my_prefix, value) 00136 if generic_name is not None: 00137 return generic_name 00138 else: 00139 return None 00140 00141 # If we don't have it... 00142 return combine_name_prefix('Other', name, []) 00143 00144 # Returns header name for particular item 00145 def name_to_agg_header(name, params): 00146 for key, value in params.iteritems(): 00147 if not value.has_key('path') or not value.has_key('type'): 00148 return None 00149 my_prefix = value['path'] 00150 if value['type'] == 'GenericAnalyzer' or value['type'] == 'diagnostic_aggregator/GenericAnalyzer': 00151 generic_name = name_to_full_generic(name, my_prefix, value, header=True) 00152 if generic_name is not None: 00153 return generic_name 00154 else: 00155 return None 00156 00157 # If we don't have it... 00158 return header_name('Other') 00159 00160 ##\brief Uses aggregator parameters to compare diagnostics with aggregated output 00161 class TestAggregator(unittest.TestCase): 00162 def __init__(self, *args): 00163 super(TestAggregator, self).__init__(*args) 00164 parser = OptionParser(usage="./%prog [options]", prog="aggregator_test.py") 00165 parser.add_option('--gtest_output', action="store", dest="gtest") 00166 parser.add_option('--param_name', action="store", dest="param", 00167 default='diag_agg', metavar="PARAM_NAME", 00168 help="Name of parameter that defines analyzers") 00169 parser.add_option('--duration', action="store", dest="duration", 00170 default=10, metavar="DURATIION", 00171 help="Duration of test") 00172 parser.add_option('--base_path', action="store", dest="base_path", 00173 default="", metavar="BASE_PATH", 00174 help="Base path for all output topics") 00175 00176 self.diag_msgs = {} 00177 self.agg_msgs = {} 00178 00179 rospy.init_node('test_diag_agg') 00180 options, args = parser.parse_args(rospy.myargv()) 00181 00182 global prefix 00183 prefix = options.base_path 00184 00185 self.params = rospy.get_param(options.param) 00186 self.duration = options.duration 00187 rospy.Subscriber('diagnostics_agg', DiagnosticArray, self.cb) 00188 rospy.Subscriber('diagnostics', DiagnosticArray, self.diag_cb) 00189 00190 self._mutex = threading.Lock() 00191 00192 def diag_cb(self, msg): 00193 with self._mutex: 00194 for stat in msg.status: 00195 self.diag_msgs[stat.name] = stat 00196 00197 00198 def cb(self, msg): 00199 with self._mutex: 00200 for stat in msg.status: 00201 self.agg_msgs[stat.name] = stat 00202 00203 def test_agg(self): 00204 start = rospy.get_time() 00205 while not rospy.is_shutdown(): 00206 sleep(1.0) 00207 if rospy.get_time() - start > self.duration: 00208 break 00209 00210 self.assert_(not rospy.is_shutdown(), "Rospy shutdown") 00211 00212 with self._mutex: 00213 all_headers = {} 00214 00215 for name, msg in self.agg_msgs.iteritems(): 00216 self.assert_(name.startswith('/'), "Aggregated name %s doesn't start with \"/\"" % name) 00217 00218 # Go through all messages and check that we have them in aggregate 00219 for name, msg in self.diag_msgs.iteritems(): 00220 agg_name = name_to_agg_name(name, self.params) 00221 00222 self.assert_(agg_name is not None, 'Aggregated name is None for %s' % name) 00223 self.assert_(self.agg_msgs.has_key(agg_name), 'No matching name found for name: %s, aggregated name: %s' % (name, agg_name)) 00224 self.assert_(msg.level == self.agg_msgs[agg_name].level, 'Status level of original, aggregated messages doesn\'t match. Name: %s, aggregated name: %s.' % (name, agg_name)) 00225 self.assert_(msg.message == self.agg_msgs[agg_name].message, 'Status message of original, aggregated messages doesn\'t match. Name: %s, aggregated name: %s' % (name, agg_name)) 00226 00227 # This is because the analyzers only reports stale if 00228 # all messages underneath it are stale 00229 if self.agg_msgs[agg_name].level == 3: # Stale 00230 self.agg_msgs[agg_name].level = -1 00231 00232 header = name_to_agg_header(name, self.params) 00233 if all_headers.has_key(header): 00234 all_headers[header] = max(all_headers[header], self.agg_msgs[agg_name].level) 00235 else: 00236 all_headers[header] = self.agg_msgs[agg_name].level 00237 00238 del self.agg_msgs[agg_name] 00239 00240 # Check that we have all_headers 00241 for header, lvl in all_headers.iteritems(): 00242 # If everything is stale, report stale. Otherwise, it should report an error 00243 if lvl == -1: 00244 lvl = 3 00245 00246 self.assert_(self.agg_msgs.has_key(header), "Header %s not found in messages" % header) 00247 self.assert_(self.agg_msgs[header].level == lvl, "Level of header %s doesn't match expected value." % header) 00248 del self.agg_msgs[header] 00249 00250 # Check that we have the main header message 00251 if len(prefix) > 0: 00252 self.assert_(len(self.agg_msgs) == 1, "Incorrect number of messages remaining: %d. Messages: %s" % (len(self.agg_msgs), str(self.agg_msgs))) 00253 00254 self.assert_(self.agg_msgs.has_key(prefix), "Global prefix not found in messages: %s. Messages: %s" % (prefix, str(self.agg_msgs))) 00255 else: 00256 self.assert_(len(self.agg_msgs) == 0, "Incorrect number of messages remaining: %d. Messages: %s. Expected 0." % (len(self.agg_msgs), str(self.agg_msgs))) 00257 00258 00259 00260 if __name__ == '__main__': 00261 print 'SYS ARGS:', sys.argv 00262 rostest.run(PKG, sys.argv[0], TestAggregator, sys.argv)