lms.testing.serverrunner
1import logging 2import typing 3 4import edq.core.errors 5import edq.net.exchange 6import edq.net.request 7import edq.net.settings 8import edq.testing.serverrunner 9import edq.util.parse 10import edq.util.reflection 11 12import lms.backend.instance 13import lms.cli.parser 14import lms.model.backend 15import lms.model.constants 16import lms.util.net 17 18BACKEND_REQUEST_CLEANING_FUNCS: typing.Dict[typing.Union[lms.model.constants.BackendType, None], typing.Callable] = { 19 lms.model.constants.BackendType.BLACKBOARD: lms.util.net.clean_blackboard_response, 20 lms.model.constants.BackendType.CANVAS: lms.util.net.clean_canvas_response, 21 lms.model.constants.BackendType.MOODLE: lms.util.net.clean_moodle_response, 22} 23 24BACKEND_EXCHANGE_FINALIZING_FUNCS: typing.Dict[typing.Union[lms.model.constants.BackendType, None], typing.Callable] = { 25 lms.model.constants.BackendType.CANVAS: lms.util.net.finalize_canvas_exchange, 26 lms.model.constants.BackendType.MOODLE: lms.util.net.finalize_moodle_exchange, 27} 28 29class LMSServerRunner(edq.testing.serverrunner.ServerRunner): 30 """ A server runner specifically for LMS servers. """ 31 32 def __init__(self, 33 backend_type: typing.Union[lms.model.constants.BackendType, str, None] = None, 34 **kwargs: typing.Any) -> None: 35 super().__init__(**kwargs) 36 37 if (isinstance(backend_type, str)): 38 backend_type = lms.model.constants.BackendType(backend_type) 39 40 self.backend_type: typing.Union[lms.model.constants.BackendType, None] = backend_type 41 """ 42 The type of server being run. 43 This value will be resolved after the server is started 44 (since part of resolution may involve pinging the server. 45 """ 46 47 self.backend: typing.Union[lms.model.backend.APIBackend, None] = None 48 """ 49 The backend currently being used while this server runner is active. 50 This may not always be set. 51 """ 52 53 self._old_exchanges_clean_response_func: typing.Union[str, None] = None 54 """ 55 The value of edq.net.settings.get_exchanges_clean_response_func() when start() is called. 56 The original value may be changed in start(), and will be reset in stop(). 57 """ 58 59 self._old_exchanges_finalize_func: typing.Union[str, None] = None 60 """ 61 The value of edq.net.settings.get_exchanges_finalize_func() when start() is called. 62 The original value may be changed in start(), and will be reset in stop(). 63 """ 64 65 self._old_set_exchanges_clean_response_func: bool = False 66 """ 67 The value of lms.cli.parser._set_exchanges_clean_response_func when start() is called. 68 The original value may be changed in start(), and will be reset in stop(). 69 """ 70 71 self._old_request_complete_callback: typing.Union[edq.net.exchange.HTTPExchangeComplete, None] = None 72 """ 73 The value of edq.net.settings.get_request_complete_callback() when start() is called. 74 The original value may be changed in start(), and will be reset in stop(). 75 """ 76 77 self._old_serverrunner_logging_level: typing.Union[int, None] = None 78 """ 79 The logging level for the edq server runner befoe tests are run. 80 The original value may be changed in start(), and will be reset in stop(). 81 """ 82 83 def start(self) -> None: 84 # Set configs. 85 86 exchange_clean_func = BACKEND_REQUEST_CLEANING_FUNCS.get(self.backend_type, lms.util.net.clean_lms_response) 87 exchange_clean_func_name = edq.util.reflection.get_qualified_name(exchange_clean_func) 88 self._old_exchanges_clean_response_func = edq.net.settings.get_exchanges_clean_response_func() 89 edq.net.settings.set_exchanges_clean_response_func(exchange_clean_func_name) 90 91 self._old_exchanges_finalize_func = edq.net.settings.get_exchanges_finalize_func() 92 exchange_finalize_func = BACKEND_EXCHANGE_FINALIZING_FUNCS.get(self.backend_type, None) 93 if (exchange_finalize_func is not None): 94 exchange_finalize_func_name = edq.util.reflection.get_qualified_name(exchange_finalize_func) 95 edq.net.settings.set_exchanges_finalize_func(exchange_finalize_func_name) 96 else: 97 edq.net.settings.set_exchanges_finalize_func() 98 99 self._old_set_exchanges_clean_response_func = lms.cli.parser._set_exchanges_clean_response_func 100 lms.cli.parser._set_exchanges_clean_response_func = False 101 102 def _request_complete_callback(exchange: edq.net.exchange.HTTPExchange) -> None: 103 # Restart if the request is a write. 104 if (edq.util.parse.boolean(exchange.headers.get(lms.model.constants.HEADER_KEY_WRITE, False))): 105 self.restart() 106 107 self._old_request_complete_callback = edq.net.settings.get_request_complete_callback() 108 edq.net.settings.set_request_complete_callback(typing.cast(edq.net.exchange.HTTPExchangeComplete, _request_complete_callback)) 109 110 # Disable logging from the runner, since it may disrupt CLI tests. 111 logger = logging.getLogger('edq.testing.serverrunner') 112 self._old_serverrunner_logging_level = logger.level 113 logger.setLevel(logging.WARNING) 114 115 # Start the server. 116 super().start() 117 118 def stop(self) -> bool: 119 if (self._old_serverrunner_logging_level is not None): 120 logger = logging.getLogger('edq.testing.serverrunner') 121 logger.setLevel(self._old_serverrunner_logging_level) 122 self._old_serverrunner_logging_level = None 123 124 if (not super().stop()): 125 return False 126 127 # Restore old configs. 128 129 edq.net.settings.set_exchanges_clean_response_func(self._old_exchanges_clean_response_func) 130 self._old_exchanges_clean_response_func = None 131 132 lms.cli.parser._set_exchanges_clean_response_func = self._old_set_exchanges_clean_response_func 133 self._old_set_exchanges_clean_response_func = False 134 135 edq.net.settings.set_request_complete_callback(self._old_request_complete_callback) 136 self._old_request_complete_callback = None 137 138 edq.net.settings.set_exchanges_finalize_func(self._old_exchanges_finalize_func) 139 self._old_exchanges_finalize_func = None 140 141 return True 142 143 def restart(self) -> None: 144 super().restart() 145 146 # Inform the backend that a restart happened. 147 if (self.backend is not None): 148 self.backend.reset_connection() 149 150 def identify_server(self) -> bool: 151 try: 152 backend_type = lms.backend.instance.guess_backend_type_from_request(self.server, timeout_secs = self.identify_wait_secs) 153 except edq.core.errors.RetryError: 154 return False 155 156 return (backend_type is not None)
BACKEND_REQUEST_CLEANING_FUNCS: Dict[Optional[lms.model.constants.BackendType], Callable] =
{<BackendType.BLACKBOARD: 'blackboard'>: <function clean_blackboard_response>, <BackendType.CANVAS: 'canvas'>: <function clean_canvas_response>, <BackendType.MOODLE: 'moodle'>: <function clean_moodle_response>}
BACKEND_EXCHANGE_FINALIZING_FUNCS: Dict[Optional[lms.model.constants.BackendType], Callable] =
{<BackendType.CANVAS: 'canvas'>: <function finalize_canvas_exchange>, <BackendType.MOODLE: 'moodle'>: <function finalize_moodle_exchange>}
class
LMSServerRunner(edq.testing.serverrunner.ServerRunner):
30class LMSServerRunner(edq.testing.serverrunner.ServerRunner): 31 """ A server runner specifically for LMS servers. """ 32 33 def __init__(self, 34 backend_type: typing.Union[lms.model.constants.BackendType, str, None] = None, 35 **kwargs: typing.Any) -> None: 36 super().__init__(**kwargs) 37 38 if (isinstance(backend_type, str)): 39 backend_type = lms.model.constants.BackendType(backend_type) 40 41 self.backend_type: typing.Union[lms.model.constants.BackendType, None] = backend_type 42 """ 43 The type of server being run. 44 This value will be resolved after the server is started 45 (since part of resolution may involve pinging the server. 46 """ 47 48 self.backend: typing.Union[lms.model.backend.APIBackend, None] = None 49 """ 50 The backend currently being used while this server runner is active. 51 This may not always be set. 52 """ 53 54 self._old_exchanges_clean_response_func: typing.Union[str, None] = None 55 """ 56 The value of edq.net.settings.get_exchanges_clean_response_func() when start() is called. 57 The original value may be changed in start(), and will be reset in stop(). 58 """ 59 60 self._old_exchanges_finalize_func: typing.Union[str, None] = None 61 """ 62 The value of edq.net.settings.get_exchanges_finalize_func() when start() is called. 63 The original value may be changed in start(), and will be reset in stop(). 64 """ 65 66 self._old_set_exchanges_clean_response_func: bool = False 67 """ 68 The value of lms.cli.parser._set_exchanges_clean_response_func when start() is called. 69 The original value may be changed in start(), and will be reset in stop(). 70 """ 71 72 self._old_request_complete_callback: typing.Union[edq.net.exchange.HTTPExchangeComplete, None] = None 73 """ 74 The value of edq.net.settings.get_request_complete_callback() when start() is called. 75 The original value may be changed in start(), and will be reset in stop(). 76 """ 77 78 self._old_serverrunner_logging_level: typing.Union[int, None] = None 79 """ 80 The logging level for the edq server runner befoe tests are run. 81 The original value may be changed in start(), and will be reset in stop(). 82 """ 83 84 def start(self) -> None: 85 # Set configs. 86 87 exchange_clean_func = BACKEND_REQUEST_CLEANING_FUNCS.get(self.backend_type, lms.util.net.clean_lms_response) 88 exchange_clean_func_name = edq.util.reflection.get_qualified_name(exchange_clean_func) 89 self._old_exchanges_clean_response_func = edq.net.settings.get_exchanges_clean_response_func() 90 edq.net.settings.set_exchanges_clean_response_func(exchange_clean_func_name) 91 92 self._old_exchanges_finalize_func = edq.net.settings.get_exchanges_finalize_func() 93 exchange_finalize_func = BACKEND_EXCHANGE_FINALIZING_FUNCS.get(self.backend_type, None) 94 if (exchange_finalize_func is not None): 95 exchange_finalize_func_name = edq.util.reflection.get_qualified_name(exchange_finalize_func) 96 edq.net.settings.set_exchanges_finalize_func(exchange_finalize_func_name) 97 else: 98 edq.net.settings.set_exchanges_finalize_func() 99 100 self._old_set_exchanges_clean_response_func = lms.cli.parser._set_exchanges_clean_response_func 101 lms.cli.parser._set_exchanges_clean_response_func = False 102 103 def _request_complete_callback(exchange: edq.net.exchange.HTTPExchange) -> None: 104 # Restart if the request is a write. 105 if (edq.util.parse.boolean(exchange.headers.get(lms.model.constants.HEADER_KEY_WRITE, False))): 106 self.restart() 107 108 self._old_request_complete_callback = edq.net.settings.get_request_complete_callback() 109 edq.net.settings.set_request_complete_callback(typing.cast(edq.net.exchange.HTTPExchangeComplete, _request_complete_callback)) 110 111 # Disable logging from the runner, since it may disrupt CLI tests. 112 logger = logging.getLogger('edq.testing.serverrunner') 113 self._old_serverrunner_logging_level = logger.level 114 logger.setLevel(logging.WARNING) 115 116 # Start the server. 117 super().start() 118 119 def stop(self) -> bool: 120 if (self._old_serverrunner_logging_level is not None): 121 logger = logging.getLogger('edq.testing.serverrunner') 122 logger.setLevel(self._old_serverrunner_logging_level) 123 self._old_serverrunner_logging_level = None 124 125 if (not super().stop()): 126 return False 127 128 # Restore old configs. 129 130 edq.net.settings.set_exchanges_clean_response_func(self._old_exchanges_clean_response_func) 131 self._old_exchanges_clean_response_func = None 132 133 lms.cli.parser._set_exchanges_clean_response_func = self._old_set_exchanges_clean_response_func 134 self._old_set_exchanges_clean_response_func = False 135 136 edq.net.settings.set_request_complete_callback(self._old_request_complete_callback) 137 self._old_request_complete_callback = None 138 139 edq.net.settings.set_exchanges_finalize_func(self._old_exchanges_finalize_func) 140 self._old_exchanges_finalize_func = None 141 142 return True 143 144 def restart(self) -> None: 145 super().restart() 146 147 # Inform the backend that a restart happened. 148 if (self.backend is not None): 149 self.backend.reset_connection() 150 151 def identify_server(self) -> bool: 152 try: 153 backend_type = lms.backend.instance.guess_backend_type_from_request(self.server, timeout_secs = self.identify_wait_secs) 154 except edq.core.errors.RetryError: 155 return False 156 157 return (backend_type is not None)
A server runner specifically for LMS servers.
LMSServerRunner( backend_type: Union[lms.model.constants.BackendType, str, NoneType] = None, **kwargs: Any)
33 def __init__(self, 34 backend_type: typing.Union[lms.model.constants.BackendType, str, None] = None, 35 **kwargs: typing.Any) -> None: 36 super().__init__(**kwargs) 37 38 if (isinstance(backend_type, str)): 39 backend_type = lms.model.constants.BackendType(backend_type) 40 41 self.backend_type: typing.Union[lms.model.constants.BackendType, None] = backend_type 42 """ 43 The type of server being run. 44 This value will be resolved after the server is started 45 (since part of resolution may involve pinging the server. 46 """ 47 48 self.backend: typing.Union[lms.model.backend.APIBackend, None] = None 49 """ 50 The backend currently being used while this server runner is active. 51 This may not always be set. 52 """ 53 54 self._old_exchanges_clean_response_func: typing.Union[str, None] = None 55 """ 56 The value of edq.net.settings.get_exchanges_clean_response_func() when start() is called. 57 The original value may be changed in start(), and will be reset in stop(). 58 """ 59 60 self._old_exchanges_finalize_func: typing.Union[str, None] = None 61 """ 62 The value of edq.net.settings.get_exchanges_finalize_func() when start() is called. 63 The original value may be changed in start(), and will be reset in stop(). 64 """ 65 66 self._old_set_exchanges_clean_response_func: bool = False 67 """ 68 The value of lms.cli.parser._set_exchanges_clean_response_func when start() is called. 69 The original value may be changed in start(), and will be reset in stop(). 70 """ 71 72 self._old_request_complete_callback: typing.Union[edq.net.exchange.HTTPExchangeComplete, None] = None 73 """ 74 The value of edq.net.settings.get_request_complete_callback() when start() is called. 75 The original value may be changed in start(), and will be reset in stop(). 76 """ 77 78 self._old_serverrunner_logging_level: typing.Union[int, None] = None 79 """ 80 The logging level for the edq server runner befoe tests are run. 81 The original value may be changed in start(), and will be reset in stop(). 82 """
backend_type: Optional[lms.model.constants.BackendType]
The type of server being run. This value will be resolved after the server is started (since part of resolution may involve pinging the server.
backend: Optional[lms.model.backend.APIBackend]
The backend currently being used while this server runner is active. This may not always be set.
def
start(self) -> None:
84 def start(self) -> None: 85 # Set configs. 86 87 exchange_clean_func = BACKEND_REQUEST_CLEANING_FUNCS.get(self.backend_type, lms.util.net.clean_lms_response) 88 exchange_clean_func_name = edq.util.reflection.get_qualified_name(exchange_clean_func) 89 self._old_exchanges_clean_response_func = edq.net.settings.get_exchanges_clean_response_func() 90 edq.net.settings.set_exchanges_clean_response_func(exchange_clean_func_name) 91 92 self._old_exchanges_finalize_func = edq.net.settings.get_exchanges_finalize_func() 93 exchange_finalize_func = BACKEND_EXCHANGE_FINALIZING_FUNCS.get(self.backend_type, None) 94 if (exchange_finalize_func is not None): 95 exchange_finalize_func_name = edq.util.reflection.get_qualified_name(exchange_finalize_func) 96 edq.net.settings.set_exchanges_finalize_func(exchange_finalize_func_name) 97 else: 98 edq.net.settings.set_exchanges_finalize_func() 99 100 self._old_set_exchanges_clean_response_func = lms.cli.parser._set_exchanges_clean_response_func 101 lms.cli.parser._set_exchanges_clean_response_func = False 102 103 def _request_complete_callback(exchange: edq.net.exchange.HTTPExchange) -> None: 104 # Restart if the request is a write. 105 if (edq.util.parse.boolean(exchange.headers.get(lms.model.constants.HEADER_KEY_WRITE, False))): 106 self.restart() 107 108 self._old_request_complete_callback = edq.net.settings.get_request_complete_callback() 109 edq.net.settings.set_request_complete_callback(typing.cast(edq.net.exchange.HTTPExchangeComplete, _request_complete_callback)) 110 111 # Disable logging from the runner, since it may disrupt CLI tests. 112 logger = logging.getLogger('edq.testing.serverrunner') 113 self._old_serverrunner_logging_level = logger.level 114 logger.setLevel(logging.WARNING) 115 116 # Start the server. 117 super().start()
Start the server.
def
stop(self) -> bool:
119 def stop(self) -> bool: 120 if (self._old_serverrunner_logging_level is not None): 121 logger = logging.getLogger('edq.testing.serverrunner') 122 logger.setLevel(self._old_serverrunner_logging_level) 123 self._old_serverrunner_logging_level = None 124 125 if (not super().stop()): 126 return False 127 128 # Restore old configs. 129 130 edq.net.settings.set_exchanges_clean_response_func(self._old_exchanges_clean_response_func) 131 self._old_exchanges_clean_response_func = None 132 133 lms.cli.parser._set_exchanges_clean_response_func = self._old_set_exchanges_clean_response_func 134 self._old_set_exchanges_clean_response_func = False 135 136 edq.net.settings.set_request_complete_callback(self._old_request_complete_callback) 137 self._old_request_complete_callback = None 138 139 edq.net.settings.set_exchanges_finalize_func(self._old_exchanges_finalize_func) 140 self._old_exchanges_finalize_func = None 141 142 return True
Stop the server. Return true if child classes should perform shutdown behavior.
def
restart(self) -> None:
144 def restart(self) -> None: 145 super().restart() 146 147 # Inform the backend that a restart happened. 148 if (self.backend is not None): 149 self.backend.reset_connection()
Restart the server.
def
identify_server(self) -> bool:
151 def identify_server(self) -> bool: 152 try: 153 backend_type = lms.backend.instance.guess_backend_type_from_request(self.server, timeout_secs = self.identify_wait_secs) 154 except edq.core.errors.RetryError: 155 return False 156 157 return (backend_type is not None)
Attempt to identify the target server and return true on a successful attempt. This is used on startup to wait for the server to complete startup.
Child classes must implement this or set self.startup_skip_identify to true.