This commit is contained in:
Ivan
2022-04-05 11:42:28 +03:00
commit 6dc0eb0fcf
5565 changed files with 1200500 additions and 0 deletions

View File

@@ -0,0 +1,38 @@
cmake_minimum_required(VERSION 2.8.3)
project(test_rosmaster)
find_package(catkin REQUIRED COMPONENTS genmsg rostest std_msgs)
if(CATKIN_ENABLE_TESTING)
add_message_files(DIRECTORY msg
FILES
String.msg
Arrays.msg
CompositeA.msg CompositeB.msg Composite.msg
Embed.msg Floats.msg Simple.msg
RosmsgA.msg
RosmsgB.msg
RosmsgC.msg
TestArrays.msg
TestHeader.msg
TestPrimitives.msg
TestString.msg
TVals.msg
)
add_service_files(DIRECTORY srv
FILES
AddTwoInts.srv
RossrvA.srv
RossrvB.srv
)
generate_messages(DEPENDENCIES std_msgs)
endif()
catkin_package()
if(CATKIN_ENABLE_TESTING)
add_rostest(test/rosmaster.test)
add_rostest(test/paramserver.test)
endif()

View File

@@ -0,0 +1,7 @@
#for rostopic tests
int8[] int8_arr
uint8[] uint8_arr
int32[] int32_arr
uint32[] uint32_arr
string[] string_arr
time[] time_arr

View File

@@ -0,0 +1,3 @@
# composite message. required for testing import calculation in generators
CompositeA a
CompositeB b

View File

@@ -0,0 +1,6 @@
# This represents an orientation in free space in quaternion form.
float64 x
float64 y
float64 z
float64 w

View File

@@ -0,0 +1,4 @@
# copy of geometry_msgs/Point for testing
float64 x
float64 y
float64 z

View File

@@ -0,0 +1,3 @@
#for rostopic tests
Simple simple
Arrays arrays

View File

@@ -0,0 +1,3 @@
# for rostopic tests
float32 float32
float64 float64

View File

@@ -0,0 +1 @@
int32 a

View File

@@ -0,0 +1 @@
Empty empty

View File

@@ -0,0 +1,2 @@
String s1
String s2

View File

@@ -0,0 +1,10 @@
# for rostopic tests
byte b
int16 int16
int32 int32
int64 int64
char c
uint16 uint16
uint32 uint32
uint64 uint64
string str

View File

@@ -0,0 +1,2 @@
# Copy of std_msgs/String.msg to avoid having tests require a dependency on std_msgs.
string data

View File

@@ -0,0 +1,3 @@
# for rostopic tests
time t
duration d

View File

@@ -0,0 +1,10 @@
# caller_id of most recent node to send this message
string caller_id
# caller_id of the original node to send this message
string orig_caller_id
int32[] int32_array
float32[] float32_array
time[] time_array
TestString[] test_string_array
# TODO: array of arrays

View File

@@ -0,0 +1,8 @@
Header header
# caller_id of most recent node to send this message
string caller_id
# caller_id of the original node to send this message
string orig_caller_id
byte auto_header # autoset header on response

View File

@@ -0,0 +1,21 @@
# Integration test message of all primitive types
# caller_id of most recent node to send this message
string caller_id
# caller_id of the original node to send this message
string orig_caller_id
string str
byte b
int16 int16
int32 int32
int64 int64
char c
uint16 uint16
uint32 uint32
uint64 uint64
float32 float32
float64 float64
time t
duration d

View File

@@ -0,0 +1,6 @@
# Integration test message
# caller_id of most recent node to send this message
string caller_id
# caller_id of the original node to send this message
string orig_caller_id
string data

View File

@@ -0,0 +1,21 @@
<package>
<name>test_rosmaster</name>
<version>1.12.14</version>
<description>
Tests for rosmaster which depend on rostest.
</description>
<maintainer email="dthomas@osrfoundation.org">Dirk Thomas</maintainer>
<license>BSD</license>
<url>http://ros.org/wiki/rosmaster</url>
<author>Ken Conley</author>
<buildtool_depend version_gte="0.5.68">catkin</buildtool_depend>
<build_depend>genmsg</build_depend>
<build_depend>rosgraph</build_depend>
<build_depend>rostest</build_depend>
<build_depend>std_msgs</build_depend>
<test_depend>rosbuild</test_depend>
</package>

View File

@@ -0,0 +1,4 @@
int64 a
int64 b
---
int64 sum

View File

@@ -0,0 +1,3 @@
int32 areq
---
int32 aresp

View File

@@ -0,0 +1,3 @@
Empty empty
---
Empty empty

View File

@@ -0,0 +1,4 @@
pubs:
'/chatter_out': std_msgs/String
'/int64': std_msgs/Int64
subs: {}

View File

@@ -0,0 +1,505 @@
#!/usr/bin/env python
# Software License Agreement (BSD License)
#
# Copyright (c) 2008, Willow Garage, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of Willow Garage, Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# This is a rewrite of the old node API tests, which focus too much on
# a completed node API and don't facilitate easy bring up of a new
# client library.
import os
import sys
import string
import time
import unittest
try:
from xmlrpc.client import Fault, ServerProxy
except ImportError:
from xmlrpclib import Fault, ServerProxy
import rosunit
import rosgraph
TCPROS = 'TCPROS'
CALLER_ID = '/test_harness'
TEST_NODE_NAME = '/test_node' #default
class TopicDescription(object):
def __init__(self, topic_name, topic_type):
self.topic_name = topic_name
self.topic_type = topic_type
#validate topic
if not rosgraph.names.is_legal_name(topic_name):
raise ValueError('topic name: %s'%(topic_name))
# validate type
p, t = topic_type.split('/')
class TopicDescriptionList(object):
def __init__(self, xmlrpcvalue):
# [ [topic1, topicType1]...[topicN, topicTypeN]]]
if not type(xmlrpcvalue) == list:
raise ValueError("publications must be a list")
self.topics = []
for n, t in xmlrpcvalue:
self.topics.append(TopicDescription(n, t))
def as_dict(self):
d = {}
for t in self.topics:
d[t.topic_name] = t.topic_type
return d
class TestSlaveApi(unittest.TestCase):
def __init__(self, *args, **kwds):
super(TestSlaveApi, self).__init__(*args)
self.ns = os.environ.get(rosgraph.ROS_NAMESPACE, rosgraph.names.GLOBALNS)
# load in name of test node
self.test_node = 'test_node' #default
self.required_pubs = TopicDescriptionList([])
self.required_subs = TopicDescriptionList([])
for arg in sys.argv:
if arg.startswith("--node="):
self.test_node = arg[len("--node="):]
if arg.startswith("--profile="):
self.test_node_profile = arg[len("--profile="):]
self.load_profile(self.test_node_profile)
# resolve
self.test_node = rosgraph.names.ns_join(self.ns, self.test_node)
def load_profile(self, filename):
import yaml
with open(filename) as f:
d = yaml.load(f)
self.required_pubs = d.get('pubs', {})
self.required_subs = d.get('subs', {})
def setUp(self):
self.caller_id = CALLER_ID
# retrieve handle on node
# give ourselves 10 seconds for node to appear
timeout_t = 10.0 + time.time()
self.node_api = None
self.master = rosgraph.Master(self.caller_id)
while time.time() < timeout_t and not self.node_api:
try:
self.node_api = self.master.lookupNode(self.test_node)
except:
time.sleep(0.1)
if not self.node_api:
self.fail("master did not return XML-RPC API for [%s, %s]"%(self.caller_id, self.test_node))
print "[%s] API = %s"%(self.test_node, self.node_api)
self.assert_(self.node_api.startswith('http'))
self.node = ServerProxy(self.node_api)
# hack: sleep for a couple seconds just in case the node is
# still registering with the master.
time.sleep(2.)
def apiSuccess(self, args):
"""
unit test assertion that fails if status code is not 1 and otherwise returns the value parameter.
@param args: returnv value from ROS API call
@type args: [int, str, val]
@return: value parameter from args (arg[2] for master/slave API)
"""
self.assert_(len(args) == 3, "invalid API return value triplet: %s"%str(args))
self.last_code, self.last_msg, self.last_val = args
assert self.last_code == 1, "status code is not 1: %s"%self.last_msg
return self.last_val
def apiFail(self, args):
"""
unit test assertions that fails if status code is not 0 and otherwise returns true.
@param args: returnv value from ROS API call
@type args: [int, str, val]
@return: True if status code is 0
"""
self.assert_(len(args) == 3, "invalid API return value triplet: %s"%str(args))
self.last_code, self.last_msg, self.last_val = args
assert self.last_code == 0, "Call should have failed with status code 0: %s"%self.last_msg
def apiError(self, args, msg=None):
"""
unit test assertion that fails if status code is not -1 and otherwise returns true.
@param args: returnv value from ROS API call
@type args: [int, str, val]
@return: True if status code is -1
"""
self.assert_(len(args) == 3, "invalid API return value triplet: %s"%str(args))
self.last_code, self.last_msg, self.last_val = args
if msg:
assert self.last_code == -1, "%s (return msg was %s)"%(msg, self.last_msg)
else:
assert self.last_code == -1, "Call should have returned error -1 code: %s"%self.last_msg
def check_uri(self, uri):
"""
validates a URI as being http(s)
"""
import urlparse
parsed = urlparse.urlparse(uri)
self.assert_(parsed[0] in ['http', 'https'], 'protocol [%s] is [%s] invalid'%(parsed[0], uri))
self.assert_(parsed[1], 'host missing [%s]'%uri)
self.assert_(parsed.port, 'port missing/invalid [%s]'%uri)
def test_getPid(self):
"""
validate node.getPid(caller_id)
"""
# test success
pid = self.apiSuccess(self.node.getPid(self.caller_id))
self.assert_(pid > 0)
# test with bad arity: accept error or fault
try:
self.apiError(self.node.getPid())
except Fault:
pass
def test_rosout(self):
"""
make sure rosout is in publication and connection list
"""
val = self.apiSuccess(self.node.getPublications(self.caller_id))
pubs_d = TopicDescriptionList(val).as_dict()
self.assertTrue('/rosout' in pubs_d, "node is not publishing to rosout")
self.assertEquals('rosgraph_msgs/Log', pubs_d['/rosout'], "/rosout is not correct type")
def test_simtime(self):
"""
test that node obeys simtime (/Clock) contract
http://www.ros.org/wiki/Clock
"""
try:
use_sim_time = self.master.getParam('/use_sim_time')
except:
use_sim_time = False
val = self.apiSuccess(self.node.getSubscriptions(self.caller_id))
subs_d = TopicDescriptionList(val).as_dict()
if use_sim_time:
self.assertTrue('/clock' in subs_d, "node is not subscribing to clock")
self.assertEquals('rosgraph_msgs/Clock', subs_d['/clock'], "/clock is not correct type")
else:
self.assertFalse('/clock' in subs_d, "node is subscribed to /clock even though /use_sim_time is false")
def test_getPublications(self):
"""
validate node.getPublications(caller_id)
"""
# test success
pubs_value = self.apiSuccess(self.node.getPublications(self.caller_id))
pubs = TopicDescriptionList(pubs_value)
pubs_dict = pubs.as_dict()
# this is separately tested by test_rosout
if '/rosout' in pubs_dict:
del pubs_dict['/rosout']
self.assertEquals(self.required_pubs, pubs_dict)
# test with bad arity: accept error or fault
try:
self.apiError(self.node.getPublications())
except Fault:
pass
try:
self.apiError(self.node.getPublications(self.caller_id, 'something extra'))
except Fault:
pass
def test_getSubscriptions(self):
"""
validate node.getSubscriptions(caller_id)
"""
# test success
value = self.apiSuccess(self.node.getSubscriptions(self.caller_id))
subs = TopicDescriptionList(value)
subs_dict = subs.as_dict()
self.assertEquals(self.required_subs, subs_dict)
# test with bad arity: accept error or fault
try:
self.apiError(self.node.getSubscriptions())
except Fault:
pass
try:
self.apiError(self.node.getSubscriptions(self.caller_id, 'something extra'))
except Fault:
pass
## validate node.paramUpdate(caller_id, key, value)
def test_paramUpdate(self):
node = self.node
good_key = rosgraph.names.ns_join(self.ns, 'good_key')
bad_key = rosgraph.names.ns_join(self.ns, 'bad_key')
# node is not subscribed to good_key (yet)
self.apiError(node.paramUpdate(self.caller_id, good_key, 'good_value'))
# test bad key
self.apiError(node.paramUpdate(self.caller_id, '', 'bad'))
self.apiError(node.paramUpdate(self.caller_id, 'no_namespace', 'bad'))
# test with bad arity: accept error or fault
try:
self.apiError(node.paramUpdate(self.caller_id, bad_key))
except Fault:
pass
try:
self.apiError(node.paramUpdate(self.caller_id))
except Fault:
pass
# we can't actually test success cases without forcing node to subscribe
#self.apiSuccess(node.paramUpdate(self.caller_id, good_key, 1))
#self.apiSuccess(node.paramUpdate(self.caller_id, good_key, True))
#self.apiSuccess(node.paramUpdate(self.caller_id, good_key, 10.0))
def xtest_getUri(self):
"""
Future: validate node.getUri(caller_id). It would be nice to
make this official API as it provides some debugging info.
"""
# test success
self.check_uri(self.apiSuccess(self.node.getUri(self.caller_id)))
# test bad arity
try:
self.apiError(self.node.getUri(self.caller_id, 'bad'))
except Fault:
pass
try:
self.apiError(self.node.getUri())
except Fault:
pass
def test_getMasterUri(self):
"""
validate node.getMasterUri(caller_id)
"""
# test success
uri = self.apiSuccess(self.node.getMasterUri(self.caller_id))
self.check_uri(uri)
# check against env, canonicalize for comparison
import urlparse
master_env = rosgraph.get_master_uri()
if not master_env.endswith('/'):
master_env = master_env + '/'
self.assertEquals(urlparse.urlparse(master_env), urlparse.urlparse(uri))
# test bad arity
try:
self.apiError(self.node.getMasterUri(self.caller_id, 'bad'))
except Fault:
pass
try:
self.apiError(self.node.getMasterUri())
except Fault:
pass
def test_publisherUpdate(self):
"""
validate node.publisherUpdate(caller_id, topic, uris)
"""
node = self.node
probe_topic = rosgraph.names.ns_join(self.ns, 'probe_topic')
fake_topic = rosgraph.names.ns_join(self.ns, 'fake_topic')
# test success
# still success even if not actually interested in topic
self.apiSuccess(node.publisherUpdate(self.caller_id, fake_topic,
['http://localhost:1234', 'http://localhost:5678']))
self.apiSuccess(node.publisherUpdate(self.caller_id, fake_topic,
[]))
# try it with it the /probe_topic, which will exercise some error branches in the client
self.apiSuccess(node.publisherUpdate(self.caller_id, probe_topic,
['http://unroutablefakeservice:1234']))
# give it some time to make sure it's attempted contact
time.sleep(1.0)
# check that it's still there
self.apiSuccess(node.publisherUpdate(self.caller_id, probe_topic,
[]))
# test bad args
try:
self.apiError(node.publisherUpdate(self.caller_id, '/bad_topic', 'bad'))
except Fault:
pass
try:
self.apiError(node.publisherUpdate(self.caller_id, '/bad_topic', 2))
except Fault:
pass
try:
self.apiError(node.publisherUpdate(self.caller_id, '/bad_topic', False))
except Fault:
pass
try:
self.apiError(node.publisherUpdate(self.caller_id, '/bad_topic', ['bad']))
except Fault:
pass
# test bad arity
try:
self.apiError(node.publisherUpdate())
except Fault:
pass
try:
self.apiError(node.getBusStats(self.caller_id, 'bad'))
except Fault:
pass
try:
self.apiError(node.getBusStats())
except Fault:
pass
def check_TCPROS(self, protocol_params):
self.assert_(protocol_params, "no protocol params returned")
self.assert_(type(protocol_params) == list, "protocol params must be a list: %s"%protocol_params)
self.assertEquals(3, len(protocol_params), "TCPROS params should have length 3: %s"%protocol_params)
self.assertEquals(protocol_params[0], TCPROS)
# expect ['TCPROS', 1.2.3.4, 1234]
self.assertEquals(protocol_params[0], TCPROS)
def testRequestTopic(self):
node = self.node
protocols = [[TCPROS]]
publications = node.getPublications(self.caller_id)
topics = self.required_pubs.keys()
probe_topic = topics[0] if topics else None
fake_topic = rosgraph.names.ns_join(self.ns, 'fake_topic')
# currently only support TCPROS as we require all clients to support this
protocols = [[TCPROS]]
for topic in topics:
self.check_TCPROS(self.apiSuccess(node.requestTopic(self.caller_id, topic, protocols)))
protocols = [['FakeTransport', 1234, 5678], [TCPROS], ['AnotherFakeTransport']]
# try each one more time, this time with more protocol choices
for topic in topics:
self.check_TCPROS(self.apiSuccess(node.requestTopic(self.caller_id, topic, protocols)))
# test bad arity
if probe_topic:
try:
self.apiError(node.requestTopic(self.caller_id, probe_topic, protocols, 'extra stuff'))
except Fault:
pass
try:
self.apiError(node.requestTopic(self.caller_id, probe_topic))
except Fault:
pass
try:
self.apiError(node.requestTopic(self.caller_id))
except Fault:
pass
try:
self.apiError(node.requestTopic())
except Fault:
pass
# test bad args
try:
self.apiError(node.requestTopic(self.caller_id, 1, protocols))
except Fault:
pass
try:
self.apiError(node.requestTopic(self.caller_id, '', protocols))
except Fault:
pass
try:
self.apiError(node.requestTopic(self.caller_id, fake_topic, protocols))
except Fault:
pass
try:
self.apiError(node.requestTopic(self.caller_id, probe_topic, 'fake-protocols'))
except Fault:
pass
def test_getBusInfo(self):
#TODO: finish
# there should be a connection to rosout
# test bad arity
try:
self.apiError(self.node.getBusInfo(self.caller_id, 'bad'))
except Fault:
pass
try:
self.apiError(self.node.getBusInfo())
except Fault:
pass
## test the state of the master based on expected node registration
def test_registrations(self):
# setUp() ensures the node has registered with the master
# check actual URIs
node_name = self.test_node
pubs, subs, srvs = self.master.getSystemState()
pub_topics = [t for t, _ in pubs]
sub_topics = [t for t, _ in subs]
# make sure all required topics are registered
for t in self.required_pubs:
self.assert_(t in pub_topics, "node did not register publication %s on master"%(t))
for t in self.required_subs:
self.assert_(t in sub_topics, "node did not register subscription %s on master"%(t))
# check for node URI on master
for topic, node_list in pubs:
if topic in self.required_pubs:
self.assert_(node_name in node_list, "%s not in %s"%(self.node_api, node_list))
for topic, node_list in subs:
if topic in self.required_subs:
self.assert_(node_name in node_list, "%s not in %s"%(self.node_api, node_list))
for service, srv_list in srvs:
#TODO: no service tests yet
pass
if __name__ == '__main__':
rosunit.unitrun('test_rosmaster', sys.argv[0], TestSlaveApi)

View File

