Пример #1
0
    def setUp(self):
        self.connection_cache_node = roslaunch.core.Node(
            'rocon_python_comms',
            'connection_cache.py',
            name='connection_cache',
            remap_args=[
                ('~list', '/pyros_ros/connections_list'),
                ('~diff', '/pyros_ros/connections_diff'),
            ])
        try:
            self.connection_cache_proc = self.launch.launch(
                self.connection_cache_node)
        except roslaunch.RLException as rlexc:
            raise nose.SkipTest(
                "Connection Cache Node not found (part of rocon_python_comms pkg). Skipping test."
            )

        node_api = None
        with timeout(5) as t:
            while not t.timed_out and node_api is None:
                node_api = rosnode.get_api_uri(rospy.get_master(),
                                               'connection_cache')

        assert node_api is not None  # make sure the connection cache node is started before moving on.

        super(TestPyrosROSCache, self).setUp(enable_cache=True)
Пример #2
0
    def setUp(self):
        self.connection_cache_node = roslaunch.core.Node(
            'rocon_python_comms',
            'connection_cache.py',
            name='connection_cache',
            remap_args=[('~list', rospy.resolve_name('~connections_list')),
                        ('~diff', rospy.resolve_name('~connections_diff'))])
        # Easier to remap the node topic to the proxy ones, instead of the opposite, since there is no dynamic remapping.
        # However for normal usecase, remapping the proxy handles is preferable.
        try:
            self.connection_cache_proc = TestRosInterface.launch.launch(
                self.connection_cache_node)
        except roslaunch.RLException as rlexc:
            raise nose.SkipTest(
                "Connection Cache Node not found (part of rocon_python_comms pkg). Skipping test."
            )

        assert self.connection_cache_proc.is_alive()

        # wait for node to be started
        node_api = None
        with timeout(5) as t:
            while not t.timed_out and node_api is None:
                node_api = rosnode.get_api_uri(rospy.get_master(),
                                               'connection_cache')

        assert node_api is not None  # make sure the connection cache node is started before moving on.

        self.strpub = rospy.Publisher('/test/string', String, queue_size=1)
        self.emppub = rospy.Publisher('/test/empty', Empty, queue_size=1)

        self.interface = RosInterface(True)
Пример #3
0
    def test_service_appear_expose_update(self):
        """
        Test service exposing functionality for a service which already exists in
        the ros environment. Simple Normal usecase
        Sequence : APPEAR -> EXPOSE -> UPDATE
        :return:
        """

        servicename = '/test/empsrv'
        dt = self.interface.expose_services([servicename])
        # every added service should be in the list of args
        self.assertTrue(servicename in self.interface.services_args)
        # service backend has not been created since the update didn't run yet
        self.assertTrue(servicename not in self.interface.services.keys())

        # NOTE : We need to wait to make sure the tests nodes are started...
        with timeout(5) as t:
            while not t.timed_out and servicename not in dt[0]:
                dt = self.interface.update()

        # TODO : improve that by providing an easier interface for it.

        # every exposed service should remain in the list of args ( in case regex match another service )
        self.assertTrue(servicename in self.interface.services_args)
        # make sure the service backend has been created
        self.assertTrue(servicename in self.interface.services.keys())
