def __init__(self, name: str, desc: str, author: str, licence: str, core: Core): """ :param name: Name of the plugin :param desc: Description of the plugin :param author: Author of the plugin :param licence: License of the plugin :param core: Core instance """ # [<name>, <description>, <author>, <license>] self._metadata = [name, desc, author, licence] self.function_meta = {} # active threads self._active_threads = {int: Thread()} core.set_run_handler(self._handle_run) self.core = core # A dict containing all functions this plugin wants to register self.functions = {} for f in RemoteFunction.remote_functions: self.functions[f.__name__] = f self.function_meta[f.__name__] = [f.__doc__, f.args]
def test_connect_functional(self): core = Core() mock_sock = mocks.connection_socket(core._rpc._connection) core._rpc._connection.listen = Mock() core._rpc._connection._init_crypto = Mock() core.connect("localhost", 1234) ip = mock_sock.connect.call_args[0][0][0] port = mock_sock.connect.call_args[0][0][1] self.assertEqual(ip, socket.gethostbyname("localhost")) self.assertEqual(port, 1234)
def test_run_incoming(self): mock_foo = Mock() def foo(a: ctypes.c_bool, b: ctypes.c_byte, c: ctypes.c_uint64, d: ctypes.c_int64, e: ctypes.c_double, f: ctypes.c_char_p, g: ctypes.c_long): mock_foo(a, b, c, d, e, f, g) RemoteFunction(foo) core = Core() plug = Plugin("foo", "bar", "bob", "alice", core) mock_send = mocks.core_rpc_send(core) # pretend that we received a message call = ApiRun("id", "foo", [True, b'hi', 5, -82, 7.23, "hi", 64]) call.msg.arguments[0][0] = None # remove plugin_id call.msg.arguments[0][1] = 123 # set some call id core._rpc._message_callback(call.msg.pack()) # wait for execution to finish plug._active_threads[123].join() mock_foo.assert_called_with(True, b'hi', 5, -82, 7.23, "hi", 64) # check response msg = mock_send.call_args_list[0][0][0] self.assertEqual(msg.get_type(), 1) self.assertEqual(msg.get_msgid(), call.msg.get_msgid()) self.assertEqual(msg.error, None) self.assertEqual(msg.response[0], 123)
def test_run_functional(self): core = Core() rplug = RemotePlugin("plugin_id", "foo", "bar", "bob", "alice", core) mock_send = mocks.rpc_connection_send(core._rpc) rplug.run("function", [1, "hi", 42.317, b'hi']) outgoing = msgpack.unpackb(mock_send.call_args[0][0]) self.assertEqual(0, outgoing[0]) self.assertEqual(b'run', outgoing[2]) self.assertEqual([b'plugin_id', None], outgoing[3][0]) self.assertEqual(b"function", outgoing[3][1]) self.assertEqual([1, b'hi', 42.317, b'hi'], outgoing[3][2])
def test_10_handle_run(self): core = Core() plug = Plugin("foo", "bar", "bob", "alice", core) send = mocks.core_rpc_send(core) # catch results/responses mock = Mock() mock.__name__ = "foo" mock.return_value = "return" plug.functions["foo"] = mock plug.function_meta["foo"] = (["", []]) msg = MRequest() msg.function = "run" msg.arguments = [[None, 123], b'foo', [1, 1.1, "hi"]] error, response = plug._handle_run(msg) self.assertIsNotNone(plug._active_threads.get(123)) plug._active_threads.pop(123).join() mock.assert_called_with([1, 1.1, "hi"]) # request was valid + 1x result self.assertEqual(send.call_count, 1) self.assertEqual(send.call_args_list[0][0][0].arguments[0][0], 123) self.assertEqual(send.call_args_list[0][0][0].arguments[1][0], "return") # (response is sent by msgpack-rpc handler) self.assertEqual(response, [123]) self.assertIsNone(error) send.reset_mock() # reset call count msg.arguments = [[None, 123], b'mock', [1, 1.1, "hi"]] error, response = plug._handle_run(msg) # request was invalid -> error response self.assertEqual(error, [404, "Function does not exist!"]) self.assertIsNone(response) with self.assertRaises(KeyError): plug._active_threads[123] send.reset_mock() # reset call count msg.arguments = [None, b'mock', [1, 1.1, "hi"]] error, response = plug._handle_run(msg) # request was invalid -> error response self.assertEqual(error, [400, "Message is not a valid run call"]) self.assertIsNone(response) with self.assertRaises(KeyError): plug._active_threads[123]
def test_complete_run(self): # In this test a plugin is created and is calling itself. # The called_lock is used to ensure that we receive # the response before the result called_lock = Lock() called_lock.acquire() def add(a: ctypes.c_int64, b: ctypes.c_int64): "add two ints" called_lock.acquire() return a + b RemoteFunction(add) core = Core() plug = Plugin("foo", "bar", "bob", "alice", core) rplug = RemotePlugin("plugin_id", "foo", "bar", "bob", "alice", core) mock_send = mocks.rpc_connection_send(core._rpc) result = rplug.run("add", [7, 8]) # receive request msg = MRequest.from_unpacked(msgpack.unpackb( mock_send.call_args[0][0])) msg.arguments[0][0] = None # remove plugin id msg.arguments[0][1] = 123 # set call id core._rpc._message_callback(msg.pack()) # receive response data = mock_send.call_args_list[1][0][0] core._rpc._message_callback(data) # start execution called_lock.release() # wait for execution to finish plug._active_threads[123].join() # receive result data = mock_send.call_args_list[2][0][0] core._rpc._message_callback(data) self.assertEqual(result.get_result(blocking=True), 15) self.assertEqual(result.get_status(), 2) self.assertEqual(result._error, None) self.assertEqual(result.get_id(), 123)
def test_register_functional(self): def fun2(a: ctypes.c_bool, b: ctypes.c_byte, c: ctypes.c_uint64, d: ctypes.c_int64, e: ctypes.c_double, f: ctypes.c_char_p, g: ctypes.c_long): pass RemoteFunction(fun2) core = Core() plug = Plugin("foo", "bar", "bob", "alice", core) mock_send = mocks.rpc_connection_send(core._rpc) plug.register(blocking=False) outgoing = msgpack.unpackb(mock_send.call_args[0][0]) self.assertEqual(0, outgoing[0]) self.assertEqual(b'register', outgoing[2]) self.assertEqual([b"foo", b"bar", b"bob", b"alice"], outgoing[3][0]) self.assertIn([b'fun2', b'', [False, b'', 3, -1, 2.0, b'', -1]], outgoing[3][1])
def test_00_register(self): core = Core() plug = Plugin("foo", "bar", "bob", "alice", core) rpc_send_mock = mocks.core_rpc_send(core) plug.register(blocking=False) call_args = rpc_send_mock.call_args[0][0] self.assertEqual(call_args._type, 0) # 0 indicates message request self.assertTrue(0 <= call_args._msgid < pow(2, 32)) # valid msgid self.assertEqual(call_args.function, 'register') # register request self.assertEqual(call_args.arguments[0], ['foo', 'bar', 'bob', 'alice']) # metadata self.assertEquals( len(call_args.arguments[1]), 0) # no function registered
def test_complete_register(self): def fun(): pass RemoteFunction(fun) core = Core() plug = Plugin("foo", "bar", "bob", "alice", core) mock_send = mocks.rpc_connection_send(core._rpc) result = plug.register(blocking=False) outgoing = msgpack.unpackb(mock_send.call_args_list[0][0][0]) # validate outgoing self.assertEqual(0, outgoing[0]) self.assertEqual(b'register', outgoing[2]) self.assertEqual([b"foo", b"bar", b"bob", b"alice"], outgoing[3][0]) self.assertIn([b'fun', b'', []], outgoing[3][1]) # test response handling self.assertEqual(result.get_status(), 0) # no response yet response = MResponse(outgoing[1]) # send invalid response (Second field is set to None) core._rpc._handle_response(response) self.assertEqual(result.get_status(), -1) # make sure response is only handled once with self.assertRaises(KeyError): core._rpc._handle_response(response) # test valid response result = plug.register(blocking=False) outgoing = msgpack.unpackb(mock_send.call_args_list[1][0][0]) response = MResponse(outgoing[1]) response.response = [] core._rpc._handle_response(response) self.assertEqual(result.get_status(), 2) # cleanup remote_functions RemoteFunction.remote_functions = {}
def connect(addr: str, port: int): global __core __core = Core() __core.connect(addr, port)