Merge lp:~canonical-platform-qa/unity8/dash-as-app-autopilot into lp:unity8
- dash-as-app-autopilot
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Michał Sawicz |
Approved revision: | 1128 |
Merged at revision: | 1112 |
Proposed branch: | lp:~canonical-platform-qa/unity8/dash-as-app-autopilot |
Merge into: | lp:unity8 |
Prerequisite: | lp:~unity-team/unity8/dash-as-app |
Diff against target: |
918 lines (+390/-179) 12 files modified
tests/autopilot/unity8/application_lifecycle/tests/test_application_lifecycle.py (+1/-1) tests/autopilot/unity8/application_lifecycle/tests/test_url_dispatcher.py (+3/-2) tests/autopilot/unity8/fixture_setup.py (+64/-0) tests/autopilot/unity8/process_helpers.py (+92/-23) tests/autopilot/unity8/shell/emulators/dash.py (+21/-8) tests/autopilot/unity8/shell/emulators/greeter.py (+4/-1) tests/autopilot/unity8/shell/emulators/main_window.py (+34/-20) tests/autopilot/unity8/shell/tests/__init__.py (+143/-50) tests/autopilot/unity8/shell/tests/test_emulators.py (+7/-18) tests/autopilot/unity8/shell/tests/test_lock_screen.py (+7/-47) tests/autopilot/unity8/shell/tests/test_notifications.py (+12/-7) tests/autopilot/unity8/shell/tests/test_upstart.py (+2/-2) |
To merge this branch: | bzr merge lp:~canonical-platform-qa/unity8/dash-as-app-autopilot |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michał Sawicz | Approve | ||
PS Jenkins bot (community) | continuous-integration | Needs Fixing | |
Review via email: mp+229123@code.launchpad.net |
This proposal supersedes a proposal from 2014-07-30.
Commit message
Update the autopilot tests to work with the new dash app.
Description of the change
Leo Arias (elopio) wrote : Posted in a previous version of this proposal | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1120
http://
Executed test runs:
FAILURE: http://
UNSTABLE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1123
http://
Executed test runs:
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Leo Arias (elopio) wrote : | # |
\o/ Only one error on the autopilot tests, the one that exposes a bug.
Michał Sawicz (saviq) wrote : | # |
Minor comments inline.
Thanks!
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1128
http://
Executed test runs:
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Michał Sawicz (saviq) wrote : | # |
Still good.
* Did you perform an exploratory manual test run of the code change and any related functionality?
Yes.
* Did CI run pass? If not, please explain why.
Known ap issue that's being fixed in the prerequisite, unrelated issues in qmltests.
Preview Diff
1 | === modified file 'tests/autopilot/unity8/application_lifecycle/tests/test_application_lifecycle.py' |
2 | --- tests/autopilot/unity8/application_lifecycle/tests/test_application_lifecycle.py 2014-06-11 15:36:51 +0000 |
3 | +++ tests/autopilot/unity8/application_lifecycle/tests/test_application_lifecycle.py 2014-08-04 16:55:24 +0000 |
4 | @@ -106,7 +106,7 @@ |
5 | self.assert_current_focused_application(application_name) |
6 | |
7 | self.main_window.show_dash_swiping() |
8 | - self.assert_current_focused_application('') |
9 | + self.assert_current_focused_application('unity8-dash') |
10 | |
11 | process_helpers.lock_unity() |
12 | greeter = self.main_window.get_greeter() |
13 | |
14 | === modified file 'tests/autopilot/unity8/application_lifecycle/tests/test_url_dispatcher.py' |
15 | --- tests/autopilot/unity8/application_lifecycle/tests/test_url_dispatcher.py 2014-06-11 15:36:51 +0000 |
16 | +++ tests/autopilot/unity8/application_lifecycle/tests/test_url_dispatcher.py 2014-08-04 16:55:24 +0000 |
17 | @@ -39,7 +39,8 @@ |
18 | desktop_file_name = os.path.basename(desktop_file_path) |
19 | application_name, _ = os.path.splitext(desktop_file_name) |
20 | |
21 | - self.assertEqual('', self.main_window.get_current_focused_app_id()) |
22 | + self.assertEqual( |
23 | + 'unity8-dash', self.main_window.get_current_focused_app_id()) |
24 | self.addCleanup(os.system, 'pkill qmlscene') |
25 | |
26 | subprocess.check_call( |
27 | @@ -47,4 +48,4 @@ |
28 | self.assert_current_focused_application(application_name) |
29 | |
30 | self.main_window.show_dash_swiping() |
31 | - self.assert_current_focused_application('') |
32 | + self.assert_current_focused_application('unity8-dash') |
33 | |
34 | === added file 'tests/autopilot/unity8/fixture_setup.py' |
35 | --- tests/autopilot/unity8/fixture_setup.py 1970-01-01 00:00:00 +0000 |
36 | +++ tests/autopilot/unity8/fixture_setup.py 2014-08-04 16:55:24 +0000 |
37 | @@ -0,0 +1,64 @@ |
38 | +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
39 | +# |
40 | +# Unity Autopilot Test Suite |
41 | +# Copyright (C) 2014 Canonical |
42 | +# |
43 | +# This program is free software: you can redistribute it and/or modify |
44 | +# it under the terms of the GNU General Public License as published by |
45 | +# the Free Software Foundation, either version 3 of the License, or |
46 | +# (at your option) any later version. |
47 | +# |
48 | +# This program is distributed in the hope that it will be useful, |
49 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
50 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
51 | +# GNU General Public License for more details. |
52 | +# |
53 | +# You should have received a copy of the GNU General Public License |
54 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
55 | +# |
56 | + |
57 | +import fixtures |
58 | +from autopilot import introspection |
59 | + |
60 | +from unity8 import process_helpers |
61 | +from unity8.shell import emulators |
62 | + |
63 | + |
64 | +class LaunchDashApp(fixtures.Fixture): |
65 | + |
66 | + """Fixture to launch the Dash app.""" |
67 | + |
68 | + def __init__(self, binary_path, variables): |
69 | + """Initialize an instance. |
70 | + |
71 | + :param str binary_path: The path to the Dash app binary. |
72 | + :param variables: The variables to use when launching the app. |
73 | + :type variables: A dictionary. |
74 | + |
75 | + """ |
76 | + super(LaunchDashApp, self).__init__() |
77 | + self.binary_path = binary_path |
78 | + self.variables = variables |
79 | + |
80 | + def setUp(self): |
81 | + """Launch the dash app when the fixture is used.""" |
82 | + super(LaunchDashApp, self).setUp() |
83 | + self.addCleanup(self.stop_application) |
84 | + self.application_proxy = self.launch_application() |
85 | + |
86 | + def launch_application(self): |
87 | + binary_arg = 'BINARY={}'.format(self.binary_path) |
88 | + testability_arg = 'QT_LOAD_TESTABILITY={}'.format(1) |
89 | + env_args = [ |
90 | + '{}={}'.format(key, value) for key, value in self.variables.items() |
91 | + ] |
92 | + all_args = [binary_arg, testability_arg] + env_args |
93 | + |
94 | + pid = process_helpers.start_job('unity8-dash', *all_args) |
95 | + return introspection.get_proxy_object_for_existing_process( |
96 | + pid=pid, |
97 | + emulator_base=emulators.UnityEmulatorBase, |
98 | + ) |
99 | + |
100 | + def stop_application(self): |
101 | + process_helpers.stop_job('unity8-dash') |
102 | |
103 | === modified file 'tests/autopilot/unity8/process_helpers.py' |
104 | --- tests/autopilot/unity8/process_helpers.py 2014-06-27 13:13:04 +0000 |
105 | +++ tests/autopilot/unity8/process_helpers.py 2014-08-04 16:55:24 +0000 |
106 | @@ -36,6 +36,10 @@ |
107 | logger = logging.getLogger(__name__) |
108 | |
109 | |
110 | +class JobError(Exception): |
111 | + pass |
112 | + |
113 | + |
114 | class CannotAccessUnity(Exception): |
115 | pass |
116 | |
117 | @@ -134,48 +138,113 @@ |
118 | """ |
119 | status = _get_unity_status() |
120 | if "start/" in status: |
121 | - try: |
122 | - output = subprocess.check_output( |
123 | - ['/sbin/initctl', 'stop', 'unity8']) |
124 | - logger.info(output) |
125 | - except subprocess.CalledProcessError as e: |
126 | - e.args += ( |
127 | - "Failed to stop running instance of unity8: %s" % e.output, |
128 | - ) |
129 | - raise |
130 | - |
131 | + stop_job('unity8') |
132 | + |
133 | + pid = start_job('unity8', *args) |
134 | + return _get_unity_proxy_object(pid) |
135 | + |
136 | + |
137 | +def start_job(name, *args): |
138 | + """Start a job. |
139 | + |
140 | + :param str name: The name of the job. |
141 | + :param args: The arguments to be used when starting the job. |
142 | + :return: The process id of the started job. |
143 | + :raises CalledProcessError: if the job failed to start. |
144 | + |
145 | + """ |
146 | + logger.info('Starting job {} with arguments {}.'.format(name, args)) |
147 | + command = ['/sbin/initctl', 'start', name] + list(args) |
148 | try: |
149 | - command = ['/sbin/initctl', 'start', 'unity8'] + list(args) |
150 | output = subprocess.check_output( |
151 | command, |
152 | stderr=subprocess.STDOUT, |
153 | universal_newlines=True, |
154 | ) |
155 | logger.info(output) |
156 | - pid = _get_unity_pid() |
157 | + pid = get_job_pid(name) |
158 | except subprocess.CalledProcessError as e: |
159 | - e.args += ("Failed to start unity8: %s" % e.output,) |
160 | + e.args += ('Failed to start {}: {}.'.format(name, e.output),) |
161 | raise |
162 | else: |
163 | - return _get_unity_proxy_object(pid) |
164 | - |
165 | - |
166 | -def _get_unity_status(): |
167 | + return pid |
168 | + |
169 | + |
170 | +def get_job_pid(name): |
171 | + """Return the process id of a running job. |
172 | + |
173 | + :param str name: The name of the job. |
174 | + :raises JobError: if the job is not running. |
175 | + |
176 | + """ |
177 | + status = get_job_status(name) |
178 | + if "start/" not in status: |
179 | + raise JobError('{} is not in the running state.'.format(name)) |
180 | + return int(status.split()[-1]) |
181 | + |
182 | + |
183 | +def get_job_status(name): |
184 | + """Return the status of a job. |
185 | + |
186 | + :param str name: The name of the job. |
187 | + :raises JobError: if it's not possible to get the status of the job. |
188 | + |
189 | + """ |
190 | try: |
191 | return subprocess.check_output([ |
192 | '/sbin/initctl', |
193 | 'status', |
194 | - 'unity8' |
195 | + name |
196 | ], universal_newlines=True) |
197 | + except subprocess.CalledProcessError as error: |
198 | + raise JobError( |
199 | + "Unable to get {}'s status: {}".format(name, error) |
200 | + ) |
201 | + |
202 | + |
203 | +def stop_job(name): |
204 | + """Stop a job. |
205 | + |
206 | + :param str name: The name of the job. |
207 | + :raises CalledProcessError: if the job failed to stop. |
208 | + |
209 | + """ |
210 | + logger.info('Stoping job {}.'.format(name)) |
211 | + command = ['/sbin/initctl', 'stop', name] |
212 | + try: |
213 | + output = subprocess.check_output( |
214 | + command, |
215 | + stderr=subprocess.STDOUT, |
216 | + universal_newlines=True, |
217 | + ) |
218 | + logger.info(output) |
219 | except subprocess.CalledProcessError as e: |
220 | - raise CannotAccessUnity("Unable to get unity's status: %s" % str(e)) |
221 | + e.args += ('Failed to stop {}: {}.'.format(name, e.output),) |
222 | + raise |
223 | + |
224 | + |
225 | +def is_job_running(name): |
226 | + """Return True if the job is running. Otherwise, False. |
227 | + |
228 | + :param str name: The name of the job. |
229 | + :raises JobError: if it's not possible to get the status of the job. |
230 | + |
231 | + """ |
232 | + return 'start/' in get_job_status(name) |
233 | + |
234 | + |
235 | +def _get_unity_status(): |
236 | + try: |
237 | + return get_job_status('unity8') |
238 | + except JobError as error: |
239 | + raise CannotAccessUnity(str(error)) |
240 | |
241 | |
242 | def _get_unity_pid(): |
243 | - status = _get_unity_status() |
244 | - if "start/" not in status: |
245 | - raise CannotAccessUnity("Unity is not in the running state.") |
246 | - return int(status.split()[-1]) |
247 | + try: |
248 | + return get_job_pid('unity8') |
249 | + except JobError as error: |
250 | + raise CannotAccessUnity(str(error)) |
251 | |
252 | |
253 | def _get_unity_proxy_object(pid): |
254 | |
255 | === modified file 'tests/autopilot/unity8/shell/emulators/dash.py' |
256 | --- tests/autopilot/unity8/shell/emulators/dash.py 2014-07-28 17:06:57 +0000 |
257 | +++ tests/autopilot/unity8/shell/emulators/dash.py 2014-08-04 16:55:24 +0000 |
258 | @@ -31,6 +31,17 @@ |
259 | logger = logging.getLogger(__name__) |
260 | |
261 | |
262 | +class DashApp(object): |
263 | + |
264 | + """Autopilot helper for the Dash app.""" |
265 | + |
266 | + def __init__(self, app_proxy): |
267 | + self.app_proxy = app_proxy |
268 | + self.main_view = self.app_proxy.select_single( |
269 | + toolkit_emulators.MainView) |
270 | + self.dash = self.main_view.select_single(Dash) |
271 | + |
272 | + |
273 | class Dash(emulators.UnityEmulatorBase): |
274 | """An emulator that understands the Dash.""" |
275 | |
276 | @@ -109,20 +120,22 @@ |
277 | @autopilot_logging.log_action(logger.info) |
278 | def _scroll_to_left_scope(self): |
279 | original_index = self.dash_content_list.currentIndex |
280 | - dashContent = self.select_single(objectName="dashContent") |
281 | - start_x = dashContent.width / 3 |
282 | - stop_x = dashContent.width / 3 * 2 |
283 | - start_y = stop_y = dashContent.globalRect.y + 1 |
284 | + dash_content = self.select_single(objectName="dashContent") |
285 | + x, y, width, height = dash_content.globalRect |
286 | + start_x = x + width / 3 |
287 | + stop_x = x + width / 3 * 2 |
288 | + start_y = stop_y = y + 1 |
289 | self.pointing_device.drag(start_x, start_y, stop_x, stop_y) |
290 | self.dash_content_list.currentIndex.wait_for(original_index - 1) |
291 | |
292 | @autopilot_logging.log_action(logger.info) |
293 | def _scroll_to_right_scope(self): |
294 | original_index = self.dash_content_list.currentIndex |
295 | - dashContent = self.select_single(objectName="dashContent") |
296 | - start_x = dashContent.width / 3 * 2 |
297 | - stop_x = dashContent.width / 3 |
298 | - start_y = stop_y = dashContent.globalRect.y + 1 |
299 | + dash_content = self.select_single(objectName="dashContent") |
300 | + x, y, width, height = dash_content.globalRect |
301 | + start_x = x + width / 3 * 2 |
302 | + stop_x = x + width / 3 |
303 | + start_y = stop_y = y + 1 |
304 | self.pointing_device.drag(start_x, start_y, stop_x, stop_y) |
305 | self.dash_content_list.currentIndex.wait_for(original_index + 1) |
306 | |
307 | |
308 | === modified file 'tests/autopilot/unity8/shell/emulators/greeter.py' |
309 | --- tests/autopilot/unity8/shell/emulators/greeter.py 2014-01-09 15:53:05 +0000 |
310 | +++ tests/autopilot/unity8/shell/emulators/greeter.py 2014-08-04 16:55:24 +0000 |
311 | @@ -17,6 +17,8 @@ |
312 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
313 | # |
314 | |
315 | +import ubuntuuitoolkit |
316 | + |
317 | from unity8.shell.emulators import UnityEmulatorBase |
318 | |
319 | |
320 | @@ -37,4 +39,5 @@ |
321 | self.created.wait_for(False) |
322 | |
323 | def get_prompt(self): |
324 | - return self.select_single("TextField", objectName="passwordInput") |
325 | + return self.select_single( |
326 | + ubuntuuitoolkit.TextField, objectName='passwordInput') |
327 | |
328 | === modified file 'tests/autopilot/unity8/shell/emulators/main_window.py' |
329 | --- tests/autopilot/unity8/shell/emulators/main_window.py 2014-07-09 13:16:20 +0000 |
330 | +++ tests/autopilot/unity8/shell/emulators/main_window.py 2014-08-04 16:55:24 +0000 |
331 | @@ -25,7 +25,6 @@ |
332 | from unity8.shell import emulators |
333 | from unity8.shell.emulators.greeter import Greeter |
334 | from unity8.shell.emulators.hud import Hud |
335 | -from unity8.shell.emulators.dash import Dash |
336 | from unity8.shell.emulators.launcher import Launcher |
337 | |
338 | logger = logging.getLogger(__name__) |
339 | @@ -61,9 +60,6 @@ |
340 | def get_hud_edge_drag_area(self): |
341 | return self.select_single(objectName="hudDragArea") |
342 | |
343 | - def get_dash(self): |
344 | - return self.select_single(Dash) |
345 | - |
346 | def get_bottombar(self): |
347 | return self.select_single("Bottombar") |
348 | |
349 | @@ -76,12 +72,6 @@ |
350 | objectName="pinPadLoader" |
351 | ) |
352 | |
353 | - def get_pinPadButton(self, buttonId): |
354 | - return self.select_single( |
355 | - "PinPadButton", |
356 | - objectName="pinPadButton%i" % buttonId |
357 | - ) |
358 | - |
359 | def get_lockscreen(self): |
360 | return self.select_single("Lockscreen") |
361 | |
362 | @@ -117,20 +107,44 @@ |
363 | @autopilot_logging.log_action(logger.info) |
364 | def show_dash_swiping(self): |
365 | """Show the dash swiping from the left.""" |
366 | - width = self.width |
367 | - height = self.height |
368 | - start_x = 0 |
369 | - start_y = height // 2 |
370 | - end_x = width |
371 | - end_y = start_y |
372 | + x, y, width, height = self._get_shell().globalRect |
373 | + start_x = x |
374 | + end_x = x + width |
375 | + start_y = end_y = y + height // 2 |
376 | |
377 | self.pointing_device.drag(start_x, start_y, end_x, end_y) |
378 | - return self.get_dash() |
379 | + |
380 | + def _get_shell(self): |
381 | + return self.select_single('Shell') |
382 | |
383 | def get_current_focused_app_id(self): |
384 | """Return the id of the focused application.""" |
385 | - return self.select_single('Shell').focusedApplicationId |
386 | + return self._get_shell().focusedApplicationId |
387 | |
388 | @autopilot_logging.log_action(logger.info) |
389 | - def search(self, query): |
390 | - self.get_dash().enter_search_query(query) |
391 | + def enter_pin_code(self, code): |
392 | + """Enter code 'code' into the single-pin lightdm pincode entry screen. |
393 | + |
394 | + :param code: must be a string of numeric characters. |
395 | + :raises: TypeError if code is not a string. |
396 | + :raises: ValueError if code contains non-numeric characters. |
397 | + |
398 | + """ |
399 | + if not isinstance(code, str): |
400 | + raise TypeError( |
401 | + "'code' parameter must be a string, not %r." |
402 | + % type(code) |
403 | + ) |
404 | + for num in code: |
405 | + if not num.isdigit(): |
406 | + raise ValueError( |
407 | + "'code' parameter contains non-numeric characters." |
408 | + ) |
409 | + self.pointing_device.click_object( |
410 | + self._get_pinpad_button(int(num))) |
411 | + |
412 | + def _get_pinpad_button(self, button_id): |
413 | + return self.select_single( |
414 | + 'PinPadButton', |
415 | + objectName='pinPadButton{}'.format(button_id) |
416 | + ) |
417 | |
418 | === modified file 'tests/autopilot/unity8/shell/tests/__init__.py' |
419 | --- tests/autopilot/unity8/shell/tests/__init__.py 2014-06-27 13:13:04 +0000 |
420 | +++ tests/autopilot/unity8/shell/tests/__init__.py 2014-08-04 16:55:24 +0000 |
421 | @@ -24,6 +24,7 @@ |
422 | except ImportError: |
423 | Gio = None |
424 | |
425 | +from autopilot import introspection |
426 | from autopilot.platform import model |
427 | from autopilot.testcase import AutopilotTestCase |
428 | from autopilot.matchers import Eventually |
429 | @@ -34,6 +35,10 @@ |
430 | import subprocess |
431 | import sys |
432 | from testtools.matchers import Equals |
433 | +from ubuntuuitoolkit import ( |
434 | + fixture_setup as toolkit_fixtures, |
435 | + ubuntu_scenarios |
436 | +) |
437 | |
438 | from unity8 import ( |
439 | get_lib_path, |
440 | @@ -42,9 +47,15 @@ |
441 | get_default_extra_mock_libraries, |
442 | get_data_dirs |
443 | ) |
444 | -from unity8.process_helpers import restart_unity_with_testability |
445 | -from unity8.shell.emulators import main_window as main_window_emulator |
446 | -from unity8.shell.emulators.dash import Dash |
447 | +from unity8 import ( |
448 | + fixture_setup, |
449 | + process_helpers |
450 | +) |
451 | +from unity8.shell import emulators |
452 | +from unity8.shell.emulators import ( |
453 | + dash as dash_helpers, |
454 | + main_window as main_window_emulator, |
455 | +) |
456 | |
457 | |
458 | logger = logging.getLogger(__name__) |
459 | @@ -78,6 +89,26 @@ |
460 | return [native] |
461 | |
462 | |
463 | +def is_unity7_running(): |
464 | + """Return True if Unity7 is running. Otherwise, return False.""" |
465 | + return ( |
466 | + Gio is not None and |
467 | + UNITYSHELL_GSETTINGS_SCHEMA in |
468 | + Gio.Settings.list_relocatable_schemas() |
469 | + ) |
470 | + |
471 | + |
472 | +def get_qml_import_path_with_mock(): |
473 | + """Return the QML2_IMPORT_PATH value with the mock path prepended.""" |
474 | + qml_import_path = [get_mocks_library_path()] |
475 | + if os.getenv('QML2_IMPORT_PATH') is not None: |
476 | + qml_import_path.append(os.getenv('QML2_IMPORT_PATH')) |
477 | + |
478 | + qml_import_path = ':'.join(qml_import_path) |
479 | + logger.info("New QML2 import path: %s", qml_import_path) |
480 | + return qml_import_path |
481 | + |
482 | + |
483 | class UnityTestCase(AutopilotTestCase): |
484 | |
485 | """A test case base class for the Unity shell tests.""" |
486 | @@ -118,23 +149,8 @@ |
487 | |
488 | def setUp(self): |
489 | super(UnityTestCase, self).setUp() |
490 | - if (Gio is not None and |
491 | - UNITYSHELL_GSETTINGS_SCHEMA in |
492 | - Gio.Settings.list_relocatable_schemas()): |
493 | - |
494 | - # Hide Unity launcher |
495 | - self._unityshell_schema = Gio.Settings.new_with_path( |
496 | - UNITYSHELL_GSETTINGS_SCHEMA, |
497 | - UNITYSHELL_GSETTINGS_PATH, |
498 | - ) |
499 | - self._launcher_hide_mode = self._unityshell_schema.get_int( |
500 | - UNITYSHELL_LAUNCHER_KEY, |
501 | - ) |
502 | - self._unityshell_schema.set_int( |
503 | - UNITYSHELL_LAUNCHER_KEY, |
504 | - UNITYSHELL_LAUNCHER_MODE, |
505 | - ) |
506 | - self.addCleanup(self._reset_launcher) |
507 | + if is_unity7_running(): |
508 | + self.useFixture(toolkit_fixtures.HideUnity7Launcher()) |
509 | |
510 | self._proxy = None |
511 | self._lightdm_mock_type = None |
512 | @@ -151,13 +167,6 @@ |
513 | self.touch = Touch.create() |
514 | self._setup_display_details() |
515 | |
516 | - def _reset_launcher(self): |
517 | - """Reset Unity launcher hide mode""" |
518 | - self._unityshell_schema.set_int( |
519 | - UNITYSHELL_LAUNCHER_KEY, |
520 | - self._launcher_hide_mode, |
521 | - ) |
522 | - |
523 | def _setup_display_details(self): |
524 | scale_divisor = self._determine_geometry() |
525 | self._setup_grid_size(scale_divisor) |
526 | @@ -263,7 +272,9 @@ |
527 | self.patch_lightdm_mock() |
528 | |
529 | if self._qml_mock_enabled: |
530 | - self._setup_extra_mock_environment_patch() |
531 | + self._environment['QML2_IMPORT_PATH'] = ( |
532 | + get_qml_import_path_with_mock() |
533 | + ) |
534 | |
535 | if self._data_dirs_mock_enabled: |
536 | self._patch_data_dirs() |
537 | @@ -292,9 +303,14 @@ |
538 | |
539 | # Ensure that the dash is visible before we return: |
540 | logger.debug("Unity started, waiting for it to be ready.") |
541 | - self.assertUnityReady() |
542 | + self.wait_for_unity() |
543 | logger.debug("Unity loaded and ready.") |
544 | |
545 | + if model() == 'Desktop': |
546 | + # On desktop, close the dash because it's opened in a separate |
547 | + # window and it gets in the way. |
548 | + process_helpers.stop_job('unity8-dash') |
549 | + |
550 | return app_proxy |
551 | |
552 | def _launch_unity_with_upstart(self, binary_path, args): |
553 | @@ -308,7 +324,7 @@ |
554 | |
555 | self.addCleanup(self._cleanup_launching_upstart_unity) |
556 | |
557 | - return restart_unity_with_testability(*all_args) |
558 | + return process_helpers.restart_unity_with_testability(*all_args) |
559 | |
560 | def _cleanup_launching_upstart_unity(self): |
561 | logger.info("Stopping unity") |
562 | @@ -353,15 +369,6 @@ |
563 | ) |
564 | return lightdm_mock_path |
565 | |
566 | - def _setup_extra_mock_environment_patch(self): |
567 | - qml_import_path = [get_mocks_library_path()] |
568 | - if os.getenv('QML2_IMPORT_PATH') is not None: |
569 | - qml_import_path.append(os.getenv('QML2_IMPORT_PATH')) |
570 | - |
571 | - qml_import_path = ':'.join(qml_import_path) |
572 | - logger.info("New QML2 import path: %s", qml_import_path) |
573 | - self._environment['QML2_IMPORT_PATH'] = qml_import_path |
574 | - |
575 | def _set_proxy(self, proxy): |
576 | """Keep a copy of the proxy object, so we can use it to get common |
577 | parts of the shell later on. |
578 | @@ -373,10 +380,80 @@ |
579 | def _clear_proxy(self): |
580 | self._proxy = None |
581 | |
582 | - def assertUnityReady(self): |
583 | - dash = self.get_dash() |
584 | - home_scope = dash.get_scope('clickscope') |
585 | - |
586 | + def wait_for_unity(self): |
587 | + greeter_content_loader = self.main_window.wait_select_single( |
588 | + objectName='greeterContentLoader') |
589 | + greeter_content_loader.progress.wait_for(1) |
590 | + |
591 | + def get_dash(self): |
592 | + pid = process_helpers.get_job_pid('unity8-dash') |
593 | + dash_proxy = introspection.get_proxy_object_for_existing_process( |
594 | + pid=pid, |
595 | + emulator_base=emulators.UnityEmulatorBase, |
596 | + ) |
597 | + dash_app = dash_helpers.DashApp(dash_proxy) |
598 | + return dash_app.dash |
599 | + |
600 | + @property |
601 | + def main_window(self): |
602 | + return self._proxy.select_single(main_window_emulator.QQuickView) |
603 | + |
604 | + |
605 | +class DashBaseTestCase(AutopilotTestCase): |
606 | + |
607 | + scenarios = ubuntu_scenarios.get_device_simulation_scenarios() |
608 | + qml_mock_enabled = True |
609 | + environment = {} |
610 | + |
611 | + def setUp(self): |
612 | + super(DashBaseTestCase, self).setUp() |
613 | + |
614 | + if is_unity7_running(): |
615 | + self.useFixture(toolkit_fixtures.HideUnity7Launcher()) |
616 | + |
617 | + if model() != 'Desktop': |
618 | + # On the phone, we need unity to be running and unlocked. |
619 | + self.addCleanup(process_helpers.stop_job, 'unity8') |
620 | + process_helpers.restart_unity_with_testability() |
621 | + process_helpers.unlock_unity() |
622 | + |
623 | + self.ensure_dash_not_running() |
624 | + |
625 | + if self.qml_mock_enabled: |
626 | + self.environment['QML2_IMPORT_PATH'] = ( |
627 | + get_qml_import_path_with_mock() |
628 | + ) |
629 | + |
630 | + if self.should_simulate_device(): |
631 | + # This sets the grid units, so it should be called before launching |
632 | + # the app. |
633 | + self.simulate_device() |
634 | + |
635 | + binary_path = get_binary_path('unity8-dash') |
636 | + dash_proxy = self.launch_dash(binary_path, self.environment) |
637 | + |
638 | + if self.should_simulate_device(): |
639 | + # XXX Currently we have no way to launch the application with a |
640 | + # specific size, so we must resize it after it's launched. |
641 | + # --elopio - 2014-06-25 |
642 | + self.resize_window() |
643 | + |
644 | + self.dash_app = dash_helpers.DashApp(dash_proxy) |
645 | + self.dash = self.dash_app.dash |
646 | + self.wait_for_dash() |
647 | + |
648 | + def ensure_dash_not_running(self): |
649 | + if process_helpers.is_job_running('unity8-dash'): |
650 | + process_helpers.stop_job('unity8-dash') |
651 | + |
652 | + def launch_dash(self, binary_path, variables): |
653 | + launch_dash_app_fixture = fixture_setup.LaunchDashApp( |
654 | + binary_path, variables) |
655 | + self.useFixture(launch_dash_app_fixture) |
656 | + return launch_dash_app_fixture.application_proxy |
657 | + |
658 | + def wait_for_dash(self): |
659 | + home_scope = self.dash.get_scope('clickscope') |
660 | # FIXME! There is a huge timeout here for when we're doing CI on |
661 | # VMs. See lp:1203715 |
662 | self.assertThat( |
663 | @@ -385,10 +462,26 @@ |
664 | ) |
665 | self.assertThat(home_scope.isCurrent, Eventually(Equals(True))) |
666 | |
667 | - def get_dash(self): |
668 | - dash = self._proxy.wait_select_single(Dash) |
669 | - return dash |
670 | - |
671 | - @property |
672 | - def main_window(self): |
673 | - return self._proxy.select_single(main_window_emulator.QQuickView) |
674 | + def should_simulate_device(self): |
675 | + return (hasattr(self, 'app_width') and hasattr(self, 'app_height') and |
676 | + hasattr(self, 'grid_unit_px')) |
677 | + |
678 | + def simulate_device(self): |
679 | + simulate_device_fixture = self.useFixture( |
680 | + toolkit_fixtures.SimulateDevice( |
681 | + self.app_width, self.app_height, self.grid_unit_px)) |
682 | + self.app_width = simulate_device_fixture.app_width |
683 | + self.app_height = simulate_device_fixture.app_height |
684 | + |
685 | + def resize_window(self): |
686 | + application = self.process_manager.get_running_applications()[0] |
687 | + window = application.get_windows()[0] |
688 | + window.resize(self.app_width, self.app_height) |
689 | + |
690 | + def get_window_size(): |
691 | + _, _, window_width, window_height = window.geometry |
692 | + return window_width, window_height |
693 | + |
694 | + self.assertThat( |
695 | + get_window_size, |
696 | + Eventually(Equals((self.app_width, self.app_height)))) |
697 | |
698 | === modified file 'tests/autopilot/unity8/shell/tests/test_emulators.py' |
699 | --- tests/autopilot/unity8/shell/tests/test_emulators.py 2014-07-25 11:49:28 +0000 |
700 | +++ tests/autopilot/unity8/shell/tests/test_emulators.py 2014-08-04 16:55:24 +0000 |
701 | @@ -47,25 +47,14 @@ |
702 | unity_proxy = self.launch_unity() |
703 | process_helpers.unlock_unity(unity_proxy) |
704 | |
705 | + |
706 | +class DashEmulatorTestCase(tests.DashBaseTestCase): |
707 | + |
708 | def test_search(self): |
709 | - self.main_window.search('Test') |
710 | - text_field = self.main_window.get_dash()._get_search_text_field() |
711 | + self.dash.enter_search_query('Test') |
712 | + text_field = self.dash._get_search_text_field() |
713 | self.assertEqual(text_field.text, 'Test') |
714 | |
715 | - |
716 | -class DashBaseTestCase(tests.UnityTestCase): |
717 | - |
718 | - scenarios = tests._get_device_emulation_scenarios() |
719 | - |
720 | - def setUp(self): |
721 | - super(DashBaseTestCase, self).setUp() |
722 | - unity_proxy = self.launch_unity() |
723 | - process_helpers.unlock_unity(unity_proxy) |
724 | - self.dash = self.main_window.get_dash() |
725 | - |
726 | - |
727 | -class DashEmulatorTestCase(DashBaseTestCase): |
728 | - |
729 | def test_open_unexisting_scope(self): |
730 | scope_name = 'unexisting' |
731 | with mock.patch.object(self.dash, 'pointing_device') as mock_pointer: |
732 | @@ -145,7 +134,7 @@ |
733 | self.assertIsInstance(scope, dash_emulators.GenericScopeView) |
734 | |
735 | |
736 | -class GenericScopeViewEmulatorTestCase(DashBaseTestCase): |
737 | +class GenericScopeViewEmulatorTestCase(tests.DashBaseTestCase): |
738 | |
739 | def setUp(self): |
740 | # Set up the fake scopes before launching unity. |
741 | @@ -159,7 +148,7 @@ |
742 | self.assertTrue(preview.isCurrent) |
743 | |
744 | |
745 | -class DashAppsEmulatorTestCase(DashBaseTestCase): |
746 | +class DashAppsEmulatorTestCase(tests.DashBaseTestCase): |
747 | |
748 | available_applications = [ |
749 | 'Title.2.0', 'Title.2.1', 'Title.2.2', 'Title.2.3', 'Title.2.4', |
750 | |
751 | === modified file 'tests/autopilot/unity8/shell/tests/test_lock_screen.py' |
752 | --- tests/autopilot/unity8/shell/tests/test_lock_screen.py 2014-06-18 01:52:15 +0000 |
753 | +++ tests/autopilot/unity8/shell/tests/test_lock_screen.py 2014-08-04 16:55:24 +0000 |
754 | @@ -51,7 +51,7 @@ |
755 | if greeter.narrowMode: |
756 | unlock_unity(unity_proxy) |
757 | lockscreen = self._wait_for_lockscreen() |
758 | - self._enter_pincode("1234") |
759 | + self.main_window.enter_pin_code("1234") |
760 | self.assertThat(lockscreen.shown, Eventually(Equals(False))) |
761 | else: |
762 | self._enter_prompt_passphrase("1234") |
763 | @@ -81,7 +81,7 @@ |
764 | if greeter.narrowMode: |
765 | unlock_unity(unity_proxy) |
766 | lockscreen = self._wait_for_lockscreen() |
767 | - self._enter_pincode("4321") |
768 | + self.main_window.enter_pin_code("4321") |
769 | pinentryField = self.main_window.get_pinentryField() |
770 | self.assertThat(pinentryField.text, Eventually(Equals(""))) |
771 | self.assertThat(lockscreen.shown, Eventually(Equals(True))) |
772 | @@ -118,28 +118,6 @@ |
773 | self.assertThat(lockscreen.shown, Eventually(Equals(True))) |
774 | return lockscreen |
775 | |
776 | - def _enter_pincode(self, code): |
777 | - """Enter code 'code' into the single-pin lightdm pincode entry |
778 | - screen. |
779 | - |
780 | - :param code: must be a string of numeric characters. |
781 | - :raises: TypeError if code is not a string. |
782 | - :raises: ValueError if code contains non-numeric characters. |
783 | - |
784 | - """ |
785 | - |
786 | - if not isinstance(code, basestring): |
787 | - raise TypeError( |
788 | - "'code' parameter must be a string, not %r." |
789 | - % type(code) |
790 | - ) |
791 | - for num in code: |
792 | - if not num.isdigit(): |
793 | - raise ValueError( |
794 | - "'code' parameter contains non-numeric characters." |
795 | - ) |
796 | - self.touch.tap_object(self.main_window.get_pinPadButton(int(num))) |
797 | - |
798 | def _enter_pin_passphrase(self, passphrase): |
799 | """Enter the password specified in 'passphrase' into the password entry |
800 | field of the pin lock screen. |
801 | @@ -148,18 +126,15 @@ |
802 | :raises: TypeError if passphrase is not a string. |
803 | |
804 | """ |
805 | - if not isinstance(passphrase, basestring): |
806 | + if not isinstance(passphrase, str): |
807 | raise TypeError( |
808 | "'passphrase' parameter must be a string, not %r." |
809 | % type(passphrase) |
810 | ) |
811 | |
812 | - pinentryField = self.main_window.get_pinentryField() |
813 | - self.touch.tap_object(pinentryField) |
814 | - self.assertThat(pinentryField.activeFocus, Eventually(Equals(True))) |
815 | - for character in passphrase: |
816 | - self._type_character(character, pinentryField) |
817 | - logger.debug("Typed passphrase: %s", pinentryField.text) |
818 | + pin_entry_field = self.main_window.get_pinentryField() |
819 | + pin_entry_field.write(passphrase) |
820 | + logger.debug("Typed passphrase: %s", pin_entry_field.text) |
821 | self.keyboard.type("\n") |
822 | |
823 | def _enter_prompt_passphrase(self, passphrase): |
824 | @@ -177,21 +152,6 @@ |
825 | ) |
826 | |
827 | prompt = self.main_window.get_greeter().get_prompt() |
828 | - self.touch.tap_object(prompt) |
829 | - self.assertThat(prompt.activeFocus, Eventually(Equals(True))) |
830 | - for character in passphrase: |
831 | - self._type_character(character, prompt) |
832 | + prompt.write(passphrase) |
833 | logger.debug("Typed passphrase: %s", prompt.text) |
834 | self.keyboard.type("\n") |
835 | - |
836 | - def _type_character(self, character, prompt, retries=5): |
837 | - current_text = prompt.text |
838 | - self.keyboard.type(character) |
839 | - try: |
840 | - self.assertThat( |
841 | - prompt.text, Eventually(Equals(current_text + character))) |
842 | - except AssertionError: |
843 | - if retries > 0: |
844 | - self._type_character(character, prompt, retries-1) |
845 | - else: |
846 | - raise |
847 | |
848 | === modified file 'tests/autopilot/unity8/shell/tests/test_notifications.py' |
849 | --- tests/autopilot/unity8/shell/tests/test_notifications.py 2014-07-18 15:13:35 +0000 |
850 | +++ tests/autopilot/unity8/shell/tests/test_notifications.py 2014-08-04 16:55:24 +0000 |
851 | @@ -154,7 +154,7 @@ |
852 | 'Notification', objectName='notification1') |
853 | notification = get_notification() |
854 | |
855 | - self.touch.tap_object( |
856 | + notification.pointing_device.click_object( |
857 | notification.select_single(objectName="interactiveArea") |
858 | ) |
859 | |
860 | @@ -201,11 +201,14 @@ |
861 | 'Notification', objectName='notification1') |
862 | notification = get_notification() |
863 | self._assert_notification(notification, summary, body, True, True, 1.0) |
864 | - initial_height = notification.height |
865 | - self.touch.tap_object(notification.select_single(objectName="combobutton_dropdown")) |
866 | - self.assertThat(notification.select_single(objectName="button2").expanded, Eventually(Equals(True))) |
867 | + notification.pointing_device.click_object( |
868 | + notification.select_single(objectName="combobutton_dropdown")) |
869 | + self.assertThat( |
870 | + notification.select_single(objectName="button2").expanded, |
871 | + Eventually(Equals(True))) |
872 | time.sleep(2) |
873 | - self.touch.tap_object(notification.select_single(objectName="button4")) |
874 | + notification.pointing_device.click_object( |
875 | + notification.select_single(objectName="button4")) |
876 | self.assert_notification_action_id_was_called("action_decline_4") |
877 | |
878 | def test_modal_sd_without_greeter(self): |
879 | @@ -249,7 +252,8 @@ |
880 | notification = get_notification() |
881 | self._assert_notification( |
882 | notification, summary, body, True, False, 1.0) |
883 | - self.touch.tap_object(notification.select_single(objectName="button0")) |
884 | + notification.pointing_device.click_object( |
885 | + notification.select_single(objectName="button0")) |
886 | self.assert_notification_action_id_was_called("action_accept") |
887 | |
888 | def test_modal_sd_with_greeter(self): |
889 | @@ -291,7 +295,8 @@ |
890 | notification = get_notification() |
891 | self._assert_notification( |
892 | notification, summary, body, True, False, 1.0) |
893 | - self.touch.tap_object(notification.select_single(objectName="button0")) |
894 | + notification.pointing_device.click_object( |
895 | + notification.select_single(objectName="button0")) |
896 | self.assert_notification_action_id_was_called("action_accept") |
897 | |
898 | def _create_interactive_notification( |
899 | |
900 | === modified file 'tests/autopilot/unity8/shell/tests/test_upstart.py' |
901 | --- tests/autopilot/unity8/shell/tests/test_upstart.py 2014-07-31 10:11:27 +0000 |
902 | +++ tests/autopilot/unity8/shell/tests/test_upstart.py 2014-08-04 16:55:24 +0000 |
903 | @@ -119,7 +119,7 @@ |
904 | self._set_proxy() |
905 | |
906 | logger.debug("Unity started, waiting for it to be ready.") |
907 | - self.assertUnityReady() |
908 | + self.wait_for_unity() |
909 | logger.debug("Unity loaded and ready.") |
910 | |
911 | def test_expect_sigstop(self): |
912 | @@ -137,5 +137,5 @@ |
913 | |
914 | logger.debug("Unity started, waiting for it to be ready.") |
915 | self._set_proxy() |
916 | - self.assertUnityReady() |
917 | + self.wait_for_unity() |
918 | logger.debug("Unity loaded and ready.") |
Not ready yet. Just proposing to start getting jenkins results earlier.