@@ -0,0 +1,468 @@
# Software License Agreement (BSD License)
#
# Copyright (c) 2008, Willow Garage, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of Willow Garage, Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# Revision $Id: testSlave.py 1100 2008-05-29 20:23:54Z sfkwc $
import os
import sys
import string
import time
try:
from xmlrpc.client import ServerProxy
except ImportError:
from xmlrpclib import ServerProxy
import rospy
import rosgraph
from rosclient import *
NODE_INTEGRATION_NAME = "node_integration_test"
_name = None
## set_node_name() must be called prior to the unit test so that the test harness knows its
## ROS name.
def set_node_name(name):
global _name
_name = name
# Have to try as hard as possible to not use rospy code in testing rospy code, so this is
# a reimplementation of the caller ID spec so that NodeApiTestCase knows its name
## reimplementation of caller ID spec separately from rospy
def get_caller_id():
if _name is None:
raise Exception("set_node_name has not been called yet")
ros_ns = os.environ.get(rosgraph.ROS_NAMESPACE, rosgraph.names.GLOBALNS)
return rosgraph.names.ns_join(ros_ns, _name)
class _MasterTestCase(TestRosClient):
def __init__(self, *args):
super(_MasterTestCase, self).__init__(*args)
self.ns = os.environ.get(rosgraph.ROS_NAMESPACE, rosgraph.names.GLOBALNS)
self.caller_id = get_caller_id()
def setUp(self):
super(_MasterTestCase, self).setUp()
self.master_uri = os.environ.get(rosgraph.ROS_MASTER_URI, None)
self._checkUri(self.master_uri)
self.master = ServerProxy(self.master_uri)
## validates a URI as being http(s)
def _checkUri(self, uri):
import urlparse
parsed = urlparse.urlparse(uri)
self.assert_(parsed[0] in ['http', 'https'], 'protocol [%s] in [%s] invalid'%(parsed[0], uri))
self.assert_(parsed[1], 'host missing [%s]'%uri)
if not sys.version.startswith('2.4'): #check not available on py24
self.assert_(parsed.port, 'port missing/invalid [%s]'%uri)
## Expects a single test node to be running with name 'test_node' and subscribed to 'test_string'
class MasterApiTestCase(_MasterTestCase):
## validate master.getMasterUri(caller_id)
def _testGetMasterUri(self):
# test with bad arity
self.apiError(self.master.getMasterUri())
# test success
uri = self.apiSuccess(self.master.getMasterUri(self.caller_id))
self._checkUri(uri)
# make sure we agree on ports
import urlparse
parsed = urlparse.urlparse(uri)
parsed2 = urlparse.urlparse(self.master_uri)
self.assertEquals(parsed.port, parsed2.port, "expected ports do not match")
## validate master.getPid(caller_id)
def _testGetPid(self):
# test with bad arity
self.apiError(self.master.getPid())
# test success
pid = self.apiSuccess(self.master.getPid(self.caller_id))
self.assert_(pid > 0)
## validate master.getUri(caller_id)
def _testGetUri(self):
# test with bad arity
self.apiError(self.master.getUri())
# test success
uri = self.apiSuccess(self.master.getUri(self.caller_id))
self.assert_(type(uri) == str)
## common test subroutine of both register and unregister tests. registers the common test cases
def _subTestRegisterServiceSuccess(self):
master = self.master
caller_id = '/service_node'
caller_api = 'http://localhost:4567/'
service_base = '/service'
# test success
for i in range(0, 10):
service_name = "%s-%s"%(service_base, i)
service_api = 'rosrpc://localhost:123%s/'%i
# register the service
self.apiSuccess(master.registerService(caller_id, service_name, service_api, caller_api))
# test master state
val = self.apiSuccess(master.lookupService(caller_id, service_name))
self.assertEquals(service_api, val)
val = self.apiSuccess(master.lookupNode(self.caller_id, caller_id))
self.assertEquals(caller_api, val)
_, _, srvs = self.apiSuccess(master.getSystemState(self.caller_id))
for j in range(0, i+1):
jservice_name = "%s-%s"%(service_base, j)
jentry = [jservice_name, [caller_id]]
self.assert_(jentry in srvs, "master service list %s is missing %s"%(srvs, jentry))
# TODO: have to test subscriber callback
# TODO: validate with getSystemState()
## validate master.registerService(caller_id, service, service_api, caller_api)
def _testRegisterServiceSuccess(self):
self._subTestRegisterServiceSuccess()
def _testUnregisterServiceSuccess(self):
self._subTestRegisterServiceSuccess()
master = self.master
caller_id = '/service_node'
caller_api = 'http://localhost:4567/'
service_base = '/service'
for i in range(0, 10):
service_name = "%s-%s"%(service_base, i)
service_api = 'rosrpc://localhost:123%s/'%i
# unregister the service
code, msg, val = master.unregisterService(caller_id, service_name, service_api)
self.assertEquals(code, 1, "code != 1, return message was [%s]"%msg)
# test the master state
self.apiError(master.lookupService(self.caller_id, service_name), "master has a reference to unregistered service. message from master for unregister was [%s]"%msg)
if i < 9:
val = self.apiSuccess(master.lookupNode(self.caller_id, caller_id))
self.assertEquals(caller_api, val, "master prematurely invalidated node entry for [%s] (lookupNode)"%caller_id)
_, _, srvs = self.apiSuccess(master.getSystemState(self.caller_id))
for j in range(0, i+1):
jservice_name = "%s-%s"%(service_base, j)
jentry = [jservice_name, [caller_id]]
self.assert_(jentry not in srvs, "master service list %s should not have %s"%(srvs, jentry))
for j in range(i+1, 10):
jservice_name = "%s-%s"%(service_base, j)
jentry = [jservice_name, [caller_id]]
self.assert_(jentry in srvs, "master service list %s is missing %s"%(srvs, jentry))
# TODO: have to test subscriber callback
# Master's state should be zero'd out now
# - #457 make sure that lookupNode isn't returning stale info
self.apiError(master.lookupNode(self.caller_id, caller_id), "master has a stale reference to unregistered service node API")
_, _, srvs = self.apiSuccess(master.getSystemState(self.caller_id))
srvs = [s for s in srvs if not s[0].startswith('/rosout/') and not s[0].endswith('/get_loggers') and not s[0].endswith('/set_logger_level')]
self.assertEquals(0, len(srvs), "all services should have been unregistered: %s"%srvs)
def _testRegisterServiceInvalid(self):
master = self.master
service = '/service'
service_api = 'rosrpc://localhost:1234/'
caller_api = 'http://localhost:4567/'
# test with bad arity
self.apiError(master.registerService())
self.apiError(master.registerService(self.caller_id, service))
self.apiError(master.registerService(self.caller_id, service, service_api))
# test with bad args
self.apiError(master.registerService(self.caller_id, '', service_api, caller_api))
self.apiError(master.registerService(self.caller_id, service, '', caller_api))
self.apiError(master.registerService(self.caller_id, service, service_api, ''))
def _testUnregisterServiceInvalid(self):
master = self.master
service = '/service'
service_api = 'rosrpc://localhost:1234/'
# test with bad arity
self.apiError(master.unregisterService())
self.apiError(master.unregisterService(self.caller_id, service))
# test with bad args
self.apiError(master.unregisterService(self.caller_id, '', service_api))
self.apiError(master.unregisterService(self.caller_id, service, ''))
def _testRegisterPublisherInvalid(self):
master = self.master
topic = '/pub_topic'
topic_type = 'test_rosmaster/String'
caller_api = 'http://localhost:4567/'
# test with bad arity
self.apiError(master.registerPublisher())
self.apiError(master.registerPublisher(self.caller_id, topic))
self.apiError(master.registerPublisher(self.caller_id, topic, topic_type))
# test with bad args
self.apiError(master.registerPublisher(self.caller_id, '', topic_type, caller_api))
self.apiError(master.registerPublisher(self.caller_id, topic, '', caller_api))
self.apiError(master.registerPublisher(self.caller_id, topic, topic_type, ''))
def _testUnregisterPublisherInvalid(self):
master = self.master
topic = '/pub_topic'
caller_api = 'http://localhost:4567/'
# test with bad arity
self.apiError(master.unregisterPublisher())
self.apiError(master.unregisterPublisher(self.caller_id, topic))
# test with bad args
self.apiError(master.unregisterPublisher(self.caller_id, '', caller_api))
self.apiError(master.unregisterPublisher(self.caller_id, topic, ''))
def _testRegisterSubscriberInvalid(self):
master = self.master
topic = '/sub_topic'
topic_type = 'test_rosmaster/String'
caller_api = 'http://localhost:4567/'
# test with bad arity
self.apiError(master.registerSubscriber())
self.apiError(master.registerSubscriber(self.caller_id, topic))
self.apiError(master.registerSubscriber(self.caller_id, topic, topic_type))
# test with bad args
self.apiError(master.registerSubscriber(self.caller_id, '', topic_type, caller_api))
self.apiError(master.registerSubscriber(self.caller_id, topic, '', caller_api))
self.apiError(master.registerSubscriber(self.caller_id, topic, topic_type, ''))
def _testUnregisterSubscriberInvalid(self):
master = self.master
topic = '/sub_topic'
caller_api = 'http://localhost:4567/'
# test with bad arity
self.apiError(master.registerSubscriber())
self.apiError(master.registerSubscriber(self.caller_id, topic))
# test with bad args
self.apiError(master.unregisterSubscriber(self.caller_id, '', caller_api))
self.apiError(master.unregisterSubscriber(self.caller_id, topic, ''))
## common test subroutine of both register and unregister tests. registers the common test cases
def _subTestRegisterPublisherSuccess(self):
master = self.master
caller_id = '/pub_node'
caller_api = 'http://localhost:4567/'
topic_base = '/pub_topic'
topic_type = 'test_rosmaster/String'
# test success
for i in range(0, 10):
topic_name = "%s-%s"%(topic_base, i)
# register the topic
self.apiSuccess(master.registerPublisher(caller_id, topic_name, topic_type, caller_api))
# test master state
# - master knows caller_id
val = self.apiSuccess(master.lookupNode(self.caller_id, caller_id))
self.assertEquals(caller_api, val)
# - master knows topic type
val = self.apiSuccess(master.getPublishedTopics(self.caller_id, '/'))
self.assert_([topic_name, topic_type] in val, "master does not know topic type: %s"%val)
# - test new api as well
val = self.apiSuccess(master.getTopicTypes(self.caller_id))
self.assert_([topic_name, topic_type] in val, "master does not know topic type: %s"%val)
pubs, _, _ = self.apiSuccess(master.getSystemState(self.caller_id))
for j in range(0, i+1):
jtopic_name = "%s-%s"%(topic_base, j)
jentry = [jtopic_name, [caller_id]]
self.assert_(jentry in pubs, "master pub/sub list %s is missing %s"%(pubs, jentry))
# TODO: have to test subscriber callback
## #591: this test may change if we make registering '*' unsupported
def _testRegisterPublisherTypes(self):
master = self.master
caller_id = '/pub_node'
caller_api = 'http://localhost:4567/'
topic_name = '/type_test_pub_topic'
# register anytype first
val = self.apiSuccess(master.registerPublisher(caller_id, topic_name, '*', caller_api))
self.assertEquals([], val) # should report no subscribers
val = self.apiSuccess(master.getPublishedTopics(self.caller_id, '/'))
self.assert_([topic_name, '*'] in val, "master is not reporting * as type: %s"%val)
# - test new api as well
val = self.apiSuccess(master.getTopicTypes(self.caller_id))
self.assert_([topic_name, '*'] in val, "master is not reporting * as type: %s"%val)
# register a grounded type and make sure that '*' can't overwrite it
for t in ['test_rosmaster/String', '*']:
val = self.apiSuccess(master.registerPublisher(caller_id, topic_name, t, caller_api))
self.assertEquals([], val) # should report no subscribers
val = self.apiSuccess(master.getPublishedTopics(self.caller_id, '/'))
self.assert_([topic_name, 'test_rosmaster/String'] in val, "master is not reporting test_rosmaster/String as type: %s"%val)
val = self.apiSuccess(master.getTopicTypes(self.caller_id))
self.assert_([topic_name, 'test_rosmaster/String'] in val, "master is not reporting test_rosmaster/String as type: %s"%val)
## validate master.registerPublisher(caller_id, topic, topic_api, caller_api)
def _testRegisterPublisherSuccess(self):
self._subTestRegisterPublisherSuccess()
# a couple more test cases to verify that registerPublisher's return value is correct
master = self.master
topic = '/pub_topic-0'
type = 'test_rosmaster/String'
pub_caller_api = 'http://localhost:4567/'
subs = []
for i in range(5678, 5685):
api = 'http://localhost:%s'%i
subs.append(api)
self.apiSuccess(master.registerSubscriber('/sub_node-%i'%i, topic, type, api))
val = self.apiSuccess(master.registerPublisher('/pub_node', topic, type, pub_caller_api))
self.assertEquals(subs, val)
def _testUnregisterPublisherSuccess(self):
self._subTestRegisterPublisherSuccess()
master = self.master
caller_id = '/pub_node'
caller_api = 'http://localhost:4567/'
topic_base = '/pub_topic'
for i in range(0, 10):
topic_name = "%s-%s"%(topic_base, i)
# unregister the topic
code, msg, val = master.unregisterPublisher(caller_id, topic_name, caller_api)
self.assertEquals(code, 1, "code != 1, return message was [%s]"%msg)
# test the master state
if i < 9:
val = self.apiSuccess(master.lookupNode(self.caller_id, caller_id))
self.assertEquals(caller_api, val, "master prematurely invalidated node entry for [%s] (lookupNode)"%caller_id)
pubs, _, _ = self.apiSuccess(master.getSystemState(self.caller_id))
for j in range(0, i+1):
jtopic_name = "%s-%s"%(topic_base, j)
jentry = [jtopic_name, [caller_id]]
self.assert_(jentry not in pubs, "master pub/sub list %s should not have %s"%(pubs, jentry))
for j in range(i+1, 10):
jtopic_name = "%s-%s"%(topic_base, j)
jentry = [jtopic_name, [caller_id]]
self.assert_(jentry in pubs, "master pub/sub list %s is missing %s"%(pubs, jentry))
# TODO: have to test subscriber callback
# - #457 make sure that lookupNode isn't returning stale info
self.apiError(master.lookupNode(self.caller_id, caller_id), "master has a stale reference to unregistered topic node API. pubs are %s"%pubs)
## common test subroutine of both register and unregister tests. registers the common test cases
## 'simple' test cases do not setup any publisher state to validate against
def _subTestRegisterSubscriberSimpleSuccess(self):
master = self.master
caller_id = '/sub_node'
caller_api = 'http://localhost:4567/'
topic_base = '/sub_topic'
topic_type = 'test_rosmaster/String'
# test success
for i in range(0, 10):
topic_name = "%s-%s"%(topic_base, i)
# register the topic
self.apiSuccess(master.registerSubscriber(caller_id, topic_name, topic_type, caller_api))
# test master state
# - master knows caller_id
val = self.apiSuccess(master.lookupNode(self.caller_id, caller_id))
self.assertEquals(caller_api, val)
# - master should know topic type
val = self.apiSuccess(master.getTopicTypes(self.caller_id))
self.assert_([topic_name, topic_type] in val, "master does not know topic type: %s"%val)
_, subs, _ = self.apiSuccess(master.getSystemState(self.caller_id))
for j in range(0, i+1):
jtopic_name = "%s-%s"%(topic_base, j)
jentry = [jtopic_name, [caller_id]]
self.assert_(jentry in subs, "master pub/sub list %s is missing %s"%(subs, jentry))
## validate master.registerSubscriber(caller_id, topic, topic_api, caller_api)
def _testRegisterSubscriberSimpleSuccess(self):
self._subTestRegisterSubscriberSimpleSuccess()
def _testUnregisterSubscriberSuccess(self):
self._subTestRegisterSubscriberSimpleSuccess()
master = self.master
caller_id = '/sub_node'
caller_api = 'http://localhost:4567/'
topic_base = '/sub_topic'
for i in range(0, 10):
topic_name = "%s-%s"%(topic_base, i)
# unregister the topic
code, msg, val = master.unregisterSubscriber(caller_id, topic_name, caller_api)
self.assertEquals(code, 1, "code != 1, return message was [%s]"%msg)
# test the master state
if i < 9:
val = self.apiSuccess(master.lookupNode(self.caller_id, caller_id))
self.assertEquals(caller_api, val, "master prematurely invalidated node entry for [%s] (lookupNode)"%caller_id)
_, subs, _ = self.apiSuccess(master.getSystemState(self.caller_id))
for j in range(0, i+1):
jtopic_name = "%s-%s"%(topic_base, j)
jentry = [jtopic_name, [caller_id]]
self.assert_(jentry not in subs, "master pub/sub list %s should not have %s"%(subs, jentry))
for j in range(i+1, 10):
jtopic_name = "%s-%s"%(topic_base, j)
jentry = [jtopic_name, [caller_id]]
self.assert_(jentry in subs, "master pub/sub list %s is missing %s"%(subs, jentry))
# - #457 make sure that lookupNode isn't returning stale info
self.apiError(master.lookupNode(self.caller_id, caller_id), "master has a stale reference to unregistered topic node API. subs are %s"%subs)

View File