Пример #4
0
        def appear_disappear():
            # create the publisher and then try exposing the topic again, simulating
            # it coming online before expose call.
            nonexistent_pub = TopicBack._create_pub(topicname, Empty, queue_size=1)

            with timeout(5) as t:
                dt = DiffTuple([], [])
                while not t.timed_out and nonexistent_pub.resolved_name not in dt.added:
                    dt = self.interface.update()
                    self.assertEqual(dt.removed, [])  # nothing removed

            self.assertTrue(not t.timed_out)
            self.assertTrue(nonexistent_pub.resolved_name in dt.added)  # detected
            # TODO : do we need a test with subscriber ?

            # every added topic should be in the list of args
            self.assertTrue(topicname in self.interface.topics_args)
            # topic backend has been created
            self.assertTrue(topicname in self.interface.topics.keys())

            # up to here possible sequences should have been already tested by previous tests
            # Now comes our actual disappearance / withholding test
            TopicBack._remove_pub(nonexistent_pub)

            # every added topic should be in the list of args
            self.assertTrue(topicname in self.interface.topics_args)
            # the backend should STILL be there
            self.assertTrue(topicname in self.interface.topics.keys())
            # Note the Topic implementation should take care of possible errors in this case

            with timeout(5) as t:
                dt = DiffTuple([], [])
                while not t.timed_out and topicname not in dt.removed:
                    dt = self.interface.update()
                    self.assertEqual(dt.added, [])  # nothing added

            self.assertTrue(not t.timed_out)
            self.assertTrue(topicname in dt.removed)  # detected lost
            # every exposed topic should remain in the list of args ( in case regex match another topic )
            self.assertTrue(topicname in self.interface.topics_args)
            # make sure the topic backend should NOT be there any longer
            self.assertTrue(topicname not in self.interface.topics.keys())
Пример #5
0
    def test_service_expose_appear_update(self):
        """
        Test basic service adding functionality for a service which does not yet exist
        in the ros environment ( + corner cases )
        Sequence : (UPDATE? ->) -> EXPOSE -> (UPDATE? ->) APPEAR -> UPDATE
        :return:
        """
        servicename = '/test/absentsrv1'
        # every added service should be in the list of args
        self.assertTrue(servicename not in self.interface.services_args)
        # the backend should not have been created
        self.assertTrue(servicename not in self.interface.services.keys())
        # First update should not change state
        dt = self.interface.update()
        self.assertEqual(dt.added, [])  # nothing added
        self.assertEqual(dt.removed, [])  # nothing removed
        # every added service should be in the list of args
        self.assertTrue(servicename not in self.interface.services_args)
        # the backend should not have been created
        self.assertTrue(servicename not in self.interface.services.keys())

        self.interface.expose_services([servicename])
        # every added service should be in the list of args
        self.assertTrue(servicename in self.interface.services_args)
        # the backend should not have been created
        self.assertTrue(servicename not in self.interface.services.keys())
        dt = self.interface.update()
        self.assertEqual(dt.added, [])  # nothing added
        self.assertEqual(dt.removed, [])  # nothing removed
        # make sure the service is STILL in the list of args
        self.assertTrue(servicename in self.interface.services_args)
        # make sure the service backend has STILL not been created
        self.assertTrue(servicename not in self.interface.services.keys())

        # create the service and then try updating again, simulating
        # it coming online after expose call.
        nonexistent_srv = rospy.Service(servicename, EmptySrv, srv_cb)
        try:
            with timeout(5) as t:
                dt = DiffTuple([], [])
                while not t.timed_out and nonexistent_srv.resolved_name not in dt.added:
                    dt = self.interface.update()
                    self.assertEqual(dt.removed, [])  # nothing removed

            self.assertTrue(nonexistent_srv.resolved_name in dt.added)  # nonexistent_srv added
            # every exposed service should remain in the list of args ( in case regex match another service )
            self.assertTrue(servicename in self.interface.services_args)
            # make sure the service backend has been created
            self.assertTrue(servicename in self.interface.services.keys())
        finally:
            nonexistent_srv.shutdown('testing complete')
