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 == 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, time
102  uinput = evdev.UInput(name='unity8-autopilot-power-button',
103  devnode='/dev/autopilot-uinput')
104  # One press and release to turn screen off (locking unity)
105  uinput.write(evdev.ecodes.EV_KEY, evdev.ecodes.KEY_POWER, 1)
106  uinput.write(evdev.ecodes.EV_KEY, evdev.ecodes.KEY_POWER, 0)
107  uinput.syn()
108  time.sleep(1)
109  # And another press and release to turn screen back on
110  uinput.write(evdev.ecodes.EV_KEY, evdev.ecodes.KEY_POWER, 1)
111  uinput.write(evdev.ecodes.EV_KEY, evdev.ecodes.KEY_POWER, 0)
112  uinput.syn()
113 
114 
115 def restart_unity_with_testability(*args):
116  """Restarts (or starts) unity with testability enabled.
117 
118  Passes *args arguments to the launched process.
119 
120  """
121  args += ("QT_LOAD_TESTABILITY=1",)
122  return restart_unity(*args)
123 
124 
125 def restart_unity(*args):
126  """Restarts (or starts) unity8 using the provided arguments.
127 
128  Passes *args arguments to the launched process.
129 
130  :raises subprocess.CalledProcessError: if unable to stop or start the
131  unity8 upstart job.
132 
133  """
134  status = _get_unity_status()
135  if "start/" in status:
136  try:
137  output = subprocess.check_output(
138  ['/sbin/initctl', 'stop', 'unity8'])
139  logger.info(output)
140  except subprocess.CalledProcessError as e:
141  e.args += (
142  "Failed to stop running instance of unity8: %s" % e.output,
143  )
144  raise
145 
146  try:
147  command = ['/sbin/initctl', 'start', 'unity8'] + list(args)
148  output = subprocess.check_output(
149  command,
150  stderr=subprocess.STDOUT,
151  universal_newlines=True,
152  )
153  logger.info(output)
154  pid = _get_unity_pid()
155  except subprocess.CalledProcessError as e:
156  e.args += ("Failed to start unity8: %s" % e.output,)
157  raise
158  else:
159  return _get_unity_proxy_object(pid)
160 
161 
162 def _get_unity_status():
163  try:
164  return subprocess.check_output([
165  '/sbin/initctl',
166  'status',
167  'unity8'
168  ], universal_newlines=True)
169  except subprocess.CalledProcessError as e:
170  raise CannotAccessUnity("Unable to get unity's status: %s" % str(e))
171 
172 
173 def _get_unity_pid():
174  status = _get_unity_status()
175  if not "start/" in status:
176  raise CannotAccessUnity("Unity is not in the running state.")
177  return int(status.split()[-1])
178 
179 
180 def _get_unity_proxy_object(pid):
181  return get_proxy_object_for_existing_process(
182  pid=pid,
183  emulator_base=emulators.UnityEmulatorBase,
184  )