$search
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