Merge lp:~knightrider0xd/openlp/preview-shows-live-fix-1080596 into lp:openlp

Proposed by Ian Knight
Status: Merged
Merged at revision: 2661
Proposed branch: lp:~knightrider0xd/openlp/preview-shows-live-fix-1080596
Merge into: lp:openlp
Diff against target: 687 lines (+472/-19)
9 files modified
openlp/core/lib/__init__.py (+26/-2)
openlp/core/lib/serviceitem.py (+2/-0)
openlp/core/ui/lib/listpreviewwidget.py (+8/-6)
openlp/core/ui/slidecontroller.py (+15/-3)
openlp/plugins/presentations/lib/presentationcontroller.py (+2/-2)
tests/functional/openlp_core_lib/test_lib.py (+190/-2)
tests/functional/openlp_core_lib/test_serviceitem.py (+5/-2)
tests/functional/openlp_core_ui/test_slidecontroller.py (+170/-1)
tests/functional/openlp_core_ui_lib/test_listpreviewwidget.py (+54/-1)
To merge this branch: bzr merge lp:~knightrider0xd/openlp/preview-shows-live-fix-1080596
Reviewer Review Type Date Requested Status
Tim Bentley Approve
Raoul Snyman Approve
Review via email: mp+294803@code.launchpad.net

This proposal supersedes a proposal from 2016-05-06.

Description of the change

Fixes bug 1080596 where presentations in the preview pane display live view rather than preview of selected slide.
In addition, fixes the aspect ratio & quality of thumbnails by saving them in the correct aspect ratio at a higher resolution, and loading them through the image manager.

New test cases implemented, or existing cases modified to test coverage complete for changes.

lp:~knightrider0xd/openlp/preview-shows-live-fix-1080596 (revision 2652)
[SUCCESS] https://ci.openlp.io/job/Branch-01-Pull/1554/
[SUCCESS] https://ci.openlp.io/job/Branch-02-Functional-Tests/1465/

Failing interface & coverage tests due to issue with unrelated crosswalk import module.

To post a comment you must log in.
Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Just one clarification?

review: Needs Information
Revision history for this message
Raoul Snyman (raoul-snyman) wrote :

Looks OK to me.

