def run_assertions(reactor: ReactorInterface):
     cb1 = ReactorCommand.create_callback(callback=ROUTE_CALLBACK,
                                          envelope=env1)
     cb2 = ReactorCommand.create_callback(callback=ROUTE_CALLBACK,
                                          envelope=env2)
     reactor._run_callback.assert_has_calls(
         [call(cb1), call(cb2)], any_order=False)
Exemple #2
0
    def test_subpub_1_n_1(self):
        """
        Tests sub/pub with 1 publisher and 3 subs, with one message
        """
        def configure_interface(reactor: ReactorInterface):
            reactor._run_callback = MagicMock()
            return reactor

        def run_assertions(reactor: ReactorInterface):
            callback = 'route'
            data = env.serialize()
            reactor._run_callback.assert_called_once_with(callback, data)

        env = random_envelope()

        sub1 = MPReactorInterface(config_fn=configure_interface, assert_fn=run_assertions, name='** SUB1')
        sub2 = MPReactorInterface(config_fn=configure_interface, assert_fn=run_assertions, name='** SUB2')
        sub3 = MPReactorInterface(config_fn=configure_interface, assert_fn=run_assertions, name='** SUB3')
        pub = MPReactorInterface(name='++ PUB')

        add_sub_cmd = ReactorCommand.create_cmd(SubPubExecutor.__name__, SubPubExecutor.add_sub.__name__, url=URL,
                                                filter=FILTER)
        add_pub_cmd = ReactorCommand.create_cmd(SubPubExecutor.__name__, SubPubExecutor.add_pub.__name__, url=URL)
        send_pub_cmd = ReactorCommand.create_cmd(SubPubExecutor.__name__, SubPubExecutor.send_pub.__name__,
                                                 envelope=env, filter=FILTER)

        sub1.send_cmd(add_sub_cmd)
        sub2.send_cmd(add_sub_cmd)
        sub3.send_cmd(add_sub_cmd)
        pub.send_cmd(add_pub_cmd)
        time.sleep(0.2)
        pub.send_cmd(send_pub_cmd)

        self.start()
Exemple #3
0
    def test_1_1_1(self):
        """
        Tests pub/sub 1-1 (one sub one pub) with one message
        """
        def configure_interface(reactor: ReactorInterface):
            reactor._run_callback = MagicMock()
            return reactor

        def run_assertions(reactor: ReactorInterface):
            callback = 'route'
            data = env.serialize()
            reactor._run_callback.assert_called_once_with(callback, data)

        env = random_envelope()

        sub = MPReactorInterface(config_fn=configure_interface, assert_fn=run_assertions, name='** SUB')
        pub = MPReactorInterface(name='++ PUB')

        # test
        self.execute_python('node_1', something_silly, async=True)
        # end tests

        add_sub_cmd = ReactorCommand.create_cmd(SubPubExecutor.__name__, SubPubExecutor.add_sub.__name__, url=URL,
                                                filter=FILTER)
        add_pub_cmd = ReactorCommand.create_cmd(SubPubExecutor.__name__, SubPubExecutor.add_pub.__name__, url=URL)
        send_pub_cmd = ReactorCommand.create_cmd(SubPubExecutor.__name__, SubPubExecutor.send_pub.__name__,
                                                 envelope=env, filter=FILTER)

        sub.send_cmd(add_sub_cmd)
        pub.send_cmd(add_pub_cmd)
        time.sleep(0.2)  # To allow time for subs to connect to pub before pub sends data
        pub.send_cmd(send_pub_cmd)

        self.start()