@@ -0,0 +1,365 @@
# Software License Agreement (BSD License)
#
# Copyright (c) 2008, Willow Garage, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of Willow Garage, Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# Revision $Id: testSlave.py 1100 2008-05-29 20:23:54Z sfkwc $
import os
import sys
import string
import time
try:
from xmlrpc.client import ServerProxy
except ImportError:
from xmlrpclib import ServerProxy
import rospy
import rosgraph
from rosclient import *
NODE_INTEGRATION_NAME = "node_integration_test"
_required_subscriptions = 'test_string_in', 'test_primitives_in', 'test_arrays_in', 'test_header_in', 'probe_topic'
# only publishers determine topic type, so we test against their declared spec
_required_publications_map = {
'test_string_out': 'test_rosmaster/TestString',
'test_primitives_out': 'test_rosmaster/TestPrimitives',
'test_arrays_out': 'test_rosmaster/TestArrays',
'test_header_out': 'test_rosmaster/TestHeader',
}
_required_publications = _required_publications_map.keys()
_TCPROS = 'TCPROS'
# NOTE: probe_topic is a unpublished topic that merely exists to test
# APIs that talk about subscriptions (e.g. publisherUpdate)
_name = None
## set_node_name() must be called prior to the unit test so that the test harness knows its
## ROS name.
def set_node_name(name):
global _name
_name = name
# Have to try as hard as possible to not use rospy code in testing rospy code, so this is
# a reimplementation of the caller ID spec so that NodeApiTestCase knows its name
## reimplementation of caller ID spec separately from rospy
def get_caller_id():
if _name is None:
raise Exception("set_node_name has not been called yet")
ros_ns = os.environ.get(rosgraph.ROS_NAMESPACE, rosgraph.names.GLOBALNS)
return rosgraph.names.ns_join(ros_ns, _name)
## Parent of node API and integration test cases. Performs common state setup
class _NodeTestCase(TestRosClient):
def __init__(self, *args):
super(_NodeTestCase, self).__init__(*args)
self.ns = os.environ.get(rosgraph.ROS_NAMESPACE, rosgraph.names.GLOBALNS)
self.caller_id = get_caller_id()
# load in name of test node
self.test_node = 'test_node' #default
for arg in sys.argv:
if arg.startswith("--node="):
self.test_node = arg[len("--node="):]
# resolve
self.test_node = rosgraph.names.ns_join(self.ns, self.test_node)
def setUp(self):
super(_NodeTestCase, self).setUp()
# retrieve handle on node
# give ourselves five seconds for node to appear
import time
timeout_t = 5.0 + time.time()
self.node_api = None
while time.time() < timeout_t and not self.node_api:
code, msg, node_api = self.master.lookupNode(self.caller_id, self.test_node)
if code == 1:
self.node_api = node_api
if not self.node_api:
self.fail("master did not return XML-RPC API for [%s, %s]"%(self.caller_id, self.test_node))
print("[%s] API = %s" %(self.test_node, self.node_api))
self.assert_(self.node_api.startswith('http'))
self.node = ServerProxy(self.node_api)
## validates a URI as being http(s)
def _checkUri(self, uri):
try:
from urllib.parse import urlparse
except ImportError:
from urlparse import urlparse
parsed = urlparse(uri)
self.assert_(parsed[0] in ['http', 'https'], 'protocol [%s] in [%s] invalid'%(parsed[0], uri))
self.assert_(parsed[1], 'host missing [%s]'%uri)
if not sys.version.startswith('2.4'): #check not available on py24
self.assert_(parsed.port, 'port missing/invalid [%s]'%uri)
## dynamically create the expected topic->type map based on the current name resolution context
def _createTopicTypeMap(self):
new_map = {}
for t in _required_publications_map.keys():
new_map[rospy.resolve_name(t)] = _required_publications_map[t]
return new_map
## Expects a single test node to be running with name 'test_node' and subscribed to 'test_string'
class NodeApiTestCase(_NodeTestCase):
## validate node.getPid(caller_id)
def testGetPid(self):
# test with bad arity
self.apiError(self.node.getPid())
# test success
pid = self.apiSuccess(self.node.getPid(self.caller_id))
self.assert_(pid > 0)
## subroutine for testGetSubscriptions/testGetPublications
def _checkTopics(self, required, actual):
actual = [t for t, _ in actual]
missing = set(required) - set(actual)
self.failIf(len(missing), 'missing required topics: %s'%(','.join(missing)))
## validate node.getPublications(caller_id)
def testGetPublications(self):
# test with bad arity
self.apiError(self.node.getPublications())
self.apiError(self.node.getPublications(self.caller_id, 'something extra'))
# test success
self._checkTopics([rospy.resolve_name(t) for t in _required_publications],
self.apiSuccess(self.node.getPublications(self.caller_id)))
## validate node.getSubscriptions(caller_id)
def testGetSubscriptions(self):
# test with bad arity
self.apiError(self.node.getSubscriptions())
self.apiError(self.node.getSubscriptions(self.caller_id, 'something extra'))
# test success
self._checkTopics([rospy.resolve_name(t) for t in _required_subscriptions],
self.apiSuccess(self.node.getSubscriptions(self.caller_id)))
## validate node.paramUpdate(caller_id, key, value)
def testParamUpdate(self):
node = self.node
good_key = rosgraph.names.ns_join(self.ns, 'good_key')
bad_key = rosgraph.names.ns_join(self.ns, 'bad_key')
# test bad key
self.apiError(node.paramUpdate(self.caller_id, '', 'bad'))
self.apiError(node.paramUpdate(self.caller_id, 'no_namespace', 'bad'))
# test with bad arity
self.apiError(node.paramUpdate(self.caller_id, bad_key))
self.apiError(node.paramUpdate(self.caller_id))
# node is not subscribed to good_key (yet)
self.apiError(node.paramUpdate(self.caller_id, good_key, 'good_value'))
# we can't actually test success cases without forcing node to subscribe
#self.apiSuccess(node.paramUpdate(self.caller_id, good_key, 1))
#self.apiSuccess(node.paramUpdate(self.caller_id, good_key, True))
#self.apiSuccess(node.paramUpdate(self.caller_id, good_key, 10.0))
## validate node.getUri(caller_id)
def testGetUri(self):
# test bad arity
self.apiError(self.node.getUri(self.caller_id, 'bad'))
self.apiError(self.node.getUri())
# test success
self._checkUri(self.apiSuccess(self.node.getUri(self.caller_id)))
## validate node.getName(caller_id)
def testGetName(self):
# test bad arity
self.apiError(self.node.getName(self.caller_id, 'bad'))
self.apiError(self.node.getName())
# test success
val = self.apiSuccess(self.node.getName(self.caller_id))
self.assert_(len(val), "empty name")
## validate node.getMasterUri(caller_id)
def testGetMasterUri(self):
# test bad arity
self.apiError(self.node.getMasterUri(self.caller_id, 'bad'))
self.apiError(self.node.getMasterUri())
# test success
uri = self.apiSuccess(self.node.getMasterUri(self.caller_id))
self._checkUri(uri)
self.assertEquals(rosgraph.get_master_uri(), uri)
## validate node.publisherUpdate(caller_id, topic, uris)
def testPublisherUpdate(self):
node = self.node
probe_topic = rosgraph.names.ns_join(self.ns, 'probe_topic')
fake_topic = rosgraph.names.ns_join(self.ns, 'fake_topic')
# test bad arity
self.apiError(node.getBusStats(self.caller_id, 'bad'))
self.apiError(node.getBusStats())
# test bad args
self.apiError(node.publisherUpdate(self.caller_id, '/bad_topic', 'bad'))
self.apiError(node.publisherUpdate(self.caller_id, '/bad_topic', 2))
self.apiError(node.publisherUpdate(self.caller_id, '/bad_topic', False))
self.apiError(node.publisherUpdate(self.caller_id, '/bad_topic', ['bad']))
self.apiError(node.publisherUpdate())
# test success
# still success even if not actually interested in topic
self.apiSuccess(node.publisherUpdate(self.caller_id, fake_topic,
['http://localhost:1234', 'http://localhost:5678']))
self.apiSuccess(node.publisherUpdate(self.caller_id, fake_topic,
[]))
# try it with it the /probe_topic, which will exercise some error branches in the client
self.apiSuccess(node.publisherUpdate(self.caller_id, probe_topic,
['http://unroutablefakeservice:1234']))
# give it some time to make sure it's attempted contact
time.sleep(1.0)
# check that it's still there
self.apiSuccess(node.publisherUpdate(self.caller_id, probe_topic,
[]))
def _checkTCPROS(self, protocol_params):
self.assert_(protocol_params, "no protocol params returned")
self.assert_(type(protocol_params) == list, "protocol params must be a list: %s"%protocol_params)
self.assertEquals(3, len(protocol_params), "TCPROS params should have length 3: %s"%protocol_params)
self.assertEquals(protocol_params[0], _TCPROS)
# expect ['TCPROS', 1.2.3.4, 1234]
self.assertEquals(protocol_params[0], _TCPROS)
def testRequestTopic(self):
node = self.node
protocols = [[_TCPROS]]
probe_topic = rosgraph.names.ns_join(self.ns, 'probe_topic')
fake_topic = rosgraph.names.ns_join(self.ns, 'fake_topic')
# test bad arity
self.apiError(node.requestTopic(self.caller_id, probe_topic, protocols, 'extra stuff'))
self.apiError(node.requestTopic(self.caller_id, probe_topic))
self.apiError(node.requestTopic(self.caller_id))
self.apiError(node.requestTopic())
# test bad args
self.apiError(node.requestTopic(self.caller_id, 1, protocols))
self.apiError(node.requestTopic(self.caller_id, '', protocols))
self.apiError(node.requestTopic(self.caller_id, fake_topic, protocols))
self.apiError(node.requestTopic(self.caller_id, probe_topic, 'fake-protocols'))
topics = [rosgraph.names.ns_join(self.ns, t) for t in _required_publications]
# currently only support TCPROS as we require all clients to support this
protocols = [[_TCPROS]]
for topic in topics:
self._checkTCPROS(self.apiSuccess(node.requestTopic(self.caller_id, topic, protocols)))
protocols = [['FakeTransport', 1234, 5678], [_TCPROS], ['AnotherFakeTransport']]
# try each one more time, this time with more protocol choices
for topic in topics:
self._checkTCPROS(self.apiSuccess(node.requestTopic(self.caller_id, topic, protocols)))
def testGetBusInfo(self):
# test bad arity
self.apiError(self.node.getBusInfo(self.caller_id, 'bad'))
self.apiError(self.node.getBusInfo())
#TODO: finish
def testGetBusStats(self):
# test bad arity
self.apiError(self.node.getBusStats(self.caller_id, 'bad'))
self.apiError(self.node.getBusStats())
#TODO: finish
## test the state of the master based on expected node registration
def testRegistrations(self):
# setUp() ensures the node has registered with the master
topics = self.apiSuccess(self.master.getPublishedTopics(self.caller_id, ''))
topic_names = [t for t, type in topics]
required_topic_pubs = [rospy.resolve_name(t) for t in _required_publications]
required_topic_subs = [rospy.resolve_name(t) for t in _required_subscriptions]
self._checkTopics(required_topic_pubs, topics)
# now check types
topicTypeMap = self._createTopicTypeMap()
for topic, type in topics:
if topic in topicTypeMap:
self.assertEquals(type, topicTypeMap[topic], "topic [%s]: type [%s] does not match expected [%s]"%(type, topic, topicTypeMap[topic]))
# now check actual URIs
node_name = self.test_node
systemState = self.apiSuccess(self.master.getSystemState(self.caller_id))
pubs, subs, srvs = systemState
for topic, list in pubs:
if topic in required_topic_pubs:
self.assert_(node_name in list, "%s not in %s"%(self.node_api, list))
for topic, list in subs:
if topic in required_topic_subs:
self.assert_(node_name in list, "%s not in %s"%(self.node_api, list))
for service, list in srvs:
#TODO: no service tests yet
pass
## Performs end-to-end integration tests of a test_node. NodeIntegrationTestCase
## itself is a rospy node and thus implicitly depends on rospy functionality.
class NodeIntegrationTestCase(_NodeTestCase):
def __init__(self, *args):
super(NodeIntegrationTestCase, self).__init__(*args)
rospy.init_node(NODE_INTEGRATION_NAME)
def testString(self):
pub = rospy.Publisher('test_string_in', test_rosmaster.msg.String)
sub = rospy.Subscriber('test_string_in', test_rosmaster.msg.String)
#TODO: publish a bunch and check sequencing + caller_id
pub.unregister()
sub.unregister()
def testPrimitives(self):
pub = rospy.Publisher('test_primitives_in', test_rosmaster.msg.String)
sub = rospy.Subscriber('test_primitives_out', test_rosmaster.msg.String)
#TODO: publish a bunch and check sequencing + caller_id
pub.unregister()
sub.unregister()
def testArrays(self):
pub = rospy.Publisher('test_header_in', test_rosmaster.msg.String)
sub = rospy.Subscriber('test_header_out', test_rosmaster.msg.String)
#TODO: publish a bunch and check sequencing + caller_id
pub.unregister()
sub.unregister()
def testHeader(self):
#msg.auto_header = True
pub = rospy.Publisher('test_header_in', test_rosmaster.msg.String)
sub = rospy.Subscriber('test_header_out', test_rosmaster.msg.String)
#TODO: publish a bunch and check sequencing + caller_id
pub.unregister()
sub.unregister()

View File

@@ -0,0 +1,101 @@
#!/usr/bin/env python
# Software License Agreement (BSD License)
#
# Copyright (c) 2008, Willow Garage, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of Willow Garage, Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
## Simple demo of a rospy service client that calls a service to add
## two integers.
PKG = 'test_rosmaster' # this package name
import sys
import os
import string
import rospy
# imports the AddTwoInts service
from test_rosmaster.srv import *
## add two numbers using the add_two_ints service
## @param x int: first number to add
## @param y int: second number to add
def add_two_ints_client(x, y):
# NOTE: you don't have to call rospy.init_node() to make calls against
# a service. This is because service clients do not have to be
# nodes.
# block until the add_two_ints service is available
# you can optionally specify a timeout
rospy.wait_for_service('add_two_ints')
try:
# create a handle to the add_two_ints service
add_two_ints = rospy.ServiceProxy('add_two_ints', AddTwoInts)
print "Requesting %s+%s"%(x, y)
# simplified style
resp1 = add_two_ints(x, y)
# formal style
resp2 = add_two_ints.call(AddTwoIntsRequest(x, y))
if not resp1.sum == (x + y):
raise Exception("test failure, returned sum was %s"%resp1.sum)
if not resp2.sum == (x + y):
raise Exception("test failure, returned sum was %s"%resp2.sum)
return resp1.sum
except rospy.ServiceException as e:
print "Service call failed: %s"%e
def usage():
return "%s [x y]"%sys.argv[0]
if __name__ == "__main__":
argv = rospy.myargv()
if len(argv) == 1:
import random
x = random.randint(-50000, 50000)
y = random.randint(-50000, 50000)
elif len(argv) == 3:
try:
x = string.atoi(argv[1])
y = string.atoi(argv[2])
except:
print usage()
sys.exit(1)
else:
print usage()
sys.exit(1)
print "%s + %s = %s"%(x, y, add_two_ints_client(x, y))

View File

@@ -0,0 +1,57 @@
#!/usr/bin/env python
# Software License Agreement (BSD License)
#
# Copyright (c) 2008, Willow Garage, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of Willow Garage, Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# Revision $Id: add_two_ints_server 3804 2009-02-11 02:16:00Z rob_wheeler $
## Simple demo of a rospy service that add two integers
PKG = 'test_rosmaster' # this package name
NAME = 'add_two_ints_server'
# import the AddTwoInts service
from test_rosmaster.srv import *
import rospy
def add_two_ints(req):
print("Returning [%s + %s = %s]" % (req.a, req.b, (req.a + req.b)))
return AddTwoIntsResponse(req.a + req.b)
def add_two_ints_server():
rospy.init_node(NAME)
s = rospy.Service('add_two_ints', AddTwoInts, add_two_ints)
# spin() keeps Python from exiting until node is shutdown
rospy.spin()
if __name__ == "__main__":
add_two_ints_server()

View File

@@ -0,0 +1,57 @@
#!/usr/bin/env python
# Software License Agreement (BSD License)
#
# Copyright (c) 2008, Willow Garage, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of Willow Garage, Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
## variant of add two ints that fails if the first argument is -1
PKG = 'test_rosmaster' # this package name
NAME = 'add_two_ints_server'
# import the AddTwoInts service
from test_rosmaster.srv import *
import rospy
def add_two_ints(req):
if req.a == -1:
raise rospy.ServiceException("intentional failure")
print("Returning [%s + %s = %s]" % (req.a, req.b, (req.a + req.b)))
return {'sum': req.a + req.b}
def add_two_ints_server():
rospy.init_node(NAME)
s = rospy.Service('add_two_ints', AddTwoInts, add_two_ints)
# spin() keeps Python from exiting until node is shutdown
rospy.spin()
if __name__ == "__main__":
add_two_ints_server()

View File

@@ -0,0 +1,59 @@
#!/usr/bin/env python
# Software License Agreement (BSD License)
#
# Copyright (c) 2010, Willow Garage, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of Willow Garage, Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
"""
This publishes wall clock to the /clock topic to test simulated time routines
"""
import time
import rospy
from rosgraph_msgs.msg import Clock
def faketime():
rospy.set_param('/use_sim_time', True)
pub = rospy.Publisher('/clock', Clock)
rospy.init_node('fake_time')
c = Clock()
while not rospy.is_shutdown():
float_secs = time.time()
secs = int(float_secs)
c.clock.nsecs = int((float_secs - secs) * 1000000000)
c.clock.secs = secs
pub.publish(c)
time.sleep(0.01)
if __name__ == '__main__':
try:
faketime()
except rospy.ROSInterruptException: pass

View File

@@ -0,0 +1,47 @@
#!/usr/bin/env python
PKG = 'test_ros'
MODULE = 'test_ros.testAllCommonFlows' #this is the module that will be invoked by python
######################################################
# BOILERPLATE: Should not have to modify anything
# below except for very last line
######################################################
# Bootstrap ourselves into latest rospy install
import sys, os
BOOTSTRAP_VERSION = "0.1"
# Read in ROS_ROOT
if not os.environ.has_key('ROS_ROOT'):
print """\nCannot run ROS: ROS_ROOT is not set.\nPlease set the ROS_ROOT environment variable to the
location of your ROS install.\n"""
sys.exit(-1)
rosRoot = os.environ['ROS_ROOT']
# Read in the rospy directory location from the 'rospack latest rospy' command
rospackLatest = os.popen(os.path.join(rosRoot,'rospack')+' latest rospy', 'r')
rospyDir = rospackLatest.read()
rospackLatest.close()
if rospyDir is None or not os.path.isdir(rospyDir.strip()):
print "\nERROR: Cannot locate rospy installation.\n"
sys.exit(-1)
# Run launcher bootstrapper
sys.path.append(os.path.join(rospyDir.strip(),'scripts'))
import launcher
manifestFile = launcher.getManifestFile(sys.argv[0], PKG)
launcher.init(BOOTSTRAP_VERSION)
launchCommand, launchArgs, launchEnv = \
launcher.getLaunchCommands(manifestFile, MODULE)
launcher.ready(launchCommand, launchArgs, launchEnv, BOOTSTRAP_VERSION)
######################################################
# END BOILERPLATE:
# You may wish to modify the exec command below to
# customize the behavior of your node, e.g.:
# * env['FOO'] = bar
# * launchArgs.append('--test')
######################################################
os.execvpe(launchCommand, launchArgs, launchEnv)

View File

@@ -0,0 +1,47 @@
#!/usr/bin/env python
PKG = 'test_ros'
MODULE = 'test_ros.testMaster' #this is the module that will be invoked by python
######################################################
# BOILERPLATE: Should not have to modify anything
# below except for very last line
######################################################
# Bootstrap ourselves into latest rospy install
import sys, os
BOOTSTRAP_VERSION = "0.1"
# Read in ROS_ROOT
if not os.environ.has_key('ROS_ROOT'):
print """\nCannot run ROS: ROS_ROOT is not set.\nPlease set the ROS_ROOT environment variable to the
location of your ROS install.\n"""
sys.exit(-1)
rosRoot = os.environ['ROS_ROOT']
# Read in the rospy directory location from the 'rospack latest rospy' command
rospackLatest = os.popen(os.path.join(rosRoot,'rospack')+' latest rospy', 'r')
rospyDir = rospackLatest.read()
rospackLatest.close()
if rospyDir is None or not os.path.isdir(rospyDir.strip()):
print "\nERROR: Cannot locate rospy installation.\n"
sys.exit(-1)
# Run launcher bootstrapper
sys.path.append(os.path.join(rospyDir.strip(),'scripts'))
import launcher
manifestFile = launcher.getManifestFile(sys.argv[0], PKG)
launcher.init(BOOTSTRAP_VERSION)
launchCommand, launchArgs, launchEnv = \
launcher.getLaunchCommands(manifestFile, MODULE)
launcher.ready(launchCommand, launchArgs, launchEnv, BOOTSTRAP_VERSION)
######################################################
# END BOILERPLATE:
# You may wish to modify the exec command below to
# customize the behavior of your node, e.g.:
# * env['FOO'] = bar
# * launchArgs.append('--test')
######################################################
os.execvpe(launchCommand, launchArgs, launchEnv)

View File

@@ -0,0 +1,47 @@
#!/usr/bin/env python
PKG = 'test_ros'
MODULE = 'test_ros.testSlave' #this is the module that will be invoked by python
######################################################
# BOILERPLATE: Should not have to modify anything
# below except for very last line
######################################################
# Bootstrap ourselves into latest rospy install
import sys, os
BOOTSTRAP_VERSION = "0.1"
# Read in ROS_ROOT
if not os.environ.has_key('ROS_ROOT'):
print """\nCannot run ROS: ROS_ROOT is not set.\nPlease set the ROS_ROOT environment variable to the
location of your ROS install.\n"""
sys.exit(-1)
rosRoot = os.environ['ROS_ROOT']
# Read in the rospy directory location from the 'rospack latest rospy' command
rospackLatest = os.popen(os.path.join(rosRoot,'rospack')+' latest rospy', 'r')
rospyDir = rospackLatest.read()
rospackLatest.close()
if rospyDir is None or not os.path.isdir(rospyDir.strip()):
print "\nERROR: Cannot locate rospy installation.\n"
sys.exit(-1)
# Run launcher bootstrapper
sys.path.append(os.path.join(rospyDir.strip(),'scripts'))
import launcher
manifestFile = launcher.getManifestFile(sys.argv[0], PKG)
launcher.init(BOOTSTRAP_VERSION)
launchCommand, launchArgs, launchEnv = \
launcher.getLaunchCommands(manifestFile, MODULE)
launcher.ready(launchCommand, launchArgs, launchEnv, BOOTSTRAP_VERSION)
######################################################
# END BOILERPLATE:
# You may wish to modify the exec command below to
# customize the behavior of your node, e.g.:
# * env['FOO'] = bar
# * launchArgs.append('--test')
######################################################
os.execvpe(launchCommand, launchArgs, launchEnv)

View File

