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


test_ros
Author(s): Ken Conley/kwc@willowgarage.com
autogenerated on Sat Dec 28 2013 17:35:49