1
# -*- coding: utf-8 -*-
2
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
4
###############################################################################
5
# OpenLP - Open Source Lyrics Projection #
6
# --------------------------------------------------------------------------- #
7
# Copyright (c) 2008-2017 OpenLP Developers #
8
# --------------------------------------------------------------------------- #
9
# This program is free software; you can redistribute it and/or modify it #
10
# under the terms of the GNU General Public License as published by the Free #
11
# Software Foundation; version 2 of the License. #
13
# This program is distributed in the hope that it will be useful, but WITHOUT #
14
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
15
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
18
# You should have received a copy of the GNU General Public License along #
19
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
20
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
21
###############################################################################
23
Package to test the openlp.core.ui.slidecontroller package.
25
from unittest import TestCase, skipUnless
26
from unittest.mock import MagicMock, patch
28
from PyQt5 import QtCore
30
from openlp.core.common import is_macosx
31
from openlp.core.common.path import Path
32
from openlp.core.common.registry import Registry
33
from openlp.core.display.screens import ScreenList
34
from openlp.core.lib import PluginManager
35
from openlp.core.ui import MainDisplay, AudioPlayer
36
from openlp.core.ui.maindisplay import TRANSPARENT_STYLESHEET, OPAQUE_STYLESHEET
37
from tests.helpers.testmixin import TestMixin
40
from ctypes import pythonapi, c_void_p, c_char_p, py_object
41
from sip import voidptr
42
from objc import objc_object
43
from AppKit import NSMainMenuWindowLevel, NSWindowCollectionBehaviorManaged
46
class TestMainDisplay(TestCase, TestMixin):
50
Set up the components need for all tests.
52
# Mocked out desktop object
53
self.desktop = MagicMock()
54
self.desktop.primaryScreen.return_value = 0
55
self.desktop.screenCount.return_value = 2
56
self.desktop.screenGeometry.side_effect = lambda x: {0: QtCore.QRect(0, 0, 1024, 768),
57
1: QtCore.QRect(0, 0, 1024, 768)}[x]
58
self.screens = ScreenList.create(self.desktop)
60
self.registry = Registry()
61
self.setup_application()
62
Registry().register('application', self.app)
63
self.mocked_audio_player = patch('openlp.core.ui.maindisplay.AudioPlayer')
64
self.mocked_audio_player.start()
70
self.mocked_audio_player.stop()
73
def test_initial_main_display(self):
75
Test the initial Main Display state
77
# GIVEN: A new SlideController instance.
79
display.is_live = True
81
# WHEN: The default controller is built.
82
main_display = MainDisplay(display)
84
# THEN: The controller should be a live controller.
85
assert main_display.is_live is True, 'The main display should be a live controller'
87
def test_set_transparency_enabled(self):
89
Test setting the display to be transparent
91
# GIVEN: An instance of MainDisplay
93
main_display = MainDisplay(display)
95
# WHEN: Transparency is enabled
96
main_display.set_transparency(True)
98
# THEN: The transparent stylesheet should be used
99
assert TRANSPARENT_STYLESHEET == main_display.styleSheet(), \
100
'The MainDisplay should use the transparent stylesheet'
101
assert main_display.autoFillBackground() is False, \
102
'The MainDisplay should not have autoFillBackground set'
103
assert main_display.testAttribute(QtCore.Qt.WA_TranslucentBackground) is True, \
104
'The MainDisplay should have a translucent background'
106
def test_set_transparency_disabled(self):
108
Test setting the display to be opaque
110
# GIVEN: An instance of MainDisplay
111
display = MagicMock()
112
main_display = MainDisplay(display)
114
# WHEN: Transparency is disabled
115
main_display.set_transparency(False)
117
# THEN: The opaque stylesheet should be used
118
assert OPAQUE_STYLESHEET == main_display.styleSheet(), \
119
'The MainDisplay should use the opaque stylesheet'
120
assert main_display.testAttribute(QtCore.Qt.WA_TranslucentBackground) is False, \
121
'The MainDisplay should not have a translucent background'
123
def test_css_changed(self):
125
Test that when the CSS changes, the plugins are looped over and given an opportunity to update the CSS
127
# GIVEN: A mocked list of plugins, a mocked display and a MainDisplay
128
mocked_songs_plugin = MagicMock()
129
mocked_bibles_plugin = MagicMock()
130
mocked_plugin_manager = MagicMock()
131
mocked_plugin_manager.plugins = [mocked_songs_plugin, mocked_bibles_plugin]
132
Registry().register('plugin_manager', mocked_plugin_manager)
133
display = MagicMock()
134
main_display = MainDisplay(display)
135
# This is set up dynamically, so we need to mock it out for now
136
main_display.frame = MagicMock()
138
# WHEN: The css_changed() method is triggered
139
main_display.css_changed()
141
# THEN: The plugins should have each been given an opportunity to add their bit to the CSS
142
mocked_songs_plugin.refresh_css.assert_called_with(main_display.frame)
143
mocked_bibles_plugin.refresh_css.assert_called_with(main_display.frame)
145
@skipUnless(is_macosx(), 'Can only run test on Mac OS X due to pyobjc dependency.')
146
def test_macosx_display_window_flags_state(self):
148
Test that on Mac OS X we set the proper window flags
150
# GIVEN: A new SlideController instance on Mac OS X.
151
self.screens.set_current_display(0)
152
display = MagicMock()
154
# WHEN: The default controller is built.
155
main_display = MainDisplay(display)
157
# THEN: The window flags should be the same as those needed on Mac OS X.
158
assert QtCore.Qt.Window | QtCore.Qt.FramelessWindowHint | QtCore.Qt.NoDropShadowWindowHint == \
159
main_display.windowFlags(), \
160
'The window flags should be Qt.Window, Qt.FramelessWindowHint, and Qt.NoDropShadowWindowHint.'
162
@skipUnless(is_macosx(), 'Can only run test on Mac OS X due to pyobjc dependency.')
163
def test_macosx_display(self):
165
Test display on Mac OS X
167
# GIVEN: A new SlideController instance on Mac OS X.
168
self.screens.set_current_display(0)
169
display = MagicMock()
171
# WHEN: The default controller is built and a reference to the underlying NSView is stored.
172
main_display = MainDisplay(display)
174
nsview_pointer = main_display.winId().ascapsule()
176
nsview_pointer = voidptr(main_display.winId()).ascapsule()
177
pythonapi.PyCapsule_SetName.restype = c_void_p
178
pythonapi.PyCapsule_SetName.argtypes = [py_object, c_char_p]
179
pythonapi.PyCapsule_SetName(nsview_pointer, c_char_p(b"objc.__object__"))
180
pyobjc_nsview = objc_object(cobject=nsview_pointer)
182
# THEN: The window level and collection behavior should be the same as those needed for Mac OS X.
183
assert pyobjc_nsview.window().level() == NSMainMenuWindowLevel + 2, \
184
'Window level should be NSMainMenuWindowLevel + 2'
185
assert pyobjc_nsview.window().collectionBehavior() == NSWindowCollectionBehaviorManaged, \
186
'Window collection behavior should be NSWindowCollectionBehaviorManaged'
188
@patch('openlp.core.ui.maindisplay.Settings')
189
def test_show_display_startup_logo(self, MockedSettings):
190
# GIVEN: Mocked show_display, setting for logo visibility
191
display = MagicMock()
192
main_display = MainDisplay(display)
193
main_display.frame = MagicMock()
194
main_display.isHidden = MagicMock()
195
main_display.isHidden.return_value = True
196
main_display.setVisible = MagicMock()
197
mocked_settings = MagicMock()
198
mocked_settings.value.return_value = False
199
MockedSettings.return_value = mocked_settings
200
main_display.shake_web_view = MagicMock()
202
# WHEN: show_display is called.
203
main_display.show_display()
205
# THEN: setVisible should had been called with "True"
206
main_display.setVisible.assert_called_once_with(True)
208
@patch('openlp.core.ui.maindisplay.Settings')
209
def test_show_display_hide_startup_logo(self, MockedSettings):
210
# GIVEN: Mocked show_display, setting for logo visibility
211
display = MagicMock()
212
main_display = MainDisplay(display)
213
main_display.frame = MagicMock()
214
main_display.isHidden = MagicMock()
215
main_display.isHidden.return_value = False
216
main_display.setVisible = MagicMock()
217
mocked_settings = MagicMock()
218
mocked_settings.value.return_value = False
219
MockedSettings.return_value = mocked_settings
220
main_display.shake_web_view = MagicMock()
222
# WHEN: show_display is called.
223
main_display.show_display()
225
# THEN: setVisible should had not been called
226
main_display.setVisible.assert_not_called()
228
@patch('openlp.core.ui.maindisplay.Settings')
229
@patch('openlp.core.ui.maindisplay.build_html')
230
def test_build_html_no_video(self, MockedSettings, Mocked_build_html):
231
# GIVEN: Mocked display
232
display = MagicMock()
233
mocked_media_controller = MagicMock()
235
Registry().register('media_controller', mocked_media_controller)
236
main_display = MainDisplay(display)
237
main_display.frame = MagicMock()
238
mocked_settings = MagicMock()
239
mocked_settings.value.return_value = False
240
MockedSettings.return_value = mocked_settings
241
main_display.shake_web_view = MagicMock()
242
service_item = MagicMock()
243
mocked_plugin = MagicMock()
244
display.plugin_manager = PluginManager()
245
display.plugin_manager.plugins = [mocked_plugin]
246
main_display.web_view = MagicMock()
248
# WHEN: build_html is called with a normal service item and a non video theme.
249
main_display.build_html(service_item)
251
# THEN: the following should had not been called
252
assert main_display.web_view.setHtml.call_count == 1, 'setHTML should be called once'
253
assert main_display.media_controller.video.call_count == 0, \
254
'Media Controller video should not have been called'
256
@patch('openlp.core.ui.maindisplay.Settings')
257
@patch('openlp.core.ui.maindisplay.build_html')
258
def test_build_html_video(self, MockedSettings, Mocked_build_html):
259
# GIVEN: Mocked display
260
display = MagicMock()
261
mocked_media_controller = MagicMock()
263
Registry().register('media_controller', mocked_media_controller)
264
main_display = MainDisplay(display)
265
main_display.frame = MagicMock()
266
mocked_settings = MagicMock()
267
mocked_settings.value.return_value = False
268
MockedSettings.return_value = mocked_settings
269
main_display.shake_web_view = MagicMock()
270
service_item = MagicMock()
271
service_item.theme_data = MagicMock()
272
service_item.theme_data.background_type = 'video'
273
service_item.theme_data.theme_name = 'name'
274
service_item.theme_data.background_filename = Path('background_filename')
275
mocked_plugin = MagicMock()
276
display.plugin_manager = PluginManager()
277
display.plugin_manager.plugins = [mocked_plugin]
278
main_display.web_view = MagicMock()
280
# WHEN: build_html is called with a normal service item and a video theme.
281
main_display.build_html(service_item)
283
# THEN: the following should had not been called
284
assert main_display.web_view.setHtml.call_count == 1, 'setHTML should be called once'
285
assert main_display.media_controller.video.call_count == 1, \
286
'Media Controller video should have been called once'
289
def test_calling_next_item_in_playlist():
291
Test the AudioPlayer.next() method
293
# GIVEN: An instance of AudioPlayer with a mocked out playlist
294
audio_player = AudioPlayer(None)
296
# WHEN: next is called.
297
with patch.object(audio_player, 'playlist') as mocked_playlist:
300
# THEN: playlist.next should had been called once.
301
mocked_playlist.next.assert_called_once_with()