Exemple #4
0
    async def _lookup_ip(self, cmd, url, vk, *args, **kwargs):
        ip, node = None, None
        try:
            node = await self.dht.network.lookup_ip(vk)
        except Exception as e:
            delim_line = '!' * 64
            err_msg = '\n\n' + delim_line + '\n' + delim_line
            err_msg += '\n ERROR CAUGHT IN LOOKUP FUNCTION {}\ncalled \w args={}\nand kwargs={}\n'\
                        .format(args, kwargs)
            err_msg += '\nError Message: '
            err_msg += '\n\n{}'.format(traceback.format_exc())
            err_msg += '\n' + delim_line + '\n' + delim_line
            self.log.error(err_msg)

        if node is None:

            kwargs = cmd.kwargs
            callback = ReactorCommand.create_callback(
                callback=StateInput.LOOKUP_FAILED, **kwargs)
            self.log.debug(
                "Sending callback failure to mainthread {}".format(callback))
            self.socket.send(callback.serialize())
            # TODO -- send callback to SM saying hey i couldnt lookup this vk

            return

        # Send interpolated command back through pipeline
        ip = node.ip if type(node) == Node else node
        new_url = IPUtils.interpolate_url(url, ip)
        kwargs = cmd.kwargs
        kwargs['url'] = new_url
        new_cmd = ReactorCommand.create_cmd(envelope=cmd.envelope, **kwargs)

        self._execute_cmd(new_cmd)
Exemple #5
0
    def test_pubsub_1_1_n_filters(self):
        """
        Test pub/sub 1-1 with multiple filters, only some of which should be received
        """
        def configure_interface(reactor: ReactorInterface):
            reactor._run_callback = MagicMock()
            return reactor

        def run_assertions(reactor: ReactorInterface):
            callback = 'route'
            data = env.serialize()
            reactor._run_callback.assert_called_once_with(callback, data)

        env = random_envelope()
        env2 = random_envelope()

        sub = MPReactorInterface(config_fn=configure_interface, assert_fn=run_assertions, name='** SUB')
        pub = MPReactorInterface(name='++ PUB 1')
        pub = MPReactorInterface(name='++ PUB 2')

        add_sub_cmd = ReactorCommand.create_cmd(SubPubExecutor.__name__, SubPubExecutor.add_sub.__name__, url=URL,
                                                filter=FILTER)
        add_pub_cmd = ReactorCommand.create_cmd(SubPubExecutor.__name__, SubPubExecutor.add_pub.__name__, url=URL)
        send_pub_cmd = ReactorCommand.create_cmd(SubPubExecutor.__name__, SubPubExecutor.send_pub.__name__,
                                                 envelope=env, filter=FILTER)

        sub.send_cmd(add_sub_cmd)
        pub.send_cmd(add_pub_cmd)
        time.sleep(0.2)  # To allow time for subs to connect to pub before pub sends data
        pub.send_cmd(send_pub_cmd)

        time.sleep(0.2) # Allow pubs to go through

        self.start()
    def test_1_1_n_delay(self):
        """
        Tests pub/sub 1-1 with 3 messages, and a 0.2 second delay between messages. The messages should be received in
        order they are sent
        """
        def configure_interface(reactor: ReactorInterface):
            reactor._run_callback = MagicMock()
            return reactor

        def run_assertions(reactor: ReactorInterface):
            cb1 = ReactorCommand.create_callback(callback=ROUTE_CALLBACK,
                                                 envelope=env1)
            cb2 = ReactorCommand.create_callback(callback=ROUTE_CALLBACK,
                                                 envelope=env2)
            reactor._run_callback.assert_has_calls(
                [call(cb1), call(cb2)], any_order=False)

        env1 = random_envelope()
        env2 = random_envelope()

        sub = MPReactorInterface(config_fn=configure_interface,
                                 assert_fn=run_assertions,
                                 name='** SUB')
        pub = MPReactorInterface(name='++ PUB')

        add_sub_cmd = ReactorCommand.create_cmd(
            SubPubExecutor.__name__,
            SubPubExecutor.add_sub.__name__,
            url=URL,
            filter=FILTER)
        add_pub_cmd = ReactorCommand.create_cmd(
            SubPubExecutor.__name__, SubPubExecutor.add_pub.__name__, url=URL)
        send_pub_cmd1 = ReactorCommand.create_cmd(
            SubPubExecutor.__name__,
            SubPubExecutor.send_pub.__name__,
            envelope=env1,
            filter=FILTER)
        send_pub_cmd2 = ReactorCommand.create_cmd(
            SubPubExecutor.__name__,
            SubPubExecutor.send_pub.__name__,
            envelope=env2,
            filter=FILTER)

        sub.send_cmd(add_sub_cmd)
        pub.send_cmd(add_pub_cmd)
        time.sleep(0.2)

        pub.send_cmd(send_pub_cmd1)
        time.sleep(0.1)  # Give time for first message to go through first
        pub.send_cmd(send_pub_cmd2)

        time.sleep(0.2)  # To allow both pubs to go through

        self.start()
    def test_1_1_n(self):
        """
        Tests pub/sub 1-1 with 2 messages (any order with no delay in sends)
        """
        def configure_interface(reactor: ReactorInterface):
            reactor._run_callback = MagicMock()
            return reactor

        def run_assertions(reactor: ReactorInterface):
            cb1 = ReactorCommand.create_callback(callback=ROUTE_CALLBACK,
                                                 envelope=env1)
            cb2 = ReactorCommand.create_callback(callback=ROUTE_CALLBACK,
                                                 envelope=env2)
            reactor._run_callback.assert_has_calls(
                [call(cb1), call(cb2)], any_order=True)

        env1 = random_envelope()
        env2 = random_envelope()

        sub = MPReactorInterface(config_fn=configure_interface,
                                 assert_fn=run_assertions,
                                 name='** SUB')
        pub = MPReactorInterface(name='++ PUB')

        add_sub_cmd = ReactorCommand.create_cmd(
            SubPubExecutor.__name__,
            SubPubExecutor.add_sub.__name__,
            url=URL,
            filter=FILTER)
        add_pub_cmd = ReactorCommand.create_cmd(
            SubPubExecutor.__name__, SubPubExecutor.add_pub.__name__, url=URL)
        send_pub_cmd1 = ReactorCommand.create_cmd(
            SubPubExecutor.__name__,
            SubPubExecutor.send_pub.__name__,
            envelope=env1,
            filter=FILTER)
        send_pub_cmd2 = ReactorCommand.create_cmd(
            SubPubExecutor.__name__,
            SubPubExecutor.send_pub.__name__,
            envelope=env2,
            filter=FILTER)

        sub.send_cmd(add_sub_cmd)
        pub.send_cmd(add_pub_cmd)
        time.sleep(0.2)
        pub.send_cmd(send_pub_cmd1)
        pub.send_cmd(send_pub_cmd2)

        time.sleep(0.2)  # Give time for both pubs to go through

        self.start()
