diff --git a/setup.py b/setup.py index 9bf059b4..2f458abe 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ from setuptools import setup, find_packages -__version__ = "7.9.1" +__version__ = "7.9.2" result_handlers = [ "db = rotest.core.result.handlers.db_handler:DBHandler", @@ -34,7 +34,7 @@ url="https://github.com/gregoil/rotest", keywords="testing system django unittest", install_requires=[ - 'django>=1.8,<2.0', + 'django>=1.8,<2', 'py', 'ipdbugger>=2.5', 'xlwt', @@ -50,7 +50,7 @@ 'cached_property', 'channels>=1,<2', 'websocket-client>=0.56', - 'pywin32<224; sys.platform == "win32"' + 'pywin32<224; sys.platform == "win32"', ], extras_require={ ':python_version=="2.7"': ['statistics'], @@ -64,6 +64,7 @@ "pathlib2", "flake8", "pylint", + "waiting", ] }, python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*", diff --git a/src/rotest/management/client/websocket_client.py b/src/rotest/management/client/websocket_client.py index 204c316f..f35f0a49 100644 --- a/src/rotest/management/client/websocket_client.py +++ b/src/rotest/management/client/websocket_client.py @@ -1,9 +1,11 @@ """Websocket client that sends pings periodically.""" -# pylint: disable=broad-except,bare-except +# pylint: disable=broad-except,bare-except,no-self-use import threading import websocket +from rotest.common import core_log + class PingingWebsocket(websocket.WebSocket): """Websocket client that sends pings periodically. @@ -11,7 +13,7 @@ class PingingWebsocket(websocket.WebSocket): Attributes: ping_interval (number): interval between pings in seconds, default 15. """ - def __init__(self, ping_interval=15, *args, **kwargs): + def __init__(self, ping_interval=10, *args, **kwargs): super(PingingWebsocket, self).__init__(*args, **kwargs) self.ping_interval = ping_interval self.pinging_thread = None @@ -32,6 +34,10 @@ def close(self, *args, **kwargs): self.pinging_event.set() self.pinging_thread.join() + def handle_disconnection(self): + """Called on server disconnection.""" + core_log.warning("Server disconnetion detected!") + def ping_loop(self): """Ping periodically until the finish event.""" while not self.pinging_event.wait(self.ping_interval): @@ -39,4 +45,5 @@ def ping_loop(self): self.send("ping") except: # noqa + self.handle_disconnection() break diff --git a/tests/management/test_resource_manager.py b/tests/management/test_resource_manager.py index 90f6ffad..167f6d85 100644 --- a/tests/management/test_resource_manager.py +++ b/tests/management/test_resource_manager.py @@ -9,8 +9,10 @@ import time from threading import Thread +from unittest import TestCase import mock +from waiting import wait from future.builtins import zip, range from django.db.models.query_utils import Q from django.contrib.auth.models import User @@ -20,10 +22,12 @@ from rotest.management import BaseResource, ResourceRequest from rotest.management.models.resource_data import DataPointer from rotest.management.client.manager import ClientResourceManager +from rotest.management.client.websocket_client import PingingWebsocket from rotest.management.common.resource_descriptor import \ ResourceDescriptor as Descriptor from rotest.management.models.ut_models import (DemoResourceData, DemoComplexResourceData) + from rotest.management.models.ut_resources import (DemoService, DemoResource, DemoComplexResource) @@ -2036,3 +2040,29 @@ def test_locking_resource_with_group_none(self): % (self.NO_GROUP_RESOURCE, resource.name)) self.get_resource(self.NO_GROUP_RESOURCE, owner="") + + +class ClientWebsocketTests(TestCase): + """Tests for client behavior.""" + + TEST_ADDRESS = 'localhost' + + @mock.patch('websocket._core.handshake') + @mock.patch('websocket._core.connect') + @mock.patch('rotest.management.client.websocket_client.' + 'PingingWebsocket.handle_disconnection') + def test_server_disconnection(self, mock_callback, mock_connect, _): + """Check that 'close_session' is called at disconnect.""" + client = PingingWebsocket(ping_interval=0.2) + mock_socket = mock.MagicMock() + mock_connect.return_value = mock_socket, self.TEST_ADDRESS + client.connect(self.TEST_ADDRESS) + wait(lambda: mock_socket.send.call_count > 1, + timeout_seconds=2, sleep_seconds=0.1, + waiting_for='Waiting for 2 pings') + + mock_callback.assert_not_called() + mock_socket.send.side_effect = RuntimeError + wait(lambda: mock_callback.call_count > 0, + timeout_seconds=2, sleep_seconds=0.1, + waiting_for='Waiting for disconnection')