from unittest.mock import Mock
from unittest.mock import patch
import pytest
from schrodinger.application.livedesign import live_report_widget
from schrodinger.application.livedesign import login
from schrodinger.Qt.QtGui import QColor
from schrodinger.test import custom_test_classes
from schrodinger.test import ld_mock_modules
from schrodinger.test import mock_ld_models
from schrodinger.ui.qt.appframework2 import tasks
LIVEDESIGN = 'schrodinger.application.livedesign'
LD_BASE_CLASSES = f'{LIVEDESIGN}.ld_base_classes'
LOGIN = f'{LIVEDESIGN}.login'
EMM = f'{LIVEDESIGN}.export_map_manager'
LD_IMPORT = f'{LIVEDESIGN}.ld_import'
LD_EXPORT = f'{LIVEDESIGN}.ld_export'
IE_BASE_PANEL = LD_BASE_CLASSES + '.ImportExportBasePanel'
MBM = 'schrodinger.ui.qt.widgetmixins.basicmixins.MessageBoxMixin'
WARNING = f'{MBM}.warning'
[docs]class MockBaseLDTreeItemWrapper(mock_ld_models.MockNameAndID):
[docs] def __init__(self, name=None, id_=None, title=None, live_report=None):
if live_report is not None:
id_ = live_report.id
name = live_report.title
title = live_report.title
super().__init__(name, id_)
self.title = title
[docs]class BaseTestCases:
[docs] class ExportBasePanelTest(custom_test_classes.MultiModulePatchMixin):
MODULE_MAP = {'login': [LD_EXPORT], 'get_ld_client_and_models': LOGIN}
PANEL_CLASS = NotImplemented
[docs] def setup(self):
super().setup()
self.panel = self.PANEL_CLASS()
mock_login = self._getLogin()
model = self.panel.model
model.ld_client = self.mock_ldclient
model.ld_models = self.mock_models
model.ld_destination.host = mock_login.get_host()
def _getLogin(self):
"""
:return: an instance of the class used to mock the `login` module
:rtype: ld_mock_modules.MockLogin
"""
return self._mock_module_map['login']
def _setupMockedModules(self):
mock_login = ld_mock_modules.MockLogin()
self._mock_module_map['login'] = mock_login
_, self.mock_ldclient, self.mock_models = mock_login.get_ld_client_and_models(
)
mock_GLDCAM = Mock()
mock_GLDCAM.return_value = mock_login.get_ld_client_and_models()
self._mock_module_map['get_ld_client_and_models'] = mock_GLDCAM
[docs] def test_setStatus(self):
"""
Verify that the `setStatus()` method properly alters the task bar.
"""
panel = self.panel
bar = panel.status_bar
# Test 1: Text only
text = 'test 1'
panel.setStatus(text)
assert bar.currentMessage() == text
# TODO: test color from the style sheet. Should look something like:
# sheet_txt = (f'QStatusBar{{color: rgb({color.red()},{color.green()},'
# f'{color.blue()}); font:bold}}')
# Test 2: Text and color
text = 'test 2'
red = QColor(255, 0, 0, 0)
panel.setStatus(text, color=red)
assert bar.currentMessage() == text
# TODO: test for red
[docs] @patch(f'{EMM}.ExportMapManager.getAvailableMappings')
def test__onLiveReportTextEdited(self, mock_avail_maps):
"""
Verify that the panel responds appropriately when the user finishes
editing the live report line edit. This should update relevant
instance variables of the panel and attempt to retrieve information
from the LiveDesign server.
`mock_avail_maps` is necessary to avoid any actual directories
being accessed (PANEL-15417).
"""
mock_avail_maps.return_value = []
panel = self.panel
ld_dest = panel.model.ld_destination
ld_client = panel.model.ld_client
lr_widget = panel.lr_widget
lrw_model = lr_widget.model
login = self._getLogin()
orig_host = login.get_host()
# Establish default values
assert ld_dest.host == orig_host
assert ld_dest.proj_name == ''
assert ld_dest.lr_name == ''
assert ld_dest.lr_id == ''
assert lrw_model.previous_lr_user_text == ''
# Set up mocks
exp_lr_name = 'FernGully: The Last Rainforest'
exp_proj_id = mock_ld_models.DEFAULT_PROJ_ID
exp_proj_name = None
for project in ld_client.projects():
if project.id == exp_proj_id:
exp_proj_name = project.name
break
mock_lr = mock_ld_models.MockLiveReport(title=exp_lr_name,
project_id=exp_proj_id)
mock_lr = ld_client.create_live_report(mock_lr)
exp_lr_id = mock_lr.id
# If a new URL is entered, the panel model should update
lr_url = (f'{orig_host}/livedesign/#/projects/{exp_proj_id}/'
f'livereports/{exp_lr_id}')
lrw_model.lr_user_text = lr_url
# The panel model should not update until the user has finished
# editing the text in the line edit
assert ld_dest.host == orig_host
assert ld_dest.proj_name == ''
assert ld_dest.lr_name == ''
assert ld_dest.lr_id == ''
assert lrw_model.previous_lr_user_text == ''
# Once this occurs, relevant model data will be assigned
lr_widget._onLiveReportURLLoad()
assert ld_dest.host == orig_host
assert ld_dest.proj_name == exp_proj_name
assert ld_dest.lr_name == exp_lr_name
assert ld_dest.lr_id == exp_lr_id
assert lrw_model.previous_lr_user_text == lr_url
# If the text is edited but the value is unchanged, do nothing
lr_widget._onLiveReportURLLoad()
assert ld_dest.host == orig_host
assert ld_dest.proj_name == exp_proj_name
assert ld_dest.lr_name == exp_lr_name
assert ld_dest.lr_id == exp_lr_id
assert lrw_model.previous_lr_user_text == lr_url
# If an invalid string is entered, clear everything
lrw_model.lr_user_text = 'an invalid URL'
with patch(WARNING) as mock_warning:
lr_widget._onLiveReportURLLoad()
assert mock_warning.call_count == 1
# After the warning is displayed, clear everything in the widget,
# including the "last_lr_text" value; otherwise, if the user enters
# the same bad value, they will not be presented with the same
# warning.
assert ld_dest.host == orig_host
assert ld_dest.proj_name == ''
assert ld_dest.lr_name == ''
assert ld_dest.lr_id == ''
assert lrw_model.previous_lr_user_text == ''
[docs] class ImportBasePanelTest(custom_test_classes.MultiModulePatchMixin):
MODULE_MAP = {
'login': [LD_BASE_CLASSES, LD_IMPORT],
'get_ld_client_and_models': LOGIN
}
PANEL_CLASS = None
[docs] def setup(self):
super().setup()
self.panel = self.PANEL_CLASS()
mock_login = self._getLogin()
host = mock_login.get_host()
print(f'setup() host: {host}')
mock_state = {
login.CLIENT: self.mock_ldclient,
login.MODELS: self.mock_models,
login.HOST: host
}
self.panel.loadLDClient()
print(f'host 1: {self.panel.lr_widget.model.ld_destination.host}')
self.panel.processPrevPanel(mock_state)
print(f'host 2: {self.panel.lr_widget.model.ld_destination.host}')
def _getLogin(self):
return self._mock_module_map['login']
def _setupMockedModules(self):
mock_login = ld_mock_modules.MockLogin()
self._mock_module_map['login'] = mock_login
_, self.mock_ldclient, self.mock_models = mock_login.get_ld_client_and_models(
)
mock_GLDCAM = Mock()
mock_GLDCAM.return_value = mock_login.get_ld_client_and_models()
self._mock_module_map['get_ld_client_and_models'] = mock_GLDCAM
[docs] def testInstantiation(self):
"""
Make sure that the panel can be instantiated
"""
assert isinstance(self.panel, self.PANEL_CLASS)
[docs] @patch(WARNING)
@patch(IE_BASE_PANEL + '.error')
@patch(IE_BASE_PANEL + '.info')
@patch(IE_BASE_PANEL + '.question')
def test_processTaskMessage(self, mock_question, mock_info, mock_error,
mock_warning):
"""
Test if all messaging signals for the Task Runner are connected to
the GUI.
"""
panel = self.panel
# Test 1: Warning msg
panel.processTaskMessage(tasks.WARNING, 'testing')
mock_warning.assert_called_once_with('testing', '')
# Test 2: Critical msg
panel.processTaskMessage(tasks.ERROR, 'testing2')
mock_error.assert_called_once_with('testing2', '')
# Test 3: Information msg
panel.processTaskMessage(tasks.INFO, 'testing3')
mock_info.assert_called_once_with('testing3', '')
# Test 4: Question
panel.processTaskMessage(tasks.QUESTION, 'testing4')
mock_question.assert_called_once_with('testing4', '')
# Test 5: Invalid msg
with pytest.raises(ValueError):
panel.processTaskMessage(1837, 'testing5')
# Test 6: Status bar msg
panel.processTaskMessage(tasks.STATUS, 'testing2')
assert panel.status_bar.currentMessage() == 'testing2'
# Test 7: Status bar msg that should be ignored by
# processTaskMessage()
panel.processTaskMessage(tasks.STATUS, 'Task completed')
assert panel.status_bar.currentMessage() == 'testing2'
# Test 7: Second status bar msg that should be ignored by
# processTaskMessage()
panel.processTaskMessage(tasks.STATUS, 'Task status: Finished')
assert panel.status_bar.currentMessage() == 'testing2'
[docs] @patch(f'{EMM}.ExportMapManager.getAvailableMappings')
def test__onLiveReportURLLoad(self, mock_avail_maps):
"""
Verify that the panel responds appropriately when the user finishes
editing the live report line edit and clicks the confirm button. This should
update relevant instance variables of the panel and attempt to
retrieve information from the LiveDesign server.
"""
# PANEL-15417 Added mock_avail_maps patch to avoid actual directory being called
mock_avail_maps.return_value = []
panel = self.panel
lr_widget, lrw_ui = panel.lr_widget, panel.lr_widget.ui
# Establish default values
assert panel.project_id == ''
assert panel.live_report_id == ''
assert lrw_ui.project_state_lbl.text(
) == live_report_widget.NONE_SELECTED
assert lrw_ui.lr_state_lbl.text(
) == live_report_widget.NONE_SELECTED
# Set up mocks
mock_ld_client = panel.ld_client
exp_title = 'FernGully: The Last Rainforest'
exp_proj_id = mock_ld_models.DEFAULT_PROJ_ID
exp_proj_name = None
for project in mock_ld_client.projects():
if project.id == exp_proj_id:
exp_proj_name = project.name
break
mock_lr = mock_ld_models.MockLiveReport(title=exp_title,
project_id=exp_proj_id)
mock_lr = mock_ld_client.create_live_report(mock_lr)
exp_lr_id = mock_lr.id
# If a new URL is entered, instance variables should be changed
login = self._getLogin()
host = login.get_host()
lr_url = (f'{host}/livedesign/#/projects/{exp_proj_id}/livereports/'
f'{exp_lr_id}')
lrw_ui.lr_text_le.setText(lr_url)
lr_widget._onLiveReportURLLoad()
assert panel.project_id == exp_proj_id
assert panel.live_report_id == exp_lr_id
assert panel.project_name == exp_proj_name
assert lrw_ui.project_state_lbl.text() == get_bolded_text(
exp_proj_name)
assert lrw_ui.lr_state_lbl.text() == get_bolded_text(exp_title)
# If the text is edited but the value is unchanged, do nothing
lr_widget._onLiveReportURLLoad()
assert panel.project_id == exp_proj_id
assert panel.live_report_id == exp_lr_id
assert panel.project_name == exp_proj_name
assert lrw_ui.project_state_lbl.text() == get_bolded_text(
exp_proj_name)
assert lrw_ui.lr_state_lbl.text() == get_bolded_text(exp_title)
# If an invalid string is entered, clear everything
bad_lr_text = 'no scrubs'
lrw_ui.lr_text_le.setText(bad_lr_text)
with patch(WARNING) as mock_warning:
lr_widget._onLiveReportURLLoad()
assert mock_warning.call_count == 1
# After the warning is displayed, clear everything in the widget
assert panel.project_id == ''
assert panel.live_report_id == ''
assert lrw_ui.project_state_lbl.text(
) == live_report_widget.NONE_SELECTED
assert lrw_ui.lr_state_lbl.text(
) == live_report_widget.NONE_SELECTED
[docs]def get_bolded_text(text):
"""
Returns the text with HTML bold tags applied
:param text: text to bold
:type text: str
:return: bolded text
:rtype: str
"""
return f'<b>{text}</b>'