Exemple #8
0
    def send_request_env(self, envelope: Envelope, timeout=0, ip: str='', vk: str=''):
        url = "tcp://{}:{}".format(ip or vk, Constants.Ports.RouterDealer)
        reply_uuid = EnvelopeAuth.reply_uuid(envelope.meta.uuid)

        cmd = ReactorCommand.create_cmd(DealerRouterExecutor.__name__, DealerRouterExecutor.request.__name__, url=url,
                                        envelope=envelope, timeout=timeout, reply_uuid=reply_uuid)
        self.interface.send_cmd(cmd)
Exemple #9
0
    async def _recv_messages(self):
        try:
            # Notify parent proc that this proc is ready
            self.log.debug("reactorcore notifying main proc of ready")
            self.socket.send(CHILD_RDY_SIG)

            self.log.info(
                "-- Daemon proc listening to main proc on PAIR Socket at {} --"
                .format(self.url))
            while True:
                self.log.debug(
                    "ReactorDaemon awaiting for command from main thread...")
                cmd_bin = await self.socket.recv()
                self.log.debug("Got cmd from queue: {}".format(cmd_bin))

                if cmd_bin == KILL_SIG:
                    self.log.debug(
                        "Daemon Process got kill signal from main proc")
                    self._teardown()
                    return

                # Should from_bytes be in a try/catch? I suppose if we get a bad command from the main proc we might as well
                # blow up because this is very likely because of a development error, so no try/catch for now
                cmd = ReactorCommand.from_bytes(cmd_bin)
                assert cmd.class_name and cmd.func_name, "Received invalid command with no class/func name!"

                self._execute_cmd(cmd)

        except asyncio.CancelledError:
            self.log.warning("some ish got cacnelerd")