Пример #6
0
    def test_topic_expose_appear_update(self):
        """
        Test basic topic adding functionality for a topic which does not yet exist
        in the ros environment ( + corner cases )
        Sequence : (UPDATE? ->) -> EXPOSE -> (UPDATE? ->) APPEAR -> UPDATE
        :return:
        """
        topicname = '/test/nonexistent2'
        # every added topic should be in the list of args
        self.assertTrue(topicname not in self.interface.topics_args)
        # the backend should not have been created
        self.assertTrue(topicname not in self.interface.topics.keys())
        # First update should not change state
        dt = self.interface.update()
        self.assertTrue(topicname not in dt.added)  # not detected
        # every added topic should be in the list of args
        self.assertTrue(topicname not in self.interface.topics_args)
        # the backend should not have been created
        self.assertTrue(topicname not in self.interface.topics.keys())

        self.interface.expose_topics([topicname])
        # every added topic should be in the list of args
        self.assertTrue(topicname in self.interface.topics_args)
        # the backend should not have been created
        self.assertTrue(topicname not in self.interface.topics.keys())
        dt = self.interface.update()
        self.assertTrue(topicname not in dt.added)  # not detected
        # make sure the topic is STILL in the list of args
        self.assertTrue(topicname in self.interface.topics_args)
        # make sure the topic backend has STILL not been created
        self.assertTrue(topicname not in self.interface.topics.keys())

        # create the publisher and then try updating again, simulating
        # it coming online after expose call.
        nonexistent_pub = rospy.Publisher(topicname, Empty, queue_size=1)
        with timeout(5) as t:
            while not t.timed_out and topicname not in dt.added:
                dt = self.interface.update()
                self.assertEqual(dt.removed, [])  # nothing removed

        self.assertTrue(not t.timed_out)
        self.assertTrue(topicname in dt.added)  # detected
        # TODO : do we need a test with subscriber ?

        # every exposed topic should remain in the list of args ( in case regex match another topic )
        self.assertTrue(topicname in self.interface.topics_args)
        # make sure the topic backend has been created
        self.assertTrue(topicname in self.interface.topics.keys())

        nonexistent_pub.unregister()  # https://github.com/ros/ros_comm/issues/111 ( topic is still registered on master... )
Пример #7
0
    def test_service_appear_update_expose(self):
        """
        Test service exposing functionality for a service which already exists in
        the ros environment. Normal usecase
        Sequence : (UPDATE?) -> APPEAR -> UPDATE -> EXPOSE (-> UPDATE?)
        :return:
        """
        servicename = '/test/absentsrv1'
        # every added service should be in the list of args
        self.assertTrue(servicename not in self.interface.services_args)
        # the backend should not have been created
        self.assertTrue(servicename not in self.interface.services.keys())
        # First update should not change state
        dt = self.interface.update()
        self.assertEqual(dt.added, [])  # nothing added
        self.assertEqual(dt.removed, [])  # nothing removed
        # every added service should be in the list of args
        self.assertTrue(servicename not in self.interface.services_args)
        # the backend should not have been created
        self.assertTrue(servicename not in self.interface.services.keys())

        # create the service and then try exposing the service again, simulating
        # it coming online before expose call.
        nonexistent_srv = rospy.Service(servicename, EmptySrv, srv_cb)
        try:
            with timeout(5) as t:
                while not t.timed_out and nonexistent_srv.resolved_name not in self.interface.services_available:
                    dt = self.interface.update()
                    self.assertEqual(dt.added, [])  # nothing added (not exposed yet)
                    self.assertEqual(dt.removed, [])  # nothing removed

            # every added service should be in the list of args
            self.assertTrue(servicename not in self.interface.services_args)
            # the backend should not have been created
            self.assertTrue(servicename not in self.interface.services.keys())

            # here we are sure the interface knows the service is available
            # it will be exposed right now
            self.interface.expose_services([servicename])

            # every exposed service should remain in the list of args ( in case regex match another service )
            self.assertTrue(servicename in self.interface.services_args)
            # make sure the service backend has been created
            self.assertTrue(servicename in self.interface.services.keys())
        finally:
            nonexistent_srv.shutdown('testing complete')
