@@ -1,81 +1,187 @@
11# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2−# Copyright 2013 Canonical
32#
4−# This program is free software: you can redistribute it and/or modify it
5−# under the terms of the GNU General Public License version 3, as published
6−# by the Free Software Foundation.
7−
8−"""unity8 autopilot tests."""
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2012-2013 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+#
919
10−import os.path
11−import sysconfig
20+"""unity autopilot tests."""
1221
22+from autopilot.platform import model
1323from autopilot.testcase import AutopilotTestCase
1424from autopilot.matchers import Eventually
15−from autopilot.platform import model
16−from testtools.matchers import Equals
25+from autopilot.input import Touch
26+from autopilot.display import Display
27+import logging
28+import os.path
29+from testtools.matchers import Equals, NotEquals
1730
31+from unity8 import get_lib_path, get_binary_path, get_mocks_library_path
32+from unity8.shell.emulators import UnityEmulatorBase
33+from unity8.shell.emulators.dash import Dash
1834from unity8.shell.emulators.main_window import MainWindow
1935
20−class FormFactors(object):
21− Phone, Tablet, Desktop = range(3)
2236
23−class ShellTestCase(AutopilotTestCase):
37+logger = logging.getLogger(__name__)
2438
25− """A common test case class that provides several useful methods for shell tests."""
2639
27− libdir = "/usr/lib/{0}/unity8".format(sysconfig.get_config_var('MULTIARCH'))
28− lightdm_mock = "full"
40+def _get_device_emulation_scenarios():
41+ if model() == 'Desktop':
42+ return [
43+ (
44+ 'Desktop Nexus 4',
45+ dict(app_width=768, app_height=1280, grid_unit_px=18)
46+ ),
47+ (
48+ 'Desktop Nexus 10',
49+ dict(app_width=2560, app_height=1600, grid_unit_px=20)
50+ ),
51+ ]
52+ else:
53+ return [
54+ (
55+ 'Native Device',
56+ dict(app_width=0, app_height=0, grid_unit_px=0)
57+ )
58+ ]
2959
30− def setUp(self, geometry, grid_size):
31− super(ShellTestCase, self).setUp()
32− # Lets assume we are installed system wide if this file is somewhere in /usr
33− if grid_size != "0":
34− os.environ['GRID_UNIT_PX'] = grid_size
35− self.grid_size = int(grid_size)
36− else:
37− self.grid_size = int(os.environ['GRID_UNIT_PX'])
38− if os.path.realpath(__file__).startswith("/usr/"):
39− self.launch_test_installed(geometry)
60+
61+class UnityTestCase(AutopilotTestCase):
62+
63+ """A test case base class for the Unity shell tests."""
64+
65+ def setUp(self):
66+ super(UnityTestCase, self).setUp()
67+ self._proxy = None
68+ self.touch = Touch.create()
69+ self._setup_display_details()
70+
71+ def _setup_display_details(self):
72+ scale_divisor = self._determine_geometry()
73+ self._setup_grid_size(scale_divisor)
74+
75+ def _determine_geometry(self):
76+ """Use the geometry that may be supplied or use the default."""
77+ width = getattr(self, 'app_width', 0)
78+ height = getattr(self, 'app_height', 0)
79+ scale_divisor = 1
80+ if width == 0 and width == 0:
81+ self.unity_geometry_args = ['-fullscreen']
4082 else:
41− self.launch_test_local(geometry)
42−
43− def launch_test_local(self, geometry):
44− os.environ['LD_LIBRARY_PATH'] = "../../builddir/tests/mocks/libusermetrics:../../builddir/tests/mocks/LightDM" + self.lightdm_mock
45− os.environ['QML2_IMPORT_PATH'] = "../../builddir/tests/mocks"
46− if geometry != "0x0":
47− self.app = self.launch_test_application(
48− "../../builddir/unity8", "-geometry", geometry, "-frameless", app_type='qt')
83+ if self._geo_larger_than_display(width, height):
84+ scale_divisor = self._get_scaled_down_geo(width, height)
85+ width = width / scale_divisor
86+ height = height / scale_divisor
87+ logger.info(
88+ "Geometry larger than display, scaled down to: %dx%d",
89+ width,
90+ height
91+ )
92+ geo_string = "%dx%d" % (width, height)
93+ self.unity_geometry_args = [
94+ '-geometry',
95+ geo_string,
96+ '-frameless',
97+ '-mousetouch'
98+ ]
99+ return scale_divisor
100+
101+ def _setup_grid_size(self, scale_divisor):
102+ """Use the grid size that may be supplied or use the default."""
103+ if getattr(self, 'grid_unit_px', 0) == 0:
104+ self.grid_size = int(os.getenv('GRID_UNIT_PX'))
49105 else:
50− self.app = self.launch_test_application(
51− "../../builddir/unity8", "-fullscreen", app_type='qt')
52−
53− def launch_test_installed(self, geometry):
54− os.environ['LD_LIBRARY_PATH'] = "{0}/qml/mocks/libusermetrics:{0}/qml/mocks/LightDM/{1}".format(self.libdir, self.lightdm_mock)
55− os.environ['QML2_IMPORT_PATH'] = "{0}/qml/mocks".format(self.libdir)
56− if model() == 'Desktop' and geometry != "0x0":
57− self.app = self.launch_test_application(
58− "unity8", "-geometry", geometry, "-frameless", app_type='qt')
106+ self.grid_size = int(self.grid_unit_px / scale_divisor)
107+ self.patch_environment("GRID_UNIT_PX", str(self.grid_size))
108+
109+ def _geo_larger_than_display(self, width, height):
110+ should_scale = getattr(self, 'scale_geo', True)
111+ if should_scale:
112+ screen = Display.create()
113+ screen_width = screen.get_screen_width()
114+ screen_height = screen.get_screen_height()
115+ return (width > screen_width) or (height > screen_height)
59116 else:
60− self.app = self.launch_test_application(
61− "unity8", "-fullscreen", app_type='qt')
117+ return False
118+
119+ def _get_scaled_down_geo(self, width, height):
120+ divisor = 1
121+ while self._geo_larger_than_display(width / divisor, height / divisor):
122+ divisor = divisor * 2
123+ return divisor
124+
125+ def launch_unity(self):
126+ """Launch the unity shell, return a proxy object for it."""
127+ binary_path = get_binary_path()
128+ lib_path = get_lib_path()
129+
130+ logger.info(
131+ "Lib path is '%s', binary path is '%s'",
132+ lib_path,
133+ binary_path
134+ )
135+
136+ self._setup_extra_mock_environment_patch()
137+
138+ app_proxy = self.launch_test_application(
139+ binary_path,
140+ *self.unity_geometry_args,
141+ app_type='qt',
142+ emulator_base=UnityEmulatorBase
143+ )
144+ self._set_proxy(app_proxy)
145+
146+ # Ensure that the dash is visible before we return:
147+ logger.debug("Unity started, waiting for it to be ready.")
148+ self.assertUnityReady()
149+ logger.debug("Unity loaded and ready.")
150+
151+ return app_proxy
152+
153+ def _setup_extra_mock_environment_patch(self):
154+ mocks_library_path = get_mocks_library_path()
155+ self.patch_environment('QML2_IMPORT_PATH', mocks_library_path)
156+
157+ def _set_proxy(self, proxy):
158+ """Keep a copy of the proxy object, so we can use it to get common
159+ parts of the shell later on.
160+
161+ """
162+ self._proxy = proxy
163+ self.addCleanup(self._clear_proxy)
164+
165+ def _clear_proxy(self):
166+ self._proxy = None
62167
63− def skipWrapper(*args, **kwargs):
64− pass
168+ def assertUnityReady(self):
169+ dash = self.get_dash()
170+ home_scope = dash.get_scope('home')
65171
66− def form_factor(self):
67− return FormFactors.Desktop
172+ # FIXME! There is a huge timeout here for when we're doing CI on
173+ # VMs. See lp:1203715
174+ self.assertThat(
175+ home_scope.isLoaded,
176+ Eventually(Equals(True), timeout=60)
177+ )
178+ self.assertThat(home_scope.isCurrent, Eventually(Equals(True)))
68179
69− def __getattribute__(self, attr_name):
70− attr = object.__getattribute__(self, attr_name);
71− if attr_name.startswith("test_"):
72− try:
73− if self.form_factor() in attr.blacklist:
74− return self.skipWrapper
75− except:
76− pass
77− return attr
180+ def get_dash(self):
181+ dash = self._proxy.select_single(Dash)
182+ self.assertThat(dash, NotEquals(None))
183+ return dash
78184
79185 @property
80186 def main_window(self):
81− return MainWindow(self.app)
187+ return MainWindow(self._proxy)