review: Approve
Revision history for this message
Tim Bentley (trb143) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'openlp/core/lib/__init__.py'
2--- openlp/core/lib/__init__.py 2016-04-23 19:28:52 +0000
3+++ openlp/core/lib/__init__.py 2016-05-16 12:42:11 +0000
4@@ -55,9 +55,13 @@
5
6 ``Theme``
7 This says, that the image is used by a theme.
8+
9+ ``CommandPlugins``
10+ This states that an image is being used by a command plugin.
11 """
12 ImagePlugin = 1
13 Theme = 2
14+ CommandPlugins = 3
15
16
17 class MediaType(object):
18@@ -174,10 +178,30 @@
19 ext = os.path.splitext(thumb_path)[1].lower()
20 reader = QtGui.QImageReader(image_path)
21 if size is None:
22- ratio = reader.size().width() / reader.size().height()
23+ # No size given; use default height of 88
24+ if reader.size().isEmpty():
25+ ratio = 1
26+ else:
27+ ratio = reader.size().width() / reader.size().height()
28 reader.setScaledSize(QtCore.QSize(int(ratio * 88), 88))
29- else:
30+ elif size.isValid():
31+ # Complete size given
32 reader.setScaledSize(size)
33+ else:
34+ # Invalid size given
35+ if reader.size().isEmpty():
36+ ratio = 1
37+ else:
38+ ratio = reader.size().width() / reader.size().height()
39+ if size.width() >= 0:
40+ # Valid width; scale height
41+ reader.setScaledSize(QtCore.QSize(size.width(), int(size.width() / ratio)))
42+ elif size.height() >= 0:
43+ # Valid height; scale width
44+ reader.setScaledSize(QtCore.QSize(int(ratio * size.height()), size.height()))
45+ else:
46+ # Invalid; use default height of 88
47+ reader.setScaledSize(QtCore.QSize(int(ratio * 88), 88))
48 thumb = reader.read()
49 thumb.save(thumb_path, ext[1:])
50 if not return_icon:
51
52=== modified file 'openlp/core/lib/serviceitem.py'
53--- openlp/core/lib/serviceitem.py 2016-02-14 17:53:16 +0000
54+++ openlp/core/lib/serviceitem.py 2016-05-16 12:42:11 +0000
55@@ -334,6 +334,8 @@
56 file_location_hash, ntpath.basename(image))
57 self._raw_frames.append({'title': file_name, 'image': image, 'path': path,
58 'display_title': display_title, 'notes': notes})
59+ if self.is_capable(ItemCapabilities.HasThumbnails):
60+ self.image_manager.add_image(image, ImageSource.CommandPlugins, '#000000')
61 self._new_item()
62
63 def get_service_repr(self, lite_save):
64
65=== modified file 'openlp/core/ui/lib/listpreviewwidget.py'
66--- openlp/core/ui/lib/listpreviewwidget.py 2016-04-22 18:32:59 +0000
67+++ openlp/core/ui/lib/listpreviewwidget.py 2016-05-16 12:42:11 +0000
68@@ -27,7 +27,7 @@
69 from PyQt5 import QtCore, QtGui, QtWidgets
70
71 from openlp.core.common import RegistryProperties, Settings
72-from openlp.core.lib import ImageSource, ServiceItem
73+from openlp.core.lib import ImageSource, ItemCapabilities, ServiceItem
74
75
76 class ListPreviewWidget(QtWidgets.QTableWidget, RegistryProperties):
77@@ -152,14 +152,16 @@
78 else:
79 label.setScaledContents(True)
80 if self.service_item.is_command():
81- pixmap = QtGui.QPixmap(frame['image'])
82- pixmap.setDevicePixelRatio(label.devicePixelRatio())
83- label.setPixmap(pixmap)
84+ if self.service_item.is_capable(ItemCapabilities.HasThumbnails):
85+ image = self.image_manager.get_image(frame['image'], ImageSource.CommandPlugins)
86+ pixmap = QtGui.QPixmap.fromImage(image)
87+ else:
88+ pixmap = QtGui.QPixmap(frame['image'])
89 else:
90 image = self.image_manager.get_image(frame['path'], ImageSource.ImagePlugin)
91 pixmap = QtGui.QPixmap.fromImage(image)
92- pixmap.setDevicePixelRatio(label.devicePixelRatio())
93- label.setPixmap(pixmap)
94+ pixmap.setDevicePixelRatio(label.devicePixelRatio())
95+ label.setPixmap(pixmap)
96 slide_height = width // self.screen_ratio
97 # Setup and validate row height cap if in use.
98 max_img_row_height = Settings().value('advanced/slide max height')
99
100=== modified file 'openlp/core/ui/slidecontroller.py'
101--- openlp/core/ui/slidecontroller.py 2016-04-22 18:32:45 +0000
102+++ openlp/core/ui/slidecontroller.py 2016-05-16 12:42:11 +0000
103@@ -1135,9 +1135,21 @@
104 """
105 self.log_debug('update_preview %s ' % self.screens.current['primary'])
106 if self.service_item and self.service_item.is_capable(ItemCapabilities.ProvidesOwnDisplay):
107- # Grab now, but try again in a couple of seconds if slide change is slow
108- QtCore.QTimer.singleShot(500, self.grab_maindisplay)
109- QtCore.QTimer.singleShot(2500, self.grab_maindisplay)
110+ if self.is_live:
111+ # If live, grab screen-cap of main display now
112+ QtCore.QTimer.singleShot(500, self.grab_maindisplay)
113+ # but take another in a couple of seconds in case slide change is slow
114+ QtCore.QTimer.singleShot(2500, self.grab_maindisplay)
115+ else:
116+ # If not live, use the slide's thumbnail/icon instead
117+ image_path = self.service_item.get_rendered_frame(self.selected_row)
118+ if self.service_item.is_capable(ItemCapabilities.HasThumbnails):
119+ image = self.image_manager.get_image(image_path, ImageSource.CommandPlugins)
120+ self.slide_image = QtGui.QPixmap.fromImage(image)
121+ else:
122+ self.slide_image = QtGui.QPixmap(image_path)
123+ self.slide_image.setDevicePixelRatio(self.main_window.devicePixelRatio())
124+ self.slide_preview.setPixmap(self.slide_image)
125 else:
126 self.slide_image = self.display.preview()
127 self.slide_image.setDevicePixelRatio(self.main_window.devicePixelRatio())
128
129=== modified file 'openlp/plugins/presentations/lib/presentationcontroller.py'
130--- openlp/plugins/presentations/lib/presentationcontroller.py 2015-12-31 22:46:06 +0000
131+++ openlp/plugins/presentations/lib/presentationcontroller.py 2016-05-16 12:42:11 +0000
132@@ -242,13 +242,13 @@
133
134 def convert_thumbnail(self, file, idx):
135 """
136- Convert the slide image the application made to a standard 320x240 .png image.
137+ Convert the slide image the application made to a scaled 360px height .png image.
138 """
139 if self.check_thumbnails():
140 return
141 if os.path.isfile(file):
142 thumb_path = self.get_thumbnail_path(idx, False)
143- create_thumb(file, thumb_path, False, QtCore.QSize(320, 240))
144+ create_thumb(file, thumb_path, False, QtCore.QSize(-1, 360))
145
146 def get_thumbnail_path(self, slide_no, check_exists):
147 """
148
149=== modified file 'tests/functional/openlp_core_lib/test_lib.py'
150--- tests/functional/openlp_core_lib/test_lib.py 2015-12-31 22:46:06 +0000
151+++ tests/functional/openlp_core_lib/test_lib.py 2016-05-16 12:42:11 +0000
152@@ -250,7 +250,7 @@
153
154 def create_thumb_with_size_test(self):
155 """
156- Test the create_thumb() function
157+ Test the create_thumb() function with a given size.
158 """
159 # GIVEN: An image to create a thumb of.
160 image_path = os.path.join(TEST_PATH, 'church.jpg')
161@@ -270,7 +270,7 @@
162 # WHEN: Create the thumb.
163 icon = create_thumb(image_path, thumb_path, size=thumb_size)
164
165- # THEN: Check if the thumb was created.
166+ # THEN: Check if the thumb was created and scaled to the given size.
167 self.assertTrue(os.path.exists(thumb_path), 'Test was not ran, because the thumb already exists')
168 self.assertIsInstance(icon, QtGui.QIcon, 'The icon should be a QIcon')
169 self.assertFalse(icon.isNull(), 'The icon should not be null')
170@@ -282,6 +282,194 @@
171 except:
172 pass
173
174+ def create_thumb_no_size_test(self):
175+ """
176+ Test the create_thumb() function with no size specified.
177+ """
178+ # GIVEN: An image to create a thumb of.
179+ image_path = os.path.join(TEST_PATH, 'church.jpg')
180+ thumb_path = os.path.join(TEST_PATH, 'church_thumb.jpg')
181+ expected_size = QtCore.QSize(63, 88)
182+
183+ # Remove the thumb so that the test actually tests if the thumb will be created. Maybe it was not deleted in the
184+ # last test.
185+ try:
186+ os.remove(thumb_path)
187+ except:
188+ pass
189+
190+ # Only continue when the thumb does not exist.
191+ self.assertFalse(os.path.exists(thumb_path), 'Test was not run, because the thumb already exists.')
192+
193+ # WHEN: Create the thumb.
194+ icon = create_thumb(image_path, thumb_path)
195+
196+ # THEN: Check if the thumb was created, retaining its aspect ratio.
197+ self.assertTrue(os.path.exists(thumb_path), 'Test was not ran, because the thumb already exists')
198+ self.assertIsInstance(icon, QtGui.QIcon, 'The icon should be a QIcon')
199+ self.assertFalse(icon.isNull(), 'The icon should not be null')
200+ self.assertEqual(expected_size, QtGui.QImageReader(thumb_path).size(), 'The thumb should have the given size')
201+
202+ # Remove the thumb so that the test actually tests if the thumb will be created.
203+ try:
204+ os.remove(thumb_path)
205+ except:
206+ pass
207+
208+ def create_thumb_invalid_size_test(self):
209+ """
210+ Test the create_thumb() function with invalid size specified.
211+ """
212+ # GIVEN: An image to create a thumb of.
213+ image_path = os.path.join(TEST_PATH, 'church.jpg')
214+ thumb_path = os.path.join(TEST_PATH, 'church_thumb.jpg')
215+ thumb_size = QtCore.QSize(-1, -1)
216+ expected_size = QtCore.QSize(63, 88)
217+
218+ # Remove the thumb so that the test actually tests if the thumb will be created. Maybe it was not deleted in the
219+ # last test.
220+ try:
221+ os.remove(thumb_path)
222+ except:
223+ pass
224+
225+ # Only continue when the thumb does not exist.
226+ self.assertFalse(os.path.exists(thumb_path), 'Test was not run, because the thumb already exists.')
227+
228+ # WHEN: Create the thumb.
229+ icon = create_thumb(image_path, thumb_path, size=thumb_size)
230+
231+ # THEN: Check if the thumb was created, retaining its aspect ratio.
232+ self.assertTrue(os.path.exists(thumb_path), 'Test was not ran, because the thumb already exists')
233+ self.assertIsInstance(icon, QtGui.QIcon, 'The icon should be a QIcon')
234+ self.assertFalse(icon.isNull(), 'The icon should not be null')
235+ self.assertEqual(expected_size, QtGui.QImageReader(thumb_path).size(), 'The thumb should have the given size')
236+
237+ # Remove the thumb so that the test actually tests if the thumb will be created.
238+ try:
239+ os.remove(thumb_path)
240+ except:
241+ pass
242+
243+ def create_thumb_width_only_test(self):
244+ """
245+ Test the create_thumb() function with a size of only width specified.
246+ """
247+ # GIVEN: An image to create a thumb of.
248+ image_path = os.path.join(TEST_PATH, 'church.jpg')
249+ thumb_path = os.path.join(TEST_PATH, 'church_thumb.jpg')
250+ thumb_size = QtCore.QSize(100, -1)
251+ expected_size = QtCore.QSize(100, 137)
252+
253+ # Remove the thumb so that the test actually tests if the thumb will be created. Maybe it was not deleted in the
254+ # last test.
255+ try:
256+ os.remove(thumb_path)
257+ except:
258+ pass
259+
260+ # Only continue when the thumb does not exist.
261+ self.assertFalse(os.path.exists(thumb_path), 'Test was not run, because the thumb already exists.')
262+
263+ # WHEN: Create the thumb.
264+ icon = create_thumb(image_path, thumb_path, size=thumb_size)
265+
266+ # THEN: Check if the thumb was created, retaining its aspect ratio.
267+ self.assertTrue(os.path.exists(thumb_path), 'Test was not ran, because the thumb already exists')
268+ self.assertIsInstance(icon, QtGui.QIcon, 'The icon should be a QIcon')
269+ self.assertFalse(icon.isNull(), 'The icon should not be null')
270+ self.assertEqual(expected_size, QtGui.QImageReader(thumb_path).size(), 'The thumb should have the given size')
271+
272+ # Remove the thumb so that the test actually tests if the thumb will be created.
273+ try:
274+ os.remove(thumb_path)
275+ except:
276+ pass
277+
278+ def create_thumb_height_only_test(self):
279+ """
280+ Test the create_thumb() function with a size of only height specified.
281+ """
282+ # GIVEN: An image to create a thumb of.
283+ image_path = os.path.join(TEST_PATH, 'church.jpg')
284+ thumb_path = os.path.join(TEST_PATH, 'church_thumb.jpg')
285+ thumb_size = QtCore.QSize(-1, 100)
286+ expected_size = QtCore.QSize(72, 100)
287+
288+ # Remove the thumb so that the test actually tests if the thumb will be created. Maybe it was not deleted in the
289+ # last test.
290+ try:
291+ os.remove(thumb_path)
292+ except:
293+ pass
294+
295+ # Only continue when the thumb does not exist.
296+ self.assertFalse(os.path.exists(thumb_path), 'Test was not run, because the thumb already exists.')
297+
298+ # WHEN: Create the thumb.
299+ icon = create_thumb(image_path, thumb_path, size=thumb_size)
300+
301+ # THEN: Check if the thumb was created, retaining its aspect ratio.
302+ self.assertTrue(os.path.exists(thumb_path), 'Test was not ran, because the thumb already exists')
303+ self.assertIsInstance(icon, QtGui.QIcon, 'The icon should be a QIcon')
304+ self.assertFalse(icon.isNull(), 'The icon should not be null')
305+ self.assertEqual(expected_size, QtGui.QImageReader(thumb_path).size(), 'The thumb should have the given size')
306+
307+ # Remove the thumb so that the test actually tests if the thumb will be created.
308+ try:
309+ os.remove(thumb_path)
310+ except:
311+ pass
312+
313+ def create_thumb_empty_img_test(self):
314+ """
315+ Test the create_thumb() function with a size of only height specified.
316+ """
317+ # GIVEN: An image to create a thumb of.
318+ image_path = os.path.join(TEST_PATH, 'church.jpg')
319+ thumb_path = os.path.join(TEST_PATH, 'church_thumb.jpg')
320+ thumb_size = QtCore.QSize(-1, 100)
321+ expected_size_1 = QtCore.QSize(88, 88)
322+ expected_size_2 = QtCore.QSize(100, 100)
323+
324+
325+ # Remove the thumb so that the test actually tests if the thumb will be created. Maybe it was not deleted in the
326+ # last test.
327+ try:
328+ os.remove(thumb_path)
329+ except:
330+ pass
331+
332+ # Only continue when the thumb does not exist.
333+ self.assertFalse(os.path.exists(thumb_path), 'Test was not run, because the thumb already exists.')
334+
335+ # WHEN: Create the thumb.
336+ with patch('openlp.core.lib.QtGui.QImageReader.size') as mocked_size:
337+ mocked_size.return_value = QtCore.QSize(0, 0)
338+ icon = create_thumb(image_path, thumb_path, size=None)
339+
340+ # THEN: Check if the thumb was created with aspect ratio of 1.
341+ self.assertTrue(os.path.exists(thumb_path), 'Test was not ran, because the thumb already exists')
342+ self.assertIsInstance(icon, QtGui.QIcon, 'The icon should be a QIcon')
343+ self.assertFalse(icon.isNull(), 'The icon should not be null')
344+ self.assertEqual(expected_size_1, QtGui.QImageReader(thumb_path).size(), 'The thumb should have the given size')
345+
346+ # WHEN: Create the thumb.
347+ with patch('openlp.core.lib.QtGui.QImageReader.size') as mocked_size:
348+ mocked_size.return_value = QtCore.QSize(0, 0)
349+ icon = create_thumb(image_path, thumb_path, size=thumb_size)
350+
351+ # THEN: Check if the thumb was created with aspect ratio of 1.
352+ self.assertIsInstance(icon, QtGui.QIcon, 'The icon should be a QIcon')
353+ self.assertFalse(icon.isNull(), 'The icon should not be null')
354+ self.assertEqual(expected_size_2, QtGui.QImageReader(thumb_path).size(), 'The thumb should have the given size')
355+
356+ # Remove the thumb so that the test actually tests if the thumb will be created.
357+ try:
358+ os.remove(thumb_path)
359+ except:
360+ pass
361+
362 def check_item_selected_true_test(self):
363 """
364 Test that the check_item_selected() function returns True when there are selected indexes
365
366=== modified file 'tests/functional/openlp_core_lib/test_serviceitem.py'
367--- tests/functional/openlp_core_lib/test_serviceitem.py 2015-12-31 22:46:06 +0000
368+++ tests/functional/openlp_core_lib/test_serviceitem.py 2016-05-16 12:42:11 +0000
369@@ -244,14 +244,16 @@
370 self.assertEqual(service_item.service_item_type, ServiceItemType.Command, 'It should be a Command')
371 self.assertEqual(service_item.get_frames()[0], frame, 'Frames should match')
372
373+ @patch(u'openlp.core.lib.serviceitem.ServiceItem.image_manager')
374 @patch('openlp.core.lib.serviceitem.AppLocation.get_section_data_path')
375- def add_from_command_for_a_presentation_thumb_test(self, mocked_get_section_data_path):
376+ def add_from_command_for_a_presentation_thumb_test(self, mocked_get_section_data_path, mocked_image_manager):
377 """
378- Test the Service Item - adding a presentation, and updating the thumb path
379+ Test the Service Item - adding a presentation, updating the thumb path & adding the thumb to image_manager
380 """
381 # GIVEN: A service item, a mocked AppLocation and presentation data
382 mocked_get_section_data_path.return_value = os.path.join('mocked', 'section', 'path')
383 service_item = ServiceItem(None)
384+ service_item.add_capability(ItemCapabilities.HasThumbnails)
385 service_item.has_original_files = False
386 service_item.name = 'presentations'
387 presentation_name = 'test.pptx'
388@@ -270,6 +272,7 @@
389 # THEN: verify that it is setup as a Command and that the frame data matches
390 self.assertEqual(service_item.service_item_type, ServiceItemType.Command, 'It should be a Command')
391 self.assertEqual(service_item.get_frames()[0], frame, 'Frames should match')
392+ self.assertEqual(1, mocked_image_manager.add_image.call_count, 'image_manager should be used')
393
394 def service_item_load_optical_media_from_service_test(self):
395 """
396
397=== modified file 'tests/functional/openlp_core_ui/test_slidecontroller.py'
398--- tests/functional/openlp_core_ui/test_slidecontroller.py 2016-02-27 14:25:31 +0000
399+++ tests/functional/openlp_core_ui/test_slidecontroller.py 2016-05-16 12:42:11 +0000
400@@ -26,7 +26,7 @@
401
402 from unittest import TestCase
403 from openlp.core import Registry
404-from openlp.core.lib import ServiceItemAction
405+from openlp.core.lib import ImageSource, ServiceItemAction
406 from openlp.core.ui import SlideController, LiveController, PreviewController
407 from openlp.core.ui.slidecontroller import InfoLabel, WIDE_MENU, NON_TEXT_MENU
408
409@@ -713,6 +713,175 @@
410 slide_controller.theme_screen, slide_controller.blank_screen
411 ])
412
413+ @patch(u'openlp.core.ui.slidecontroller.SlideController.image_manager')
414+ @patch(u'PyQt5.QtCore.QTimer.singleShot')
415+ def update_preview_test_live(self, mocked_singleShot, mocked_image_manager):
416+ """
417+ Test that the preview screen is updated with a screen grab for live service items
418+ """
419+ # GIVEN: A mocked live service item, a mocked image_manager, a mocked Registry,
420+ # and a slide controller with many mocks.
421+ # Mocked Live Item
422+ mocked_live_item = MagicMock()
423+ mocked_live_item.get_rendered_frame.return_value = ''
424+ mocked_live_item.is_capable = MagicMock()
425+ mocked_live_item.is_capable.side_effect = [True, True]
426+ # Mock image_manager
427+ mocked_image_manager.get_image.return_value = QtGui.QImage()
428+ # Mock Registry
429+ Registry.create()
430+ mocked_main_window = MagicMock()
431+ Registry().register('main_window', mocked_main_window)
432+ # Mock SlideController
433+ slide_controller = SlideController(None)
434+ slide_controller.service_item = mocked_live_item
435+ slide_controller.is_live = True
436+ slide_controller.log_debug = MagicMock()
437+ slide_controller.selected_row = MagicMock()
438+ slide_controller.screens = MagicMock()
439+ slide_controller.screens.current = {'primary': ''}
440+ slide_controller.display = MagicMock()
441+ slide_controller.display.preview.return_value = QtGui.QImage()
442+ slide_controller.grab_maindisplay = MagicMock()
443+ slide_controller.slide_preview = MagicMock()
444+ slide_controller.slide_count = 0
445+
446+ # WHEN: update_preview is called
447+ slide_controller.update_preview()
448+
449+ # THEN: A screen_grab should have been called
450+ self.assertEqual(0, slide_controller.slide_preview.setPixmap.call_count, 'setPixmap should not be called')
451+ self.assertEqual(0, slide_controller.display.preview.call_count, 'display.preview() should not be called')
452+ self.assertEqual(2, mocked_singleShot.call_count,
453+ 'Timer to grab_maindisplay should have been called 2 times')
454+ self.assertEqual(0, mocked_image_manager.get_image.call_count, 'image_manager not be called')
455+
456+ @patch(u'openlp.core.ui.slidecontroller.SlideController.image_manager')
457+ @patch(u'PyQt5.QtCore.QTimer.singleShot')
458+ def update_preview_test_pres(self, mocked_singleShot, mocked_image_manager):
459+ """
460+ Test that the preview screen is updated with the correct preview for presentation service items
461+ """
462+ # GIVEN: A mocked presentation service item, a mocked image_manager, a mocked Registry,
463+ # and a slide controller with many mocks.
464+ # Mocked Presentation Item
465+ mocked_pres_item = MagicMock()
466+ mocked_pres_item.get_rendered_frame.return_value = ''
467+ mocked_pres_item.is_capable = MagicMock()
468+ mocked_pres_item.is_capable.side_effect = [True, True]
469+ # Mock image_manager
470+ mocked_image_manager.get_image.return_value = QtGui.QImage()
471+ # Mock Registry
472+ Registry.create()
473+ mocked_main_window = MagicMock()
474+ Registry().register('main_window', mocked_main_window)
475+ # Mock SlideController
476+ slide_controller = SlideController(None)
477+ slide_controller.service_item = mocked_pres_item
478+ slide_controller.is_live = False
479+ slide_controller.log_debug = MagicMock()
480+ slide_controller.selected_row = MagicMock()
481+ slide_controller.screens = MagicMock()
482+ slide_controller.screens.current = {'primary': ''}
483+ slide_controller.display = MagicMock()
484+ slide_controller.display.preview.return_value = QtGui.QImage()
485+ slide_controller.grab_maindisplay = MagicMock()
486+ slide_controller.slide_preview = MagicMock()
487+ slide_controller.slide_count = 0
488+
489+ # WHEN: update_preview is called
490+ slide_controller.update_preview()
491+
492+ # THEN: setPixmap and the image_manager should have been called
493+ self.assertEqual(1, slide_controller.slide_preview.setPixmap.call_count, 'setPixmap should be called')
494+ self.assertEqual(0, slide_controller.display.preview.call_count, 'display.preview() should not be called')
495+ self.assertEqual(0, mocked_singleShot.call_count, 'Timer to grab_maindisplay should not be called')
496+ self.assertEqual(1, mocked_image_manager.get_image.call_count, 'image_manager should be called')
497+
498+ @patch(u'openlp.core.ui.slidecontroller.SlideController.image_manager')
499+ @patch(u'PyQt5.QtCore.QTimer.singleShot')
500+ def update_preview_test_media(self, mocked_singleShot, mocked_image_manager):
501+ """
502+ Test that the preview screen is updated with the correct preview for media service items
503+ """
504+ # GIVEN: A mocked media service item, a mocked image_manager, a mocked Registry,
505+ # and a slide controller with many mocks.
506+ # Mocked Media Item
507+ mocked_media_item = MagicMock()
508+ mocked_media_item.get_rendered_frame.return_value = ''
509+ mocked_media_item.is_capable = MagicMock()
510+ mocked_media_item.is_capable.side_effect = [True, False]
511+ # Mock image_manager
512+ mocked_image_manager.get_image.return_value = QtGui.QImage()
513+ # Mock Registry
514+ Registry.create()
515+ mocked_main_window = MagicMock()
516+ Registry().register('main_window', mocked_main_window)
517+ # Mock SlideController
518+ slide_controller = SlideController(None)
519+ slide_controller.service_item = mocked_media_item
520+ slide_controller.is_live = False
521+ slide_controller.log_debug = MagicMock()
522+ slide_controller.selected_row = MagicMock()
523+ slide_controller.screens = MagicMock()
524+ slide_controller.screens.current = {'primary': ''}
525+ slide_controller.display = MagicMock()
526+ slide_controller.display.preview.return_value = QtGui.QImage()
527+ slide_controller.grab_maindisplay = MagicMock()
528+ slide_controller.slide_preview = MagicMock()
529+ slide_controller.slide_count = 0
530+
531+ # WHEN: update_preview is called
532+ slide_controller.update_preview()
533+
534+ # THEN: setPixmap should have been called
535+ self.assertEqual(1, slide_controller.slide_preview.setPixmap.call_count, 'setPixmap should be called')
536+ self.assertEqual(0, slide_controller.display.preview.call_count, 'display.preview() should not be called')
537+ self.assertEqual(0, mocked_singleShot.call_count, 'Timer to grab_maindisplay should not be called')
538+ self.assertEqual(0, mocked_image_manager.get_image.call_count, 'image_manager should not be called')
539+
540+ @patch(u'openlp.core.ui.slidecontroller.SlideController.image_manager')
541+ @patch(u'PyQt5.QtCore.QTimer.singleShot')
542+ def update_preview_test_image(self, mocked_singleShot, mocked_image_manager):
543+ """
544+ Test that the preview screen is updated with the correct preview for image service items
545+ """
546+ # GIVEN: A mocked image service item, a mocked image_manager, a mocked Registry,
547+ # and a slide controller with many mocks.
548+ # Mocked Image Item
549+ mocked_img_item = MagicMock()
550+ mocked_img_item.get_rendered_frame.return_value = ''
551+ mocked_img_item.is_capable = MagicMock()
552+ mocked_img_item.is_capable.side_effect = [False, True]
553+ # Mock image_manager
554+ mocked_image_manager.get_image.return_value = QtGui.QImage()
555+ # Mock Registry
556+ Registry.create()
557+ mocked_main_window = MagicMock()
558+ Registry().register('main_window', mocked_main_window)
559+ # Mock SlideController
560+ slide_controller = SlideController(None)
561+ slide_controller.service_item = mocked_img_item
562+ slide_controller.is_live = False
563+ slide_controller.log_debug = MagicMock()
564+ slide_controller.selected_row = MagicMock()
565+ slide_controller.screens = MagicMock()
566+ slide_controller.screens.current = {'primary': ''}
567+ slide_controller.display = MagicMock()
568+ slide_controller.display.preview.return_value = QtGui.QImage()
569+ slide_controller.grab_maindisplay = MagicMock()
570+ slide_controller.slide_preview = MagicMock()
571+ slide_controller.slide_count = 0
572+
573+ # WHEN: update_preview is called
574+ slide_controller.update_preview()
575+
576+ # THEN: setPixmap and display.preview should have been called
577+ self.assertEqual(1, slide_controller.slide_preview.setPixmap.call_count, 'setPixmap should be called')
578+ self.assertEqual(1, slide_controller.display.preview.call_count, 'display.preview() should be called')
579+ self.assertEqual(0, mocked_singleShot.call_count, 'Timer to grab_maindisplay should not be called')
580+ self.assertEqual(0, mocked_image_manager.get_image.call_count, 'image_manager should not be called')
581+
582
583 class TestInfoLabel(TestCase):
584
585
586=== modified file 'tests/functional/openlp_core_ui_lib/test_listpreviewwidget.py'
587--- tests/functional/openlp_core_ui_lib/test_listpreviewwidget.py 2016-04-22 18:35:23 +0000
588+++ tests/functional/openlp_core_ui_lib/test_listpreviewwidget.py 2016-05-16 12:42:11 +0000
589@@ -24,9 +24,11 @@
590 """
591 from unittest import TestCase
592
593+from PyQt5 import QtGui
594+
595 from openlp.core.common import Settings
596 from openlp.core.ui.lib.listpreviewwidget import ListPreviewWidget
597-from openlp.core.lib import ServiceItem
598+from openlp.core.lib import ImageSource, ServiceItem
599
600 from tests.functional import MagicMock, patch, call
601
602@@ -72,6 +74,53 @@
603 self.assertIsNotNone(list_preview_widget, 'The ListPreviewWidget object should not be None')
604 self.assertEquals(list_preview_widget.screen_ratio, 1, 'Should not be called')
605
606+ @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.image_manager')
607+ @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.resizeRowsToContents')
608+ @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.setRowHeight')
609+ def replace_service_item_test_thumbs(self, mocked_setRowHeight, mocked_resizeRowsToContents,
610+ mocked_image_manager):
611+ """
612+ Test that thubmails for different slides are loaded properly in replace_service_item.
613+ """
614+ # GIVEN: A setting to adjust "Max height for non-text slides in slide controller",
615+ # different ServiceItem(s), an ImageManager, and a ListPreviewWidget.
616+
617+ # Mock Settings().value('advanced/slide max height')
618+ self.mocked_Settings_obj.value.return_value = 0
619+ # Mock self.viewport().width()
620+ self.mocked_viewport_obj.width.return_value = 200
621+ # Mock Image service item
622+ mocked_img_service_item = MagicMock()
623+ mocked_img_service_item.is_text.return_value = False
624+ mocked_img_service_item.is_media.return_value = False
625+ mocked_img_service_item.is_command.return_value = False
626+ mocked_img_service_item.is_capable.return_value = False
627+ mocked_img_service_item.get_frames.return_value = [{'title': None, 'path': 'TEST1', 'image': 'FAIL'},
628+ {'title': None, 'path': 'TEST2', 'image': 'FAIL'}]
629+ # Mock Command service item
630+ mocked_cmd_service_item = MagicMock()
631+ mocked_cmd_service_item.is_text.return_value = False
632+ mocked_cmd_service_item.is_media.return_value = False
633+ mocked_cmd_service_item.is_command.return_value = True
634+ mocked_cmd_service_item.is_capable.return_value = True
635+ mocked_cmd_service_item.get_frames.return_value = [{'title': None, 'path': 'FAIL', 'image': 'TEST3'},
636+ {'title': None, 'path': 'FAIL', 'image': 'TEST4'}]
637+ # Mock image_manager
638+ mocked_image_manager.get_image.return_value = QtGui.QImage()
639+
640+ # init ListPreviewWidget and load service item
641+ list_preview_widget = ListPreviewWidget(None, 1)
642+
643+ # WHEN: replace_service_item is called
644+ list_preview_widget.replace_service_item(mocked_img_service_item, 200, 0)
645+ list_preview_widget.replace_service_item(mocked_cmd_service_item, 200, 0)
646+
647+ # THEN: The ImageManager should be called in the appriopriate manner for each service item.
648+ self.assertEquals(mocked_image_manager.get_image.call_count, 4, 'Should be called once for each slide')
649+ calls = [call('TEST1', ImageSource.ImagePlugin), call('TEST2', ImageSource.ImagePlugin),
650+ call('TEST3', ImageSource.CommandPlugins), call('TEST4', ImageSource.CommandPlugins)]
651+ mocked_image_manager.get_image.assert_has_calls(calls)
652+
653 @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.resizeRowsToContents')
654 @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.setRowHeight')
655 def replace_recalculate_layout_test_text(self, mocked_setRowHeight, mocked_resizeRowsToContents):
656@@ -120,6 +169,7 @@
657 # Mock image service item
658 service_item = MagicMock()
659 service_item.is_text.return_value = False
660+ service_item.is_capable.return_value = False
661 service_item.get_frames.return_value = [{'title': None, 'path': None, 'image': None},
662 {'title': None, 'path': None, 'image': None}]
663 # init ListPreviewWidget and load service item
664@@ -156,6 +206,7 @@
665 # Mock image service item
666 service_item = MagicMock()
667 service_item.is_text.return_value = False
668+ service_item.is_capable.return_value = False
669 service_item.get_frames.return_value = [{'title': None, 'path': None, 'image': None},
670 {'title': None, 'path': None, 'image': None}]
671 # init ListPreviewWidget and load service item
672@@ -225,6 +276,7 @@
673 # Mock image service item
674 service_item = MagicMock()
675 service_item.is_text.return_value = False
676+ service_item.is_capable.return_value = False
677 service_item.get_frames.return_value = [{'title': None, 'path': None, 'image': None},
678 {'title': None, 'path': None, 'image': None}]
679 # Mock self.cellWidget().children().setMaximumWidth()
680@@ -261,6 +313,7 @@
681 # Mock image service item
682 service_item = MagicMock()
683 service_item.is_text.return_value = False
684+ service_item.is_capable.return_value = False
685 service_item.get_frames.return_value = [{'title': None, 'path': None, 'image': None},
686 {'title': None, 'path': None, 'image': None}]
687 # Mock self.cellWidget().children().setMaximumWidth()