Exemple #10
0
 def remove_sub_filter(self, filter: str):
     """
     Removes a filters from the sub socket. Unlike the remove_sub API, this does not disconnect a URL. It only
     unsubscribes to 'filter
     :param filter: A string to use as the filter frame. This filter will be unsubscribed.
     """
     cmd = ReactorCommand.create_cmd(SubPubExecutor.__name__, SubPubExecutor.remove_sub_filter.__name__, filter=filter)
     self.interface.send_cmd(cmd)
Exemple #11
0
 def add_router(self, ip: str='', vk: str=''):
     """
     Add a router socket at url. Routers are like 'async repliers', and can connect to many Dealer sockets (N-1)
     :param url: The URL the router socket should BIND to
     :param vk: The Node's VK to connect to. This will be looked up in the overlay network
     """
     url = "tcp://{}:{}".format(ip or vk, Constants.Ports.RouterDealer)
     cmd = ReactorCommand.create_cmd(DealerRouterExecutor.__name__, DealerRouterExecutor.add_router.__name__, url=url)
     self.interface.send_cmd(cmd)
Exemple #12
0
 def remove_pub(self, ip: str='', vk: str=''):
     """
     Removes a publisher (duh)
     :param url: The URL of the router that the created dealer socket should CONNECT to.
     :param vk: The Node's VK to connect to. This will be looked up in the overlay network
     """
     url = "tcp://{}:{}".format(ip or vk, Constants.Ports.PubSub)
     cmd = ReactorCommand.create_cmd(SubPubExecutor.__name__, SubPubExecutor.remove_pub.__name__, url=url)
     self.interface.send_cmd(cmd)
Exemple #13
0
 def send_pub_env(self, filter: str, envelope: Envelope):
     """
     Publish envelope with filter frame 'filter'.
     :param filter: A string to use as the filter frame
     :param envelope: An instance of Envelope
     """
     cmd = ReactorCommand.create_cmd(SubPubExecutor.__name__, SubPubExecutor.send_pub.__name__, filter=filter,
                                     envelope=envelope)
     self.interface.send_cmd(cmd)
Exemple #14
0
    def call_on_mp(self, callback: str, header: str=None, envelope_binary: bytes=None, **kwargs):
        if header:
            kwargs['header'] = header

        cmd = ReactorCommand.create_callback(callback=callback, envelope_binary=envelope_binary, **kwargs)

        # self.log.critical("\ncalling callback cmd to reactor interface: {}".format(cmd))  # DEBUG line remove this

        self.inproc_socket.send(cmd.serialize())
Exemple #15
0
 def add_pub(self, ip: str=''):
     """
     Create a publisher socket that BINDS to 'url'
     :param url: The URL to publish under.
     :param vk: The Node's VK to connect to. This will be looked up in the overlay network
     """
     url = "tcp://{}:{}".format(ip or vk, Constants.Ports.PubSub)
     cmd = ReactorCommand.create_cmd(SubPubExecutor.__name__, SubPubExecutor.add_pub.__name__, url=url)
     self.interface.send_cmd(cmd)