Пример #8
0
    def test_topic_appear_update_expose(self):
        """
        Test topic exposing functionality for a topic which already exists in
        the ros environment. Normal usecase
        Sequence : (UPDATE?) -> APPEAR -> UPDATE -> EXPOSE (-> UPDATE?)
        :return:
        """

        topicname = '/test/nonexistent1'
        # every added topic should be in the list of args
        self.assertTrue(topicname not in self.interface.topics_args)
        # the backend should not have been created
        self.assertTrue(topicname not in self.interface.topics.keys())
        # First update should not change state
        dt = self.interface.update()
        self.assertEqual(dt.added, [])  # nothing added
        self.assertEqual(dt.removed, [])  # nothing removed
        # every added topic should be in the list of args
        self.assertTrue(topicname not in self.interface.topics_args)
        # the backend should not have been created
        self.assertTrue(topicname not in self.interface.topics.keys())

        # create the publisher and then try exposing the topic again, simulating
        # it coming online before expose call.
        nonexistent_pub = rospy.Publisher(topicname, Empty, queue_size=1)
        with timeout(5) as t:
            while not t.timed_out and nonexistent_pub.resolved_name not in self.interface.topics_available:
                dt = self.interface.update()
                self.assertEqual(dt.added, [])  # nothing added (not exposed yet)
                self.assertEqual(dt.removed, [])  # nothing removed

        self.assertTrue(not t.timed_out)
        # TODO : do we need a test with subscriber ?

        # every added topic should be in the list of args
        self.assertTrue(topicname not in self.interface.topics_args)
        # the backend should not have been created
        self.assertTrue(topicname not in self.interface.topics.keys())

        self.interface.expose_topics([topicname])
        # every exposed topic should remain in the list of args ( in case regex match another topic )
        self.assertTrue(topicname in self.interface.topics_args)
        # make sure the topic backend has been created
        self.assertTrue(topicname in self.interface.topics.keys())

        nonexistent_pub.unregister()  # https://github.com/ros/ros_comm/issues/111 ( topic is still registered on master... )
Пример #9
0
    def test_rosnode_services_setup(
        self
    ):  # Here we check that this node actually provides all the services
        rosn = PyrosROS()
        try:
            nose.tools.assert_true(not rosn.is_alive())
            rosn.start()
            nose.tools.assert_true(rosn.is_alive())

            print("Discovering services Service...")
            services = zmp.discover("services",
                                    5)  # we wait a bit to let it time to start
            nose.tools.assert_true(services is not None)
            print("services providers : {svc}".format(svc=services.providers))
            nose.tools.assert_equal(len(services.providers), 1)
            nose.tools.assert_true(
                rosn.name in [p[0] for p in services.providers])

            res = services.call()
            nose.tools.assert_true('echo_service' not in res.keys(
            ))  # test_topic has not been created, detected or exposed

            print("Discovering setup Service...")
            setup = zmp.discover("setup",
                                 5)  # we wait a bit to let it time to start
            nose.tools.assert_true(setup is not None)
            print("setup providers : {svc}".format(svc=setup.providers))
            nose.tools.assert_equal(len(setup.providers), 1)
            nose.tools.assert_true(
                rosn.name in [p[0] for p in setup.providers])

            rospy.set_param(
                '/string_echo/topic_name',
                '~topic')  # private names to not mess things up too much
            rospy.set_param('/string_echo/echo_topic_name', '~echo_topic')
            rospy.set_param('/string_echo/echo_service_name', '~echo_service')

            string_echo_node = roslaunch.core.Node('pyros_test',
                                                   'echo.py',
                                                   name='string_echo')
            string_echo_process = self.launch.launch(string_echo_node)
            try:

                new_config = setup.call(
                    kwargs={
                        'services': ['/string_echo/echo_service'],
                        'topics': [],
                        'params': [],
                        'enable_cache': self.enable_cache,
                    })
                # What we get here is non deterministic
                # however we can wait for topic to be detected to make sure we get it after some time

                res = services.call()

                with timeout(5) as t:
                    while not t.timed_out and not '/string_echo/echo_service' in res.keys(
                    ):
                        rospy.rostime.wallsleep(1)
                        res = services.call()

                nose.tools.assert_true('/string_echo/echo_service' in res.keys(
                ))  # test_topic has been created, detected and exposed

            finally:
                # finishing all processes
                if string_echo_process is not None and string_echo_process.is_alive(
                ):
                    string_echo_process.stop()

            nose.tools.assert_true(not string_echo_process.is_alive())

        finally:
            # finishing PyrosROS process
            if rosn is not None and rosn.is_alive():
                rosn.shutdown()

        nose.tools.assert_true(not rosn.is_alive())
