Merge lp:~rhuddie/ubuntu-autopilot-tests/video_playback_tests into lp:ubuntu-autopilot-tests/ubuntu-experience-tests

Proposed by Richard Huddie
Status: Needs review
Proposed branch: lp:~rhuddie/ubuntu-autopilot-tests/video_playback_tests
Merge into: lp:ubuntu-autopilot-tests/ubuntu-experience-tests
Diff against target: 655 lines (+609/-0)
6 files modified
debian/control (+3/-0)
setup.py (+1/-0)
ubuntu_experience_tests/tests/multimedia/__init__.py (+38/-0)
ubuntu_experience_tests/tests/multimedia/config.ini (+8/-0)
ubuntu_experience_tests/tests/multimedia/test_video_playback.py (+542/-0)
ubuntu_experience_tests/tests/multimedia/video_samples/__init__.py (+17/-0)
To merge this branch: bzr merge lp:~rhuddie/ubuntu-autopilot-tests/video_playback_tests
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Needs Fixing
Ubuntu Testcase Admins Pending
Review via email: mp+240014@code.launchpad.net

Description of the change

A new test for playing videos in the mediaplayer-app and validating playback is working.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
35. By Richard Huddie

change relative paths to run from tests directory

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
36. By Richard Huddie

update import paths

37. By Richard Huddie

Update default helpers methods and file path

38. By Richard Huddie

use parameters for defaults

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
39. By Richard Huddie

remove config file path and use default instead

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
40. By Richard Huddie

add __init__.py to samples dir

41. By Richard Huddie

add data_files to setup.py

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
42. By Richard Huddie

add file names

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
43. By Richard Huddie

use include_package_data

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
44. By Richard Huddie

read boolean value from ini

45. By Richard Huddie

add MP4 and ini using package_data, exclude src types from video list

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
46. By Richard Huddie

remove unknown --fullscreen option

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
47. By Richard Huddie

change base class and tidy

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)

Unmerged revisions

47. By Richard Huddie

change base class and tidy

46. By Richard Huddie

remove unknown --fullscreen option

45. By Richard Huddie

add MP4 and ini using package_data, exclude src types from video list

44. By Richard Huddie

read boolean value from ini

43. By Richard Huddie

use include_package_data

42. By Richard Huddie

add file names

41. By Richard Huddie

add data_files to setup.py

40. By Richard Huddie

add __init__.py to samples dir

39. By Richard Huddie

remove config file path and use default instead

38. By Richard Huddie

