diff --git a/pyros_httpbin/CMakeLists.txt b/pyros_httpbin/CMakeLists.txt
index 1ce36c3..8d3db3f 100644
--- a/pyros_httpbin/CMakeLists.txt
+++ b/pyros_httpbin/CMakeLists.txt
@@ -70,10 +70,10 @@ catkin_pip_package(pyros_httpbin)
#######
if (CATKIN_ENABLE_TESTING)
- catkin_add_pytests(tests/test_httpbin.py)
+ # catkin_add_pytests(tests/test_httpbin.py)
# Removing. somehow rostest cannot run the httpbin node. Rostest is too buggy we should drop it.
#find_package(rostest REQUIRED)
- #add_rostest(tests/test_httpbin.test)
+ catkin_add_nosetests(tests/test_httpbin.py)
endif()
#####
@@ -83,5 +83,8 @@ endif()
install(
PROGRAMS
nodes/httpbin.py
+ nodes/leaktest.py
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
+
+install(DIRECTORY launch DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
diff --git a/pyros_httpbin/README.md b/pyros_httpbin/README.md
index 36a7169..fc3b904 100644
--- a/pyros_httpbin/README.md
+++ b/pyros_httpbin/README.md
@@ -10,3 +10,36 @@ In particular we need to find out how to mix and match:
- library to easily build ros-web client (and zmp-web ?)
- test for a ros-web client using httpbin
- gopher software application (for yujinrobot)
+
+---
+## test
+
+mkdir build
+
+cd build & cmake ../ & make & source devel/setup.bash & make test
+
+or
+
+py.test ../tests/test_httpbin.py -vv
+
+
+## test for memory leak
+
+source devel/setup.bash & cd ../scripts & ./leaktest.sh
+
+or
+
+roslaunch pyros_httpbin leaktest.launch --screen
+
+## Troubleshooting
+
+easy_install netaddr, when occur no module netaddr
+
+git clone https://github.com/pyros-dev/pyros-setup.git & cd pyros-setup & python setup.py install, when occur no module pyros_setup
+
+cd pyros_schemas-examples/build & source devel/setup.bash, when occur path error
+
+## Docker
+
+docker run -it --rm --name yujin-d3-devel-memory_leak_test --network=host zaxrok/yujin-d3:devel-memory_leak_test bash
+
diff --git a/pyros_httpbin/launch/leaktest.launch b/pyros_httpbin/launch/leaktest.launch
new file mode 100644
index 0000000..5532cd4
--- /dev/null
+++ b/pyros_httpbin/launch/leaktest.launch
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/pyros_httpbin/nodes/fixed_leaktest.py b/pyros_httpbin/nodes/fixed_leaktest.py
new file mode 100755
index 0000000..4ac861a
--- /dev/null
+++ b/pyros_httpbin/nodes/fixed_leaktest.py
@@ -0,0 +1,161 @@
+#!/usr/bin/env python
+#
+# License: Yujin
+#
+##############################################################################
+# Documentation
+##############################################################################
+"""
+Simple utility to start a gopher scheduler ROS node from the command line.
+"""
+##############################################################################
+# Imports
+##############################################################################
+import os
+import psutil
+import sys
+import argparse
+import json
+import requests
+sys.path.insert(0, '/home/bcc/Tutorial/playbooks/gopher_docker/gopher_webclients/install/lib/python2.7/dist-packages')
+try:
+ import rocon_console.console as console
+ import pyros_schemas
+ import pyros_httpbin
+
+ import rospy
+ from pyros_httpbin.srv import HttpbinPostJson, HttpbinPostJsonRequest, HttpbinPostJsonResponse
+ from pyros_httpbin.msg import HttpRequestHeaders, HttpbinPostArgs, HttpbinPostBody, HttpbinPostBody2
+ import pyros_msgs.opt_as_array # This will duck punch the standard message type initialization code.
+
+except ImportError:
+ # Because we need to access Ros message types here (from ROS env or from virtualenv, or from somewhere else)
+ import pyros_setup
+ # We rely on default configuration in the environment to point us ot the proper distro and workspace
+ pyros_setup.configurable_import().configure().activate()
+
+ import rocon_console.console as console
+ import pyros_schemas
+ import pyros_httpbin
+
+ import rospy
+ from pyros_httpbin.srv import HttpbinPostJson, HttpbinPostJsonRequest, HttpbinPostJsonResponse
+ from pyros_httpbin.msg import HttpRequestHeaders, HttpbinPostArgs, HttpbinPostBody, HttpbinPostBody2
+ import pyros_msgs.opt_as_array # This will duck punch the standard message type initialization code.
+
+
+# patching messages types with optional fields
+pyros_msgs.opt_as_array.duck_punch(HttpRequestHeaders, [
+ 'User_Agent',
+ 'Accept',
+ 'Accept_Encoding',
+ 'Accept_Language',
+ 'Host',
+ 'Referer',
+ 'Upgrade_Insecure_Requests',
+])
+
+pyros_msgs.opt_as_array.duck_punch(HttpbinPostArgs, ['argopt'])
+pyros_msgs.opt_as_array.duck_punch(HttpbinPostJson._request_class, ['headers'])
+pyros_msgs.opt_as_array.duck_punch(HttpbinPostArgs, ['argopt'])
+pyros_msgs.opt_as_array.duck_punch(HttpbinPostBody, ['testoptitem'])
+pyros_msgs.opt_as_array.duck_punch(HttpbinPostBody2, ['subtestoptstring', 'subtestoptint', 'subtestoptfloat'])
+##############################################################################
+# Helpers
+##############################################################################
+
+class StatusCodeException(Exception):
+ pass
+
+class TestPyrosSchemas(object):
+
+ def __init__(self):
+ rospy.Service('/test/pyros_schemas', HttpbinPostJson, self.test_pyros_schemas2)
+ rospy.wait_for_service('/test/pyros_schemas', timeout=10)
+ self.test_service_proxy = rospy.ServiceProxy('/test/pyros_schemas', HttpbinPostJson)
+
+ @pyros_schemas.with_service_schemas(HttpbinPostJson)
+ def test_pyros_schemas2(self, data, data_dict, errors):
+ print (" => {0}".format(data_dict)) # to help with debugging
+ h = data_dict.get('headers', {})
+ h.update({"Content-type": "application/json"})
+ p = data_dict.get('params')
+ d = data_dict.get('json')
+ response = requests.post('http://httpbin.org/post', headers=h, params=p, data=json.dumps(d))
+
+ if response.status_code == requests.status_codes.codes.OK: # TODO : easy way to check all "OK" codes
+ print (" <= {0}".format(response.json()))
+ return response.json()
+ else:
+ raise StatusCodeException(response.status_code)
+
+ def test_pyros_schemas(self, req):
+ resp = HttpbinPostJsonResponse()
+ return resp
+
+ def spin(self):
+ count = 0
+ pid = os.getpid()
+ process = psutil.Process(pid)
+ while not rospy.core.is_shutdown():
+ ros_data = pyros_httpbin.msg.HttpbinPostBody(
+ testitem=pyros_httpbin.msg.HttpbinPostBody2(
+ subteststring='teststr',
+ # subtestoptstring='', optional, lets not care about it
+ subteststringarray=['str1', 'str2', 'str3'],
+ subtestint=42,
+ # subtestoptint=21, #optional, lets not care about it
+ subtestintarray=[4, 2, 1],
+ subtestfloat=42.,
+ # subtestoptfloat=21., #optional, lets not care about it
+ subtestfloatarray=[4., 2., 1.],
+ ),
+ # testoptitem optional lets not care about it
+ testitemarray=[
+ pyros_httpbin.msg.HttpbinPostBody2(
+ subteststring='teststr1',
+ # subtestoptstring='', optional, lets not care about it
+ subteststringarray=['str1', 'str2', 'str3'],
+ subtestint=42,
+ # subtestoptint=21, #optional, lets not care about it
+ subtestintarray=[4, 2, 1],
+ subtestfloat=42.,
+ # subtestoptfloat=21., #optional, lets not care about it
+ subtestfloatarray=[4., 2., 1.],
+ ),
+ pyros_httpbin.msg.HttpbinPostBody2(
+ subteststring='teststr2',
+ # subtestoptstring='', optional, lets not care about it
+ subteststringarray=['str1', 'str2', 'str3'],
+ subtestint=42,
+ # subtestoptint=21, #optional, lets not care about it
+ subtestintarray=[4, 2, 1],
+ subtestfloat=42.,
+ # subtestoptfloat=21., #optional, lets not care about it
+ subtestfloatarray=[4., 2., 1.],
+ ),
+ ]
+ )
+ req = pyros_httpbin.srv.HttpbinPostJson._request_class(
+ params=pyros_httpbin.msg.HttpbinPostArgs(
+ arg='testarg',
+ # argopt='', # optional, let not care about it
+ arglist=['arg1', 'arg2']
+ # httpbin removes the list if only one arg here, but we do expect list in response.
+ # TODO : fix this...
+ ),
+ # headers=pyros_httpbin.msg.HttpRequestHeaders(), # optional, let not care about it
+ json=ros_data
+ )
+ resp = self.test_service_proxy(req)
+ rospy.loginfo("COUNT: {0}".format(count))
+ rospy.loginfo("RESP: {0}".format(resp))
+ count += 1
+ mem = process.get_memory_info()[0]
+ rospy.loginfo("Memory usage(%s):%d" % (pid, mem))
+ rospy.sleep(0.01)
+
+if __name__ == '__main__':
+ rospy.init_node('test_pyros_schemas', log_level=rospy.INFO)
+ _node = TestPyrosSchemas()
+ _node.spin()
diff --git a/pyros_httpbin/nodes/leaktest.py b/pyros_httpbin/nodes/leaktest.py
new file mode 100755
index 0000000..68cf993
--- /dev/null
+++ b/pyros_httpbin/nodes/leaktest.py
@@ -0,0 +1,346 @@
+#!/usr/bin/env python
+#
+# License: Yujin
+#
+##############################################################################
+# Documentation
+##############################################################################
+"""
+Simple utility to start a gopher scheduler ROS node from the command line.
+"""
+##############################################################################
+# Imports
+##############################################################################
+import os
+import psutil
+import sys
+import argparse
+import json
+import requests
+try:
+ import rocon_console.console as console
+ import pyros_schemas
+ import pyros_httpbin
+
+ import rospy
+ from pyros_httpbin.srv import HttpbinPostJson, HttpbinPostJsonRequest, HttpbinPostJsonResponse
+ from pyros_httpbin.msg import HttpRequestHeaders, HttpbinPostArgs, HttpbinPostBody, HttpbinPostBody2
+ import pyros_msgs.opt_as_array # This will duck punch the standard message type initialization code.
+
+except ImportError:
+ # Because we need to access Ros message types here (from ROS env or from virtualenv, or from somewhere else)
+ import pyros_setup
+ # We rely on default configuration in the environment to point us ot the proper distro and workspace
+ pyros_setup.configurable_import().configure().activate()
+
+ import rocon_console.console as console
+ import pyros_schemas
+ import pyros_httpbin
+
+ import rospy
+ from pyros_httpbin.srv import HttpbinPostJson, HttpbinPostJsonRequest, HttpbinPostJsonResponse
+ from pyros_httpbin.msg import HttpRequestHeaders, HttpbinPostArgs, HttpbinPostBody, HttpbinPostBody2
+ import pyros_msgs.opt_as_array # This will duck punch the standard message type initialization code.
+
+
+# patching messages types with optional fields
+pyros_msgs.opt_as_array.duck_punch(HttpRequestHeaders, [
+ 'User_Agent',
+ 'Accept',
+ 'Accept_Encoding',
+ 'Accept_Language',
+ 'Host',
+ 'Referer',
+ 'Upgrade_Insecure_Requests',
+])
+
+pyros_msgs.opt_as_array.duck_punch(HttpbinPostArgs, ['argopt'])
+pyros_msgs.opt_as_array.duck_punch(HttpbinPostJson._request_class, ['headers'])
+pyros_msgs.opt_as_array.duck_punch(HttpbinPostArgs, ['argopt'])
+pyros_msgs.opt_as_array.duck_punch(HttpbinPostBody, ['testoptitem'])
+pyros_msgs.opt_as_array.duck_punch(HttpbinPostBody2, ['subtestoptstring', 'subtestoptint', 'subtestoptfloat'])
+##############################################################################
+# Helpers
+##############################################################################
+
+class StatusCodeException(Exception):
+ pass
+
+from pyros_schemas.ros.schemagic import create
+
+import six
+from pyros_schemas.ros.types_mapping import ros_msgtype_mapping
+from pyros_schemas.ros.utils import _get_msg_class, _get_rosmsg_fields_as_dict, _get_rosmsg_members_as_dict
+from pyros_schemas.ros.schema import RosSchema, pre_load, post_load, pre_dump, post_dump
+from pyros_schemas.ros.basic_fields import (
+ RosNested,
+ RosList,
+)
+from pyros_schemas.ros.optional_fields import (
+ RosOpt,
+)
+
+import marshmallow
+
+# Statically proxying marshmallow useful decorators for methods
+pre_load = marshmallow.pre_load
+post_load = marshmallow.post_load
+pre_dump = marshmallow.pre_dump
+post_dump = marshmallow.post_dump
+
+from pyros_schemas.ros.exceptions import PyrosSchemasValidationError
+
+class RosSchema(marshmallow.Schema):
+ """Inheriting the Marshmallow schema to extend behavior introspecting into slots for ROS messages
+ Not using pre_load, post_load, pre_dump or post_dump here, to simplify things for when we need to create schemas dynamically.
+ pre_load, post_load, pre_dump, post_dump should still be used in derived Schemas, to customize the serialization
+ This class only factor serialization behavior required by ROS generated message types.
+ """
+
+ _valid_ros_msgtype = None # fill this in your Schema class for enforcing msgtype validation on load
+ _generated_ros_msgtype = None # fill this in your Schema class for automatically generating msgtype on dump
+
+ def __init__(self, strict=True, **kwargs): # default to strict behavior
+ super(RosSchema, self).__init__(strict=strict, **kwargs)
+
+ def load(self, data, many=None, partial=None):
+ """Overloading load function to transform a ROS msg type into a dict for marshmallow"""
+ # early type validation if required
+ if self.strict and self._valid_ros_msgtype and not isinstance(data, self._valid_ros_msgtype):
+ raise PyrosSchemasValidationError('data type should be {0}'.format(self._valid_ros_msgtype))
+ data_dict = _get_rosmsg_members_as_dict(data)
+ try:
+ unmarshal_result = super(RosSchema, self).load(data_dict, many=many, partial=partial)
+ except marshmallow.ValidationError as ve:
+ raise PyrosSchemasValidationError('ERROR occurred during deserialization: {ve}'.format(**locals()))
+ return unmarshal_result
+
+ def dump(self, obj, many=None, update_fields=True, **kwargs):
+ """Overloading dump function to transform a dict into a ROS msg from marshmallow"""
+ try:
+ obj_dict = _get_rosmsg_members_as_dict(obj) # in case we get something that is not a dict...
+ # because ROS field naming conventions are different than python dict key conventions
+ obj_rosfixed_dict = {k.replace('-', '_'): v for k, v in obj_dict.items()} # TODO : come up with a generic function
+ data_dict, errors = super(RosSchema, self).dump(obj_rosfixed_dict, many=many, update_fields=update_fields, **kwargs)
+ except marshmallow.ValidationError as ve:
+ raise PyrosSchemasValidationError('ERROR occurred during serialization: {ve}'.format(**locals()))
+ if self._generated_ros_msgtype and not errors:
+ obj = self._generated_ros_msgtype(**data_dict)
+ else:
+ obj = data_dict # we return directly
+ return marshmallow.MarshalResult(obj, errors)
+
+
+# TODO : find cleaner way, maybe a RosMagikSchema class like thing...
+def create(ros_msg_class,
+ pre_load_fun=None,
+ post_load_fun=None,
+ pre_dump_fun=None,
+ post_dump_fun=None,
+ **kwargs):
+ """
+ Factory method that creates a Schema class for this ROS message type by introspecting the ros_msg_class, and then instanciate it.
+ :param ros_msg_class: the message class for which we need serialization. It can be the string specifying the message type or the type itself
+ :param pre_load_fun: a callable that will be run before load(). It should be of the form : schema, data -> data
+ :param post_load_fun: a callable that will be run after load(). It should be of the form : schema, data -> data
+ Note that type validation is already implemented internally. check with_explicitly_matched_type decorator for more details
+ :param pre_dump_fun: a callable that will be run before dump(). It should be of the form : schema, data -> data
+ Note that type validation is already implemented internally. check with_explicitly_matched_type decorator for more details
+ :param post_dump_fun: a callable that will be run after dump(). It should be of the form : schema, data -> data
+ :param kwargs: any keyword argument will be added to the schema class.
+ :return: A Schema that handles all (dict --load()--> ros_msg_class --dump()--> dict) serialization
+ """
+
+ if isinstance(ros_msg_class, six.string_types): # if we get a string it s a ros description, not the class itself
+ ros_msg_class = _get_msg_class(ros_msg_class)
+ # and keep going
+
+ members_types = _get_rosmsg_fields_as_dict(ros_msg_class)
+ members = {}
+ schema_instance = RosSchema()
+ for s, stype in members_types.iteritems():
+ # Note here we rely entirely on _opt_slots from the class to be set properly
+ # for both Nested or List representation of optional fields
+ ros_schema_inst = None
+ if stype.endswith("[]"):
+ if stype[:-2] in ros_msgtype_mapping:
+ # ENDING RECURSION with well known array type
+ if hasattr(ros_msg_class, '_opt_slots') and s in ros_msg_class._opt_slots:
+ ros_schema_inst = RosOpt(ros_msgtype_mapping[stype[:-2]]())
+ else:
+ ros_schema_inst = RosList(ros_msgtype_mapping[stype[:-2]]())
+ else:
+ # RECURSING in Nested fields
+ if hasattr(ros_msg_class, '_opt_slots') and s in ros_msg_class._opt_slots:
+ ros_schema_inst = RosOpt(RosNested(create(stype[:-2]))) # we need to nest the next (Ros)Schema
+ else:
+ ros_schema_inst = RosList(RosNested(create(stype[:-2]))) # we need to nest the next (Ros)Schema
+ else:
+ if stype in ros_msgtype_mapping:
+ # ENDING RECURSION with well known basic type
+ ros_schema_inst = ros_msgtype_mapping[stype]() # TODO : shouldn't we check for opt slots here ?
+ else:
+ # RECURSING in Nested fields
+ if hasattr(ros_msg_class, '_opt_slots') and s in ros_msg_class._opt_slots:
+ ros_schema_inst = RosOpt(create(stype))
+ else:
+ ros_schema_inst = RosNested(create(stype)) # we need to nest the next (Ros)Schema
+
+ members.setdefault(s, ros_schema_inst)
+ schema_instance.declared_fields[s] = ros_schema_inst
+ schema_instance.fields[s] = ros_schema_inst
+ # supporting extra customization of the serialization
+ if pre_load_fun:
+ schema_instance.declared_fields['_helper_pre_load'] = pre_load(pre_load_fun)
+ if post_load_fun:
+ schema_instance.declared_fields['_helper_post_load'] = post_load(post_load_fun)
+ if pre_dump_fun:
+ schema_instance.declared_fields['_helper_pre_dump'] = pre_dump(pre_dump_fun)
+ if post_dump_fun:
+ schema_instance.declared_fields['_helper_post_dump'] = post_dump(post_dump_fun)
+
+ # adding extra members if needed
+ for k, v in kwargs:
+ schema_instance.declared_fields[k] = v
+ #members[k] = v
+
+ #members['_valid_ros_msgtype'] = ros_msg_class
+ #members['_generated_ros_msgtype'] = ros_msg_class
+
+ schema_instance._valid_ros_msgtype = ros_msg_class
+ schema_instance._generated_ros_msgtype = ros_msg_class
+ #rosobj.__class__.__name__ = ros_msg_class.__name__ + 'Schema'
+
+
+ # MsgSchema = type(ros_msg_class.__name__ + 'Schema', (RosSchema,), members)
+ # schema_instance = MsgSchema()
+
+ #print('2', rosobj.__dict__)
+
+ #return schema_instance
+ return schema_instance
+
+from pyros_schemas.ros.exceptions import PyrosSchemasServiceRequestException, PyrosSchemasServiceResponseException
+
+from functools import wraps
+def with_service_schemas(service_class):
+ def with_service_schemas_decorator(func):
+ @wraps(func)
+ # TODO : handle funcitons AND methods ?
+ def func_wrapper(*data): # we need to expose only one argument for ROS or two for methods
+
+ try:
+ request_schema = create(service_class._request_class)
+ data_dict, errors = request_schema.load(data[-1]) # we assume the last argument always contains the ROS data
+ # print('......', data[-1], data_dict, errors)
+ except Exception as e:
+ raise PyrosSchemasServiceRequestException(e)
+
+ # we should call the function with original and parsed argument,
+ # including potential errors, just in case, at least temporarily...
+ data_extended = data + (data_dict, errors)
+ response = func(*data_extended)
+ # we also let the function trigger its own exceptions
+
+ try:
+ response_schema = create(service_class._response_class)
+ response_ros, errors = response_schema.dump(response)
+ return response_ros
+ except Exception as e:
+ raise PyrosSchemasServiceResponseException(e)
+
+ return func_wrapper
+ return with_service_schemas_decorator
+
+class TestPyrosSchemas(object):
+
+ def __init__(self):
+ rospy.Service('/test/pyros_schemas', HttpbinPostJson, self.test_pyros_schemas2)
+ rospy.wait_for_service('/test/pyros_schemas', timeout=10)
+ self.test_service_proxy = rospy.ServiceProxy('/test/pyros_schemas', HttpbinPostJson)
+
+ @with_service_schemas(HttpbinPostJson)
+ def test_pyros_schemas2(self, data, data_dict, errors):
+ print (" => {0}".format(data_dict)) # to help with debugging
+ h = data_dict.get('headers', {})
+ h.update({"Content-type": "application/json"})
+ p = data_dict.get('params')
+ d = data_dict.get('json')
+ response = requests.post('http://httpbin.org/post', headers=h, params=p, data=json.dumps(d))
+
+ if response.status_code == requests.status_codes.codes.OK: # TODO : easy way to check all "OK" codes
+ print (" <= {0}".format(response.json()))
+ return response.json()
+ else:
+ raise StatusCodeException(response.status_code)
+
+ def test_pyros_schemas(self, req):
+ resp = HttpbinPostJsonResponse()
+ return resp
+
+ def spin(self):
+ count = 0
+ pid = os.getpid()
+ process = psutil.Process(pid)
+ while not rospy.core.is_shutdown():
+ ros_data = pyros_httpbin.msg.HttpbinPostBody(
+ testitem=pyros_httpbin.msg.HttpbinPostBody2(
+ subteststring='teststr',
+ # subtestoptstring='', optional, lets not care about it
+ subteststringarray=['str1', 'str2', 'str3'],
+ subtestint=42,
+ # subtestoptint=21, #optional, lets not care about it
+ subtestintarray=[4, 2, 1],
+ subtestfloat=42.,
+ # subtestoptfloat=21., #optional, lets not care about it
+ subtestfloatarray=[4., 2., 1.],
+ ),
+ # testoptitem optional lets not care about it
+ testitemarray=[
+ pyros_httpbin.msg.HttpbinPostBody2(
+ subteststring='teststr1',
+ # subtestoptstring='', optional, lets not care about it
+ subteststringarray=['str1', 'str2', 'str3'],
+ subtestint=42,
+ # subtestoptint=21, #optional, lets not care about it
+ subtestintarray=[4, 2, 1],
+ subtestfloat=42.,
+ # subtestoptfloat=21., #optional, lets not care about it
+ subtestfloatarray=[4., 2., 1.],
+ ),
+ pyros_httpbin.msg.HttpbinPostBody2(
+ subteststring='teststr2',
+ # subtestoptstring='', optional, lets not care about it
+ subteststringarray=['str1', 'str2', 'str3'],
+ subtestint=42,
+ # subtestoptint=21, #optional, lets not care about it
+ subtestintarray=[4, 2, 1],
+ subtestfloat=42.,
+ # subtestoptfloat=21., #optional, lets not care about it
+ subtestfloatarray=[4., 2., 1.],
+ ),
+ ]
+ )
+ req = pyros_httpbin.srv.HttpbinPostJson._request_class(
+ params=pyros_httpbin.msg.HttpbinPostArgs(
+ arg='testarg',
+ # argopt='', # optional, let not care about it
+ arglist=['arg1', 'arg2']
+ # httpbin removes the list if only one arg here, but we do expect list in response.
+ # TODO : fix this...
+ ),
+ # headers=pyros_httpbin.msg.HttpRequestHeaders(), # optional, let not care about it
+ json=ros_data
+ )
+ resp = self.test_service_proxy(req)
+ rospy.loginfo("COUNT: {0}".format(count))
+ rospy.loginfo("RESP: {0}".format(resp))
+ count += 1
+ mem = process.get_memory_info()[0]
+ rospy.loginfo("Memory usage(%s):%d" % (pid, mem))
+ rospy.sleep(0.01)
+
+if __name__ == '__main__':
+ rospy.init_node('test_pyros_schemas', log_level=rospy.INFO)
+ _node = TestPyrosSchemas()
+ _node.spin()
diff --git a/pyros_httpbin/scripts/leaktest.sh b/pyros_httpbin/scripts/leaktest.sh
new file mode 100755
index 0000000..95ca07d
--- /dev/null
+++ b/pyros_httpbin/scripts/leaktest.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+roslaunch pyros_httpbin leaktest.launch & export APP_PID=$!
+echo "pid: $APP_PID"
+top -b -n 1000 -p $APP_PID | grep $APP_PID &
+#konsole -e htop -p $APP_PID
+
diff --git a/pyros_httpbin/scripts/toppid.sh b/pyros_httpbin/scripts/toppid.sh
new file mode 100755
index 0000000..8dd5d62
--- /dev/null
+++ b/pyros_httpbin/scripts/toppid.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+export APP_PID=$1
+echo "pid: $APP_PID"
+top -b -n 1000 -p $APP_PID | grep $APP_PID &
+#konsole -e htop -p $APP_PID
+
diff --git a/pyros_httpbin/tests/test_httpbin.py b/pyros_httpbin/tests/test_httpbin.py
index d5da7db..9b4c320 100755
--- a/pyros_httpbin/tests/test_httpbin.py
+++ b/pyros_httpbin/tests/test_httpbin.py
@@ -29,6 +29,7 @@
import logging
import netaddr
import marshmallow
+import pytest
# test node process not setup by default (rostest dont need it here)
httpbin_process = None
@@ -68,7 +69,6 @@ def teardown_module():
pyros_utils.rostest_nose.rostest_nose_teardown_module()
-
class TestHttpBin(unittest.TestCase):
def test1_ip(self):