Merge lp:~rhuddie/ubuntu-autopilot-tests/video_playback_tests into lp:ubuntu-autopilot-tests/ubuntu-experience-tests
- video_playback_tests
- Merge into ubuntu-experience-tests
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 |
Related bugs: |
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 |
Commit message
Description of the change
A new test for playing videos in the mediaplayer-app and validating playback is working.
PS Jenkins bot (ps-jenkins) wrote : | # |
- 35. By Richard Huddie
-
change relative paths to run from tests directory
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:35
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:34
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 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
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:36
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:38
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:38
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 39. By Richard Huddie
-
remove config file path and use default instead
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:39
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 40. By Richard Huddie
-
add __init__.py to samples dir
- 41. By Richard Huddie
-
add data_files to setup.py
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:40
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:40
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:41
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 42. By Richard Huddie
-
add file names
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:42
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 43. By Richard Huddie
-
use include_
package_ data
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:43
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 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
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:45
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 46. By Richard Huddie
-
remove unknown --fullscreen option
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:45
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http://
Executed test runs:
None: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:45
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:46
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:46
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 47. By Richard Huddie
-
change base class and tidy
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:47
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
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
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' |
653 | Binary 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' |
655 | Binary 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 |
FAILED: Continuous integration, rev:34 /code.launchpad .net/~rhuddie/ ubuntu- autopilot- tests/video_ playback_ tests/+ merge/240014/ +edit-commit- message
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http:// jenkins. qa.ubuntu. com/job/ ubuntu- autopilot- tests-ubuntu- experience- tests-ci/ 46/ jenkins. qa.ubuntu. com/job/ generic- deb-autopilot- utopic- touch/6284 jenkins. qa.ubuntu. com/job/ ubuntu- autopilot- tests-ubuntu- experience- tests-utopic- amd64-ci/ 46 jenkins. qa.ubuntu. com/job/ ubuntu- autopilot- tests-ubuntu- experience- tests-utopic- armhf-ci/ 46 jenkins. qa.ubuntu. com/job/ ubuntu- autopilot- tests-ubuntu- experience- tests-utopic- i386-ci/ 46 jenkins. qa.ubuntu. com/job/ generic- deb-autopilot- runner- mako/5893 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- utopic- armhf/7536 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- utopic- armhf/7536/ artifact/ work/output/ *zip*/output. zip s-jenkins. ubuntu- ci:8080/ job/touch- flash-device/ 15177
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/ubuntu- autopilot- tests-ubuntu- experience- tests-ci/ 46/rebuild
http://