Merge ~xnox/autopilot:master into autopilot:master
- Git
- lp:~xnox/autopilot
- master
- Merge into master
Proposed by
Dimitri John Ledkov
Status: | Needs review |
---|---|
Proposed branch: | ~xnox/autopilot:master |
Merge into: | autopilot:master |
Diff against target: |
1211 lines (+15/-1051) 8 files modified
autopilot/application/__init__.py (+0/-4) autopilot/application/_launcher.py (+1/-262) autopilot/testcase.py (+0/-74) autopilot/tests/functional/test_ap_apps.py (+0/-7) autopilot/tests/functional/test_application_launcher.py (+0/-50) autopilot/tests/unit/test_application_launcher.py (+0/-651) debian/changelog (+13/-0) debian/control (+1/-3) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Paride Legovini | Approve | ||
Review via email: mp+385115@code.launchpad.net |
Commit message
Drop ual support, blocking groovy transitions.
Description of the change
To post a comment you must log in.
Unmerged commits
- 20f75d0... by Dimitri John Ledkov
-
Drop upstart, click, ubuntu-app-launch support. All of those things are removed from the Ubuntu Archive.
* Drop upstart, click, ubuntu-app-launch support. All of those things
are removed from the Ubuntu Archive.
* No-change rebuild to drop python3.7.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/autopilot/application/__init__.py b/autopilot/application/__init__.py | |||
2 | index 8ebda84..efaac1e 100644 | |||
3 | --- a/autopilot/application/__init__.py | |||
4 | +++ b/autopilot/application/__init__.py | |||
5 | @@ -20,15 +20,11 @@ | |||
6 | 20 | """Base package for application launching and environment management.""" | 20 | """Base package for application launching and environment management.""" |
7 | 21 | 21 | ||
8 | 22 | from autopilot.application._launcher import ( | 22 | from autopilot.application._launcher import ( |
9 | 23 | ClickApplicationLauncher, | ||
10 | 24 | get_application_launcher_wrapper, | 23 | get_application_launcher_wrapper, |
11 | 25 | NormalApplicationLauncher, | 24 | NormalApplicationLauncher, |
12 | 26 | UpstartApplicationLauncher, | ||
13 | 27 | ) | 25 | ) |
14 | 28 | 26 | ||
15 | 29 | __all__ = [ | 27 | __all__ = [ |
16 | 30 | 'ClickApplicationLauncher', | ||
17 | 31 | 'NormalApplicationLauncher', | 28 | 'NormalApplicationLauncher', |
18 | 32 | 'UpstartApplicationLauncher', | ||
19 | 33 | 'get_application_launcher_wrapper', | 29 | 'get_application_launcher_wrapper', |
20 | 34 | ] | 30 | ] |
21 | diff --git a/autopilot/application/_launcher.py b/autopilot/application/_launcher.py | |||
22 | index 8ec391f..72fef39 100644 | |||
23 | --- a/autopilot/application/_launcher.py | |||
24 | +++ b/autopilot/application/_launcher.py | |||
25 | @@ -20,12 +20,7 @@ | |||
26 | 20 | """Base module for application launchers.""" | 20 | """Base module for application launchers.""" |
27 | 21 | 21 | ||
28 | 22 | import fixtures | 22 | import fixtures |
35 | 23 | from gi import require_version | 23 | from gi.repository import GLib |
30 | 24 | try: | ||
31 | 25 | require_version('UbuntuAppLaunch', '3') | ||
32 | 26 | except ValueError: | ||
33 | 27 | require_version('UbuntuAppLaunch', '2') | ||
34 | 28 | from gi.repository import GLib, UbuntuAppLaunch | ||
36 | 29 | 24 | ||
37 | 30 | import json | 25 | import json |
38 | 31 | import logging | 26 | import logging |
39 | @@ -81,233 +76,6 @@ class ApplicationLauncher(FixtureWithDirectAddDetail): | |||
40 | 81 | raise NotImplementedError("Sub-classes must implement this method.") | 76 | raise NotImplementedError("Sub-classes must implement this method.") |
41 | 82 | 77 | ||
42 | 83 | 78 | ||
43 | 84 | class UpstartApplicationLauncher(ApplicationLauncher): | ||
44 | 85 | |||
45 | 86 | """A launcher class that launches applications with UpstartAppLaunch.""" | ||
46 | 87 | __doc__ += ApplicationLauncher.__doc__ | ||
47 | 88 | |||
48 | 89 | Timeout = object() | ||
49 | 90 | Failed = object() | ||
50 | 91 | Started = object() | ||
51 | 92 | Stopped = object() | ||
52 | 93 | |||
53 | 94 | def launch(self, app_id, app_uris=[]): | ||
54 | 95 | """Launch an application with upstart. | ||
55 | 96 | |||
56 | 97 | This method launches an application via the ``upstart-app-launch`` | ||
57 | 98 | library, on platforms that support it. | ||
58 | 99 | |||
59 | 100 | Usage is similar to NormalApplicationLauncher:: | ||
60 | 101 | |||
61 | 102 | from autopilot.application import UpstartApplicationLauncher | ||
62 | 103 | launcher = UpstartApplicationLauncher() | ||
63 | 104 | launcher.setUp() | ||
64 | 105 | app_proxy = launcher.launch('gallery-app') | ||
65 | 106 | |||
66 | 107 | :param app_id: name of the application to launch | ||
67 | 108 | :param app_uris: list of separate application uris to launch | ||
68 | 109 | :raises RuntimeError: If the specified application cannot be launched. | ||
69 | 110 | |||
70 | 111 | :returns: proxy object for the launched package application | ||
71 | 112 | |||
72 | 113 | """ | ||
73 | 114 | if isinstance(app_uris, str): | ||
74 | 115 | app_uris = [app_uris] | ||
75 | 116 | if isinstance(app_uris, bytes): | ||
76 | 117 | app_uris = [app_uris.decode()] | ||
77 | 118 | _logger.info( | ||
78 | 119 | "Attempting to launch application '%s' with URIs '%s' via " | ||
79 | 120 | "upstart-app-launch", | ||
80 | 121 | app_id, | ||
81 | 122 | ','.join(app_uris) | ||
82 | 123 | ) | ||
83 | 124 | state = {} | ||
84 | 125 | state['loop'] = self._get_glib_loop() | ||
85 | 126 | state['expected_app_id'] = app_id | ||
86 | 127 | state['message'] = '' | ||
87 | 128 | |||
88 | 129 | UbuntuAppLaunch.observer_add_app_failed(self._on_failed, state) | ||
89 | 130 | UbuntuAppLaunch.observer_add_app_started(self._on_started, state) | ||
90 | 131 | UbuntuAppLaunch.observer_add_app_focus(self._on_started, state) | ||
91 | 132 | GLib.timeout_add_seconds(10.0, self._on_timeout, state) | ||
92 | 133 | |||
93 | 134 | self._launch_app(app_id, app_uris) | ||
94 | 135 | state['loop'].run() | ||
95 | 136 | UbuntuAppLaunch.observer_delete_app_failed(self._on_failed) | ||
96 | 137 | UbuntuAppLaunch.observer_delete_app_started(self._on_started) | ||
97 | 138 | UbuntuAppLaunch.observer_delete_app_focus(self._on_started) | ||
98 | 139 | self._maybe_add_application_cleanups(state) | ||
99 | 140 | self._check_status_error( | ||
100 | 141 | state.get('status', None), | ||
101 | 142 | state.get('message', '') | ||
102 | 143 | ) | ||
103 | 144 | pid = self._get_pid_for_launched_app(app_id) | ||
104 | 145 | |||
105 | 146 | return self._get_proxy_object(pid) | ||
106 | 147 | |||
107 | 148 | def _get_proxy_object(self, pid): | ||
108 | 149 | return get_proxy_object_for_existing_process( | ||
109 | 150 | dbus_bus=self.dbus_bus, | ||
110 | 151 | emulator_base=self.proxy_base, | ||
111 | 152 | pid=pid | ||
112 | 153 | ) | ||
113 | 154 | |||
114 | 155 | @staticmethod | ||
115 | 156 | def _on_failed(launched_app_id, failure_type, state): | ||
116 | 157 | if launched_app_id == state['expected_app_id']: | ||
117 | 158 | if failure_type == UbuntuAppLaunch.AppFailed.CRASH: | ||
118 | 159 | state['message'] = 'Application crashed.' | ||
119 | 160 | elif failure_type == UbuntuAppLaunch.AppFailed.START_FAILURE: | ||
120 | 161 | state['message'] = 'Application failed to start.' | ||
121 | 162 | state['status'] = UpstartApplicationLauncher.Failed | ||
122 | 163 | state['loop'].quit() | ||
123 | 164 | |||
124 | 165 | @staticmethod | ||
125 | 166 | def _on_started(launched_app_id, state): | ||
126 | 167 | if launched_app_id == state['expected_app_id']: | ||
127 | 168 | state['status'] = UpstartApplicationLauncher.Started | ||
128 | 169 | state['loop'].quit() | ||
129 | 170 | |||
130 | 171 | @staticmethod | ||
131 | 172 | def _on_stopped(stopped_app_id, state): | ||
132 | 173 | if stopped_app_id == state['expected_app_id']: | ||
133 | 174 | state['status'] = UpstartApplicationLauncher.Stopped | ||
134 | 175 | state['loop'].quit() | ||
135 | 176 | |||
136 | 177 | @staticmethod | ||
137 | 178 | def _on_timeout(state): | ||
138 | 179 | state['status'] = UpstartApplicationLauncher.Timeout | ||
139 | 180 | state['loop'].quit() | ||
140 | 181 | |||
141 | 182 | def _maybe_add_application_cleanups(self, state): | ||
142 | 183 | if state.get('status', None) == UpstartApplicationLauncher.Started: | ||
143 | 184 | app_id = state['expected_app_id'] | ||
144 | 185 | self.addCleanup(self._stop_application, app_id) | ||
145 | 186 | self.addCleanup(self._attach_application_log, app_id) | ||
146 | 187 | |||
147 | 188 | @staticmethod | ||
148 | 189 | def _get_user_unit_match(app_id): | ||
149 | 190 | return 'ubuntu-app-launch-*-%s-*.service' % app_id | ||
150 | 191 | |||
151 | 192 | def _attach_application_log(self, app_id): | ||
152 | 193 | j = journal.Reader() | ||
153 | 194 | j.log_level(journal.LOG_INFO) | ||
154 | 195 | j.add_match(_SYSTEMD_USER_UNIT=self._get_user_unit_match(app_id)) | ||
155 | 196 | log_data = '' | ||
156 | 197 | for i in j: | ||
157 | 198 | log_data += str(i) + '\n' | ||
158 | 199 | if len(log_data) > 0: | ||
159 | 200 | self.caseAddDetail('Application Log (%s)' % app_id, | ||
160 | 201 | safe_text_content(log_data)) | ||
161 | 202 | |||
162 | 203 | def _stop_application(self, app_id): | ||
163 | 204 | state = {} | ||
164 | 205 | state['loop'] = self._get_glib_loop() | ||
165 | 206 | state['expected_app_id'] = app_id | ||
166 | 207 | |||
167 | 208 | UbuntuAppLaunch.observer_add_app_stop(self._on_stopped, state) | ||
168 | 209 | GLib.timeout_add_seconds(10.0, self._on_timeout, state) | ||
169 | 210 | |||
170 | 211 | UbuntuAppLaunch.stop_application(app_id) | ||
171 | 212 | state['loop'].run() | ||
172 | 213 | UbuntuAppLaunch.observer_delete_app_stop(self._on_stopped) | ||
173 | 214 | |||
174 | 215 | if state.get('status', None) == UpstartApplicationLauncher.Timeout: | ||
175 | 216 | _logger.error( | ||
176 | 217 | "Timed out waiting for Application with app_id '%s' to stop.", | ||
177 | 218 | app_id | ||
178 | 219 | ) | ||
179 | 220 | |||
180 | 221 | @staticmethod | ||
181 | 222 | def _get_glib_loop(): | ||
182 | 223 | return GLib.MainLoop() | ||
183 | 224 | |||
184 | 225 | @staticmethod | ||
185 | 226 | def _get_pid_for_launched_app(app_id): | ||
186 | 227 | return UbuntuAppLaunch.get_primary_pid(app_id) | ||
187 | 228 | |||
188 | 229 | @staticmethod | ||
189 | 230 | def _launch_app(app_name, app_uris): | ||
190 | 231 | UbuntuAppLaunch.start_application_test(app_name, app_uris) | ||
191 | 232 | |||
192 | 233 | @staticmethod | ||
193 | 234 | def _check_status_error(status, extra_message=''): | ||
194 | 235 | message_parts = [] | ||
195 | 236 | if status == UpstartApplicationLauncher.Timeout: | ||
196 | 237 | message_parts.append( | ||
197 | 238 | "Timed out while waiting for application to launch" | ||
198 | 239 | ) | ||
199 | 240 | elif status == UpstartApplicationLauncher.Failed: | ||
200 | 241 | message_parts.append("Application Launch Failed") | ||
201 | 242 | if message_parts and extra_message: | ||
202 | 243 | message_parts.append(extra_message) | ||
203 | 244 | if message_parts: | ||
204 | 245 | raise RuntimeError(': '.join(message_parts)) | ||
205 | 246 | |||
206 | 247 | |||
207 | 248 | class AlreadyLaunchedUpstartLauncher(UpstartApplicationLauncher): | ||
208 | 249 | """Launcher that doesn't wait for a proxy object. | ||
209 | 250 | |||
210 | 251 | This is useful when you are 're-launching' an already running application | ||
211 | 252 | and it's state has changed to suspended. | ||
212 | 253 | |||
213 | 254 | """ | ||
214 | 255 | |||
215 | 256 | def _get_proxy_object(self, pid): | ||
216 | 257 | # Don't wait for a proxy object | ||
217 | 258 | return None | ||
218 | 259 | |||
219 | 260 | |||
220 | 261 | class ClickApplicationLauncher(UpstartApplicationLauncher): | ||
221 | 262 | |||
222 | 263 | """Fixture to manage launching a Click application.""" | ||
223 | 264 | __doc__ += ApplicationLauncher.__doc__ | ||
224 | 265 | |||
225 | 266 | def launch(self, package_id, app_name=None, app_uris=[]): | ||
226 | 267 | """Launch a click package application with introspection enabled. | ||
227 | 268 | |||
228 | 269 | This method takes care of launching a click package with introspection | ||
229 | 270 | exabled. You probably want to use this method if your application is | ||
230 | 271 | packaged in a click application, or is started via upstart. | ||
231 | 272 | |||
232 | 273 | Usage is similar to NormalApplicationLauncher.launch:: | ||
233 | 274 | |||
234 | 275 | from autopilot.application import ClickApplicationLauncher | ||
235 | 276 | launcher = ClickApplicationLauncher() | ||
236 | 277 | launcher.setUp() | ||
237 | 278 | app_proxy = launcher.launch('com.ubuntu.dropping-letters') | ||
238 | 279 | |||
239 | 280 | :param package_id: The Click package name you want to launch. For | ||
240 | 281 | example: ``com.ubuntu.dropping-letters`` | ||
241 | 282 | :param app_name: Currently, only one application can be packaged in a | ||
242 | 283 | click package, and this parameter can be left at None. If | ||
243 | 284 | specified, it should be the application name you wish to launch. | ||
244 | 285 | :param app_uris: Parameters used to launch the click package. This | ||
245 | 286 | parameter will be left empty if not used. | ||
246 | 287 | |||
247 | 288 | :raises RuntimeError: If the specified package_id cannot be found in | ||
248 | 289 | the click package manifest. | ||
249 | 290 | :raises RuntimeError: If the specified app_name cannot be found within | ||
250 | 291 | the specified click package. | ||
251 | 292 | |||
252 | 293 | :returns: proxy object for the launched package application | ||
253 | 294 | |||
254 | 295 | """ | ||
255 | 296 | if isinstance(app_uris, str): | ||
256 | 297 | app_uris = [app_uris] | ||
257 | 298 | if isinstance(app_uris, bytes): | ||
258 | 299 | app_uris = [app_uris.decode()] | ||
259 | 300 | _logger.info( | ||
260 | 301 | "Attempting to launch click application '%s' from click package " | ||
261 | 302 | " '%s' and URIs '%s'", | ||
262 | 303 | app_name if app_name is not None else "(default)", | ||
263 | 304 | package_id, | ||
264 | 305 | ','.join(app_uris) | ||
265 | 306 | ) | ||
266 | 307 | app_id = _get_click_app_id(package_id, app_name) | ||
267 | 308 | return super().launch(app_id, app_uris) | ||
268 | 309 | |||
269 | 310 | |||
270 | 311 | class NormalApplicationLauncher(ApplicationLauncher): | 79 | class NormalApplicationLauncher(ApplicationLauncher): |
271 | 312 | 80 | ||
272 | 313 | """Fixture to manage launching an application.""" | 81 | """Fixture to manage launching an application.""" |
273 | @@ -470,35 +238,6 @@ def launch_process(application, args, capture_output=False, **kwargs): | |||
274 | 470 | return process | 238 | return process |
275 | 471 | 239 | ||
276 | 472 | 240 | ||
277 | 473 | def _get_click_app_id(package_id, app_name=None): | ||
278 | 474 | for pkg in _get_click_manifest(): | ||
279 | 475 | if pkg['name'] == package_id: | ||
280 | 476 | if app_name is None: | ||
281 | 477 | # py3 dict.keys isn't indexable. | ||
282 | 478 | app_name = list(pkg['hooks'].keys())[0] | ||
283 | 479 | elif app_name not in pkg['hooks']: | ||
284 | 480 | raise RuntimeError( | ||
285 | 481 | "Application '{}' is not present within the click " | ||
286 | 482 | "package '{}'.".format(app_name, package_id)) | ||
287 | 483 | |||
288 | 484 | return "{0}_{1}_{2}".format(package_id, app_name, pkg['version']) | ||
289 | 485 | raise RuntimeError( | ||
290 | 486 | "Unable to find package '{}' in the click manifest." | ||
291 | 487 | .format(package_id) | ||
292 | 488 | ) | ||
293 | 489 | |||
294 | 490 | |||
295 | 491 | def _get_click_manifest(): | ||
296 | 492 | """Return the click package manifest as a python list.""" | ||
297 | 493 | # get the whole click package manifest every time - it seems fast enough | ||
298 | 494 | # but this is a potential optimisation point for the future: | ||
299 | 495 | click_manifest_str = subprocess.check_output( | ||
300 | 496 | ["click", "list", "--manifest"], | ||
301 | 497 | universal_newlines=True | ||
302 | 498 | ) | ||
303 | 499 | return json.loads(click_manifest_str) | ||
304 | 500 | |||
305 | 501 | |||
306 | 502 | def _get_application_environment(app_type=None, app_path=None): | 241 | def _get_application_environment(app_type=None, app_path=None): |
307 | 503 | if app_type is None and app_path is None: | 242 | if app_type is None and app_path is None: |
308 | 504 | raise ValueError("Must specify either app_type or app_path.") | 243 | raise ValueError("Must specify either app_type or app_path.") |
309 | diff --git a/autopilot/testcase.py b/autopilot/testcase.py | |||
310 | index af8c2d4..8360eea 100644 | |||
311 | --- a/autopilot/testcase.py | |||
312 | +++ b/autopilot/testcase.py | |||
313 | @@ -57,9 +57,7 @@ from testtools.testcase import _ExpectedFailure | |||
314 | 57 | from unittest.case import SkipTest | 57 | from unittest.case import SkipTest |
315 | 58 | 58 | ||
316 | 59 | from autopilot.application import ( | 59 | from autopilot.application import ( |
317 | 60 | ClickApplicationLauncher, | ||
318 | 61 | NormalApplicationLauncher, | 60 | NormalApplicationLauncher, |
319 | 62 | UpstartApplicationLauncher, | ||
320 | 63 | ) | 61 | ) |
321 | 64 | from autopilot.display import Display, get_screenshot_data | 62 | from autopilot.display import Display, get_screenshot_data |
322 | 65 | from autopilot.globals import get_debug_profile_fixture, get_test_timeout | 63 | from autopilot.globals import get_debug_profile_fixture, get_test_timeout |
323 | @@ -285,78 +283,6 @@ class AutopilotTestCase(TestWithScenarios, TestCase, KeybindingsHelper): | |||
324 | 285 | ) | 283 | ) |
325 | 286 | return launcher.launch(application, arguments, **launch_args) | 284 | return launcher.launch(application, arguments, **launch_args) |
326 | 287 | 285 | ||
327 | 288 | def launch_click_package(self, package_id, app_name=None, app_uris=[], | ||
328 | 289 | **kwargs): | ||
329 | 290 | """Launch a click package application with introspection enabled. | ||
330 | 291 | |||
331 | 292 | This method takes care of launching a click package with introspection | ||
332 | 293 | exabled. You probably want to use this method if your application is | ||
333 | 294 | packaged in a click application, or is started via upstart. | ||
334 | 295 | |||
335 | 296 | Usage is similar to the | ||
336 | 297 | :py:meth:`AutopilotTestCase.launch_test_application`:: | ||
337 | 298 | |||
338 | 299 | app_proxy = self.launch_click_package( | ||
339 | 300 | "com.ubuntu.dropping-letters" | ||
340 | 301 | ) | ||
341 | 302 | |||
342 | 303 | :param package_id: The Click package name you want to launch. For | ||
343 | 304 | example: ``com.ubuntu.dropping-letters`` | ||
344 | 305 | :param app_name: Currently, only one application can be packaged in a | ||
345 | 306 | click package, and this parameter can be left at None. If | ||
346 | 307 | specified, it should be the application name you wish to launch. | ||
347 | 308 | :param app_uris: Parameters used to launch the click package. This | ||
348 | 309 | parameter will be left empty if not used. | ||
349 | 310 | |||
350 | 311 | :keyword emulator_base: If set, specifies the base class to be used for | ||
351 | 312 | all emulators for this loaded application. | ||
352 | 313 | |||
353 | 314 | :raises RuntimeError: If the specified package_id cannot be found in | ||
354 | 315 | the click package manifest. | ||
355 | 316 | :raises RuntimeError: If the specified app_name cannot be found within | ||
356 | 317 | the specified click package. | ||
357 | 318 | |||
358 | 319 | :returns: proxy object for the launched package application | ||
359 | 320 | |||
360 | 321 | """ | ||
361 | 322 | launcher = self.useFixture( | ||
362 | 323 | ClickApplicationLauncher( | ||
363 | 324 | case_addDetail=self.addDetailUniqueName, | ||
364 | 325 | **kwargs | ||
365 | 326 | ) | ||
366 | 327 | ) | ||
367 | 328 | return launcher.launch(package_id, app_name, app_uris) | ||
368 | 329 | |||
369 | 330 | def launch_upstart_application(self, application_name, uris=[], | ||
370 | 331 | launcher_class=UpstartApplicationLauncher, | ||
371 | 332 | **kwargs): | ||
372 | 333 | """Launch an application with upstart. | ||
373 | 334 | |||
374 | 335 | This method launched an application via the ``ubuntu-app-launch`` | ||
375 | 336 | library, on platforms that support it. | ||
376 | 337 | |||
377 | 338 | Usage is similar to the | ||
378 | 339 | :py:meth:`AutopilotTestCase.launch_test_application`:: | ||
379 | 340 | |||
380 | 341 | app_proxy = self.launch_upstart_application("gallery-app") | ||
381 | 342 | |||
382 | 343 | :param application_name: The name of the application to launch. | ||
383 | 344 | :param launcher_class: The application launcher class to use. Useful if | ||
384 | 345 | you need to overwrite the default to do something custom (i.e. using | ||
385 | 346 | AlreadyLaunchedUpstartLauncher) | ||
386 | 347 | :keyword emulator_base: If set, specifies the base class to be used for | ||
387 | 348 | all emulators for this loaded application. | ||
388 | 349 | |||
389 | 350 | :raises RuntimeError: If the specified application cannot be launched. | ||
390 | 351 | """ | ||
391 | 352 | launcher = self.useFixture( | ||
392 | 353 | launcher_class( | ||
393 | 354 | case_addDetail=self.addDetailUniqueName, | ||
394 | 355 | **kwargs | ||
395 | 356 | ) | ||
396 | 357 | ) | ||
397 | 358 | return launcher.launch(application_name, uris) | ||
398 | 359 | |||
399 | 360 | def _compare_system_with_app_snapshot(self): | 286 | def _compare_system_with_app_snapshot(self): |
400 | 361 | """Compare the currently running application with the last snapshot. | 287 | """Compare the currently running application with the last snapshot. |
401 | 362 | 288 | ||
402 | diff --git a/autopilot/tests/functional/test_ap_apps.py b/autopilot/tests/functional/test_ap_apps.py | |||
403 | index 71d5928..de66fa0 100644 | |||
404 | --- a/autopilot/tests/functional/test_ap_apps.py | |||
405 | +++ b/autopilot/tests/functional/test_ap_apps.py | |||
406 | @@ -37,7 +37,6 @@ from fixtures import EnvironmentVariable | |||
407 | 37 | 37 | ||
408 | 38 | from autopilot.application import ( | 38 | from autopilot.application import ( |
409 | 39 | NormalApplicationLauncher, | 39 | NormalApplicationLauncher, |
410 | 40 | UpstartApplicationLauncher, | ||
411 | 41 | ) | 40 | ) |
412 | 42 | from autopilot.exceptions import ProcessSearchError | 41 | from autopilot.exceptions import ProcessSearchError |
413 | 43 | from autopilot.process import ProcessManager | 42 | from autopilot.process import ProcessManager |
414 | @@ -296,12 +295,6 @@ class QtTests(ApplicationTests, QmlTestMixin): | |||
415 | 296 | ) | 295 | ) |
416 | 297 | self.assertTrue(app_proxy is not None) | 296 | self.assertTrue(app_proxy is not None) |
417 | 298 | 297 | ||
418 | 299 | def test_can_launch_upstart_app(self): | ||
419 | 300 | path = self.get_qml_viewer_app_path() | ||
420 | 301 | fixture = self.useFixture(TempDesktopFile(exec_=path,)) | ||
421 | 302 | launcher = self.useFixture(UpstartApplicationLauncher()) | ||
422 | 303 | launcher.launch(fixture.get_desktop_file_id()) | ||
423 | 304 | |||
424 | 305 | @skipIf(model() != "Desktop", "Only suitable on Desktop (Qt4)") | 298 | @skipIf(model() != "Desktop", "Only suitable on Desktop (Qt4)") |
425 | 306 | def test_can_launch_normal_qt_script(self): | 299 | def test_can_launch_normal_qt_script(self): |
426 | 307 | path = self.write_script(dedent("""\ | 300 | path = self.write_script(dedent("""\ |
427 | diff --git a/autopilot/tests/functional/test_application_launcher.py b/autopilot/tests/functional/test_application_launcher.py | |||
428 | index f9c1e48..7e16a0c 100644 | |||
429 | --- a/autopilot/tests/functional/test_application_launcher.py | |||
430 | +++ b/autopilot/tests/functional/test_application_launcher.py | |||
431 | @@ -44,53 +44,3 @@ class AutopilotTestCaseClassTests(TestCase): | |||
432 | 44 | uf.return_value.launch.assert_called_once_with('a', ('b', 'c')) | 44 | uf.return_value.launch.assert_called_once_with('a', ('b', 'c')) |
433 | 45 | self.assertEqual(result, uf.return_value.launch.return_value) | 45 | self.assertEqual(result, uf.return_value.launch.return_value) |
434 | 46 | 46 | ||
435 | 47 | @patch('autopilot.testcase.ClickApplicationLauncher') | ||
436 | 48 | def test_launch_click_package(self, cal): | ||
437 | 49 | class LauncherTest(AutopilotTestCase): | ||
438 | 50 | |||
439 | 51 | """Test launchers.""" | ||
440 | 52 | |||
441 | 53 | def test_anything(self): | ||
442 | 54 | pass | ||
443 | 55 | |||
444 | 56 | test_case = LauncherTest('test_anything') | ||
445 | 57 | with patch.object(test_case, 'useFixture') as uf: | ||
446 | 58 | result = test_case.launch_click_package('a', 'b', ['c', 'd']) | ||
447 | 59 | uf.assert_called_once_with(cal.return_value) | ||
448 | 60 | uf.return_value.launch.assert_called_once_with( | ||
449 | 61 | 'a', 'b', ['c', 'd'] | ||
450 | 62 | ) | ||
451 | 63 | self.assertEqual(result, uf.return_value.launch.return_value) | ||
452 | 64 | |||
453 | 65 | @patch('autopilot.testcase.UpstartApplicationLauncher') | ||
454 | 66 | def test_launch_upstart_application_defaults(self, ual): | ||
455 | 67 | class LauncherTest(AutopilotTestCase): | ||
456 | 68 | |||
457 | 69 | """Test launchers.""" | ||
458 | 70 | |||
459 | 71 | def test_anything(self): | ||
460 | 72 | pass | ||
461 | 73 | |||
462 | 74 | test_case = LauncherTest('test_anything') | ||
463 | 75 | with patch.object(test_case, 'useFixture') as uf: | ||
464 | 76 | result = test_case.launch_upstart_application( | ||
465 | 77 | 'a', ['b'], launcher_class=ual | ||
466 | 78 | ) | ||
467 | 79 | uf.assert_called_once_with(ual.return_value) | ||
468 | 80 | uf.return_value.launch.assert_called_once_with('a', ['b']) | ||
469 | 81 | self.assertEqual(result, uf.return_value.launch.return_value) | ||
470 | 82 | |||
471 | 83 | def test_launch_upstart_application_custom_launcher(self): | ||
472 | 84 | class LauncherTest(AutopilotTestCase): | ||
473 | 85 | |||
474 | 86 | """Test launchers.""" | ||
475 | 87 | |||
476 | 88 | def test_anything(self): | ||
477 | 89 | pass | ||
478 | 90 | |||
479 | 91 | test_case = LauncherTest('test_anything') | ||
480 | 92 | self.assertRaises( | ||
481 | 93 | NotImplementedError, | ||
482 | 94 | test_case.launch_upstart_application, | ||
483 | 95 | 'a', ['b'], launcher_class=_launcher.ApplicationLauncher | ||
484 | 96 | ) | ||
485 | diff --git a/autopilot/tests/unit/test_application_launcher.py b/autopilot/tests/unit/test_application_launcher.py | |||
486 | index 3de7819..af92c42 100644 | |||
487 | --- a/autopilot/tests/unit/test_application_launcher.py | |||
488 | +++ b/autopilot/tests/unit/test_application_launcher.py | |||
489 | @@ -36,9 +36,7 @@ from testtools.content import text_content | |||
490 | 36 | from unittest.mock import MagicMock, Mock, patch | 36 | from unittest.mock import MagicMock, Mock, patch |
491 | 37 | 37 | ||
492 | 38 | from autopilot.application import ( | 38 | from autopilot.application import ( |
493 | 39 | ClickApplicationLauncher, | ||
494 | 40 | NormalApplicationLauncher, | 39 | NormalApplicationLauncher, |
495 | 41 | UpstartApplicationLauncher, | ||
496 | 42 | ) | 40 | ) |
497 | 43 | from autopilot.application._environment import ( | 41 | from autopilot.application._environment import ( |
498 | 44 | GtkApplicationEnvironment, | 42 | GtkApplicationEnvironment, |
499 | @@ -53,8 +51,6 @@ from autopilot.application._launcher import ( | |||
500 | 53 | _get_app_env_from_string_hint, | 51 | _get_app_env_from_string_hint, |
501 | 54 | _get_application_environment, | 52 | _get_application_environment, |
502 | 55 | _get_application_path, | 53 | _get_application_path, |
503 | 56 | _get_click_app_id, | ||
504 | 57 | _get_click_manifest, | ||
505 | 58 | _is_process_running, | 54 | _is_process_running, |
506 | 59 | _kill_process, | 55 | _kill_process, |
507 | 60 | ) | 56 | ) |
508 | @@ -261,635 +257,6 @@ class NormalApplicationLauncherTests(TestCase): | |||
509 | 261 | ) | 257 | ) |
510 | 262 | 258 | ||
511 | 263 | 259 | ||
512 | 264 | class ClickApplicationLauncherTests(TestCase): | ||
513 | 265 | |||
514 | 266 | def test_raises_exception_on_unknown_kwargs(self): | ||
515 | 267 | self.assertThat( | ||
516 | 268 | lambda: ClickApplicationLauncher(self.addDetail, unknown=True), | ||
517 | 269 | raises(TypeError("__init__() got an unexpected keyword argument " | ||
518 | 270 | "'unknown'")) | ||
519 | 271 | ) | ||
520 | 272 | |||
521 | 273 | @patch('autopilot.application._launcher._get_click_app_id') | ||
522 | 274 | def test_handle_string(self, gcai): | ||
523 | 275 | class FakeUpstartBase(_l.ApplicationLauncher): | ||
524 | 276 | launch_call_args = [] | ||
525 | 277 | |||
526 | 278 | def launch(self, *args): | ||
527 | 279 | FakeUpstartBase.launch_call_args = list(args) | ||
528 | 280 | |||
529 | 281 | patcher = patch.object( | ||
530 | 282 | _l.ClickApplicationLauncher, | ||
531 | 283 | '__bases__', | ||
532 | 284 | (FakeUpstartBase,) | ||
533 | 285 | ) | ||
534 | 286 | token = self.getUniqueString() | ||
535 | 287 | with patcher: | ||
536 | 288 | # Prevent mock from trying to delete __bases__ | ||
537 | 289 | patcher.is_local = True | ||
538 | 290 | launcher = self.useFixture( | ||
539 | 291 | _l.ClickApplicationLauncher()) | ||
540 | 292 | launcher.launch('', '', token) | ||
541 | 293 | self.assertEqual( | ||
542 | 294 | FakeUpstartBase.launch_call_args, | ||
543 | 295 | [gcai.return_value, [token]]) | ||
544 | 296 | |||
545 | 297 | @patch('autopilot.application._launcher._get_click_app_id') | ||
546 | 298 | def test_handle_bytes(self, gcai): | ||
547 | 299 | class FakeUpstartBase(_l.ApplicationLauncher): | ||
548 | 300 | launch_call_args = [] | ||
549 | 301 | |||
550 | 302 | def launch(self, *args): | ||
551 | 303 | FakeUpstartBase.launch_call_args = list(args) | ||
552 | 304 | |||
553 | 305 | patcher = patch.object( | ||
554 | 306 | _l.ClickApplicationLauncher, | ||
555 | 307 | '__bases__', | ||
556 | 308 | (FakeUpstartBase,) | ||
557 | 309 | ) | ||
558 | 310 | token = self.getUniqueString() | ||
559 | 311 | with patcher: | ||
560 | 312 | # Prevent mock from trying to delete __bases__ | ||
561 | 313 | patcher.is_local = True | ||
562 | 314 | launcher = self.useFixture( | ||
563 | 315 | _l.ClickApplicationLauncher()) | ||
564 | 316 | launcher.launch('', '', token.encode()) | ||
565 | 317 | self.assertEqual( | ||
566 | 318 | FakeUpstartBase.launch_call_args, | ||
567 | 319 | [gcai.return_value, [token]]) | ||
568 | 320 | |||
569 | 321 | @patch('autopilot.application._launcher._get_click_app_id') | ||
570 | 322 | def test_handle_list(self, gcai): | ||
571 | 323 | class FakeUpstartBase(_l.ApplicationLauncher): | ||
572 | 324 | launch_call_args = [] | ||
573 | 325 | |||
574 | 326 | def launch(self, *args): | ||
575 | 327 | FakeUpstartBase.launch_call_args = list(args) | ||
576 | 328 | |||
577 | 329 | patcher = patch.object( | ||
578 | 330 | _l.ClickApplicationLauncher, | ||
579 | 331 | '__bases__', | ||
580 | 332 | (FakeUpstartBase,) | ||
581 | 333 | ) | ||
582 | 334 | token = self.getUniqueString() | ||
583 | 335 | with patcher: | ||
584 | 336 | # Prevent mock from trying to delete __bases__ | ||
585 | 337 | patcher.is_local = True | ||
586 | 338 | launcher = self.useFixture( | ||
587 | 339 | _l.ClickApplicationLauncher()) | ||
588 | 340 | launcher.launch('', '', [token]) | ||
589 | 341 | self.assertEqual( | ||
590 | 342 | FakeUpstartBase.launch_call_args, | ||
591 | 343 | [gcai.return_value, [token]]) | ||
592 | 344 | |||
593 | 345 | @patch('autopilot.application._launcher._get_click_app_id') | ||
594 | 346 | def test_call_get_click_app_id(self, gcai): | ||
595 | 347 | class FakeUpstartBase(_l.ApplicationLauncher): | ||
596 | 348 | launch_call_args = [] | ||
597 | 349 | |||
598 | 350 | def launch(self, *args): | ||
599 | 351 | FakeUpstartBase.launch_call_args = list(args) | ||
600 | 352 | |||
601 | 353 | patcher = patch.object( | ||
602 | 354 | _l.ClickApplicationLauncher, | ||
603 | 355 | '__bases__', | ||
604 | 356 | (FakeUpstartBase,) | ||
605 | 357 | ) | ||
606 | 358 | token_a = self.getUniqueString() | ||
607 | 359 | token_b = self.getUniqueString() | ||
608 | 360 | with patcher: | ||
609 | 361 | # Prevent mock from trying to delete __bases__ | ||
610 | 362 | patcher.is_local = True | ||
611 | 363 | launcher = self.useFixture( | ||
612 | 364 | _l.ClickApplicationLauncher()) | ||
613 | 365 | launcher.launch(token_a, token_b) | ||
614 | 366 | gcai.assert_called_once_with(token_a, token_b) | ||
615 | 367 | |||
616 | 368 | @patch('autopilot.application._launcher._get_click_app_id') | ||
617 | 369 | def test_call_upstart_launch(self, gcai): | ||
618 | 370 | class FakeUpstartBase(_l.ApplicationLauncher): | ||
619 | 371 | launch_call_args = [] | ||
620 | 372 | |||
621 | 373 | def launch(self, *args): | ||
622 | 374 | FakeUpstartBase.launch_call_args = list(args) | ||
623 | 375 | |||
624 | 376 | patcher = patch.object( | ||
625 | 377 | _l.ClickApplicationLauncher, | ||
626 | 378 | '__bases__', | ||
627 | 379 | (FakeUpstartBase,) | ||
628 | 380 | ) | ||
629 | 381 | with patcher: | ||
630 | 382 | # Prevent mock from trying to delete __bases__ | ||
631 | 383 | patcher.is_local = True | ||
632 | 384 | launcher = self.useFixture( | ||
633 | 385 | _l.ClickApplicationLauncher()) | ||
634 | 386 | launcher.launch('', '') | ||
635 | 387 | self.assertEqual(launcher.launch_call_args, | ||
636 | 388 | [gcai.return_value, []]) | ||
637 | 389 | |||
638 | 390 | |||
639 | 391 | class ClickFunctionTests(TestCase): | ||
640 | 392 | |||
641 | 393 | def test_get_click_app_id_raises_runtimeerror_on_empty_manifest(self): | ||
642 | 394 | """_get_click_app_id must raise a RuntimeError if the requested | ||
643 | 395 | package id is not found in the click manifest. | ||
644 | 396 | |||
645 | 397 | """ | ||
646 | 398 | with patch.object(_l, '_get_click_manifest', return_value=[]): | ||
647 | 399 | self.assertThat( | ||
648 | 400 | lambda: _get_click_app_id("com.autopilot.testing"), | ||
649 | 401 | raises( | ||
650 | 402 | RuntimeError( | ||
651 | 403 | "Unable to find package 'com.autopilot.testing' in " | ||
652 | 404 | "the click manifest." | ||
653 | 405 | ) | ||
654 | 406 | ) | ||
655 | 407 | ) | ||
656 | 408 | |||
657 | 409 | def test_get_click_app_id_raises_runtimeerror_on_missing_package(self): | ||
658 | 410 | with patch.object(_l, '_get_click_manifest') as cm: | ||
659 | 411 | cm.return_value = [ | ||
660 | 412 | { | ||
661 | 413 | 'name': 'com.not.expected.name', | ||
662 | 414 | 'hooks': {'bar': {}}, 'version': '1.0' | ||
663 | 415 | } | ||
664 | 416 | ] | ||
665 | 417 | |||
666 | 418 | self.assertThat( | ||
667 | 419 | lambda: _get_click_app_id("com.autopilot.testing"), | ||
668 | 420 | raises( | ||
669 | 421 | RuntimeError( | ||
670 | 422 | "Unable to find package 'com.autopilot.testing' in " | ||
671 | 423 | "the click manifest." | ||
672 | 424 | ) | ||
673 | 425 | ) | ||
674 | 426 | ) | ||
675 | 427 | |||
676 | 428 | def test_get_click_app_id_raises_runtimeerror_on_wrong_app(self): | ||
677 | 429 | """get_click_app_id must raise a RuntimeError if the requested | ||
678 | 430 | application is not found within the click package. | ||
679 | 431 | |||
680 | 432 | """ | ||
681 | 433 | with patch.object(_l, '_get_click_manifest') as cm: | ||
682 | 434 | cm.return_value = [{'name': 'com.autopilot.testing', 'hooks': {}}] | ||
683 | 435 | |||
684 | 436 | self.assertThat( | ||
685 | 437 | lambda: _get_click_app_id("com.autopilot.testing", "bar"), | ||
686 | 438 | raises( | ||
687 | 439 | RuntimeError( | ||
688 | 440 | "Application 'bar' is not present within the click " | ||
689 | 441 | "package 'com.autopilot.testing'." | ||
690 | 442 | ) | ||
691 | 443 | ) | ||
692 | 444 | ) | ||
693 | 445 | |||
694 | 446 | def test_get_click_app_id_returns_id(self): | ||
695 | 447 | with patch.object(_l, '_get_click_manifest') as cm: | ||
696 | 448 | cm.return_value = [ | ||
697 | 449 | { | ||
698 | 450 | 'name': 'com.autopilot.testing', | ||
699 | 451 | 'hooks': {'bar': {}}, 'version': '1.0' | ||
700 | 452 | } | ||
701 | 453 | ] | ||
702 | 454 | |||
703 | 455 | self.assertThat( | ||
704 | 456 | _get_click_app_id("com.autopilot.testing", "bar"), | ||
705 | 457 | Equals("com.autopilot.testing_bar_1.0") | ||
706 | 458 | ) | ||
707 | 459 | |||
708 | 460 | def test_get_click_app_id_returns_id_without_appid_passed(self): | ||
709 | 461 | with patch.object(_l, '_get_click_manifest') as cm: | ||
710 | 462 | cm.return_value = [ | ||
711 | 463 | { | ||
712 | 464 | 'name': 'com.autopilot.testing', | ||
713 | 465 | 'hooks': {'bar': {}}, 'version': '1.0' | ||
714 | 466 | } | ||
715 | 467 | ] | ||
716 | 468 | |||
717 | 469 | self.assertThat( | ||
718 | 470 | _get_click_app_id("com.autopilot.testing"), | ||
719 | 471 | Equals("com.autopilot.testing_bar_1.0") | ||
720 | 472 | ) | ||
721 | 473 | |||
722 | 474 | |||
723 | 475 | class UpstartApplicationLauncherTests(TestCase): | ||
724 | 476 | |||
725 | 477 | def test_can_construct_UpstartApplicationLauncher(self): | ||
726 | 478 | UpstartApplicationLauncher(self.addDetail) | ||
727 | 479 | |||
728 | 480 | def test_raises_exception_on_unknown_kwargs(self): | ||
729 | 481 | self.assertThat( | ||
730 | 482 | lambda: UpstartApplicationLauncher(self.addDetail, unknown=True), | ||
731 | 483 | raises(TypeError("__init__() got an unexpected keyword argument " | ||
732 | 484 | "'unknown'")) | ||
733 | 485 | ) | ||
734 | 486 | |||
735 | 487 | def test_on_failed_only_sets_status_on_correct_app_id(self): | ||
736 | 488 | state = { | ||
737 | 489 | 'expected_app_id': 'gedit', | ||
738 | 490 | } | ||
739 | 491 | |||
740 | 492 | UpstartApplicationLauncher._on_failed('some_game', None, state) | ||
741 | 493 | |||
742 | 494 | self.assertThat(state, Not(Contains('status'))) | ||
743 | 495 | |||
744 | 496 | def assertFunctionSetsCorrectStateAndQuits(self, observer, expected_state): | ||
745 | 497 | """Assert that the observer observer sets the correct state id. | ||
746 | 498 | |||
747 | 499 | :param observer: The observer callable you want to test. | ||
748 | 500 | :param expected_state: The state id the observer callable must set. | ||
749 | 501 | |||
750 | 502 | """ | ||
751 | 503 | expected_app_id = self.getUniqueString() | ||
752 | 504 | state = { | ||
753 | 505 | 'expected_app_id': expected_app_id, | ||
754 | 506 | 'loop': Mock() | ||
755 | 507 | } | ||
756 | 508 | |||
757 | 509 | if observer == UpstartApplicationLauncher._on_failed: | ||
758 | 510 | observer(expected_app_id, None, state) | ||
759 | 511 | elif observer == UpstartApplicationLauncher._on_started or \ | ||
760 | 512 | observer == UpstartApplicationLauncher._on_stopped: | ||
761 | 513 | observer(expected_app_id, state) | ||
762 | 514 | else: | ||
763 | 515 | observer(state) | ||
764 | 516 | |||
765 | 517 | self.assertThat( | ||
766 | 518 | state['status'], | ||
767 | 519 | Equals(expected_state) | ||
768 | 520 | ) | ||
769 | 521 | state['loop'].quit.assert_called_once_with() | ||
770 | 522 | |||
771 | 523 | def test_on_failed_sets_status_with_correct_app_id(self): | ||
772 | 524 | self.assertFunctionSetsCorrectStateAndQuits( | ||
773 | 525 | UpstartApplicationLauncher._on_failed, | ||
774 | 526 | UpstartApplicationLauncher.Failed | ||
775 | 527 | ) | ||
776 | 528 | |||
777 | 529 | def test_on_started_sets_status_with_correct_app_id(self): | ||
778 | 530 | self.assertFunctionSetsCorrectStateAndQuits( | ||
779 | 531 | UpstartApplicationLauncher._on_started, | ||
780 | 532 | UpstartApplicationLauncher.Started | ||
781 | 533 | ) | ||
782 | 534 | |||
783 | 535 | def test_on_timeout_sets_status_and_exits_loop(self): | ||
784 | 536 | self.assertFunctionSetsCorrectStateAndQuits( | ||
785 | 537 | UpstartApplicationLauncher._on_timeout, | ||
786 | 538 | UpstartApplicationLauncher.Timeout | ||
787 | 539 | ) | ||
788 | 540 | |||
789 | 541 | def test_on_started_only_sets_status_on_correct_app_id(self): | ||
790 | 542 | state = { | ||
791 | 543 | 'expected_app_id': 'gedit', | ||
792 | 544 | } | ||
793 | 545 | |||
794 | 546 | UpstartApplicationLauncher._on_started('some_game', state) | ||
795 | 547 | |||
796 | 548 | self.assertThat(state, Not(Contains('status'))) | ||
797 | 549 | |||
798 | 550 | def test_on_stopped_only_sets_status_on_correct_app_id(self): | ||
799 | 551 | state = { | ||
800 | 552 | 'expected_app_id': 'gedit', | ||
801 | 553 | } | ||
802 | 554 | |||
803 | 555 | UpstartApplicationLauncher._on_stopped('some_game', state) | ||
804 | 556 | self.assertThat(state, Not(Contains('status'))) | ||
805 | 557 | |||
806 | 558 | def test_on_stopped_sets_status_and_exits_loop(self): | ||
807 | 559 | self.assertFunctionSetsCorrectStateAndQuits( | ||
808 | 560 | UpstartApplicationLauncher._on_stopped, | ||
809 | 561 | UpstartApplicationLauncher.Stopped | ||
810 | 562 | ) | ||
811 | 563 | |||
812 | 564 | def test_get_pid_calls_upstart_module(self): | ||
813 | 565 | expected_return = self.getUniqueInteger() | ||
814 | 566 | with patch.object(_l, 'UbuntuAppLaunch') as mock_ual: | ||
815 | 567 | mock_ual.get_primary_pid.return_value = expected_return | ||
816 | 568 | observed = UpstartApplicationLauncher._get_pid_for_launched_app( | ||
817 | 569 | 'gedit' | ||
818 | 570 | ) | ||
819 | 571 | |||
820 | 572 | mock_ual.get_primary_pid.assert_called_once_with('gedit') | ||
821 | 573 | self.assertThat(expected_return, Equals(observed)) | ||
822 | 574 | |||
823 | 575 | def test_launch_app_calls_upstart_module(self): | ||
824 | 576 | with patch.object(_l, 'UbuntuAppLaunch') as mock_ual: | ||
825 | 577 | UpstartApplicationLauncher._launch_app( | ||
826 | 578 | 'gedit', | ||
827 | 579 | ['some', 'uris'] | ||
828 | 580 | ) | ||
829 | 581 | |||
830 | 582 | mock_ual.start_application_test.assert_called_once_with( | ||
831 | 583 | 'gedit', | ||
832 | 584 | ['some', 'uris'] | ||
833 | 585 | ) | ||
834 | 586 | |||
835 | 587 | def test_check_error_raises_RuntimeError_on_timeout(self): | ||
836 | 588 | fn = lambda: UpstartApplicationLauncher._check_status_error( | ||
837 | 589 | UpstartApplicationLauncher.Timeout | ||
838 | 590 | ) | ||
839 | 591 | self.assertThat( | ||
840 | 592 | fn, | ||
841 | 593 | raises( | ||
842 | 594 | RuntimeError( | ||
843 | 595 | "Timed out while waiting for application to launch" | ||
844 | 596 | ) | ||
845 | 597 | ) | ||
846 | 598 | ) | ||
847 | 599 | |||
848 | 600 | def test_check_error_raises_RuntimeError_on_failure(self): | ||
849 | 601 | fn = lambda: UpstartApplicationLauncher._check_status_error( | ||
850 | 602 | UpstartApplicationLauncher.Failed | ||
851 | 603 | ) | ||
852 | 604 | self.assertThat( | ||
853 | 605 | fn, | ||
854 | 606 | raises( | ||
855 | 607 | RuntimeError( | ||
856 | 608 | "Application Launch Failed" | ||
857 | 609 | ) | ||
858 | 610 | ) | ||
859 | 611 | ) | ||
860 | 612 | |||
861 | 613 | def test_check_error_raises_RuntimeError_with_extra_message(self): | ||
862 | 614 | fn = lambda: UpstartApplicationLauncher._check_status_error( | ||
863 | 615 | UpstartApplicationLauncher.Failed, | ||
864 | 616 | "extra message" | ||
865 | 617 | ) | ||
866 | 618 | self.assertThat( | ||
867 | 619 | fn, | ||
868 | 620 | raises( | ||
869 | 621 | RuntimeError( | ||
870 | 622 | "Application Launch Failed: extra message" | ||
871 | 623 | ) | ||
872 | 624 | ) | ||
873 | 625 | ) | ||
874 | 626 | |||
875 | 627 | def test_check_error_does_nothing_on_None(self): | ||
876 | 628 | UpstartApplicationLauncher._check_status_error(None) | ||
877 | 629 | |||
878 | 630 | def test_get_loop_returns_glib_mainloop_instance(self): | ||
879 | 631 | loop = UpstartApplicationLauncher._get_glib_loop() | ||
880 | 632 | self.assertThat(loop, IsInstance(GLib.MainLoop)) | ||
881 | 633 | |||
882 | 634 | @patch('autopilot.application._launcher.UbuntuAppLaunch') | ||
883 | 635 | @patch('autopilot.application._launcher.' | ||
884 | 636 | 'get_proxy_object_for_existing_process') | ||
885 | 637 | def test_handle_string(self, gpofep, ual): | ||
886 | 638 | launcher = UpstartApplicationLauncher() | ||
887 | 639 | token_a = self.getUniqueString() | ||
888 | 640 | token_b = self.getUniqueString() | ||
889 | 641 | with patch.object(launcher, '_launch_app') as la: | ||
890 | 642 | with patch.object(launcher, '_get_pid_for_launched_app'): | ||
891 | 643 | with patch.object(launcher, '_get_glib_loop'): | ||
892 | 644 | launcher.launch(token_a, token_b) | ||
893 | 645 | la.assert_called_once_with(token_a, [token_b]) | ||
894 | 646 | |||
895 | 647 | @patch('autopilot.application._launcher.UbuntuAppLaunch') | ||
896 | 648 | @patch('autopilot.application._launcher.' | ||
897 | 649 | 'get_proxy_object_for_existing_process') | ||
898 | 650 | def test_handle_bytes(self, gpofep, ual): | ||
899 | 651 | launcher = UpstartApplicationLauncher() | ||
900 | 652 | token_a = self.getUniqueString() | ||
901 | 653 | token_b = self.getUniqueString() | ||
902 | 654 | with patch.object(launcher, '_launch_app') as la: | ||
903 | 655 | with patch.object(launcher, '_get_pid_for_launched_app'): | ||
904 | 656 | with patch.object(launcher, '_get_glib_loop'): | ||
905 | 657 | launcher.launch(token_a, token_b.encode()) | ||
906 | 658 | la.assert_called_once_with(token_a, [token_b]) | ||
907 | 659 | |||
908 | 660 | @patch('autopilot.application._launcher.UbuntuAppLaunch') | ||
909 | 661 | @patch('autopilot.application._launcher.' | ||
910 | 662 | 'get_proxy_object_for_existing_process') | ||
911 | 663 | def test_handle_list(self, gpofep, ual): | ||
912 | 664 | launcher = UpstartApplicationLauncher() | ||
913 | 665 | token_a = self.getUniqueString() | ||
914 | 666 | token_b = self.getUniqueString() | ||
915 | 667 | with patch.object(launcher, '_launch_app') as la: | ||
916 | 668 | with patch.object(launcher, '_get_pid_for_launched_app'): | ||
917 | 669 | with patch.object(launcher, '_get_glib_loop'): | ||
918 | 670 | launcher.launch(token_a, [token_b]) | ||
919 | 671 | la.assert_called_once_with(token_a, [token_b]) | ||
920 | 672 | |||
921 | 673 | @patch('autopilot.application._launcher.UbuntuAppLaunch') | ||
922 | 674 | @patch('autopilot.application._launcher.' | ||
923 | 675 | 'get_proxy_object_for_existing_process') | ||
924 | 676 | def test_calls_get_pid(self, gpofep, ual): | ||
925 | 677 | launcher = UpstartApplicationLauncher() | ||
926 | 678 | token = self.getUniqueString() | ||
927 | 679 | with patch.object(launcher, '_launch_app'): | ||
928 | 680 | with patch.object(launcher, '_get_pid_for_launched_app') as gp: | ||
929 | 681 | with patch.object(launcher, '_get_glib_loop'): | ||
930 | 682 | launcher.launch(token) | ||
931 | 683 | gp.assert_called_once_with(token) | ||
932 | 684 | |||
933 | 685 | @patch('autopilot.application._launcher.UbuntuAppLaunch') | ||
934 | 686 | @patch('autopilot.application._launcher.' | ||
935 | 687 | 'get_proxy_object_for_existing_process') | ||
936 | 688 | def test_gets_correct_proxy_object(self, gpofep, ual): | ||
937 | 689 | launcher = UpstartApplicationLauncher() | ||
938 | 690 | with patch.object(launcher, '_launch_app'): | ||
939 | 691 | with patch.object(launcher, '_get_pid_for_launched_app') as gp: | ||
940 | 692 | with patch.object(launcher, '_get_glib_loop'): | ||
941 | 693 | launcher.launch('') | ||
942 | 694 | gpofep.assert_called_once_with(pid=gp.return_value, | ||
943 | 695 | emulator_base=None, | ||
944 | 696 | dbus_bus='session') | ||
945 | 697 | |||
946 | 698 | @patch('autopilot.application._launcher.UbuntuAppLaunch') | ||
947 | 699 | @patch('autopilot.application._launcher.' | ||
948 | 700 | 'get_proxy_object_for_existing_process') | ||
949 | 701 | def test_returns_proxy_object(self, gpofep, ual): | ||
950 | 702 | launcher = UpstartApplicationLauncher() | ||
951 | 703 | with patch.object(launcher, '_launch_app'): | ||
952 | 704 | with patch.object(launcher, '_get_pid_for_launched_app'): | ||
953 | 705 | with patch.object(launcher, '_get_glib_loop'): | ||
954 | 706 | result = launcher.launch('') | ||
955 | 707 | self.assertEqual(result, gpofep.return_value) | ||
956 | 708 | |||
957 | 709 | @patch('autopilot.application._launcher.UbuntuAppLaunch') | ||
958 | 710 | @patch('autopilot.application._launcher.' | ||
959 | 711 | 'get_proxy_object_for_existing_process') | ||
960 | 712 | def test_calls_get_glib_loop(self, gpofep, ual): | ||
961 | 713 | launcher = UpstartApplicationLauncher() | ||
962 | 714 | with patch.object(launcher, '_launch_app'): | ||
963 | 715 | with patch.object(launcher, '_get_pid_for_launched_app'): | ||
964 | 716 | with patch.object(launcher, '_get_glib_loop') as ggl: | ||
965 | 717 | launcher.launch('') | ||
966 | 718 | ggl.assert_called_once_with() | ||
967 | 719 | |||
968 | 720 | def assertFailedObserverSetsExtraMessage(self, fail_type, expected_msg): | ||
969 | 721 | """Assert that the on_failed observer must set the expected message | ||
970 | 722 | for a particular failure_type.""" | ||
971 | 723 | expected_app_id = self.getUniqueString() | ||
972 | 724 | state = { | ||
973 | 725 | 'expected_app_id': expected_app_id, | ||
974 | 726 | 'loop': Mock() | ||
975 | 727 | } | ||
976 | 728 | UpstartApplicationLauncher._on_failed( | ||
977 | 729 | expected_app_id, | ||
978 | 730 | fail_type, | ||
979 | 731 | state | ||
980 | 732 | ) | ||
981 | 733 | self.assertEqual(expected_msg, state['message']) | ||
982 | 734 | |||
983 | 735 | def test_on_failed_sets_message_for_app_crash(self): | ||
984 | 736 | self.assertFailedObserverSetsExtraMessage( | ||
985 | 737 | _l.UbuntuAppLaunch.AppFailed.CRASH, | ||
986 | 738 | 'Application crashed.' | ||
987 | 739 | ) | ||
988 | 740 | |||
989 | 741 | def test_on_failed_sets_message_for_app_start_failure(self): | ||
990 | 742 | self.assertFailedObserverSetsExtraMessage( | ||
991 | 743 | _l.UbuntuAppLaunch.AppFailed.START_FAILURE, | ||
992 | 744 | 'Application failed to start.' | ||
993 | 745 | ) | ||
994 | 746 | |||
995 | 747 | def test_add_application_cleanups_adds_both_cleanup_actions(self): | ||
996 | 748 | token = self.getUniqueString() | ||
997 | 749 | state = { | ||
998 | 750 | 'status': UpstartApplicationLauncher.Started, | ||
999 | 751 | 'expected_app_id': token, | ||
1000 | 752 | } | ||
1001 | 753 | launcher = UpstartApplicationLauncher(Mock()) | ||
1002 | 754 | launcher.setUp() | ||
1003 | 755 | launcher._maybe_add_application_cleanups(state) | ||
1004 | 756 | self.assertThat( | ||
1005 | 757 | launcher._cleanups._cleanups, | ||
1006 | 758 | Contains( | ||
1007 | 759 | (launcher._attach_application_log, (token,), {}) | ||
1008 | 760 | ) | ||
1009 | 761 | ) | ||
1010 | 762 | self.assertThat( | ||
1011 | 763 | launcher._cleanups._cleanups, | ||
1012 | 764 | Contains( | ||
1013 | 765 | (launcher._stop_application, (token,), {}) | ||
1014 | 766 | ) | ||
1015 | 767 | ) | ||
1016 | 768 | |||
1017 | 769 | def test_add_application_cleanups_does_nothing_when_app_timedout(self): | ||
1018 | 770 | state = { | ||
1019 | 771 | 'status': UpstartApplicationLauncher.Timeout, | ||
1020 | 772 | } | ||
1021 | 773 | launcher = UpstartApplicationLauncher(Mock()) | ||
1022 | 774 | launcher.setUp() | ||
1023 | 775 | launcher._maybe_add_application_cleanups(state) | ||
1024 | 776 | self.assertThat(launcher._cleanups._cleanups, HasLength(0)) | ||
1025 | 777 | |||
1026 | 778 | def test_add_application_cleanups_does_nothing_when_app_failed(self): | ||
1027 | 779 | state = { | ||
1028 | 780 | 'status': UpstartApplicationLauncher.Failed, | ||
1029 | 781 | } | ||
1030 | 782 | launcher = UpstartApplicationLauncher(Mock()) | ||
1031 | 783 | launcher.setUp() | ||
1032 | 784 | launcher._maybe_add_application_cleanups(state) | ||
1033 | 785 | self.assertThat(launcher._cleanups._cleanups, HasLength(0)) | ||
1034 | 786 | |||
1035 | 787 | def test_attach_application_log_does_nothing_wth_no_log_specified(self): | ||
1036 | 788 | app_id = self.getUniqueString() | ||
1037 | 789 | case_addDetail = Mock() | ||
1038 | 790 | launcher = UpstartApplicationLauncher(case_addDetail) | ||
1039 | 791 | j = MagicMock(spec=_l.journal.Reader) | ||
1040 | 792 | with patch.object(_l.journal, 'Reader', return_value=j): | ||
1041 | 793 | launcher._attach_application_log(app_id) | ||
1042 | 794 | expected = launcher._get_user_unit_match(app_id) | ||
1043 | 795 | j.add_match.assert_called_once_with(_SYSTEMD_USER_UNIT=expected) | ||
1044 | 796 | self.assertEqual(0, case_addDetail.call_count) | ||
1045 | 797 | |||
1046 | 798 | def test_attach_application_log_attaches_log(self): | ||
1047 | 799 | token = self.getUniqueString() | ||
1048 | 800 | case_addDetail = Mock() | ||
1049 | 801 | launcher = UpstartApplicationLauncher(case_addDetail) | ||
1050 | 802 | app_id = self.getUniqueString() | ||
1051 | 803 | j = MagicMock(spec=_l.journal.Reader) | ||
1052 | 804 | j.__iter__ = lambda x: iter([token]) | ||
1053 | 805 | with patch.object(_l.journal, 'Reader', return_value=j): | ||
1054 | 806 | launcher._attach_application_log(app_id) | ||
1055 | 807 | |||
1056 | 808 | self.assertEqual(1, case_addDetail.call_count) | ||
1057 | 809 | content_name, content_obj = case_addDetail.call_args[0] | ||
1058 | 810 | self.assertEqual( | ||
1059 | 811 | "Application Log (%s)" % app_id, | ||
1060 | 812 | content_name | ||
1061 | 813 | ) | ||
1062 | 814 | self.assertThat(content_obj.as_text(), Contains(token)) | ||
1063 | 815 | |||
1064 | 816 | def test_stop_adds_app_stopped_observer(self): | ||
1065 | 817 | mock_add_detail = Mock() | ||
1066 | 818 | mock_glib_loop = Mock() | ||
1067 | 819 | patch_get_loop = patch.object( | ||
1068 | 820 | UpstartApplicationLauncher, | ||
1069 | 821 | '_get_glib_loop', | ||
1070 | 822 | new=mock_glib_loop, | ||
1071 | 823 | ) | ||
1072 | 824 | mock_UAL = Mock() | ||
1073 | 825 | patch_UAL = patch.object(_l, 'UbuntuAppLaunch', new=mock_UAL) | ||
1074 | 826 | launcher = UpstartApplicationLauncher(mock_add_detail) | ||
1075 | 827 | app_id = self.getUniqueString() | ||
1076 | 828 | with ExitStack() as patches: | ||
1077 | 829 | patches.enter_context(patch_get_loop) | ||
1078 | 830 | patches.enter_context(patch_UAL) | ||
1079 | 831 | |||
1080 | 832 | launcher._stop_application(app_id) | ||
1081 | 833 | call_args = mock_UAL.observer_add_app_stop.call_args[0] | ||
1082 | 834 | self.assertThat( | ||
1083 | 835 | call_args[0], | ||
1084 | 836 | Equals(UpstartApplicationLauncher._on_stopped) | ||
1085 | 837 | ) | ||
1086 | 838 | self.assertThat(call_args[1]['expected_app_id'], Equals(app_id)) | ||
1087 | 839 | |||
1088 | 840 | def test_stop_calls_libUAL_stop_function(self): | ||
1089 | 841 | mock_add_detail = Mock() | ||
1090 | 842 | mock_glib_loop = Mock() | ||
1091 | 843 | patch_get_loop = patch.object( | ||
1092 | 844 | UpstartApplicationLauncher, | ||
1093 | 845 | '_get_glib_loop', | ||
1094 | 846 | new=mock_glib_loop, | ||
1095 | 847 | ) | ||
1096 | 848 | mock_UAL = Mock() | ||
1097 | 849 | patch_UAL = patch.object(_l, 'UbuntuAppLaunch', new=mock_UAL) | ||
1098 | 850 | launcher = UpstartApplicationLauncher(mock_add_detail) | ||
1099 | 851 | app_id = self.getUniqueString() | ||
1100 | 852 | with ExitStack() as patches: | ||
1101 | 853 | patches.enter_context(patch_get_loop) | ||
1102 | 854 | patches.enter_context(patch_UAL) | ||
1103 | 855 | |||
1104 | 856 | launcher._stop_application(app_id) | ||
1105 | 857 | mock_UAL.stop_application.assert_called_once_with(app_id) | ||
1106 | 858 | |||
1107 | 859 | def test_stop_logs_error_on_timeout(self): | ||
1108 | 860 | mock_add_detail = Mock() | ||
1109 | 861 | mock_glib_loop = Mock() | ||
1110 | 862 | patch_get_loop = patch.object( | ||
1111 | 863 | UpstartApplicationLauncher, | ||
1112 | 864 | '_get_glib_loop', | ||
1113 | 865 | new=mock_glib_loop, | ||
1114 | 866 | ) | ||
1115 | 867 | mock_UAL = Mock() | ||
1116 | 868 | |||
1117 | 869 | # we replace the add_observer function with one that can set the | ||
1118 | 870 | # tiemout state, so we can ibject the timeout condition within the | ||
1119 | 871 | # glib loop. This is ugly, but necessary. | ||
1120 | 872 | def fake_add_observer(fn, state): | ||
1121 | 873 | state['status'] = UpstartApplicationLauncher.Timeout | ||
1122 | 874 | mock_UAL.observer_add_app_stop = fake_add_observer | ||
1123 | 875 | patch_UAL = patch.object(_l, 'UbuntuAppLaunch', new=mock_UAL) | ||
1124 | 876 | launcher = UpstartApplicationLauncher(mock_add_detail) | ||
1125 | 877 | app_id = self.getUniqueString() | ||
1126 | 878 | mock_logger = Mock() | ||
1127 | 879 | patch_logger = patch.object(_l, '_logger', new=mock_logger) | ||
1128 | 880 | with ExitStack() as patches: | ||
1129 | 881 | patches.enter_context(patch_get_loop) | ||
1130 | 882 | patches.enter_context(patch_UAL) | ||
1131 | 883 | patches.enter_context(patch_logger) | ||
1132 | 884 | |||
1133 | 885 | launcher._stop_application(app_id) | ||
1134 | 886 | |||
1135 | 887 | mock_logger.error.assert_called_once_with( | ||
1136 | 888 | "Timed out waiting for Application with app_id '%s' to stop.", | ||
1137 | 889 | app_id | ||
1138 | 890 | ) | ||
1139 | 891 | |||
1140 | 892 | |||
1141 | 893 | class ApplicationLauncherInternalTests(TestCase): | 260 | class ApplicationLauncherInternalTests(TestCase): |
1142 | 894 | 261 | ||
1143 | 895 | def test_get_app_env_from_string_hint_returns_qt_env(self): | 262 | def test_get_app_env_from_string_hint_returns_qt_env(self): |
1144 | @@ -1098,24 +465,6 @@ class ApplicationLauncherInternalTests(TestCase): | |||
1145 | 1098 | get_application_launcher_wrapper(""), Equals(None) | 465 | get_application_launcher_wrapper(""), Equals(None) |
1146 | 1099 | ) | 466 | ) |
1147 | 1100 | 467 | ||
1148 | 1101 | def test_get_click_manifest_returns_python_object(self): | ||
1149 | 1102 | example_manifest = """ | ||
1150 | 1103 | [{ | ||
1151 | 1104 | "description": "Calculator application", | ||
1152 | 1105 | "framework": "ubuntu-sdk-13.10", | ||
1153 | 1106 | "hooks": { | ||
1154 | 1107 | "calculator": { | ||
1155 | 1108 | "apparmor": "apparmor/calculator.json", | ||
1156 | 1109 | "desktop": "ubuntu-calculator-app.desktop" | ||
1157 | 1110 | } | ||
1158 | 1111 | }, | ||
1159 | 1112 | "icon": "calculator64.png" | ||
1160 | 1113 | }] | ||
1161 | 1114 | """ | ||
1162 | 1115 | with patch.object(_l.subprocess, 'check_output') as check_output: | ||
1163 | 1116 | check_output.return_value = example_manifest | ||
1164 | 1117 | self.assertThat(_get_click_manifest(), IsInstance(list)) | ||
1165 | 1118 | |||
1166 | 1119 | @patch.object(_l.psutil, 'pid_exists') | 468 | @patch.object(_l.psutil, 'pid_exists') |
1167 | 1120 | def test_is_process_running_checks_with_pid(self, pid_exists): | 469 | def test_is_process_running_checks_with_pid(self, pid_exists): |
1168 | 1121 | pid_exists.return_value = True | 470 | pid_exists.return_value = True |
1169 | diff --git a/debian/changelog b/debian/changelog | |||
1170 | index b1b6624..a951fae 100644 | |||
1171 | --- a/debian/changelog | |||
1172 | +++ b/debian/changelog | |||
1173 | @@ -1,3 +1,16 @@ | |||
1174 | 1 | autopilot (1.6.0+17.04.20170313-0ubuntu9) groovy; urgency=medium | ||
1175 | 2 | |||
1176 | 3 | * Drop upstart, click, ubuntu-app-launch support. All of those things | ||
1177 | 4 | are removed from the Ubuntu Archive. | ||
1178 | 5 | |||
1179 | 6 | -- Dimitri John Ledkov <xnox@ubuntu.com> Thu, 04 Jun 2020 14:10:05 +0100 | ||
1180 | 7 | |||
1181 | 8 | autopilot (1.6.0+17.04.20170313-0ubuntu8) focal; urgency=medium | ||
1182 | 9 | |||
1183 | 10 | * No-change rebuild to drop python3.7. | ||
1184 | 11 | |||
1185 | 12 | -- Matthias Klose <doko@ubuntu.com> Tue, 18 Feb 2020 09:16:48 +0100 | ||
1186 | 13 | |||
1187 | 1 | autopilot (1.6.0+17.04.20170313-0ubuntu7) focal; urgency=medium | 14 | autopilot (1.6.0+17.04.20170313-0ubuntu7) focal; urgency=medium |
1188 | 2 | 15 | ||
1189 | 3 | * autopilot-vis has been removed, so removed test_vis_main which depends on | 16 | * autopilot-vis has been removed, so removed test_vis_main which depends on |
1190 | diff --git a/debian/control b/debian/control | |||
1191 | index 30f73e8..27699b6 100644 | |||
1192 | --- a/debian/control | |||
1193 | +++ b/debian/control | |||
1194 | @@ -7,7 +7,6 @@ Build-Depends: debhelper (>= 9.0.0), | |||
1195 | 7 | dh-python, | 7 | dh-python, |
1196 | 8 | gir1.2-gtk-3.0, | 8 | gir1.2-gtk-3.0, |
1197 | 9 | gir1.2-ibus-1.0, | 9 | gir1.2-ibus-1.0, |
1198 | 10 | gir1.2-ubuntu-app-launch-3 | gir1.2-ubuntu-app-launch-2, | ||
1199 | 11 | graphviz, | 10 | graphviz, |
1200 | 12 | libjs-jquery, | 11 | libjs-jquery, |
1201 | 13 | libjs-underscore, | 12 | libjs-underscore, |
1202 | @@ -39,8 +38,7 @@ X-Python3-Version: >= 3.3 | |||
1203 | 39 | 38 | ||
1204 | 40 | Package: python3-autopilot | 39 | Package: python3-autopilot |
1205 | 41 | Architecture: all | 40 | Architecture: all |
1208 | 42 | Depends: gir1.2-ubuntu-app-launch-3 | gir1.2-ubuntu-app-launch-2, | 41 | Depends: libjs-jquery, |
1207 | 43 | libjs-jquery, | ||
1209 | 44 | libjs-underscore, | 42 | libjs-underscore, |
1210 | 45 | mir-utils, | 43 | mir-utils, |
1211 | 46 | python3-dateutil, | 44 | python3-dateutil, |
The proposed change looks reasonable to me, however I do not use autopilot so I'm probably not the best person to review this. I will approve but won't merge.