57
54
from testscenarios import TestWithScenarios
58
55
from testtools import TestCase
59
from testtools.content import text_content, content_from_file
60
56
from testtools.matchers import Equals
58
from autopilot.application import (
59
ClickApplicationLauncher,
60
get_application_launcher_wrapper,
61
NormalApplicationLauncher,
62
63
from autopilot.process import ProcessManager
63
64
from autopilot.input import Keyboard, Mouse
64
65
from autopilot.introspection import (
65
get_application_launcher,
66
get_application_launcher_from_string_hint,
67
get_autopilot_proxy_object_for_process,
68
66
get_proxy_object_for_existing_process,
71
from autopilot.introspection.utilities import _get_click_app_id
72
68
from autopilot.display import Display
73
69
from autopilot.utilities import on_test_started, sleep
74
70
from autopilot.keybindings import KeybindingsHelper
252
248
data is retrievable via this object.
255
app_path = subprocess.check_output(['which', application],
256
universal_newlines=True).strip()
257
# Get a launcher, tests can override this if they need:
258
launcher_hint = kwargs.pop('app_type', '')
260
if launcher_hint != '':
261
launcher = get_application_launcher_from_string_hint(launcher_hint)
264
launcher = self.pick_app_launcher(app_path)
269
"Autopilot could not determine the correct introspection type "
270
"to use. You can specify one by overriding the "
271
"AutopilotTestCase.pick_app_launcher method.")
272
emulator_base = kwargs.pop('emulator_base', None)
273
dbus_bus = kwargs.pop('dbus_bus', 'session')
275
if dbus_bus != 'session':
276
self.patch_environment("DBUS_SESSION_BUS_ADDRESS", dbus_bus)
278
process = launch_application(launcher, app_path, *arguments, **kwargs)
279
self.addCleanup(self._kill_process_and_attach_logs, process)
280
return get_autopilot_proxy_object_for_process(
251
launcher = self.useFixture(
252
NormalApplicationLauncher(self.addDetail, **kwargs)
255
return self._launch_test_application(launcher, application, *arguments)
286
257
def launch_click_package(self, package_id, app_name=None, **kwargs):
287
258
"""Launch a click package application with introspection enabled.
312
283
the specified click package.
315
app_id = _get_click_app_id(package_id, app_name)
316
# sadly, we cannot re-use the existing launch_test_application
317
# since upstart is a little odd.
318
# set the qt testability env:
322
"QT_LOAD_TESTABILITY=1",
324
log_dir = os.path.expanduser('~/.cache/upstart/')
325
log_name = 'application-click-{}.log'.format(app_id)
326
log_path = os.path.join(log_dir, log_name)
328
lambda: self.addDetail(
330
content_from_file(log_path)
334
# launch the application:
335
subprocess.check_output([
338
"APP_ID={}".format(app_id),
340
# perhaps we should do this with a regular expression instead?
343
list_output = subprocess.check_output([
347
"APP_ID={}".format(app_id)
349
except subprocess.CalledProcessError:
350
# application not started yet.
353
for line in list_output.split('\n'):
354
if app_id in line and "start/running" in line:
355
target_pid = int(line.split()[-1])
357
self.addCleanup(self._kill_pid, target_pid)
359
"Click package %s has been launched with PID %d",
364
emulator_base = kwargs.pop('emulator_base', None)
365
proxy = get_proxy_object_for_existing_process(
367
emulator_base=emulator_base
369
# reset the upstart env, and hope no one else launched,
370
# or they'll have introspection enabled as well,
371
# although this isn't the worth thing in the world.
375
"QT_LOAD_TESTABILITY",
378
# give the app time to launch - maybe this is not needed?:
382
"Could not find autopilot interface for click package"
383
" '{}' after 10 seconds.".format(app_id)
286
launcher = self.useFixture(
287
ClickApplicationLauncher(self.addDetail, **kwargs)
289
return self._launch_test_application(launcher, package_id, app_name)
291
# Wrapper function tying the newer ApplicationLauncher behaviour with the
292
# previous (to be depreciated) behaviour
293
def _launch_test_application(self, launcher_instance, application, *args):
295
dbus_bus = launcher_instance.dbus_bus
296
if dbus_bus != 'session':
297
self.patch_environment("DBUS_SESSION_BUS_ADDRESS", dbus_bus)
299
pid = launcher_instance.launch(application, *args)
300
process = getattr(launcher_instance, 'process', None)
302
proxy_obj = get_proxy_object_for_existing_process(
306
emulator_base=launcher_instance.emulator_base,
308
proxy_obj.set_process(process)
386
312
def _compare_system_with_app_snapshot(self):
387
313
"""Compare the currently running application with the last snapshot.
529
455
own implemetnation.
531
457
The default implementation calls
532
:py:func:`autopilot.introspection.get_application_launcher`
458
:py:func:`autopilot.application.get_application_launcher_wrapper`
535
# default implementation is in autopilot.introspection:
536
return get_application_launcher(app_path)
538
def _kill_pid(self, pid):
539
"""Kill the process with the specified pid."""
540
logger.info("waiting for process to exit.")
542
logger.info("Killing process %d", pid)
543
os.killpg(pid, signal.SIGTERM)
545
logger.info("Appears process has already exited.")
547
if not _is_process_running(pid):
551
"Killing process group, since it hasn't exited after "
554
os.killpg(pid, signal.SIGKILL)
557
def _kill_process(self, process):
558
"""Kill the process, and return the stdout, stderr and return code."""
561
logger.info("waiting for process to exit.")
563
logger.info("Killing process %d", process.pid)
564
os.killpg(process.pid, signal.SIGTERM)
566
logger.info("Appears process has already exited.")
568
tmp_out, tmp_err = process.communicate()
571
if not _is_process_running(process.pid):
575
"Killing process group, since it hasn't exited after "
578
os.killpg(process.pid, signal.SIGKILL)
580
return stdout, stderr, process.returncode
582
def _kill_process_and_attach_logs(self, process):
583
stdout, stderr, return_code = self._kill_process(process)
584
self.addDetail('process-return-code', text_content(str(return_code)))
585
self.addDetail('process-stdout', text_content(stdout))
586
self.addDetail('process-stderr', text_content(stderr))
589
def _is_process_running(pid):
590
return psutil.pid_exists(pid)
461
# default implementation is in autopilot.application:
462
return get_application_launcher_wrapper(app_path)
593
465
def _get_process_snapshot():