Пример #10
0
    def test_rosnode_services(
            self):  # Here we check that this node actually discovers topics

        # Starting underlying system before
        rospy.set_param(
            '/string_echo/topic_name',
            '~topic')  # private names to not mess things up too much
        rospy.set_param('/string_echo/echo_topic_name', '~echo_topic')
        rospy.set_param('/string_echo/echo_service_name', '~echo_service')

        string_echo_node = roslaunch.core.Node('pyros_test',
                                               'echo.py',
                                               name='string_echo')
        try:
            string_echo_process = self.launch.launch(string_echo_node)
        except roslaunch.RLException as rlexc:
            logging.error(
                "pyros_test is needed to run this test. Please verify that it is installed in your ROS environment"
            )
            raise
        try:
            # Starting PyrosROS with preconfigured services
            rosn = PyrosROS(
                kwargs={
                    'services': ['/string_echo/echo_service'],
                    'enable_cache': self.enable_cache
                })  # careful assuming the service fullname here
            try:
                nose.tools.assert_true(not rosn.is_alive())
                rosn.start()
                nose.tools.assert_true(rosn.is_alive())

                print("Discovering services Service...")
                services = zmp.discover(
                    "services", 5)  # we wait a bit to let it time to start
                nose.tools.assert_true(services is not None)
                print("services providers : {svc}".format(
                    svc=services.providers))
                nose.tools.assert_equal(len(services.providers), 1)
                nose.tools.assert_true(
                    rosn.name in [p[0] for p in services.providers])

                res = services.call()
                # What we get here is non deterministic
                # however we can wait for service to be detected to make sure we get it after some time

                with timeout(5) as t:
                    while not t.timed_out and not '/string_echo/echo_service' in res.keys(
                    ):
                        rospy.rostime.wallsleep(1)
                        res = services.call()

                nose.tools.assert_true('/string_echo/echo_service' in res.keys(
                ))  # echo_service has been created, detected and exposed
            finally:
                # finishing PyrosROS process
                if rosn is not None and rosn.is_alive():
                    rosn.shutdown()

            nose.tools.assert_true(not rosn.is_alive())

        finally:
            # finishing string_pub_process
            if string_echo_process is not None and string_echo_process.is_alive(
            ):
                string_echo_process.stop()

        nose.tools.assert_true(not string_echo_process.is_alive())
Пример #11
0
    def test_service_disappear_update_withhold(self):
        """
        Test service exposing functionality for a service which already exists in
        the ros environment. Normal usecase
        Sequence : (UPDATE? ->) DISAPPEAR -> UPDATE -> WITHHOLD (-> UPDATE ?)
        :return:
        """
        servicename = '/test/absentsrv1'
        # service should not be in the list of args
        self.assertTrue(servicename not in self.interface.services_args)
        # the backend should not have been created
        self.assertTrue(servicename not in self.interface.services.keys())

        self.interface.expose_services([servicename])
        # every added service should be in the list of args
        self.assertTrue(servicename in self.interface.services_args)
        # service backend has NOT been created yet
        self.assertTrue(servicename not in self.interface.services.keys())

        # create the service and then try updating again, simulating
        # it coming online after expose call.
        nonexistent_srv = rospy.Service(servicename, EmptySrv, srv_cb)
        try:
            dt = self.interface.update()
            self.assertTrue(nonexistent_srv.resolved_name in dt.added)  # nonexistent added
            self.assertEqual(dt.removed, [])  # nothing removed

            # service should be in the list of args
            self.assertTrue(servicename in self.interface.services_args)
            # the backend should have been created
            self.assertTrue(servicename in self.interface.services.keys())

        # up to here possible sequences should have been already tested by previous tests
        # Now comes our actual disappearance / withholding test
        finally:
            nonexistent_srv.shutdown('testing disappearing service')

        # every added service should be in the list of args
        self.assertTrue(servicename in self.interface.services_args)
        # the backend should STILL be there
        self.assertTrue(servicename in self.interface.services.keys())
        # Note the service implementation should take care of possible errors in this case

        # wait here until service actually disappear from cache proxy
        with timeout(5) as t:
            while not t.timed_out and nonexistent_srv.resolved_name not in dt.removed:
                dt = self.interface.update()
                self.assertEqual(dt.added, [])  # nothing added

        self.assertTrue(nonexistent_srv.resolved_name in dt.removed)  # nonexistent_srv removed
        # every exposed service should remain in the list of args ( in case regex match another service )
        self.assertTrue(servicename in self.interface.services_args)
        # make sure the service backend should NOT be there any longer
        self.assertTrue(servicename not in self.interface.services.keys())

        # TODO : test that coming back actually works

        self.interface.expose_services([])
        # every withhold service should NOT be in the list of args
        self.assertTrue(servicename not in self.interface.services_args)
        # service backend has not been created
        self.assertTrue(servicename not in self.interface.services.keys())

        dt = self.interface.update()
        self.assertEqual(dt.added, [])  # nothing added
        self.assertEqual(dt.removed, [])  # nothing removed
        # every withhold service should NOT be in the list of args
        self.assertTrue(servicename not in self.interface.services_args)
        # make sure the service backend has been created
        self.assertTrue(servicename not in self.interface.services.keys())