Exemple #16
0
    def _lookup_failed(self, cmd: ReactorCommand):

        kwargs = cmd.kwargs
        del (kwargs['callback'])
        new_cmd = ReactorCommand.create_cmd(envelope=cmd.envelope, **kwargs)

        import time
        time.sleep(0.5)

        self.sm.composer.interface.send_cmd(new_cmd)
    def test_req_1_1_1(self):
        """
        Tests dealer router 1/1 (1 dealer, 1 router) with 1 message request (dealer sends router 1 message)
        """
        def run_assertions(reactor: ReactorInterface):
            cb = ReactorCommand.create_callback(callback=ROUTE_REQ_CALLBACK,
                                                envelope=env,
                                                header=DEALER_ID)
            reactor._run_callback.assert_called_once_with(cb)

        DEALER_URL = URLS[0]
        DEALER_ID = "id-" + DEALER_URL  # irl this would a node's vk
        ROUTER_URL = URLS[1]

        env = random_envelope()

        dealer = MPReactorInterface(name='DEALER')
        router = MPReactorInterface(config_fn=config_reactor,
                                    assert_fn=run_assertions,
                                    name='ROUTER')

        add_dealer = ReactorCommand.create_cmd(
            class_name=DealerRouterExecutor.__name__,
            func_name=DealerRouterExecutor.add_dealer.__name__,
            url=ROUTER_URL,
            id=DEALER_ID)
        add_router = ReactorCommand.create_cmd(
            class_name=DealerRouterExecutor.__name__,
            func_name=DealerRouterExecutor.add_router.__name__,
            url=ROUTER_URL)
        request = ReactorCommand.create_cmd(
            class_name=DealerRouterExecutor.__name__,
            func_name=DealerRouterExecutor.request.__name__,
            url=ROUTER_URL,
            envelope=env)

        dealer.send_cmd(add_dealer)
        router.send_cmd(add_router)

        time.sleep(0.2)
        dealer.send_cmd(request)

        self.start()
Exemple #18
0
 def remove_sub_url(self, ip: str='', vk: str=''):
     """
     Disconnects the sub URL from url. Unlike the remove_sub API, this does not remove a filter from the
     socket. It only disconnects from the url.
     received
     :param url: The URL of the router that the created dealer socket should CONNECT to.
     :param vk: The Node's VK to connect to. This will be looked up in the overlay network
     """
     url = "tcp://{}:{}".format(ip or vk, Constants.Ports.PubSub)
     cmd = ReactorCommand.create_cmd(SubPubExecutor.__name__, SubPubExecutor.remove_sub_url.__name__, url=url)
     self.interface.send_cmd(cmd)
Exemple #19
0
 def add_sub(self, filter: str, ip: str='', vk: str=''):
     """
     Connects the subscriber socket to listen to 'URL' with filter 'filter'.
     :param url: The URL to CONNECT the sub socket to (ex 'tcp://17.1.3.4:4200')
     :param filter: The filter to subscribe to. Only data published with this filter will be received. Currently,
     only one filter per CONNECT is supported.
     """
     # url = "tcp://{}:{}".format(ip or vk, Constants.Ports.PubSub)
     url = "tcp://{}:{}".format(ip or vk, Constants.Ports.PubSub)
     cmd = ReactorCommand.create_cmd(SubPubExecutor.__name__, SubPubExecutor.add_sub.__name__, filter=filter, url=url, vk=vk)
     self.interface.send_cmd(cmd)
    def test_create_callback(self):
        """
        Tests create_callback
        """
        callback = 'route'
        kwargs = {'cats': '18', 'dogs': 'over 9000'}

        cmd = ReactorCommand.create_callback(callback, **kwargs)

        self.assertEqual(cmd.callback, callback)
        for k in kwargs:
            self.assertEqual(cmd.kwargs[k], kwargs[k])
    def test_create_with_envelope(self):
        """
        Tests creating a message with an envelope produces an object with the expected properties
        """
        sk, vk = ED25519Wallet.new()
        tx = StandardTransactionBuilder.random_tx()
        sender = 'me'
        env = Envelope.create_from_message(message=tx, signing_key=sk)

        cmd = ReactorCommand.create_cmd('some_cls', 'some_func', envelope=env)

        self.assertTrue(ReactorCommand.envelope, env)