@@ -0,0 +1,588 @@
#!/usr/bin/env python
# Software License Agreement (BSD License)
#
# Copyright (c) 2008, Willow Garage, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of Willow Garage, Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# Revision $Id: test_embed_msg.py 1986 2008-08-26 23:57:56Z sfkwc $
## Integration test for empty services to test serializers
## and transport
import sys
import unittest
try:
from xmlrpc.client import DateTime
except ImportError:
from xmlrpclib import DateTime
import math
import datetime
import random
import traceback
import rostest
from roslib.names import make_global_ns, ns_join
from rosclient import TestRosClient
HAS_PARAM = True
## Parameter Server API Test Cases. These tests are individually
## enabled by param server test nodes. Each test assumes a fresh param
## server, so we cannot run within the same roslaunch/rostest session.
class ParamServerTestCase(TestRosClient):
def _setParam(self, ctx, myState, testVals, master):
ctx = make_global_ns(ctx)
for type, vals in testVals:
try:
callerId = ns_join(ctx, "node")
count = 0
for val in vals:
key = "%s-%s"%(type,count)
#print("master.setParam(%s,%s)"%(callerId, key))
master.setParam(callerId, key, val)
self.assert_(self.apiSuccess(master.hasParam(callerId, key)))
trueKey = ns_join(ctx, key)
myState[trueKey] = val
count += 1
except Exception:
assert "getParam failed on type[%s], val[%s]"%(type,val)
#self._checkParamState(myState)
def _checkParamState(self, myState):
master = self.master
callerId = 'master' #validate from root
for (k, v) in myState.items():
assert self.apiSuccess(master.hasParam(callerId, k))
#print("verifying parameter %s"%k)
try:
v2 = self.apiSuccess(master.getParam(callerId, k))
except:
raise Exception("Exception raised while calling master.getParam(%s,%s): %s"%(callerId, k, traceback.format_exc()))
if isinstance(v2, DateTime):
self.assertEquals(DateTime(v), v2, "[%s]: %s != %s, %s"%(k, v, v2, v2.__class__))
elif type(v2) == float:
self.assertAlmostEqual(v, v2, 3, "[%s]: %s != %s, %s"%(k, v, v2, v2.__class__))
else:
self.assertEquals(v, v2)
paramNames = myState.keys()
remoteParamNames = self.apiSuccess(master.getParamNames(callerId))
# filter out the roslaunch params like run id and roslaunch/, which are always set
remoteParamNames = [p for p in remoteParamNames if not p in ['/run_id', '/rosdistro', '/rosversion']]
remoteParamNames = [p for p in remoteParamNames if not p.startswith('/roslaunch/')]
assert not set(paramNames) ^ set(remoteParamNames), "parameter server keys do not match local: %s"%(set(paramNames)^set(remoteParamNames))
# _testHasParam: test hasParam API
def _testHasParam(self):
master = self.master
caller_id = '/node'
self.failIf(self.apiSuccess(master.hasParam(caller_id, '/new_param')))
self.apiSuccess(master.setParam(caller_id, '/new_param', 1))
self.assert_(self.apiSuccess(master.hasParam(caller_id, '/new_param')))
# test with relative-name resolution
self.assert_(self.apiSuccess(master.hasParam(caller_id, 'new_param')))
# test with param in sub-namespace
self.failIf(self.apiSuccess(master.hasParam(caller_id, '/sub/sub2/new_param2')))
# - verify that parameter tree does not exist yet (#587)
self.failIf(self.apiSuccess(master.hasParam(caller_id, '/sub/sub2/')))
self.failIf(self.apiSuccess(master.hasParam(caller_id, '/sub/sub2')))
self.failIf(self.apiSuccess(master.hasParam(caller_id, '/sub/')))
self.failIf(self.apiSuccess(master.hasParam(caller_id, '/sub')))
self.apiSuccess(master.setParam(caller_id, '/sub/sub2/new_param2', 1))
self.assert_(self.apiSuccess(master.hasParam(caller_id, '/sub/sub2/new_param2')))
# - verify that parameter tree now exists (#587)
self.assert_(self.apiSuccess(master.hasParam(caller_id, '/sub/sub2/')))
self.assert_(self.apiSuccess(master.hasParam(caller_id, '/sub/sub2')))
self.assert_(self.apiSuccess(master.hasParam(caller_id, '/sub/')))
self.assert_(self.apiSuccess(master.hasParam(caller_id, '/sub')))
# test with relative-name resolution
self.assert_(self.apiSuccess(master.hasParam(caller_id, 'sub/sub2/new_param2')))
self.assert_(self.apiSuccess(master.hasParam('/sub/node', 'sub2/new_param2')))
self.assert_(self.apiSuccess(master.hasParam('/sub/sub2/node', 'new_param2')))
self.assert_(self.apiSuccess(master.hasParam('/sub/node', 'sub2')))
self.assert_(self.apiSuccess(master.hasParam('/node', 'sub')))
# testSearchParam: test upwards-looking parameter search
def _testSearchParam(self):
master = self.master
caller_id = '/node'
# vals are mostly identical, save some randomness. we want
# identical structure in order to stress lookup rules
val1 = { 'level1_p1': random.randint(0, 10000),
'level1_p2' : { 'level2_p2': random.randint(0, 10000) }}
val2 = { 'level1_p1': random.randint(0, 10000),
'level1_p2' : { 'level2_p2': random.randint(0, 10000) }}
val3 = { 'level1_p1': random.randint(0, 10000),
'level1_p2' : { 'level2_p2': random.randint(0, 10000) }}
val4 = { 'level1_p1': random.randint(0, 10000),
'level1_p2' : { 'level2_p2': random.randint(0, 10000) }}
full_dict = {}
# set the val parameter at three levels so we can validate search
caller_id = '/root'
self.failIf(self.apiSuccess(master.hasParam(caller_id, '/param')))
self.apiSuccess(master.setParam(caller_id, '/param', val1))
# - test param
self.assertEquals('/param', self.apiSuccess(master.searchParam(caller_id, 'param')))
self.failIf(self.apiSuccess(master.hasParam(caller_id, '/level1/param')))
self.apiSuccess(master.setParam(caller_id, '/level1/param', val2))
self.assertEquals(val2, self.apiSuccess(master.getParam(caller_id, '/level1/param')))
# - test search param
self.assertEquals('/param',
self.apiSuccess(master.searchParam(caller_id, 'param')))
self.assertEquals('/level1/param',
self.apiSuccess(master.searchParam('/level1/node', 'param')))
self.failIf(self.apiSuccess(master.hasParam(caller_id, '/level1/level2/param')))
self.apiSuccess(master.setParam(caller_id, '/level1/level2/param', val3))
# - test search param
self.assertEquals('/param',
self.apiSuccess(master.searchParam(caller_id, 'param')))
self.assertEquals('/level1/param',
self.apiSuccess(master.searchParam('/level1/node', 'param')))
self.assertEquals('/level1/level2/param',
self.apiSuccess(master.searchParam('/level1/level2/node', 'param')))
self.assertEquals('/level1/level2/param',
self.apiSuccess(master.searchParam('/level1/level2/level3/level4/node', 'param')))
self.failIf(self.apiSuccess(master.hasParam(caller_id, '/level1/level2/level3/level4/param')))
self.apiSuccess(master.setParam(caller_id, '/level1/level2/level3/level4/param', val4))
# - test search param
self.assertEquals('/param',
self.apiSuccess(master.searchParam(caller_id, 'param')))
self.assertEquals('/level1/param',
self.apiSuccess(master.searchParam('/level1/node', 'param')))
self.assertEquals('/level1/level2/param',
self.apiSuccess(master.searchParam('/level1/level2/node', 'param')))
self.assertEquals('/level1/level2/param',
self.apiSuccess(master.searchParam('/level1/level2/level3/node', 'param')))
self.assertEquals('/level1/level2/level3/level4/param',
self.apiSuccess(master.searchParam('/level1/level2/level3/level4/node', 'param')))
# test completely different hierarchy, should go to top
self.assertEquals('/param', self.apiSuccess(master.searchParam('/not/level1/level2/level3/level4/node', 'param')))
# test looking for param/sub_param
tests = [('/param', '/not/level1/level2/level3/level4/node'),
('/level1/param', '/level1/node'),
('/level1/param', '/level1/notlevel2/notlevel3/node'),
('/level1/level2/param', '/level1/level2/node'),
('/level1/level2/param', '/level1/level2/level3/node'),
('/level1/level2/param', '/level1/level2/notlevel3/notlevel3/node'),
('/level1/level2/level3/level4/param', '/level1/level2/level3/level4/node'),
('/level1/level2/level3/level4/param', '/level1/level2/level3/level4/l5/l6/node'),
]
for pbase, caller_id in tests:
self.assertEquals(pbase + '/level1_p1',
self.apiSuccess(master.searchParam(caller_id, 'param/level1_p1')))
key = pbase+'/level1_p2/level2_p2'
self.assertEquals(key,
self.apiSuccess(master.searchParam(caller_id, 'param/level1_p2/level2_p2')))
# delete the sub key and repeat, should get the same result as searchParam does partial matches
# - we may have already deleted the parameter in a previous iteration, so make sure
if self.apiSuccess(master.hasParam(caller_id, key)):
self.apiSuccess(master.deleteParam(caller_id, key))
self.assertEquals(key,
self.apiSuccess(master.searchParam(caller_id, 'param/level1_p2/level2_p2')))
# to make sure that it didn't work spuriously, search for non-existent key
self.assertEquals(pbase + '/non_existent',
self.apiSuccess(master.searchParam(caller_id, 'param/non_existent')))
self.assertEquals(pbase + '/level1_p2/non_existent',
self.apiSuccess(master.searchParam(caller_id, 'param/level1_p2/non_existent')))
## remove common keys that roslaunch places on param server
def _filterDict(self, d):
for k in ['run_id', 'roslaunch', 'rosversion', 'rosdistro']:
if k in d:
del d[k]
return d
# testGetParam: test basic getParam behavior. Value encoding verified separately by testParamValues
def _testGetParam(self):
master = self.master
caller_id = '/node'
val = random.randint(0, 10000)
full_dict = {}
# very similar to has param sequence
self.failIf(self.apiSuccess(master.hasParam(caller_id, '/new_param')))
self.apiSuccess(master.setParam(caller_id, '/new_param', val))
full_dict['new_param'] = val
self.assertEquals(val, self.apiSuccess(master.getParam(caller_id, '/new_param')))
# test with relative-name resolution
self.assertEquals(val, self.apiSuccess(master.getParam(caller_id, 'new_param')))
# test full get
ps_full_dict = self.apiSuccess(master.getParam(caller_id, '/'))
self._filterDict(ps_full_dict)
self.assertEquals(full_dict, ps_full_dict)
# test with param in sub-namespace
val = random.randint(0, 10000)
self.failIf(self.apiSuccess(master.hasParam(caller_id, '/sub/sub2/new_param2')))
self.apiSuccess(master.setParam(caller_id, '/sub/sub2/new_param2', val))
full_dict['sub'] = {'sub2': { 'new_param2': val }}
self.assertEquals(val, self.apiSuccess(master.getParam(caller_id, '/sub/sub2/new_param2')))
# test with relative-name resolution
self.assertEquals(val, self.apiSuccess(master.getParam(caller_id, 'sub/sub2/new_param2')))
self.assertEquals(val, self.apiSuccess(master.getParam('/sub/node', 'sub2/new_param2')))
self.assertEquals(val, self.apiSuccess(master.getParam('/sub/sub2/node', 'new_param2')))
# test that parameter server allows gets across namespaces (#493)
self.assertEquals(val, self.apiSuccess(master.getParam('/foo/bar/baz/blah/node', '/sub/sub2/new_param2')))
# test full get
ps_full_dict = self.apiSuccess(master.getParam(caller_id, '/'))
self._filterDict(ps_full_dict)
self.assertEquals(full_dict, ps_full_dict)
# test that parameter server namespace-get (#587)
val1 = random.randint(0, 10000)
val2 = random.randint(0, 10000)
val3 = random.randint(0, 10000)
self.failIf(self.apiSuccess(master.hasParam(caller_id, '/gains/P')))
self.failIf(self.apiSuccess(master.hasParam(caller_id, '/gains/I')))
self.failIf(self.apiSuccess(master.hasParam(caller_id, '/gains/D')))
self.failIf(self.apiSuccess(master.hasParam(caller_id, '/gains')))
self.apiSuccess(master.setParam(caller_id, '/gains/P', val1))
self.apiSuccess(master.setParam(caller_id, '/gains/I', val2))
self.apiSuccess(master.setParam(caller_id, '/gains/D', val3))
pid = {'P': val1, 'I': val2, 'D': val3}
full_dict['gains'] = pid
self.assertEquals(pid,
self.apiSuccess(master.getParam(caller_id, '/gains')))
self.assertEquals(pid,
self.apiSuccess(master.getParam(caller_id, '/gains/')))
ps_full_dict = self.apiSuccess(master.getParam(caller_id, '/'))
self._filterDict(ps_full_dict)
self.assertEquals(full_dict, ps_full_dict)
self.failIf(self.apiSuccess(master.hasParam(caller_id, '/ns/gains/P')))
self.failIf(self.apiSuccess(master.hasParam(caller_id, '/ns/gains/I')))
self.failIf(self.apiSuccess(master.hasParam(caller_id, '/ns/gains/D')))
self.failIf(self.apiSuccess(master.hasParam(caller_id, '/ns/gains')))
self.apiSuccess(master.setParam(caller_id, '/ns/gains/P', val1))
self.apiSuccess(master.setParam(caller_id, '/ns/gains/I', val2))
self.apiSuccess(master.setParam(caller_id, '/ns/gains/D', val3))
full_dict['ns'] = {'gains': pid}
self.assertEquals(pid,
self.apiSuccess(master.getParam(caller_id, '/ns/gains')))
self.assertEquals({'gains': pid},
self.apiSuccess(master.getParam(caller_id, '/ns/')))
self.assertEquals({'gains': pid},
self.apiSuccess(master.getParam(caller_id, '/ns')))
ps_full_dict = self.apiSuccess(master.getParam(caller_id, '/'))
self._filterDict(ps_full_dict)
self.assertEquals(full_dict, ps_full_dict)
# testSetParam: test basic setParam behavior. Value encoding verified separately by testParamValues
def _testSetParam(self):
master = self.master
caller_id = '/node'
val = random.randint(0, 10000)
# very similar to has param sequence
self.failIf(self.apiSuccess(master.hasParam(caller_id, '/new_param')))
self.apiSuccess(master.setParam(caller_id, '/new_param', val))
self.assertEquals(val, self.apiSuccess(master.getParam(caller_id, '/new_param')))
# test with relative-name resolution
self.assertEquals(val, self.apiSuccess(master.getParam(caller_id, 'new_param')))
# test type mutation, including from dictionary to value and back
vals = ['a', {'a': 'b'}, 1, 1., 'foo', {'c': 'd'}, 4]
for v in vals:
self.apiSuccess(master.setParam(caller_id, '/multi/multi_param', v))
self.assertEquals(v, self.apiSuccess(master.getParam(caller_id, 'multi/multi_param')))
# test with param in sub-namespace
val = random.randint(0, 10000)
self.failIf(self.apiSuccess(master.hasParam(caller_id, '/sub/sub2/new_param2')))
self.apiSuccess(master.setParam(caller_id, '/sub/sub2/new_param2', val))
self.assertEquals(val, self.apiSuccess(master.getParam(caller_id, '/sub/sub2/new_param2')))
# test with relative-name resolution
self.assertEquals(val, self.apiSuccess(master.getParam(caller_id, 'sub/sub2/new_param2')))
self.assertEquals(val, self.apiSuccess(master.getParam('/sub/node', 'sub2/new_param2')))
self.assertEquals(val, self.apiSuccess(master.getParam('/sub/sub2/node', 'new_param2')))
# test that parameter server namespace-set (#587)
self.failIf(self.apiSuccess(master.hasParam(caller_id, '/gains/P')))
self.failIf(self.apiSuccess(master.hasParam(caller_id, '/gains/I')))
self.failIf(self.apiSuccess(master.hasParam(caller_id, '/gains/D')))
self.failIf(self.apiSuccess(master.hasParam(caller_id, '/gains')))
pid = {'P': random.randint(0, 10000), 'I': random.randint(0, 10000), 'D': random.randint(0, 10000)}
self.apiSuccess(master.setParam(caller_id, '/gains', pid))
self.assertEquals(pid, self.apiSuccess(master.getParam(caller_id, '/gains')))
self.assertEquals(pid['P'], self.apiSuccess(master.getParam(caller_id, '/gains/P')))
self.assertEquals(pid['I'], self.apiSuccess(master.getParam(caller_id, '/gains/I')))
self.assertEquals(pid['D'], self.apiSuccess(master.getParam(caller_id, '/gains/D')))
subns = {'gains1': pid, 'gains2': pid}
self.apiSuccess(master.setParam(caller_id, '/ns', subns))
self.assertEquals(pid['P'], self.apiSuccess(master.getParam(caller_id, '/ns/gains1/P')))
self.assertEquals(pid['I'], self.apiSuccess(master.getParam(caller_id, '/ns/gains1/I')))
self.assertEquals(pid['D'], self.apiSuccess(master.getParam(caller_id, '/ns/gains1/D')))
self.assertEquals(pid, self.apiSuccess(master.getParam(caller_id, '/ns/gains1')))
self.assertEquals(pid, self.apiSuccess(master.getParam(caller_id, '/ns/gains2')))
self.assertEquals(subns, self.apiSuccess(master.getParam(caller_id, '/ns/')))
# test empty dictionary set
self.apiSuccess(master.setParam(caller_id, '/ns', {}))
# - param should still exist
self.assert_(self.apiSuccess(master.hasParam(caller_id, '/ns/')))
# - value should remain dictionary
self.assertEquals({}, self.apiSuccess(master.getParam(caller_id, '/ns/')))
# - value2 below /ns/ should be erased
self.failIf(self.apiSuccess(master.hasParam(caller_id, '/ns/gains1')))
self.failIf(self.apiSuccess(master.hasParam(caller_id, '/ns/gains1/P')))
# testParamValues: test storage of all XML-RPC compatible types"""
def _testParamValues(self):
try:
from xmlrpc.client import Binary
except ImportError:
from xmlrpclib import Binary
str_of_bytes = ''.join([chr(n) for n in range(0, 255)])
if not isinstance(str_of_bytes, bytes):
str_of_bytes = bytes(range(255))
testVals = [
['int', [0, 1024, 2147483647, -2147483647]],
['boolean', [True, False]],
#no longer testing null char
#['string', ['', '\0', 'x', 'hello', ''.join([chr(n) for n in range(0, 255)])]],
['unicode-string', [u'', u'hello', b'Andr\302\202'.decode('utf-8'), b'\377\376A\000n\000d\000r\000\202\000'.decode('utf-16')]],
['string-easy-ascii', [chr(n) for n in range(32, 128)]],
#['string-mean-ascii-low', [chr(n) for n in range(9, 10)]], #separate for easier book-keeping
#['string-mean-ascii-low', [chr(n) for n in range(1, 31)]], #separate for easier book-keeping
#['string-mean-signed', [chr(n) for n in range(129, 256)]],
['string', ['', 'x', 'hello-there', 'new\nline', 'tab\t']],
['double', [0.0, math.pi, -math.pi, 3.4028235e+38, -3.4028235e+38]],
#TODO: microseconds?
['datetime', [datetime.datetime(2005, 12, 6, 12, 13, 14), datetime.datetime(1492, 12, 6, 12, 13, 14)]],
['base64', [Binary(b''), Binary(b'\0'), Binary(str_of_bytes)]],
['array', [[], [1, 2, 3], ['a', 'b', 'c'], [0.0, 0.1, 0.2, 2.0, 2.1, -4.0],
[1, 'a', True], [[1, 2, 3], ['a', 'b', 'c'], [1.0, 2.1, 3.2]]]
],
]
master = self.master
print("Putting parameters onto the server")
# put our params into the parameter server
contexts = ['', 'scope1', 'scope/sub1/sub2']
myState = {}
failures = []
for ctx in contexts:
self._setParam(ctx, myState, testVals, master)
self._checkParamState(myState)
print("Deleting all of our parameters")
# delete all of our parameters
ctx = ''
count = 0
for key in list(myState.keys()):
count += 1
print("deleting [%s], [%s]" % (ctx, key))
self.apiSuccess(master.deleteParam(ctx, key))
del myState[key]
# far too intensive to check every time
if count % 50 == 0:
self._checkParamState(myState)
self._checkParamState(myState)
def _testEncapsulation(self):
"""testEncapsulation: test encapsulation: setting same parameter at different levels"""
master = self.master
myState = {}
self._checkParamState(myState)
testContexts = ['', 'en', 'en/sub1', 'en/sub2', 'en/sub1/sub2']
for c in testContexts:
c = make_global_ns(c)
testKey = 'param1'
testVal = random.randint(-1000000, 100000)
callerId = ns_join(c, "node")
trueKey = ns_join(c, testKey)
master.setParam(callerId, testKey, testVal)
myState[trueKey] = testVal
# make sure the master has the parameter under both keys and that they are equal
v1 = self.apiSuccess(master.getParam('/', trueKey))
v2 = self.apiSuccess(master.getParam(callerId, testKey))
assert v1 == v2, "[%s]: %s vs. [%s,%s]: %s"%(trueKey, v1, callerId, testKey, v2)
assert self.apiSuccess(master.hasParam(callerId, testKey)), testKey
assert self.apiSuccess(master.hasParam('node', trueKey)), trueKey
self._checkParamState(myState)
def _testPrivateNames(self):
master = self.master
myState = {}
self._checkParamState(myState)
testContexts = ['', 'sub1', 'sub1/sub2', 'sub1/sub2/sub3']
for c in testContexts:
c = make_global_ns(c)
callerId = ns_join(c, "node")
keyStub = "param1"
testKey = "~%s"%keyStub
testVal = random.randint(-1000000, 100000)
master.setParam(callerId, testKey, testVal)
trueKey = ns_join(callerId, keyStub)
myState[trueKey] = testVal
print("checking", trueKey)
v1 = self.apiSuccess(master.getParam('/', trueKey))
v2 = self.apiSuccess(master.getParam(callerId, testKey))
assert v1 == v2, "[%s]: %s vs. [%s,%s]: %s"%(trueKey, v1, callerId, testKey, v2)
assert self.apiSuccess(master.hasParam(callerId, testKey)), testKey
assert self.apiSuccess(master.hasParam('node', trueKey)), trueKey
#test setting a local param on a different node
testKey = ns_join("altnode","param2")
testVal = random.randint(-1000000, 100000)
master.setParam(callerId, testKey, testVal)
trueKey = ns_join(c, testKey)
altCallerId = ns_join(c, "altnode")
myState[trueKey] = testVal
v1 = self.apiSuccess(master.getParam(altCallerId, "~param2"))
v2 = self.apiSuccess(master.getParam(callerId, testKey))
assert v1 == v2
assert self.apiSuccess(master.hasParam(callerId, testKey)), testKey
assert self.apiSuccess(master.hasParam(altCallerId, "~param2"))
self._checkParamState(myState)
## testScopeUp: test that parameter server can chain up scopes to find/delete parameters
def _testScopeUp(self):
master = self.master
myState = {}
self._checkParamState(myState)
testVal = random.randint(-1000000, 100000)
master.setParam('/', 'uparam1', testVal)
myState['/uparam1'] = testVal
assert testVal == self.apiSuccess(master.getParam('/node', 'uparam1'))
assert testVal == self.apiSuccess(master.getParam('/uptest/node', 'uparam1'))
assert testVal == self.apiSuccess(master.getParam('/uptest/sub1/node', 'uparam1'))
assert testVal == self.apiSuccess(master.getParam('/uptest/sub1/sub2/node', 'uparam1'))
testVal = random.randint(-1000000, 100000)
master.setParam('/uptest2/sub1/node', 'uparam2', testVal)
myState['/uptest2/sub1/uparam2'] = testVal
assert testVal == self.apiSuccess(master.getParam('/uptest2/sub1/node', 'uparam2'))
assert testVal == self.apiSuccess(master.getParam('/uptest2/sub1/sub2/node', 'uparam2'))
assert testVal == self.apiSuccess(master.getParam('/uptest2/sub1/sub2/sub3/node', 'uparam2'))
# make sure we can ascend then descend
if 0:
testVal = random.randint(-1000000, 100000)
master.setParam('/uptest3/node', 'alt2/alt3/uparam3', testVal)
myState['/uptest3/alt2/alt3/uparam3'] = testVal
assert testVal == self.apiSuccess(master.getParam('/uptest3/sub1/node', 'alt2/alt3/uparam3'))
assert testVal == self.apiSuccess(master.getParam('/uptest3/sub1/sub2/node', 'alt2/alt3/uparam3'))
assert testVal == self.apiSuccess(master.getParam('/uptest3/sub1/sub2/sub3/node', 'alt2/alt3/uparam3'))
self._checkParamState(myState)
#verify upwards deletion
self.apiSuccess(master.deleteParam('/uptest/sub1/sub2/node', 'uparam1'))
del myState['/uparam1']
self._checkParamState(myState)
self.apiSuccess(master.deleteParam('/uptest2/sub1/sub2/sub3/node', 'uparam2'))
del myState['/uptest2/sub1/uparam2']
if 0:
self.apiSuccess(master.deleteParam('/uptest3/sub1/sub2/sub3/node', 'alt2/alt3/uparam3'))
del myState['/uptest3/alt2/alt3/uparam3']
self._checkParamState(myState)
## testScopeDown: test scoping rules for sub contexts
def _testScopeDown(self):
master = self.master
myState = {}
self._checkParamState(myState)
# test that parameter server down not chain down scopes
testVal = random.randint(-1000000, 100000)
master.setParam('/down/one/two/three/node', 'dparam1', testVal)
myState['/down/one/two/three/dparam1'] = testVal
assert not self.apiSuccess(master.hasParam('/down/one/node', 'dparam1'))
assert not self.apiSuccess(master.hasParam('/down/one/two/node', 'dparam1'))
self.apiError(master.getParam('/down/one/node', 'dparam1'))
self.apiError(master.getParam('/down/one/two/node', 'dparam1'))
# test that parameter server allows setting of parameters further down (1)
testVal = random.randint(-1000000, 100000)
master.setParam('/', '/down2/dparam2', testVal)
myState['/down2/dparam2'] = testVal
assert testVal == self.apiSuccess(master.getParam('/down2/node', 'dparam2'))
assert testVal == self.apiSuccess(master.getParam('/', 'down2/dparam2'))
assert not self.apiSuccess(master.hasParam('/down2/node', 'down2/dparam2'))
self.apiError(master.getParam('/down2/node', 'down2/dparam2'))
self._checkParamState(myState)
# test that parameter server allows setting of parameters further down (2)
testVal = random.randint(-1000000, 100000)
master.setParam('/', '/down3/sub/dparam3', testVal)
myState['/down3/sub/dparam3'] = testVal
assert testVal == self.apiSuccess(master.getParam('/down3/sub/node', 'dparam3'))
assert testVal == self.apiSuccess(master.getParam('/down3/node', 'sub/dparam3'))
assert testVal == self.apiSuccess(master.getParam('/', 'down3/sub/dparam3'))
assert testVal == self.apiSuccess(master.getParam('/down3/sub/sub2/node', 'dparam3'))
assert not self.apiSuccess(master.hasParam('/down3/sub/node', 'sub/dparam3'))
assert not self.apiSuccess(master.hasParam('/down3/sub/node', 'down3/sub/dparam3'))
self.apiError(master.getParam('/down3/sub/node', 'sub/dparam3'))
self.apiError(master.getParam('/down3/sub/node', 'down3/sub/dparam3'))
self._checkParamState(myState)
# test downwards deletion
master.setParam('/', '/down4/sub/dparam4A', testVal)
self.apiSuccess(master.deleteParam('/down4/sub/node', 'dparam4A'))
assert not self.apiSuccess(master.hasParam('/down4/sub', 'dparam4A'))
master.setParam('/', '/down4/sub/dparam4B', testVal)
self.apiSuccess(master.deleteParam('/down4/node', 'sub/dparam4B'))
assert not self.apiSuccess(master.hasParam('/down4/sub', 'dparam4B'))
master.setParam('/', '/down4/sub/dparam4C', testVal)
self.apiSuccess(master.deleteParam('/', 'down4/sub/dparam4C'))
assert not self.apiSuccess(master.hasParam('/down4/sub/node', 'dparam4C'))
self._checkParamState(myState)

