@@ -28,9 +28,12 @@ from autopilot.platform import model
28from autopilot.testcase import AutopilotTestCase
28from autopilot.testcase import AutopilotTestCase
29from autopilot.matchers import Eventually
29from autopilot.matchers import Eventually
30from autopilot.input import Touch
30from autopilot.input import Touch
31+from autopilot.introspection import get_proxy_object_for_existing_process
31from autopilot.display import Display
32from autopilot.display import Display
32import logging
33import logging
33import os.path
34import os.path
34from testtools.matchers import Equals, NotEquals
37from testtools.matchers import Equals, NotEquals
36from unity8 import (
39from unity8 import (
@@ -38,6 +41,7 @@ from unity8 import (
38 get_binary_path,
41 get_binary_path,
39 get_mocks_library_path,
42 get_mocks_library_path,
40 get_default_extra_mock_libraries,
43 get_default_extra_mock_libraries,
42from unity8.shell.emulators import UnityEmulatorBase
46from unity8.shell.emulators import UnityEmulatorBase
43from unity8.shell.emulators.dash import Dash
47from unity8.shell.emulators.dash import Dash
@@ -51,6 +55,7 @@ UNITYSHELL_GSETTINGS_PATH = "/org/compiz/profiles/unity/plugins/unityshell/"
51UNITYSHELL_LAUNCHER_KEY = "launcher-hide-mode"
55UNITYSHELL_LAUNCHER_KEY = "launcher-hide-mode"
52UNITYSHELL_LAUNCHER_MODE = 1 # launcher hidden
56UNITYSHELL_LAUNCHER_MODE = 1 # launcher hidden
54def _get_device_emulation_scenarios(devices='All'):
59def _get_device_emulation_scenarios(devices='All'):
55 nexus4 = ('Desktop Nexus 4',
60 nexus4 = ('Desktop Nexus 4',
56 dict(app_width=768, app_height=1280, grid_unit_px=18))
61 dict(app_width=768, app_height=1280, grid_unit_px=18))
@@ -67,14 +72,43 @@ def _get_device_emulation_scenarios(devices='All'):
67 elif devices == 'Nexus10':
72 elif devices == 'Nexus10':
68 return [nexus10]
73 return [nexus10]
70− raise RuntimeException('Unrecognized device-option "%s" passed.' % devices)
76+ 'Unrecognized device-option "%s" passed.' % devices
72 return [native]
79 return [native]
74class UnityTestCase(AutopilotTestCase):
82class UnityTestCase(AutopilotTestCase):
76 """A test case base class for the Unity shell tests."""
84 """A test case base class for the Unity shell tests."""
89+ output = subprocess.check_output([
93+ ], stderr=subprocess.STDOUT)
94+ except subprocess.CalledProcessError, e:
96+ "Error: `initctl status unity8` failed, most probably the unity8 session could not be found:\n\n"
98+ "Please install unity8 or copy data/unity8.conf to {1}\n".format(
100+ os.path.join(os.getenv("XDG_CONFIG_HOME", os.path.join(os.getenv("HOME"), ".config")), "upstart")
104+ if "start/" in output:
106+ "Error: Unity is currently running, these tests require it to be 'stopped'.\n"
107+ "Please run this command before running these tests: \n"
108+ "initctl stop unity8\n"
78 def setUp(self):
112 def setUp(self):
79 super(UnityTestCase, self).setUp()
113 super(UnityTestCase, self).setUp()
80 if Gio is not None and UNITYSHELL_GSETTINGS_SCHEMA in Gio.Settings.list_relocatable_schemas():
114 if Gio is not None and UNITYSHELL_GSETTINGS_SCHEMA in Gio.Settings.list_relocatable_schemas():
@@ -87,6 +121,16 @@ class UnityTestCase(AutopilotTestCase):
87 self._proxy = None
121 self._proxy = None
88 self._lightdm_mock_type = None
122 self._lightdm_mock_type = None
89 self._qml_mock_enabled = True
123 self._qml_mock_enabled = True
124+ self._environment = {}
126+ self._patch_data_dirs()
128+ #### FIXME: This is a work around re: lp:1238417 ####
129+ if model() != "Desktop":
130+ from autopilot.input import _uinput
131+ _uinput._touch_device = _uinput.create_touch_device()
90 self.touch = Touch.create()
134 self.touch = Touch.create()
91 self._setup_display_details()
135 self._setup_display_details()
@@ -129,6 +173,9 @@ class UnityTestCase(AutopilotTestCase):
129 self.grid_size = int(os.getenv('GRID_UNIT_PX'))
173 self.grid_size = int(os.getenv('GRID_UNIT_PX'))
131 self.grid_size = int(self.grid_unit_px / scale_divisor)
175 self.grid_size = int(self.grid_unit_px / scale_divisor)
176+ self._environment["GRID_UNIT_PX"] = str(self.grid_size)
177+ # FIXME this is only needed for Hud.get_close_button_coords
178+ # we should probably rework it so that it's not required
132 self.patch_environment("GRID_UNIT_PX", str(self.grid_size))
179 self.patch_environment("GRID_UNIT_PX", str(self.grid_size))
134 def _geo_larger_than_display(self, width, height):
181 def _geo_larger_than_display(self, width, height):
@@ -147,6 +194,38 @@ class UnityTestCase(AutopilotTestCase):
147 divisor = divisor * 2
194 divisor = divisor * 2
148 return divisor
195 return divisor
197+ def _patch_environment(self, key, value):
198+ """Wrapper for patching env for upstart environment."""
200+ current_value = subprocess.check_output([
205+ ], stderr=subprocess.STDOUT).rstrip()
206+ except subprocess.CalledProcessError:
207+ current_value = None
213+ "%s=%s" % (key, value)
214+ ], stderr=subprocess.STDOUT)
215+ self.addCleanup(self._upstart_reset_env, key, current_value)
217+ def _upstart_reset_env(self, key, value):
218+ logger.info("Resetting upstart env %s to %s", key, value)
220+ subprocess.call(["/sbin/initctl", "unset-env", key], stderr=subprocess.STDOUT)
226+ "%s=%s" % (key, value)
227+ ], stderr=subprocess.STDOUT)
150 def launch_unity(self, **kwargs):
229 def launch_unity(self, **kwargs):
151 """Launch the unity shell, return a proxy object for it."""
230 """Launch the unity shell, return a proxy object for it."""
152 binary_path = get_binary_path()
231 binary_path = get_binary_path()
@@ -164,13 +243,22 @@ class UnityTestCase(AutopilotTestCase):
164 if self._qml_mock_enabled:
243 if self._qml_mock_enabled:
165 self._setup_extra_mock_environment_patch()
244 self._setup_extra_mock_environment_patch()
167− app_proxy = self.launch_test_application(
246+ # FIXME: we shouldn't be doing this
247+ # $MIR_SOCKET, fallback to $XDG_RUNTIME_DIR/mir_socket and /tmp/mir_socket as last resort
249+ os.unlink(os.getenv('MIR_SOCKET', os.path.join(os.getenv('XDG_RUNTIME_DIR', "/tmp"), "mir_socket")))
253+ os.unlink("/tmp/mir_socket")
257+ app_proxy = self._launch_unity_with_upstart(
168 binary_path,
258 binary_path,
169− *self.unity_geometry_args,
259+ self.unity_geometry_args
171− emulator_base=UnityEmulatorBase,
174 self._set_proxy(app_proxy)
262 self._set_proxy(app_proxy)
176 # Ensure that the dash is visible before we return:
264 # Ensure that the dash is visible before we return:
@@ -180,6 +268,43 @@ class UnityTestCase(AutopilotTestCase):
181 return app_proxy
269 return app_proxy
271+ def _launch_unity_with_upstart(self, binary_path, args):
272+ logger.info("Starting unity")
273+ self._patch_environment("QT_LOAD_TESTABILITY", 1)
275+ binary_arg = "BINARY=%s" % binary_path
276+ extra_args = "ARGS=%s" % " ".join(args)
278+ output = subprocess.check_output([
284+ ] + ["%s=%s" % (k,v) for k,v in self._environment.iteritems()],
285+ stderr=subprocess.STDOUT)
287+ self.addCleanup(self._cleanup_launching_upstart_unity)
289+ pid = int(output.split()[-1])
291+ return get_proxy_object_for_existing_process(
293+ emulator_base=UnityEmulatorBase,
296+ def _cleanup_launching_upstart_unity(self):
297+ logger.info("Stopping unity")
299+ subprocess.check_output(["/sbin/initctl", "stop", "unity8"], stderr=subprocess.STDOUT)
300+ except subprocess.CalledProcessError:
301+ logger.warning("Appears unity was already stopped!")
303+ def _patch_data_dirs(self):
304+ data_dirs = get_data_dirs()
305+ if data_dirs is not None:
306+ self._environment['XDG_DATA_DIRS'] = data_dirs
183 def patch_lightdm_mock(self, mock_type='single'):
308 def patch_lightdm_mock(self, mock_type='single'):
184 self._lightdm_mock_type = mock_type
309 self._lightdm_mock_type = mock_type
185 logger.info("Setting up LightDM mock type '%s'", mock_type)
310 logger.info("Setting up LightDM mock type '%s'", mock_type)
@@ -193,7 +318,7 @@ class UnityTestCase(AutopilotTestCase):
193 new_ld_library_path = ':'.join(new_ld_library_path)
318 new_ld_library_path = ':'.join(new_ld_library_path)
194 logger.info("New library path: %s", new_ld_library_path)
319 logger.info("New library path: %s", new_ld_library_path)
196− self.patch_environment('LD_LIBRARY_PATH', new_ld_library_path)
321+ self._environment['LD_LIBRARY_PATH'] = new_ld_library_path
198 def _get_lightdm_mock_path(self, mock_type):
323 def _get_lightdm_mock_path(self, mock_type):
199 lib_path = get_mocks_library_path()
324 lib_path = get_mocks_library_path()
@@ -215,7 +340,7 @@ class UnityTestCase(AutopilotTestCase):
216 qml_import_path = ':'.join(qml_import_path)
341 qml_import_path = ':'.join(qml_import_path)
217 logger.info("New QML2 import path: %s", qml_import_path)
342 logger.info("New QML2 import path: %s", qml_import_path)
218− self.patch_environment('QML2_IMPORT_PATH', qml_import_path)
343+ self._environment['QML2_IMPORT_PATH'] = qml_import_path
220 def _set_proxy(self, proxy):
345 def _set_proxy(self, proxy):
221 """Keep a copy of the proxy object, so we can use it to get common
346 """Keep a copy of the proxy object, so we can use it to get common