Merge ~xnox/autopilot:master into autopilot: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)
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.

To post a comment you must log in.
Revision history for this message
Paride Legovini (paride) wrote :

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.

review: Approve

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
diff --git a/autopilot/application/__init__.py b/autopilot/application/__init__.py
index 8ebda84..efaac1e 100644
--- a/autopilot/application/__init__.py
+++ b/autopilot/application/__init__.py
@@ -20,15 +20,11 @@
20"""Base package for application launching and environment management."""20"""Base package for application launching and environment management."""
2121
22from autopilot.application._launcher import (22from autopilot.application._launcher import (
23 ClickApplicationLauncher,
24 get_application_launcher_wrapper,23 get_application_launcher_wrapper,
25 NormalApplicationLauncher,24 NormalApplicationLauncher,
26 UpstartApplicationLauncher,
27)25)
2826
29__all__ = [27__all__ = [
30 'ClickApplicationLauncher',
31 'NormalApplicationLauncher',28 'NormalApplicationLauncher',
32 'UpstartApplicationLauncher',
33 'get_application_launcher_wrapper',29 'get_application_launcher_wrapper',
34]30]
diff --git a/autopilot/application/_launcher.py b/autopilot/application/_launcher.py
index 8ec391f..72fef39 100644
--- a/autopilot/application/_launcher.py
+++ b/autopilot/application/_launcher.py
@@ -20,12 +20,7 @@
20"""Base module for application launchers."""20"""Base module for application launchers."""
2121
22import fixtures22import fixtures
23from gi import require_version23from gi.repository import GLib
24try:
25 require_version('UbuntuAppLaunch', '3')
26except ValueError:
27 require_version('UbuntuAppLaunch', '2')
28from gi.repository import GLib, UbuntuAppLaunch
2924
30import json25import json
31import logging26import logging
@@ -81,233 +76,6 @@ class ApplicationLauncher(FixtureWithDirectAddDetail):
81 raise NotImplementedError("Sub-classes must implement this method.")76 raise NotImplementedError("Sub-classes must implement this method.")
8277
8378
84class UpstartApplicationLauncher(ApplicationLauncher):
85
86 """A launcher class that launches applications with UpstartAppLaunch."""
87 __doc__ += ApplicationLauncher.__doc__
88
89 Timeout = object()
90 Failed = object()
91 Started = object()
92 Stopped = object()
93
94 def launch(self, app_id, app_uris=[]):
95 """Launch an application with upstart.
96
97 This method launches an application via the ``upstart-app-launch``
98 library, on platforms that support it.
99
100 Usage is similar to NormalApplicationLauncher::
101
102 from autopilot.application import UpstartApplicationLauncher
103 launcher = UpstartApplicationLauncher()
104 launcher.setUp()
105 app_proxy = launcher.launch('gallery-app')
106
107 :param app_id: name of the application to launch
108 :param app_uris: list of separate application uris to launch
109 :raises RuntimeError: If the specified application cannot be launched.
110
111 :returns: proxy object for the launched package application
112
113 """
114 if isinstance(app_uris, str):
115 app_uris = [app_uris]
116 if isinstance(app_uris, bytes):
117 app_uris = [app_uris.decode()]
118 _logger.info(
119 "Attempting to launch application '%s' with URIs '%s' via "
120 "upstart-app-launch",
121 app_id,
122 ','.join(app_uris)
123 )
124 state = {}
125 state['loop'] = self._get_glib_loop()
126 state['expected_app_id'] = app_id
127 state['message'] = ''
128
129 UbuntuAppLaunch.observer_add_app_failed(self._on_failed, state)
130 UbuntuAppLaunch.observer_add_app_started(self._on_started, state)
131 UbuntuAppLaunch.observer_add_app_focus(self._on_started, state)
132 GLib.timeout_add_seconds(10.0, self._on_timeout, state)
133
134 self._launch_app(app_id, app_uris)
135 state['loop'].run()
136 UbuntuAppLaunch.observer_delete_app_failed(self._on_failed)
137 UbuntuAppLaunch.observer_delete_app_started(self._on_started)
138 UbuntuAppLaunch.observer_delete_app_focus(self._on_started)
139 self._maybe_add_application_cleanups(state)
140 self._check_status_error(
141 state.get('status', None),
142 state.get('message', '')
143 )
144 pid = self._get_pid_for_launched_app(app_id)
145
146 return self._get_proxy_object(pid)
147
148 def _get_proxy_object(self, pid):
149 return get_proxy_object_for_existing_process(
150 dbus_bus=self.dbus_bus,
151 emulator_base=self.proxy_base,
152 pid=pid
153 )
154
155 @staticmethod
156 def _on_failed(launched_app_id, failure_type, state):
157 if launched_app_id == state['expected_app_id']:
158 if failure_type == UbuntuAppLaunch.AppFailed.CRASH:
159 state['message'] = 'Application crashed.'
160 elif failure_type == UbuntuAppLaunch.AppFailed.START_FAILURE:
161 state['message'] = 'Application failed to start.'
162 state['status'] = UpstartApplicationLauncher.Failed
163 state['loop'].quit()
164
165 @staticmethod
166 def _on_started(launched_app_id, state):
167 if launched_app_id == state['expected_app_id']:
168 state['status'] = UpstartApplicationLauncher.Started
169 state['loop'].quit()
170
171 @staticmethod
172 def _on_stopped(stopped_app_id, state):
173 if stopped_app_id == state['expected_app_id']:
174 state['status'] = UpstartApplicationLauncher.Stopped
175 state['loop'].quit()
176
177 @staticmethod
178 def _on_timeout(state):
179 state['status'] = UpstartApplicationLauncher.Timeout
180 state['loop'].quit()
181
182 def _maybe_add_application_cleanups(self, state):
183 if state.get('status', None) == UpstartApplicationLauncher.Started:
184 app_id = state['expected_app_id']
185 self.addCleanup(self._stop_application, app_id)
186 self.addCleanup(self._attach_application_log, app_id)
187
188 @staticmethod
189 def _get_user_unit_match(app_id):
190 return 'ubuntu-app-launch-*-%s-*.service' % app_id
191
192 def _attach_application_log(self, app_id):
193 j = journal.Reader()
194 j.log_level(journal.LOG_INFO)
195 j.add_match(_SYSTEMD_USER_UNIT=self._get_user_unit_match(app_id))
196 log_data = ''
197 for i in j:
198 log_data += str(i) + '\n'
199 if len(log_data) > 0:
200 self.caseAddDetail('Application Log (%s)' % app_id,
201 safe_text_content(log_data))
202
203 def _stop_application(self, app_id):
204 state = {}
205 state['loop'] = self._get_glib_loop()
206 state['expected_app_id'] = app_id
207
208 UbuntuAppLaunch.observer_add_app_stop(self._on_stopped, state)
209 GLib.timeout_add_seconds(10.0, self._on_timeout, state)
210
211 UbuntuAppLaunch.stop_application(app_id)
212 state['loop'].run()
213 UbuntuAppLaunch.observer_delete_app_stop(self._on_stopped)
214
215 if state.get('status', None) == UpstartApplicationLauncher.Timeout:
216 _logger.error(
217 "Timed out waiting for Application with app_id '%s' to stop.",
218 app_id
219 )
220
221 @staticmethod
222 def _get_glib_loop():
223 return GLib.MainLoop()
224
225 @staticmethod
226 def _get_pid_for_launched_app(app_id):
227 return UbuntuAppLaunch.get_primary_pid(app_id)
228
229 @staticmethod
230 def _launch_app(app_name, app_uris):
231 UbuntuAppLaunch.start_application_test(app_name, app_uris)
232
233 @staticmethod
234 def _check_status_error(status, extra_message=''):
235 message_parts = []
236 if status == UpstartApplicationLauncher.Timeout:
237 message_parts.append(
238 "Timed out while waiting for application to launch"
239 )
240 elif status == UpstartApplicationLauncher.Failed:
241 message_parts.append("Application Launch Failed")
242 if message_parts and extra_message:
243 message_parts.append(extra_message)
244 if message_parts:
245 raise RuntimeError(': '.join(message_parts))
246
247
248class AlreadyLaunchedUpstartLauncher(UpstartApplicationLauncher):
249 """Launcher that doesn't wait for a proxy object.
250
251 This is useful when you are 're-launching' an already running application
252 and it's state has changed to suspended.
253
254 """
255
256 def _get_proxy_object(self, pid):
257 # Don't wait for a proxy object
258 return None
259
260
261class ClickApplicationLauncher(UpstartApplicationLauncher):
262
263 """Fixture to manage launching a Click application."""
264 __doc__ += ApplicationLauncher.__doc__
265
266 def launch(self, package_id, app_name=None, app_uris=[]):
267 """Launch a click package application with introspection enabled.
268
269 This method takes care of launching a click package with introspection
270 exabled. You probably want to use this method if your application is
271 packaged in a click application, or is started via upstart.
272
273 Usage is similar to NormalApplicationLauncher.launch::
274
275 from autopilot.application import ClickApplicationLauncher
276 launcher = ClickApplicationLauncher()
277 launcher.setUp()
278 app_proxy = launcher.launch('com.ubuntu.dropping-letters')
279
280 :param package_id: The Click package name you want to launch. For
281 example: ``com.ubuntu.dropping-letters``
282 :param app_name: Currently, only one application can be packaged in a
283 click package, and this parameter can be left at None. If
284 specified, it should be the application name you wish to launch.
285 :param app_uris: Parameters used to launch the click package. This
286 parameter will be left empty if not used.
287
288 :raises RuntimeError: If the specified package_id cannot be found in
289 the click package manifest.
290 :raises RuntimeError: If the specified app_name cannot be found within
291 the specified click package.
292
293 :returns: proxy object for the launched package application
294
295 """
296 if isinstance(app_uris, str):
297 app_uris = [app_uris]
298 if isinstance(app_uris, bytes):
299 app_uris = [app_uris.decode()]
300 _logger.info(
301 "Attempting to launch click application '%s' from click package "
302 " '%s' and URIs '%s'",
303 app_name if app_name is not None else "(default)",
304 package_id,
305 ','.join(app_uris)
306 )
307 app_id = _get_click_app_id(package_id, app_name)
308 return super().launch(app_id, app_uris)
309
310
311class NormalApplicationLauncher(ApplicationLauncher):79class NormalApplicationLauncher(ApplicationLauncher):
31280
313 """Fixture to manage launching an application."""81 """Fixture to manage launching an application."""
@@ -470,35 +238,6 @@ def launch_process(application, args, capture_output=False, **kwargs):
470 return process238 return process
471239
472240
473def _get_click_app_id(package_id, app_name=None):
474 for pkg in _get_click_manifest():
475 if pkg['name'] == package_id:
476 if app_name is None:
477 # py3 dict.keys isn't indexable.
478 app_name = list(pkg['hooks'].keys())[0]
479 elif app_name not in pkg['hooks']:
480 raise RuntimeError(
481 "Application '{}' is not present within the click "
482 "package '{}'.".format(app_name, package_id))
483
484 return "{0}_{1}_{2}".format(package_id, app_name, pkg['version'])
485 raise RuntimeError(
486 "Unable to find package '{}' in the click manifest."
487 .format(package_id)
488 )
489
490
491def _get_click_manifest():
492 """Return the click package manifest as a python list."""
493 # get the whole click package manifest every time - it seems fast enough
494 # but this is a potential optimisation point for the future:
495 click_manifest_str = subprocess.check_output(
496 ["click", "list", "--manifest"],
497 universal_newlines=True
498 )
499 return json.loads(click_manifest_str)
500
501
502def _get_application_environment(app_type=None, app_path=None):241def _get_application_environment(app_type=None, app_path=None):
503 if app_type is None and app_path is None:242 if app_type is None and app_path is None:
504 raise ValueError("Must specify either app_type or app_path.")243 raise ValueError("Must specify either app_type or app_path.")
diff --git a/autopilot/testcase.py b/autopilot/testcase.py
index af8c2d4..8360eea 100644
--- a/autopilot/testcase.py
+++ b/autopilot/testcase.py
@@ -57,9 +57,7 @@ from testtools.testcase import _ExpectedFailure
57from unittest.case import SkipTest57from unittest.case import SkipTest
5858
59from autopilot.application import (59from autopilot.application import (
60 ClickApplicationLauncher,
61 NormalApplicationLauncher,60 NormalApplicationLauncher,
62 UpstartApplicationLauncher,
63)61)
64from autopilot.display import Display, get_screenshot_data62from autopilot.display import Display, get_screenshot_data
65from autopilot.globals import get_debug_profile_fixture, get_test_timeout63from autopilot.globals import get_debug_profile_fixture, get_test_timeout
@@ -285,78 +283,6 @@ class AutopilotTestCase(TestWithScenarios, TestCase, KeybindingsHelper):
285 )283 )
286 return launcher.launch(application, arguments, **launch_args)284 return launcher.launch(application, arguments, **launch_args)
287285
288 def launch_click_package(self, package_id, app_name=None, app_uris=[],
289 **kwargs):
290 """Launch a click package application with introspection enabled.
291
292 This method takes care of launching a click package with introspection
293 exabled. You probably want to use this method if your application is
294 packaged in a click application, or is started via upstart.
295
296 Usage is similar to the
297 :py:meth:`AutopilotTestCase.launch_test_application`::
298
299 app_proxy = self.launch_click_package(
300 "com.ubuntu.dropping-letters"
301 )
302
303 :param package_id: The Click package name you want to launch. For
304 example: ``com.ubuntu.dropping-letters``
305 :param app_name: Currently, only one application can be packaged in a
306 click package, and this parameter can be left at None. If
307 specified, it should be the application name you wish to launch.
308 :param app_uris: Parameters used to launch the click package. This
309 parameter will be left empty if not used.
310
311 :keyword emulator_base: If set, specifies the base class to be used for
312 all emulators for this loaded application.
313
314 :raises RuntimeError: If the specified package_id cannot be found in
315 the click package manifest.
316 :raises RuntimeError: If the specified app_name cannot be found within
317 the specified click package.
318
319 :returns: proxy object for the launched package application
320
321 """
322 launcher = self.useFixture(
323 ClickApplicationLauncher(
324 case_addDetail=self.addDetailUniqueName,
325 **kwargs
326 )
327 )
328 return launcher.launch(package_id, app_name, app_uris)
329
330 def launch_upstart_application(self, application_name, uris=[],
331 launcher_class=UpstartApplicationLauncher,
332 **kwargs):
333 """Launch an application with upstart.
334
335 This method launched an application via the ``ubuntu-app-launch``
336 library, on platforms that support it.
337
338 Usage is similar to the
339 :py:meth:`AutopilotTestCase.launch_test_application`::
340
341 app_proxy = self.launch_upstart_application("gallery-app")
342
343 :param application_name: The name of the application to launch.
344 :param launcher_class: The application launcher class to use. Useful if
345 you need to overwrite the default to do something custom (i.e. using
346 AlreadyLaunchedUpstartLauncher)
347 :keyword emulator_base: If set, specifies the base class to be used for
348 all emulators for this loaded application.
349
350 :raises RuntimeError: If the specified application cannot be launched.
351 """
352 launcher = self.useFixture(
353 launcher_class(
354 case_addDetail=self.addDetailUniqueName,
355 **kwargs
356 )
357 )
358 return launcher.launch(application_name, uris)
359
360 def _compare_system_with_app_snapshot(self):286 def _compare_system_with_app_snapshot(self):
361 """Compare the currently running application with the last snapshot.287 """Compare the currently running application with the last snapshot.
362288
diff --git a/autopilot/tests/functional/test_ap_apps.py b/autopilot/tests/functional/test_ap_apps.py
index 71d5928..de66fa0 100644
--- a/autopilot/tests/functional/test_ap_apps.py
+++ b/autopilot/tests/functional/test_ap_apps.py
@@ -37,7 +37,6 @@ from fixtures import EnvironmentVariable
3737
38from autopilot.application import (38from autopilot.application import (
39 NormalApplicationLauncher,39 NormalApplicationLauncher,
40 UpstartApplicationLauncher,
41)40)
42from autopilot.exceptions import ProcessSearchError41from autopilot.exceptions import ProcessSearchError
43from autopilot.process import ProcessManager42from autopilot.process import ProcessManager
@@ -296,12 +295,6 @@ class QtTests(ApplicationTests, QmlTestMixin):
296 )295 )
297 self.assertTrue(app_proxy is not None)296 self.assertTrue(app_proxy is not None)
298297
299 def test_can_launch_upstart_app(self):
300 path = self.get_qml_viewer_app_path()
301 fixture = self.useFixture(TempDesktopFile(exec_=path,))
302 launcher = self.useFixture(UpstartApplicationLauncher())
303 launcher.launch(fixture.get_desktop_file_id())
304
305 @skipIf(model() != "Desktop", "Only suitable on Desktop (Qt4)")298 @skipIf(model() != "Desktop", "Only suitable on Desktop (Qt4)")
306 def test_can_launch_normal_qt_script(self):299 def test_can_launch_normal_qt_script(self):
307 path = self.write_script(dedent("""\300 path = self.write_script(dedent("""\
diff --git a/autopilot/tests/functional/test_application_launcher.py b/autopilot/tests/functional/test_application_launcher.py
index f9c1e48..7e16a0c 100644
--- a/autopilot/tests/functional/test_application_launcher.py
+++ b/autopilot/tests/functional/test_application_launcher.py
@@ -44,53 +44,3 @@ class AutopilotTestCaseClassTests(TestCase):
44 uf.return_value.launch.assert_called_once_with('a', ('b', 'c'))44 uf.return_value.launch.assert_called_once_with('a', ('b', 'c'))
45 self.assertEqual(result, uf.return_value.launch.return_value)45 self.assertEqual(result, uf.return_value.launch.return_value)
4646
47 @patch('autopilot.testcase.ClickApplicationLauncher')
48 def test_launch_click_package(self, cal):
49 class LauncherTest(AutopilotTestCase):
50
51 """Test launchers."""
52
53 def test_anything(self):
54 pass
55
56 test_case = LauncherTest('test_anything')
57 with patch.object(test_case, 'useFixture') as uf:
58 result = test_case.launch_click_package('a', 'b', ['c', 'd'])
59 uf.assert_called_once_with(cal.return_value)
60 uf.return_value.launch.assert_called_once_with(
61 'a', 'b', ['c', 'd']
62 )
63 self.assertEqual(result, uf.return_value.launch.return_value)
64
65 @patch('autopilot.testcase.UpstartApplicationLauncher')
66 def test_launch_upstart_application_defaults(self, ual):
67 class LauncherTest(AutopilotTestCase):
68
69 """Test launchers."""
70
71 def test_anything(self):
72 pass
73
74 test_case = LauncherTest('test_anything')
75 with patch.object(test_case, 'useFixture') as uf:
76 result = test_case.launch_upstart_application(
77 'a', ['b'], launcher_class=ual
78 )
79 uf.assert_called_once_with(ual.return_value)
80 uf.return_value.launch.assert_called_once_with('a', ['b'])
81 self.assertEqual(result, uf.return_value.launch.return_value)
82
83 def test_launch_upstart_application_custom_launcher(self):
84 class LauncherTest(AutopilotTestCase):
85
86 """Test launchers."""
87
88 def test_anything(self):
89 pass
90
91 test_case = LauncherTest('test_anything')
92 self.assertRaises(
93 NotImplementedError,
94 test_case.launch_upstart_application,
95 'a', ['b'], launcher_class=_launcher.ApplicationLauncher
96 )
diff --git a/autopilot/tests/unit/test_application_launcher.py b/autopilot/tests/unit/test_application_launcher.py
index 3de7819..af92c42 100644
--- a/autopilot/tests/unit/test_application_launcher.py
+++ b/autopilot/tests/unit/test_application_launcher.py
@@ -36,9 +36,7 @@ from testtools.content import text_content
36from unittest.mock import MagicMock, Mock, patch36from unittest.mock import MagicMock, Mock, patch
3737
38from autopilot.application import (38from autopilot.application import (
39 ClickApplicationLauncher,
40 NormalApplicationLauncher,39 NormalApplicationLauncher,
41 UpstartApplicationLauncher,
42)40)
43from autopilot.application._environment import (41from autopilot.application._environment import (
44 GtkApplicationEnvironment,42 GtkApplicationEnvironment,
@@ -53,8 +51,6 @@ from autopilot.application._launcher import (
53 _get_app_env_from_string_hint,51 _get_app_env_from_string_hint,
54 _get_application_environment,52 _get_application_environment,
55 _get_application_path,53 _get_application_path,
56 _get_click_app_id,
57 _get_click_manifest,
58 _is_process_running,54 _is_process_running,
59 _kill_process,55 _kill_process,
60)56)
@@ -261,635 +257,6 @@ class NormalApplicationLauncherTests(TestCase):
261 )257 )
262258
263259
264class ClickApplicationLauncherTests(TestCase):
265
266 def test_raises_exception_on_unknown_kwargs(self):
267 self.assertThat(
268 lambda: ClickApplicationLauncher(self.addDetail, unknown=True),
269 raises(TypeError("__init__() got an unexpected keyword argument "
270 "'unknown'"))
271 )
272
273 @patch('autopilot.application._launcher._get_click_app_id')
274 def test_handle_string(self, gcai):
275 class FakeUpstartBase(_l.ApplicationLauncher):
276 launch_call_args = []
277
278 def launch(self, *args):
279 FakeUpstartBase.launch_call_args = list(args)
280
281 patcher = patch.object(
282 _l.ClickApplicationLauncher,
283 '__bases__',
284 (FakeUpstartBase,)
285 )
286 token = self.getUniqueString()
287 with patcher:
288 # Prevent mock from trying to delete __bases__
289 patcher.is_local = True
290 launcher = self.useFixture(
291 _l.ClickApplicationLauncher())
292 launcher.launch('', '', token)
293 self.assertEqual(
294 FakeUpstartBase.launch_call_args,
295 [gcai.return_value, [token]])
296
297 @patch('autopilot.application._launcher._get_click_app_id')
298 def test_handle_bytes(self, gcai):
299 class FakeUpstartBase(_l.ApplicationLauncher):
300 launch_call_args = []
301
302 def launch(self, *args):
303 FakeUpstartBase.launch_call_args = list(args)
304
305 patcher = patch.object(
306 _l.ClickApplicationLauncher,
307 '__bases__',
308 (FakeUpstartBase,)
309 )
310 token = self.getUniqueString()
311 with patcher:
312 # Prevent mock from trying to delete __bases__
313 patcher.is_local = True
314 launcher = self.useFixture(
315 _l.ClickApplicationLauncher())
316 launcher.launch('', '', token.encode())
317 self.assertEqual(
318 FakeUpstartBase.launch_call_args,
319 [gcai.return_value, [token]])
320
321 @patch('autopilot.application._launcher._get_click_app_id')
322 def test_handle_list(self, gcai):
323 class FakeUpstartBase(_l.ApplicationLauncher):
324 launch_call_args = []
325
326 def launch(self, *args):
327 FakeUpstartBase.launch_call_args = list(args)
328
329 patcher = patch.object(
330 _l.ClickApplicationLauncher,
331 '__bases__',
332 (FakeUpstartBase,)
333 )
334 token = self.getUniqueString()
335 with patcher:
336 # Prevent mock from trying to delete __bases__
337 patcher.is_local = True
338 launcher = self.useFixture(
339 _l.ClickApplicationLauncher())
340 launcher.launch('', '', [token])
341 self.assertEqual(
342 FakeUpstartBase.launch_call_args,
343 [gcai.return_value, [token]])
344
345 @patch('autopilot.application._launcher._get_click_app_id')
346 def test_call_get_click_app_id(self, gcai):
347 class FakeUpstartBase(_l.ApplicationLauncher):
348 launch_call_args = []
349
350 def launch(self, *args):
351 FakeUpstartBase.launch_call_args = list(args)
352
353 patcher = patch.object(
354 _l.ClickApplicationLauncher,
355 '__bases__',
356 (FakeUpstartBase,)
357 )
358 token_a = self.getUniqueString()
359 token_b = self.getUniqueString()
360 with patcher:
361 # Prevent mock from trying to delete __bases__
362 patcher.is_local = True
363 launcher = self.useFixture(
364 _l.ClickApplicationLauncher())
365 launcher.launch(token_a, token_b)
366 gcai.assert_called_once_with(token_a, token_b)
367
368 @patch('autopilot.application._launcher._get_click_app_id')
369 def test_call_upstart_launch(self, gcai):
370 class FakeUpstartBase(_l.ApplicationLauncher):
371 launch_call_args = []
372
373 def launch(self, *args):
374 FakeUpstartBase.launch_call_args = list(args)
375
376 patcher = patch.object(
377 _l.ClickApplicationLauncher,
378 '__bases__',
379 (FakeUpstartBase,)
380 )
381 with patcher:
382 # Prevent mock from trying to delete __bases__
383 patcher.is_local = True
384 launcher = self.useFixture(
385 _l.ClickApplicationLauncher())
386 launcher.launch('', '')
387 self.assertEqual(launcher.launch_call_args,
388 [gcai.return_value, []])
389
390
391class ClickFunctionTests(TestCase):
392
393 def test_get_click_app_id_raises_runtimeerror_on_empty_manifest(self):
394 """_get_click_app_id must raise a RuntimeError if the requested
395 package id is not found in the click manifest.
396
397 """
398 with patch.object(_l, '_get_click_manifest', return_value=[]):
399 self.assertThat(
400 lambda: _get_click_app_id("com.autopilot.testing"),
401 raises(
402 RuntimeError(
403 "Unable to find package 'com.autopilot.testing' in "
404 "the click manifest."
405 )
406 )
407 )
408
409 def test_get_click_app_id_raises_runtimeerror_on_missing_package(self):
410 with patch.object(_l, '_get_click_manifest') as cm:
411 cm.return_value = [
412 {
413 'name': 'com.not.expected.name',
414 'hooks': {'bar': {}}, 'version': '1.0'
415 }
416 ]
417
418 self.assertThat(
419 lambda: _get_click_app_id("com.autopilot.testing"),
420 raises(
421 RuntimeError(
422 "Unable to find package 'com.autopilot.testing' in "
423 "the click manifest."
424 )
425 )
426 )
427
428 def test_get_click_app_id_raises_runtimeerror_on_wrong_app(self):
429 """get_click_app_id must raise a RuntimeError if the requested
430 application is not found within the click package.
431
432 """
433 with patch.object(_l, '_get_click_manifest') as cm:
434 cm.return_value = [{'name': 'com.autopilot.testing', 'hooks': {}}]
435
436 self.assertThat(
437 lambda: _get_click_app_id("com.autopilot.testing", "bar"),
438 raises(
439 RuntimeError(
440 "Application 'bar' is not present within the click "
441 "package 'com.autopilot.testing'."
442 )
443 )
444 )
445
446 def test_get_click_app_id_returns_id(self):
447 with patch.object(_l, '_get_click_manifest') as cm:
448 cm.return_value = [
449 {
450 'name': 'com.autopilot.testing',
451 'hooks': {'bar': {}}, 'version': '1.0'
452 }
453 ]
454
455 self.assertThat(
456 _get_click_app_id("com.autopilot.testing", "bar"),
457 Equals("com.autopilot.testing_bar_1.0")
458 )
459
460 def test_get_click_app_id_returns_id_without_appid_passed(self):
461 with patch.object(_l, '_get_click_manifest') as cm:
462 cm.return_value = [
463 {
464 'name': 'com.autopilot.testing',
465 'hooks': {'bar': {}}, 'version': '1.0'
466 }
467 ]
468
469 self.assertThat(
470 _get_click_app_id("com.autopilot.testing"),
471 Equals("com.autopilot.testing_bar_1.0")
472 )
473
474
475class UpstartApplicationLauncherTests(TestCase):
476
477 def test_can_construct_UpstartApplicationLauncher(self):
478 UpstartApplicationLauncher(self.addDetail)
479
480 def test_raises_exception_on_unknown_kwargs(self):
481 self.assertThat(
482 lambda: UpstartApplicationLauncher(self.addDetail, unknown=True),
483 raises(TypeError("__init__() got an unexpected keyword argument "
484 "'unknown'"))
485 )
486
487 def test_on_failed_only_sets_status_on_correct_app_id(self):
488 state = {
489 'expected_app_id': 'gedit',
490 }
491
492 UpstartApplicationLauncher._on_failed('some_game', None, state)
493
494 self.assertThat(state, Not(Contains('status')))
495
496 def assertFunctionSetsCorrectStateAndQuits(self, observer, expected_state):
497 """Assert that the observer observer sets the correct state id.
498
499 :param observer: The observer callable you want to test.
500 :param expected_state: The state id the observer callable must set.
501
502 """
503 expected_app_id = self.getUniqueString()
504 state = {
505 'expected_app_id': expected_app_id,
506 'loop': Mock()
507 }
508
509 if observer == UpstartApplicationLauncher._on_failed:
510 observer(expected_app_id, None, state)
511 elif observer == UpstartApplicationLauncher._on_started or \
512 observer == UpstartApplicationLauncher._on_stopped:
513 observer(expected_app_id, state)
514 else:
515 observer(state)
516
517 self.assertThat(
518 state['status'],
519 Equals(expected_state)
520 )
521 state['loop'].quit.assert_called_once_with()
522
523 def test_on_failed_sets_status_with_correct_app_id(self):
524 self.assertFunctionSetsCorrectStateAndQuits(
525 UpstartApplicationLauncher._on_failed,
526 UpstartApplicationLauncher.Failed
527 )
528
529 def test_on_started_sets_status_with_correct_app_id(self):
530 self.assertFunctionSetsCorrectStateAndQuits(
531 UpstartApplicationLauncher._on_started,
532 UpstartApplicationLauncher.Started
533 )
534
535 def test_on_timeout_sets_status_and_exits_loop(self):
536 self.assertFunctionSetsCorrectStateAndQuits(
537 UpstartApplicationLauncher._on_timeout,
538 UpstartApplicationLauncher.Timeout
539 )
540
541 def test_on_started_only_sets_status_on_correct_app_id(self):
542 state = {
543 'expected_app_id': 'gedit',
544 }
545
546 UpstartApplicationLauncher._on_started('some_game', state)
547
548 self.assertThat(state, Not(Contains('status')))
549
550 def test_on_stopped_only_sets_status_on_correct_app_id(self):
551 state = {
552 'expected_app_id': 'gedit',
553 }
554
555 UpstartApplicationLauncher._on_stopped('some_game', state)
556 self.assertThat(state, Not(Contains('status')))
557
558 def test_on_stopped_sets_status_and_exits_loop(self):
559 self.assertFunctionSetsCorrectStateAndQuits(
560 UpstartApplicationLauncher._on_stopped,
561 UpstartApplicationLauncher.Stopped
562 )
563
564 def test_get_pid_calls_upstart_module(self):
565 expected_return = self.getUniqueInteger()
566 with patch.object(_l, 'UbuntuAppLaunch') as mock_ual:
567 mock_ual.get_primary_pid.return_value = expected_return
568 observed = UpstartApplicationLauncher._get_pid_for_launched_app(
569 'gedit'
570 )
571
572 mock_ual.get_primary_pid.assert_called_once_with('gedit')
573 self.assertThat(expected_return, Equals(observed))
574
575 def test_launch_app_calls_upstart_module(self):
576 with patch.object(_l, 'UbuntuAppLaunch') as mock_ual:
577 UpstartApplicationLauncher._launch_app(
578 'gedit',
579 ['some', 'uris']
580 )
581
582 mock_ual.start_application_test.assert_called_once_with(
583 'gedit',
584 ['some', 'uris']
585 )
586
587 def test_check_error_raises_RuntimeError_on_timeout(self):
588 fn = lambda: UpstartApplicationLauncher._check_status_error(
589 UpstartApplicationLauncher.Timeout
590 )
591 self.assertThat(
592 fn,
593 raises(
594 RuntimeError(
595 "Timed out while waiting for application to launch"
596 )
597 )
598 )
599
600 def test_check_error_raises_RuntimeError_on_failure(self):
601 fn = lambda: UpstartApplicationLauncher._check_status_error(
602 UpstartApplicationLauncher.Failed
603 )
604 self.assertThat(
605 fn,
606 raises(
607 RuntimeError(
608 "Application Launch Failed"
609 )
610 )
611 )
612
613 def test_check_error_raises_RuntimeError_with_extra_message(self):
614 fn = lambda: UpstartApplicationLauncher._check_status_error(
615 UpstartApplicationLauncher.Failed,
616 "extra message"
617 )
618 self.assertThat(
619 fn,
620 raises(
621 RuntimeError(
622 "Application Launch Failed: extra message"
623 )
624 )
625 )
626
627 def test_check_error_does_nothing_on_None(self):
628 UpstartApplicationLauncher._check_status_error(None)
629
630 def test_get_loop_returns_glib_mainloop_instance(self):
631 loop = UpstartApplicationLauncher._get_glib_loop()
632 self.assertThat(loop, IsInstance(GLib.MainLoop))
633
634 @patch('autopilot.application._launcher.UbuntuAppLaunch')
635 @patch('autopilot.application._launcher.'
636 'get_proxy_object_for_existing_process')
637 def test_handle_string(self, gpofep, ual):
638 launcher = UpstartApplicationLauncher()
639 token_a = self.getUniqueString()
640 token_b = self.getUniqueString()
641 with patch.object(launcher, '_launch_app') as la:
642 with patch.object(launcher, '_get_pid_for_launched_app'):
643 with patch.object(launcher, '_get_glib_loop'):
644 launcher.launch(token_a, token_b)
645 la.assert_called_once_with(token_a, [token_b])
646
647 @patch('autopilot.application._launcher.UbuntuAppLaunch')
648 @patch('autopilot.application._launcher.'
649 'get_proxy_object_for_existing_process')
650 def test_handle_bytes(self, gpofep, ual):
651 launcher = UpstartApplicationLauncher()
652 token_a = self.getUniqueString()
653 token_b = self.getUniqueString()
654 with patch.object(launcher, '_launch_app') as la:
655 with patch.object(launcher, '_get_pid_for_launched_app'):
656 with patch.object(launcher, '_get_glib_loop'):
657 launcher.launch(token_a, token_b.encode())
658 la.assert_called_once_with(token_a, [token_b])
659
660 @patch('autopilot.application._launcher.UbuntuAppLaunch')
661 @patch('autopilot.application._launcher.'
662 'get_proxy_object_for_existing_process')
663 def test_handle_list(self, gpofep, ual):
664 launcher = UpstartApplicationLauncher()
665 token_a = self.getUniqueString()
666 token_b = self.getUniqueString()
667 with patch.object(launcher, '_launch_app') as la:
668 with patch.object(launcher, '_get_pid_for_launched_app'):
669 with patch.object(launcher, '_get_glib_loop'):
670 launcher.launch(token_a, [token_b])
671 la.assert_called_once_with(token_a, [token_b])
672
673 @patch('autopilot.application._launcher.UbuntuAppLaunch')
674 @patch('autopilot.application._launcher.'
675 'get_proxy_object_for_existing_process')
676 def test_calls_get_pid(self, gpofep, ual):
677 launcher = UpstartApplicationLauncher()
678 token = self.getUniqueString()
679 with patch.object(launcher, '_launch_app'):
680 with patch.object(launcher, '_get_pid_for_launched_app') as gp:
681 with patch.object(launcher, '_get_glib_loop'):
682 launcher.launch(token)
683 gp.assert_called_once_with(token)
684
685 @patch('autopilot.application._launcher.UbuntuAppLaunch')
686 @patch('autopilot.application._launcher.'
687 'get_proxy_object_for_existing_process')
688 def test_gets_correct_proxy_object(self, gpofep, ual):
689 launcher = UpstartApplicationLauncher()
690 with patch.object(launcher, '_launch_app'):
691 with patch.object(launcher, '_get_pid_for_launched_app') as gp:
692 with patch.object(launcher, '_get_glib_loop'):
693 launcher.launch('')
694 gpofep.assert_called_once_with(pid=gp.return_value,
695 emulator_base=None,
696 dbus_bus='session')
697
698 @patch('autopilot.application._launcher.UbuntuAppLaunch')
699 @patch('autopilot.application._launcher.'
700 'get_proxy_object_for_existing_process')
701 def test_returns_proxy_object(self, gpofep, ual):
702 launcher = UpstartApplicationLauncher()
703 with patch.object(launcher, '_launch_app'):
704 with patch.object(launcher, '_get_pid_for_launched_app'):
705 with patch.object(launcher, '_get_glib_loop'):
706 result = launcher.launch('')
707 self.assertEqual(result, gpofep.return_value)
708
709 @patch('autopilot.application._launcher.UbuntuAppLaunch')
710 @patch('autopilot.application._launcher.'
711 'get_proxy_object_for_existing_process')
712 def test_calls_get_glib_loop(self, gpofep, ual):
713 launcher = UpstartApplicationLauncher()
714 with patch.object(launcher, '_launch_app'):
715 with patch.object(launcher, '_get_pid_for_launched_app'):
716 with patch.object(launcher, '_get_glib_loop') as ggl:
717 launcher.launch('')
718 ggl.assert_called_once_with()
719
720 def assertFailedObserverSetsExtraMessage(self, fail_type, expected_msg):
721 """Assert that the on_failed observer must set the expected message
722 for a particular failure_type."""
723 expected_app_id = self.getUniqueString()
724 state = {
725 'expected_app_id': expected_app_id,
726 'loop': Mock()
727 }
728 UpstartApplicationLauncher._on_failed(
729 expected_app_id,
730 fail_type,
731 state
732 )
733 self.assertEqual(expected_msg, state['message'])
734
735 def test_on_failed_sets_message_for_app_crash(self):
736 self.assertFailedObserverSetsExtraMessage(
737 _l.UbuntuAppLaunch.AppFailed.CRASH,
738 'Application crashed.'
739 )
740
741 def test_on_failed_sets_message_for_app_start_failure(self):
742 self.assertFailedObserverSetsExtraMessage(
743 _l.UbuntuAppLaunch.AppFailed.START_FAILURE,
744 'Application failed to start.'
745 )
746
747 def test_add_application_cleanups_adds_both_cleanup_actions(self):
748 token = self.getUniqueString()
749 state = {
750 'status': UpstartApplicationLauncher.Started,
751 'expected_app_id': token,
752 }
753 launcher = UpstartApplicationLauncher(Mock())
754 launcher.setUp()
755 launcher._maybe_add_application_cleanups(state)
756 self.assertThat(
757 launcher._cleanups._cleanups,
758 Contains(
759 (launcher._attach_application_log, (token,), {})
760 )
761 )
762 self.assertThat(
763 launcher._cleanups._cleanups,
764 Contains(
765 (launcher._stop_application, (token,), {})
766 )
767 )
768
769 def test_add_application_cleanups_does_nothing_when_app_timedout(self):
770 state = {
771 'status': UpstartApplicationLauncher.Timeout,
772 }
773 launcher = UpstartApplicationLauncher(Mock())
774 launcher.setUp()
775 launcher._maybe_add_application_cleanups(state)
776 self.assertThat(launcher._cleanups._cleanups, HasLength(0))
777
778 def test_add_application_cleanups_does_nothing_when_app_failed(self):
779 state = {
780 'status': UpstartApplicationLauncher.Failed,
781 }
782 launcher = UpstartApplicationLauncher(Mock())
783 launcher.setUp()
784 launcher._maybe_add_application_cleanups(state)
785 self.assertThat(launcher._cleanups._cleanups, HasLength(0))
786
787 def test_attach_application_log_does_nothing_wth_no_log_specified(self):
788 app_id = self.getUniqueString()
789 case_addDetail = Mock()
790 launcher = UpstartApplicationLauncher(case_addDetail)
791 j = MagicMock(spec=_l.journal.Reader)
792 with patch.object(_l.journal, 'Reader', return_value=j):
793 launcher._attach_application_log(app_id)
794 expected = launcher._get_user_unit_match(app_id)
795 j.add_match.assert_called_once_with(_SYSTEMD_USER_UNIT=expected)
796 self.assertEqual(0, case_addDetail.call_count)
797
798 def test_attach_application_log_attaches_log(self):
799 token = self.getUniqueString()
800 case_addDetail = Mock()
801 launcher = UpstartApplicationLauncher(case_addDetail)
802 app_id = self.getUniqueString()
803 j = MagicMock(spec=_l.journal.Reader)
804 j.__iter__ = lambda x: iter([token])
805 with patch.object(_l.journal, 'Reader', return_value=j):
806 launcher._attach_application_log(app_id)
807
808 self.assertEqual(1, case_addDetail.call_count)
809 content_name, content_obj = case_addDetail.call_args[0]
810 self.assertEqual(
811 "Application Log (%s)" % app_id,
812 content_name
813 )
814 self.assertThat(content_obj.as_text(), Contains(token))
815
816 def test_stop_adds_app_stopped_observer(self):
817 mock_add_detail = Mock()
818 mock_glib_loop = Mock()
819 patch_get_loop = patch.object(
820 UpstartApplicationLauncher,
821 '_get_glib_loop',
822 new=mock_glib_loop,
823 )
824 mock_UAL = Mock()
825 patch_UAL = patch.object(_l, 'UbuntuAppLaunch', new=mock_UAL)
826 launcher = UpstartApplicationLauncher(mock_add_detail)
827 app_id = self.getUniqueString()
828 with ExitStack() as patches:
829 patches.enter_context(patch_get_loop)
830 patches.enter_context(patch_UAL)
831
832 launcher._stop_application(app_id)
833 call_args = mock_UAL.observer_add_app_stop.call_args[0]
834 self.assertThat(
835 call_args[0],
836 Equals(UpstartApplicationLauncher._on_stopped)
837 )
838 self.assertThat(call_args[1]['expected_app_id'], Equals(app_id))
839
840 def test_stop_calls_libUAL_stop_function(self):
841 mock_add_detail = Mock()
842 mock_glib_loop = Mock()
843 patch_get_loop = patch.object(
844 UpstartApplicationLauncher,
845 '_get_glib_loop',
846 new=mock_glib_loop,
847 )
848 mock_UAL = Mock()
849 patch_UAL = patch.object(_l, 'UbuntuAppLaunch', new=mock_UAL)
850 launcher = UpstartApplicationLauncher(mock_add_detail)
851 app_id = self.getUniqueString()
852 with ExitStack() as patches:
853 patches.enter_context(patch_get_loop)
854 patches.enter_context(patch_UAL)
855
856 launcher._stop_application(app_id)
857 mock_UAL.stop_application.assert_called_once_with(app_id)
858
859 def test_stop_logs_error_on_timeout(self):
860 mock_add_detail = Mock()
861 mock_glib_loop = Mock()
862 patch_get_loop = patch.object(
863 UpstartApplicationLauncher,
864 '_get_glib_loop',
865 new=mock_glib_loop,
866 )
867 mock_UAL = Mock()
868
869 # we replace the add_observer function with one that can set the
870 # tiemout state, so we can ibject the timeout condition within the
871 # glib loop. This is ugly, but necessary.
872 def fake_add_observer(fn, state):
873 state['status'] = UpstartApplicationLauncher.Timeout
874 mock_UAL.observer_add_app_stop = fake_add_observer
875 patch_UAL = patch.object(_l, 'UbuntuAppLaunch', new=mock_UAL)
876 launcher = UpstartApplicationLauncher(mock_add_detail)
877 app_id = self.getUniqueString()
878 mock_logger = Mock()
879 patch_logger = patch.object(_l, '_logger', new=mock_logger)
880 with ExitStack() as patches:
881 patches.enter_context(patch_get_loop)
882 patches.enter_context(patch_UAL)
883 patches.enter_context(patch_logger)
884
885 launcher._stop_application(app_id)
886
887 mock_logger.error.assert_called_once_with(
888 "Timed out waiting for Application with app_id '%s' to stop.",
889 app_id
890 )
891
892
893class ApplicationLauncherInternalTests(TestCase):260class ApplicationLauncherInternalTests(TestCase):
894261
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):
@@ -1098,24 +465,6 @@ class ApplicationLauncherInternalTests(TestCase):
1098 get_application_launcher_wrapper(""), Equals(None)465 get_application_launcher_wrapper(""), Equals(None)
1099 )466 )
1100467
1101 def test_get_click_manifest_returns_python_object(self):
1102 example_manifest = """
1103 [{
1104 "description": "Calculator application",
1105 "framework": "ubuntu-sdk-13.10",
1106 "hooks": {
1107 "calculator": {
1108 "apparmor": "apparmor/calculator.json",
1109 "desktop": "ubuntu-calculator-app.desktop"
1110 }
1111 },
1112 "icon": "calculator64.png"
1113 }]
1114 """
1115 with patch.object(_l.subprocess, 'check_output') as check_output:
1116 check_output.return_value = example_manifest
1117 self.assertThat(_get_click_manifest(), IsInstance(list))
1118
1119 @patch.object(_l.psutil, 'pid_exists')468 @patch.object(_l.psutil, 'pid_exists')
1120 def test_is_process_running_checks_with_pid(self, pid_exists):469 def test_is_process_running_checks_with_pid(self, pid_exists):
1121 pid_exists.return_value = True470 pid_exists.return_value = True
diff --git a/debian/changelog b/debian/changelog
index b1b6624..a951fae 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,16 @@
1autopilot (1.6.0+17.04.20170313-0ubuntu9) groovy; urgency=medium
2
3 * Drop upstart, click, ubuntu-app-launch support. All of those things
4 are removed from the Ubuntu Archive.
5
6 -- Dimitri John Ledkov <xnox@ubuntu.com> Thu, 04 Jun 2020 14:10:05 +0100
7
8autopilot (1.6.0+17.04.20170313-0ubuntu8) focal; urgency=medium
9
10 * No-change rebuild to drop python3.7.
11
12 -- Matthias Klose <doko@ubuntu.com> Tue, 18 Feb 2020 09:16:48 +0100
13
1autopilot (1.6.0+17.04.20170313-0ubuntu7) focal; urgency=medium14autopilot (1.6.0+17.04.20170313-0ubuntu7) focal; urgency=medium
215
3 * autopilot-vis has been removed, so removed test_vis_main which depends on16 * autopilot-vis has been removed, so removed test_vis_main which depends on
diff --git a/debian/control b/debian/control
index 30f73e8..27699b6 100644
--- a/debian/control
+++ b/debian/control
@@ -7,7 +7,6 @@ Build-Depends: debhelper (>= 9.0.0),
7 dh-python,7 dh-python,
8 gir1.2-gtk-3.0,8 gir1.2-gtk-3.0,
9 gir1.2-ibus-1.0,9 gir1.2-ibus-1.0,
10 gir1.2-ubuntu-app-launch-3 | gir1.2-ubuntu-app-launch-2,
11 graphviz,10 graphviz,
12 libjs-jquery,11 libjs-jquery,
13 libjs-underscore,12 libjs-underscore,
@@ -39,8 +38,7 @@ X-Python3-Version: >= 3.3
3938
40Package: python3-autopilot39Package: python3-autopilot
41Architecture: all40Architecture: all
42Depends: gir1.2-ubuntu-app-launch-3 | gir1.2-ubuntu-app-launch-2,41Depends: libjs-jquery,
43 libjs-jquery,
44 libjs-underscore,42 libjs-underscore,
45 mir-utils,43 mir-utils,
46 python3-dateutil,44 python3-dateutil,

Subscribers

People subscribed via source and target branches