View File

@@ -0,0 +1,4 @@
<launch>
<master type="test_rosmaster" />
<include file="$(find test_rosmaster)/test/test-param-server.xml" />
</launch>

View File

@@ -0,0 +1,81 @@
#!/usr/bin/env python
# Software License Agreement (BSD License)
#
# Copyright (c) 2008, Willow Garage, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of Willow Garage, Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# Revision $Id: test_embed_msg.py 1986 2008-08-26 23:57:56Z sfkwc $
import unittest
try:
from xmlrpc.client import ServerProxy
except ImportError:
from xmlrpclib import ServerProxy
import rosgraph
class TestRosClient(unittest.TestCase):
def setUp(self):
self.last_code = None
self.last_msg = None
self.last_val = None
self.master = ServerProxy(rosgraph.get_master_uri())
def tearDown(self):
self.master = None
## unit test assertion that fails if status code is not 1 and otherwise returns the value parameter
## @param args [int, str, val]: returnv value from ROS API call
## @return val value parameter from args (arg[2] for master/slave API)
def apiSuccess(self, args):
self.assert_(len(args) == 3, "invalid API return value triplet: %s"%str(args))
self.last_code, self.last_msg, self.last_val = args
assert self.last_code == 1, "status code is not 1: %s"%self.last_msg
return self.last_val
## unit test assertions that fails if status code is not 0 and otherwise returns true
## @param args [int, str, val]: returnv value from ROS API call
## @return True if status code is 0
def apiFail(self, args):
self.assert_(len(args) == 3, "invalid API return value triplet: %s"%str(args))
self.last_code, self.last_msg, self.last_val = args
assert self.last_code == 0, "Call should have failed with status code 0: %s"%self.last_msg
## unit test assertion that fails if status code is not -1 and otherwise returns true
## @return True if status code is -1
def apiError(self, args, msg=None):
self.assert_(len(args) == 3, "invalid API return value triplet: %s"%str(args))
self.last_code, self.last_msg, self.last_val = args
if msg:
assert self.last_code == -1, "%s (return msg was %s)"%(msg, self.last_msg)
else:
assert self.last_code == -1, "Call should have returned error -1 code: %s"%self.last_msg

View File

@@ -0,0 +1,22 @@
<launch>
<master type="test_rosmaster" />
<test test-name="test_rosmaster_simple_API" args="--simple" pkg="test_rosmaster" type="test_master_api.py" />
<test test-name="test_rosmaster_registerService_success" args="--regsrvsuccess" pkg="test_rosmaster" type="test_master_api.py" />
<test test-name="test_rosmaster_registerPublisher_success" args="--regpubsuccess" pkg="test_rosmaster" type="test_master_api.py" />
<test test-name="test_rosmaster_registerPublisher_types" args="--regpubtypes" pkg="test_rosmaster" type="test_master_api.py" />
<test test-name="test_rosmaster_registerSubscriber_simple_success" args="--regsubsimplesuccess" pkg="test_rosmaster" type="test_master_api.py" />
<test test-name="test_rosmaster_unregisterService_success" args="--unregsrvsuccess" pkg="test_rosmaster" type="test_master_api.py" />
<test test-name="test_rosmaster_unregisterPublisher_success" args="--unregpubsuccess" pkg="test_rosmaster" type="test_master_api.py" />
<test test-name="test_rosmaster_unregisterSubscriber_success" args="--unregsubsuccess" pkg="test_rosmaster" type="test_master_api.py" />
<test test-name="test_rosmaster_registerService_invalid" args="--regsrvinvalid" pkg="test_rosmaster" type="test_master_api.py" />
<test test-name="test_rosmaster_registerPublisher_invalid" args="--regpubinvalid" pkg="test_rosmaster" type="test_master_api.py" />
<test test-name="test_rosmaster_registerSubscriber_invalid" args="--regsubinvalid" pkg="test_rosmaster" type="test_master_api.py" />
<test test-name="test_rosmaster_unregisterService_invalid" args="--unregsrvinvalid" pkg="test_rosmaster" type="test_master_api.py" />
<test test-name="test_rosmaster_unregisterPublisher_invalid" args="--unregpubinvalid" pkg="test_rosmaster" type="test_master_api.py" />
<test test-name="test_rosmaster_unregisterSubscriber_invalid" args="--unregsubinvalid" pkg="test_rosmaster" type="test_master_api.py" />
</launch>

View File

@@ -0,0 +1,16 @@
<!-- this test is designed to be included into another rostest XML file with a 'master' tag setting the appropriate 'type' -->
<launch>
<test test-name="param_server_encapsulation" pkg="test_rosmaster" type="test_ps_encapsulation.py" />
<!-- values test usually takes just under a minute, so pad the time limit for slow builds -->
<test time-limit="120.0" test-name="param_server_values" pkg="test_rosmaster" type="test_ps_values.py" />
<test test-name="param_server_search_param" pkg="test_rosmaster" type="test_ps_search_param.py" />
<test test-name="param_has_param" pkg="test_rosmaster" type="test_ps_has_param.py" />
<test test-name="param_get_param" pkg="test_rosmaster" type="test_ps_get_param.py" />
<test test-name="param_set_param" pkg="test_rosmaster" type="test_ps_set_param.py" />
<!-- parameter server-side name resolution not supported -->
<test test-name="param_server_private_names" pkg="test_rosmaster" type="test_ps_private_names.py" />
</launch>

View File

