master.py
Go to the documentation of this file.
00001 # Software License Agreement (BSD License)
00002 #
00003 # Copyright (c) 2008, Willow Garage, Inc.
00004 # All rights reserved.
00005 #
00006 # Redistribution and use in source and binary forms, with or without
00007 # modification, are permitted provided that the following conditions
00008 # are met:
00009 #
00010 #  * Redistributions of source code must retain the above copyright
00011 #    notice, this list of conditions and the following disclaimer.
00012 #  * Redistributions in binary form must reproduce the above
00013 #    copyright notice, this list of conditions and the following
00014 #    disclaimer in the documentation and/or other materials provided
00015 #    with the distribution.
00016 #  * Neither the name of Willow Garage, Inc. nor the names of its
00017 #    contributors may be used to endorse or promote products derived
00018 #    from this software without specific prior written permission.
00019 #
00020 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00021 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00022 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00023 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00024 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00025 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00026 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00027 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00028 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00029 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00030 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00031 # POSSIBILITY OF SUCH DAMAGE.
00032 #
00033 # Revision $Id: testSlave.py 1100 2008-05-29 20:23:54Z sfkwc $
00034 
00035 import os
00036 import sys
00037 import string
00038 import time
00039 import xmlrpclib
00040 
00041 import rospy
00042 import rosgraph
00043 
00044 from rosclient import *
00045 
00046 NODE_INTEGRATION_NAME = "node_integration_test"
00047 
00048 _name = None
00049 ## set_node_name() must be called prior to the unit test so that the test harness knows its
00050 ## ROS name.
00051 def set_node_name(name):
00052     global _name
00053     _name = name
00054 
00055 # Have to try as hard as possible to not use rospy code in testing rospy code, so this is
00056 # a reimplementation of the caller ID spec so that NodeApiTestCase knows its name
00057 ## reimplementation of caller ID spec separately from rospy
00058 def get_caller_id():
00059     if _name is None:
00060         raise Exception("set_node_name has not been called yet")
00061     ros_ns = os.environ.get(rosgraph.ROS_NAMESPACE, rosgraph.names.GLOBALNS)
00062     return rosgraph.names.ns_join(ros_ns, _name)    
00063     
00064 class _MasterTestCase(TestRosClient):
00065 
00066     def __init__(self, *args):
00067         super(_MasterTestCase, self).__init__(*args)
00068         self.ns = os.environ.get(rosgraph.ROS_NAMESPACE, rosgraph.names.GLOBALNS)
00069         self.caller_id = get_caller_id()
00070         
00071     def setUp(self):
00072         super(_MasterTestCase, self).setUp()
00073         self.master_uri = os.environ.get(rosgraph.ROS_MASTER_URI, None)
00074         self._checkUri(self.master_uri)
00075         self.master = xmlrpclib.ServerProxy(self.master_uri)
00076 
00077     ## validates a URI as being http(s)
00078     def _checkUri(self, uri):
00079         import urlparse
00080         parsed = urlparse.urlparse(uri)
00081         self.assert_(parsed[0] in ['http', 'https'], 'protocol [%s] in [%s] invalid'%(parsed[0], uri))
00082         self.assert_(parsed[1], 'host missing [%s]'%uri)
00083         if not sys.version.startswith('2.4'): #check not available on py24
00084             self.assert_(parsed.port, 'port missing/invalid [%s]'%uri)        
00085     
00086 ## Expects a single test node to be running with name 'test_node' and subscribed to 'test_string'
00087 class MasterApiTestCase(_MasterTestCase):
00088 
00089     ## validate master.getMasterUri(caller_id)
00090     def _testGetMasterUri(self):
00091         # test with bad arity
00092         self.apiError(self.master.getMasterUri())
00093         # test success        
00094         uri = self.apiSuccess(self.master.getMasterUri(self.caller_id))
00095         self._checkUri(uri)
00096 
00097         # make sure we agree on ports
00098         import urlparse
00099         parsed = urlparse.urlparse(uri)
00100         parsed2 = urlparse.urlparse(self.master_uri)        
00101         
00102         self.assertEquals(parsed.port, parsed2.port, "expected ports do not match")
00103 
00104     ## validate master.getPid(caller_id)        
00105     def _testGetPid(self):
00106         # test with bad arity
00107         self.apiError(self.master.getPid())
00108         # test success        
00109         pid = self.apiSuccess(self.master.getPid(self.caller_id))
00110         self.assert_(pid > 0)
00111 
00112     ## validate master.getUri(caller_id)        
00113     def _testGetUri(self):
00114         # test with bad arity
00115         self.apiError(self.master.getUri())
00116         # test success        
00117         uri = self.apiSuccess(self.master.getUri(self.caller_id))
00118         self.assert_(type(uri) == str)
00119         
00120     ## common test subroutine of both register and unregister tests. registers the common test cases
00121     def _subTestRegisterServiceSuccess(self):
00122         master = self.master
00123         
00124         caller_id = '/service_node'
00125         caller_api = 'http://localhost:4567/'                
00126         service_base = '/service'
00127         
00128         # test success        
00129         for i in xrange(0, 10):
00130             service_name = "%s-%s"%(service_base, i)
00131             service_api = 'rosrpc://localhost:123%s/'%i
00132             # register the service
00133             self.apiSuccess(master.registerService(caller_id, service_name, service_api, caller_api))
00134             # test master state
00135             val = self.apiSuccess(master.lookupService(caller_id, service_name))
00136             self.assertEquals(service_api, val)
00137             val = self.apiSuccess(master.lookupNode(self.caller_id, caller_id)) 
00138             self.assertEquals(caller_api, val)
00139 
00140             _, _, srvs = self.apiSuccess(master.getSystemState(self.caller_id))
00141             for j in xrange(0, i+1):
00142                 jservice_name = "%s-%s"%(service_base, j)
00143                 jentry = [jservice_name, [caller_id]]
00144                 self.assert_(jentry in srvs, "master service list %s is missing %s"%(srvs, jentry))
00145 
00146         # TODO: have to test subscriber callback
00147         # TODO: validate with getSystemState()
00148 
00149     ## validate master.registerService(caller_id, service, service_api, caller_api) 
00150     def _testRegisterServiceSuccess(self):
00151         self._subTestRegisterServiceSuccess()
00152 
00153     def _testUnregisterServiceSuccess(self):
00154         self._subTestRegisterServiceSuccess()
00155         master = self.master
00156         caller_id = '/service_node'
00157         caller_api = 'http://localhost:4567/'                
00158         service_base = '/service'
00159 
00160         for i in xrange(0, 10):
00161             service_name = "%s-%s"%(service_base, i)
00162             service_api = 'rosrpc://localhost:123%s/'%i
00163 
00164             # unregister the service
00165             code, msg, val = master.unregisterService(caller_id, service_name, service_api)
00166             self.assertEquals(code, 1, "code != 1, return message was [%s]"%msg)
00167 
00168             # test the master state
00169             self.apiError(master.lookupService(self.caller_id, service_name), "master has a reference to unregistered service. message from master for unregister was [%s]"%msg)
00170 
00171             if i < 9:
00172                 val = self.apiSuccess(master.lookupNode(self.caller_id, caller_id))
00173                 self.assertEquals(caller_api, val, "master prematurely invalidated node entry for [%s] (lookupNode)"%caller_id)
00174             
00175             _, _, srvs = self.apiSuccess(master.getSystemState(self.caller_id))
00176             for j in xrange(0, i+1):
00177                 jservice_name = "%s-%s"%(service_base, j)
00178                 jentry = [jservice_name, [caller_id]]
00179                 self.assert_(jentry not in srvs, "master service list %s should not have %s"%(srvs, jentry))
00180             for j in xrange(i+1, 10):
00181                 jservice_name = "%s-%s"%(service_base, j)
00182                 jentry = [jservice_name, [caller_id]]
00183                 self.assert_(jentry in srvs, "master service list %s is missing %s"%(srvs, jentry))
00184 
00185             # TODO: have to test subscriber callback
00186             
00187         # Master's state should be zero'd out now
00188         
00189         #  - #457 make sure that lookupNode isn't returning stale info
00190         self.apiError(master.lookupNode(self.caller_id, caller_id), "master has a stale reference to unregistered service node API")
00191         _, _, srvs = self.apiSuccess(master.getSystemState(self.caller_id))
00192         srvs = [s for s in srvs if not s[0].startswith('/rosout/') and not s[0].endswith('/get_loggers') and not s[0].endswith('/set_logger_level')]
00193         self.assertEquals(0, len(srvs), "all services should have been unregistered: %s"%srvs)
00194 
00195     def _testRegisterServiceInvalid(self):
00196         master = self.master
00197         
00198         service = '/service'
00199         service_api = 'rosrpc://localhost:1234/'
00200         caller_api = 'http://localhost:4567/'                
00201 
00202         # test with bad arity
00203         self.apiError(master.registerService())
00204         self.apiError(master.registerService(self.caller_id, service))
00205         self.apiError(master.registerService(self.caller_id, service, service_api))
00206 
00207         # test with bad args
00208         self.apiError(master.registerService(self.caller_id, '', service_api, caller_api))
00209         self.apiError(master.registerService(self.caller_id, service, '', caller_api))        
00210         self.apiError(master.registerService(self.caller_id, service, service_api, ''))                
00211         
00212     def _testUnregisterServiceInvalid(self):
00213         master = self.master
00214         
00215         service = '/service'
00216         service_api = 'rosrpc://localhost:1234/'
00217 
00218         # test with bad arity
00219         self.apiError(master.unregisterService())
00220         self.apiError(master.unregisterService(self.caller_id, service))
00221 
00222         # test with bad args
00223         self.apiError(master.unregisterService(self.caller_id, '', service_api))
00224         self.apiError(master.unregisterService(self.caller_id, service, ''))        
00225 
00226     def _testRegisterPublisherInvalid(self):
00227         master = self.master
00228         
00229         topic = '/pub_topic'
00230         topic_type = 'test_rosmaster/String'        
00231         caller_api = 'http://localhost:4567/'                
00232 
00233         # test with bad arity
00234         self.apiError(master.registerPublisher())
00235         self.apiError(master.registerPublisher(self.caller_id, topic))
00236         self.apiError(master.registerPublisher(self.caller_id, topic, topic_type))
00237 
00238         # test with bad args
00239         self.apiError(master.registerPublisher(self.caller_id, '',    topic_type, caller_api))
00240         self.apiError(master.registerPublisher(self.caller_id, topic, '',         caller_api))        
00241         self.apiError(master.registerPublisher(self.caller_id, topic, topic_type, ''))
00242 
00243     def _testUnregisterPublisherInvalid(self):
00244         master = self.master
00245         
00246         topic = '/pub_topic'
00247         caller_api = 'http://localhost:4567/'                
00248 
00249         # test with bad arity
00250         self.apiError(master.unregisterPublisher())
00251         self.apiError(master.unregisterPublisher(self.caller_id, topic))
00252 
00253         # test with bad args
00254         self.apiError(master.unregisterPublisher(self.caller_id, '',    caller_api))
00255         self.apiError(master.unregisterPublisher(self.caller_id, topic, ''))
00256 
00257     def _testRegisterSubscriberInvalid(self):
00258         master = self.master
00259         
00260         topic = '/sub_topic'
00261         topic_type = 'test_rosmaster/String'        
00262         caller_api = 'http://localhost:4567/'                
00263 
00264         # test with bad arity
00265         self.apiError(master.registerSubscriber())
00266         self.apiError(master.registerSubscriber(self.caller_id, topic))
00267         self.apiError(master.registerSubscriber(self.caller_id, topic, topic_type))
00268 
00269         # test with bad args
00270         self.apiError(master.registerSubscriber(self.caller_id, '',    topic_type, caller_api))
00271         self.apiError(master.registerSubscriber(self.caller_id, topic, '',         caller_api))        
00272         self.apiError(master.registerSubscriber(self.caller_id, topic, topic_type, ''))
00273 
00274     def _testUnregisterSubscriberInvalid(self):
00275         master = self.master
00276         
00277         topic = '/sub_topic'
00278         caller_api = 'http://localhost:4567/'                
00279 
00280         # test with bad arity
00281         self.apiError(master.registerSubscriber())
00282         self.apiError(master.registerSubscriber(self.caller_id, topic))
00283 
00284         # test with bad args
00285         self.apiError(master.unregisterSubscriber(self.caller_id, '',    caller_api))
00286         self.apiError(master.unregisterSubscriber(self.caller_id, topic, ''))
00287 
00288     ## common test subroutine of both register and unregister tests. registers the common test cases
00289     def _subTestRegisterPublisherSuccess(self):
00290         master = self.master
00291         
00292         caller_id = '/pub_node'
00293         caller_api = 'http://localhost:4567/'                
00294         topic_base = '/pub_topic'
00295         topic_type = 'test_rosmaster/String'  
00296         
00297         # test success        
00298         for i in xrange(0, 10):
00299             topic_name = "%s-%s"%(topic_base, i)
00300             # register the topic
00301             self.apiSuccess(master.registerPublisher(caller_id, topic_name, topic_type, caller_api))
00302             # test master state
00303             # - master knows caller_id
00304             val = self.apiSuccess(master.lookupNode(self.caller_id, caller_id))
00305             self.assertEquals(caller_api, val)
00306             # - master knows topic type
00307             val = self.apiSuccess(master.getPublishedTopics(self.caller_id, '/'))
00308             self.assert_([topic_name, topic_type] in val, "master does not know topic type: %s"%val)
00309             #   - test new api as well
00310             val = self.apiSuccess(master.getTopicTypes(self.caller_id))
00311             self.assert_([topic_name, topic_type] in val, "master does not know topic type: %s"%val)
00312 
00313             pubs, _, _ = self.apiSuccess(master.getSystemState(self.caller_id))
00314             for j in xrange(0, i+1):
00315                 jtopic_name = "%s-%s"%(topic_base, j)
00316                 jentry = [jtopic_name, [caller_id]]
00317                 self.assert_(jentry in pubs, "master pub/sub list %s is missing %s"%(pubs, jentry))
00318             
00319         # TODO: have to test subscriber callback
00320 
00321     ## #591: this test may change if we make registering '*' unsupported
00322     def _testRegisterPublisherTypes(self):
00323         master = self.master
00324         caller_id = '/pub_node'
00325         caller_api = 'http://localhost:4567/'                
00326         topic_name = '/type_test_pub_topic'
00327 
00328         # register anytype first
00329         val = self.apiSuccess(master.registerPublisher(caller_id, topic_name, '*', caller_api))
00330         self.assertEquals([], val) # should report no subscribers
00331         val = self.apiSuccess(master.getPublishedTopics(self.caller_id, '/'))
00332         self.assert_([topic_name, '*'] in val, "master is not reporting * as type: %s"%val)
00333         #  - test new api as well
00334         val = self.apiSuccess(master.getTopicTypes(self.caller_id))
00335         self.assert_([topic_name, '*'] in val, "master is not reporting * as type: %s"%val)
00336         
00337         # register a grounded type and make sure that '*' can't overwrite it
00338         for t in ['test_rosmaster/String', '*']:
00339             val = self.apiSuccess(master.registerPublisher(caller_id, topic_name, t, caller_api))   
00340             self.assertEquals([], val) # should report no subscribers
00341             val = self.apiSuccess(master.getPublishedTopics(self.caller_id, '/'))
00342             self.assert_([topic_name, 'test_rosmaster/String'] in val, "master is not reporting test_rosmaster/String as type: %s"%val)
00343 
00344             val = self.apiSuccess(master.getTopicTypes(self.caller_id))
00345             self.assert_([topic_name, 'test_rosmaster/String'] in val, "master is not reporting test_rosmaster/String as type: %s"%val)
00346         
00347     ## validate master.registerPublisher(caller_id, topic, topic_api, caller_api) 
00348     def _testRegisterPublisherSuccess(self):
00349         self._subTestRegisterPublisherSuccess()
00350 
00351         # a couple more test cases to verify that registerPublisher's return value is correct
00352         master = self.master
00353         topic = '/pub_topic-0'
00354         type = 'test_rosmaster/String'
00355         pub_caller_api = 'http://localhost:4567/'
00356         
00357         subs = []
00358         for i in xrange(5678, 5685):
00359             api = 'http://localhost:%s'%i
00360             subs.append(api)
00361             self.apiSuccess(master.registerSubscriber('/sub_node-%i'%i, topic, type, api))
00362             val = self.apiSuccess(master.registerPublisher('/pub_node', topic, type, pub_caller_api))            
00363             self.assertEquals(subs, val)
00364         
00365     def _testUnregisterPublisherSuccess(self):
00366         self._subTestRegisterPublisherSuccess()
00367         master = self.master
00368         caller_id = '/pub_node'
00369         caller_api = 'http://localhost:4567/' 
00370         topic_base = '/pub_topic'
00371 
00372         for i in xrange(0, 10):
00373             topic_name = "%s-%s"%(topic_base, i)
00374 
00375             # unregister the topic
00376             code, msg, val = master.unregisterPublisher(caller_id, topic_name, caller_api)
00377             self.assertEquals(code, 1, "code != 1, return message was [%s]"%msg)
00378 
00379             # test the master state
00380             if i < 9:
00381                 val = self.apiSuccess(master.lookupNode(self.caller_id, caller_id))
00382                 self.assertEquals(caller_api, val, "master prematurely invalidated node entry for [%s] (lookupNode)"%caller_id)
00383 
00384             pubs, _, _ = self.apiSuccess(master.getSystemState(self.caller_id))
00385             for j in xrange(0, i+1):
00386                 jtopic_name = "%s-%s"%(topic_base, j)
00387                 jentry = [jtopic_name, [caller_id]]
00388                 self.assert_(jentry not in pubs, "master pub/sub list %s should not have %s"%(pubs, jentry))
00389             for j in xrange(i+1, 10):
00390                 jtopic_name = "%s-%s"%(topic_base, j)
00391                 jentry = [jtopic_name, [caller_id]]
00392                 self.assert_(jentry in pubs, "master pub/sub list %s is missing %s"%(pubs, jentry))
00393 
00394             # TODO: have to test subscriber callback
00395             
00396         #  - #457 make sure that lookupNode isn't returning stale info
00397         self.apiError(master.lookupNode(self.caller_id, caller_id), "master has a stale reference to unregistered topic node API. pubs are %s"%pubs)
00398 
00399     ## common test subroutine of both register and unregister tests. registers the common test cases
00400     ## 'simple' test cases do not setup any publisher state to validate against
00401     def _subTestRegisterSubscriberSimpleSuccess(self):
00402         master = self.master
00403         
00404         caller_id = '/sub_node'
00405         caller_api = 'http://localhost:4567/'                
00406         topic_base = '/sub_topic'
00407         topic_type = 'test_rosmaster/String'  
00408         
00409         # test success        
00410         for i in xrange(0, 10):
00411             topic_name = "%s-%s"%(topic_base, i)
00412             # register the topic
00413             self.apiSuccess(master.registerSubscriber(caller_id, topic_name, topic_type, caller_api))
00414             # test master state
00415             # - master knows caller_id
00416             val = self.apiSuccess(master.lookupNode(self.caller_id, caller_id))
00417             self.assertEquals(caller_api, val)
00418 
00419             # - master should know topic type
00420             val = self.apiSuccess(master.getTopicTypes(self.caller_id))
00421             self.assert_([topic_name, topic_type] in val, "master does not know topic type: %s"%val)
00422             
00423             _, subs, _ = self.apiSuccess(master.getSystemState(self.caller_id))
00424             for j in xrange(0, i+1):
00425                 jtopic_name = "%s-%s"%(topic_base, j)
00426                 jentry = [jtopic_name, [caller_id]]
00427                 self.assert_(jentry in subs, "master pub/sub list %s is missing %s"%(subs, jentry))
00428             
00429     ## validate master.registerSubscriber(caller_id, topic, topic_api, caller_api) 
00430     def _testRegisterSubscriberSimpleSuccess(self):
00431         self._subTestRegisterSubscriberSimpleSuccess()
00432 
00433     def _testUnregisterSubscriberSuccess(self):
00434         self._subTestRegisterSubscriberSimpleSuccess()
00435         master = self.master
00436         caller_id = '/sub_node'
00437         caller_api = 'http://localhost:4567/' 
00438         topic_base = '/sub_topic'
00439 
00440         for i in xrange(0, 10):
00441             topic_name = "%s-%s"%(topic_base, i)
00442 
00443             # unregister the topic
00444             code, msg, val = master.unregisterSubscriber(caller_id, topic_name, caller_api)
00445             self.assertEquals(code, 1, "code != 1, return message was [%s]"%msg)
00446 
00447             # test the master state
00448             if i < 9:
00449                 val = self.apiSuccess(master.lookupNode(self.caller_id, caller_id))
00450                 self.assertEquals(caller_api, val, "master prematurely invalidated node entry for [%s] (lookupNode)"%caller_id)
00451 
00452             _, subs, _ = self.apiSuccess(master.getSystemState(self.caller_id))
00453             for j in xrange(0, i+1):
00454                 jtopic_name = "%s-%s"%(topic_base, j)
00455                 jentry = [jtopic_name, [caller_id]]
00456                 self.assert_(jentry not in subs, "master pub/sub list %s should not have %s"%(subs, jentry))
00457             for j in xrange(i+1, 10):
00458                 jtopic_name = "%s-%s"%(topic_base, j)
00459                 jentry = [jtopic_name, [caller_id]]
00460                 self.assert_(jentry in subs, "master pub/sub list %s is missing %s"%(subs, jentry))
00461 
00462         #  - #457 make sure that lookupNode isn't returning stale info
00463         self.apiError(master.lookupNode(self.caller_id, caller_id), "master has a stale reference to unregistered topic node API. subs are %s"%subs)
00464 
00465 


test_rosmaster
Author(s): Ken Conley
autogenerated on Mon Oct 6 2014 11:47:16