Unity 8
 All Classes Functions Properties
process_helpers.py
1 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2 #
3 # Unity Autopilot Utilities
4 # Copyright (C) 2013, 2014 Canonical
5 #
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 #
19 
20 import logging
21 import subprocess
22 import sys
23 
24 # This has to work in both python 3 (ap 1.5) and py2 (ap legacy 1.4.1) so we
25 # pick the correct location in each case. Remove the py2 branch once we no
26 # longer need to support python 2.
27 if sys.version >= '3':
28  from autopilot.exceptions import ProcessSearchError
29 else:
30  from autopilot.introspection import ProcessSearchError
31 from autopilot.introspection import get_proxy_object_for_existing_process
32 
33 from unity8.shell import emulators
34 from unity8.shell.emulators import main_window as main_window_emulator
35 
36 logger = logging.getLogger(__name__)
37 
38 
39 class CannotAccessUnity(Exception):
40  pass
41 
42 
43 def unlock_unity(unity_proxy_obj=None):
44  """Helper function that attempts to unlock the unity greeter.
45 
46  If unity_proxy_object is None create a proxy object by querying for the
47  running unity process.
48  Otherwise re-use the passed proxy object.
49 
50  :raises RuntimeError: if the greeter attempts and fails to be unlocked.
51 
52  :raises RuntimeWarning: when the greeter cannot be found because it is
53  already unlocked.
54  :raises CannotAccessUnity: if unity is not introspectable or cannot be
55  found on dbus.
56  :raises CannotAccessUnity: if unity's upstart status is not "start" or the
57  upstart job cannot be found at all.
58 
59  """
60  if unity_proxy_obj is None:
61  try:
62  pid = _get_unity_pid()
63  unity = _get_unity_proxy_object(pid)
64  main_window = unity.select_single(main_window_emulator.QQuickView)
65  except ProcessSearchError as e:
66  raise CannotAccessUnity(
67  "Cannot introspect unity, make sure that it has been started "
68  "with testability. Perhaps use the function "
69  "'restart_unity_with_testability' this module provides."
70  "(%s)"
71  % str(e)
72  )
73  else:
74  main_window = unity_proxy_obj.select_single(
75  main_window_emulator.QQuickView)
76 
77  greeter = main_window.get_greeter()
78  if greeter.created is False:
79  raise RuntimeWarning("Greeter appears to be already unlocked.")
80 
81  # Because of potential input jerkiness under heavy load,
82  # retry unlocking the greeter two times.
83  # https://bugs.launchpad.net/ubuntu/+bug/1260113
84 
85  retries = 3
86  while retries > 0:
87  try:
88  greeter.swipe()
89  except AssertionError:
90  retries -= 1
91  if retries == 0:
92  raise
93  logger.info("Failed to unlock greeter, trying again...")
94  else:
95  logger.info("Greeter unlocked, continuing.")
96  break
97 
98 
99 def lock_unity(unity_proxy_obj=None):
100  """Helper function that attempts to lock the unity greeter."""
101  import evdev
102  import time
103  uinput = evdev.UInput(name='unity8-autopilot-power-button',
104  devnode='/dev/autopilot-uinput')
105  # One press and release to turn screen off (locking unity)
106  uinput.write(evdev.ecodes.EV_KEY, evdev.ecodes.KEY_POWER, 1)
107  uinput.write(evdev.ecodes.EV_KEY, evdev.ecodes.KEY_POWER, 0)
108  uinput.syn()
109  time.sleep(1)
110  # And another press and release to turn screen back on
111  uinput.write(evdev.ecodes.EV_KEY, evdev.ecodes.KEY_POWER, 1)
112  uinput.write(evdev.ecodes.EV_KEY, evdev.ecodes.KEY_POWER, 0)
113  uinput.syn()
114 
115 
116 def restart_unity_with_testability(*args):
117  """Restarts (or starts) unity with testability enabled.
118 
119  Passes *args arguments to the launched process.
120 
121  """
122  args += ("QT_LOAD_TESTABILITY=1",)
123  return restart_unity(*args)
124 
125 
126 def restart_unity(*args):
127  """Restarts (or starts) unity8 using the provided arguments.
128 
129  Passes *args arguments to the launched process.
130 
131  :raises subprocess.CalledProcessError: if unable to stop or start the
132  unity8 upstart job.
133 
134  """
135  status = _get_unity_status()
136  if "start/" in status:
137  try:
138  output = subprocess.check_output(
139  ['/sbin/initctl', 'stop', 'unity8'])
140  logger.info(output)
141  except subprocess.CalledProcessError as e:
142  e.args += (
143  "Failed to stop running instance of unity8: %s" % e.output,
144  )
145  raise
146 
147  try:
148  command = ['/sbin/initctl', 'start', 'unity8'] + list(args)
149  output = subprocess.check_output(
150  command,
151  stderr=subprocess.STDOUT,
152  universal_newlines=True,
153  )
154  logger.info(output)
155  pid = _get_unity_pid()
156  except subprocess.CalledProcessError as e:
157  e.args += ("Failed to start unity8: %s" % e.output,)
158  raise
159  else:
160  return _get_unity_proxy_object(pid)
161 
162 
163 def _get_unity_status():
164  try:
165  return subprocess.check_output([
166  '/sbin/initctl',
167  'status',
168  'unity8'
169  ], universal_newlines=True)
170  except subprocess.CalledProcessError as e:
171  raise CannotAccessUnity("Unable to get unity's status: %s" % str(e))
172 
173 
174 def _get_unity_pid():
175  status = _get_unity_status()
176  if "start/" not in status:
177  raise CannotAccessUnity("Unity is not in the running state.")
178  return int(status.split()[-1])
179 
180 
181 def _get_unity_proxy_object(pid):
182  return get_proxy_object_for_existing_process(
183  pid=pid,
184  emulator_base=emulators.UnityEmulatorBase,
185  )