@@ -0,0 +1,883 @@
# Software License Agreement (BSD License)
#
# Copyright (c) 2008, Willow Garage, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of Willow Garage, Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# Revision $Id$
"""
testMaster: ROS integration test cases for master XML-RPC API
To run, invoke nodes/testMaster
"""
import os, sys, getopt, traceback, logging, socket
import datetime, math, random
try:
from xmlrpc.client import DateTime, ServerProxy
except ImportError:
from xmlrpclib import DateTime, ServerProxy
import unittest
import rospy
from rostest import *
from testSlave import msMain
MYPKG = 'test_rosmaster'
HAS_PARAM = True
singletest = 'testGetFlowNames'
singletest = None
#singletest = 'testDotLocalNames'
def verifyNodeAddress(master, callerId, name, machine, addr, port):
if not name:
raise Exception("name is None")
rmachine, raddr, rport = apiSuccess(master.getNodeAddress(callerId, name))
if machine:
assert rmachine == machine, "Node [%s] is running on '%s' instead of '%s'"%(name, rmachine, machine)
if port:
assert rport == port, "Node [%s] is running on '%s' instead of '%s'"%(name, rport, port)
else:
assert rport, "Node [%s] does not have a registered port"%name
if addr:
if addr == 'localhost':
addr = socket.gethostname()
if raddr == 'localhost':
raddr = socket.gethostname()
addrinfo = socket.getaddrinfo(addr, 0, 0, 0, socket.SOL_TCP)
raddrinfo = socket.getaddrinfo(raddr, 0, 0, 0, socket.SOL_TCP)
assert raddrinfo == addrinfo, "%s!=%s" % (raddrinfo, addrinfo)
#ping the node
apiSuccess(ServerProxy("http://%s:%s/"%(raddr, rport)).getPid(''))
def testGraphState(master, graphNodes, graphFlows):
graph = apiSuccess(master.getGraph(''))
diff = set(graph[0]) ^ set(graphNodes)
assert not diff, "Graph nodes %s does not match expected %s: %s"%(graph[0], graphNodes, diff)
# stringify for comparison
expectedFlows = ["%s:%s:1"%f for f in graphFlows] # :1 = connected
print graph[1]
remoteFlows = ["%s:%s:%s"%(src,snk,c) for (src,snk,c) in graph[1]]
if expectedFlows or remoteFlows:
#assert set(expectedFlows) ^ set(remoteFlows), "Graph flows [%s] does not match expected [%s]"%(graph[1], graphFlows)
diff = set(expectedFlows) ^ set(remoteFlows)
assert not diff, "Graph flows %s does not match expected %s: %s"%(expectedFlows, remoteFlows, diff)
def testParamState(master, myState):
callerId = 'master' #validate from root
for (k, v) in myState.items():
if HAS_PARAM:
assert apiSuccess(master.hasParam(callerId, k))
print "verifying parameter %s"%k
v2 = apiSuccess(master.getParam(callerId, k))
if isinstance(v2, DateTime):
assert DateTime(v) == v2, "[%s]: %s != %s, %s"%(k, v, v2, v2.__class__)
else:
assert v == v2, "[%s]: %s != %s, %s"%(k, v, v2, v2.__class__)
paramNames = myState.keys()
remoteParamNames = apiSuccess(master.getParamNames(callerId))
assert not set(paramNames) ^ set(remoteParamNames), "parameter server keys do not match local"
class ParamServerTestCase(ROSGraphTestCase):
"""Parameter Server API Test Cases"""
def setUp(self):
super(ParamServerTestCase, self).setUp()
def tearDown(self):
super(ParamServerTestCase, self).tearDown()
def _testSetParam(self, ctx, myState, testVals, master):
for type, vals in testVals:
try:
if ctx:
callerId = "%s.node"%ctx
else:
callerId = "node"
count = 0
for val in vals:
key = "%s-%s"%(type,count)
print "master.setParam(%s,%s,%s)"%(callerId, key, val)
master.setParam(callerId, key, val)
if HAS_PARAM:
assert apiSuccess(master.hasParam(callerId, key))
if ctx:
trueKey = "%s.%s"%(ctx, key)
else:
trueKey = key
myState[trueKey] = val
count += 1
except:
assert "getParam failed on type[%s], val[%s]"%(type,val)
testParamState(master, myState)
def testParamValues(self):
"""testParamValues: test storage of all XML-RPC compatible types"""
try:
from xmlrpc.client import Binary
except ImportError:
from xmlrpclib import Binary
testVals = [
['int', [0, 1024, 2147483647, -2147483647]],
['boolean', [True, False]],
['string', ['', '\0', 'x', 'hello', ''.join([chr(n) for n in range(0, 255)])]],
['double', [0.0, math.pi, -math.pi, 3.4028235e+38, -3.4028235e+38]],
#TODO: microseconds?
['datetime', [datetime.datetime(2005, 12, 6, 12, 13, 14), datetime.datetime(1492, 12, 6, 12, 13, 14)]],
['base64', [Binary(''), Binary('\0'), Binary(''.join([chr(n) for n in range(0, 255)]))]],
['struct', [{ "a": 2, "b": 4},
{"a" : "b", "c" : "d"},
{"a" : {"b" : { "c" : "d"}}}]],
['array', [[], [1, 2, 3], ['a', 'b', 'c'], [0.0, 0.1, 0.2, 2.0, 2.1, -4.0],
[1, 'a', math.pi], [[1, 2, 3], ['a', 'b', 'c'], [1.0, 2.1, 3.2]]]
],
]
master = self.master
print "Putting parameters onto the server"
# put our params into the parameter server
contexts = ['', 'scope1', 'scope2', 'scope.subscope1', 'scope.sub1.sub2']
myState = {}
for ctx in contexts:
self._testSetParam(ctx, myState, testVals, master)
print "Deleting all of our parameters"
# delete all of our parameters
paramKeys = myState.keys()
for key in paramKeys:
apiSuccess(master.deleteParam('', key))
del myState[key]
testParamState(master, myState)
def testEncapsulation(self):
"""testEncapsulation: test encapsulation: setting same parameter at different levels"""
master = self.master
myState = {}
testParamState(master, myState)
testContexts = ['', 'en', 'en.sub1', 'en.sub2', 'en.sub1.sub2']
for c in testContexts:
testKey = 'param1'
testVal = random.randint(-1000000, 100000)
if c:
callerId = "%s.node"%c
trueKey = "%s.%s"%(c,testKey)
else:
callerId ="node"
trueKey = testKey
master.setParam(callerId, testKey, testVal)
myState[trueKey] = testVal
# make sure the master has the parameter under both keys and that they are equal
v1 = apiSuccess(master.getParam('', trueKey))
v2 = apiSuccess(master.getParam(callerId, testKey))
assert v1 == v2, "[%s]: %s vs. [%s,%s]: %s"%(trueKey, v1, callerId, testKey, v2)
if HAS_PARAM:
assert apiSuccess(master.hasParam(callerId, testKey)), testKey
assert apiSuccess(master.hasParam('node', trueKey)), trueKey
testParamState(master, myState)
def testDotLocalNames(self):
master = self.master
myState = {}
testParamState(master, myState)
testContexts = ['', 'sub1', 'sub1.sub2', 'sub1.sub2.sub3']
for c in testContexts:
if c:
callerId = "%s.node"%c
else:
callerId = "node"
testKey = ".param1"
testVal = random.randint(-1000000, 100000)
master.setParam(callerId, testKey, testVal)
trueKey = callerId+testKey
myState[trueKey] = testVal
v1 = apiSuccess(master.getParam('node', trueKey))
v2 = apiSuccess(master.getParam(callerId, testKey))
assert v1 == v2, "[%s]: %s vs. [%s,%s]: %s"%(trueKey, v1, callerId, testKey, v2)
if HAS_PARAM:
assert apiSuccess(master.hasParam(callerId, testKey)), testKey
assert apiSuccess(master.hasParam('node', trueKey)), trueKey
#test setting a local param on a different node
testKey = "altnode.param2"
testVal = random.randint(-1000000, 100000)
master.setParam(callerId, testKey, testVal)
if c:
trueKey = "%s.%s"%(c,testKey)
altCallerId = "%s.altnode"%c
else:
trueKey = testKey
altCallerId = "altnode"
myState[trueKey] = testVal
v1 = apiSuccess(master.getParam(altCallerId, ".param2"))
v2 = apiSuccess(master.getParam(callerId, testKey))
assert v1 == v2
if HAS_PARAM:
assert apiSuccess(master.hasParam(callerId, testKey)), testKey
assert apiSuccess(master.hasParam(altCallerId, ".param2"))
testParamState(master, myState)
def testScopeUp(self):
"""testScopeUp: test that parameter server can chain up scopes to find/delete parameters"""
master = self.master
myState = {}
testParamState(master, myState)
testVal = random.randint(-1000000, 100000)
master.setParam('', 'uparam1', testVal)
myState['uparam1'] = testVal
assert testVal == apiSuccess(master.getParam('node', 'uparam1'))
assert testVal == apiSuccess(master.getParam('uptest.node', 'uparam1'))
assert testVal == apiSuccess(master.getParam('uptest.sub1.node', 'uparam1'))
assert testVal == apiSuccess(master.getParam('uptest.sub1.sub2.node', 'uparam1'))
testVal = random.randint(-1000000, 100000)
master.setParam('uptest2.sub1.node', 'uparam2', testVal)
myState['uptest2.sub1.uparam2'] = testVal
assert testVal == apiSuccess(master.getParam('uptest2.sub1.node', 'uparam2'))
assert testVal == apiSuccess(master.getParam('uptest2.sub1.sub2.node', 'uparam2'))
assert testVal == apiSuccess(master.getParam('uptest2.sub1.sub2.sub3.node', 'uparam2'))
testParamState(master, myState)
#verify upwards deletion
apiSuccess(master.deleteParam('uptest.sub1.sub2.node', 'uparam1'))
del myState['uparam1']
testParamState(master, myState)
apiSuccess(master.deleteParam('uptest2.sub1.sub2.sub3.node', 'uparam2'))
del myState['uptest2.sub1.uparam2']
testParamState(master, myState)
def testScopeDown(self):
"""testScopeDown: test scoping rules for sub contexts"""
master = self.master
myState = {}
testParamState(master, myState)
# test that parameter server down not chain down scopes
testVal = random.randint(-1000000, 100000)
master.setParam('down.one.two.three.node', 'dparam1', testVal)
myState['down.one.two.three.dparam1'] = testVal
if HAS_PARAM:
assert not apiSuccess(master.hasParam('down.one', 'dparam1'))
assert not apiSuccess(master.hasParam('down.one.two', 'dparam1'))
apiError(master.getParam('down.one.node', 'dparam1'))
apiError(master.getParam('down.one.two.node', 'dparam1'))
# test that parameter server allows setting of parameters further down (1)
testVal = random.randint(-1000000, 100000)
master.setParam('node', 'down2.dparam2', testVal)
myState['down2.dparam2'] = testVal
assert testVal == apiSuccess(master.getParam('down2.node', 'dparam2'))
assert testVal == apiSuccess(master.getParam('', 'down2.dparam2'))
if HAS_PARAM:
assert not apiSuccess(master.hasParam('down2.node', 'down2.dparam2'))
apiError(master.getParam('down2.node', 'down2.dparam2'))
testParamState(master, myState)
# test that parameter server allows setting of parameters further down (2)
testVal = random.randint(-1000000, 100000)
master.setParam('node', 'down3.sub.dparam3', testVal)
myState['down3.sub.dparam3'] = testVal
assert testVal == apiSuccess(master.getParam('down3.sub.node', 'dparam3'))
assert testVal == apiSuccess(master.getParam('down3.node', 'sub.dparam3'))
assert testVal == apiSuccess(master.getParam('', 'down3.sub.dparam3'))
assert testVal == apiSuccess(master.getParam('down3.sub.sub2.node', 'dparam3'))
if HAS_PARAM:
assert not apiSuccess(master.hasParam('down3.sub.node', 'sub.dparam3'))
assert not apiSuccess(master.hasParam('down3.sub.node', 'down3.sub.dparam3'))
apiError(master.getParam('down3.sub.node', 'sub.dparam3'))
apiError(master.getParam('down3.sub.node', 'down3.sub.dparam3'))
testParamState(master, myState)
# test downwards deletion
master.setParam('node', 'down4.sub.dparam4A', testVal)
apiSuccess(master.deleteParam('down4.sub.node', 'dparam4A'))
if HAS_PARAM:
assert not apiSuccess(master.hasParam('down4.sub', 'dparam4A'))
master.setParam('node', 'down4.sub.dparam4B', testVal)
apiSuccess(master.deleteParam('down4.node', 'sub.dparam4B'))
if HAS_PARAM:
assert not apiSuccess(master.hasParam('down4.sub', 'dparam4B'))
master.setParam('node', 'down4.sub.dparam4C', testVal)
apiSuccess(master.deleteParam('', 'down4.sub.dparam4C'))
if HAS_PARAM:
assert not apiSuccess(master.hasParam('down4.sub.node', 'dparam4C'))
testParamState(master, myState)
class MasterTestCase(ROSGraphTestCase):
"""Master API Test Cases -- those not covered by ParamServer and AddKillNode"""
def setUp(self):
super(MasterTestCase, self).setUp()
def tearDown(self):
super(MasterTestCase, self).tearDown()
def _verifyFlowNameState(self, master, state):
flows = apiSuccess(master.getFlowNames('node1', ''))
assert len(flows) == len(state.values()), "Master reported a different number of flows"
for val in state.values():
assert val in flows, "flows does not contain %s : %s"%(val, flows)
def testPromoteFlow(self):
master = self.master
state = {}
callerId = 'node1'
type = 'common_flows/String'
#setup node1 with outflows outflow1..3 and inflow1..3
apiSuccess(master.registerNode(callerId, 'node1', 'localhost', 1234, []))
for i in range(1, 4):
for dir in ['inflow', 'outflow']:
locator = 'node1:%s%s'%(dir, i)
apiSuccess(master.registerFlow(callerId, 'node1', locator, dir, type))
state[locator] = [locator, dir, type, 0]
#test promote of a non-existent flow
apiError(master.promoteFlow(callerId, 'node1:notAFlow', 'node1:newNotAFlow'))
# - :outflow1 should resolve to parent, which is a non-existent flow
apiError(master.promoteFlow(callerId, ':outflow1', 'node1:newNotAFlow'))
## Play with Outflows
#successfully promote outflow1 to global namespace
dir = 'outflow'
apiSuccess(master.promoteFlow(callerId, 'node1:outflow1', ':outflow1'))
state[':outflow1'] = [':outflow1', dir, type, 1]
# - verify with .local syntax
apiSuccess(master.promoteFlow(callerId, '.:outflow2', ':outflow2'))
state[':outflow2'] = [':outflow2', dir, type, 1]
## Play with Inflows
#successfully promote inflow1 to the same namespace
dir = 'inflow'
tests = [
['node1:inflow1', 'node1:newInflow1'],
['node1:inflow2', 'sibling:inflow2'],
['node1:inflow3', 'node1:subgraph1:inflow3']
]
for source, target in tests:
apiSuccess(master.promoteFlow(callerId, source, target))
state[target] = [target, dir, type, 1]
self._verifyFlowNameState(master, state)
#TODO: test promotion to an already promoted/registered flow name
apiError(master.promoteFlow(callerId, 'node1:outflow1', 'node1:outflow2'))
apiError(master.promoteFlow(callerId, 'node1:outflow2', ':outflow1'))
apiError(master.promoteFlow(callerId, 'node1:inflow1', 'sibling:inflow2'))
#TODO: test promote of a promoted flow
#TODO: test promote with both :flow and .:flow
#TODO: test name resolution with subgraphs and callerIds better
def testRegisterFlow(self):
master = self.master
state = {}
type = 'common_flows/String'
for callerId in ['node1', 'subgraph.node1', 'grandparent.parent.node1']:
#setup node1 with outflows outflow1..3 and inflow1..3
apiSuccess(master.registerNode(callerId, 'node1', 'localhost', 1234, []))
for i in range(1, 4):
for dir in ['inflow', 'outflow']:
if dir == 'inflow':
locator = 'node1:%s%s'%(dir, i)
else:
locator = '.:%s%s'%(dir, i) #test .local resolution
apiSuccess(master.registerFlow(callerId, 'node1', locator, dir, type))
realLocator = '%s:%s%s'%(callerId, dir, i)
state[realLocator] = [realLocator, dir, type, 0]
#test register on a non-existent node
apiError(master.registerFlow(callerId, 'notNode', 'notANode:outflow', 'outflow', type))
#test register on bad locators
apiError(master.registerFlow(callerId, 'node1', '', 'outflow', type))
apiError(master.registerFlow(callerId, 'node1', 'badflow1', 'outflow', type))
#test register on a bad direction
apiError(master.registerFlow(callerId, 'node1', 'node1:badflow2', 'spiralflow', type))
#test register on invalid types
apiError(master.registerFlow(callerId, 'node1', 'node1:badflow3', 'outflow', ''))
apiError(master.registerFlow(callerId, 'node1', 'node1:badflow4', 'outflow', 'faketype'))
#test register to an already promoted/registered flow name
apiError(master.registerFlow(callerId, 'node1', 'node1:outflow1', 'outflow', type))
apiSuccess(master.promoteFlow(callerId, 'node1:outflow1', 'node1:newOutflow1'))
state['%s:newOutflow1'%callerId] = ['node1:newOutflow1', dir, type, 1]
apiError(master.registerFlow(callerId, 'node1', 'node1:newOutflow1', 'outflow', type))
self._verifyFlowNameState(master, state)
def testUnregisterFlow(self):
master = self.master
state = {}
type = 'common_flows/String'
for callerId in ['node1', 'subgraph.node1', 'grandparent.parent.node1']:
#setup node1 with outflows outflow1..3 and inflow1..3
apiSuccess(master.registerNode(callerId, 'node1', 'localhost', 1234, []))
for i in range(1, 4):
for dir in ['inflow', 'outflow']:
if dir == 'inflow':
locator = rlocator = 'node1:%s%s'%(dir, i)
else:
locator = '.:%s%s'%(dir, i) #test .local resolution
rlocator = 'node1:%s%s'%(dir, i) #test .local resolution
apiSuccess(master.registerFlow(callerId, 'node1', locator, dir, type))
realLocator = '%s:%s%s'%(callerId, dir, i)
state[realLocator] = [realLocator, dir, type, 0]
self._verifyFlowNameState(master, state)
#test bad unregisters
apiError(master.unregisterFlow(callerId, 'notANode:outflow'))
apiError(master.unregisterFlow(callerId, ''))
apiError(master.unregisterFlow(callerId.replace('node1', 'node2'), 'outflow1'))
apiSuccess(master.unregisterFlow(callerId, 'node1:outflow1'))
del state['%s:outflow1'%callerId]
apiSuccess(master.unregisterFlow(callerId, '.:outflow2'))
del state['%s:outflow2'%callerId]
apiSuccess(master.unregisterFlow(callerId.replace('node1', 'node2'), 'node1:outflow3'))
del state['%s:outflow3'%callerId]
apiSuccess(master.unregisterFlow('master', '%s:inflow1'%callerId))
del state['%s:inflow1'%callerId]
self._verifyFlowNameState(master, state)
#test register to an already unregistered flow
apiError(master.unregisterFlow(callerId, 'node1:outflow1'))
#test transitive unregisters
apiSuccess(master.promoteFlow(callerId, 'node1:inflow3', ':inflow3A'))
apiSuccess(master.promoteFlow(callerId, ':inflow3A', ':inflow3B'))
# - should unregister both 3A and 3B
apiSuccess(master.unregisterFlow(callerId, 'node1:inflow3'))
del state['%s:inflow3'%callerId]
self._verifyFlowNameState(master, state)
def testGetFlowNames(self):
master = self.master
pkg,node = testNode
subgraph = 'sub1.sub2'
name = 'testGFN_A'
port = apiSuccess(master.addNode('caller', subgraph, name, pkg, node, TEST_MACHINE, 0))
#testNode must have :in and :out
#:in and :out
testFlowNames = ["%s:%s"%(name, v) for v in ["in", "out"]]
print "FLOW NAMES", apiSuccess(master.getFlowNames('master', ''))
flows = apiSuccess(master.getFlowNames('%s.caller'%subgraph, ''))
assert not set([x[0] for x in flows]) ^ set(testFlowNames), "%s vs. %s"%([x[0] for x in flows], testFlowNames)
inDirs = [x[1] for x in flows if x[0].endswith(':in')]
outDirs = [x[1] for x in flows if x[0].endswith(':out')]
assert not filter(lambda x: x != "inflow", inDirs), inDirs
assert not filter(lambda x: x != "outflow", outDirs), outDirs
assert not filter(lambda x: x != "common_flows/String", [x[2] for x in flows]) #all flow types should be String
assert not filter(lambda x: x, [x[3] for x in flows]) #promoted flag should all be zero
#test callerId scoping and subgraph parameter
testFlowNames = ["%s.%s:%s"%(subgraph, name, v) for v in ["in", "out"]]
flows = apiSuccess(master.getFlowNames('caller', subgraph))
assert not set([x[0] for x in flows]) ^ set(testFlowNames), "%s vs. %s"%([x[0] for x in flows], testFlowNames)
flows = apiSuccess(master.getFlowNames('caller', "%s.%s"%(subgraph, name)))
assert not set([x[0] for x in flows]) ^ set(testFlowNames), "%s vs. %s"%([x[0] for x in flows], testFlowNames)
testFlowNames = ["sub2.%s:%s"%(name, v) for v in ["in", "out"]]
flows = apiSuccess(master.getFlowNames('sub1.caller', 'sub2'))
assert not set([x[0] for x in flows]) ^ set(testFlowNames), "%s vs. %s"%([x[0] for x in flows], testFlowNames)
testFlowNames = ["%s.%s:%s"%(subgraph, name, v) for v in ["in", "out"]]
flows = apiSuccess(master.getFlowNames('caller', ''))
flowNames = [x[0] for x in flows]
assert not set(flowNames) ^ set(testFlowNames)
def testGetNodeAddress(self):
def validate(val, callerId, name, port):
assert len(val) == 3, "getNodeAddress did not return 3-element list for value field"
assert type(val[0]) == str and type(val[1]) == str and type(val[2]) == int,\
"getNodeAddress did not return (str, str, int) for value field"
verifyNodeAddress(master, callerId, name, TEST_MACHINE, 'localhost', port)
#NOTE: this does not do full coverage on rospy, as many of the branch
#conditions in rospy are inaccessible via external call (they required
#corrupted data to be inserted into the master, which registerNode prevents)
master = self.master
#test that invalid case still meets type spec
code, msg, val = master.getNodeAddress('', 'testGetNodeAddress-fake')
assert code == -1, "getNodeAddress did not return failure code 0 for non-existent node"
assert len(val) == 3, "getNodeAddress did not return 3-element list for value field in error case"
assert type(val[0]) == str and type(val[1]) == str and type(val[2]) == int,\
"getNodeAddress did not return (str, str, int) for value field in error case"
#start a node to test valid case against
name = 'testGetNodeAddress-1'
port = 7981
pkg, node = testNode
apiSuccess(master.addNode('', '', name, pkg, node, TEST_MACHINE, port))
val = apiSuccess(master.getNodeAddress('', name))
validate(val, '', name, port)
#verify Graph Resource Name scoping rules
name = 'testGetNodeAddress-2'
port = 7982
apiSuccess(master.addNode('', 'gna1.gna2', name, pkg, node, TEST_MACHINE, port))
#make sure we have a node
tests = [
#test exact name matches
['gna1.gna2.node', name],
['gna1.node', 'gna2.%s'%name],
['', 'gna1.gna2.%s'%name],
#make sure that gNA searches upwards
['gna1.gna2.gna3.node', name],
]
for test in tests:
callerId, name = test
validate(apiSuccess(master.getNodeAddress(callerId, name)), callerId, name, port)
#make sure that it gNA doesn't search upwards when subcontext is specified
val = apiError(master.getNodeAddress('gna1.gna2', 'gna3.%s'%name))
def _verifyNodeDead(self, port):
testUri = "http://localhost:%s/"%port
try:
ServerProxy(testUri).getPid('node')
self.fail("test node is still running")
except:
pass
def testAddKillNode(self):
"""testAddKillNode: test adding then killing nodes"""
#TODO: more test cases
master = self.master
pkg,node = testNode
apiError(master.killNode('node','nonexistentNode'))
name = 'testAddKillA'
port = apiSuccess(master.addNode('node', 'subgraph', name, pkg, node, TEST_MACHINE, 0))
# - doesn't traverse across
apiError(master.killNode('different.subgraph.node', name))
# - doesn't traverse down
apiError(master.killNode('node', name))
# - doesn't traverse up
apiError(master.killNode('subgraph.sub2.node', name))
# - kill it
apiSuccess(master.killNode('subgraph.node', name))
# - push on the name resolution a bit
tests = [['node', '', 'testAddKillB'],
['node', 'g1.g2', 'testAddKillB'],
['node', 'g1', 'testAddKillB'],
['g1.g2.node', 'g3', 'testAddKillB'],
]
for callerId, subcontext, nodeName in tests:
port = apiSuccess(master.addNode(callerId, subcontext, nodeName, pkg, node, TEST_MACHINE, 0))
if subcontext:
name = "%s.%s"%(subcontext, nodeName)
else:
name = nodeName
apiSuccess(master.killNode(callerId, name))
self._verifyNodeDead(port)
def testAddNode(self):
"""testAddNode: test master.addNode(callerId, subcontext, name, pkg, pkgNode, machine, port)"""
master = self.master
graphNodes = ['master']
graphFlows = []
# Failure cases
pkg, node = testNode
errors = [
# - subcontext
['', 12, 'testAddNodeError1', pkg, node, TEST_MACHINE, 0],
#name
['', '', 123, pkg, node, TEST_MACHINE, 0],
# - invalid package implementation type
['', '', 'testAddNodeError2', 123, node, TEST_MACHINE, 0],
# - node impl name
['', '', 'testAddNodeError3', pkg, '', TEST_MACHINE, 0],
['', '', 'testAddNodeError4', pkg, 123, TEST_MACHINE, 0],
# - machine parameter
['', '', 'testAddNodeError6', pkg, node, 'unknown', 0],
['', '', 'testAddNodeError7', pkg, 'noNode', 123, 0],
# - port
['', '', 'testAddNodeError8', pkg, node, TEST_MACHINE, -80],
['', '', 'testAddNodeError9', pkg, node, TEST_MACHINE, "80"],
]
for args in errors:
try:
apiError(master.addNode(*args))
except Exception, e:
self.fail("addNodeError case failed with args[%s] and exception [%s]"%(args, e))
# - non-existent node implementation (this takes awhile)
apiFail(master.addNode('', '', 'testAddNodeFail1', pkg, 'notANode', TEST_MACHINE, 0))
testGraphState(master, graphNodes, graphFlows)
tests = [[['','testAddNode1'], [TEST_MACHINE, 7980]],
[['','testAddNode2'], [TEST_MACHINE, 0]],
[['','testAddNode3'], ['', 0]],
[['','testAddNode4'], [TEST_MACHINE, 0]],
[['','testAddNode5'], [TEST_MACHINE, 0]],
[['','testAddNode6'], [TEST_MACHINE, 0]],
[['','testAddNode7'], [TEST_MACHINE, 0]],
# subcontext
[['push', 'testAddNode8'], [TEST_MACHINE, 0]],
[['push.one.two', 'testAddNode9'], [TEST_MACHINE, 0]],
[['stanford.addNodeTest','testAddNodeA'], [TEST_MACHINE, 0]],
[['wg.addNodeTest', 'testAddNodeA'], [TEST_MACHINE, 0]],
]
for fullname, args in tests:
print "testAddNode: testing", fullname
subcontext, name = fullname
if subcontext:
fullname = '%s.%s'%(subcontext, name)
else:
fullname = name
machine, port = args
apiSuccess(master.addNode('', subcontext, name, pkg, node, machine, port))
verifyNodeAddress(master, '', fullname, machine, 'localhost', port)
graphNodes.append(fullname)
testGraphState(master, graphNodes, graphFlows)
# duplicate call should succeed
apiSuccess(master.addNode('', subcontext, name, pkg, node, machine, port))
#TODO: more stress testing of name resolution with non-root callerIds
# duplicate call with different params should fail
port = apiSuccess(master.addNode('node', 'duplicate.test', 'nodeA', pkg, node, TEST_MACHINE, 0))
apiError(master.addNode('node', 'duplicate.test', 'nodeA', pkg, node, TEST_MACHINE, port + 1))
apiError(master.addNode('node', 'duplicate.test', 'nodeA', pkg+'foo', node, TEST_MACHINE, port))
apiError(master.addNode('node', 'duplicate.test', 'nodeA', pkg, node+'foo', TEST_MACHINE, port))
#TODO: different machine
def testGetGraph(self):
#TODO: in ROS 2.0, graph will be scoped by callerId. For now its safe to assume
#that we are implicitly testing getGraph()
pass
def testAddMachine(self):
master = self.master
host, port = masterAddr
#test invalid calls on addMachine
# - name
apiError(master.addMachine('node', '', self.rosRoot, host, 22, '', ''))
apiError(master.addMachine('node', 123, self.rosRoot, host, 22, '', ''))
# - ros root
apiError(master.addMachine('node', 'name', '', host, 22, '', ''))
apiError(master.addMachine('node', 'name', 123, host, 22, '', ''))
# - address
apiError(master.addMachine('node', 'name', '', '', 22, '', ''))
apiError(master.addMachine('node', 'name', '', 123, 22, '', ''))
# - ssh port
apiError(master.addMachine('node', 'name', '', host, -22, '', ''))
apiError(master.addMachine('node', 'name', '', host, "22", '', ''))
tests = [
['node', 'testAddMachine-1'],
['g1.node', 'testAddMachine-2'],
['g1.g2.g3.node', 'testAddMachine-3'],
['g1.g2.node', 'testAddMachine-4'],
]
rosRoot = self.rosRoot
for callerId, m in tests:
apiSuccess(master.addMachine(callerId, m, rosRoot, host, 22, '', ''))
# - duplicate safe
apiSuccess(master.addMachine(callerId, m, rosRoot, host, 22, '', ''))
# - test error for each parameter being changed
apiError(master.addMachine(callerId, m, rosRoot+'/foo/', host, 22, '', ''))
apiError(master.addMachine(callerId, m, rosRoot, 'www.google.com', 22, '', ''))
apiError(master.addMachine(callerId, m, rosRoot, host, 21, '', ''))
apiError(master.addMachine(callerId, m, rosRoot, host, 22, 'fake-user', ''))
apiError(master.addMachine(callerId, m, rosRoot, host, 22, '', 'fake-password'))
#TODO: rewrite once master has a method for interrogating machines
def testRegisterNode_Flows(self):
#TODO: test flows param
pass
def testRegisterNode(self):
master = self.master
flows = []
# - invalid name
apiError(master.registerNode('', '', 'localhost', 80, flows))
# - invalid address
apiError(master.registerNode('', 'registerNodeFail2', '', 80, flows))
# - invalid ports
apiError(master.registerNode('', 'registerNodeFail3', 'localhost', -80, flows))
apiError(master.registerNode('', 'registerNodeFail4', 'localhost', 0, flows))
#implicitly test registerNode via local exec of test node
# - this actually tests the slave as much as the master, but I don't want
# to call registerNode with correct parameters as it is ambiguous whether
# or not the master should verify that the slave node actually exists
testCases = [
['', 'registerNodeExternal-1'],
['', 'rne.registerNodeExternal-2'],
['rne', 'registerNodeExternal-3'],
['', 'rne.rne2.rne3.registerNodeExternal-4'],
['rne', 'rne2.rne3.registerNodeExternal-5'],
['rne.rne2.rne3', 'registerNodeExternal-6'],
]
for context, name in testCases:
try:
if context:
fullName = "%s.%s"%(context, name)
callerId = "%s.node"%context
else:
fullName = name
callerId = "node"
startTestNode(fullName)
# Block until node is responsive before continuing
# - give test node 4 seconds to start
timeoutT = time.time() + 4.0
node = getTestNode()
val = None
while time.time() < timeoutT and not val:
try:
_, _, val = node.getPid('')
except:
pass
assert val, "unable to start test node for registerNode test case"
# - we don't know the machine in this case
verifyNodeAddress(master, callerId, name, None, testNodeAddr[0], testNodeAddr[1])
finally:
stopTestNode()
def testConnectFlow(self):
master = self.master
pkg, node = testNode
graphNodes = ['master']
graphFlows = []
machine = TEST_MACHINE
for i in range(1, 5):
apiSuccess(master.addNode('m', '', 'baseTcfNode-%s'%i, pkg, node, machine, 0))
graphNodes.append('baseTcfNode-%s'%i) #for testing up leveling
apiSuccess(master.addNode('m', '', 'tcfNode-%s'%i, pkg, node, machine, 0))
graphNodes.append('tcfNode-%s'%i)
apiSuccess(master.addNode('m', 'tcf1', 'tcfNode-%s'%i, pkg, node, machine, 0))
graphNodes.append('tcf1.tcfNode-%s'%i)
apiSuccess(master.addNode('m', 'tcf2', 'tcfNode-%s'%i, pkg, node, machine, 0))
graphNodes.append('tcf2.tcfNode-%s'%i)
apiSuccess(master.addNode('m', 'tcf1.sub1', 'tcfNode-%s'%i, pkg, node, machine, 0))
graphNodes.append('tcf1.sub1.tcfNode-%s'%i)
apiSuccess(master.addNode('m', 'tcf2.sub1', 'tcfNode-%s'%i, pkg, node, machine, 0))
graphNodes.append('tcf2.sub1.tcfNode-%s'%i)
apiSuccess(master.addNode('m', 'tcf3', 'tcfNode3-%s'%i, pkg, node, machine, 0))
graphNodes.append('tcf3.tcfNode3-%s'%i)
testGraphState(master, graphNodes, graphFlows)
reliable = 1
# illegal cases
illegal = [
# - name resolution scope
['node.tcf1', 'baseTcfNode-1:out', 'tcfNode-1:in'],
['tcf1.node', 'tcfNode-1:out', 'baseTcfNode-1:in'],
['tcf1.tcfNode-1', 'tcfNode-1:out', 'tcf1.tcfNode-1:in'],
['tcf1.tcfNode-1', ':out', 'tcf1.tcfNode-1:in'],
['tcf1.tcfNode-1', 'tcf1.tcfNode-2:out', ':in'],
['tcf1.node', 'tcf1.tcfNode-1:out', 'tcfNode-1:in'],
['tcf1.node', 'tcf2.tcfNode-1:out', 'tcfNode-1:in'],
['tcf1.node', 'tcfNode-1:out', 'tcf2.tcfNode-1:in'],
['tcf1.node', 'sub1.tcfNode-1:out', 'tcf2.sub1.tcfNode-1:in'],
['tcf1.sub1.node','baseTcfNode-1:out', 'tcfNode-1:in'],
['tcf1.sub1.node','sub1.tcfNode-1:out', 'tcfNode-1:in'],
['tcf1.sub1.node','tcf2.tcfNode-1:out', 'tcfNode-1:in'],
]
for callerId, source, sink in illegal:
apiError(master.connectFlow(callerId, source, sink, reliable))
# single source to sink sink
singleCase = [
#straight forward cases
['node', 'tcfNode-1:out', 'tcfNode-2:in', 'tcfNode-1:out', 'tcfNode-2:in',],
['tcfNode-1','tcf1.tcfNode-1:out', 'tcf1.tcfNode-2:in', 'tcf1.tcfNode-1:out', 'tcf1.tcfNode-2:in'],
['tcf2.tcfNode-1', 'tcfNode-1:out', 'tcfNode-2:in', 'tcf2.tcfNode-1:out', 'tcf2.tcfNode-2:in'],
['tcf1.tcfNode-2', 'sub1.tcfNode-1:out', 'sub1.tcfNode-2:in', 'tcf1.sub1.tcfNode-1:out', 'tcf1.sub1.tcfNode-2:in'],
['tcf2.tcfNode-1', 'tcfNode-1:out', 'sub1.tcfNode-1:in', 'tcf2.tcfNode-1:out', 'tcf2.sub1.tcfNode-1:in'],
# '.locator' naming test
['tcf2.sub1.tcfNode-1', '.:out', 'tcfNode-2:in', 'tcf2.sub1.tcfNode-1:out', 'tcf2.sub1.tcfNode-2:in'],
['tcf3.tcfNode3-2', 'tcfNode3-1:out', '.:in', 'tcf3.tcfNode3-1:out', 'tcf3.tcfNode3-2:in'],
]
for callerId, source, sink, sourceFull, sinkFull in singleCase:
apiSuccess(master.connectFlow(callerId, source, sink, reliable))
graphFlows.append((sourceFull, sinkFull))
testGraphState(master, graphNodes, graphFlows)
# - already connected
# Spec is fuzzy here. It's possible that this should be a succeed.
apiError(master.connectFlow('tcf2.node', 'tcfNode-1:out', 'tcfNode-2:in', reliable))
apiError(master.connectFlow('node', 'tcf1.tcfNode-1:out', 'tcf1.tcfNode-2:in', reliable))
#TODO: test single source to multiple sinks
#TODO: test multiple sources to single sink
def testKillFlow(self):
master = self.master
if 0:
master.killFlow(callerId, source, sink)
def testMisc(self):
master = self.master
assert master is not None, "master is None"
masterUri = apiSuccess(master.getMasterUri(''))
assert masterUri == apiSuccess(master.getMasterUri('a.different.id')), master.getMasterUri('a.different.id')
assert (getMasterUri() == masterUri) or \
(getMasterUriAlt() == masterUri), masterUri
#getPid
pid = apiSuccess(master.getPid(''))
assert pid == apiSuccess(master.getPid('a.different.id')), master.getPid('a.different.id')
#callerId must be string
apiError(master.getPid(0))
apiError(master.getMasterUri(0))
#shutdown
try:
master.shutdown('some.id')
except:
pass
time.sleep(0.1)
try:
code, status, val = master.getPid('')
assert code < 1, "Master is still running after shutdown"
except:
pass
def testMasterMain(argv, stdout, env):
return msMain(argv, stdout, env, [ParamServerTestCase, MasterTestCase], singletest)
if __name__ == '__main__':
testMasterMain(sys.argv, sys.stdout, os.environ)

