testMaster.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$
00034 """
00035 testMaster: ROS integration test cases for master XML-RPC API
00036 
00037 To run, invoke nodes/testMaster
00038 """
00039 
00040 import os, sys, getopt, traceback, logging, socket
00041 import datetime, xmlrpclib, math, random
00042 import unittest
00043 import rospy
00044 from rostest import *
00045 from testSlave import msMain
00046 
00047 MYPKG = 'test_rosmaster'
00048 
00049 HAS_PARAM = True
00050 
00051 singletest = 'testGetFlowNames'
00052 singletest = None
00053 #singletest = 'testDotLocalNames'
00054 
00055 def verifyNodeAddress(master, callerId, name, machine, addr, port):
00056     if not name:
00057         raise Exception("name is None")
00058     rmachine, raddr, rport = apiSuccess(master.getNodeAddress(callerId, name))
00059     if machine:
00060         assert rmachine == machine, "Node [%s] is running on '%s' instead of '%s'"%(name, rmachine, machine)
00061     if port:
00062         assert rport == port, "Node [%s] is running on '%s' instead of '%s'"%(name, rport, port)
00063     else:
00064         assert rport, "Node [%s] does not have a registered port"%name
00065     if addr:
00066         if addr == 'localhost':
00067             addr = socket.gethostname()
00068         if raddr == 'localhost':
00069             raddr = socket.gethostname()
00070         addrinfo = socket.getaddrinfo(addr, 0, 0, 0, socket.SOL_TCP)
00071         raddrinfo = socket.getaddrinfo(raddr, 0, 0, 0, socket.SOL_TCP)
00072         assert raddrinfo == addrinfo, "%s!=%s" % (raddrinfo, addrinfo)
00073     #ping the node
00074     apiSuccess(xmlrpclib.ServerProxy("http://%s:%s/"%(raddr, rport)).getPid(''))
00075 
00076 def testGraphState(master, graphNodes, graphFlows):
00077     graph = apiSuccess(master.getGraph(''))
00078     diff = set(graph[0]) ^ set(graphNodes)
00079     assert not diff, "Graph nodes %s does not match expected %s: %s"%(graph[0], graphNodes, diff)
00080     # stringify for comparison
00081     expectedFlows = ["%s:%s:1"%f for f in graphFlows] # :1 = connected
00082     print graph[1]
00083     remoteFlows = ["%s:%s:%s"%(src,snk,c) for (src,snk,c) in graph[1]]
00084     if expectedFlows or remoteFlows:
00085         #assert set(expectedFlows) ^ set(remoteFlows), "Graph flows [%s] does not match expected [%s]"%(graph[1], graphFlows)
00086         diff = set(expectedFlows) ^ set(remoteFlows)
00087         assert not diff, "Graph flows %s does not match expected %s: %s"%(expectedFlows, remoteFlows, diff)
00088 
00089 def testParamState(master, myState):
00090     callerId = 'master' #validate from root 
00091     for (k, v) in myState.iteritems():
00092         if HAS_PARAM:
00093             assert apiSuccess(master.hasParam(callerId, k))
00094         print "verifying parameter %s"%k
00095         v2 = apiSuccess(master.getParam(callerId, k))
00096         if isinstance(v2, xmlrpclib.DateTime):
00097             assert xmlrpclib.DateTime(v) == v2, "[%s]: %s != %s, %s"%(k, v, v2, v2.__class__)
00098         else:
00099             assert v == v2, "[%s]: %s != %s, %s"%(k, v, v2, v2.__class__)
00100     paramNames = myState.keys()
00101     remoteParamNames = apiSuccess(master.getParamNames(callerId))
00102     assert not set(paramNames) ^ set(remoteParamNames), "parameter server keys do not match local" 
00103 
00104 class ParamServerTestCase(ROSGraphTestCase):
00105     """Parameter Server API Test Cases"""
00106     
00107     def setUp(self):
00108         super(ParamServerTestCase, self).setUp()
00109     
00110     def tearDown(self):
00111         super(ParamServerTestCase, self).tearDown()        
00112 
00113     def _testSetParam(self, ctx, myState, testVals, master):
00114         for type, vals in testVals:
00115             try:
00116                 if ctx:
00117                     callerId = "%s.node"%ctx
00118                 else:
00119                     callerId = "node"
00120                 count = 0
00121                 for val in vals:
00122                     key = "%s-%s"%(type,count)
00123                     print "master.setParam(%s,%s,%s)"%(callerId, key, val)
00124                     master.setParam(callerId, key, val)
00125                     if HAS_PARAM:                    
00126                         assert apiSuccess(master.hasParam(callerId, key))
00127                     if ctx:
00128                         trueKey = "%s.%s"%(ctx, key)
00129                     else:
00130                         trueKey = key
00131                     myState[trueKey] = val
00132                     count += 1
00133             except:
00134                 assert "getParam failed on type[%s], val[%s]"%(type,val)
00135         testParamState(master, myState)
00136 
00137     def testParamValues(self):
00138         """testParamValues: test storage of all XML-RPC compatible types"""
00139         from xmlrpclib import Binary
00140         testVals = [
00141             ['int', [0, 1024, 2147483647, -2147483647]],
00142             ['boolean', [True, False]],
00143             ['string', ['', '\0', 'x', 'hello', ''.join([chr(n) for n in xrange(0, 255)])]],
00144             ['double', [0.0, math.pi, -math.pi, 3.4028235e+38, -3.4028235e+38]],
00145             #TODO: microseconds?
00146             ['datetime', [datetime.datetime(2005, 12, 6, 12, 13, 14), datetime.datetime(1492, 12, 6, 12, 13, 14)]],
00147             ['base64', [Binary(''), Binary('\0'), Binary(''.join([chr(n) for n in xrange(0, 255)]))]],
00148             ['struct', [{ "a": 2, "b": 4},
00149                         {"a" : "b", "c" : "d"},
00150                         {"a" : {"b" : { "c" : "d"}}}]],
00151             ['array', [[], [1, 2, 3], ['a', 'b', 'c'], [0.0, 0.1, 0.2, 2.0, 2.1, -4.0],
00152                        [1, 'a', math.pi], [[1, 2, 3], ['a', 'b', 'c'], [1.0, 2.1, 3.2]]]
00153              ],
00154             ]
00155         master = self.master
00156 
00157         print "Putting parameters onto the server"
00158         # put our params into the parameter server
00159         contexts = ['', 'scope1', 'scope2', 'scope.subscope1', 'scope.sub1.sub2']
00160         myState = {}
00161         for ctx in contexts:
00162             self._testSetParam(ctx, myState, testVals, master)
00163 
00164         print "Deleting all of our parameters"
00165         # delete all of our parameters
00166         paramKeys = myState.keys()
00167         for key in paramKeys:
00168             apiSuccess(master.deleteParam('', key))
00169             del myState[key]
00170         testParamState(master, myState)
00171 
00172     def testEncapsulation(self):
00173         """testEncapsulation: test encapsulation: setting same parameter at different levels"""
00174         master = self.master
00175         myState = {}
00176         testParamState(master, myState)
00177 
00178         testContexts = ['', 'en', 'en.sub1', 'en.sub2', 'en.sub1.sub2']
00179         for c in testContexts:
00180             testKey = 'param1'
00181             testVal = random.randint(-1000000, 100000)
00182             if c:
00183                 callerId = "%s.node"%c
00184                 trueKey = "%s.%s"%(c,testKey)
00185             else:
00186                 callerId ="node"
00187                 trueKey = testKey
00188             master.setParam(callerId, testKey, testVal)
00189             myState[trueKey] = testVal
00190             # make sure the master has the parameter under both keys and that they are equal
00191             v1 = apiSuccess(master.getParam('', trueKey))
00192             v2 = apiSuccess(master.getParam(callerId, testKey))
00193             assert v1 == v2, "[%s]: %s vs. [%s,%s]: %s"%(trueKey, v1, callerId, testKey, v2)
00194             if HAS_PARAM:
00195                 assert apiSuccess(master.hasParam(callerId, testKey)), testKey
00196                 assert apiSuccess(master.hasParam('node', trueKey)), trueKey
00197 
00198         testParamState(master, myState)
00199 
00200     def testDotLocalNames(self):
00201         master = self.master
00202         myState = {}
00203         testParamState(master, myState)
00204 
00205         testContexts = ['', 'sub1', 'sub1.sub2', 'sub1.sub2.sub3']
00206         for c in testContexts:
00207             if c:
00208                 callerId = "%s.node"%c
00209             else:
00210                 callerId = "node"
00211             testKey = ".param1"
00212             testVal = random.randint(-1000000, 100000)            
00213             master.setParam(callerId, testKey, testVal)
00214             trueKey = callerId+testKey
00215             myState[trueKey] = testVal
00216 
00217             v1 = apiSuccess(master.getParam('node', trueKey))
00218             v2 = apiSuccess(master.getParam(callerId, testKey))
00219             assert v1 == v2, "[%s]: %s vs. [%s,%s]: %s"%(trueKey, v1, callerId, testKey, v2)
00220             if HAS_PARAM:
00221                 assert apiSuccess(master.hasParam(callerId, testKey)), testKey
00222                 assert apiSuccess(master.hasParam('node', trueKey)), trueKey
00223 
00224             #test setting a local param on a different node
00225             testKey = "altnode.param2"
00226             testVal = random.randint(-1000000, 100000)            
00227             master.setParam(callerId, testKey, testVal)
00228             if c:
00229                 trueKey = "%s.%s"%(c,testKey)
00230                 altCallerId = "%s.altnode"%c
00231             else:
00232                 trueKey = testKey
00233                 altCallerId = "altnode"
00234             myState[trueKey] = testVal
00235 
00236             v1 = apiSuccess(master.getParam(altCallerId, ".param2"))
00237             v2 = apiSuccess(master.getParam(callerId, testKey))
00238             assert v1 == v2
00239             if HAS_PARAM:
00240                 assert apiSuccess(master.hasParam(callerId, testKey)), testKey
00241                 assert apiSuccess(master.hasParam(altCallerId, ".param2"))
00242 
00243         testParamState(master, myState)
00244 
00245     def testScopeUp(self):
00246         """testScopeUp: test that parameter server can chain up scopes to find/delete parameters"""
00247         master = self.master
00248         myState = {}
00249         testParamState(master, myState)
00250         
00251         testVal = random.randint(-1000000, 100000)
00252         master.setParam('', 'uparam1', testVal)
00253         myState['uparam1'] = testVal 
00254         assert testVal == apiSuccess(master.getParam('node', 'uparam1'))
00255         assert testVal == apiSuccess(master.getParam('uptest.node', 'uparam1'))
00256         assert testVal == apiSuccess(master.getParam('uptest.sub1.node', 'uparam1'))
00257         assert testVal == apiSuccess(master.getParam('uptest.sub1.sub2.node', 'uparam1'))
00258 
00259         testVal = random.randint(-1000000, 100000)
00260         master.setParam('uptest2.sub1.node', 'uparam2', testVal)
00261         myState['uptest2.sub1.uparam2'] = testVal 
00262         assert testVal == apiSuccess(master.getParam('uptest2.sub1.node', 'uparam2'))
00263         assert testVal == apiSuccess(master.getParam('uptest2.sub1.sub2.node', 'uparam2'))
00264         assert testVal == apiSuccess(master.getParam('uptest2.sub1.sub2.sub3.node', 'uparam2'))
00265         testParamState(master, myState)
00266 
00267         #verify upwards deletion
00268         apiSuccess(master.deleteParam('uptest.sub1.sub2.node', 'uparam1'))
00269         del myState['uparam1']
00270         testParamState(master, myState)        
00271         apiSuccess(master.deleteParam('uptest2.sub1.sub2.sub3.node', 'uparam2'))
00272         del myState['uptest2.sub1.uparam2']
00273         testParamState(master, myState)        
00274         
00275     def testScopeDown(self):
00276         """testScopeDown: test scoping rules for sub contexts"""
00277         master = self.master
00278         myState = {}
00279         testParamState(master, myState)
00280 
00281         # test that parameter server down not chain down scopes
00282         testVal = random.randint(-1000000, 100000)
00283         master.setParam('down.one.two.three.node', 'dparam1', testVal)
00284         myState['down.one.two.three.dparam1'] = testVal
00285         if HAS_PARAM:
00286             assert not apiSuccess(master.hasParam('down.one', 'dparam1')) 
00287             assert not apiSuccess(master.hasParam('down.one.two', 'dparam1'))
00288         apiError(master.getParam('down.one.node', 'dparam1')) 
00289         apiError(master.getParam('down.one.two.node', 'dparam1'))
00290 
00291         # test that parameter server allows setting of parameters further down (1)
00292         testVal = random.randint(-1000000, 100000)
00293         master.setParam('node', 'down2.dparam2', testVal)
00294         myState['down2.dparam2'] = testVal         
00295         assert testVal == apiSuccess(master.getParam('down2.node', 'dparam2'))
00296         assert testVal == apiSuccess(master.getParam('', 'down2.dparam2'))
00297         if HAS_PARAM:
00298             assert not apiSuccess(master.hasParam('down2.node', 'down2.dparam2'))
00299         apiError(master.getParam('down2.node', 'down2.dparam2'))
00300         testParamState(master, myState)
00301         
00302         # test that parameter server allows setting of parameters further down (2)
00303         testVal = random.randint(-1000000, 100000)
00304         master.setParam('node', 'down3.sub.dparam3', testVal)
00305         myState['down3.sub.dparam3'] = testVal
00306         assert testVal == apiSuccess(master.getParam('down3.sub.node', 'dparam3'))
00307         assert testVal == apiSuccess(master.getParam('down3.node', 'sub.dparam3'))
00308         assert testVal == apiSuccess(master.getParam('', 'down3.sub.dparam3'))
00309         assert testVal == apiSuccess(master.getParam('down3.sub.sub2.node', 'dparam3'))
00310         if HAS_PARAM:        
00311             assert not apiSuccess(master.hasParam('down3.sub.node', 'sub.dparam3'))
00312             assert not apiSuccess(master.hasParam('down3.sub.node', 'down3.sub.dparam3'))
00313         apiError(master.getParam('down3.sub.node', 'sub.dparam3'))
00314         apiError(master.getParam('down3.sub.node', 'down3.sub.dparam3'))
00315         testParamState(master, myState)
00316 
00317         # test downwards deletion
00318         master.setParam('node', 'down4.sub.dparam4A', testVal)
00319         apiSuccess(master.deleteParam('down4.sub.node', 'dparam4A'))
00320         if HAS_PARAM:        
00321             assert not apiSuccess(master.hasParam('down4.sub', 'dparam4A'))
00322         master.setParam('node', 'down4.sub.dparam4B', testVal)        
00323         apiSuccess(master.deleteParam('down4.node', 'sub.dparam4B'))
00324         if HAS_PARAM:        
00325             assert not apiSuccess(master.hasParam('down4.sub', 'dparam4B'))
00326         master.setParam('node', 'down4.sub.dparam4C', testVal)
00327         apiSuccess(master.deleteParam('', 'down4.sub.dparam4C'))
00328         if HAS_PARAM:        
00329             assert not apiSuccess(master.hasParam('down4.sub.node', 'dparam4C'))
00330         testParamState(master, myState)
00331         
00332 class MasterTestCase(ROSGraphTestCase):
00333     """Master API Test Cases -- those not covered by ParamServer and AddKillNode"""
00334 
00335     def setUp(self):
00336         super(MasterTestCase, self).setUp()
00337     
00338     def tearDown(self):
00339         super(MasterTestCase, self).tearDown() 
00340 
00341     def _verifyFlowNameState(self, master, state):
00342         flows = apiSuccess(master.getFlowNames('node1', ''))
00343         assert len(flows) == len(state.values()), "Master reported a different number of flows"
00344         for val in state.itervalues():
00345             assert val in flows, "flows does not contain %s : %s"%(val, flows)
00346 
00347     def testPromoteFlow(self):
00348         master = self.master
00349         state = {}
00350         callerId = 'node1'
00351         type = 'common_flows/String'
00352         #setup node1 with outflows outflow1..3 and inflow1..3
00353         apiSuccess(master.registerNode(callerId, 'node1', 'localhost', 1234, []))
00354         for i in range(1, 4):
00355             for dir in ['inflow', 'outflow']:
00356                 locator = 'node1:%s%s'%(dir, i)
00357                 apiSuccess(master.registerFlow(callerId, 'node1', locator, dir, type))
00358                 state[locator] = [locator, dir, type, 0]
00359         
00360         #test promote of a non-existent flow
00361         apiError(master.promoteFlow(callerId, 'node1:notAFlow', 'node1:newNotAFlow'))
00362         # - :outflow1 should resolve to parent, which is a non-existent flow
00363         apiError(master.promoteFlow(callerId, ':outflow1', 'node1:newNotAFlow'))        
00364 
00365         ## Play with Outflows
00366         #successfully promote outflow1 to global namespace
00367         dir = 'outflow'
00368         apiSuccess(master.promoteFlow(callerId, 'node1:outflow1', ':outflow1'))
00369         state[':outflow1'] = [':outflow1', dir, type, 1]        
00370         # - verify with .local syntax
00371         apiSuccess(master.promoteFlow(callerId, '.:outflow2', ':outflow2'))        
00372         state[':outflow2'] = [':outflow2', dir, type, 1]
00373 
00374         ## Play with Inflows
00375         #successfully promote inflow1 to the same namespace
00376         dir = 'inflow'
00377         tests = [
00378             ['node1:inflow1',  'node1:newInflow1'],
00379             ['node1:inflow2', 'sibling:inflow2'],
00380             ['node1:inflow3', 'node1:subgraph1:inflow3']
00381             ]
00382         for source, target in tests:
00383             apiSuccess(master.promoteFlow(callerId, source, target))
00384             state[target] = [target, dir, type, 1]
00385         
00386         self._verifyFlowNameState(master, state)
00387 
00388         #TODO: test promotion to an already promoted/registered flow name
00389         apiError(master.promoteFlow(callerId, 'node1:outflow1', 'node1:outflow2'))
00390         apiError(master.promoteFlow(callerId, 'node1:outflow2', ':outflow1'))
00391         apiError(master.promoteFlow(callerId, 'node1:inflow1', 'sibling:inflow2'))        
00392 
00393         #TODO: test promote of a promoted flow
00394         #TODO: test promote with both :flow and .:flow
00395         #TODO: test name resolution with subgraphs and callerIds better
00396     
00397     def testRegisterFlow(self):
00398         master = self.master
00399         state = {}
00400         type = 'common_flows/String'
00401         for callerId in ['node1', 'subgraph.node1', 'grandparent.parent.node1']:
00402             #setup node1 with outflows outflow1..3 and inflow1..3
00403             apiSuccess(master.registerNode(callerId, 'node1', 'localhost', 1234, []))
00404             for i in range(1, 4):
00405                 for dir in ['inflow', 'outflow']:
00406                     if dir == 'inflow':
00407                         locator = 'node1:%s%s'%(dir, i)
00408                     else:
00409                         locator = '.:%s%s'%(dir, i) #test .local resolution
00410                     apiSuccess(master.registerFlow(callerId, 'node1', locator, dir, type))
00411                     realLocator = '%s:%s%s'%(callerId, dir, i)
00412                     state[realLocator] = [realLocator, dir, type, 0]
00413             #test register on a non-existent node
00414             apiError(master.registerFlow(callerId, 'notNode', 'notANode:outflow', 'outflow', type))
00415             #test register on bad locators
00416             apiError(master.registerFlow(callerId, 'node1', '', 'outflow', type))
00417             apiError(master.registerFlow(callerId, 'node1', 'badflow1', 'outflow', type))            
00418             #test register on a bad direction
00419             apiError(master.registerFlow(callerId, 'node1', 'node1:badflow2', 'spiralflow', type))
00420             #test register on invalid types
00421             apiError(master.registerFlow(callerId, 'node1', 'node1:badflow3', 'outflow', ''))
00422             apiError(master.registerFlow(callerId, 'node1', 'node1:badflow4', 'outflow', 'faketype'))
00423 
00424             #test register to an already promoted/registered flow name
00425             apiError(master.registerFlow(callerId, 'node1', 'node1:outflow1', 'outflow', type))
00426             apiSuccess(master.promoteFlow(callerId, 'node1:outflow1', 'node1:newOutflow1'))
00427             state['%s:newOutflow1'%callerId] = ['node1:newOutflow1', dir, type, 1]            
00428             apiError(master.registerFlow(callerId, 'node1', 'node1:newOutflow1', 'outflow', type))
00429 
00430             self._verifyFlowNameState(master, state)
00431 
00432     def testUnregisterFlow(self):
00433         master = self.master
00434         state = {}
00435         type = 'common_flows/String'
00436         for callerId in ['node1', 'subgraph.node1', 'grandparent.parent.node1']:
00437             #setup node1 with outflows outflow1..3 and inflow1..3
00438             apiSuccess(master.registerNode(callerId, 'node1', 'localhost', 1234, []))
00439             for i in range(1, 4):
00440                 for dir in ['inflow', 'outflow']:
00441                     if dir == 'inflow':
00442                         locator = rlocator = 'node1:%s%s'%(dir, i)
00443                     else:
00444                         locator = '.:%s%s'%(dir, i) #test .local resolution
00445                         rlocator = 'node1:%s%s'%(dir, i) #test .local resolution
00446                     apiSuccess(master.registerFlow(callerId, 'node1', locator, dir, type))
00447                     realLocator = '%s:%s%s'%(callerId, dir, i)
00448                     state[realLocator] = [realLocator, dir, type, 0]
00449 
00450             self._verifyFlowNameState(master, state)
00451             
00452             #test bad unregisters
00453             apiError(master.unregisterFlow(callerId, 'notANode:outflow'))
00454             apiError(master.unregisterFlow(callerId, ''))
00455             apiError(master.unregisterFlow(callerId.replace('node1', 'node2'), 'outflow1'))
00456 
00457             apiSuccess(master.unregisterFlow(callerId, 'node1:outflow1'))
00458             del state['%s:outflow1'%callerId]
00459             apiSuccess(master.unregisterFlow(callerId, '.:outflow2'))
00460             del state['%s:outflow2'%callerId]            
00461             apiSuccess(master.unregisterFlow(callerId.replace('node1', 'node2'), 'node1:outflow3'))
00462             del state['%s:outflow3'%callerId]             
00463             apiSuccess(master.unregisterFlow('master', '%s:inflow1'%callerId))
00464             del state['%s:inflow1'%callerId]
00465 
00466             self._verifyFlowNameState(master, state)
00467             
00468             #test register to an already unregistered flow
00469             apiError(master.unregisterFlow(callerId, 'node1:outflow1'))
00470 
00471             #test transitive unregisters
00472             apiSuccess(master.promoteFlow(callerId, 'node1:inflow3', ':inflow3A'))
00473             apiSuccess(master.promoteFlow(callerId, ':inflow3A', ':inflow3B'))
00474             # - should unregister both 3A and 3B
00475             apiSuccess(master.unregisterFlow(callerId, 'node1:inflow3'))
00476             del state['%s:inflow3'%callerId]
00477             
00478             self._verifyFlowNameState(master, state)
00479     
00480     def testGetFlowNames(self):
00481         master = self.master
00482         pkg,node = testNode
00483         subgraph = 'sub1.sub2'
00484         name = 'testGFN_A'
00485         port = apiSuccess(master.addNode('caller', subgraph, name, pkg, node, TEST_MACHINE, 0))        
00486         #testNode must have :in and :out
00487         #:in and :out
00488         testFlowNames = ["%s:%s"%(name, v) for v in ["in", "out"]]
00489         print "FLOW NAMES", apiSuccess(master.getFlowNames('master', ''))        
00490         flows = apiSuccess(master.getFlowNames('%s.caller'%subgraph, ''))
00491         assert not set([x[0] for x in flows]) ^ set(testFlowNames), "%s vs. %s"%([x[0] for x in flows], testFlowNames)
00492         
00493         inDirs = [x[1] for x in flows if x[0].endswith(':in')]
00494         outDirs = [x[1] for x in flows if x[0].endswith(':out')]        
00495         assert not filter(lambda x: x != "inflow", inDirs), inDirs
00496         assert not filter(lambda x: x != "outflow", outDirs), outDirs
00497         assert not filter(lambda x: x != "common_flows/String", [x[2] for x in flows]) #all flow types should be String
00498         assert not filter(lambda x: x, [x[3] for x in flows]) #promoted flag should all be zero
00499 
00500         #test callerId scoping and subgraph parameter
00501         testFlowNames = ["%s.%s:%s"%(subgraph, name, v) for v in ["in", "out"]]        
00502         flows = apiSuccess(master.getFlowNames('caller', subgraph))
00503         assert not set([x[0] for x in flows]) ^ set(testFlowNames), "%s vs. %s"%([x[0] for x in flows], testFlowNames)
00504         flows = apiSuccess(master.getFlowNames('caller', "%s.%s"%(subgraph, name)))
00505         assert not set([x[0] for x in flows]) ^ set(testFlowNames), "%s vs. %s"%([x[0] for x in flows], testFlowNames)
00506 
00507         testFlowNames = ["sub2.%s:%s"%(name, v) for v in ["in", "out"]]        
00508         flows = apiSuccess(master.getFlowNames('sub1.caller', 'sub2'))
00509         assert not set([x[0] for x in flows]) ^ set(testFlowNames), "%s vs. %s"%([x[0] for x in flows], testFlowNames)
00510         
00511 
00512         testFlowNames = ["%s.%s:%s"%(subgraph, name, v) for v in ["in", "out"]]        
00513         flows = apiSuccess(master.getFlowNames('caller', ''))
00514         flowNames = [x[0] for x in flows]
00515         assert not set(flowNames) ^ set(testFlowNames)
00516 
00517     
00518     def testGetNodeAddress(self):
00519         def validate(val, callerId, name, port):
00520             assert len(val) == 3, "getNodeAddress did not return 3-element list for value field"
00521             assert type(val[0]) == str and type(val[1]) == str and type(val[2]) == int,\
00522                    "getNodeAddress did not return (str, str, int) for value field"
00523             verifyNodeAddress(master, callerId, name, TEST_MACHINE, 'localhost', port)
00524         
00525         #NOTE: this does not do full coverage on rospy, as many of the branch
00526         #conditions in rospy are inaccessible via external call (they required
00527         #corrupted data to be inserted into the master, which registerNode prevents)
00528 
00529         master = self.master
00530         #test that invalid case still meets type spec
00531         code, msg, val = master.getNodeAddress('', 'testGetNodeAddress-fake')
00532         assert code == -1, "getNodeAddress did not return failure code 0 for non-existent node"
00533         assert len(val) == 3, "getNodeAddress did not return 3-element list for value field in error case"
00534         assert type(val[0]) == str and type(val[1]) == str and type(val[2]) == int,\
00535                "getNodeAddress did not return (str, str, int) for value field in error case"
00536 
00537         #start a node to test valid case against
00538         name = 'testGetNodeAddress-1'
00539         port = 7981
00540         pkg, node = testNode
00541         apiSuccess(master.addNode('', '', name, pkg, node, TEST_MACHINE, port))
00542         val = apiSuccess(master.getNodeAddress('', name))
00543         validate(val, '', name, port)
00544 
00545         #verify Graph Resource Name scoping rules
00546         name = 'testGetNodeAddress-2'
00547         port = 7982
00548         apiSuccess(master.addNode('', 'gna1.gna2', name, pkg, node, TEST_MACHINE, port))
00549         #make sure we have a node
00550         tests = [
00551             #test exact name matches
00552             ['gna1.gna2.node', name],
00553             ['gna1.node', 'gna2.%s'%name],
00554             ['', 'gna1.gna2.%s'%name],
00555             #make sure that gNA searches upwards 
00556             ['gna1.gna2.gna3.node', name],
00557             ]
00558         for test in tests:
00559             callerId, name = test
00560             validate(apiSuccess(master.getNodeAddress(callerId, name)), callerId, name, port)
00561         
00562         #make sure that it gNA doesn't search upwards when subcontext is specified
00563         val = apiError(master.getNodeAddress('gna1.gna2', 'gna3.%s'%name))
00564 
00565     def _verifyNodeDead(self, port):
00566         testUri = "http://localhost:%s/"%port
00567         try:
00568             xmlrpclib.ServerProxy(testUri).getPid('node')
00569             self.fail("test node is still running")
00570         except:
00571             pass
00572         
00573     def testAddKillNode(self):
00574         """testAddKillNode: test adding then killing nodes"""
00575         #TODO: more test cases
00576         master = self.master
00577         pkg,node = testNode        
00578         apiError(master.killNode('node','nonexistentNode'))
00579         name = 'testAddKillA'
00580         port = apiSuccess(master.addNode('node', 'subgraph', name, pkg, node, TEST_MACHINE, 0))
00581         # - doesn't traverse across        
00582         apiError(master.killNode('different.subgraph.node', name))
00583         # - doesn't traverse down
00584         apiError(master.killNode('node', name))
00585         # - doesn't traverse up        
00586         apiError(master.killNode('subgraph.sub2.node', name))
00587         # - kill it
00588         apiSuccess(master.killNode('subgraph.node', name))
00589 
00590         # - push on the name resolution a bit
00591         tests = [['node', '', 'testAddKillB'],
00592                  ['node', 'g1.g2', 'testAddKillB'],
00593                  ['node', 'g1', 'testAddKillB'],
00594                  ['g1.g2.node', 'g3', 'testAddKillB'],
00595                  ]
00596         for callerId, subcontext, nodeName in tests:
00597             port = apiSuccess(master.addNode(callerId, subcontext, nodeName, pkg, node, TEST_MACHINE, 0))
00598             if subcontext:
00599                 name = "%s.%s"%(subcontext, nodeName)
00600             else:
00601                 name = nodeName
00602             apiSuccess(master.killNode(callerId, name))
00603             self._verifyNodeDead(port)
00604 
00605     def testAddNode(self):
00606         """testAddNode: test master.addNode(callerId, subcontext, name, pkg, pkgNode, machine, port)"""
00607         master = self.master
00608         graphNodes = ['master']
00609         graphFlows = []
00610         # Failure cases
00611         pkg, node = testNode
00612         errors = [
00613             # - subcontext
00614             ['', 12, 'testAddNodeError1', pkg, node, TEST_MACHINE, 0],
00615             #name
00616             ['', '', 123,                pkg, node, TEST_MACHINE, 0],
00617             # - invalid package implementation type
00618             ['', '', 'testAddNodeError2', 123,         node, TEST_MACHINE, 0],
00619             # - node impl name
00620             ['', '', 'testAddNodeError3', pkg, '',       TEST_MACHINE, 0],
00621             ['', '', 'testAddNodeError4', pkg, 123,      TEST_MACHINE, 0],
00622             # - machine parameter
00623             ['', '', 'testAddNodeError6', pkg, node, 'unknown', 0],
00624             ['', '', 'testAddNodeError7', pkg, 'noNode', 123, 0],
00625             # - port
00626             ['', '', 'testAddNodeError8', pkg, node, TEST_MACHINE, -80],
00627             ['', '', 'testAddNodeError9', pkg, node, TEST_MACHINE, "80"],
00628             ]
00629         for args in errors:
00630             try:
00631                 apiError(master.addNode(*args))
00632             except Exception, e:
00633                 self.fail("addNodeError case failed with args[%s] and exception [%s]"%(args, e))
00634         # - non-existent node implementation (this takes awhile)
00635         apiFail(master.addNode('', '', 'testAddNodeFail1', pkg, 'notANode', TEST_MACHINE, 0))
00636 
00637         testGraphState(master, graphNodes, graphFlows)
00638         tests = [[['','testAddNode1'], [TEST_MACHINE, 7980]],
00639                  [['','testAddNode2'], [TEST_MACHINE, 0]],
00640                  [['','testAddNode3'], ['', 0]],
00641                  [['','testAddNode4'], [TEST_MACHINE, 0]],
00642                  [['','testAddNode5'], [TEST_MACHINE, 0]],
00643                  [['','testAddNode6'], [TEST_MACHINE, 0]],
00644                  [['','testAddNode7'], [TEST_MACHINE, 0]],
00645                  # subcontext
00646                  [['push',                'testAddNode8'], [TEST_MACHINE, 0]],
00647                  [['push.one.two',        'testAddNode9'], [TEST_MACHINE, 0]],
00648                  [['stanford.addNodeTest','testAddNodeA'], [TEST_MACHINE, 0]],
00649                  [['wg.addNodeTest',      'testAddNodeA'], [TEST_MACHINE, 0]],
00650                  ]
00651         for fullname, args in tests:
00652             print "testAddNode: testing", fullname
00653             subcontext, name = fullname
00654             if subcontext:
00655                 fullname = '%s.%s'%(subcontext, name)
00656             else:
00657                 fullname = name
00658             machine, port = args
00659             apiSuccess(master.addNode('', subcontext, name, pkg, node, machine, port))
00660             verifyNodeAddress(master, '', fullname, machine, 'localhost', port)
00661             graphNodes.append(fullname)
00662             testGraphState(master, graphNodes, graphFlows)
00663             # duplicate call should succeed
00664             apiSuccess(master.addNode('', subcontext, name, pkg, node, machine, port))
00665 
00666         #TODO: more stress testing of name resolution with non-root callerIds
00667         
00668         # duplicate call with different params should fail
00669         port = apiSuccess(master.addNode('node', 'duplicate.test', 'nodeA', pkg, node, TEST_MACHINE, 0))
00670         apiError(master.addNode('node', 'duplicate.test', 'nodeA', pkg, node, TEST_MACHINE, port + 1))
00671         apiError(master.addNode('node', 'duplicate.test', 'nodeA', pkg+'foo', node, TEST_MACHINE, port))
00672         apiError(master.addNode('node', 'duplicate.test', 'nodeA', pkg, node+'foo', TEST_MACHINE, port))        
00673         #TODO: different machine
00674     
00675     def testGetGraph(self):
00676         #TODO: in ROS 2.0, graph will be scoped by callerId. For now its safe to assume
00677         #that we are implicitly testing getGraph()
00678         pass
00679     
00680     def testAddMachine(self):
00681         master = self.master
00682         host, port = masterAddr
00683         #test invalid calls on addMachine
00684         # - name
00685         apiError(master.addMachine('node', '', self.rosRoot, host, 22, '', ''))
00686         apiError(master.addMachine('node', 123, self.rosRoot, host, 22, '', ''))        
00687         # - ros root
00688         apiError(master.addMachine('node', 'name', '', host, 22, '', ''))
00689         apiError(master.addMachine('node', 'name', 123, host, 22, '', ''))        
00690         # - address
00691         apiError(master.addMachine('node', 'name', '', '', 22, '', ''))
00692         apiError(master.addMachine('node', 'name', '', 123, 22, '', ''))        
00693         # - ssh port 
00694         apiError(master.addMachine('node', 'name', '', host, -22, '', ''))
00695         apiError(master.addMachine('node', 'name', '', host, "22", '', ''))
00696         
00697         tests = [
00698             ['node', 'testAddMachine-1'],
00699             ['g1.node', 'testAddMachine-2'],
00700             ['g1.g2.g3.node', 'testAddMachine-3'],
00701             ['g1.g2.node', 'testAddMachine-4'],            
00702             ]
00703         rosRoot = self.rosRoot
00704         for callerId, m in tests:
00705             apiSuccess(master.addMachine(callerId, m, rosRoot, host, 22, '', ''))
00706             # - duplicate safe
00707             apiSuccess(master.addMachine(callerId, m, rosRoot, host, 22, '', ''))
00708             # - test error for each parameter being changed
00709             apiError(master.addMachine(callerId, m, rosRoot+'/foo/', host, 22, '', ''))
00710             apiError(master.addMachine(callerId, m, rosRoot, 'www.google.com', 22, '', ''))            
00711             apiError(master.addMachine(callerId, m, rosRoot, host, 21, '', ''))
00712             apiError(master.addMachine(callerId, m, rosRoot, host, 22, 'fake-user', ''))
00713             apiError(master.addMachine(callerId, m, rosRoot, host, 22, '', 'fake-password'))
00714             
00715         #TODO: rewrite once master has a method for interrogating machines
00716 
00717     def testRegisterNode_Flows(self):
00718         #TODO: test flows param
00719         pass
00720 
00721     def testRegisterNode(self):
00722         master = self.master
00723         flows = []
00724         # - invalid name
00725         apiError(master.registerNode('', '', 'localhost', 80, flows))
00726         # - invalid address
00727         apiError(master.registerNode('', 'registerNodeFail2', '', 80, flows))
00728         # - invalid ports
00729         apiError(master.registerNode('', 'registerNodeFail3', 'localhost', -80, flows))
00730         apiError(master.registerNode('', 'registerNodeFail4', 'localhost', 0, flows))        
00731         
00732         #implicitly test registerNode via local exec of test node
00733         # - this actually tests the slave as much as the master, but I don't want
00734         #   to call registerNode with correct parameters as it is ambiguous whether
00735         #   or not the master should verify that the slave node actually exists
00736         testCases = [
00737             ['', 'registerNodeExternal-1'],
00738             ['', 'rne.registerNodeExternal-2'],
00739             ['rne', 'registerNodeExternal-3'],
00740             ['', 'rne.rne2.rne3.registerNodeExternal-4'],
00741             ['rne', 'rne2.rne3.registerNodeExternal-5'],
00742             ['rne.rne2.rne3', 'registerNodeExternal-6'],
00743             ]
00744         for context, name in testCases:
00745             try:
00746                 if context:
00747                     fullName = "%s.%s"%(context, name)
00748                     callerId = "%s.node"%context 
00749                 else:
00750                     fullName = name
00751                     callerId = "node"
00752                 startTestNode(fullName)
00753                 # Block until node is responsive before continuing
00754                 # - give test node 4 seconds to start
00755                 timeoutT = time.time() + 4.0 
00756                 node = getTestNode()
00757                 val = None
00758                 while time.time() < timeoutT and not val:
00759                     try:
00760                         _, _, val = node.getPid('')
00761                     except:
00762                         pass
00763                 assert val, "unable to start test node for registerNode test case"
00764                 # - we don't know the machine in this case
00765                 verifyNodeAddress(master, callerId, name, None, testNodeAddr[0], testNodeAddr[1])
00766             finally:
00767                 stopTestNode()
00768             
00769     def testConnectFlow(self):
00770         master = self.master
00771         pkg, node = testNode
00772         graphNodes = ['master']
00773         graphFlows = []
00774         machine = TEST_MACHINE
00775         for i in range(1, 5):
00776             apiSuccess(master.addNode('m', '', 'baseTcfNode-%s'%i, pkg, node, machine, 0))
00777             graphNodes.append('baseTcfNode-%s'%i) #for testing up leveling
00778             apiSuccess(master.addNode('m', '', 'tcfNode-%s'%i, pkg, node, machine, 0)) 
00779             graphNodes.append('tcfNode-%s'%i)
00780             apiSuccess(master.addNode('m', 'tcf1', 'tcfNode-%s'%i, pkg, node, machine, 0))
00781             graphNodes.append('tcf1.tcfNode-%s'%i)
00782             apiSuccess(master.addNode('m', 'tcf2', 'tcfNode-%s'%i, pkg, node, machine, 0))
00783             graphNodes.append('tcf2.tcfNode-%s'%i)
00784             apiSuccess(master.addNode('m', 'tcf1.sub1', 'tcfNode-%s'%i, pkg, node, machine, 0))
00785             graphNodes.append('tcf1.sub1.tcfNode-%s'%i)
00786             apiSuccess(master.addNode('m', 'tcf2.sub1', 'tcfNode-%s'%i, pkg, node, machine, 0))
00787             graphNodes.append('tcf2.sub1.tcfNode-%s'%i)
00788             apiSuccess(master.addNode('m', 'tcf3', 'tcfNode3-%s'%i, pkg, node, machine, 0))
00789             graphNodes.append('tcf3.tcfNode3-%s'%i)
00790             testGraphState(master, graphNodes, graphFlows)
00791             
00792         reliable = 1
00793 
00794         # illegal cases
00795         illegal = [
00796             # - name resolution scope
00797             ['node.tcf1',   'baseTcfNode-1:out', 'tcfNode-1:in'],
00798             ['tcf1.node',   'tcfNode-1:out', 'baseTcfNode-1:in'],
00799             ['tcf1.tcfNode-1', 'tcfNode-1:out', 'tcf1.tcfNode-1:in'],
00800             ['tcf1.tcfNode-1', ':out', 'tcf1.tcfNode-1:in'],
00801             ['tcf1.tcfNode-1', 'tcf1.tcfNode-2:out', ':in'],                    
00802             ['tcf1.node',   'tcf1.tcfNode-1:out', 'tcfNode-1:in'],
00803             ['tcf1.node',   'tcf2.tcfNode-1:out', 'tcfNode-1:in'],
00804             ['tcf1.node',   'tcfNode-1:out', 'tcf2.tcfNode-1:in'],
00805             ['tcf1.node',   'sub1.tcfNode-1:out', 'tcf2.sub1.tcfNode-1:in'],
00806             ['tcf1.sub1.node','baseTcfNode-1:out', 'tcfNode-1:in'],
00807             ['tcf1.sub1.node','sub1.tcfNode-1:out', 'tcfNode-1:in'],
00808             ['tcf1.sub1.node','tcf2.tcfNode-1:out', 'tcfNode-1:in'],            
00809             ]
00810         for callerId, source, sink in illegal:
00811             apiError(master.connectFlow(callerId, source, sink, reliable))
00812 
00813         # single source to sink sink
00814         singleCase = [
00815             #straight forward cases
00816             ['node', 'tcfNode-1:out', 'tcfNode-2:in', 'tcfNode-1:out', 'tcfNode-2:in',],
00817             ['tcfNode-1','tcf1.tcfNode-1:out', 'tcf1.tcfNode-2:in', 'tcf1.tcfNode-1:out', 'tcf1.tcfNode-2:in'],
00818             ['tcf2.tcfNode-1', 'tcfNode-1:out', 'tcfNode-2:in', 'tcf2.tcfNode-1:out', 'tcf2.tcfNode-2:in'],
00819             ['tcf1.tcfNode-2', 'sub1.tcfNode-1:out', 'sub1.tcfNode-2:in', 'tcf1.sub1.tcfNode-1:out', 'tcf1.sub1.tcfNode-2:in'],
00820             ['tcf2.tcfNode-1', 'tcfNode-1:out', 'sub1.tcfNode-1:in', 'tcf2.tcfNode-1:out', 'tcf2.sub1.tcfNode-1:in'],
00821             
00822             # '.locator' naming test
00823             ['tcf2.sub1.tcfNode-1', '.:out', 'tcfNode-2:in', 'tcf2.sub1.tcfNode-1:out', 'tcf2.sub1.tcfNode-2:in'],
00824             ['tcf3.tcfNode3-2', 'tcfNode3-1:out', '.:in', 'tcf3.tcfNode3-1:out', 'tcf3.tcfNode3-2:in'], 
00825             ]
00826         for callerId, source, sink, sourceFull, sinkFull in singleCase:        
00827             apiSuccess(master.connectFlow(callerId, source, sink, reliable)) 
00828             graphFlows.append((sourceFull, sinkFull))
00829             testGraphState(master, graphNodes, graphFlows)            
00830 
00831         # - already connected
00832         # Spec is fuzzy here. It's possible that this should be a succeed.
00833         apiError(master.connectFlow('tcf2.node',   'tcfNode-1:out', 'tcfNode-2:in', reliable))        
00834         apiError(master.connectFlow('node',        'tcf1.tcfNode-1:out', 'tcf1.tcfNode-2:in', reliable))        
00835         
00836         #TODO: test single source to multiple sinks
00837         #TODO: test multiple sources to single sink
00838 
00839 
00840     def testKillFlow(self):
00841         master = self.master
00842         if 0:
00843             master.killFlow(callerId, source, sink)             
00844 
00845     def testMisc(self):
00846         master = self.master
00847         assert master is not None, "master is None"
00848         masterUri = apiSuccess(master.getMasterUri(''))
00849         assert masterUri == apiSuccess(master.getMasterUri('a.different.id')), master.getMasterUri('a.different.id')
00850         assert (getMasterUri() == masterUri) or \
00851                (getMasterUriAlt() == masterUri), masterUri
00852         #getPid
00853         pid = apiSuccess(master.getPid(''))
00854         assert pid == apiSuccess(master.getPid('a.different.id')), master.getPid('a.different.id')
00855         #callerId must be string
00856         apiError(master.getPid(0))
00857         apiError(master.getMasterUri(0))
00858         #shutdown
00859         try:
00860             master.shutdown('some.id')
00861         except:
00862             pass
00863         time.sleep(0.1)
00864         try:
00865             code, status, val = master.getPid('')
00866             assert code < 1, "Master is still running after shutdown"
00867         except:
00868             pass
00869         
00870 def testMasterMain(argv, stdout, env):
00871     return msMain(argv, stdout, env, [ParamServerTestCase, MasterTestCase], singletest) 
00872         
00873 if __name__ == '__main__':
00874     testMasterMain(sys.argv, sys.stdout, os.environ)
00875 
00876         


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