aggregator_test.py
Go to the documentation of this file.
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)


diagnostic_aggregator
Author(s): Kevin Watts, Brice Rebsamen
autogenerated on Sun Oct 5 2014 23:27:29