use parameters for defaults

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/control'
2--- debian/control 2014-08-08 06:53:37 +0000
3+++ debian/control 2014-11-03 16:07:13 +0000
4@@ -22,6 +22,9 @@
5 ubuntu-ui-toolkit-autopilot,
6 unity8-autopilot,
7 url-dispatcher-tools,
8+ mediaplayer-app-autopilot,
9+ python3-evdev,
10+ imagemagick,
11 Description: Ubuntu user experience Autopilot tests
12 This package provides a set of autopilot tests for testing
13 the inter-app integration under Unity8
14
15=== modified file 'setup.py'
16--- setup.py 2014-06-19 03:06:13 +0000
17+++ setup.py 2014-11-03 16:07:13 +0000
18@@ -32,4 +32,5 @@
19 license='GPLv3',
20 packages=setuptools.find_packages(),
21 test_suite='ubuntu_experience_tests.tests',
22+ package_data={'': ['*.ini', '*.MP4'], },
23 )
24
25=== added directory 'ubuntu_experience_tests/tests/multimedia'
26=== added file 'ubuntu_experience_tests/tests/multimedia/__init__.py'
27--- ubuntu_experience_tests/tests/multimedia/__init__.py 1970-01-01 00:00:00 +0000
28+++ ubuntu_experience_tests/tests/multimedia/__init__.py 2014-11-03 16:07:13 +0000
29@@ -0,0 +1,38 @@
30+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
31+#
32+# Copyright 2014 Canonical Ltd.
33+#
34+# This file is part of ubuntu-experience-tests.
35+#
36+# This program is free software; you can redistribute it and/or modify
37+# it under the terms of the GNU General Public License version 3, as published
38+# by the Free Software Foundation.
39+#
40+# This program is distributed in the hope that it will be useful,
41+# but WITHOUT ANY WARRANTY; without even the implied warranty of
42+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
43+# GNU General Public License for more details.
44+#
45+# You should have received a copy of the GNU General Public License
46+# along with this program. If not, see <http://www.gnu.org/licenses/>
47+
48+
49+import os
50+
51+DEFAULT_CONFIG_FILE = 'config.ini'
52+DEFAULT_SAMPLES_DIR = 'video_samples'
53+
54+
55+def _get_current_dir():
56+ """Return the current dir path"""
57+ return os.path.dirname(__file__)
58+
59+
60+def get_default_config_file():
61+ """Return the path for the default config file"""
62+ return os.path.join(_get_current_dir(), DEFAULT_CONFIG_FILE)
63+
64+
65+def get_default_samples_dir():
66+ """Return the path for the default samples directory"""
67+ return os.path.join(_get_current_dir(), DEFAULT_SAMPLES_DIR)
68
69=== added file 'ubuntu_experience_tests/tests/multimedia/config.ini'
70--- ubuntu_experience_tests/tests/multimedia/config.ini 1970-01-01 00:00:00 +0000
71+++ ubuntu_experience_tests/tests/multimedia/config.ini 2014-11-03 16:07:13 +0000
72@@ -0,0 +1,8 @@
73+[default]
74+sample-dir =
75+sample-file =
76+sample-interval-sec = 0.5
77+sample-check-count = 3
78+timeout = 10
79+run-all = no
80+
81
82=== added file 'ubuntu_experience_tests/tests/multimedia/test_video_playback.py'
83--- ubuntu_experience_tests/tests/multimedia/test_video_playback.py 1970-01-01 00:00:00 +0000
84+++ ubuntu_experience_tests/tests/multimedia/test_video_playback.py 2014-11-03 16:07:13 +0000
85@@ -0,0 +1,542 @@
86+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
87+#
88+# Copyright 2014 Canonical Ltd.
89+#
90+# This file is part of ubuntu-experience-tests.
91+#
92+# This program is free software; you can redistribute it and/or modify
93+# it under the terms of the GNU General Public License version 3, as published
94+# by the Free Software Foundation.
95+#
96+# This program is distributed in the hope that it will be useful,
97+# but WITHOUT ANY WARRANTY; without even the implied warranty of
98+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
99+# GNU General Public License for more details.
100+#
101+# You should have received a copy of the GNU General Public License
102+# along with this program. If not, see <http://www.gnu.org/licenses/>
103+
104+import configparser
105+import os
106+import re
107+import subprocess
108+import tempfile
109+import time
110+from collections import deque
111+from threading import Timer
112+
113+from autopilot import get_test_configuration
114+from autopilot.display import Display
115+from autopilot.matchers import Eventually
116+from mediaplayer_app.emulators.main_window import MainWindow
117+from testtools.matchers import Equals
118+from ubuntuuitoolkit import emulators as toolkit_emulators
119+from ubuntu_experience_tests import tests
120+
121+
122+class ConfigData:
123+ """Class to read config data from file and cmd line options"""
124+
125+ # config keys
126+ KEY_SAMPLE_DIR = 'sample-dir'
127+ KEY_SAMPLE_FILE = 'sample-file'
128+ KEY_SAMPLE_INTERVAL = 'sample-interval-sec'
129+ KEY_SAMPLE_CHECK_COUNT = 'sample-check-count'
130+ KEY_TIMEOUT = 'timeout'
131+ KEY_CONFIG_FILE = 'config-file'
132+ KEY_CONFIG_SECTION = 'config-section'
133+ KEY_RUN_ALL = 'run-all'
134+ KEY_DEFAULT = 'default'
135+
136+ # Default configuration values
137+ DEFAULT_RUN_ALL = False
138+ DEFAULT_SAMPLE_INTERVAL = 1
139+ DEFAULT_SAMPLE_CHECK_COUNT = 5
140+ DEFAULT_TIMEOUT = 0
141+ DEFAULT_SAMPLE_FILE = ''
142+
143+ def __init__(self, cmdline_config):
144+ """Init using command line options for config file and section"""
145+ # get cmdline options
146+ self.cmdline_config = cmdline_config
147+ # set default setup parameters
148+ self.run_all = False
149+ self.config_file = tests.multimedia.get_default_config_file()
150+ self.config_section = self.KEY_DEFAULT
151+ self.parser = configparser.ConfigParser()
152+ # now load config from files and cmdline
153+ self.load_config()
154+
155+ def load_config(self):
156+ """Load the test config using command line and file"""
157+ # reset the test config parameters
158+ self.sample_dir = tests.multimedia.get_default_samples_dir()
159+ self.sample_file = self.DEFAULT_SAMPLE_FILE
160+ self.sample_interval = self.DEFAULT_SAMPLE_INTERVAL
161+ self.sample_check_count = self.DEFAULT_SAMPLE_CHECK_COUNT
162+ self.timeout = self.DEFAULT_TIMEOUT
163+ # get config file from cmdline options if specified
164+ self.get_custom_config_from_cmdline()
165+ # now read config from file
166+ self.get_test_config_from_file()
167+ # finally overwrite config with any cmdline options
168+ self.get_test_config_from_cmdline()
169+
170+ def get_custom_config_from_cmdline(self):
171+ """Read the custom config options from cmd line parameters"""
172+ # Get config-file and config-section from cmd line options
173+ if self.KEY_CONFIG_FILE in self.cmdline_config:
174+ self.config_file = self.cmdline_config[self.KEY_CONFIG_FILE]
175+ if self.KEY_CONFIG_SECTION in self.cmdline_config:
176+ self.config_section = self.cmdline_config[self.KEY_CONFIG_SECTION]
177+ # Read the specified config file
178+ self.parser.read(self.config_file)
179+ # now get run-all parameter. check cmdline first, then file
180+ if self.KEY_RUN_ALL in self.cmdline_config:
181+ self.run_all = True
182+ elif self.KEY_RUN_ALL in self.parser[self.config_section]:
183+ if self.parser[self.config_section].getboolean(self.KEY_RUN_ALL):
184+ self.run_all = True
185+
186+ def get_test_config_from_cmdline(self):
187+ """Read test parameters from cmd line options"""
188+ if self.KEY_SAMPLE_DIR in self.cmdline_config:
189+ self.sample_dir = self.cmdline_config[self.KEY_SAMPLE_DIR]
190+ self.sample_file = ''
191+ if self.KEY_SAMPLE_FILE in self.cmdline_config:
192+ self.sample_file = self.cmdline_config[self.KEY_SAMPLE_FILE]
193+ # if a file is specified, then remove the directory config,
194+ # otherwise this will take priority
195+ self.sample_dir = ''
196+ if self.KEY_SAMPLE_INTERVAL in self.cmdline_config:
197+ self.sample_interval = int(
198+ self.cmdline_config[self.KEY_SAMPLE_INTERVAL])
199+ if self.KEY_SAMPLE_CHECK_COUNT in self.cmdline_config:
200+ self.sample_check_count = int(
201+ self.cmdline_config[self.KEY_SAMPLE_CHECK_COUNT])
202+ if self.KEY_TIMEOUT in self.cmdline_config:
203+ self.timeout = int(self.cmdline_config[self.KEY_TIMEOUT])
204+
205+ def get_test_config_from_file(self):
206+ """Read test parameters from config file"""
207+ if self.KEY_SAMPLE_DIR in self.parser[self.config_section]:
208+ val = self.parser[self.config_section][self.KEY_SAMPLE_DIR]
209+ if len(val) > 0:
210+ self.sample_dir = val
211+ self.sample_file = ''
212+ if self.KEY_SAMPLE_FILE in self.parser[self.config_section]:
213+ val = self.parser[self.config_section][self.KEY_SAMPLE_FILE]
214+ if len(val) > 0:
215+ self.sample_file = val
216+ # if a file is specified, then remove the directory config,
217+ # otherwise this will take priority
218+ self.sample_dir = ''
219+ if self.KEY_SAMPLE_INTERVAL in self.parser[self.config_section]:
220+ val = self.parser[self.config_section][self.KEY_SAMPLE_INTERVAL]
221+ if len(val) > 0:
222+ self.sample_interval = float(val)
223+ if self.KEY_SAMPLE_CHECK_COUNT in self.parser[self.config_section]:
224+ val = self.parser[self.config_section][self.KEY_SAMPLE_CHECK_COUNT]
225+ if len(val) > 0:
226+ self.sample_check_count = int(val)
227+ if self.KEY_TIMEOUT in self.parser[self.config_section]:
228+ val = self.parser[self.config_section][self.KEY_TIMEOUT]
229+ if len(val) > 0:
230+ self.timeout = int(val)
231+
232+
233+class VideoPlaybackTestCase(tests.UbuntuExperienceTestCase):
234+ """Autopilot test suite for playing videos in Mediaplayer-app"""
235+
236+ def setUp(self):
237+ """Setup test suite"""
238+ super(VideoPlaybackTestCase, self).setUp()
239+ self.pointing_device = toolkit_emulators.get_pointing_device()
240+ # calculate screen crop area based on device geometry
241+ self.crop_area = self.get_crop_area()
242+ # create config data using any command line options
243+ self.config = ConfigData(get_test_configuration())
244+
245+ def start_timer(self):
246+ """Start a timer if a time limit has been set"""
247+ if self.is_timeout_set():
248+ self.test_timer = Timer(self.config.timeout, self.on_timeout)
249+ self.test_timer.start()
250+
251+ def cancel_timer_if_active(self):
252+ """Cancel timer if it is set and still running"""
253+ if self.is_timeout_set() and not self.timeout:
254+ self.test_timer.cancel()
255+
256+ def on_timeout(self):
257+ """Timer timeout event handler"""
258+ # Flag that a timeout has occured
259+ self.timeout = True
260+
261+ def is_timeout_set(self):
262+ """
263+ Decide whether a timeout is required
264+ :return: True if timeout is set
265+ False otherwise
266+ """
267+ ret = False
268+ if self.config.timeout > 0:
269+ ret = True
270+ return ret
271+
272+ def open_media_player_app(self, sample_file):
273+ """
274+ Open media player app with the specified sample file
275+ :param sample_file: Path to file to open
276+ """
277+ app = self.launch_test_application(
278+ "mediaplayer-app",
279+ sample_file,
280+ "--desktop_file_hint="
281+ "/usr/share/applications/mediaplayer-app.desktop",
282+ app_type='qt')
283+ self.player = MainWindow(app).get_player()
284+ self.assertThat(self.player.playing, Eventually(Equals(True)))
285+
286+ def close_media_player_app(self):
287+ """Close the running mediaplayer-app instance"""
288+ out = subprocess.check_output(['pidof', '-s', 'mediaplayer-app'])
289+ pid = out.decode().strip()
290+ subprocess.check_call(['kill', '-9', pid])
291+
292+ def is_playing(self):
293+ """
294+ Check if the media player is currently playing video
295+ :return: True if it is playing, False otherwise
296+ """
297+ return self.player.playing
298+
299+ def get_rgba_screenshot(self, target_file):
300+ """
301+ Take a screenshot in rgba format
302+ :return: Path of screenshot rgba file
303+ """
304+ try:
305+ subprocess.check_call([
306+ 'mirscreencast',
307+ '-m', '/run/mir_socket',
308+ '-n', '1',
309+ '-f', target_file
310+ ])
311+ except FileNotFoundError as e:
312+ e.args += ("The utility 'mirscreencast' is not available.", )
313+ raise
314+ except subprocess.CalledProcessError as e:
315+ e.args += ("Failed to take screenshot.", )
316+ raise
317+
318+ def convert_rgba_image(self, rgba_source_file, target_file):
319+ """
320+ Convert rgba file to PNG target file
321+ :param rgba_source_file: Path to rgba source image
322+ :param target_file: Path to PNG target file
323+ """
324+ try:
325+ subprocess.check_call([
326+ 'convert',
327+ '-alpha', 'off',
328+ '-depth', '8',
329+ '-size', '540x960',
330+ 'rgba:{}[0]'.format(rgba_source_file),
331+ target_file
332+ ])
333+ except FileNotFoundError as e:
334+ e.args += ("'convert' is not available, install imagemagick.", )
335+ raise
336+ except subprocess.CalledProcessError as e:
337+ e.args += ("Failed to convert screenshot to png format.", )
338+ raise
339+
340+ def get_screenshot(self):
341+ """
342+ Take screen shot and return file path of png file
343+ :return: Path of PNG screen image file
344+ """
345+ timestamp = int(time.time())
346+ filename_rgba = 'screenshot-{}.rgba'.format(timestamp)
347+ filename_png = 'screenshot-{}.png'.format(timestamp)
348+ temp_dir = tempfile.gettempdir()
349+ filepath_rgba = os.path.join(temp_dir, filename_rgba)
350+ filepath_png = os.path.join(temp_dir, filename_png)
351+ self.get_rgba_screenshot(filepath_rgba)
352+ self.convert_rgba_image(filepath_rgba, filepath_png)
353+ # finished with rgba file so delete it
354+ os.remove(filepath_rgba)
355+ return filepath_png
356+
357+ def get_crop_area(self):
358+ """
359+ Calculate the crop area at centre of screen in pixel format
360+ [width]x[height]+[x_start]+[y_start]
361+ :return: Crop area measured in pixels
362+ """
363+ crop_fmt = '{0}x{1}+{2}+{3}'
364+ width = 100
365+ height = 100
366+ display = Display.create()
367+ centre_x = display.get_screen_width() // 2
368+ centre_y = display.get_screen_height() // 2
369+ x_start = centre_x - (width // 2)
370+ y_start = centre_y - (height // 2)
371+ crop_str = crop_fmt.format(width, height, x_start, y_start)
372+ return crop_str
373+
374+ def crop_screenshot(self, source_file):
375+ """
376+ Crop the screen shot and return path for cropped image
377+ :param source_file: Path of image file to crop
378+ :return: Path of cropped image file
379+ """
380+ split_path = os.path.splitext(source_file)
381+ path = split_path[0]
382+ ext = split_path[1]
383+ crop_file_path = '{0}_crop{1}'.format(path, ext)
384+
385+ try:
386+ subprocess.check_call([
387+ 'convert',
388+ source_file,
389+ '-crop', self.crop_area,
390+ crop_file_path
391+ ])
392+ except FileNotFoundError as e:
393+ e.args += ("The utility 'convert' is not available.", )
394+ raise
395+ except subprocess.CalledProcessError as e:
396+ e.args += ("Failed to crop screenshot.", )
397+ raise
398+ return crop_file_path
399+
400+ def get_average_colour(self, source_file):
401+ """
402+ Calculate the average colour of the specified image
403+ """
404+ try:
405+ ret = subprocess.check_output([
406+ 'convert',
407+ source_file,
408+ '-resize', '1x1',
409+ 'txt:'
410+ ])
411+ except FileNotFoundError as e:
412+ e.args += ("The utility 'convert' is not available.", )
413+ raise
414+ except subprocess.CalledProcessError as e:
415+ e.args += ("Failed to get average colour.", )
416+ raise
417+ return self.get_hex_colour_code(ret.decode())
418+
419+ def get_hex_colour_code(self, colour_string):
420+ """
421+ Get the hex colour code from the specified colour string
422+ :param colour_string: String representing colour output
423+ :return: Hex representation of colour code
424+ """
425+ colour_pattern = re.compile(r'^#.*#(.*?)\s+.*$',
426+ re.IGNORECASE | re.DOTALL)
427+ hex_code = None
428+ match = colour_pattern.search(colour_string)
429+ if match:
430+ hex_code = match.group(1)
431+ return hex_code
432+
433+ def capture_and_check_frames_whilst_playing(self):
434+ """
435+ Capture and check screen frames during playback
436+ :return: True if playback completes, or timeout is reached.
437+ False if the screen is frozen and video doesn't play'
438+ """
439+ # frame_colours is a FIFO queue which holds the required number of
440+ # colour samples
441+ frame_colours = deque(maxlen=self.config.sample_check_count)
442+ timer_started = False
443+ self.timeout = False
444+ while self.player.playing:
445+ if self.is_timeout_set() and not timer_started:
446+ # start the timer if required once playback has started
447+ self.start_timer()
448+ timer_started = True
449+ # take screenshot
450+ screenshot = self.get_screenshot()
451+ # store the frame colour
452+ frame_colours.append(self.calculate_frame_colour(screenshot))
453+ # no longer need the screenshot file so delete it
454+ os.remove(screenshot)
455+ # check frames to make sure playback is working
456+ if not self.check_frame_colours(frame_colours):
457+ # playback error, so exit now
458+ self.cancel_timer_if_active()
459+ return False
460+ # wait for required interval period, checking for timeouts
461+ # at 0.1 second intervals
462+ # The sample interval is defined in seconds (e.g. 0.5 sec),
463+ # so multiply by 10 to get required number of 0.1 second waits
464+ # Note: Sample period could be large (e.g. several minutes)
465+ # for long running tests, so there is a need to check for timeouts
466+ # between the sample interval.
467+ for count in range(int(self.config.sample_interval * 10)):
468+ time.sleep(0.1)
469+ # check if a timeout has occured
470+ if self.timeout:
471+ # timeout has triggered, so exit now
472+ # this is not considered a failure
473+ # no need to cancel the timer as it has triggered
474+ return True
475+ # playback has completed with no timeout
476+ self.cancel_timer_if_active()
477+ return True
478+
479+ def calculate_frame_colour(self, frame):
480+ """
481+ Calculate the colour of the centre of the specified image
482+ :param frame: Path to image file
483+ :return: Average colour of centre of image
484+ """
485+ # crop to the centre of the image
486+ crop = self.crop_screenshot(frame)
487+ # get colour of cropped area
488+ colour = self.get_average_colour(crop)
489+ # now delete cropped file
490+ os.remove(crop)
491+ return colour
492+
493+ def check_frame_colours(self, frame_colours):
494+ """
495+ Analyse the frame colours to check that playback is working
496+ :param frame_colours: dequeue object containing frame colours
497+ :return: True if frame colours are changing
498+ False if all the framecolours are the same
499+ """
500+ frames_ok = True
501+ for colour in frame_colours:
502+ # count the number of occurances of current colour in the
503+ # previous colours
504+ if frame_colours.count(colour) is frame_colours.maxlen:
505+ # all the frames are same colour, so this is a fail
506+ frames_ok = False
507+ break
508+ return frames_ok
509+
510+ def is_excluded_file(self, file_path):
511+ """
512+ Determine whether the specified file is an excluded type
513+ :param file_path: Path of the specified file
514+ :return: True if the file is an excluded type, false otherwise
515+ """
516+ # list of file extensions to ignore
517+ excluded_list = ['.py', '.pyc', '.py~']
518+ file_ext = os.path.splitext(file_path)[1]
519+ excluded = False
520+ if file_ext in excluded_list:
521+ excluded = True
522+ return excluded
523+
524+ def get_sample_files_from_dir(self, sample_dir):
525+ """
526+ Recursively search the specified directory for files to test
527+ :param sample_dir: Top level directory to search
528+ :return: List containing path of all sub-files
529+ """
530+ file_list = []
531+ for root, dirs, files in os.walk(sample_dir):
532+ for file in files:
533+ # remove any excluded types of file
534+ if not self.is_excluded_file(file):
535+ file_list.append(os.path.abspath(os.path.join(root, file)))
536+ return file_list
537+
538+ def get_sample_file_list(self):
539+ """
540+ Based on supplied config get the list of files to test
541+ :return: List of files required for the current test
542+ """
543+ # get the list of video sample files to play, based on the test config
544+ sample_files = []
545+ if self.config.sample_dir:
546+ # get the files from the specified directory
547+ sample_files = self.get_sample_files_from_dir(
548+ self.config.sample_dir)
549+ elif self.config.sample_file:
550+ # use the specified file
551+ sample_files.append(self.config.sample_file)
552+ return sample_files
553+
554+ def play_and_sample_video(self, file_path):
555+ """
556+ Play the specified sample file in the mediaplayer-app
557+ :param file_path: Path to video file to launch in mediaplayer-app
558+ :return: True if playback comleted successfully
559+ False if playback didn't complete successfully
560+ """
561+ # launch media player app
562+ self.open_media_player_app(file_path)
563+ # capture video frames
564+ # This call will run until playback is completed
565+ # timeout is reached, or a freeze is detected
566+ result = self.capture_and_check_frames_whilst_playing()
567+ # close the media app
568+ self.close_media_player_app()
569+ return result
570+
571+ def print_failures(self, failures):
572+ """
573+ Print out a list of failed video files
574+ :param failures: List containing path of all failed video files
575+ """
576+ divider = '-----------------------------------------------------------'
577+ fail_count = len(failures)
578+ if fail_count > 0:
579+ print((divider))
580+ print(('{} failures:'.format(fail_count)))
581+ print((divider))
582+ for failure in failures:
583+ print((' {}'.format(failure)))
584+ print((divider))
585+
586+ def test_video_playback_from_samples(self):
587+ """
588+ Test to play video files based on test config
589+ """
590+ failures = []
591+ sections = []
592+ if self.config.run_all:
593+ # run through every config section in the config file
594+ sections = self.config.parser.sections()
595+ else:
596+ # only use the specified config section
597+ sections.append(self.config.config_section)
598+
599+ for section in sections:
600+ # copy the current section into the config
601+ self.config.config_section = section
602+ # now update config for current section
603+ self.config.load_config()
604+ # run the test for this section
605+ video_files = self.get_sample_file_list()
606+ for video in video_files:
607+ # play all of the samples before checking results
608+ start_time = time.time()
609+ result = self.play_and_sample_video(video)
610+ if not result:
611+ # record failure time
612+ end_time = time.time()
613+ elapsed = time.gmtime(end_time - start_time)
614+ elapsed_str = time.strftime("%H:%M:%S", elapsed)
615+ # print time of failure with file name
616+ failures.append(' {0} {1}'.format(elapsed_str, video))
617+ # take a screenshot of the failure with name of video file
618+ # this will be added to the test details by autopilot
619+ video_file, video_ext = os.path.splitext(
620+ os.path.split(video)[1])
621+ screenshot_name = '{0}-{1}{2}'.format(
622+ video_file, elapsed_str, video_ext)
623+ self.take_screenshot(screenshot_name)
624+
625+ self.print_failures(failures)
626+ # fail the test if there were any failures
627+ self.assertThat(len(failures), Equals(0))
628
629=== added directory 'ubuntu_experience_tests/tests/multimedia/video_samples'
630=== added file 'ubuntu_experience_tests/tests/multimedia/video_samples/__init__.py'
631--- ubuntu_experience_tests/tests/multimedia/video_samples/__init__.py 1970-01-01 00:00:00 +0000
632+++ ubuntu_experience_tests/tests/multimedia/video_samples/__init__.py 2014-11-03 16:07:13 +0000
633@@ -0,0 +1,17 @@
634+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
635+#
636+# Copyright 2014 Canonical Ltd.
637+#
638+# This file is part of ubuntu-experience-tests.
639+#
640+# This program is free software; you can redistribute it and/or modify
641+# it under the terms of the GNU General Public License version 3, as published
642+# by the Free Software Foundation.
643+#
644+# This program is distributed in the hope that it will be useful,
645+# but WITHOUT ANY WARRANTY; without even the implied warranty of
646+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
647+# GNU General Public License for more details.
648+#
649+# You should have received a copy of the GNU General Public License
650+# along with this program. If not, see <http://www.gnu.org/licenses/>
651
652=== added file 'ubuntu_experience_tests/tests/multimedia/video_samples/big_buck_bunny_480p_H264_AAC_25fps_1800K_short.MP4'
653Binary files ubuntu_experience_tests/tests/multimedia/video_samples/big_buck_bunny_480p_H264_AAC_25fps_1800K_short.MP4 1970-01-01 00:00:00 +0000 and ubuntu_experience_tests/tests/multimedia/video_samples/big_buck_bunny_480p_H264_AAC_25fps_1800K_short.MP4 2014-11-03 16:07:13 +0000 differ
654=== added file 'ubuntu_experience_tests/tests/multimedia/video_samples/big_buck_bunny_720p_H264_AAC_25fps_3400K_short.MP4'
655Binary files ubuntu_experience_tests/tests/multimedia/video_samples/big_buck_bunny_720p_H264_AAC_25fps_3400K_short.MP4 1970-01-01 00:00:00 +0000 and ubuntu_experience_tests/tests/multimedia/video_samples/big_buck_bunny_720p_H264_AAC_25fps_3400K_short.MP4 2014-11-03 16:07:13 +0000 differ

Subscribers

People subscribed via source and target branches