def __init__(self, config_fn=None, assert_fn=None, name='TestableProcess', *args, **kwargs): super().__init__() self.log = get_logger(name) self.name = name self.url = _gen_url(name) self.config_fn = config_fn # Function to configure object with mocks self.assert_fn = assert_fn # Function to run assertions on said mocks # 'socket' is used to proxy commands to blocking object running in a child process (possibly on a VM) self.ctx = zmq.Context() self.socket = self.ctx.socket(socket_type=zmq.PAIR) self.socket.bind(self.url) # Add this object to the registry of testers MPTesterBase.testers.append(self) # Create a wrapper around the build_obj with args and kwargs. We do this b/c this function will actually be # invoked in a separate process/machine, thus we need to capture the function call to serialize it and send # it across a socket build_fn = wrap_func(type(self).build_obj, *args, **kwargs) # Create and start the subprocess that will run the blocking object self.test_proc = LProcess(target=self._run_test_proc, args=(self.name, self.url, build_fn, self.config_fn, self.assert_fn,)) self.start_test()
def _config_url_and_test_proc(self, build_fn, always_run_as_subproc): # Add this object to the registry of testers from .mp_test_case import MPTestCase MPTestCase.testers.append(self) # Create Tester object in a VM if MPTestCase.vmnet_test_active and not always_run_as_subproc: assert hasattr( MPTestCase, 'ports' ), "VMNet test is active, but MPTestCase has no attribute ports" assert MPTestCase.ports, "VMNet test is active, but MPTestCase.ports is not set" name, ip = MPTestCase.next_container() assert name in MPTestCase.ports, "Node named {} not found in ports {}".format( name, MPTestCase.ports) assert MPTEST_PORT in MPTestCase.ports[name], "MPTEST_PORT {} not found in docker node {}'s ports {}"\ .format(MPTEST_PORT, name, MPTestCase.ports[name]) url = MPTestCase.ports[name][ MPTEST_PORT] # URL the orchestration node should connect to url = url.replace('localhost', '127.0.0.1') # Adjust localhost to 127.0.0.1 url = "tcp://{}".format(url) remote_url = "tcp://{}:{}".format( ip, MPTEST_PORT) # URL the remote node should bind to self.container_name = name self.url = url self.ip = ip self.log.notice( "Creating node named {} in a docker container with name {} and ip {}" .format(self.name, name, self.ip)) runner_func = wrap_func(_run_test_proc, self.name, remote_url, build_fn, self.config_fn, self.assert_fn) # TODO -- will i need a ton of imports and stuff to make this run smoothly...? MPTestCase.execute_python(name, runner_func, async=True) # Create Tester object in a Subprocess else: self.url = _gen_url(self.name) self.log.notice( "Creating node named {} in a subprocess with IPC url {}". format(self.name, self.url)) self.test_proc = LProcess(target=_run_test_proc, name=self.name, args=( self.name, self.url, build_fn, self.config_fn, self.assert_fn, )) self.test_proc.start()
class MPTesterBase: """ TODO docstring """ tester_cls = 'UNSET' def __init__(self, config_fn=None, assert_fn=None, name='TestableProcess', always_run_as_subproc=False, *args, **kwargs): super().__init__() self.log = get_logger(name) self.name = name self.config_fn = config_fn # Function to configure object with mocks self.assert_fn = assert_fn # Function to run assertions on said mocks self.test_proc = None self.container_name = None # Name of the docker container this object is proxying to (if run on VM) # Create a wrapper around the build_obj with args and kwargs. We do this b/c this function will actually be # invoked in a separate process/machine, thus we need to capture the function call to serialize it and send # it across a socket build_fn = wrap_func(type(self).build_obj, *args, **kwargs) self._config_url_and_test_proc(build_fn, always_run_as_subproc) # 'socket' is used to proxy commands to blocking object running in a child process (or possibly on a VM) self.ctx = zmq.Context() self.socket = self.ctx.socket(socket_type=zmq.PAIR) self.log.debug("Test Orchestrator connecting to url {}".format( self.url)) self.socket.connect(self.url) # Block this process until we get a ready signal from the subprocess/VM self.wait_for_test_object() def _config_url_and_test_proc(self, build_fn, always_run_as_subproc): # Add this object to the registry of testers from .mp_test_case import MPTestCase MPTestCase.testers.append(self) # Create Tester object in a VM if MPTestCase.vmnet_test_active and not always_run_as_subproc: assert hasattr( MPTestCase, 'ports' ), "VMNet test is active, but MPTestCase has no attribute ports" assert MPTestCase.ports, "VMNet test is active, but MPTestCase.ports is not set" name, ip = MPTestCase.next_container() assert name in MPTestCase.ports, "Node named {} not found in ports {}".format( name, MPTestCase.ports) assert MPTEST_PORT in MPTestCase.ports[name], "MPTEST_PORT {} not found in docker node {}'s ports {}"\ .format(MPTEST_PORT, name, MPTestCase.ports[name]) url = MPTestCase.ports[name][ MPTEST_PORT] # URL the orchestration node should connect to url = url.replace('localhost', '127.0.0.1') # Adjust localhost to 127.0.0.1 url = "tcp://{}".format(url) remote_url = "tcp://{}:{}".format( ip, MPTEST_PORT) # URL the remote node should bind to self.container_name = name self.url = url self.ip = ip self.log.notice( "Creating node named {} in a docker container with name {} and ip {}" .format(self.name, name, self.ip)) runner_func = wrap_func(_run_test_proc, self.name, remote_url, build_fn, self.config_fn, self.assert_fn) # TODO -- will i need a ton of imports and stuff to make this run smoothly...? MPTestCase.execute_python(name, runner_func, async=True) # Create Tester object in a Subprocess else: self.url = _gen_url(self.name) self.log.notice( "Creating node named {} in a subprocess with IPC url {}". format(self.name, self.url)) self.test_proc = LProcess(target=_run_test_proc, name=self.name, args=( self.name, self.url, build_fn, self.config_fn, self.assert_fn, )) self.test_proc.start() def wait_for_test_object(self): self.log.info("Tester waiting for rdy sig from test process...") msg = self.socket.recv_pyobj() assert msg == SIG_RDY, "Got msg from child thread {} but expected SIG_RDY".format( msg) self.log.info("GOT RDY SIG: {}".format(msg)) @classmethod def build_obj(cls, *args, **kwargs) -> tuple: """ Override to define how the blocking object should be initialized. Must return 2 element tuple, in the order <loop> (an EventLoop instance), and <object> (an instance of the blocking object to test). :return: Tuple of the form (loop, object_instance) It is assumed the object being returning is passed an event loop into its constructor, which it uses internally to schedule all its events. """ raise NotImplementedError def teardown(self): self.log.debug("{} tearing down...".format(self.name)) self.socket.close() if self.test_proc: self.log.debug("Joining tester terminate {}...".format(self.name)) self.test_proc.terminate() self.test_proc.join() self.log.debug("Tester Proc {} joined".format(self.name)) if self.container_name: # TODO clean up container??? pass self.log.debug("{} done tearing down.".format(self.name)) def __repr__(self): return '<' + self.name + " " + str(type(self)) + '>'
class MPTesterBase: """ TODO docstring """ testers = [] tester_cls = 'UNSET' def __init__(self, config_fn=None, assert_fn=None, name='TestableProcess', *args, **kwargs): super().__init__() self.log = get_logger(name) self.name = name self.url = _gen_url(name) self.config_fn = config_fn # Function to configure object with mocks self.assert_fn = assert_fn # Function to run assertions on said mocks # 'socket' is used to proxy commands to blocking object running in a child process (possibly on a VM) self.ctx = zmq.Context() self.socket = self.ctx.socket(socket_type=zmq.PAIR) self.socket.bind(self.url) # Add this object to the registry of testers MPTesterBase.testers.append(self) # Create a wrapper around the build_obj with args and kwargs. We do this b/c this function will actually be # invoked in a separate process/machine, thus we need to capture the function call to serialize it and send # it across a socket build_fn = wrap_func(type(self).build_obj, *args, **kwargs) # Create and start the subprocess that will run the blocking object self.test_proc = LProcess(target=self._run_test_proc, args=(self.name, self.url, build_fn, self.config_fn, self.assert_fn,)) self.start_test() def start_test(self): self.test_proc.start() # self.log.critical("\n\n attempting to execute stuff on the vm \n\n") # execute_python('node_8', wrap_func(start_vm_test, self.name, self.url, type(self).build_obj, # self.config_fn, self.assert_fn), async=True) # execute_python('node_8', wrap_func(start_vm_test, self.name, self.url, build_reactor_obj, # self.config_fn, self.assert_fn), async=True) # execute_python('node_8', wrap_func(start_vm_test), async=True) self.log.critical("tester waiting for child proc rdy sig...") msg = self.socket.recv_pyobj() assert msg == SIG_RDY, "Got msg from child thread {} but expected SIG_RDY".format(msg) self.log.critical("GOT RDY SIG: {}".format(msg)) def _run_test_proc(self, name, url, build_fn, config_fn, assert_fn): # TODO create socket outside of loop and pass it in for tester = MPTesterProcess(name=name, url=url, build_fn=build_fn, config_fn=config_fn, assert_fn=assert_fn) tester_socket = tester.socket try: tester.start_test() except Exception as e: self.log.error("\n\n TesterProcess encountered exception outside of internal loop! Error:\n {}\n\n" .format(traceback.format_exc())) tester_socket.send_pyobj(SIG_FAIL) tester._teardown() @classmethod def build_obj(cls, *args, **kwargs) -> tuple: """ Override to define how the blocking object should be initialized. Must return 2 element tuple, in the order <loop> (an EventLoop instance), and <object> (an instance of the blocking object to test). :return: Tuple of the form (loop, object_instance) It is assumed the object being returning is passed an event loop into its constructor, which it uses internally to schedule all its events. """ raise NotImplementedError def teardown(self): # self.log.critical("\n\nTEARING DOWN\n\n") self.socket.close() # self.log.debug("---- joining {} ---".format(self.test_proc.name)) self.test_proc.join() # self.log.debug("***** {} joined *****".format(self.test_proc.name)) def __repr__(self): return self.name + " " + str(type(self))
await asyncio.sleep(self.rest_time) @input(AnswerReply) def handle_answer(self, answer: AnswerReply, envelope: Envelope, **kwargs): self.log.important2("Got answer {} ".format(answer.answer)) if __name__ == "__main__": # All these kwargs get set as instance variables on the Producer instance. So, for example, # inside producer code, self.num_pokes = 36, self.rest_time = 3, ect ect producer = LProcess(target=Producer, kwargs={ 'name': 'Sir Producer', 'num_pokes': 36, 'rest_time': 3, 'pubsub_port': PUBSUB_PORT, 'dealroute_port': DEALROUTE_PORT, 'ip': LOCAL_HOST, 'filter': FILTER, 'signing_key': PROD_SK }) pubsub_consumer = LProcess(target=PubSubConsumer, kwargs={ 'name': 'Mr. PubSubConsumer', 'pubsub_port': PUBSUB_PORT, 'ip': LOCAL_HOST, 'filter': FILTER, 'signing_key': CONSUMER1_SK }) dealroute_consumer = LProcess(target=RepReplyConsumer,