Exemple #22
0
 def add_dealer(self, ip: str='', vk: str=''):
     """
     Add a dealer socket at url. Dealers are like 'async requesters', and can connect to a single Router socket (1-1)
     (side note: A router socket, however, can connect to N dealers)
     'id' socketopt for the dealer socket will be this node's verifying key
     :param url: The URL of the router that the created dealer socket should CONNECT to.
     :param vk: The Node's VK to connect to. This will be looked up in the overlay network
     """
     url = "tcp://{}:{}".format(ip or vk, Constants.Ports.RouterDealer)
     cmd = ReactorCommand.create_cmd(DealerRouterExecutor.__name__, DealerRouterExecutor.add_dealer.__name__,
                                     id=self.verifying_key, url=url, vk=vk)
     self.interface.send_cmd(cmd)
    def test_create_with_kwargs(self):
        """
        Tests creating a message without an envelope produces an objects with the expected kwargs
        """
        kwargs = {
            'callback': 'do_something',
            'some_num': '10',
            'some_id': '0x5544ddeeff'
        }

        cmd = ReactorCommand.create(**kwargs)

        self.assertEqual(cmd.kwargs, kwargs)
Exemple #24
0
 def send_reply(self, message: MessageBase, request_envelope: Envelope):
     """
     Send a reply message (via a Router socket) for the original reqeust in request_envelope (which came from a
     Dealer socket). Replies envelope are created as a deterministic function of their original request envelope,
     so that both parties (the sender and receiver) are in agreement on what the reply envelope should look like
     :param message: A MessageBase instance that denotes the reply data
     :param request_envelope: An Envelope instance that denotes the envelope of the original request that we are
     replying to
     """
     requester_id = request_envelope.seal.verifying_key
     reply_env = self._package_reply(reply=message, req_env=request_envelope)
     cmd = ReactorCommand.create_cmd(DealerRouterExecutor.__name__, DealerRouterExecutor.reply.__name__,
                                     id=requester_id, envelope=reply_env)
     self.interface.send_cmd(cmd)
    def test_create_cmd(self):
        """
        Tests create_cmd
        """
        class_name = 'TestClass'
        func_name = 'do_something_lit'

        kwargs = {'cats': '18', 'dogs': 'over 9000'}

        cmd = ReactorCommand.create_cmd(class_name, func_name, **kwargs)

        self.assertEquals(cmd.func_name, func_name)
        self.assertEqual(cmd.class_name, class_name)
        for k in kwargs:
            self.assertEqual(cmd.kwargs[k], kwargs[k])

        self.assertEqual(cmd.envelope, None)
Exemple #26
0
 async def _recv_messages(self):
     """
     Should be for internal use only.
     Starts listening to messages from the ReactorDaemon. This method gets run_until_complete by
     invoking .start_reactor on the ReactorInterface object.
     """
     try:
         self.log.debug(
             "~~ Reactor listening to messages from ReactorDaemon ~~")
         while True:
             self.log.debug("Waiting for callback...")
             msg = await self.socket.recv()
             callback = ReactorCommand.from_bytes(msg)
             self.log.debug("Got callback cmd <{}>".format(callback))
             self.router.route_callback(callback)
     except asyncio.CancelledError:
         self.log.debug("_recv_messages future canceled!")
Exemple #27
0
    def remove_sub(self, filter: str, ip: str='', vk: str=''):
        """
        Stop subscribing to a URL and filter. Note that all other subscriber connections will drop this filter as well
        (so if there is another URL you are subscribing to with the same filter, that sub will no longer work). The
        pattern at this point is to have a single filter for each 'node type', ie witness/delegate/masternode.

        If you wish to stop subscribing to a URL, but not necessarily a filter, then call this method and pass in an
        empty string to FILTER. For example, a delegate might want to stop subscribing to a particular witness, but not
        all witnesses.

        :param filter: The filter to subscribe to. Only multipart messages with this filter as the first frame will be
        received
        :param url: The URL of the router that the created dealer socket should CONNECT to.
        :param vk: The Node's VK to connect to. This will be looked up in the overlay network
        """
        url = "tcp://{}:{}".format(ip or vk, Constants.Ports.PubSub)
        cmd = ReactorCommand.create_cmd(SubPubExecutor.__name__, SubPubExecutor.remove_sub.__name__, filter=filter, url=url, vk=vk)
        self.interface.send_cmd(cmd)