Пример #12
0
    def test_topic_update_disappear_withhold(self):
        """
        Test topic exposing functionality for a topic which already exists in
        the ros environment. Simple Normal usecase
        Sequence : UPDATE -> DISAPPEAR -> WITHHOLD
        :return:
        """

        topicname = '/test/nonexistent5'
        # every added topic should be in the list of args
        self.assertTrue(topicname not in self.interface.topics_args)
        # the backend should not have been created
        self.assertTrue(topicname not in self.interface.topics.keys())
        # First update should not change state
        dt = self.interface.update()
        self.assertEqual(dt.added, [])  # nothing added
        self.assertEqual(dt.removed, [])  # nothing removed
        # every added topic should be in the list of args
        self.assertTrue(topicname not in self.interface.topics_args)
        # the backend should not have been created
        self.assertTrue(topicname not in self.interface.topics.keys())

        dt = self.interface.expose_topics([topicname])
        self.assertEqual(dt.added, [])  # nothing added yet ( not existing )
        self.assertEqual(dt.removed, [])  # nothing removed
        # every added topic should be in the list of args
        self.assertTrue(topicname in self.interface.topics_args)
        # topic backend has not been created
        self.assertTrue(topicname not in self.interface.topics.keys())

        # create the publisher and then try exposing the topic again, simulating
        # it coming online before expose call.
        nonexistent_pub = rospy.Publisher(topicname, Empty, queue_size=1)

        with timeout(5) as t:
            dt = DiffTuple([], [])
            while not t.timed_out and nonexistent_pub.resolved_name not in dt.added:
                dt = self.interface.update()
                self.assertEqual(dt.removed, [])  # nothing removed

        self.assertTrue(not t.timed_out)
        self.assertTrue(nonexistent_pub.resolved_name in dt.added)  # added now because it just appeared
        self.assertEqual(dt.removed, [])  # nothing removed
        # TODO : do we need a test with subscriber ?

        # every added topic should be in the list of args
        self.assertTrue(topicname in self.interface.topics_args)
        # topic backend has been created
        self.assertTrue(topicname in self.interface.topics.keys())

        # up to here possible sequences should have been already tested by previous tests
        # Now comes our actual disappearrence / withholding test

        nonexistent_pub.unregister()  # https://github.com/ros/ros_comm/issues/111 ( topic is still registered on master... )
        # TODO : test disappear ( how ? )

        # every added topic should be in the list of args
        self.assertTrue(topicname in self.interface.topics_args)
        # the backend should STILL be there
        self.assertTrue(topicname in self.interface.topics.keys())
        # Note the Topic implementation should take care of possible errors in this case

        self.interface.expose_topics([])
        # every withhold topic should NOT be in the list of args
        self.assertTrue(topicname not in self.interface.topics_args)
        # topic backend should NOT be there any longer
        self.assertTrue(topicname not in self.interface.topics.keys())
