on xenial help sign in
Refactoring and cleanup of the Unity8 Autopilot tests.
84d9ad2Christopher Lee
Dash/DashContent.qml+5
@@ -115,6 +115,11 @@ Item {
115 asynchronous: true
115 asynchronous: true
116 source: scopeMapper.map(scope.id)
116 source: scopeMapper.map(scope.id)
117
117
118+ // these are needed for autopilot tests
119+ readonly property string scopeId: scope.id
120+ readonly property bool isCurrent: ListView.isCurrentItem
121+ readonly property bool isLoaded: status == Loader.Ready
122+
118 onLoaded: {
123 onLoaded: {
119 item.scope = Qt.binding(function() { return scope })
124 item.scope = Qt.binding(function() { return scope })
120 item.isCurrent = Qt.binding(function() { return ListView.isCurrentItem })
125 item.isCurrent = Qt.binding(function() { return ListView.isCurrentItem })
cmake/modules/autopilot.cmake+2−1
@@ -2,8 +2,9 @@ add_custom_target(autopilot)
2
2
3function(declare_autopilot_test TEST_NAME TEST_SUITE WORKING_DIR)
3function(declare_autopilot_test TEST_NAME TEST_SUITE WORKING_DIR)
4 add_custom_target(autopilot-${TEST_NAME}
4 add_custom_target(autopilot-${TEST_NAME}
5− COMMAND autopilot run ${TEST_SUITE}
5+ COMMAND LANG=C UBUNTU_ICON_THEME=ubuntu-mobile QML2_IMPORT_PATH=${SHELL_PRIVATE_LIBDIR}/qml/mocks autopilot run ${TEST_SUITE}
6 WORKING_DIRECTORY ${WORKING_DIR}
6 WORKING_DIRECTORY ${WORKING_DIR}
7+ DEPENDS install
7 )
8 )
8
9
9 add_dependencies(autopilot autopilot-${TEST_NAME})
10 add_dependencies(autopilot autopilot-${TEST_NAME})
tests/autopilot/setup.py+17−6
@@ -1,11 +1,22 @@
1#!/usr/bin/python
1#!/usr/bin/python
2−
3# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
4−# Copyright 2013 Canonical
5#
3#
4+# Unity Autopilot Test Suite
5+# Copyright (C) 2012-2013 Canonical
6+#
6−# This program is free software: you can redistribute it and/or modify it
7+# This program is free software: you can redistribute it and/or modify
7−# under the terms of the GNU General Public License version 3, as published
8+# it under the terms of the GNU General Public License as published by
8−# by the Free Software Foundation.
9+# the Free Software Foundation, either version 3 of the License, or
10+# (at your option) any later version.
11+#
12+# This program is distributed in the hope that it will be useful,
13+# but WITHOUT ANY WARRANTY; without even the implied warranty of
14+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+# GNU General Public License for more details.
16+#
17+# You should have received a copy of the GNU General Public License
18+# along with this program. If not, see <http://www.gnu.org/licenses/>.
19+#
9
20
10
21
11from distutils.core import setup
22from distutils.core import setup
@@ -14,7 +25,7 @@ from setuptools import find_packages
14setup(
25setup(
15 name='unity',
26 name='unity',
16 version='8.0',
27 version='8.0',
17− description='Unity 8 autopilot tests.',
28+ description='Unity autopilot tests.',
18 url='https://launchpad.net/unity',
29 url='https://launchpad.net/unity',
19 license='GPLv3',
30 license='GPLv3',
20 packages=find_packages(),
31 packages=find_packages(),
tests/autopilot/unity8/__init__.py+90−5
@@ -1,8 +1,93 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2−# Copyright 2013 Canonical
3#
2#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2012-2013 Canonical
5+#
4−# This program is free software: you can redistribute it and/or modify it
6+# This program is free software: you can redistribute it and/or modify
5−# under the terms of the GNU General Public License version 3, as published
7+# it under the terms of the GNU General Public License as published by
6−# by the Free Software Foundation.
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+"""unity autopilot tests and emulators - top level package."""
21+import os
22+import os.path
23+import subprocess
24+import sysconfig
25+
26+
27+def running_installed_tests():
28+ binary_path = get_binary_path()
29+ return binary_path.startswith('/usr')
30+
31+
32+def get_lib_path():
33+ """Return the library path to use in this test run."""
34+ if running_installed_tests():
35+ lib_path = os.path.join(
36+ "/usr/lib/",
37+ sysconfig.get_config_var('MULTIARCH'),
38+ "unity8"
39+ )
40+ else:
41+ binary_path = get_binary_path()
42+ lib_path = os.path.dirname(binary_path)
43+ return lib_path
44+
45+
46+def get_default_extra_mock_libraries():
47+ mocks_path = get_mocks_library_path()
48+ return os.path.join(mocks_path, 'libusermetrics')
49+
50+
51+def get_mocks_library_path():
52+ if running_installed_tests():
53+ mock_path = "qml/mocks/"
54+ else:
55+ mock_path = "../lib/x86_64-linux-gnu/unity8/qml/mocks/"
56+ lib_path = get_lib_path()
57+ ld_library_path = os.path.abspath(
58+ os.path.join(
59+ lib_path,
60+ mock_path,
61+ )
62+ )
63+
64+ if not os.path.exists(ld_library_path):
65+ raise RuntimeError(
66+ "Expected library path does not exists: %s." % (ld_library_path)
67+ )
68+ return ld_library_path
69+
70+
71+def get_binary_path(binary="unity8"):
72+ """Return the path to the specified binary."""
73+ binary_path = os.path.abspath(
74+ os.path.join(
75+ os.path.dirname(__file__),
76+ "../../../builddir/install/bin/%s" % binary
77+ )
78+ )
79+ if not os.path.exists(binary_path):
80+ try:
81+ binary_path = subprocess.check_output(['which', binary]).strip()
82+ except subprocess.CalledProcessError as e:
83+ raise RuntimeError("Unable to locate %s binary: %r" % (binary, e))
84+ return binary_path
85+
7
86
8−"""unity8 autopilot tests and emulators - top level package."""
87+def get_grid_size():
88+ grid_size = os.getenv('GRID_UNIT_PX')
89+ if grid_size is None:
90+ raise RuntimeError(
91+ "Environment variable GRID_UNIT_PX has not been set."
92+ )
93+ return int(grid_size)
tests/autopilot/unity8/indicators_client/__init__.py+1−1
@@ -5,4 +5,4 @@
5# under the terms of the GNU General Public License version 3, as published
5# under the terms of the GNU General Public License version 3, as published
6# by the Free Software Foundation.
6# by the Free Software Foundation.
7
7
8−"""unity8 indicators-client autopilot tests and emulators - sub level package."""
8+"""unity indicators-client autopilot tests and emulators - sub level package."""
tests/autopilot/unity8/indicators_client/tests/__init__.py+13−9
@@ -5,7 +5,7 @@
5# under the terms of the GNU General Public License version 3, as published
5# under the terms of the GNU General Public License version 3, as published
6# by the Free Software Foundation.
6# by the Free Software Foundation.
7
7
8−"""qml-phone-shell autopilot tests."""
8+"""indicators-client autopilot tests."""
9
9
10import os.path
10import os.path
11
11
@@ -15,6 +15,7 @@ from autopilot.matchers import Eventually
15from autopilot.platform import model
15from autopilot.platform import model
16from testtools.matchers import Equals
16from testtools.matchers import Equals
17
17
18+from unity8 import get_binary_path
18from unity8.indicators_client.emulators.main_window import MainWindow
19from unity8.indicators_client.emulators.main_window import MainWindow
19from logging import getLogger
20from logging import getLogger
20import sys
21import sys
@@ -29,9 +30,12 @@ class IndicatorsTestCase(AutopilotTestCase):
29
30
30 """A common test case class that provides several useful methods for indicator tests."""
31 """A common test case class that provides several useful methods for indicator tests."""
31
32
33+ geometry = None
34+ grid_unit = None
35+
32 if model() == 'Desktop':
36 if model() == 'Desktop':
33 scenarios = [
37 scenarios = [
34− ('with mouse', dict(input_device_class=Mouse)),
38+ ('with mouse', dict(input_device_class=Mouse, geometry="400x800", grid_unit="12")),
35 ]
39 ]
36 else:
40 else:
37 scenarios = [
41 scenarios = [
@@ -41,11 +45,11 @@ class IndicatorsTestCase(AutopilotTestCase):
41 def setUp(self, geometry, grid_size):
45 def setUp(self, geometry, grid_size):
42 self.pointing_device = Pointer(self.input_device_class.create())
46 self.pointing_device = Pointer(self.input_device_class.create())
43 super(IndicatorsTestCase, self).setUp()
47 super(IndicatorsTestCase, self).setUp()
44− if grid_size != "0":
48+ if grid_size is not None:
45− os.environ['GRID_UNIT_PX'] = grid_size
49+ self.patch_environment("GRID_UNIT_PX", str(grid_size))
46 self.grid_size = int(grid_size)
50 self.grid_size = int(grid_size)
47 else:
51 else:
48− self.grid_size = int(os.environ['GRID_UNIT_PX'])
52+ self.grid_size = int(os.getenv('GRID_UNIT_PX'))
49
53
50 if os.path.realpath(__file__).startswith("/usr/"):
54 if os.path.realpath(__file__).startswith("/usr/"):
51 self.launch_test_installed(geometry)
55 self.launch_test_installed(geometry)
@@ -53,15 +57,15 @@ class IndicatorsTestCase(AutopilotTestCase):
53 self.launch_test_local(geometry)
57 self.launch_test_local(geometry)
54
58
55 def launch_test_local(self, geometry):
59 def launch_test_local(self, geometry):
56− if geometry != "0x0":
60+ if geometry is None:
57− self.app = self.launch_test_application("../../builddir/src/Panel/Indicators/client/indicators-client",
61+ self.app = self.launch_test_application(get_binary_path("indicators-client"),
58 "-geometry", geometry, app_type='qt')
62 "-geometry", geometry, app_type='qt')
59 else:
63 else:
60− self.app = self.launch_test_application("../../builddir/src/Panel/Indicators/client/indicators-client",
64+ self.app = self.launch_test_application(get_binary_path("indicators-client"),
61 app_type='qt')
65 app_type='qt')
62
66
63 def launch_test_installed(self, geometry):
67 def launch_test_installed(self, geometry):
64− if geometry != "0x0":
68+ if geometry is not None:
65 self.app = self.launch_test_application("indicators-client", "-geometry", geometry, app_type='qt')
69 self.app = self.launch_test_application("indicators-client", "-geometry", geometry, app_type='qt')
66 else:
70 else:
67 self.app = self.launch_test_application("indicators-client", app_type='qt')
71 self.app = self.launch_test_application("indicators-client", app_type='qt')
tests/autopilot/unity8/indicators_client/tests/test_battery.py+4−1
@@ -8,6 +8,7 @@
8"""Add tests here if you want to ensure the behaviour of the power indicator menus are correct"""
8"""Add tests here if you want to ensure the behaviour of the power indicator menus are correct"""
9
9
10from testtools.matchers import Equals, NotEquals
10from testtools.matchers import Equals, NotEquals
11+from autopilot.input import Touch
11from autopilot.matchers import Eventually
12from autopilot.matchers import Eventually
12
13
13from unity8.indicators_client.tests import IndicatorsTestCase
14from unity8.indicators_client.tests import IndicatorsTestCase
@@ -17,7 +18,7 @@ import math
17
18
18class TestDisplayMenus(IndicatorsTestCase):
19class TestDisplayMenus(IndicatorsTestCase):
19 def setUp(self):
20 def setUp(self):
20− super(TestDisplayMenus, self).setUp("400x800", "12")
21+ super(TestDisplayMenus, self).setUp(self.geometry, self.grid_unit)
21
22
22 # This opens the messaging menu so you don't have to do that in
23 # This opens the messaging menu so you don't have to do that in
23 # every test case
24 # every test case
@@ -82,6 +83,8 @@ class TestDisplayMenus(IndicatorsTestCase):
82 def test_brightness_slider(self):
83 def test_brightness_slider(self):
83 """Test the auto-bright switch"""
84 """Test the auto-bright switch"""
84
85
86+ if self.input_device_class is Touch:
87+ self.skipTest("Dragging is broken with Touch input (LP: #1203808).")
85 fn_brightness_menu = lambda: self.app.select_single("SliderMenuItem", objectName="brightness");
88 fn_brightness_menu = lambda: self.app.select_single("SliderMenuItem", objectName="brightness");
86 self.assertThat(fn_brightness_menu, Eventually(NotEquals(None)));
89 self.assertThat(fn_brightness_menu, Eventually(NotEquals(None)));
87 brightness_menu = fn_brightness_menu();
90 brightness_menu = fn_brightness_menu();
tests/autopilot/unity8/shell/__init__.py+61−5
@@ -1,8 +1,64 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2−# Copyright 2013 Canonical
3#
2#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2012-2013 Canonical
5+#
4−# This program is free software: you can redistribute it and/or modify it
6+# This program is free software: you can redistribute it and/or modify
5−# under the terms of the GNU General Public License version 3, as published
7+# it under the terms of the GNU General Public License as published by
6−# by the Free Software Foundation.
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+"""unity shell autopilot tests and emulators - sub level package."""
21+
22+from functools import wraps
23+import logging
24+import os.path
25+
26+from unity8 import get_mocks_library_path, get_default_extra_mock_libraries
27+
28+
29+logger = logging.getLogger(__name__)
30+
31+
32+def with_lightdm_mock(mock_type):
33+ """A simple decorator that sets up the LightDM mock for a single test."""
34+ def with_lightdm_mock_internal(fn):
35+ @wraps(fn)
36+ def wrapper(*args, **kwargs):
37+ logger.info("Setting up LightDM mock type '%s'", mock_type)
38+ new_ld_library_path = "%s:%s" % (
39+ get_default_extra_mock_libraries(),
40+ _get_ld_library_path(mock_type)
41+ )
42+ logger.info("New library path: %s", new_ld_library_path)
43+ tests_self = args[0]
44+ tests_self.patch_environment(
45+ 'LD_LIBRARY_PATH',
46+ new_ld_library_path
47+ )
48+ return fn(*args, **kwargs)
49+ return wrapper
50+ return with_lightdm_mock_internal
51+
52+
53+def _get_ld_library_path(mock_type):
54+ lib_path = get_mocks_library_path()
55+ new_ld_library_path = os.path.abspath(
56+ os.path.join(lib_path, "LightDM", mock_type)
57+ )
7
58
8−"""unity8 shell autopilot tests and emulators - sub level package."""
59+ if not os.path.exists(new_ld_library_path):
60+ raise RuntimeError(
61+ "LightDM mock '%s' does not exist at path '%s'."
62+ % (mock_type, new_ld_library_path)
63+ )
64+ return new_ld_library_path
tests/autopilot/unity8/shell/emulators/__init__.py+23−4
@@ -1,6 +1,25 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2−# Copyright 2013 Canonical
3#
2#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2012-2013 Canonical
5+#
4−# This program is free software: you can redistribute it and/or modify it
6+# This program is free software: you can redistribute it and/or modify
5−# under the terms of the GNU General Public License version 3, as published
7+# it under the terms of the GNU General Public License as published by
6−# by the Free Software Foundation.
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+
21+from autopilot.introspection import CustomEmulatorBase
22+
23+
24+class UnityEmulatorBase(CustomEmulatorBase):
25+ """A base class for all unity emulators."""
tests/autopilot/unity8/shell/emulators/dash.pynew file+52
@@ -0,0 +1,52 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
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+#
19+
20+from unity8.shell.emulators import UnityEmulatorBase
21+
22+
23+class Dash(UnityEmulatorBase):
24+
25+ """An emulator that understands the Dash."""
26+
27+ def get_home_applications_grid(self):
28+ return self.select_single(
29+ "ApplicationsFilterGrid",
30+ objectName="dashHomeApplicationsGrid"
31+ )
32+
33+ def get_application_icon(self, text):
34+ """Returns a 'Tile' icon that has the text 'text' from the application
35+ grid.
36+
37+ Will return None if the icon isn't found.
38+
39+ :param text: String containing the text of the icon to search for.
40+
41+ """
42+ app_grid = self.get_home_applications_grid()
43+ resp_grid = app_grid.select_single('ResponsiveGridView')
44+ return resp_grid.select_single('Tile', text=text)
45+
46+ def get_scope(self, scope_name='home'):
47+ dash_content = self.select_single(
48+ 'QQuickListView',
49+ objectName='dashContentList'
50+ )
51+ scope_id = "%s.scope" % scope_name
52+ return dash_content.select_single('QQuickLoader', scopeId=scope_id)
tests/autopilot/unity8/shell/emulators/greeter.pynew file+40
@@ -0,0 +1,40 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
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+#
19+
20+from unity8.shell.emulators import UnityEmulatorBase
21+from autopilot.input import Touch
22+
23+
24+class Greeter(UnityEmulatorBase):
25+
26+ """An emulator that understands the greeter screen."""
27+
28+ def unlock(self):
29+ """Swipe the greeter screen away."""
30+ self.created.wait_for(True)
31+ touch = Touch.create()
32+
33+ rect = self.globalRect
34+ start_x = rect[0] + rect[2] - 3
35+ start_y = int(rect[1] + rect[3] / 2)
36+ stop_x = int(rect[0] + rect[2] * 0.2)
37+ stop_y = start_y
38+ touch.drag(start_x, start_y, stop_x, stop_y)
39+
40+ self.created.wait_for(False)
tests/autopilot/unity8/shell/emulators/hud.pynew file+83
@@ -0,0 +1,83 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
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+#
19+
20+from collections import namedtuple
21+
22+from unity8 import get_grid_size
23+from unity8.shell.emulators import UnityEmulatorBase
24+from autopilot.input import Touch
25+
26+
27+SwipeCoords = namedtuple('SwipeCoords', 'start_x end_x start_y end_y')
28+
29+
30+class Hud(UnityEmulatorBase):
31+
32+ """An emulator that understands the Hud."""
33+
34+ def show(self):
35+ """Swipes open the Hud."""
36+ touch = Touch.create()
37+
38+ window = self.get_root_instance().select_single('QQuickView')
39+ hud_show_button = window.select_single("HudButton")
40+
41+ start_x = int(window.x + (window.width / 2))
42+ end_x = start_x
43+ start_y = window.y + window.height
44+ end_y = int(hud_show_button.y + (hud_show_button.height/2))
45+
46+ touch.press(start_x, start_y)
47+ touch._finger_move(end_x, end_y)
48+ try:
49+ hud_show_button.opacity.wait_for(1.0)
50+ touch.release()
51+ self.shown.wait_for(True)
52+ except AssertionError:
53+ raise
54+ finally:
55+ if touch._touch_finger is not None:
56+ touch.release()
57+
58+ def dismiss(self):
59+ """Closes the open Hud."""
60+ # Ensure that the Hud is actually open
61+ self.shown.wait_for(True)
62+ touch = Touch.create()
63+ x, y = self.get_close_button_coords()
64+ touch.tap(x, y)
65+ self.y.wait_for(self.height)
66+
67+ def get_close_button_coords(self):
68+ """Returns the coordinates of the Huds close button bar."""
69+ rect = self.globalRect
70+ x = int(rect[0] + rect[2] / 2)
71+ y = rect[1] + get_grid_size()
72+ return x, y
73+
74+ def get_button_swipe_coords(self, main_view, hud_show_button):
75+ """Returns the coords both start and end x,y for swiping to make the
76+ 'hud show' button appear.
77+ """
78+ start_x = int(main_view.x + (main_view.width / 2))
79+ end_x = start_x
80+ start_y = main_view.y + (main_view.height - 3)
81+ end_y = int(hud_show_button.y + (hud_show_button.height/2))
82+
83+ return SwipeCoords(start_x, end_x, start_y, end_y)
tests/autopilot/unity8/shell/emulators/launcher.pynew file+38
@@ -0,0 +1,38 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
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+#
19+
20+from unity8.shell.emulators import UnityEmulatorBase
21+from autopilot.input import Touch
22+
23+
24+class Launcher(UnityEmulatorBase):
25+
26+ """An emulator that understands the Launcher."""
27+
28+ def show(self):
29+ """Swipes open the launcher."""
30+ touch = Touch.create()
31+
32+ view = self.get_root_instance().select_single('QQuickView')
33+ start_x = view.x + 1
34+ start_y = view.y + view.height / 2
35+ stop_x = start_x + self.panelWidth + 1
36+ stop_y = start_y
37+ touch.drag(start_x, start_y, stop_x, stop_y)
38+ self.shown.wait_for(True)
tests/autopilot/unity8/shell/emulators/main_window.py+43−12
@@ -1,9 +1,28 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2−# Copyright 2013 Canonical
3#
2#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2012-2013 Canonical
5+#
4−# This program is free software: you can redistribute it and/or modify it
6+# This program is free software: you can redistribute it and/or modify
5−# under the terms of the GNU General Public License version 3, as published
7+# it under the terms of the GNU General Public License as published by
6−# by the Free Software Foundation.
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+
21+from unity8.shell.emulators.greeter import Greeter
22+from unity8.shell.emulators.hud import Hud
23+from unity8.shell.emulators.dash import Dash
24+from unity8.shell.emulators.launcher import Launcher
25+
7
26
8class MainWindow(object):
27class MainWindow(object):
9 """An emulator class that makes it easy to interact with the shell"""
28 """An emulator class that makes it easy to interact with the shell"""
@@ -16,10 +35,13 @@ class MainWindow(object):
16 return self.app.select_single("QQuickView")
35 return self.app.select_single("QQuickView")
17
36
18 def get_greeter(self):
37 def get_greeter(self):
19− return self.app.select_single("Greeter")
38+ return self.app.select_single(Greeter)
20
39
21 def get_greeter_content_loader(self):
40 def get_greeter_content_loader(self):
41+ return self.app.select_single(
42+ "QQuickLoader",
22− return self.app.select_single("QQuickLoader", objectName="greeterContentLoader")
43+ objectName="greeterContentLoader"
44+ )
23
45
24 def get_login_loader(self):
46 def get_login_loader(self):
25 return self.app.select_single("QQuickLoader", objectName="loginLoader")
47 return self.app.select_single("QQuickLoader", objectName="loginLoader")
@@ -28,7 +50,7 @@ class MainWindow(object):
28 return self.app.select_single("LoginList")
50 return self.app.select_single("LoginList")
29
51
30 def get_hud(self):
52 def get_hud(self):
31− return self.app.select_single("Hud")
53+ return self.app.select_single(Hud)
32
54
33 def get_hud_showable(self):
55 def get_hud_showable(self):
34 return self.app.select_single("Showable", objectName="hudShowable")
56 return self.app.select_single("Showable", objectName="hudShowable")
@@ -37,22 +59,31 @@ class MainWindow(object):
37 return self.app.select_single("HudButton")
59 return self.app.select_single("HudButton")
38
60
39 def get_dash(self):
61 def get_dash(self):
40− return self.app.select_single("Dash")
62+ return self.app.select_single(Dash)
41
63
42 def get_dash_home_applications_grid(self):
64 def get_dash_home_applications_grid(self):
65+ return self.app.select_single(
66+ "ApplicationsFilterGrid",
43− return self.app.select_single("ApplicationsFilterGrid", objectName="dashHomeApplicationsGrid")
67+ objectName="dashHomeApplicationsGrid"
68+ )
44
69
45 def get_bottombar(self):
70 def get_bottombar(self):
46 return self.app.select_single("Bottombar")
71 return self.app.select_single("Bottombar")
47
72
48 def get_launcher(self):
73 def get_launcher(self):
49− return self.app.select_single("Launcher")
74+ return self.app.select_single(Launcher)
50
75
51 def get_pinPadLoader(self):
76 def get_pinPadLoader(self):
52− return self.app.select_single("QQuickLoader", objectName="pinPadLoader")
77+ return self.app.select_single(
78+ "QQuickLoader",
79+ objectName="pinPadLoader"
80+ )
53
81
54 def get_pinPadButton(self, buttonId):
82 def get_pinPadButton(self, buttonId):
83+ return self.app.select_single(
84+ "PinPadButton",
55− return self.app.select_single("PinPadButton", objectName="pinPadButton%i" % buttonId)
85+ objectName="pinPadButton%i" % buttonId
86+ )
56
87
57 def get_lockscreen(self):
88 def get_lockscreen(self):
58 return self.app.select_single("Lockscreen")
89 return self.app.select_single("Lockscreen")
tests/autopilot/unity8/shell/tests/__init__.py+165−59
@@ -1,81 +1,187 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2−# Copyright 2013 Canonical
3#
2#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2012-2013 Canonical
5+#
4−# This program is free software: you can redistribute it and/or modify it
6+# This program is free software: you can redistribute it and/or modify
5−# under the terms of the GNU General Public License version 3, as published
7+# it under the terms of the GNU General Public License as published by
6−# by the Free Software Foundation.
8+# the Free Software Foundation, either version 3 of the License, or
7−
9+# (at your option) any later version.
8−"""unity8 autopilot tests."""
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+#
9
19
10−import os.path
20+"""unity autopilot tests."""
11−import sysconfig
12
21
22+from autopilot.platform import model
13from autopilot.testcase import AutopilotTestCase
23from autopilot.testcase import AutopilotTestCase
14from autopilot.matchers import Eventually
24from autopilot.matchers import Eventually
15−from autopilot.platform import model
25+from autopilot.input import Touch
26+from autopilot.display import Display
27+import logging
28+import os.path
16−from testtools.matchers import Equals
29+from testtools.matchers import Equals, NotEquals
17
30
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
18from unity8.shell.emulators.main_window import MainWindow
34from unity8.shell.emulators.main_window import MainWindow
19
35
20−class FormFactors(object):
21− Phone, Tablet, Desktop = range(3)
22
36
23−class ShellTestCase(AutopilotTestCase):
37+logger = logging.getLogger(__name__)
24
38
25− """A common test case class that provides several useful methods for shell tests."""
26
39
27− libdir = "/usr/lib/{0}/unity8".format(sysconfig.get_config_var('MULTIARCH'))
40+def _get_device_emulation_scenarios():
28− lightdm_mock = "full"
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+ ]
29
59
60+
61+class UnityTestCase(AutopilotTestCase):
62+
63+ """A test case base class for the Unity shell tests."""
64+
30− def setUp(self, geometry, grid_size):
65+ def setUp(self):
31− super(ShellTestCase, self).setUp()
66+ super(UnityTestCase, self).setUp()
32− # Lets assume we are installed system wide if this file is somewhere in /usr
67+ self._proxy = None
33− if grid_size != "0":
68+ self.touch = Touch.create()
34− os.environ['GRID_UNIT_PX'] = grid_size
69+ self._setup_display_details()
35− self.grid_size = int(grid_size)
70+
36− else:
71+ def _setup_display_details(self):
37− self.grid_size = int(os.environ['GRID_UNIT_PX'])
72+ scale_divisor = self._determine_geometry()
38− if os.path.realpath(__file__).startswith("/usr/"):
73+ self._setup_grid_size(scale_divisor)
39− self.launch_test_installed(geometry)
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']
40 else:
82 else:
41− self.launch_test_local(geometry)
83+ if self._geo_larger_than_display(width, height):
42−
84+ scale_divisor = self._get_scaled_down_geo(width, height)
43− def launch_test_local(self, geometry):
85+ width = width / scale_divisor
44− os.environ['LD_LIBRARY_PATH'] = "../../builddir/tests/mocks/libusermetrics:../../builddir/tests/mocks/LightDM" + self.lightdm_mock
86+ height = height / scale_divisor
45− os.environ['QML2_IMPORT_PATH'] = "../../builddir/tests/mocks"
87+ logger.info(
46− if geometry != "0x0":
88+ "Geometry larger than display, scaled down to: %dx%d",
47− self.app = self.launch_test_application(
89+ width,
48− "../../builddir/unity8", "-geometry", geometry, "-frameless", app_type='qt')
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'))
49 else:
105 else:
50− self.app = self.launch_test_application(
106+ self.grid_size = int(self.grid_unit_px / scale_divisor)
51− "../../builddir/unity8", "-fullscreen", app_type='qt')
107+ self.patch_environment("GRID_UNIT_PX", str(self.grid_size))
52−
108+
53− def launch_test_installed(self, geometry):
109+ def _geo_larger_than_display(self, width, height):
54− os.environ['LD_LIBRARY_PATH'] = "{0}/qml/mocks/libusermetrics:{0}/qml/mocks/LightDM/{1}".format(self.libdir, self.lightdm_mock)
110+ should_scale = getattr(self, 'scale_geo', True)
55− os.environ['QML2_IMPORT_PATH'] = "{0}/qml/mocks".format(self.libdir)
111+ if should_scale:
56− if model() == 'Desktop' and geometry != "0x0":
112+ screen = Display.create()
57− self.app = self.launch_test_application(
113+ screen_width = screen.get_screen_width()
58− "unity8", "-geometry", geometry, "-frameless", app_type='qt')
114+ screen_height = screen.get_screen_height()
115+ return (width > screen_width) or (height > screen_height)
59 else:
116 else:
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+
60− self.app = self.launch_test_application(
138+ app_proxy = self.launch_test_application(
139+ binary_path,
140+ *self.unity_geometry_args,
61− "unity8", "-fullscreen", app_type='qt')
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
62
167
63− def skipWrapper(*args, **kwargs):
168+ def assertUnityReady(self):
64− pass
169+ dash = self.get_dash()
170+ home_scope = dash.get_scope('home')
65
171
66− def form_factor(self):
172+ # FIXME! There is a huge timeout here for when we're doing CI on
67− return FormFactors.Desktop
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)))
68
179
69− def __getattribute__(self, attr_name):
180+ def get_dash(self):
70− attr = object.__getattribute__(self, attr_name);
181+ dash = self._proxy.select_single(Dash)
71− if attr_name.startswith("test_"):
182+ self.assertThat(dash, NotEquals(None))
72− try:
73− if self.form_factor() in attr.blacklist:
74− return self.skipWrapper
75− except:
76− pass
77− return attr
183+ return dash
78
184
79 @property
185 @property
80 def main_window(self):
186 def main_window(self):
81− return MainWindow(self.app)
187+ return MainWindow(self._proxy)
tests/autopilot/unity8/shell/tests/helpers.pydeleted−161
@@ -1,161 +0,0 @@
1−# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2−# Copyright 2013 Canonical
3−#
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−from testtools.matchers import Equals, NotEquals
9−from autopilot.matchers import Eventually
10−
11−import logging
12−
13−logger = logging.getLogger(__name__)
14−
15−class TestShellHelpers(object):
16− """Helpers for testing the Shell"""
17−
18− def select_greeter_user(self, username):
19− greeter = self.main_window.get_greeter()
20− self.assertThat(greeter.created, Eventually(Equals(True)))
21− self.assertThat(greeter.multiUser, Eventually(Equals(True)))
22−
23− login_loader = self.main_window.get_login_loader()
24− self.assertThat(login_loader.progress, Eventually(Equals(1)))
25−
26− login_list = self.main_window.get_login_list()
27− list_view = login_list.get_children_by_type("QQuickListView")[0]
28−
29− try_count = 0
30− max_tries = 50 # just in case we go off rails
31− while try_count < max_tries:
32− users = list_view.get_children_by_type("QQuickItem")[0].get_children_by_type("QQuickItem")
33− target_user = None
34− for user in users:
35− try:
36− user_label = user.get_children_by_type("Label")[0]
37− if user.opacity < 0.1:
38− continue # off-screen item
39− if user_label.text == username:
40− target_user = user
41− break
42− elif target_user is None or user.y > target_user.y:
43− target_user = user
44− except Exception:
45− pass
46− if target_user is None:
47− break
48− user_label = target_user.get_children_by_type("Label")[0]
49− self.touch.tap_object(user_label)
50− self.assertThat(list_view.movingInternally, Eventually(Equals(False)))
51− if user_label.text == username:
52− return login_list.get_children_by_type("TextField")[0]
53− try_count = try_count + 1
54− self.fail() # We didn't find it
55−
56− def unlock_greeter(self, retries=2):
57− greeter = self.main_window.get_greeter()
58− self.assertThat(greeter.created, Eventually(Equals(True)))
59−
60− if greeter.multiUser:
61− password_field = self.select_greeter_user("No Password")
62− self.assertThat(password_field.opacity, Eventually(Equals(1)))
63− self.touch.tap_object(password_field)
64−
65− else:
66− rect = greeter.globalRect
67− start_x = rect[0] + rect[2] - 3
68− start_y = int(rect[1] + rect[3] / 2)
69− stop_x = int(rect[0] + rect[2] * 0.2)
70− stop_y = start_y
71− self.touch.drag(start_x, start_y, stop_x, stop_y)
72−
73− # Because the shell loads up lots of stuff, unlocking the greeter can
74− # be a bit stuttery while scopes are still consuming all resources.
75− # Give it another (max retries) chance
76− try:
77− self.assertThat(greeter.created, Eventually(Equals(False)))
78− except:
79− if retries > 0:
80− logger.warning("Failed to unlock greeter. Retrying...")
81− self.unlock_greeter(retries-1)
82− else:
83− logger.warning("Failed to unlock greeter. Giving up. Tests may fail...")
84−
85−
86− def open_first_dash_home_app(self, retries=2):
87− self.assertThat(lambda: self.main_window.get_dash_home_applications_grid(), Eventually(NotEquals(None)))
88− app_grid = self.main_window.get_dash_home_applications_grid()
89− # Wait for the grids to be expanded
90− self.assertThat(app_grid.get_children()[0].totalContentHeight, Eventually(NotEquals(0)))
91− self.assertThat(app_grid.get_children()[0].height, Eventually(Equals(app_grid.get_children()[0].totalContentHeight)))
92− first_app = app_grid.get_children()[0].get_children()[0].get_children()[0].get_children()[0]
93− self.touch.tap_object(first_app)
94− bottombar = self.main_window.get_bottombar()
95−
96− # if we have huge amounts of pixels (e.g. Nexus10), but slow video (e.g. VM) it might take a little
97− # until the dash is fully rendered after sliding away the greeter/lockscreen and the click might
98− # go to the void for the first time because there's no real way to know what the display has
99− # painted already. Give it another (max retries) chance.
100− try:
101− self.assertThat(lambda: getattr(bottombar, "applicationIsOnForeground"),
102− Eventually(Equals(True)))
103− except:
104− if retries > 0:
105− logger.warning("Failed to launch app. Retrying...")
106− self.open_first_dash_home_app(retries-1)
107− else:
108− logger.warning("Failed to launch app. Giving up. Tests may fail...")
109−
110−
111− def show_hud(self, retries=2):
112− self.open_first_dash_home_app()
113−
114− hud_show_button = self.main_window.get_hud_show_button()
115− hud = self.main_window.get_hud()
116− window = self.main_window.get_qml_view()
117− start_x = int(window.x + window.width / 2)
118− start_y = window.y + window.height - 3
119− self.assertThat(hud_show_button.opacity, Eventually(Equals(0)))
120− self.touch.press(start_x, start_y)
121− self.touch._finger_move(start_x, start_y - self.grid_size)
122− self.assertThat(hud_show_button.opacity, Eventually(Equals(0)))
123− self.touch._finger_move(start_x, start_y - self.grid_size * 2)
124− self.touch._finger_move(start_x, start_y - self.grid_size * 3)
125− self.touch._finger_move(start_x, start_y - self.grid_size * 4)
126− try:
127− self.assertThat(hud_show_button.opacity, Eventually(Equals(1.0)))
128− except:
129− if retries > 0:
130− logger.warning("Failed to get hud button to show. Retrying...")
131− self.touch.release()
132− self.show_hud(retries-1)
133− return
134− else:
135− logger.warning("Failed to get hud button to show. Giving up. Tests may fail...")
136− self.assertThat(hud_show_button.mouseOver, Eventually(Equals(False)))
137− self.touch._finger_move(start_x, start_y - self.grid_size * 34)
138− self.assertThat(hud_show_button.opacity, Eventually(Equals(1.0)))
139− self.assertThat(hud_show_button.mouseOver, Eventually(Equals(True)))
140− self.touch.release()
141− self.assertThat(hud_show_button.opacity, Eventually(Equals(0)))
142− self.assertThat(hud.shown, Eventually(Equals(True)))
143−
144− def close_hud_click(self):
145− hud = self.main_window.get_hud()
146− self.assertThat(hud.shown, Eventually(Equals(True)))
147− rect = hud.globalRect
148− x = int(rect[0] + rect[2] / 2)
149− y = rect[1] + self.grid_size
150− self.touch.tap(x, y)
151− self.assertThat(hud.shown, Eventually(Equals(False)))
152−
153− def show_launcher(self):
154− launcher = self.main_window.get_launcher()
155− view = self.main_window.get_qml_view()
156− start_x = view.x + 1
157− start_y = view.y + view.height / 2
158− stop_x = start_x + launcher.panelWidth + 1
159− stop_y = start_y
160− self.touch.drag(start_x, start_y, stop_x, stop_y)
161− self.assertThat(launcher.shown, Eventually(Equals(True)))
tests/autopilot/unity8/shell/tests/test_hud.pynew file+156
@@ -0,0 +1,156 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
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+#
19+
20+from __future__ import absolute_import
21+
22+from unity8.shell import with_lightdm_mock
23+from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
24+
25+from testtools.matchers import Equals
26+from autopilot.matchers import Eventually
27+
28+
29+class TestHud(UnityTestCase):
30+
31+ """Tests the Shell HUD."""
32+
33+ scenarios = _get_device_emulation_scenarios()
34+
35+ @with_lightdm_mock("single")
36+ def test_show_hud_button_appears(self):
37+ """Swiping up while an app is active must show the 'show hud' button.
38+
39+ """
40+ self.launch_unity()
41+ self.main_window.get_greeter().unlock()
42+ window = self.main_window.get_qml_view()
43+ hud_show_button = self.main_window.get_hud_show_button()
44+ hud = self.main_window.get_hud()
45+
46+ self._launch_test_app_from_app_screen()
47+
48+ swipe_coords = hud.get_button_swipe_coords(
49+ window,
50+ hud_show_button
51+ )
52+ self.touch.press(swipe_coords.start_x, swipe_coords.start_y)
53+ self.addCleanup(self.touch.release)
54+ self.touch._finger_move(swipe_coords.end_x, swipe_coords.end_y)
55+ self.assertThat(hud_show_button.opacity, Eventually(Equals(1.0)))
56+
57+ @with_lightdm_mock("single")
58+ def test_show_hud_appears(self):
59+ """Releasing the touch on the 'show hud' button must display the hud.
60+
61+ """
62+ self.launch_unity()
63+ self.main_window.get_greeter().unlock()
64+ window = self.main_window.get_qml_view()
65+ hud_show_button = self.main_window.get_hud_show_button()
66+ hud = self.main_window.get_hud()
67+
68+ self._launch_test_app_from_app_screen()
69+
70+ swipe_coords = hud.get_button_swipe_coords(
71+ window,
72+ hud_show_button
73+ )
74+ self.touch.press(swipe_coords.start_x, swipe_coords.start_y)
75+ self.addCleanup(self._maybe_release_finger)
76+ self.touch._finger_move(swipe_coords.end_x, swipe_coords.end_y)
77+
78+ self.assertThat(hud.shown, Eventually(Equals(False)))
79+ self.assertThat(hud_show_button.opacity, Eventually(Equals(1.0)))
80+ self.touch.release()
81+ self.assertThat(hud.shown, Eventually(Equals(True)))
82+
83+ @with_lightdm_mock("single")
84+ def test_hide_hud_click(self):
85+ """Tapping the close button of the Hud must dismiss it."""
86+ self.launch_unity()
87+ self.main_window.get_greeter().unlock()
88+ hud = self.main_window.get_hud()
89+
90+ self._launch_test_app_from_app_screen()
91+ hud.show()
92+
93+ x, y = hud.get_close_button_coords()
94+ self.touch.tap(x, y)
95+ self.assertThat(hud.shown, Eventually(Equals(False)))
96+
97+ @with_lightdm_mock("single")
98+ def test_hide_hud_dragging(self):
99+ """Once open the Hud must close if the upper bar is dragged and
100+ released downward.
101+
102+ """
103+ self.launch_unity()
104+ self.main_window.get_greeter().unlock()
105+ hud = self.main_window.get_hud()
106+ window = self.main_window.get_qml_view()
107+
108+ self._launch_test_app_from_app_screen()
109+ hud.show()
110+
111+ start_x, start_y = hud.get_close_button_coords()
112+ end_x = start_x
113+ end_y = int(window.height / 2)
114+
115+ self.touch.drag(start_x, start_y, end_x, end_y)
116+ self.assertThat(hud.shown, Eventually(Equals(False)))
117+
118+ @with_lightdm_mock("single")
119+ def test_launcher_hides_hud(self):
120+ """Opening the Launcher while the Hud is active must close the Hud."""
121+ self.launch_unity()
122+ self.main_window.get_greeter().unlock()
123+ hud = self.main_window.get_hud()
124+ launcher = self.main_window.get_launcher()
125+
126+ self._launch_test_app_from_app_screen()
127+
128+ hud.show()
129+ launcher.show()
130+
131+ self.assertThat(hud.shown, Eventually(Equals(False)))
132+
133+ def _launch_test_app_from_app_screen(self):
134+ """Launches the camera app using the Dash UI.
135+
136+ Because when testing on the desktop running
137+ self.launch_application() will launch the application on the desktop
138+ itself and not within the Unity UI.
139+
140+ """
141+ dash = self.main_window.get_dash()
142+ icon = dash.get_application_icon('Camera')
143+ self.touch.tap_object(icon)
144+
145+ # Ensure application is open
146+ bottombar = self.main_window.get_bottombar()
147+ self.assertThat(bottombar.applicationIsOnForeground,
148+ Eventually(Equals(True)))
149+
150+ # Because some tests are manually manipulating the finger, we want to
151+ # cleanup if the test fails, but we don't want to fail with an exception if
152+ # we don't.
153+ def _maybe_release_finger(self):
154+ """Only release the finger if it is in fact down."""
155+ if self.touch._touch_finger is not None:
156+ self.touch.release()
tests/autopilot/unity8/shell/tests/test_lock_screen.pynew file+133
@@ -0,0 +1,133 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
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+#
19+
20+
21+from __future__ import absolute_import
22+
23+from unity8.shell import with_lightdm_mock
24+from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
25+
26+from autopilot.matchers import Eventually
27+from autopilot.platform import model
28+from testtools import skipUnless
29+from testtools.matchers import Equals
30+
31+
32+class TestLockscreen(UnityTestCase):
33+
34+ """Tests for the lock screen."""
35+
36+ scenarios = _get_device_emulation_scenarios()
37+
38+ @with_lightdm_mock("single-pin")
39+ def test_can_unlock_pin_screen(self):
40+ """Must be able to unlock the PIN entry lock screen."""
41+ self.launch_unity()
42+ greeter = self.main_window.get_greeter()
43+ greeter.unlock()
44+
45+ lockscreen = self._wait_for_lockscreen()
46+ self._enter_pincode("1234")
47+
48+ self.assertThat(lockscreen.shown, Eventually(Equals(False)))
49+
50+ @skipUnless(model() == 'Desktop', "Broken passphrase entry on UInput (LP: #1203788).")
51+ @with_lightdm_mock("single-passphrase")
52+ def test_can_unlock_passphrase_screen(self):
53+ """Must be able to unlock the passphrase entry screen."""
54+ self.launch_unity()
55+ greeter = self.main_window.get_greeter()
56+ greeter.unlock()
57+
58+ lockscreen = self._wait_for_lockscreen()
59+ self._enter_passphrase("password")
60+
61+ self.assertThat(lockscreen.shown, Eventually(Equals(False)))
62+
63+ @with_lightdm_mock("single-pin")
64+ def test_pin_screen_wrong_code(self):
65+ """Entering the wrong pin code must not dismiss the lock screen."""
66+ self.launch_unity()
67+ greeter = self.main_window.get_greeter()
68+ greeter.unlock()
69+
70+ lockscreen = self._wait_for_lockscreen()
71+ self._enter_pincode("4321")
72+
73+ pinentryField = self.main_window.get_pinentryField()
74+ self.assertThat(pinentryField.text, Eventually(Equals("")))
75+ self.assertThat(lockscreen.shown, Eventually(Equals(True)))
76+
77+ @skipUnless(model() == 'Desktop', "Broken passphrase entry on UInput (LP: #1203788).")
78+ @with_lightdm_mock("single-passphrase")
79+ def test_passphrase_screen_wrong_password(self):
80+ """Entering the wrong password must not dismiss the lock screen."""
81+ self.launch_unity()
82+ greeter = self.main_window.get_greeter()
83+ greeter.unlock()
84+
85+ lockscreen = self._wait_for_lockscreen()
86+ self._enter_passphrase("foobar")
87+
88+ pinentryField = self.main_window.get_pinentryField()
89+ self.assertThat(pinentryField.text, Eventually(Equals("")))
90+ self.assertThat(lockscreen.shown, Eventually(Equals(True)))
91+
92+ def _wait_for_lockscreen(self):
93+ """Wait for the lock screen to load, and return it."""
94+ pinPadLoader = self.main_window.get_pinPadLoader()
95+ self.assertThat(pinPadLoader.progress, Eventually(Equals(1)))
96+ lockscreen = self.main_window.get_lockscreen()
97+ self.assertThat(lockscreen.shown, Eventually(Equals(True)))
98+ return lockscreen
99+
100+ def _enter_pincode(self, code):
101+ """Enter code 'code' into the single-pin lightdm pincode entry
102+ screen.
103+
104+ :param code: must be a string of numeric characters.
105+ :raises: TypeError if code is not a string.
106+ :raises: ValueError if code contains non-numeric characters.
107+
108+ """
109+
110+ if not isinstance(code, basestring):
111+ raise TypeError("'code' parameter must be a string.")
112+ for num in code:
113+ if not num.isdigit():
114+ raise ValueError(
115+ "'code' parameter contains non-numeric characters."
116+ )
117+ self.touch.tap_object(self.main_window.get_pinPadButton(int(num)))
118+
119+ def _enter_passphrase(self, passphrase):
120+ """Enter the password specified in 'passphrase' into the password entry
121+ field.
122+
123+ :param passphrase: The string you want to enter.
124+ :raises: TypeError if passphrase is not a string.
125+
126+ """
127+ if not isinstance(passphrase, basestring):
128+ raise TypeError("'passphrase' parameter must be a string.")
129+
130+ pinentryField = self.main_window.get_pinentryField()
131+ self.touch.tap_object(pinentryField)
132+ self.keyboard.type(passphrase)
133+ self.keyboard.type("\n")
tests/autopilot/unity8/shell/tests/testbigscreen.pydeleted−25
@@ -1,25 +0,0 @@
1−# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2−# Copyright 2013 Canonical
3−#
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−"""Add tests here if you want to ensure everything is aligned correctly on huge screens. Because this test does not fit on most screens, you should not use input devices here or your tests are likely to fail"""
9−
10−from testtools.matchers import Equals
11−from autopilot.matchers import Eventually
12−
13−from unity8.shell.tests import ShellTestCase
14−
15−class TestBig(ShellTestCase):
16− def setUp(self):
17− super(TestBig, self).setUp("2560x1600", "20")
18− self.assertThat(self.main_window.get_qml_view().visible, Eventually(Equals(True)))
19−
20− def tearDown(self):
21− super(TestBig, self).tearDown()
22−
23− def test_hud_not_shown_greeter(self):
24− hud_showable = self.main_window.get_hud_showable()
25− self.assertThat(hud_showable.y, Eventually(Equals(1600)))
tests/autopilot/unity8/shell/tests/testhud.pydeleted−145
@@ -1,145 +0,0 @@
1−# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2−# Copyright 2013 Canonical
3−#
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−
9−# This file contains general purpose test cases for Unity.
10−# Each test written in this file will be executed for a variety of
11−# configurations, such as Phone, Tablet or Desktop form factors.
12−#
13−# Sometimes there is the need to disable a certain test for a particular
14−# configuration. To do so, add this in a new line directly below your test:
15−#
16−# test_testname.blacklist = (FormFactors.Tablet, FormFactors.Desktop,)
17−#
18−# Available form factors are:
19−# FormFactors.Phone
20−# FormFactors.Tablet
21−# FormFactors.Desktop
22−
23−
24−"""Tests for the Shell"""
25−
26−from __future__ import absolute_import
27−
28−from unity8.shell.tests import ShellTestCase, FormFactors
29−from unity8.shell.tests.helpers import TestShellHelpers
30−
31−from autopilot.input import Mouse, Touch, Pointer
32−from testtools.matchers import Equals, NotEquals, GreaterThan, MismatchError
33−from autopilot.matchers import Eventually
34−from autopilot.display import Display
35−from autopilot.platform import model
36−
37−import unittest
38−import time
39−import os
40−from os import path
41−
42−
43−class TestHud(ShellTestCase, TestShellHelpers):
44−
45− """Tests the Shell"""
46−
47− # Scenarios:
48− # Fill in the scenarios to run the whole test suite with multiple configurations.
49− # Use app_width, app_height and grid_unit_px to set the apps geometry.
50− # Set app_width and app_height to 0 to use fullscreen.
51− # Set grid_unit_px to 0 to use the current system environment.
52−
53− if model() == 'Desktop':
54− scenarios = [
55− ('Nexus 4', dict(app_width=768, app_height=1280, grid_unit_px=18, lightdm_mock="single")),
56− ('Nexus 10', dict(app_width=2560, app_height=1600, grid_unit_px=20, lightdm_mock="full")),
57−# TODO: don't run fullscreen tests just yet as the VM is performing too badly for that. Enable this once
58−# Autopilot tests are running on bear metal.
59−# ('Fullscreen', dict(app_width=0, app_height=0, grid_unit_px=10, lightdm_mock="full")),
60− ]
61− else:
62− scenarios = [
63− ('Fullscreen', dict(app_width=0, app_height=0, grid_unit_px=0, lightdm_mock="single")),
64− ]
65−
66− def setUp(self):
67− self.touch = Touch.create()
68−
69− sg = Display().create()
70− divisor = 1
71− while (sg.get_screen_width() < self.app_width / divisor or sg.get_screen_height() < self.app_height / divisor):
72− divisor = divisor * 2
73− super(TestHud, self).setUp("%sx%s" % (self.app_width / divisor, self.app_height / divisor), "%s" % (self.grid_unit_px / divisor))
74−
75− dash = self.main_window.get_dash()
76− self.assertThat(dash.showScopeOnLoaded, Eventually(Equals(""), timeout=30))
77−
78− def test_show_hud(self):
79− hud = self.main_window.get_hud()
80− self.unlock_greeter()
81− self.show_hud()
82− self.assertThat(hud.shown, Eventually(Equals(True)))
83−
84− def test_show_hud_button_dont_open(self):
85− self.unlock_greeter()
86− self.open_first_dash_home_app()
87− hud_show_button = self.main_window.get_hud_show_button()
88− hud = self.main_window.get_hud()
89− window = self.main_window.get_qml_view()
90− start_x = int(window.x + window.width / 2)
91− start_y = window.y + window.height - 2
92− self.assertThat(hud_show_button.opacity, Eventually(Equals(0)))
93− self.touch.press(start_x, start_y)
94− self.touch._finger_move(start_x, start_y - self.grid_size)
95− self.assertThat(hud_show_button.opacity, Eventually(Equals(0)))
96− self.touch._finger_move(start_x, start_y - self.grid_size * 2)
97− self.touch._finger_move(start_x, start_y - self.grid_size * 3)
98− self.touch._finger_move(start_x, start_y - self.grid_size * 4)
99− self.assertThat(hud_show_button.opacity, Eventually(Equals(1.0)))
100− self.assertThat(hud_show_button.mouseOver, Eventually(Equals(False)))
101− self.touch._finger_move(start_x, start_y - self.grid_size * 34)
102− self.assertThat(hud_show_button.opacity, Eventually(Equals(1.0)))
103− self.assertThat(hud_show_button.mouseOver, Eventually(Equals(True)))
104− self.touch._finger_move(start_x, start_y - self.grid_size)
105− self.assertThat(hud_show_button.opacity, Eventually(Equals(1.0)))
106− self.assertThat(hud_show_button.mouseOver, Eventually(Equals(False)))
107− self.touch.release()
108− self.assertThat(hud_show_button.opacity, Eventually(Equals(0)))
109− self.assertThat(hud.shown, Eventually(Equals(False)))
110−
111− def test_hide_hud_click(self):
112− hud = self.main_window.get_hud()
113− self.unlock_greeter()
114− self.show_hud()
115− self.close_hud_click()
116− self.assertThat(hud.shown, Eventually(Equals(False)))
117−
118− def test_hide_hud_click_outside_handle(self):
119− hud = self.main_window.get_hud()
120− self.unlock_greeter()
121− self.show_hud()
122− rect = hud.globalRect
123− x = int(rect[0] + rect[2] / 2)
124− y = rect[1] + hud.handleHeight + 1
125− self.touch.tap(x, y)
126− self.assertRaises(MismatchError, lambda: self.assertThat(hud.shown, Eventually(Equals(False), timeout=3)))
127−
128− def test_hide_hud_dragging(self):
129− hud = self.main_window.get_hud()
130− self.unlock_greeter()
131− self.show_hud()
132− rect = hud.globalRect
133− start_x = rect[0] + (rect[2] - rect[0]) / 2
134− start_y = rect[1] + 1
135− stop_x = start_x
136− stop_y = start_y + (rect[3] - rect[1]) / 2
137− self.touch.drag(start_x, start_y, stop_x, stop_y)
138− self.assertThat(hud.shown, Eventually(Equals(False)))
139−
140− def test_hide_hud_launcher(self):
141− hud = self.main_window.get_hud()
142− self.unlock_greeter()
143− self.show_hud()
144− self.show_launcher()
145− self.assertThat(hud.shown, Eventually(Equals(False)))
tests/autopilot/unity8/shell/tests/testlockscreen.pydeleted−118
@@ -1,118 +0,0 @@
1−# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2−# Copyright 2013 Canonical
3−#
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−
9−# This file contains general purpose test cases for Unity.
10−# Each test written in this file will be executed for a variety of
11−# configurations, such as Phone, Tablet or Desktop form factors.
12−#
13−# Sometimes there is the need to disable a certain test for a particular
14−# configuration. To do so, add this in a new line directly below your test:
15−#
16−# test_testname.blacklist = (FormFactors.Tablet, FormFactors.Desktop,)
17−#
18−# Available form factors are:
19−# FormFactors.Phone
20−# FormFactors.Tablet
21−# FormFactors.Desktop
22−
23−
24−"""Tests for the Shell"""
25−
26−from __future__ import absolute_import
27−
28−from unity8.shell.tests import ShellTestCase, FormFactors
29−from unity8.shell.tests.helpers import TestShellHelpers
30−
31−from autopilot.input import Mouse, Touch, Pointer
32−from testtools.matchers import Equals, NotEquals, GreaterThan, MismatchError
33−from autopilot.matchers import Eventually
34−from autopilot.display import Display
35−from autopilot.platform import model
36−
37−import unittest
38−import time
39−import os
40−from os import path
41−
42−
43−class TestLockscreens(ShellTestCase, TestShellHelpers):
44−
45− """Tests the Lockscreens"""
46−
47− # Scenarios:
48− # Fill in the scenarios to run the whole test suite with multiple configurations.
49− # Use app_width, app_height and grid_unit_px to set the apps geometry.
50− # Set app_width and app_height to 0 to use fullscreen.
51− # Set grid_unit_px to 0 to use the current system environment.
52−
53− if model() == 'Desktop':
54− scenarios = [
55− ('Pinlock', dict(app_width=768, app_height=1280, grid_unit_px=18, lightdm_mock="single-pin")),
56− ('Keylock', dict(app_width=768, app_height=1280, grid_unit_px=18, lightdm_mock="single-passphrase")),
57− ]
58− else:
59− scenarios = [
60− ('Pinlock', dict(app_width=0, app_height=0, grid_unit_px=0, lightdm_mock="single-pin")),
61− ('Keylock', dict(app_width=0, app_height=0, grid_unit_px=0, lightdm_mock="single-key")),
62− ]
63−
64− def setUp(self):
65− self.touch = Touch.create()
66−
67− sg = Display().create()
68− divisor = 1
69− while (sg.get_screen_width() < self.app_width / divisor or sg.get_screen_height() < self.app_height / divisor):
70− divisor = divisor * 2
71− super(TestLockscreens, self).setUp("%sx%s" % (self.app_width / divisor, self.app_height / divisor), "%s" % (self.grid_unit_px / divisor))
72−
73− dash = self.main_window.get_dash()
74− self.assertThat(dash.showScopeOnLoaded, Eventually(Equals(""), timeout=30))
75−
76− def test_unlock(self):
77− self.unlock_greeter()
78−
79− pinPadLoader = self.main_window.get_pinPadLoader();
80− self.assertThat(pinPadLoader.progress, Eventually(Equals(1)))
81− lockscreen = self.main_window.get_lockscreen();
82− self.assertThat(lockscreen.shown, Eventually(Equals(True)))
83−
84− if self.lightdm_mock == "single-pin":
85− self.touch.tap_object(self.main_window.get_pinPadButton(1))
86− self.touch.tap_object(self.main_window.get_pinPadButton(2))
87− self.touch.tap_object(self.main_window.get_pinPadButton(3))
88− self.touch.tap_object(self.main_window.get_pinPadButton(4))
89− self.assertThat(lockscreen.shown, Eventually(Equals(False)))
90− else:
91− pinentryField = self.main_window.get_pinentryField()
92− self.touch.tap_object(pinentryField)
93− self.keyboard.type("password\n")
94− self.assertThat(lockscreen.shown, Eventually(Equals(False)))
95−
96− def test_unlock_wrong(self):
97− self.unlock_greeter()
98−
99− pinPadLoader = self.main_window.get_pinPadLoader();
100− self.assertThat(pinPadLoader.progress, Eventually(Equals(1)))
101− lockscreen = self.main_window.get_lockscreen();
102− self.assertThat(lockscreen.shown, Eventually(Equals(True)))
103− pinentryField = self.main_window.get_pinentryField()
104−
105− if self.lightdm_mock == "single-pin":
106− self.touch.tap_object(self.main_window.get_pinPadButton(4))
107− self.touch.tap_object(self.main_window.get_pinPadButton(3))
108− self.touch.tap_object(self.main_window.get_pinPadButton(2))
109− self.assertThat(pinentryField.text, Eventually(Equals("432")))
110− self.touch.tap_object(self.main_window.get_pinPadButton(1))
111−
112− self.assertThat(pinentryField.text, Eventually(Equals("")))
113− self.assertThat(lockscreen.shown, Eventually(Equals(True)))
114− else:
115− self.touch.tap_object(pinentryField)
116− self.keyboard.type("foobar\n")
117− self.assertThat(pinentryField.text, Eventually(Equals("")))
118− self.assertThat(lockscreen.shown, Eventually(Equals(True)))