View File

@@ -0,0 +1,343 @@
# Software License Agreement (BSD License)
#
# Copyright (c) 2008, Willow Garage, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of Willow Garage, Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# Revision $Id$
import os
import sys
import string
try:
from xmlrpc.client import ServerProxy
except ImportError:
from xmlrpclib import ServerProxy
import rospy
from rostest import *
singletest = None
#singletest = 'testSourceKillFlow'
#TODO: test single source to multiple sinks
#TODO: test multiple sources to single sink
# test_string_in
# test_string_out
# test_primitives_in
# test_primitives_out
# test_arrays_in
# test_arrays_out
# test_header_in
# test_header_out
_required_publications = 'test_string_out', 'test_primitives_out', 'test_arrays_out', 'test_header_out'
_required_subscriptions = 'test_string_in', 'test_primitives_in', 'test_arrays_in', 'test_header_in'
## Expects a single test node to be running with name 'test_node' and subscribed to 'test_string'
class SlaveTestCase(TestRosClient):
def setUp(self):
super(SlaveTestCase, self).setUp()
# retrieve handle on node
self.caller_id = rospy.get_caller_id()
self.node_api = self.apiSuccess(self.master.lookupNode(self.caller_id, 'test_node'))
self.assert_(self.node_api.startswith('http'))
self.node = ServerProxy(self.node_api)
def testGetPid(self):
pid = self.apiSuccess(self.node.getPid(self.caller_id))
self.assert_(pid > 0)
def testGetPublications(self):
publications = self.apiSuccess(self.node.getPublications(self.caller_id))
self.assert_(publications is not None)
expected = [rospy.resolve_name(t) for t in _required_publications]
missing = set(expected) - set(publications)
self.failIf(len(missing), 'missing required topics: %s'%(','.join(missing)))
def _subTestSourceRequestFlow(self, testName, protocols, testEval):
master = self.master
tests = [
[['testSourceRequestFlow-%s-nodeA','testSourceRequestFlow-%s-nodeB',],
['node', 'testSourceRequestFlow-%s-nodeA.out', 'testSourceRequestFlow-%s-nodeB.in']],
[['g1.testSourceRequestFlow-%s-nodeA','g1.testSourceRequestFlow-%s-nodeB',],
['g1.node', 'testSourceRequestFlow-%s-nodeA.out', 'testSourceRequestFlow-%s-nodeB.in']],
[['g1.g2.g3.testSourceRequestFlow-%s-nodeA','g1.g2.testSourceRequestFlow-%s-nodeB',],
['g1.g2.node', 'g3.testSourceRequestFlow-%s-nodeA.out', 'testSourceRequestFlow-%s-nodeB.in']],
[['g1.g2.testSourceRequestFlow-%s-nodeA','g1.g2.g3.testSourceRequestFlow-%s-nodeB',],
['g1.g2.node', 'testSourceRequestFlow-%s-nodeA.out', 'g3.testSourceRequestFlow-%s-nodeB.in']],
]
sources = {}
#start the nodes
# - save the source as we will be making calls on it
pkg, node = testNode
for test in tests:
sourceName, sinkName = [val%testName for val in test[0]]
port = apiSuccess(master.addNode('', '', sourceName, pkg, node, TEST_MACHINE, 0))
apiSuccess(master.addNode('', '', sinkName, pkg, node, TEST_MACHINE, 0))
sourceUri = 'http://%s:%s/'%(testNodeAddr[0], port)
sources[sourceName] = ServerProxy(sourceUri)
for test in tests:
sourceName, sinkName = [val%testName for val in test[0]]
source = sources[sourceName]
callerId = test[1][0]
sourceLocator, sinkLocator = [val%testName for val in test[1][1:]]
args = source.sourceRequestFlow(callerId, sourceLocator, sinkLocator, protocols)
testEval(args)
#TODO: test locator name resolution
def testSourceRequestFlow_TCPROS1(self):
def testEval(args):
protocol = apiSuccess(args)
assert type(protocol) == list
assert string.upper(protocol[0]) == 'TCPROS', "source should have returned TCPROS as the desired protocol"
assert len(protocol) == 3, "TCPROS parameter spec should be length 3"
protocols = [['TCPROS']]
self._subTestSourceRequestFlow('TCPROS1', protocols, testEval)
def testSourceRequestFlow_TCPROS2(self):
def testEval(args):
protocol = apiSuccess(args)
assert type(protocol) == list
assert string.upper(protocol[0]) == 'TCPROS', "source should have returned TCPROS as the desired protocol"
assert len(protocol) == 3, "TCPROS parameter spec should be length 3"
protocols = [['Fake1', 123, 132], ['Fake2', 1.0], ['Fake3'], ['Fake4', 'string'], ['Fake5', ['a', 'list'], ['a', 'nother', 'list']], ['TCPROS'], ['Fakelast', 'fake'] ]
self._subTestSourceRequestFlow('TCPROS2', protocols, testEval)
def testSourceRequestFlow_Fail(self):
protocols = [['Fake1', 123, 132], ['Fake2', 1.0], ['Fake3'], ['Fake4', 'string'], ['Fake5', ['a', 'list'], ['a', 'nother', 'list']], ['Fakelast', 'fake'] ]
self._subTestSourceRequestFlow('Fail', protocols, apiFail)
def testSourceRequestFlow_Errors(self):
slave = self.slave
master = self.master
#test that malformed locators return error codes
apiError(slave.sourceRequestFlow('', '', ''))
apiError(slave.sourceRequestFlow('', 'good.locator', 'badlocator'))
apiError(slave.sourceRequestFlow('', 'badlocator', 'good.locator'))
# sourceKillFlow(callerId, sourceLocator, sinkLocator)
#
# * called by master
# * returns int
def testSourceKillFlow(self):
slave = self.slave
master = self.master
#test that malformed locators return error codes
apiError(slave.sourceKillFlow('', '', ''))
apiError(slave.sourceKillFlow('', 'good.locator', 'badlocator'))
apiError(slave.sourceKillFlow('', 'badlocator', 'good.locator'))
tests = [
[['testSourceKillFlow-nodeA','testSourceKillFlow-nodeB',],
['node', 'testSourceKillFlow-nodeA.out', 'testSourceKillFlow-nodeB.in']],
[['g1.testSourceKillFlow-nodeA','g1.testSourceKillFlow-nodeB',],
['g1.node', 'testSourceKillFlow-nodeA.out', 'testSourceKillFlow-nodeB.in']],
[['g1.g2.g3.testSourceKillFlow-nodeA','g1.g2.g3.testSourceKillFlow-nodeB',],
['g1.g2.node', 'g3.testSourceKillFlow-nodeA.out', 'g3.testSourceKillFlow-nodeB.in']],
[['g1.g2.testSourceKillFlow-nodeA','g1.g2.testSourceKillFlow-nodeB',],
['g1.g2.node', 'testSourceKillFlow-nodeA.out', 'testSourceKillFlow-nodeB.in']],
[['a1.g2.g3.testSourceKillFlow-nodeA','a1.g2.testSourceKillFlow-nodeB',],
['a1.node', 'g2.g3.testSourceKillFlow-nodeA.out', 'g2.testSourceKillFlow-nodeB.in']],
[['a1.g2.testSourceKillFlow-nodeA','a1.g2.g3.testSourceKillFlow-nodeB',],
['a1.node', 'g2.testSourceKillFlow-nodeA.out', 'g2.g3.testSourceKillFlow-nodeB.in']],
]
sources = {}
#start the flows
# - save the source as we will be making calls on it
pkg, node = testNode
for test in tests:
sourceName, sinkName = test[0]
# - start the source/sink nodes
port = apiSuccess(master.addNode('', '', sourceName, pkg, node, TEST_MACHINE, 0))
apiSuccess(master.addNode('', '', sinkName, pkg, node, TEST_MACHINE, 0))
sourceUri = 'http://%s:%s/'%(testNodeAddr[0], port)
sources[sourceName] = ServerProxy(sourceUri)
# - start the flow
callerId, sourceLocator, sinkLocator = test[1]
apiSuccess(master.connectFlow(callerId, sourceLocator, sinkLocator, 1))
#attempt to kill flow with 1 wrong endpoint
for test in tests:
source = sources[test[0][0]]
callerId, sourceLocator, sinkLocator = test[1]
#sourceKill flow does a silent succeed if there is no flow to sink, but returns
#0 flows killed
val = apiSuccess(source.sourceKillFlow(callerId, sourceLocator, 'fake.sink'))
assert val == 0, "flowsKilled should be 0 for non-existent flow [fakesink]"
#sourceKill flow fails if source param does not refer to it
apiError(source.sourceKillFlow(callerId, 'fake.source', sinkLocator))
#attempt to kill flow with wrong context
for test in tests:
source = sources[test[0][0]]
callerId, sourceL, sinkL = test[1]
for sub_test in tests:
sub_callerId, sub_source, sub_sink = test[1]
if sub_callerId == callerId and sub_source == sourceL and sub_sink == sinkL:
continue
val = apiSuccess(source.sourceKillFlow(sub_callerId, sub_source, sub_sink))
assert val == 0, "flowsKilled should be 0 for non-existent flow [different context: %s,%s,%s on %s,%s,%s]"%(sub_callerId, sub_source, sub_sink, callerId, sourceL, sinkL)
# try to kill all flows on the source that we started
for test in tests:
source = sources[test[0][0]]
callerId, sourceLocator, sinkLocator = test[1]
val = apiSuccess(source.sourceKillFlow(callerId, sourceLocator, sinkLocator))
assert type(val) == int
assert val == 1, "Source did not report 1 flow killed: %s, %s"%(val, getLastMsg())
#TODO: test locator name resolution
sinkTests = [
[['testSinkConnectFlow-nodeA','testSinkConnectFlow-nodeB',],
['node', 'testSinkConnectFlow-nodeA.out', 'testSinkConnectFlow-nodeB.in']],
[['g1.testSinkConnectFlow-nodeA','g1.testSinkConnectFlow-nodeB',],
['g1.node', 'testSinkConnectFlow-nodeA.out', 'testSinkConnectFlow-nodeB.in']],
[['g1.g2.g3.testSinkConnectFlow-nodeA','g1.g2.testSinkConnectFlow-nodeB',],
['g1.g2.node', 'g3.testSinkConnectFlow-nodeA.out', 'testSinkConnectFlow-nodeB.in']],
[['g1.g2.testSinkConnectFlow-nodeA','g1.g2.g3.testSinkConnectFlow-nodeB',],
['g1.g2.node', 'testSinkConnectFlow-nodeA.out', 'g3.testSinkConnectFlow-nodeB.in']],
#separate subgraphs
[['a1.a2.testSinkConnectFlow-nodeA','b1.b2.testSinkConnectFlow-nodeB',],
['node', 'a1.a2.testSinkConnectFlow-nodeA.out', 'b1.b2.testSinkConnectFlow-nodeB.in']],
# '.locator' resolution
[['c1.node.testSinkConnectFlow-nodeA','c1.node2.testSinkConnectFlow-nodeB',],
['c1.node2', 'node.testSinkConnectFlow-nodeA.out', '.testSinkConnectFlow-nodeB.in']],
]
def _sink_StartNodes(self, tests):
"""
Test subroutine to startup all the nodes specified in the tests
"""
master = self.master
sourceUris = {}
sinks = {}
#start the nodes
# - save the sink as we will be making calls on it
pkg, node = testNode
for test in tests:
sourceName, sinkName = test[0]
sourcePort = apiSuccess(master.addNode('', '', sourceName, pkg, node, TEST_MACHINE, 0))
sinkPort = apiSuccess(master.addNode('', '', sinkName, pkg, node, TEST_MACHINE, 0))
sourceUri = 'http://%s:%s/'%(testNodeAddr[0], sourcePort)
sinkUri = 'http://%s:%s/'%(testNodeAddr[0], sinkPort)
sourceUris[sourceName] = sourceUri
sinks[sinkName] = ServerProxy(sinkUri)
return sourceUris, sinks
def _sink_StartFlows(self, tests, sourceUris, sinks):
"""
subroutine to connect the flows specified in the tests, purely
using the sink API.
In the future it would be nice to verify that the flow truly
exists, but for now trust what the sink says
"""
reliable = 1 #don't have UDP yet
for test in tests:
sourceName, sinkName = test[0]
sourceUri = sourceUris[sourceName]
sink = sinks[sinkName]
callerId, sourceLocator, sinkLocator = test[1]
print "Testing", test
val = apiSuccess(sink.sinkConnectFlow(callerId, sourceLocator, sinkLocator, sourceUri, reliable))
assert type(val) == int
def _sink_KillFlows(self, tests, sinks):
"""
subroutine to kill the flows specified in the tests, purely
using the sink API (i.e. source will still be running)
In the future it would be nice to verify that the flow was
killed, but for now trust what the sink says"""
reliable = 1 #don't have UDP yet
for test in tests:
sourceName, sinkName = test[0]
sink = sinks[sinkName]
callerId, sourceLocator, sinkLocator = test[1]
print "Testing", test
assert 1 == apiSuccess(sink.sinkKillFlow(callerId, sourceLocator, sinkLocator)),\
"sink did not report 1 flow killed: %s, %s"%(getLastVal(), getLastMsg())
#sinkConnectFlow(sourceLocator, sinkLocator, sourceXmlRpcURI, reliable)
# * API call on the sink of a flow. Called by master.
# * master requests that sink negotiate with the source to establish the flow
# * returns meaningless int
# * synchronous call, blocks until success/fail
def testSinkConnectFlow(self):
master = self.master
sourceUris, sinks = self._sink_StartNodes(self.sinkTests)
self._sink_StartFlows(self.sinkTests, sourceUris, sinks)
# sinkKillFlow(sourceLocator, sinkLocator)
# * called by master
def testSinkKillFlow(self):
slave = self.slave
master = self.master
apiError(slave.sinkKillFlow('', '', ''))
#startup the standard sink tests
sourceUris, sinks = self._sink_StartNodes(self.sinkTests)
self._sink_StartFlows(self.sinkTests, sourceUris, sinks)
self._sink_KillFlows(self.sinkTests, sinks)
def testMisc(self):
slave = self.slave
assert slave is not None, "slave is None"
#getMasterUri
masterUri = apiSuccess(slave.getMasterUri(''))
assert getMasterUri() == masterUri or getMasterUriAlt() == masterUri, masterUri
assert masterUri == apiSuccess(slave.getMasterUri('a.different.id'))
#getPid
pid = apiSuccess(slave.getPid(''))
assert pid == apiSuccess(slave.getPid('a.different.id'))
#callerId must be string
apiError(slave.getPid(0))
apiError(slave.getMasterUri(0))
try:
slave.shutdown('some.id')
except:
pass
time.sleep(0.1)
try:
code, status, val = slave.getPid('')
assert code < 1, "Slave is still running after shutdown"
except:
pass

