Lomiri
Loading...
Searching...
No Matches
__init__.py
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2#
3# Lomiri Autopilot Test Suite
4# Copyright (C) 2012, 2013, 2014, 2015 Canonical Ltd.
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"""lomiri autopilot tests."""
21
22try:
23 from gi.repository import Gio
24except ImportError:
25 Gio = None
26
27import logging
28import os
29
30from autopilot import introspection
31from autopilot.platform import model
32from autopilot.testcase import AutopilotTestCase
33from autopilot.matchers import Eventually
34from autopilot.display import Display
35from testtools.matchers import Equals
36
37import lomiriuitoolkit
38from lomiriuitoolkit import (
39 fixture_setup as toolkit_fixtures,
40 lomiri_scenarios
41)
42
43from lomiri import (
44 get_lib_path,
45 get_binary_path,
46 get_mocks_library_path,
47 get_default_extra_mock_libraries,
48 get_data_dirs
49)
50from lomiri import (
51 fixture_setup,
52 process_helpers
53)
54from lomiri import (
55 dash as dash_helpers,
56 shell
57)
58
59
60logger = logging.getLogger(__name__)
61
62LOMIRISHELL_GSETTINGS_SCHEMA = "org.compiz.lomirishell"
63LOMIRISHELL_GSETTINGS_PATH = "/org/compiz/profiles/lomiri/plugins/lomirishell/"
64LOMIRISHELL_LAUNCHER_KEY = "launcher-hide-mode"
65LOMIRISHELL_LAUNCHER_MODE = 1 # launcher hidden
66
67
69 """Return True if Lomiri7 is running. Otherwise, return False."""
70 return (
71 Gio is not None and
72 LOMIRISHELL_GSETTINGS_SCHEMA in
73 Gio.Settings.list_relocatable_schemas()
74 )
75
76
78 """Return the QML2_IMPORT_PATH value with the mock path prepended."""
79 qml_import_path = [get_mocks_library_path()]
80 if os.getenv('QML2_IMPORT_PATH') is not None:
81 qml_import_path.append(os.getenv('QML2_IMPORT_PATH'))
82
83 qml_import_path = ':'.join(qml_import_path)
84 logger.info("New QML2 import path: %s", qml_import_path)
85 return qml_import_path
86
87
88class LomiriTestCase(AutopilotTestCase):
89
90 """A test case base class for the Lomiri shell tests."""
91
92 @classmethod
93 def setUpClass(cls):
94 try:
95 is_lomiri_running = process_helpers.is_job_running('lomiri')
96 except process_helpers.JobError as e:
97 xdg_config_home = os.getenv(
98 'XDG_CONFIG_HOME', os.path.join(os.getenv('HOME'), '.config'))
99 upstart_config_path = os.path.join(xdg_config_home, 'upstart')
100 logger.error(
101 '`initctl status lomiri` failed, most probably the '
102 'lomiri session could not be found:\n\n'
103 '{0}\n'
104 'Please install lomiri or copy data/lomiri.conf to '
105 '{1}\n'.format(e.output, upstart_config_path)
106 )
107 raise e
108 else:
109 assert not is_lomiri_running, (
110 'Lomiri is currently running, these tests require it to be '
111 'stopped.\n'
112 'Please run this command before running these tests: \n'
113 'initctl stop lomiri\n')
114
115 def setUp(self):
116 super().setUp()
118 self.useFixture(toolkit_fixtures.HideLomiri7Launcher())
119
120 self._proxy = None
121 self._qml_mock_enabled = True
122 self._data_dirs_mock_enabled = True
123 self._environment = {}
124
126
127 def _setup_display_details(self):
128 scale_divisor = self._determine_geometry()
129 self._setup_grid_size(scale_divisor)
130
132 """Use the geometry that may be supplied or use the default."""
133 width = getattr(self, 'app_width', 0)
134 height = getattr(self, 'app_height', 0)
135 scale_divisor = 1
136 self.lomiri_geometry_args = []
137 if width > 0 and height > 0:
138 if self._geo_larger_than_display(width, height):
139 scale_divisor = self._get_scaled_down_geo(width, height)
140 width = width / scale_divisor
141 height = height / scale_divisor
142 logger.info(
143 "Geometry larger than display, scaled down to: %dx%d",
144 width,
145 height
146 )
147 geo_string = "%dx%d" % (width, height)
148 self.lomiri_geometry_args = [
149 '-windowgeometry',
150 geo_string,
151 '-frameless',
152 '-mousetouch'
153 ]
154 return scale_divisor
155
156 def _setup_grid_size(self, scale_divisor):
157 """Use the grid size that may be supplied or use the default."""
158 if getattr(self, 'grid_unit_px', 0) == 0:
159 if os.getenv('GRID_UNIT_PX') == None:
160 self.grid_size = 8
161 else:
162 self.grid_size = int(os.getenv('GRID_UNIT_PX'))
163 else:
164 self.grid_size = int(self.grid_unit_px / scale_divisor)
165 self._environment["GRID_UNIT_PX"] = str(self.grid_size)
166
167 def _geo_larger_than_display(self, width, height):
168 should_scale = getattr(self, 'scale_geo', True)
169 if should_scale:
170 screen = Display.create()
171 screen_width = screen.get_screen_width()
172 screen_height = screen.get_screen_height()
173 return (width > screen_width) or (height > screen_height)
174 else:
175 return False
176
177 def _get_scaled_down_geo(self, width, height):
178 divisor = 1
179 while self._geo_larger_than_display(width / divisor, height / divisor):
180 divisor = divisor * 2
181 return divisor
182
183 def launch_lomiri(self, mode="full-greeter", *args):
184 """
185 Launch the lomiri shell, return a proxy object for it.
186
187 :param str mode: The type of greeter/shell mode to use
188 :param args: A list of aguments to pass to lomiri
189
190 """
191 binary_path = get_binary_path()
192 lib_path = get_lib_path()
193
194 logger.info(
195 "Lib path is '%s', binary path is '%s'",
196 lib_path,
197 binary_path
198 )
199
200 self.patch_lightdm_mock()
201
202 if self._qml_mock_enabled:
203 self._environment['QML2_IMPORT_PATH'] = (
205 )
206
208 self._patch_data_dirs()
209
210 lomiri_cli_args_list = ["--mode={}".format(mode)]
211 if len(args) != 0:
212 lomiri_cli_args_list += args
213
214 app_proxy = self._launch_lomiri_with_upstart(
215 binary_path,
216 self.lomiri_geometry_args + lomiri_cli_args_list
217 )
218
219 self._set_proxy(app_proxy)
220
221 # Ensure that the dash is visible before we return:
222 logger.debug("Lomiri started, waiting for it to be ready.")
223 self.wait_for_lomiri()
224 logger.debug("Lomiri loaded and ready.")
225
226 if model() == 'Desktop':
227 # On desktop, close the dash because it's opened in a separate
228 # window and it gets in the way.
229 process_helpers.stop_job('lomiri-dash')
230
231 return app_proxy
232
233 def _launch_lomiri_with_upstart(self, binary_path, args):
234 logger.info("Starting lomiri")
235 self.useFixture(toolkit_fixtures.InitctlEnvironmentVariable(
236 global_=True, QT_LOAD_TESTABILITY=1))
237
238 variables = self._environment
239 variables['ARGS'] = " ".join(args)
240 launch_lomiri_fixture = fixture_setup.RestartLomiriWithTestability(
241 binary_path, variables)
242 self.useFixture(launch_lomiri_fixture)
243 return launch_lomiri_fixture.lomiri_proxy
244
245 def _patch_data_dirs(self):
246 data_dirs = get_data_dirs(self._data_dirs_mock_enabled)
247 if data_dirs is not None:
248 self._environment['XDG_DATA_DIRS'] = data_dirs
249
250 def patch_lightdm_mock(self):
251 logger.info("Setting up LightDM mock lib")
252 new_ld_library_path = [
253 get_default_extra_mock_libraries(),
255 ]
256 if os.getenv('LD_LIBRARY_PATH') is not None:
257 new_ld_library_path.append(os.getenv('LD_LIBRARY_PATH'))
258
259 new_ld_library_path = ':'.join(new_ld_library_path)
260 logger.info("New library path: %s", new_ld_library_path)
261
262 self._environment['LD_LIBRARY_PATH'] = new_ld_library_path
263
264 def _get_lightdm_mock_path(self):
265 lib_path = get_mocks_library_path()
266 lightdm_mock_path = os.path.abspath(
267 os.path.join(lib_path, "liblightdm")
268 )
269
270 if not os.path.exists(lightdm_mock_path):
271 raise RuntimeError(
272 "LightDM mock does not exist at path '%s'."
273 % (lightdm_mock_path)
274 )
275 return lightdm_mock_path
276
277 def _set_proxy(self, proxy):
278 """Keep a copy of the proxy object, so we can use it to get common
279 parts of the shell later on.
280
281 """
282 self._proxy = proxy
283 self.addCleanup(self._clear_proxy_clear_proxy)
284
285 def _clear_proxy(self):
286 self._proxy = None
287
288 def wait_for_lomiri(self):
289 greeter = self.main_window.wait_select_single(objectName='greeter')
290 greeter.waiting.wait_for(False)
291
292 def get_dash(self):
293 pid = process_helpers.get_job_pid('lomiri-dash')
294 dash_proxy = introspection.get_proxy_object_for_existing_process(
295 pid=pid,
296 emulator_base=lomiriuitoolkit.LomiriUIToolkitCustomProxyObjectBase
297 )
298 dash_app = dash_helpers.DashApp(dash_proxy)
299 return dash_app.dash
300
301 @property
302 def main_window(self):
303 return self._proxy.select_single(shell.ShellView)
304
305
306class DashBaseTestCase(AutopilotTestCase):
307
308 scenarios = lomiri_scenarios.get_device_simulation_scenarios()
309 qml_mock_enabled = True
310 environment = {}
311
312 def setUp(self):
313 super().setUp()
314
316 self.useFixture(toolkit_fixtures.HideLomiri7Launcher())
317
318 if model() != 'Desktop':
319 # On the phone, we need lomiri to be running and unlocked.
320 self.addCleanup(process_helpers.stop_job, 'lomiri')
321 process_helpers.restart_lomiri_with_testability()
322 process_helpers.unlock_lomiri()
323
324 self.ensure_dash_not_running()
325
326 if self.qml_mock_enabled:
327 self.environment['QML2_IMPORT_PATH'] = (
329 )
330
331 if self.should_simulate_device():
332 # This sets the grid units, so it should be called before launching
333 # the app.
334 self.simulate_device()
335
336 binary_path = get_binary_path('lomiri-dash')
337 dash_proxy = self.launch_dash(binary_path, self.environment)
338
339 self.dash_app = dash_helpers.DashApp(dash_proxy)
340 self.dash = self.dash_app.dash
341 self.wait_for_dash()
342
343 def ensure_dash_not_running(self):
344 if process_helpers.is_job_running('lomiri-dash'):
345 process_helpers.stop_job('lomiri-dash')
346
347 def launch_dash(self, binary_path, variables):
348 launch_dash_app_fixture = fixture_setup.LaunchDashApp(
349 binary_path, variables)
350 self.useFixture(launch_dash_app_fixture)
351 return launch_dash_app_fixture.application_proxy
352
353 def wait_for_dash(self):
354 home_scope = self.dash.get_scope_by_index(0)
355 # FIXME! There is a huge timeout here for when we're doing CI on
356 # VMs. See lp:1203715
357 self.assertThat(
358 home_scope.isLoaded,
359 Eventually(Equals(True), timeout=60)
360 )
361 self.assertThat(home_scope.isCurrent, Eventually(Equals(True)))
362
363 def should_simulate_device(self):
364 return (hasattr(self, 'app_width') and hasattr(self, 'app_height') and
365 hasattr(self, 'grid_unit_px'))
366
367 def simulate_device(self):
368 simulate_device_fixture = self.useFixture(
369 toolkit_fixtures.SimulateDevice(
370 self.app_width, self.app_height, self.grid_unit_px))
371 self.environment['GRID_UNIT_PX'] = simulate_device_fixture.grid_unit_px
372 self.environment['ARGS'] = '-windowgeometry {0}x{1}'\
373 .format(simulate_device_fixture.app_width,
374 simulate_device_fixture.app_height)
_setup_grid_size(self, scale_divisor)
Definition __init__.py:156
launch_lomiri(self, mode="full-greeter", *args)
Definition __init__.py:183
_get_scaled_down_geo(self, width, height)
Definition __init__.py:177
_launch_lomiri_with_upstart(self, binary_path, args)
Definition __init__.py:233
_geo_larger_than_display(self, width, height)
Definition __init__.py:167
get_qml_import_path_with_mock()
Definition __init__.py:77