Merge lp:~nuclearbob/autopilot/launch-function into lp:~autopilot/autopilot/temp-dev

Proposed by Max Brustkern
Status: Merged
Approved by: Thomi Richards
Approved revision: 550
Merged at revision: 517
Proposed branch: lp:~nuclearbob/autopilot/launch-function
Merge into: lp:~autopilot/autopilot/temp-dev
Diff against target: 1274 lines (+661/-297)
11 files modified
autopilot/application/__init__.py (+1/-1)
autopilot/application/_launcher.py (+216/-45)
autopilot/testcase.py (+23/-71)
autopilot/tests/acceptance/test_vis_main.py (+5/-34)
autopilot/tests/functional/test_ap_apps.py (+9/-3)
autopilot/tests/functional/test_application_mixin.py (+0/-22)
autopilot/tests/unit/test_application_launcher.py (+323/-121)
autopilot/tests/unit/test_testcase.py (+50/-0)
debian/control (+1/-0)
docs/api/autopilot.application.rst (+5/-0)
docs/tutorial/advanced_autopilot.rst (+28/-0)
To merge this branch: bzr merge lp:~nuclearbob/autopilot/launch-function
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Approve
Thomi Richards (community) Approve
Max Brustkern (community) Needs Resubmitting
Review via email: mp+220094@code.launchpad.net

Commit message

Add fixtures for launching applications

Description of the change

The work to enable app launch outside of the testcase class via fixtures is ongoing, but I'd like to get a report on whether my current changes have broken anything before proceeding further, so I'll get the gatekeeper kicked off and then make this work in progress.

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

FAILED: Continuous integration, rev:499
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https://code.launchpad.net/~nuclearbob/autopilot/launch-function/+merge/220094/+edit-commit-message

http://jenkins.qa.ubuntu.com/job/autopilot-autopilot-temp-dev-ci/79/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-autopilot-temp-dev-utopic-amd64-ci/37
        deb: http://jenkins.qa.ubuntu.com/job/autopilot-autopilot-temp-dev-utopic-amd64-ci/37/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-autopilot-temp-dev-utopic-armhf-ci/37
        deb: http://jenkins.qa.ubuntu.com/job/autopilot-autopilot-temp-dev-utopic-armhf-ci/37/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-autopilot-temp-dev-utopic-i386-ci/37
        deb: http://jenkins.qa.ubuntu.com/job/autopilot-autopilot-temp-dev-utopic-i386-ci/37/artifact/work/output/*zip*/output.zip
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-utopic-autopilot/77
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-utopic-autopilot/63
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/373
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/373/artifact/work/output/*zip*/output.zip

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/autopilot-autopilot-temp-dev-ci/79/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Max Brustkern (nuclearbob) wrote :

I'd like to get a review of everything except test_ap_apps.py. It currently passes, but I think it should probably be rewritten in spots to account for the new fixtures, so I'll have that up later today.

review: Needs Resubmitting
535. By Max Brustkern

Added new fixtures to functional tests

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

Merged temp-dev and added some updates

537. By Max Brustkern

Fixed sphinx warnings

538. By Max Brustkern

Named modules properly

539. By Max Brustkern

Doc fixes and removal of 'emulator' in most places

540. By Max Brustkern

Better rename

541. By Max Brustkern

Updated class docstrings

542. By Max Brustkern

Updated insensible docs

543. By Max Brustkern

Updated doc ordering

544. By Max Brustkern

Replaced unique things with tokens

545. By Max Brustkern

Named and documented tests better

546. By Max Brustkern

Doc updates

547. By Max Brustkern

Restored old keyword argument in signature

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

PASSED: Continuous integration, rev:536
http://jenkins.qa.ubuntu.com/job/autopilot-autopilot-temp-dev-ci/111/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-autopilot-temp-dev-utopic-amd64-ci/71
        deb: http://jenkins.qa.ubuntu.com/job/autopilot-autopilot-temp-dev-utopic-amd64-ci/71/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-autopilot-temp-dev-utopic-armhf-ci/71
        deb: http://jenkins.qa.ubuntu.com/job/autopilot-autopilot-temp-dev-utopic-armhf-ci/71/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-autopilot-temp-dev-utopic-i386-ci/71
        deb: http://jenkins.qa.ubuntu.com/job/autopilot-autopilot-temp-dev-utopic-i386-ci/71/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-utopic-autopilot/115
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-utopic-touch/99
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-utopic-autopilot/86
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/479
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/479/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/822
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/822/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-runner-mako/6630
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/7533

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/autopilot-autopilot-temp-dev-ci/111/rebuild

review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
548. By Max Brustkern

Fixed unit test

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

PASSED: Continuous integration, rev:548
http://jenkins.qa.ubuntu.com/job/autopilot-autopilot-temp-dev-ci/120/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-autopilot-temp-dev-utopic-amd64-ci/81
        deb: http://jenkins.qa.ubuntu.com/job/autopilot-autopilot-temp-dev-utopic-amd64-ci/81/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-autopilot-temp-dev-utopic-armhf-ci/81
        deb: http://jenkins.qa.ubuntu.com/job/autopilot-autopilot-temp-dev-utopic-armhf-ci/81/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-autopilot-temp-dev-utopic-i386-ci/81
        deb: http://jenkins.qa.ubuntu.com/job/autopilot-autopilot-temp-dev-utopic-i386-ci/81/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-utopic-autopilot/126
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-utopic-touch/110
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-utopic-autopilot/95
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/508
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/508/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/852
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/852/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-runner-mako/6641
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/7570

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/autopilot-autopilot-temp-dev-ci/120/rebuild

review: Approve (continuous-integration)
549. By Max Brustkern

Removed stray underscore

550. By Max Brustkern

Double backticks

Revision history for this message
Thomi Richards (thomir-deactivatedaccount) wrote :

LGTM, thanks

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