Пример #13
0
    def test_topic_withhold_update_disappear(self):
        """
        Test topic witholding functionality for a topic which doesnt exists anymore in
        the ros environment. Normal usecase
        Sequence : (-> UPDATE ?) -> WITHHOLD -> UPDATE -> DISAPPEAR (-> UPDATE ?)
        :return:
        """
        topicname = '/test/nonexistent3'
        # every added topic should be in the list of args
        self.assertTrue(topicname not in self.interface.topics_args)
        # the backend should not have been created
        self.assertTrue(topicname not in self.interface.topics.keys())
        # First update should not change state
        dt = self.interface.update()
        self.assertEqual(dt.added, [])  # nothing added
        self.assertEqual(dt.removed, [])  # nothing removed
        # every added topic should be in the list of args
        self.assertTrue(topicname not in self.interface.topics_args)
        # the backend should not have been created
        self.assertTrue(topicname not in self.interface.topics.keys())

        # create the publisher and then try exposing the topic again, simulating
        # it coming online before expose call.
        nonexistent_pub = TopicBack._create_pub(topicname, Empty, queue_size=1)
        with timeout(5) as t:
            while not t.timed_out and topicname not in self.interface.topics_available:
                dt = self.interface.update()
                self.assertEqual(dt.added, [])  # nothing added (not exposed)
                self.assertEqual(dt.removed, [])  # nothing removed
        # TODO : do we need a test with subscriber ?

        self.assertTrue(not t.timed_out)
        # topic should be in the list of args yet (not exposed)
        self.assertTrue(topicname not in self.interface.topics_args)
        # the backend should not have been created
        self.assertTrue(topicname not in self.interface.topics.keys())

        # Here we are sure the internal state has topic_name registered
        # will be exposed right away
        dt = self.interface.expose_topics([topicname])
        self.assertTrue(topicname in dt.added)  # detected and added
        self.assertEqual(dt.removed, [])  # nothing removed
        # every exposed topic should remain in the list of args ( in case regex match another topic )
        self.assertTrue(topicname in self.interface.topics_args)
        # make sure the topic backend has been created
        self.assertTrue(topicname in self.interface.topics.keys())

        dt = self.interface.update()
        self.assertEqual(dt.added, [])  # nothing added
        self.assertEqual(dt.removed, [])  # nothing removed
        # every withhold topic should STILL be in the list of args
        self.assertTrue(topicname in self.interface.topics_args)
        # topic backend should STILL be there
        self.assertTrue(topicname in self.interface.topics.keys())

        dt = self.interface.expose_topics([])
        self.assertTrue(topicname in dt.removed)  # removed
        self.assertEqual(dt.added, [])  # nothing added
        # every withhold topic should NOT be in the list of args
        self.assertTrue(topicname not in self.interface.topics_args)
        # topic backend should be GONE
        self.assertTrue(topicname not in self.interface.topics.keys())

        dt = self.interface.update()
        self.assertEqual(dt.added, [])  # nothing added
        self.assertEqual(dt.removed, [])  # nothing removed
        # every withhold topic should NOT be in the list of args
        self.assertTrue(topicname not in self.interface.topics_args)
        # topic backend should be GONE
        self.assertTrue(topicname not in self.interface.topics.keys())

        TopicBack._remove_pub(nonexistent_pub)

        dt = self.interface.update()
        self.assertEqual(dt.added, [])  # nothing added
        self.assertEqual(dt.removed, [])  # nothing removed
        # every withhold topic should NOT be in the list of args
        self.assertTrue(topicname not in self.interface.topics_args)
        # topic backend should be GONE
        self.assertTrue(topicname not in self.interface.topics.keys())