mirror of https://gitlab.com/openlp/openlp.git
Fix issue #1382 by waiting for the service_manager to become available, or giving up after 2m
This commit is contained in:
parent
8052d29fa8
commit
3593ae22b3
|
@ -19,12 +19,16 @@
|
|||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from PyQt5 import QtCore, QtNetwork
|
||||
|
||||
from openlp.core.common.mixins import LogMixin
|
||||
from openlp.core.common.registry import Registry
|
||||
|
||||
# The maximum amount of time to wait before giving up, 120s
|
||||
MAX_WAIT_TIME_MS = 120000
|
||||
|
||||
|
||||
class Server(QtCore.QObject, LogMixin):
|
||||
"""
|
||||
|
@ -34,10 +38,12 @@ class Server(QtCore.QObject, LogMixin):
|
|||
def __init__(self):
|
||||
super(Server, self).__init__()
|
||||
self.out_socket = QtNetwork.QLocalSocket()
|
||||
self.server = None
|
||||
self.server: Optional[QtNetwork.QLocalServer] = None
|
||||
self.file_name: Optional[Path | str] = None
|
||||
self.id = 'OpenLPDual'
|
||||
self._ms_waited = 0
|
||||
|
||||
def is_another_instance_running(self):
|
||||
def is_another_instance_running(self) -> bool:
|
||||
"""
|
||||
Check the see if an other instance is running
|
||||
:return: True of False
|
||||
|
@ -46,7 +52,7 @@ class Server(QtCore.QObject, LogMixin):
|
|||
self.out_socket.connectToServer(self.id)
|
||||
return self.out_socket.waitForConnected()
|
||||
|
||||
def post_to_server(self, args):
|
||||
def post_to_server(self, args: list):
|
||||
"""
|
||||
Post the file name to the over instance
|
||||
:param args: The passed arguments including maybe a file name
|
||||
|
@ -62,7 +68,7 @@ class Server(QtCore.QObject, LogMixin):
|
|||
raise Exception(str(self.out_socket.errorString()))
|
||||
self.out_socket.disconnectFromServer()
|
||||
|
||||
def start_server(self):
|
||||
def start_server(self) -> bool:
|
||||
"""
|
||||
Start the socket server to allow inter app communication
|
||||
:return:
|
||||
|
@ -90,15 +96,23 @@ class Server(QtCore.QObject, LogMixin):
|
|||
self.in_stream.setCodec('UTF-8')
|
||||
self.in_socket.readyRead.connect(self._on_ready_read)
|
||||
|
||||
@QtCore.pyqtSlot()
|
||||
def _on_ready_read(self):
|
||||
"""
|
||||
Read a record passed to the server and pass to the service manager to handle
|
||||
:return:
|
||||
"""
|
||||
msg = self.in_stream.readLine()
|
||||
if msg:
|
||||
self.log_debug("socket msg = " + msg)
|
||||
Registry().get('service_manager').load_service(Path(msg))
|
||||
if not self.file_name:
|
||||
self.file_name = self.in_stream.readLine()
|
||||
self.log_debug(f'file name = "{self.file_name}"')
|
||||
if service_manager := Registry().get('service_manager'):
|
||||
service_manager.load_service(Path(self.file_name))
|
||||
elif self._ms_waited > MAX_WAIT_TIME_MS:
|
||||
self.log_error('OpenLP is taking too long to start up, abandoning file load')
|
||||
else:
|
||||
self.log_info('Service manager is not loaded yet, waiting 500ms')
|
||||
self._ms_waited += 500
|
||||
QtCore.QTimer.singleShot(500, self._on_ready_read)
|
||||
|
||||
def close_server(self):
|
||||
"""
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
##########################################################################
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
from typing import Generator
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from openlp.core.common.registry import Registry
|
||||
|
@ -27,14 +28,14 @@ from openlp.core.server import Server
|
|||
|
||||
|
||||
@pytest.fixture
|
||||
def server(registry):
|
||||
with patch('PyQt5.QtNetwork.QLocalSocket'):
|
||||
def server(registry: Registry) -> Generator:
|
||||
with patch('openlp.core.server.QtNetwork.QLocalSocket'):
|
||||
server = Server()
|
||||
yield server
|
||||
server.close_server()
|
||||
|
||||
|
||||
def test_is_another_instance_running(server):
|
||||
def test_is_another_instance_running(server: Server):
|
||||
"""
|
||||
Run a test as if this was the first time and no instance is running
|
||||
"""
|
||||
|
@ -49,7 +50,7 @@ def test_is_another_instance_running(server):
|
|||
assert isinstance(value, MagicMock)
|
||||
|
||||
|
||||
def test_is_another_instance_running_true(server):
|
||||
def test_is_another_instance_running_true(server: Server):
|
||||
"""
|
||||
Run a test as if there is another instance running
|
||||
"""
|
||||
|
@ -65,9 +66,9 @@ def test_is_another_instance_running_true(server):
|
|||
assert value is True
|
||||
|
||||
|
||||
def test_on_read_ready(server):
|
||||
def test_on_ready_read(server: Server):
|
||||
"""
|
||||
Test the on_read_ready method calls the service_manager
|
||||
Test the _on_ready_read method calls the service_manager
|
||||
"""
|
||||
# GIVEN: A server with a service manager
|
||||
server.in_stream = MagicMock()
|
||||
|
@ -84,29 +85,150 @@ def test_on_read_ready(server):
|
|||
service_manager.load_service.assert_called_once_with(Path(file_name))
|
||||
|
||||
|
||||
@patch("PyQt5.QtCore.QTextStream")
|
||||
def test_post_to_server(mocked_stream, server):
|
||||
@patch('openlp.core.server.QtCore.QTimer')
|
||||
def test_on_ready_read_no_service_manager(MockQTimer: MagicMock, server: Server):
|
||||
"""
|
||||
Check that the _on_ready_read method calls a timer when the service_manager is not yet available
|
||||
"""
|
||||
# GIVEN: A server with a service manager
|
||||
server.in_stream = MagicMock()
|
||||
|
||||
# WHEN: a file is added to the socket and the method called
|
||||
file_name = '\\home\\superfly\\'
|
||||
server.in_stream.readLine.return_value = file_name
|
||||
server._on_ready_read()
|
||||
|
||||
# THEN: the service will be loaded
|
||||
assert server._ms_waited == 500
|
||||
MockQTimer.singleShot.assert_called_once_with(500, server._on_ready_read)
|
||||
|
||||
|
||||
def test_on_ready_read_giving_up(server: Server):
|
||||
"""
|
||||
Check that the _on_ready_read gives up when it has waited for 2 minutes and the service manager is not available
|
||||
"""
|
||||
# GIVEN: A server that has waited too long
|
||||
server.file_name = '/path/to/service.osz'
|
||||
server._ms_waited = 120500
|
||||
|
||||
# WHEN: _on_ready_read has been called from the timer
|
||||
with patch.object(server, 'log_error') as mocked_log_error:
|
||||
server._on_ready_read()
|
||||
|
||||
# THEN: the service will be loaded
|
||||
mocked_log_error.assert_called_once_with('OpenLP is taking too long to start up, abandoning file load')
|
||||
|
||||
|
||||
@patch('openlp.core.server.QtCore.QTextStream')
|
||||
def test_post_to_server(MockStream: MagicMock, server: Server):
|
||||
"""
|
||||
A Basic test with a post to the service
|
||||
:return:
|
||||
"""
|
||||
# GIVEN: A server
|
||||
# WHEN: I post to a server
|
||||
server.post_to_server(['l', 'a', 'm', 'a', 's'])
|
||||
server.post_to_server(['l', 'l', 'a', 'm', 'a', 's'])
|
||||
|
||||
# THEN: the file should be passed out to the socket
|
||||
server.out_socket.write.assert_called_once_with(b'lamas')
|
||||
server.out_socket.write.assert_called_once_with(b'llamas')
|
||||
|
||||
|
||||
@patch("PyQt5.QtCore.QTextStream")
|
||||
def test_post_to_server_openlp(mocked_stream, server):
|
||||
@patch('openlp.core.server.QtCore.QTextStream')
|
||||
def test_post_to_server_openlp(MockStream: MagicMock, server: Server):
|
||||
"""
|
||||
A Basic test with a post to the service with OpenLP
|
||||
:return:
|
||||
"""
|
||||
# GIVEN: A server
|
||||
# WHEN: I post to a server
|
||||
server.post_to_server(['l', 'a', 'm', 'a', 's', 'OpenLP'])
|
||||
server.post_to_server(['l', 'l', 'a', 'm', 'a', 's', 'OpenLP'])
|
||||
|
||||
# THEN: the file should be passed out to the socket
|
||||
server.out_socket.write.assert_called_once_with(b'lamas')
|
||||
server.out_socket.write.assert_called_once_with(b'llamas')
|
||||
|
||||
|
||||
@patch('openlp.core.server.QtCore.QTextStream')
|
||||
def test_post_to_server_openlp_exception(MockStream: MagicMock, server: Server):
|
||||
"""Test that we raise an exception when there are no bytes written"""
|
||||
# GIVEN: A server and a mocked stream
|
||||
server.out_socket.waitForBytesWritten.return_value = False
|
||||
server.out_socket.errorString.return_value = 'Error writing to socket'
|
||||
|
||||
# WHEN: post_to_server is called
|
||||
# THEN: An exception is raised
|
||||
with pytest.raises(Exception) as e:
|
||||
server.post_to_server(['filename'])
|
||||
assert 'Error writing to socket' in str(e)
|
||||
|
||||
|
||||
@patch('openlp.core.server.QtNetwork.QLocalServer')
|
||||
def test_start_server(MockLocalServer: MagicMock, server: Server):
|
||||
"""Test the start server method works correctly"""
|
||||
# GIVEN: A server
|
||||
server.out_stream = MagicMock()
|
||||
server.out_socket = MagicMock()
|
||||
server.in_stream = MagicMock()
|
||||
server.in_socket = MagicMock()
|
||||
mocked_server = MagicMock()
|
||||
MockLocalServer.return_value = mocked_server
|
||||
|
||||
# WHEN: start_server is called
|
||||
result = server.start_server()
|
||||
|
||||
# THEN: The server should have been started
|
||||
assert result is True
|
||||
assert server.out_socket is None
|
||||
assert server.out_stream is None
|
||||
assert server.in_socket is None
|
||||
assert server.in_stream is None
|
||||
assert server.server is mocked_server
|
||||
mocked_server.listen.assert_called_once_with(server.id)
|
||||
mocked_server.newConnection.connect.assert_called_once_with(server._on_new_connection)
|
||||
|
||||
|
||||
@patch('openlp.core.server.QtCore.QTextStream')
|
||||
def test_on_new_connection(MockTextStream: MagicMock, server: Server):
|
||||
"""Test that the _on_new_connection slot works correctly"""
|
||||
# GIVEN: A server with some mocked properties
|
||||
mocked_stream = MagicMock()
|
||||
MockTextStream.return_value = mocked_stream
|
||||
server.in_socket = MagicMock()
|
||||
mocked_next_socket = MagicMock()
|
||||
server.server = MagicMock(**{'nextPendingConnection.return_value': mocked_next_socket})
|
||||
|
||||
# WHEN: _on_new_connection is called
|
||||
server._on_new_connection()
|
||||
|
||||
# THEN: The correct methods and attributes should have been called/set up
|
||||
assert server.in_socket is mocked_next_socket
|
||||
assert server.in_stream is mocked_stream
|
||||
MockTextStream.assert_called_once_with(mocked_next_socket)
|
||||
mocked_stream.setCodec.assert_called_once_with('UTF-8')
|
||||
mocked_next_socket.readyRead.connect.assert_called_once_with(server._on_ready_read)
|
||||
|
||||
|
||||
@patch('openlp.core.server.QtCore.QTextStream')
|
||||
def test_on_new_connection_no_socket(MockTextStream: MagicMock, server: Server):
|
||||
"""Test that the _on_new_connection slot works correctly"""
|
||||
# GIVEN: A server with some mocked properties
|
||||
server.in_socket = MagicMock()
|
||||
server.server = MagicMock(**{'nextPendingConnection.return_value': None})
|
||||
|
||||
# WHEN: _on_new_connection is called
|
||||
server._on_new_connection()
|
||||
|
||||
# THEN: The correct methods and attributes should have been called/set up
|
||||
assert server.in_socket is None
|
||||
MockTextStream.assert_not_called()
|
||||
|
||||
|
||||
def test_close_server(server: Server):
|
||||
"""Test that the server is closed"""
|
||||
# GIVEN: A server
|
||||
server.server = MagicMock()
|
||||
|
||||
# WHEN: The close_server() method is called
|
||||
server.close_server()
|
||||
|
||||
# THEN: The server is closed
|
||||
server.server.close.assert_called_once_with()
|
||||
|
|
Loading…
Reference in New Issue