Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support to disable concurrency check for UIScreen #111

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions simpleline/input/input_handler.py
Expand Up @@ -53,6 +53,9 @@ def __init__(self, callback=None, source=None):
self._skip_concurrency_check = False
self._source = source

self._register_input_ready_signal()

def _register_input_ready_signal(self):
App.get_event_loop().register_signal_handler(InputReadySignal,
self._input_received_handler)

Expand Down
8 changes: 8 additions & 0 deletions simpleline/render/screen/__init__.py
Expand Up @@ -88,6 +88,14 @@ def title(self, title):
"""
self._title = title

@property
def input_manager(self):
"""Get input manager.

The input manager could be used to tweak input settings.
"""
return self._input_manager

@property
def password_func(self):
"""Get password function.
Expand Down
22 changes: 22 additions & 0 deletions simpleline/render/screen/input_manager.py
Expand Up @@ -47,6 +47,7 @@ def __init__(self, ui_screen):
self._ui_screen = ui_screen
self._input_error_counter = 0
self._input_error_threshold = 5
self._skip_concurrency_check = False
self._input_args = None

@property
Expand All @@ -63,6 +64,25 @@ def input_error_threshold_exceeded(self):
errors = self._input_error_counter % self._input_error_threshold
return errors == 0

@property
def skip_concurrency_check(self):
"""Should the concurrency check be skipped?

:returns bool: True if the check should be skipped.
"""
return self._skip_concurrency_check

@skip_concurrency_check.setter
def skip_concurrency_check(self, value):
"""Set if the concurrency check should be skipped when asking for user input.

WARNING: Use this option with caution. When the concurrency check is disabled you
can easily get to unexpected behavior which is hard to debug.

:param bool value: True to skip the concurrency check.
"""
self._skip_concurrency_check = value

def get_input_blocking(self, message, hidden):
"""Get blocking input from the user.

Expand All @@ -79,6 +99,7 @@ def get_input_blocking(self, message, hidden):
else:
handler = InputHandler(source=self)

handler.skip_concurrency_check = self._skip_concurrency_check
handler.get_input(message)
handler.wait_on_input()
return handler.value
Expand All @@ -102,6 +123,7 @@ def get_input(self, args=None):
if self._ui_screen.password_func:
handler.set_pass_func(self._ui_screen.password_func)

handler.skip_concurrency_check = self._skip_concurrency_check
handler.set_callback(self.process_input)
handler.get_input(prompt)

Expand Down
89 changes: 89 additions & 0 deletions tests/units/main/adv_widgets_test.py
Expand Up @@ -23,11 +23,100 @@
from unittest.mock import patch
from io import StringIO

from simpleline.render.screen import UIScreen
from simpleline.input.input_handler import InputHandler
from simpleline.render.adv_widgets import GetInputScreen, GetPasswordInputScreen

from .. import UtilityMixin


class InputHandlerMock(InputHandler):
"""Dummy InputHandler instance.

This class based on InputHandler has all threading logic disabled for simpler testing.
"""
def _register_input_ready_signal(self):
pass

def get_input(self, prompt):
pass

def wait_on_input(self):
pass


class UIScreen_TestCase(unittest.TestCase):
@patch('simpleline.render.screen.input_manager.InputHandler')
def test_uiscreen_disable_concurrency_check(self, input_handler_class_mock):
# Replace default InputHandler instance created by InputManager by our mock to have
# it stored after use.
input_handler_instance_mock = InputHandlerMock()
input_handler_class_mock.return_value = input_handler_instance_mock

screen = UIScreen()
input_manager = screen.input_manager
input_manager.skip_concurrency_check = True
screen.get_user_input("test")

self.assertTrue(screen.input_manager.skip_concurrency_check)
# Check that the created InputHandler instance has the flag correctly set.
# We don't need to check the concurrency check functionality, it is tested
# elsewhere already.
self.assertTrue(input_handler_instance_mock.skip_concurrency_check)

@patch('simpleline.render.screen.input_manager.InputHandler')
def test_uiscreen_password_disable_concurrency_check(self, input_handler_class_mock):
# Replace default InputHandler instance created by InputManager by our mock to have
# it stored after use.
input_handler_instance_mock = InputHandlerMock()
input_handler_class_mock.return_value = input_handler_instance_mock

screen = GetPasswordInputScreen(message="Test prompt")
input_manager = screen.input_manager
input_manager.skip_concurrency_check = True
screen.get_user_input("test")

self.assertTrue(screen.input_manager.skip_concurrency_check)
# Check that the created InputHandler instance has the flag correctly set.
# We don't need to check the concurrency check functionality, it is tested
# elsewhere already.
self.assertTrue(input_handler_instance_mock.skip_concurrency_check)

@patch('simpleline.render.screen.input_manager.InputHandler')
def test_uiscreen_non_blocking_input_disable_concurrency_check(self, input_handler_class_mock):
# Replace default InputHandler instance created by InputManager by our mock to have
# it stored after use.
input_handler_instance_mock = InputHandlerMock()
input_handler_class_mock.return_value = input_handler_instance_mock

screen = UIScreen()
input_manager = screen.input_manager
input_manager.skip_concurrency_check = True
screen.get_input_with_error_check("test")

self.assertTrue(screen.input_manager.skip_concurrency_check)
# Check that the created InputHandler instance has the flag correctly set.
# We don't need to check the concurrency check functionality, it is tested
# elsewhere already.
self.assertTrue(input_handler_instance_mock.skip_concurrency_check)

@patch('simpleline.render.screen.input_manager.InputHandler')
def test_uiscreen_default_concurrency_check(self, input_handler_class_mock):
# Replace default InputHandler instance created by InputManager by our mock to have
# it stored after use.
input_handler_instance_mock = InputHandlerMock()
input_handler_class_mock.return_value = input_handler_instance_mock

screen = UIScreen()
screen.get_user_input("test")

self.assertFalse(screen.input_manager.skip_concurrency_check)
# Check that the created InputHandler instance has the flag correctly set.
# We don't need to check the concurrency check functionality, it is tested
# elsewhere already.
self.assertFalse(input_handler_instance_mock.skip_concurrency_check)


@patch('simpleline.input.input_handler.InputHandlerRequest._get_input')
@patch('sys.stdout', new_callable=StringIO)
class AdvWidgets_TestCase(unittest.TestCase, UtilityMixin):
Expand Down