Exemple #28
0
    async def _recv_messages(self):
        # Notify parent proc that this proc is ready
        self.log.debug("reactorcore notifying main proc of ready")
        self.socket.send(CHILD_RDY_SIG)

        self.log.info(
            "-- Daemon proc listening to main proc on PAIR Socket at {} --".
            format(self.url))
        while True:
            self.log.debug(
                "ReactorDaemon awaiting for command from main thread...")
            cmd_bin = await self.socket.recv()
            self.log.debug("Got cmd from queue: {}".format(cmd_bin))

            if cmd_bin == KILL_SIG:
                self._teardown()
                return

            # Should from_bytes be in a try/catch? I suppose if we get a bad command from the main proc we might as well
            # blow up because this is very likely because of a development error, so no try/catch for now
            cmd = ReactorCommand.from_bytes(cmd_bin)

            await self._execute_cmd(cmd)
 def run_assertions(reactor: ReactorInterface):
     callback = ReactorCommand.create_callback(callback=ROUTE_CALLBACK,
                                               envelope=env)
     reactor._run_callback.assert_called_once_with(callback)
    def test_pubsub_1_n_n_filters(self):
        """
        Test pub/sub 1-1 with multiple filters, only some of which should be received
        """
        # TODO -- implement
        self.assertTrue(27**2 + 36**2 == 45**2)
        return

        def configure_interface(reactor: ReactorInterface):
            reactor._run_callback = MagicMock()
            return reactor

        def run_assertions(reactor: ReactorInterface):
            cb = ReactorCommand.create_callback(callback=ROUTE_CALLBACK,
                                                envelope=env1)
            reactor._run_callback.assert_called_once_with(cb)

        env1, env2, env3, env4, env5 = (random_envelope() for _ in range(5))

        sub = MPReactorInterface(config_fn=configure_interface,
                                 assert_fn=run_assertions,
                                 name='** SUB')
        pub1 = MPReactorInterface(name='++ PUB 1')
        pub2 = MPReactorInterface(name='++ PUB 2')
        pub3 = MPReactorInterface(name='++ PUB 3')

        add_sub_cmd1 = ReactorCommand.create_cmd(
            SubPubExecutor.__name__,
            SubPubExecutor.add_sub.__name__,
            url=URLS[1],
            filter=FILTERS[1])
        add_sub_cmd2 = ReactorCommand.create_cmd(
            SubPubExecutor.__name__,
            SubPubExecutor.add_sub.__name__,
            url=URLS[1],
            filter=FILTERS[2])
        add_sub_cmd3 = ReactorCommand.create_cmd(
            SubPubExecutor.__name__,
            SubPubExecutor.add_sub.__name__,
            url=URLS[1],
            filter=FILTERS[3])
        add_pub_cmd1 = ReactorCommand.create_cmd(
            SubPubExecutor.__name__, SubPubExecutor.add_pub.__name__, url=URL)
        add_pub_cmd2 = ReactorCommand.create_cmd(
            SubPubExecutor.__name__, SubPubExecutor.add_pub.__name__, url=URL)

        send_pub_cmd1 = ReactorCommand.create_cmd(
            SubPubExecutor.__name__,
            SubPubExecutor.send_pub.__name__,
            envelope=env1,
            filter=FILTERS[1])
        send_pub_cmd2 = ReactorCommand.create_cmd(
            SubPubExecutor.__name__,
            SubPubExecutor.send_pub.__name__,
            envelope=env2,
            filter=FILTERS[1])

        sub.send_cmd(add_sub_cmd1)
        sub.send_cmd(add_sub_cmd1)
        pub1.send_cmd(add_pub_cmd1)
        pub2.send_cmd(add_pub_cmd1)
        time.sleep(
            0.2
        )  # To allow time for subs to connect to pub before pub sends data
        pub1.send_cmd(send_pub_cmd1)

        time.sleep(0.2)  # Allow pubs to go through

        self.start()