PASSED: Continuous integration, rev:550
http://jenkins.qa.ubuntu.com/job/autopilot-autopilot-temp-dev-ci/122/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-autopilot-temp-dev-utopic-amd64-ci/83
        deb: http://jenkins.qa.ubuntu.com/job/autopilot-autopilot-temp-dev-utopic-amd64-ci/83/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-autopilot-temp-dev-utopic-armhf-ci/83
        deb: http://jenkins.qa.ubuntu.com/job/autopilot-autopilot-temp-dev-utopic-armhf-ci/83/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-autopilot-temp-dev-utopic-i386-ci/83
        deb: http://jenkins.qa.ubuntu.com/job/autopilot-autopilot-temp-dev-utopic-i386-ci/83/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-utopic-autopilot/129
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-utopic-touch/112
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-utopic-autopilot/99
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/517
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/517/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/863
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/863/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-runner-mako/6643
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/7582

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/autopilot-autopilot-temp-dev-ci/122/rebuild

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'autopilot/application/__init__.py'
2--- autopilot/application/__init__.py 2014-01-24 04:18:10 +0000
3+++ autopilot/application/__init__.py 2014-05-23 09:57:29 +0000
4@@ -28,7 +28,7 @@
5
6 __all__ = [
7 'ClickApplicationLauncher',
8- 'get_application_launcher_wrapper',
9 'NormalApplicationLauncher',
10 'UpstartApplicationLauncher',
11+ 'get_application_launcher_wrapper',
12 ]
13
14=== modified file 'autopilot/application/_launcher.py'
15--- autopilot/application/_launcher.py 2014-05-20 15:27:06 +0000
16+++ autopilot/application/_launcher.py 2014-05-23 09:57:29 +0000
17@@ -25,17 +25,18 @@
18 import logging
19 import os
20 import psutil
21-import six
22 import subprocess
23 import signal
24 from testtools.content import content_from_file, text_content
25
26 from autopilot._timeout import Timeout
27-from autopilot.utilities import _raise_on_unknown_kwargs
28 from autopilot.application._environment import (
29 GtkApplicationEnvironment,
30 QtApplicationEnvironment,
31 )
32+from autopilot.introspection import (
33+ get_proxy_object_for_existing_process,
34+)
35
36 _logger = logging.getLogger(__name__)
37
38@@ -45,11 +46,30 @@
39 """A class that knows how to launch an application with a certain type of
40 introspection enabled.
41
42+ :keyword case_addDetail: addDetail method to use (self.addDetail if not
43+ specified)
44+ :keyword proxy_base: custom proxy base class to use, defaults to None
45+ :keyword dbus_bus: dbus bus to use, if set to something other than the
46+ default ('session') the environment will be patched
47+
48 """
49
50- def __init__(self, case_addDetail):
51- self.case_addDetail = case_addDetail
52- super(ApplicationLauncher, self).__init__()
53+ def __init__(self, case_addDetail=None, emulator_base=None,
54+ dbus_bus='session'):
55+ super().__init__()
56+ self.case_addDetail = case_addDetail or self.addDetail
57+ self.proxy_base = emulator_base
58+ self.dbus_bus = dbus_bus
59+
60+ def setUp(self):
61+ super().setUp()
62+ if self.dbus_bus != 'session':
63+ self.useFixture(
64+ fixtures.EnvironmentVariable(
65+ "DBUS_SESSION_BUS_ADDRESS",
66+ self.dbus_bus
67+ )
68+ )
69
70 def launch(self, *arguments):
71 raise NotImplementedError("Sub-classes must implement this method.")
72@@ -57,23 +77,44 @@
73
74 class UpstartApplicationLauncher(ApplicationLauncher):
75
76- """A launcher class that launched applications with UpstartAppLaunch."""
77+ """A launcher class that launches applications with UpstartAppLaunch."""
78+ __doc__ += ApplicationLauncher.__doc__
79
80 Timeout = object()
81 Failed = object()
82 Started = object()
83 Stopped = object()
84
85- def __init__(self, case_addDetail, **kwargs):
86- super(UpstartApplicationLauncher, self).__init__(case_addDetail)
87-
88- self.emulator_base = kwargs.pop('emulator_base', None)
89- self.dbus_bus = kwargs.pop('dbus_bus', 'session')
90- self.dbus_application_name = kwargs.pop('application_name', None)
91-
92- _raise_on_unknown_kwargs(kwargs)
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@@ -94,7 +135,13 @@
128 state.get('status', None),
129 state.get('message', '')
130 )
131- return self._get_pid_for_launched_app(app_id)
132+ pid = self._get_pid_for_launched_app(app_id)
133+ proxy_object = get_proxy_object_for_existing_process(
134+ dbus_bus=self.dbus_bus,
135+ emulator_base=self.proxy_base,
136+ pid=pid
137+ )
138+ return proxy_object
139
140 @staticmethod
141 def _on_failed(launched_app_id, failure_type, state):
142@@ -184,48 +231,172 @@
143
144 class ClickApplicationLauncher(UpstartApplicationLauncher):
145
146- def launch(self, package_id, app_name, app_uris):
147+ """Fixture to manage launching a Click application."""
148+ __doc__ += ApplicationLauncher.__doc__
149+
150+ def launch(self, package_id, app_name=None, app_uris=[]):
151+ """Launch a click package application with introspection enabled.
152+
153+ This method takes care of launching a click package with introspection
154+ exabled. You probably want to use this method if your application is
155+ packaged in a click application, or is started via upstart.
156+
157+ Usage is similar to NormalApplicationLauncher.launch::
158+
159+ from autopilot.application import ClickApplicationLauncher
160+ launcher = ClickApplicationLauncher()
161+ launcher.setUp()
162+ app_proxy = launcher.launch('com.ubuntu.dropping-letters')
163+
164+ :param package_id: The Click package name you want to launch. For
165+ example: ``com.ubuntu.dropping-letters``
166+ :param app_name: Currently, only one application can be packaged in a
167+ click package, and this parameter can be left at None. If
168+ specified, it should be the application name you wish to launch.
169+ :param app_uris: Parameters used to launch the click package. This
170+ parameter will be left empty if not used.
171+
172+ :raises RuntimeError: If the specified package_id cannot be found in
173+ the click package manifest.
174+ :raises RuntimeError: If the specified app_name cannot be found within
175+ the specified click package.
176+
177+ :returns: proxy object for the launched package application
178+
179+ """
180+ if isinstance(app_uris, str):
181+ app_uris = [app_uris]
182+ if isinstance(app_uris, bytes):
183+ app_uris = [app_uris.decode()]
184+ _logger.info(
185+ "Attempting to launch click application '%s' from click package "
186+ " '%s' and URIs '%s'",
187+ app_name if app_name is not None else "(default)",
188+ package_id,
189+ ','.join(app_uris)
190+ )
191 app_id = _get_click_app_id(package_id, app_name)
192- return self._do_upstart_launch(app_id, app_uris)
193-
194- def _do_upstart_launch(self, app_id, app_uris):
195- return super(ClickApplicationLauncher, self).launch(app_id, app_uris)
196+ return super().launch(app_id, app_uris)
197
198
199 class NormalApplicationLauncher(ApplicationLauncher):
200- def __init__(self, case_addDetail, **kwargs):
201- super(NormalApplicationLauncher, self).__init__(case_addDetail)
202- self.app_type = kwargs.pop('app_type', None)
203- self.cwd = kwargs.pop('launch_dir', None)
204- self.capture_output = kwargs.pop('capture_output', True)
205-
206- self.dbus_bus = kwargs.pop('dbus_bus', 'session')
207- self.emulator_base = kwargs.pop('emulator_base', None)
208-
209- _raise_on_unknown_kwargs(kwargs)
210-
211- def launch(self, application, *arguments):
212+
213+ """Fixture to manage launching an application."""
214+ __doc__ += ApplicationLauncher.__doc__
215+
216+ def launch(self, application, arguments=[], app_type=None, cwd=None,
217+ capture_output=True):
218+ """Launch an application and return a proxy object.
219+
220+ Use this method to launch an application and start testing it. The
221+ arguments passed in ``arguments`` are used as arguments to the
222+ application to launch. Additional keyword arguments are used to control
223+ the manner in which the application is launched.
224+
225+ This fixture is designed to be flexible enough to launch all supported
226+ types of applications. Autopilot can automatically determine how to
227+ enable introspection support for dynamically linked binary
228+ applications. For example, to launch a binary Gtk application, a test
229+ might start with::
230+
231+ from autopilot.application import NormalApplicationLauncher
232+ launcher = NormalApplicationLauncher()
233+ launcher.setUp()
234+ app_proxy = launcher.launch('gedit')
235+
236+ For use within a testcase, use useFixture:
237+
238+ from autopilot.application import NormalApplicationLauncher
239+ launcher = self.useFixture(NormalApplicationLauncher())
240+ app_proxy = launcher.launch('gedit')
241+
242+ Applications can be given command line arguments by supplying an
243+ ``arguments`` argument to this method. For example, if we want to
244+ launch ``gedit`` with a certain document loaded, we might do this::
245+
246+ app_proxy = launcher.launch(
247+ 'gedit', arguments=['/tmp/test-document.txt'])
248+
249+ ... a Qt5 Qml application is launched in a similar fashion::
250+
251+ app_proxy = launcher.launch(
252+ 'qmlscene', arguments=['my_scene.qml'])
253+
254+ If you wish to launch an application that is not a dynamically linked
255+ binary, you must specify the application type. For example, a Qt4
256+ python application might be launched like this::
257+
258+ app_proxy = launcher.launch(
259+ 'my_qt_app.py', app_type='qt')
260+
261+ Similarly, a python/Gtk application is launched like so::
262+
263+ app_proxy = launcher.launch(
264+ 'my_gtk_app.py', app_type='gtk')
265+
266+ :param application: The application to launch. The application can be
267+ specified as:
268+
269+ * A full, absolute path to an executable file.
270+ (``/usr/bin/gedit``)
271+ * A relative path to an executable file.
272+ (``./build/my_app``)
273+ * An app name, which will be searched for in $PATH (``my_app``)
274+
275+ :keyword arguments: If set, the list of arguments is passed to the
276+ launched app.
277+
278+ :keyword app_type: If set, provides a hint to autopilot as to which
279+ kind of introspection to enable. This is needed when the
280+ application you wish to launch is *not* a dynamically linked
281+ binary. Valid values are 'gtk' or 'qt'. These strings are case
282+ insensitive.
283+
284+ :keyword launch_dir: If set to a directory that exists the process
285+ will be launched from that directory.
286+
287+ :keyword capture_output: If set to True (the default), the process
288+ output will be captured and attached to the test as test detail.
289+
290+ :return: A proxy object that represents the application. Introspection
291+ data is retrievable via this object.
292+
293+ """
294+ _logger.info(
295+ "Attempting to launch application '%s' with arguments '%s' as a "
296+ "normal process",
297+ application,
298+ ' '.join(arguments)
299+ )
300 app_path = _get_application_path(application)
301- app_path, arguments = self._setup_environment(app_path, *arguments)
302- self.process = self._launch_application_process(app_path, *arguments)
303-
304- return self.process.pid
305-
306- def _setup_environment(self, app_path, *arguments):
307+ app_path, arguments = self._setup_environment(
308+ app_path, app_type, arguments)
309+ process = self._launch_application_process(
310+ app_path, capture_output, cwd, arguments)
311+ proxy_object = get_proxy_object_for_existing_process(
312+ dbus_bus=self.dbus_bus,
313+ emulator_base=self.proxy_base,
314+ process=process,
315+ pid=process.pid
316+ )
317+ return proxy_object
318+
319+ def _setup_environment(self, app_path, app_type, arguments):
320 app_env = self.useFixture(
321- _get_application_environment(self.app_type, app_path)
322+ _get_application_environment(app_type, app_path)
323 )
324 return app_env.prepare_environment(
325 app_path,
326 list(arguments),
327 )
328
329- def _launch_application_process(self, app_path, *arguments):
330+ def _launch_application_process(self, app_path, capture_output, cwd,
331+ arguments):
332 process = launch_process(
333 app_path,
334 arguments,
335- self.capture_output,
336- cwd=self.cwd,
337+ capture_output,
338+ cwd=cwd,
339 )
340
341 self.addCleanup(self._kill_process_and_attach_logs, process, app_path)
342@@ -369,9 +540,9 @@
343 _attempt_kill_pid(process.pid)
344 for _ in Timeout.default():
345 tmp_out, tmp_err = process.communicate()
346- if isinstance(tmp_out, six.binary_type):
347+ if isinstance(tmp_out, bytes):
348 tmp_out = tmp_out.decode('utf-8', errors='replace')
349- if isinstance(tmp_err, six.binary_type):
350+ if isinstance(tmp_err, bytes):
351 tmp_err = tmp_err.decode('utf-8', errors='replace')
352 stdout_parts.append(tmp_out)
353 stderr_parts.append(tmp_err)
354
355=== modified file 'autopilot/testcase.py'
356--- autopilot/testcase.py 2014-05-15 15:18:12 +0000
357+++ autopilot/testcase.py 2014-05-23 09:57:29 +0000
358@@ -49,7 +49,6 @@
359 from __future__ import absolute_import
360
361 import logging
362-import six
363
364 from fixtures import EnvironmentVariable
365 from testscenarios import TestWithScenarios
366@@ -65,9 +64,6 @@
367 from autopilot.display import Display
368 from autopilot.globals import get_debug_profile_fixture
369 from autopilot.input import Keyboard, Mouse
370-from autopilot.introspection import (
371- get_proxy_object_for_existing_process,
372-)
373 from autopilot.keybindings import KeybindingsHelper
374 from autopilot.matchers import Eventually
375 from autopilot.process import ProcessManager
376@@ -246,22 +242,22 @@
377 :keyword emulator_base: If set, specifies the base class to be used for
378 all emulators for this loaded application.
379
380- :raises ValueError: if unknown keyword arguments are passed.
381 :return: A proxy object that represents the application. Introspection
382 data is retrievable via this object.
383
384 """
385- _logger.info(
386- "Attempting to launch application '%s' with arguments '%s' as a "
387- "normal process",
388- application,
389- ' '.join(arguments)
390- )
391+ launch_args = {}
392+ launch_arg_list = ['app_type', 'launch_dir', 'capture_output']
393+ for arg in launch_arg_list:
394+ if arg in kwargs:
395+ launch_args[arg] = kwargs.pop(arg)
396 launcher = self.useFixture(
397- NormalApplicationLauncher(self.addDetailUniqueName, **kwargs)
398+ NormalApplicationLauncher(
399+ case_addDetail=self.addDetailUniqueName,
400+ **kwargs
401+ )
402 )
403-
404- return self._launch_test_application(launcher, application, *arguments)
405+ return launcher.launch(application, arguments, **launch_args)
406
407 def launch_click_package(self, package_id, app_name=None, app_uris=[],
408 **kwargs):
409@@ -297,20 +293,13 @@
410 :returns: proxy object for the launched package application
411
412 """
413- if isinstance(app_uris, (six.text_type, six.binary_type)):
414- app_uris = [app_uris]
415- _logger.info(
416- "Attempting to launch click application '%s' from click package "
417- " '%s' and URIs '%s'",
418- app_name if app_name is not None else "(default)",
419- package_id,
420- ','.join(app_uris)
421- )
422 launcher = self.useFixture(
423- ClickApplicationLauncher(self.addDetailUniqueName, **kwargs)
424+ ClickApplicationLauncher(
425+ case_addDetail=self.addDetailUniqueName,
426+ **kwargs
427+ )
428 )
429- return self._launch_test_application(launcher, package_id, app_name,
430- app_uris)
431+ return launcher.launch(package_id, app_name, app_uris)
432
433 def launch_upstart_application(self, application_name, uris=[], **kwargs):
434 """Launch an application with upstart.
435@@ -328,52 +317,14 @@
436 all emulators for this loaded application.
437
438 :raises RuntimeError: If the specified application cannot be launched.
439- :raises ValueError: If unknown keyword arguments are specified.
440 """
441- if isinstance(uris, (six.text_type, six.binary_type)):
442- uris = [uris]
443- _logger.info(
444- "Attempting to launch application '%s' with URIs '%s' via "
445- "upstart-app-launch",
446- application_name,
447- ','.join(uris)
448- )
449 launcher = self.useFixture(
450- UpstartApplicationLauncher(self.addDetailUniqueName, **kwargs)
451- )
452- return self._launch_test_application(launcher, application_name, uris)
453-
454- # Wrapper function tying the newer ApplicationLauncher behaviour with the
455- # previous (to be depreciated) behaviour
456- def _launch_test_application(self, launcher_instance, application, *args):
457-
458- dbus_bus = launcher_instance.dbus_bus
459- if dbus_bus != 'session':
460- self.useFixture(
461- EnvironmentVariable("DBUS_SESSION_BUS_ADDRESS", dbus_bus))
462-
463- pid = launcher_instance.launch(application, *args)
464- application_name = getattr(
465- launcher_instance,
466- 'dbus_application_name',
467- None
468- )
469- process = getattr(launcher_instance, 'process', None)
470-
471- search_params = dict(
472- pid=pid,
473- dbus_bus=dbus_bus,
474- emulator_base=launcher_instance.emulator_base
475- )
476- if application_name is not None:
477- search_params['application_name'] = application_name
478- if process is not None:
479- search_params['process'] = process
480-
481- proxy_obj = get_proxy_object_for_existing_process(**search_params)
482- proxy_obj.set_process(process)
483-
484- return proxy_obj
485+ UpstartApplicationLauncher(
486+ case_addDetail=self.addDetailUniqueName,
487+ **kwargs
488+ )
489+ )
490+ return launcher.launch(application_name, uris)
491
492 def _compare_system_with_app_snapshot(self):
493 """Compare the currently running application with the last snapshot.
494@@ -396,7 +347,8 @@
495
496 This function is deprecated and planned for removal in autopilot 1.6.
497 New implementations should use EnvironmenVariable from the fixtures
498- module:
499+ module::
500+
501 from fixtures import EnvironmentVariable
502
503 def my_test(AutopilotTestCase):
504
505=== modified file 'autopilot/tests/acceptance/test_vis_main.py'
506--- autopilot/tests/acceptance/test_vis_main.py 2014-05-05 03:51:21 +0000
507+++ autopilot/tests/acceptance/test_vis_main.py 2014-05-23 09:57:29 +0000
508@@ -24,7 +24,6 @@
509 from testtools import skipIf
510 from testtools.matchers import Equals
511
512-from autopilot.application import NormalApplicationLauncher
513 from autopilot.introspection.dbus import CustomEmulatorBase
514 from autopilot.matchers import Eventually
515 from autopilot.platform import model
516@@ -35,47 +34,19 @@
517 pass
518
519
520-class VisToolLauncher(NormalApplicationLauncher):
521-
522- """Override code that prepares application environment.
523-
524- The vis tool does not accept the '-testability' argument as the first
525- argument, so we override _setup_environment to put the argument in the
526- correct place.
527-
528- """
529-
530- def _setup_environment(self, app_path, *arguments):
531- return app_path, arguments + ('-testability',)
532-
533-
534 class VisAcceptanceTests(AutopilotTestCase):
535
536 def launch_windowmocker(self):
537 return self.launch_test_application("window-mocker", app_type="qt")
538
539- def launch_vis(self):
540- """Launch the vis tool and return it's proxy object."""
541- launcher = self.useFixture(
542- VisToolLauncher(
543- self.addDetail,
544- app_type="qt",
545- emulator_base=VisToolEmulatorBase
546- )
547- )
548- vis_proxy = self._launch_test_application(
549- launcher,
550- sys.executable,
551- "-m"
552- "autopilot.run",
553- "vis",
554- )
555- return vis_proxy
556-
557 @skipIf(model() != "Desktop", "Vis not usable on device.")
558 def test_can_select_windowmocker(self):
559 wm = self.launch_windowmocker()
560- vis = self.launch_vis()
561+ vis = self.launch_test_application(
562+ sys.executable,
563+ '-m', 'autopilot.run', 'vis', '-testability',
564+ app_type='qt',
565+ )
566 connection_list = vis.select_single('ConnectionList')
567 connection_list.slots.trySetSelectedItem(wm.applicationName)
568 self.assertThat(
569
570=== modified file 'autopilot/tests/functional/test_ap_apps.py'
571--- autopilot/tests/functional/test_ap_apps.py 2014-05-12 21:00:00 +0000
572+++ autopilot/tests/functional/test_ap_apps.py 2014-05-23 09:57:29 +0000
573@@ -36,6 +36,10 @@
574
575 from fixtures import EnvironmentVariable
576
577+from autopilot.application import (
578+ NormalApplicationLauncher,
579+ UpstartApplicationLauncher,
580+)
581 from autopilot.exceptions import ProcessSearchError
582 from autopilot.process import ProcessManager
583 from autopilot.platform import model
584@@ -290,9 +294,10 @@
585 def test_can_launch_normal_app(self):
586 path = self.get_qml_viewer_app_path()
587 fixture = self.useFixture(TempDesktopFile(exec_=path,))
588- app_proxy = self.launch_test_application(
589+ launcher = self.useFixture(NormalApplicationLauncher())
590+ app_proxy = launcher.launch(
591 path,
592- '--desktop_file_hint=%s' % fixture.get_desktop_file_path(),
593+ ['--desktop_file_hint=%s' % fixture.get_desktop_file_path()],
594 app_type='qt'
595 )
596 self.assertTrue(app_proxy is not None)
597@@ -300,7 +305,8 @@
598 def test_can_launch_upstart_app(self):
599 path = self.get_qml_viewer_app_path()
600 fixture = self.useFixture(TempDesktopFile(exec_=path,))
601- self.launch_upstart_application(fixture.get_desktop_file_id())
602+ launcher = self.useFixture(UpstartApplicationLauncher())
603+ launcher.launch(fixture.get_desktop_file_id())
604
605 @skipIf(model() != "Desktop", "Only suitable on Desktop (Qt4)")
606 def test_can_launch_normal_qt_script(self):
607
608=== modified file 'autopilot/tests/functional/test_application_mixin.py'
609--- autopilot/tests/functional/test_application_mixin.py 2013-12-16 01:07:43 +0000
610+++ autopilot/tests/functional/test_application_mixin.py 2014-05-23 09:57:29 +0000
611@@ -44,25 +44,3 @@
612 lambda: self.launch_test_application([]), raises(TypeError))
613 self.assertThat(
614 lambda: self.launch_test_application((None,)), raises(TypeError))
615-
616- def test_launch_raises_ValueError_on_unknown_kwargs(self):
617- """launch_test_application must raise ValueError when given unknown
618- keyword arguments.
619-
620- """
621- fn = lambda: self.launch_test_application(
622- 'gedit', arg1=123, arg2='asd')
623- self.assertThat(
624- fn,
625- raises(ValueError("Unknown keyword arguments: 'arg1', 'arg2'.")))
626-
627- def test_launch_raises_ValueError_on_unknown_kwargs_with_known(self):
628- """launch_test_application must raise ValueError when given unknown
629- keyword arguments.
630-
631- """
632- fn = lambda: self.launch_test_application(
633- 'gedit', arg1=123, arg2='asd', launch_dir='/')
634- self.assertThat(
635- fn,
636- raises(ValueError("Unknown keyword arguments: 'arg1', 'arg2'.")))
637
638=== modified file 'autopilot/tests/unit/test_application_launcher.py'
639--- autopilot/tests/unit/test_application_launcher.py 2014-04-22 22:00:25 +0000
640+++ autopilot/tests/unit/test_application_launcher.py 2014-05-23 09:57:29 +0000
641@@ -34,7 +34,6 @@
642 IsInstance,
643 MatchesListwise,
644 Not,
645- Raises,
646 raises,
647 )
648 from testtools.content import text_content
649@@ -76,28 +75,34 @@
650 )
651 )
652
653+ def test_init_uses_default_values(self):
654+ launcher = ApplicationLauncher()
655+ self.assertEqual(launcher.case_addDetail, launcher.addDetail)
656+ self.assertEqual(launcher.proxy_base, None)
657+ self.assertEqual(launcher.dbus_bus, 'session')
658+
659+ def test_init_uses_passed_values(self):
660+ case_addDetail = self.getUniqueString()
661+ emulator_base = self.getUniqueString()
662+ dbus_bus = self.getUniqueString()
663+
664+ launcher = ApplicationLauncher(
665+ case_addDetail=case_addDetail,
666+ emulator_base=emulator_base,
667+ dbus_bus=dbus_bus,
668+ )
669+ self.assertEqual(launcher.case_addDetail, case_addDetail)
670+ self.assertEqual(launcher.proxy_base, emulator_base)
671+ self.assertEqual(launcher.dbus_bus, dbus_bus)
672+
673+ @patch('autopilot.application._launcher.fixtures.EnvironmentVariable')
674+ def test_setUp_patches_environment(self, ev):
675+ self.useFixture(ApplicationLauncher(dbus_bus=''))
676+ ev.assert_called_with('DBUS_SESSION_BUS_ADDRESS', '')
677+
678
679 class NormalApplicationLauncherTests(TestCase):
680
681- def test_consumes_all_known_kwargs(self):
682- test_kwargs = dict(
683- app_type=True,
684- launch_dir=True,
685- capture_output=True,
686- dbus_bus=True,
687- emulator_base=True
688- )
689- self.assertThat(
690- lambda: NormalApplicationLauncher(self.addDetail, **test_kwargs),
691- Not(Raises())
692- )
693-
694- def test_raises_value_error_on_unknown_kwargs(self):
695- self.assertThat(
696- lambda: NormalApplicationLauncher(self.addDetail, unknown=True),
697- raises(ValueError("Unknown keyword arguments: 'unknown'."))
698- )
699-
700 def test_kill_process_and_attach_logs(self):
701 mock_addDetail = Mock()
702 app_launcher = NormalApplicationLauncher(mock_addDetail)
703@@ -123,31 +128,100 @@
704 )
705
706 def test_setup_environment_returns_prepare_environment_return_value(self):
707- token = self.getUniqueString()
708- fake_env = Mock()
709- fake_env.prepare_environment.return_value = token
710-
711- app_launcher = NormalApplicationLauncher(self.addDetail)
712- app_launcher.setUp()
713-
714- with patch.object(
715- _l, '_get_application_environment', return_value=fake_env
716- ):
717+ app_launcher = self.useFixture(NormalApplicationLauncher())
718+ with patch.object(_l, '_get_application_environment') as gae:
719 self.assertThat(
720- app_launcher._setup_environment(self.getUniqueString()),
721- Equals(token)
722- )
723-
724- def test_launch_returns_process_id(self):
725- app_launcher = NormalApplicationLauncher(self.addDetail)
726-
727- with patch.object(_l, '_get_application_path', return_value=""):
728- app_launcher._setup_environment = Mock(return_value=("", "",))
729- app_launcher._launch_application_process = Mock(
730- return_value=Mock(pid=123)
731- )
732-
733- self.assertThat(app_launcher.launch(""), Equals(123))
734+ app_launcher._setup_environment(
735+ self.getUniqueString(), None, []),
736+ Equals(gae.return_value.prepare_environment.return_value)
737+ )
738+
739+ @patch('autopilot.application._launcher.'
740+ 'get_proxy_object_for_existing_process')
741+ @patch('autopilot.application._launcher._get_application_path')
742+ def test_launch_call_to_get_application_path(self, gap, _):
743+ """Test that NormalApplicationLauncher.launch calls
744+ _get_application_path with the arguments it was passed,"""
745+ launcher = NormalApplicationLauncher()
746+ with patch.object(launcher, '_launch_application_process'):
747+ with patch.object(launcher, '_setup_environment') as se:
748+ se.return_value = ('', [])
749+ token = self.getUniqueString()
750+ launcher.launch(token)
751+ gap.assert_called_once_with(token)
752+
753+ @patch('autopilot.application._launcher.'
754+ 'get_proxy_object_for_existing_process')
755+ @patch('autopilot.application._launcher._get_application_path')
756+ def test_launch_call_to_setup_environment(self, gap, _):
757+ """Test the NornmalApplicationLauncher.launch calls
758+ self._setup_environment with the correct application path from
759+ _get_application_path and the arguments passed to it."""
760+ launcher = NormalApplicationLauncher()
761+ with patch.object(launcher, '_launch_application_process'):
762+ with patch.object(launcher, '_setup_environment') as se:
763+ se.return_value = ('', [])
764+ token_a = self.getUniqueString()
765+ token_b = self.getUniqueString()
766+ token_c = self.getUniqueString()
767+ launcher.launch(token_a, arguments=[token_b, token_c])
768+ se.assert_called_once_with(
769+ gap.return_value,
770+ None,
771+ [token_b, token_c],
772+ )
773+
774+ @patch('autopilot.application._launcher.'
775+ 'get_proxy_object_for_existing_process')
776+ @patch('autopilot.application._launcher._get_application_path')
777+ def test_launch_call_to_launch_application_process(self, _, __):
778+ """Test that NormalApplicationLauncher.launch calls
779+ launch_application_process with the return values of
780+ setup_environment."""
781+ launcher = NormalApplicationLauncher()
782+ with patch.object(launcher, '_launch_application_process') as lap:
783+ with patch.object(launcher, '_setup_environment') as se:
784+ token_a = self.getUniqueString()
785+ token_b = self.getUniqueString()
786+ token_c = self.getUniqueString()
787+ se.return_value = (token_a, [token_b, token_c])
788+ launcher.launch('', arguments=['', ''])
789+ lap.assert_called_once_with(
790+ token_a,
791+ True,
792+ None,
793+ [token_b, token_c],
794+ )
795+
796+ @patch('autopilot.application._launcher.'
797+ 'get_proxy_object_for_existing_process')
798+ @patch('autopilot.application._launcher._get_application_path')
799+ def test_launch_gets_correct_proxy_object(self, _, gpofep):
800+ """Test that NormalApplicationLauncher.launch calls
801+ get_proxy_object_for_existing_process with the correct return values of
802+ other functions."""
803+ launcher = NormalApplicationLauncher()
804+ with patch.object(launcher, '_launch_application_process') as lap:
805+ with patch.object(launcher, '_setup_environment') as se:
806+ se.return_value = ('', [])
807+ launcher.launch('')
808+ gpofep.assert_called_once_with(process=lap.return_value,
809+ pid=lap.return_value.pid,
810+ emulator_base=None,
811+ dbus_bus='session')
812+
813+ @patch('autopilot.application._launcher.'
814+ 'get_proxy_object_for_existing_process')
815+ @patch('autopilot.application._launcher._get_application_path')
816+ def test_launch_returns_proxy_object(self, _, gpofep):
817+ """Test that NormalApplicationLauncher.launch returns the proxy object
818+ returned by get_proxy_object_for_existing_process."""
819+ launcher = NormalApplicationLauncher()
820+ with patch.object(launcher, '_launch_application_process'):
821+ with patch.object(launcher, '_setup_environment') as se:
822+ se.return_value = ('', [])
823+ result = launcher.launch('')
824+ self.assertEqual(result, gpofep.return_value)
825
826 def test_launch_application_process(self):
827 """The _launch_application_process method must return the process
828@@ -162,7 +236,8 @@
829 with patch.object(
830 _l, 'launch_process', return_value=expected_process_return
831 ) as patched_launch_process:
832- process = launcher._launch_application_process("/foo/bar")
833+ process = launcher._launch_application_process(
834+ "/foo/bar", False, None, [])
835
836 self.assertThat(process, Equals(expected_process_return))
837 self.assertThat(
838@@ -171,9 +246,9 @@
839 )
840 patched_launch_process.assert_called_with(
841 "/foo/bar",
842- (),
843- launcher.capture_output,
844- cwd=launcher.cwd
845+ [],
846+ False,
847+ cwd=None
848 )
849
850
851@@ -182,49 +257,126 @@
852 def test_raises_exception_on_unknown_kwargs(self):
853 self.assertThat(
854 lambda: ClickApplicationLauncher(self.addDetail, unknown=True),
855- raises(ValueError("Unknown keyword arguments: 'unknown'."))
856- )
857-
858- def test_application_name_kwarg_stored(self):
859- app_name = self.getUniqueString()
860- launcher = ClickApplicationLauncher(
861- self.addDetail,
862- application_name=app_name
863- )
864-
865- self.assertThat(
866- launcher.dbus_application_name, Equals(app_name)
867- )
868-
869- def test_click_launch_calls_upstart_launch(self):
870- launcher = ClickApplicationLauncher(self.addDetail)
871- token = self.getUniqueString()
872- with patch.object(launcher, '_do_upstart_launch') as p_dul:
873- with patch.object(_l, '_get_click_app_id') as p_gcai:
874- p_gcai.return_value = token
875- launcher.launch('some_app_id', 'some_app_name', [])
876- p_dul.assert_called_once_with(token, [])
877-
878- def test_upcalls_to_upstart(self):
879- class FakeUpstartBase(_l.ApplicationLauncher):
880- launch_call_args = []
881-
882- def launch(self, *args):
883- FakeUpstartBase.launch_call_args = list(args)
884-
885- patcher = patch.object(
886- _l.ClickApplicationLauncher,
887- '__bases__',
888- (FakeUpstartBase,)
889- )
890- with patcher:
891- # Prevent mock from trying to delete __bases__
892- patcher.is_local = True
893- launcher = ClickApplicationLauncher(self.addDetail)
894- launcher._do_upstart_launch('app_id', [])
895- self.assertEqual(
896- FakeUpstartBase.launch_call_args,
897- ['app_id', []])
898+ raises(TypeError("__init__() got an unexpected keyword argument "
899+ "'unknown'"))
900+ )
901+
902+ @patch('autopilot.application._launcher._get_click_app_id')
903+ def test_handle_string(self, gcai):
904+ class FakeUpstartBase(_l.ApplicationLauncher):
905+ launch_call_args = []
906+
907+ def launch(self, *args):
908+ FakeUpstartBase.launch_call_args = list(args)
909+
910+ patcher = patch.object(
911+ _l.ClickApplicationLauncher,
912+ '__bases__',
913+ (FakeUpstartBase,)
914+ )
915+ token = self.getUniqueString()
916+ with patcher:
917+ # Prevent mock from trying to delete __bases__
918+ patcher.is_local = True
919+ launcher = self.useFixture(
920+ _l.ClickApplicationLauncher())
921+ launcher.launch('', '', token)
922+ self.assertEqual(
923+ FakeUpstartBase.launch_call_args,
924+ [gcai.return_value, [token]])
925+
926+ @patch('autopilot.application._launcher._get_click_app_id')
927+ def test_handle_bytes(self, gcai):
928+ class FakeUpstartBase(_l.ApplicationLauncher):
929+ launch_call_args = []
930+
931+ def launch(self, *args):
932+ FakeUpstartBase.launch_call_args = list(args)
933+
934+ patcher = patch.object(
935+ _l.ClickApplicationLauncher,
936+ '__bases__',
937+ (FakeUpstartBase,)
938+ )
939+ token = self.getUniqueString()
940+ with patcher:
941+ # Prevent mock from trying to delete __bases__
942+ patcher.is_local = True
943+ launcher = self.useFixture(
944+ _l.ClickApplicationLauncher())
945+ launcher.launch('', '', token.encode())
946+ self.assertEqual(
947+ FakeUpstartBase.launch_call_args,
948+ [gcai.return_value, [token]])
949+
950+ @patch('autopilot.application._launcher._get_click_app_id')
951+ def test_handle_list(self, gcai):
952+ class FakeUpstartBase(_l.ApplicationLauncher):
953+ launch_call_args = []
954+
955+ def launch(self, *args):
956+ FakeUpstartBase.launch_call_args = list(args)
957+
958+ patcher = patch.object(
959+ _l.ClickApplicationLauncher,
960+ '__bases__',
961+ (FakeUpstartBase,)
962+ )
963+ token = self.getUniqueString()
964+ with patcher:
965+ # Prevent mock from trying to delete __bases__
966+ patcher.is_local = True
967+ launcher = self.useFixture(
968+ _l.ClickApplicationLauncher())
969+ launcher.launch('', '', [token])
970+ self.assertEqual(
971+ FakeUpstartBase.launch_call_args,
972+ [gcai.return_value, [token]])
973+
974+ @patch('autopilot.application._launcher._get_click_app_id')
975+ def test_call_get_click_app_id(self, gcai):
976+ class FakeUpstartBase(_l.ApplicationLauncher):
977+ launch_call_args = []
978+
979+ def launch(self, *args):
980+ FakeUpstartBase.launch_call_args = list(args)
981+
982+ patcher = patch.object(
983+ _l.ClickApplicationLauncher,
984+ '__bases__',
985+ (FakeUpstartBase,)
986+ )
987+ token_a = self.getUniqueString()
988+ token_b = self.getUniqueString()
989+ with patcher:
990+ # Prevent mock from trying to delete __bases__
991+ patcher.is_local = True
992+ launcher = self.useFixture(
993+ _l.ClickApplicationLauncher())
994+ launcher.launch(token_a, token_b)
995+ gcai.assert_called_once_with(token_a, token_b)
996+
997+ @patch('autopilot.application._launcher._get_click_app_id')
998+ def test_call_upstart_launch(self, gcai):
999+ class FakeUpstartBase(_l.ApplicationLauncher):
1000+ launch_call_args = []
1001+
1002+ def launch(self, *args):
1003+ FakeUpstartBase.launch_call_args = list(args)
1004+
1005+ patcher = patch.object(
1006+ _l.ClickApplicationLauncher,
1007+ '__bases__',
1008+ (FakeUpstartBase,)
1009+ )
1010+ with patcher:
1011+ # Prevent mock from trying to delete __bases__
1012+ patcher.is_local = True
1013+ launcher = self.useFixture(
1014+ _l.ClickApplicationLauncher())
1015+ launcher.launch('', '')
1016+ self.assertEqual(launcher.launch_call_args,
1017+ [gcai.return_value, []])
1018
1019
1020 class ClickFunctionTests(TestCase):
1021@@ -316,32 +468,11 @@
1022 def test_can_construct_UpstartApplicationLauncher(self):
1023 UpstartApplicationLauncher(self.addDetail)
1024
1025- def test_default_values_are_set(self):
1026- launcher = UpstartApplicationLauncher(self.addDetail)
1027- self.assertThat(launcher.emulator_base, Equals(None))
1028- self.assertThat(launcher.dbus_bus, Equals('session'))
1029-
1030- def test_can_set_emulator_base(self):
1031- mock_emulator_base = Mock()
1032- launcher = UpstartApplicationLauncher(
1033- self.addDetail,
1034- emulator_base=mock_emulator_base
1035- )
1036-
1037- self.assertThat(launcher.emulator_base, Equals(mock_emulator_base))
1038-
1039- def test_can_set_dbus_bus(self):
1040- launcher = UpstartApplicationLauncher(
1041- self.addDetail,
1042- dbus_bus='system'
1043- )
1044-
1045- self.assertThat(launcher.dbus_bus, Equals('system'))
1046-
1047 def test_raises_exception_on_unknown_kwargs(self):
1048 self.assertThat(
1049 lambda: UpstartApplicationLauncher(self.addDetail, unknown=True),
1050- raises(ValueError("Unknown keyword arguments: 'unknown'."))
1051+ raises(TypeError("__init__() got an unexpected keyword argument "
1052+ "'unknown'"))
1053 )
1054
1055 def test_on_failed_only_sets_status_on_correct_app_id(self):
1056@@ -491,13 +622,84 @@
1057 loop = UpstartApplicationLauncher._get_glib_loop()
1058 self.assertThat(loop, IsInstance(GLib.MainLoop))
1059
1060- def test_launch(self):
1061- launcher = UpstartApplicationLauncher(self.addDetail)
1062- with patch.object(launcher, '_launch_app') as patched_launch:
1063- with patch.object(launcher, '_get_glib_loop'):
1064- launcher.launch('gedit')
1065-
1066- patched_launch.assert_called_once_with('gedit', [])
1067+ @patch('autopilot.application._launcher.'
1068+ 'get_proxy_object_for_existing_process')
1069+ def test_handle_string(self, _):
1070+ launcher = UpstartApplicationLauncher()
1071+ token_a = self.getUniqueString()
1072+ token_b = self.getUniqueString()
1073+ with patch.object(launcher, '_launch_app') as la:
1074+ with patch.object(launcher, '_get_pid_for_launched_app'):
1075+ with patch.object(launcher, '_get_glib_loop'):
1076+ launcher.launch(token_a, token_b)
1077+ la.assert_called_once_with(token_a, [token_b])
1078+
1079+ @patch('autopilot.application._launcher.'
1080+ 'get_proxy_object_for_existing_process')
1081+ def test_handle_bytes(self, _):
1082+ launcher = UpstartApplicationLauncher()
1083+ token_a = self.getUniqueString()
1084+ token_b = self.getUniqueString()
1085+ with patch.object(launcher, '_launch_app') as la:
1086+ with patch.object(launcher, '_get_pid_for_launched_app'):
1087+ with patch.object(launcher, '_get_glib_loop'):
1088+ launcher.launch(token_a, token_b.encode())
1089+ la.assert_called_once_with(token_a, [token_b])
1090+
1091+ @patch('autopilot.application._launcher.'
1092+ 'get_proxy_object_for_existing_process')
1093+ def test_handle_list(self, _):
1094+ launcher = UpstartApplicationLauncher()
1095+ token_a = self.getUniqueString()
1096+ token_b = self.getUniqueString()
1097+ with patch.object(launcher, '_launch_app') as la:
1098+ with patch.object(launcher, '_get_pid_for_launched_app'):
1099+ with patch.object(launcher, '_get_glib_loop'):
1100+ launcher.launch(token_a, [token_b])
1101+ la.assert_called_once_with(token_a, [token_b])
1102+
1103+ @patch('autopilot.application._launcher.'
1104+ 'get_proxy_object_for_existing_process')
1105+ def test_calls_get_pid(self, _):
1106+ launcher = UpstartApplicationLauncher()
1107+ token = self.getUniqueString()
1108+ with patch.object(launcher, '_launch_app'):
1109+ with patch.object(launcher, '_get_pid_for_launched_app') as gp:
1110+ with patch.object(launcher, '_get_glib_loop'):
1111+ launcher.launch(token)
1112+ gp.assert_called_once_with(token)
1113+
1114+ @patch('autopilot.application._launcher.'
1115+ 'get_proxy_object_for_existing_process')
1116+ def test_gets_correct_proxy_object(self, gpofep):
1117+ launcher = UpstartApplicationLauncher()
1118+ with patch.object(launcher, '_launch_app'):
1119+ with patch.object(launcher, '_get_pid_for_launched_app') as gp:
1120+ with patch.object(launcher, '_get_glib_loop'):
1121+ launcher.launch('')
1122+ gpofep.assert_called_once_with(pid=gp.return_value,
1123+ emulator_base=None,
1124+ dbus_bus='session')
1125+
1126+ @patch('autopilot.application._launcher.'
1127+ 'get_proxy_object_for_existing_process')
1128+ def test_returns_proxy_object(self, gpofep):
1129+ launcher = UpstartApplicationLauncher()
1130+ with patch.object(launcher, '_launch_app'):
1131+ with patch.object(launcher, '_get_pid_for_launched_app'):
1132+ with patch.object(launcher, '_get_glib_loop'):
1133+ result = launcher.launch('')
1134+ self.assertEqual(result, gpofep.return_value)
1135+
1136+ @patch('autopilot.application._launcher.'
1137+ 'get_proxy_object_for_existing_process')
1138+ def test_calls_get_glib_loop(self, gpofep):
1139+ launcher = UpstartApplicationLauncher()
1140+ with patch.object(launcher, '_launch_app'):
1141+ with patch.object(launcher, '_get_pid_for_launched_app'):
1142+ with patch.object(launcher, '_get_glib_loop') as ggl:
1143+ launcher.launch('')
1144+ ggl.assert_called_once_with()
1145
1146 def assertFailedObserverSetsExtraMessage(self, fail_type, expected_msg):
1147 """Assert that the on_failed observer must set the expected message
1148
1149=== modified file 'autopilot/tests/unit/test_testcase.py'
1150--- autopilot/tests/unit/test_testcase.py 2014-05-12 20:59:37 +0000
1151+++ autopilot/tests/unit/test_testcase.py 2014-05-23 09:57:29 +0000
1152@@ -110,3 +110,53 @@
1153 ep.assert_called_once_with('foo', 'bar')
1154 self.assertIn(call().setUp(), ep.mock_calls)
1155 self.assertIn(call().cleanUp(), ep.mock_calls)
1156+
1157+ @patch('autopilot.testcase.NormalApplicationLauncher')
1158+ def test_launch_test_application(self, nal):
1159+ class LauncherTest(AutopilotTestCase):
1160+
1161+ """Test launchers."""
1162+
1163+ def test_anything(self):
1164+ pass
1165+
1166+ test_case = LauncherTest('test_anything')
1167+ with patch.object(test_case, 'useFixture') as uf:
1168+ result = test_case.launch_test_application('a', 'b', 'c')
1169+ uf.assert_called_once_with(nal.return_value)
1170+ uf.return_value.launch.assert_called_once_with('a', ('b', 'c'))
1171+ self.assertEqual(result, uf.return_value.launch.return_value)
1172+
1173+ @patch('autopilot.testcase.ClickApplicationLauncher')
1174+ def test_launch_click_package(self, cal):
1175+ class LauncherTest(AutopilotTestCase):
1176+
1177+ """Test launchers."""
1178+
1179+ def test_anything(self):
1180+ pass
1181+
1182+ test_case = LauncherTest('test_anything')
1183+ with patch.object(test_case, 'useFixture') as uf:
1184+ result = test_case.launch_click_package('a', 'b', ['c', 'd'])
1185+ uf.assert_called_once_with(cal.return_value)
1186+ uf.return_value.launch.assert_called_once_with(
1187+ 'a', 'b', ['c', 'd']
1188+ )
1189+ self.assertEqual(result, uf.return_value.launch.return_value)
1190+
1191+ @patch('autopilot.testcase.UpstartApplicationLauncher')
1192+ def test_launch_upstart_application(self, ual):
1193+ class LauncherTest(AutopilotTestCase):
1194+
1195+ """Test launchers."""
1196+
1197+ def test_anything(self):
1198+ pass
1199+
1200+ test_case = LauncherTest('test_anything')
1201+ with patch.object(test_case, 'useFixture') as uf:
1202+ result = test_case.launch_upstart_application('a', ['b'])
1203+ uf.assert_called_once_with(ual.return_value)
1204+ uf.return_value.launch.assert_called_once_with('a', ['b'])
1205+ self.assertEqual(result, uf.return_value.launch.return_value)
1206
1207=== modified file 'debian/control'
1208--- debian/control 2014-05-15 08:31:35 +0000
1209+++ debian/control 2014-05-23 09:57:29 +0000
1210@@ -134,6 +134,7 @@
1211 libautopilot-gtk (>= 1.4),
1212 libautopilot-qt (>= 1.4),
1213 python3-autopilot,
1214+ python3-dbus.mainloop.qt,
1215 python3-evdev,
1216 python3-mock,
1217 python3-pyqt4,
1218
1219=== added file 'docs/api/autopilot.application.rst'
1220--- docs/api/autopilot.application.rst 1970-01-01 00:00:00 +0000
1221+++ docs/api/autopilot.application.rst 2014-05-23 09:57:29 +0000
1222@@ -0,0 +1,5 @@
1223+``autopilot.application`` - Autopilot Application Launchers
1224++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1225+
1226+.. automodule:: autopilot.application
1227+ :members:
1228
1229=== renamed file 'docs/api/display.rst' => 'docs/api/autopilot.display.rst'
1230=== renamed file 'docs/api/emulators.rst' => 'docs/api/autopilot.emulators.rst'
1231=== renamed file 'docs/api/exceptions.rst' => 'docs/api/autopilot.exceptions.rst'
1232=== renamed file 'docs/api/gestures.rst' => 'docs/api/autopilot.gestures.rst'
1233=== renamed file 'docs/api/input.rst' => 'docs/api/autopilot.input.rst'
1234=== renamed file 'docs/api/introspection.rst' => 'docs/api/autopilot.introspection.rst'
1235=== renamed file 'docs/api/introspection.types.rst' => 'docs/api/autopilot.introspection.types.rst'
1236=== renamed file 'docs/api/matchers.rst' => 'docs/api/autopilot.matchers.rst'
1237=== renamed file 'docs/api/platform.rst' => 'docs/api/autopilot.platform.rst'
1238=== renamed file 'docs/api/process.rst' => 'docs/api/autopilot.process.rst'
1239=== renamed file 'docs/api/testcase.rst' => 'docs/api/autopilot.testcase.rst'
1240=== modified file 'docs/tutorial/advanced_autopilot.rst'
1241--- docs/tutorial/advanced_autopilot.rst 2014-05-19 07:29:26 +0000
1242+++ docs/tutorial/advanced_autopilot.rst 2014-05-23 09:57:29 +0000
1243@@ -409,3 +409,31 @@
1244
1245 # Get all QLabels in the applicaton:
1246 labels = self.app.select_many(QLabel)
1247+
1248+.. launching_applications:
1249+
1250+Launching Applications
1251+======================
1252+
1253+Applications can be launched inside of a testcase using :meth:`~autopilot.testcase.AutopilotTestCase.launch_test_application`, :meth:`~autopilot.testcase.AutopilotTestCase.launch_upstart_application`, and :meth:`~autopilot.testcase.AutopilotTestCase.launch_click_application`.
1254+
1255+Outside of testcase classes, the :class:`~autopilot.application.NormalApplicationLauncher`, :class:`~autopilot.application.UpstartApplicationLauncher`, and :class:`~autopilot.application.ClickApplicationLauncher` fixtures can be used, i.e.::
1256+
1257+ from autopilot.application import NormalApplicationLauncher
1258+
1259+ with NormalApplicationLauncher() as launcher:
1260+ launcher.launch('gedit')
1261+
1262+Within a fixture or a testcase, ``self.useFixture``can be used::
1263+
1264+ launcher = self.useFixture(NormalApplicationLauncher())
1265+ launcher.launch('gedit', ['--new-window', '/path/to/file'])
1266+
1267+Additional options can also be specified to set a custom addDetail method, a custom proxy base, or a custom dbus bus with which to patch the environment::
1268+
1269+
1270+ launcher = self.useFixture(NormalApplicationLauncher(
1271+ case_addDetail=self.addDetail,
1272+ dbus_bus='some_other_bus',
1273+ proxy_base=my_proxy_class,
1274+ ))

Subscribers

People subscribed via source and target branches