View File

@@ -0,0 +1,204 @@
#!/usr/bin/env python
# Software License Agreement (BSD License)
#
# Copyright (c) 2008, Willow Garage, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of Willow Garage, Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
## Integration test for empty services to test serializers
## and transport
PKG='test_rosmaster'
NAME = 'test_master'
import sys
import unittest
import rospy
from master import MasterApiTestCase, set_node_name
# Due to the need to have a fresh master for each of these test cases,
# we have to go through the pain of exposing each of the test cases one-by-one
class MasterSimpleApi(MasterApiTestCase):
def testGetPid(self):
self._testGetPid()
def testGetUri(self):
self._testGetUri()
class MasterRegisterServiceSuccess(MasterApiTestCase):
def testRegisterServiceSuccess(self):
self._testRegisterServiceSuccess()
class MasterRegisterPublisherSuccess(MasterApiTestCase):
def testRegisterPublisherSuccess(self):
self._testRegisterPublisherSuccess()
class MasterRegisterPublisherTypes(MasterApiTestCase):
## #591: this test may change if we make registering '*' unsupported
def testRegisterPublisherTypes(self):
self._testRegisterPublisherTypes()
class MasterRegisterSubscriberSimpleSuccess(MasterApiTestCase):
def testRegisterSubscriberSimpleSuccess(self):
self._testRegisterSubscriberSimpleSuccess()
class MasterUnregisterServiceSuccess(MasterApiTestCase):
def testUnregisterServiceSuccess(self):
self._testUnregisterServiceSuccess()
class MasterUnregisterPublisherSuccess(MasterApiTestCase):
def testUnregisterPublisherSuccess(self):
self._testUnregisterPublisherSuccess()
class MasterUnregisterSubscriberSuccess(MasterApiTestCase):
def testUnregisterSubscriberSuccess(self):
self._testUnregisterSubscriberSuccess()
class MasterRegisterServiceInvalid(MasterApiTestCase):
def testRegisterServiceInvalid(self):
self._testRegisterServiceInvalid()
class MasterRegisterPublisherInvalid(MasterApiTestCase):
def testRegisterPublisherInvalid(self):
self._testRegisterPublisherInvalid()
class MasterRegisterSubscriberInvalid(MasterApiTestCase):
def testRegisterSubscriberInvalid(self):
self._testRegisterSubscriberInvalid()
class MasterUnregisterServiceInvalid(MasterApiTestCase):
def testUnregisterServiceInvalid(self):
self._testUnregisterServiceInvalid()
class MasterUnregisterPublisherInvalid(MasterApiTestCase):
def testUnregisterPublisherInvalid(self):
self._testUnregisterPublisherInvalid()
class MasterUnregisterSubscriberInvalid(MasterApiTestCase):
def testUnregisterSubscriberInvalid(self):
self._testUnregisterSubscriberInvalid()
if __name__ == '__main__':
# this is terribly complicated on the account that we want a fresh master for each test, so we cannot
# run all the tests as a single test node. instead, we have to have a separate test node per test.
import optparse
from optparse import OptionParser
parser = OptionParser(usage="usage: %prog [options] topic", prog=NAME)
# have to redeclare --text/--cov options, which are standard rostest options
parser.add_option("--text",dest="text_ignore", default=False,
action="store_true", help="rostest standard option")
parser.add_option("--cov",dest="cov_ignore", default=False,
action="store_true", help="rostest standard option")
parser.add_option("--simple",dest="simple", default=False,
action="store_true", help="MasterSimpleApi")
parser.add_option("--gtest_output",dest="gtest_output", default='',
help="xml output file")
parser.add_option("--regsrvsuccess", dest="regsrvsuccess", default=False,
action="store_true", help="MasterRegisterServiceSuccess")
parser.add_option("--regpubsuccess", dest="regpubsuccess", default=False,
action="store_true", help="MasterRegisterPublisherSuccess")
parser.add_option("--regpubtypes", dest="regpubtypes", default=False,
action="store_true", help="MasterRegisterPublisherTypes")
parser.add_option("--regsubsimplesuccess", dest="regsubsimplesuccess", default=False,
action="store_true", help="MasterRegisterSubscriberSimpleSuccess")
parser.add_option("--unregsrvsuccess", dest="unregsrvsuccess", default=False,
action="store_true", help="MasterUnregisterServiceSuccess")
parser.add_option("--unregpubsuccess", dest="unregpubsuccess", default=False,
action="store_true", help="MasterUnregisterPublisherSuccess")
parser.add_option("--unregsubsuccess", dest="unregsubsuccess", default=False,
action="store_true", help="MasterUnregisterSubscriberSuccess")
parser.add_option("--regsrvinvalid", dest="regsrvinvalid", default=False,
action="store_true", help="MasterRegisterServiceInvalid")
parser.add_option("--regsubinvalid", dest="regsubinvalid", default=False,
action="store_true", help="MasterRegisterSubscriberInvalid")
parser.add_option("--regpubinvalid", dest="regpubinvalid", default=False,
action="store_true", help="MasterRegisterPublisherInvalid")
parser.add_option("--unregsrvinvalid", dest="unregsrvinvalid", default=False,
action="store_true", help="MasterUnregisterServiceInvalid")
parser.add_option("--unregsubinvalid", dest="unregsubinvalid", default=False,
action="store_true", help="MasterUnregisterSubscriberInvalid")
parser.add_option("--unregpubinvalid", dest="unregpubinvalid", default=False,
action="store_true", help="MasterUnregisterPublisherInvalid")
(options, args) = parser.parse_args()
if options.simple:
cls = MasterSimpleApi
elif options.regsrvsuccess:
cls = MasterRegisterServiceSuccess
elif options.regpubsuccess:
cls = MasterRegisterPublisherSuccess
elif options.regpubtypes:
cls = MasterRegisterPublisherTypes
elif options.regsubsimplesuccess:
cls = MasterRegisterSubscriberSimpleSuccess
elif options.unregsrvsuccess:
cls = MasterUnregisterServiceSuccess
elif options.unregpubsuccess:
cls = MasterUnregisterPublisherSuccess
elif options.unregsubsuccess:
cls = MasterUnregisterSubscriberSuccess
elif options.regsrvinvalid:
cls = MasterRegisterServiceInvalid
elif options.regpubinvalid:
cls = MasterRegisterPublisherInvalid
elif options.regsubinvalid:
cls = MasterRegisterSubscriberInvalid
elif options.unregsrvinvalid:
cls = MasterUnregisterServiceInvalid
elif options.unregpubinvalid:
cls = MasterUnregisterPublisherInvalid
elif options.unregsubinvalid:
cls = MasterUnregisterSubscriberInvalid
if not cls:
parser.error("you must specify a test to run with an [options] flag")
set_node_name(NAME)
rospy.init_node(NAME, disable_rostime=True)
import rostest
rostest.rosrun(PKG, NAME, cls, sys.argv)

View File

@@ -0,0 +1,47 @@
#!/usr/bin/env python
# Software License Agreement (BSD License)
#
# Copyright (c) 2008, Willow Garage, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of Willow Garage, Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
PKG='test_rosmaster'
NAME = 'test_node_api'
import sys
import rospy
import rostest
from node import NodeApiTestCase, set_node_name
if __name__ == '__main__':
rospy.init_node(NAME)
set_node_name(NAME)
rostest.rosrun(PKG, NAME, NodeApiTestCase, sys.argv)

View File

@@ -0,0 +1,50 @@
#!/usr/bin/env python
# Software License Agreement (BSD License)
#
# Copyright (c) 2008, Willow Garage, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of Willow Garage, Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
PKG = 'test_rosmaster'
NAME = 'test_ps_encapsulation'
import sys
import rospy
import rostest
from param_server_test_case import ParamServerTestCase
class PsEncapsulationTestCase(ParamServerTestCase):
def testEncapsulation(self):
return self._testEncapsulation()
if __name__ == '__main__':
rospy.init_node(NAME)
rostest.rosrun(PKG, NAME, PsEncapsulationTestCase, sys.argv)

View File

@@ -0,0 +1,50 @@
#!/usr/bin/env python
# Software License Agreement (BSD License)
#
# Copyright (c) 2008, Willow Garage, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of Willow Garage, Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
PKG='test_rosmaster'
NAME = 'test_ps_has_param'
import sys
import rospy
import rostest
from param_server_test_case import ParamServerTestCase
class PsGetParamTestCase(ParamServerTestCase):
def testGetParam(self):
return self._testGetParam()
if __name__ == '__main__':
rospy.init_node(NAME)
rostest.rosrun(PKG, NAME, PsGetParamTestCase, sys.argv)

View File

@@ -0,0 +1,50 @@
#!/usr/bin/env python
# Software License Agreement (BSD License)
#
# Copyright (c) 2008, Willow Garage, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of Willow Garage, Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
PKG = 'test_rosmaster'
NAME = 'test_ps_has_param'
import sys
import rospy
import rostest
from param_server_test_case import ParamServerTestCase
class PsHasParamTestCase(ParamServerTestCase):
def testHasParam(self):
return self._testHasParam()
if __name__ == '__main__':
rospy.init_node(NAME)
rostest.rosrun(PKG, NAME, PsHasParamTestCase, sys.argv)

View File

@@ -0,0 +1,50 @@
#!/usr/bin/env python
# Software License Agreement (BSD License)
#
# Copyright (c) 2008, Willow Garage, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of Willow Garage, Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
PKG = 'test_rosmaster'
NAME = 'test_ps_private_names'
import sys
import rospy
import rostest
from param_server_test_case import ParamServerTestCase
class PsPrivateNamesTestCase(ParamServerTestCase):
def testPrivateNames(self):
return self._testPrivateNames()
if __name__ == '__main__':
rospy.init_node(NAME)
rostest.rosrun(PKG, NAME, PsPrivateNamesTestCase, sys.argv)

View File

@@ -0,0 +1,50 @@
#!/usr/bin/env python
# Software License Agreement (BSD License)
#
# Copyright (c) 2008, Willow Garage, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of Willow Garage, Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
PKG = 'test_rosmaster'
NAME = 'test_ps_scope_down'
import sys
import rospy
import rostest
from param_server import ParamServerTestCase
class PsScopeDownTestCase(ParamServerTestCase):
def testScopeDown(self):
return self._testScopeDown()
if __name__ == '__main__':
rospy.init_node(NAME)
rostest.rosrun(PKG, NAME, PsScopeDownTestCase, sys.argv)

View File

@@ -0,0 +1,50 @@
#!/usr/bin/env python
# Software License Agreement (BSD License)
#
# Copyright (c) 2008, Willow Garage, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of Willow Garage, Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
PKG = 'test_rosmaster'
NAME = 'test_ps_scope_up'
import sys
import rospy
import rostest
from param_server import ParamServerTestCase
class PsScopeUpTestCase(ParamServerTestCase):
def testScopeUp(self):
return self._testScopeUp()
if __name__ == '__main__':
rospy.init_node(NAME)
rostest.rosrun(PKG, NAME, PsScopeUpTestCase, sys.argv)

View File

@@ -0,0 +1,50 @@
#!/usr/bin/env python
# Software License Agreement (BSD License)
#
# Copyright (c) 2008, Willow Garage, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of Willow Garage, Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
PKG = 'test_rosmaster'
NAME = 'test_ps_values'
import sys
import rospy
import rostest
from param_server_test_case import ParamServerTestCase
class PsSearchParamTestCase(ParamServerTestCase):
def testSearchParam(self):
return self._testSearchParam()
if __name__ == '__main__':
rospy.init_node(NAME)
rostest.rosrun(PKG, NAME, PsSearchParamTestCase, sys.argv)

View File

@@ -0,0 +1,50 @@
#!/usr/bin/env python
# Software License Agreement (BSD License)
#
# Copyright (c) 2008, Willow Garage, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of Willow Garage, Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
PKG = 'test_rosmaster'
NAME = 'test_ps_has_param'
import sys
import rospy
import rostest
from param_server_test_case import ParamServerTestCase
class PsSetParamTestCase(ParamServerTestCase):
def testSetParam(self):
return self._testSetParam()
if __name__ == '__main__':
rospy.init_node(NAME)
rostest.rosrun(PKG, NAME, PsSetParamTestCase, sys.argv)

View File

@@ -0,0 +1,50 @@
#!/usr/bin/env python
# Software License Agreement (BSD License)
#
# Copyright (c) 2008, Willow Garage, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of Willow Garage, Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
PKG = 'test_rosmaster'
NAME = 'test_ps_values'
import sys
import rospy
import rostest
from param_server_test_case import ParamServerTestCase
class PsValuesTestCase(ParamServerTestCase):
def testParamValues(self):
return self._testParamValues()
if __name__ == '__main__':
rospy.init_node(NAME)
rostest.rosrun(PKG, NAME, PsValuesTestCase, sys.argv)