Merge lp:~elopio/ubuntu-ui-toolkit/base_autopilot_class into lp:ubuntu-ui-toolkit

Proposed by Leo Arias
Status: Merged
Approved by: Cris Dywan
Approved revision: 777
Merged at revision: 751
Proposed branch: lp:~elopio/ubuntu-ui-toolkit/base_autopilot_class
Merge into: lp:ubuntu-ui-toolkit
Diff against target: 490 lines (+278/-111)
6 files modified
examples/ubuntu-ui-toolkit-gallery/ubuntu-ui-toolkit-gallery.desktop (+4/-0)
tests/autopilot/ubuntuuitoolkit/base.py (+37/-0)
tests/autopilot/ubuntuuitoolkit/tests/__init__.py (+115/-70)
tests/autopilot/ubuntuuitoolkit/tests/gallery/test_gallery.py (+75/-36)
tests/autopilot/ubuntuuitoolkit/tests/test_base.py (+42/-0)
tests/autopilot/ubuntuuitoolkit/tests/test_emulators.py (+5/-5)
To merge this branch: bzr merge lp:~elopio/ubuntu-ui-toolkit/base_autopilot_class
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Approve
Omer Akram (community) Approve
Review via email: mp+185170@code.launchpad.net

Commit message

Added UbuntuUIToolkitAppTestCase as a base test case for the autopilot tests.

To post a comment you must log in.
Revision history for this message
Leo Arias (elopio) wrote :

missing the UbuntuUIToolkitSingleQMLAppTestCase tests.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Omer Akram (om26er) :
review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Leo Arias (elopio) wrote :

About the last failure on jenkins:

<fginther> elopio, veebers also saw this or something similar recently. My theory is that we're running into stale pyc files (something like this: http://bugs.python.org/issue15030)
<fginther> elopio, now that we've seen it twice, it doesn't appear to be a fluke
<elopio> fginther: that used to happen on U1 jobs too. But with virtualenv and clearing the workspace for every job was easier to fix.
<fginther> elopio, good to know. We're relying on apt to clean up the devices after the test runs, I don't think this cleans up the pyc files
<fginther> elopio, I'll file a bug and start looking at a fix.
<fginther> elopio, you're results are from the ci tests, which wouldn't land the branch anyway. If it's otherwise good, just have the MP approved and the tests will be re-executed anyway. The chance of running into this same failure are slim, I've only seen it twice over a few hundered test runs.
<elopio> fginther: good. Thanks.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Francis Ginther (fginther) wrote :

Re-approving after resolving multiple jenkins issues from the day.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'examples/ubuntu-ui-toolkit-gallery/ubuntu-ui-toolkit-gallery.desktop'
2--- examples/ubuntu-ui-toolkit-gallery/ubuntu-ui-toolkit-gallery.desktop 2013-05-08 01:55:45 +0000
3+++ examples/ubuntu-ui-toolkit-gallery/ubuntu-ui-toolkit-gallery.desktop 2013-09-19 14:22:51 +0000
4@@ -4,3 +4,7 @@
5 Terminal=false
6 Type=Application
7 X-Ubuntu-Touch=true
8+# Added Icon and Path as a workaround for http://pad.lv/1227359.
9+# TODO remove them once that bug is fixed. --elopio - 2013-09-18
10+Icon=Not important
11+Path=Not important
12
13=== added file 'tests/autopilot/ubuntuuitoolkit/base.py'
14--- tests/autopilot/ubuntuuitoolkit/base.py 1970-01-01 00:00:00 +0000
15+++ tests/autopilot/ubuntuuitoolkit/base.py 2013-09-19 14:22:51 +0000
16@@ -0,0 +1,37 @@
17+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
18+#
19+# Copyright (C) 2013 Canonical Ltd.
20+#
21+# This program is free software; you can redistribute it and/or modify
22+# it under the terms of the GNU Lesser General Public License as published by
23+# the Free Software Foundation; version 3.
24+#
25+# This program is distributed in the hope that it will be useful,
26+# but WITHOUT ANY WARRANTY; without even the implied warranty of
27+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28+# GNU Lesser General Public License for more details.
29+#
30+# You should have received a copy of the GNU Lesser General Public License
31+# along with this program. If not, see <http://www.gnu.org/licenses/>.
32+
33+"""Base classes for Autopilot tests using the Ubuntu UI Toolkit."""
34+
35+from autopilot import (
36+ input,
37+ platform,
38+ testcase
39+)
40+
41+
42+class UbuntuUIToolkitAppTestCase(testcase.AutopilotTestCase):
43+ """Autopilot test case for applications using the Ubuntu UI Toolkit."""
44+
45+ def setUp(self):
46+ super(UbuntuUIToolkitAppTestCase, self).setUp()
47+ self.input_device_class = self._get_input_device_class()
48+
49+ def _get_input_device_class(self):
50+ if platform.model() == 'Desktop':
51+ return input.Mouse
52+ else:
53+ return input.Touch
54
55=== modified file 'tests/autopilot/ubuntuuitoolkit/tests/__init__.py'
56--- tests/autopilot/ubuntuuitoolkit/tests/__init__.py 2013-09-03 10:12:39 +0000
57+++ tests/autopilot/ubuntuuitoolkit/tests/__init__.py 2013-09-19 14:22:51 +0000
58@@ -16,81 +16,126 @@
59
60 """Ubuntu UI Toolkit autopilot tests."""
61
62-from os import remove
63-import os.path
64-from tempfile import mktemp
65-import subprocess
66+import os
67+import tempfile
68
69-from autopilot.input import Mouse, Touch, Pointer
70+from autopilot.input import Pointer
71 from autopilot.matchers import Eventually
72-from autopilot.platform import model
73 from testtools.matchers import Is, Not, Equals
74-from autopilot.testcase import AutopilotTestCase
75-
76-from ubuntuuitoolkit import emulators
77-
78-
79-def get_module_include_path():
80+
81+from ubuntuuitoolkit import base, emulators
82+
83+
84+_DESKTOP_FILE_CONTENTS = ("""[Desktop Entry]
85+Type=Application
86+Exec=Not important
87+Path=Not important
88+Name=Test app
89+Icon=Not important
90+""")
91+
92+
93+def _write_test_desktop_file():
94+ desktop_file_dir = get_local_desktop_file_directory()
95+ if not os.path.exists(desktop_file_dir):
96+ os.makedirs(desktop_file_dir)
97+ desktop_file = tempfile.NamedTemporaryFile(
98+ suffix='.desktop', dir=desktop_file_dir, delete=False)
99+ desktop_file.write(_DESKTOP_FILE_CONTENTS)
100+ desktop_file.close()
101+ return desktop_file.name
102+
103+
104+def get_local_desktop_file_directory():
105+ return os.path.join(os.environ['HOME'], '.local', 'share', 'applications')
106+
107+
108+def _get_module_include_path():
109+ return os.path.join(get_path_to_source_root(), 'modules')
110+
111+
112+def get_path_to_source_root():
113 return os.path.abspath(
114 os.path.join(
115- os.path.dirname(__file__),
116- '..',
117- '..',
118- '..',
119- '..',
120- 'modules')
121- )
122-
123-
124-def get_input_device_scenarios():
125- """Return the scenarios with the right input device for the platform."""
126- if model() == 'Desktop':
127- scenarios = [('with mouse', dict(input_device_class=Mouse))]
128- else:
129- scenarios = [('with touch', dict(input_device_class=Touch))]
130- return scenarios
131-
132-
133-class UbuntuUiToolkitTestCase(AutopilotTestCase):
134- """Common test case class for SDK tests."""
135-
136- scenarios = get_input_device_scenarios()
137-
138- def setUp(self):
139- self.pointing_device = Pointer(self.input_device_class.create())
140- super(UbuntuUiToolkitTestCase, self).setUp()
141- self.launch_test_qml()
142-
143- def launch_test_qml(self):
144- # If the test class has defined a 'test_qml' class attribute then we
145- # write it to disk and launch it inside the Qml Viewer. If not, then we
146- # silently do nothing (presumably the test has something else planned).
147- arch = subprocess.check_output(
148- ["dpkg-architecture", "-qDEB_HOST_MULTIARCH"]).strip()
149- if hasattr(self, 'test_qml') and isinstance(self.test_qml, basestring):
150- qml_path = mktemp(suffix='.qml')
151- open(qml_path, 'w').write(self.test_qml)
152- self.addCleanup(remove, qml_path)
153-
154- self.app = self.launch_test_application(
155- "/usr/lib/" + arch + "/qt5/bin/qmlscene",
156- "-I" + get_module_include_path(),
157- qml_path,
158- emulator_base=emulators.UbuntuUIToolkitEmulatorBase,
159- app_type='qt')
160-
161- if (hasattr(self, 'test_qml_file') and
162- isinstance(self.test_qml_file, basestring)):
163- qml_path = self.test_qml_file
164- self.app = self.launch_test_application(
165- "/usr/lib/" + arch + "/qt5/bin/qmlscene",
166- "-I" + get_module_include_path(),
167- qml_path,
168- emulator_base=emulators.UbuntuUIToolkitEmulatorBase,
169- app_type='qt')
170-
171- self.assertThat(
172- self.main_view.visible, Eventually(Equals(True)))
173+ os.path.dirname(__file__), '..', '..', '..', '..'))
174+
175+
176+class QMLStringAppTestCase(base.UbuntuUIToolkitAppTestCase):
177+ """Base test case for self tests that define the QML on an string."""
178+
179+ test_qml = ("""
180+import QtQuick 2.0
181+import Ubuntu.Components 0.1
182+
183+MainView {
184+ width: units.gu(48)
185+ height: units.gu(60)
186+}
187+""")
188+
189+ def setUp(self):
190+ super(QMLStringAppTestCase, self).setUp()
191+ self.pointing_device = Pointer(self.input_device_class.create())
192+ self.launch_application()
193+
194+ def launch_application(self):
195+ qml_file_path = self._write_test_qml_file()
196+ self.addCleanup(os.remove, qml_file_path)
197+ desktop_file_path = _write_test_desktop_file()
198+ self.addCleanup(os.remove, desktop_file_path)
199+ self.app = self.launch_test_application(
200+ 'qmlscene',
201+ '-I' + _get_module_include_path(),
202+ qml_file_path,
203+ '--desktop_file_hint={0}'.format(desktop_file_path),
204+ emulator_base=emulators.UbuntuUIToolkitEmulatorBase,
205+ app_type='qt')
206+
207+ self.assertThat(
208+ self.main_view.visible, Eventually(Equals(True)))
209+
210+ def _write_test_qml_file(self):
211+ qml_file = tempfile.NamedTemporaryFile(suffix='.qml', delete=False)
212+ qml_file.write(self.test_qml)
213+ qml_file.close()
214+ return qml_file.name
215+
216+ @property
217+ def main_view(self):
218+ return self.app.select_single(emulators.MainView)
219+
220+
221+class QMLFileAppTestCase(base.UbuntuUIToolkitAppTestCase):
222+ """Base test case for self tests that launch a QML file."""
223+
224+ test_qml_file_path = '/path/to/file.qml'
225+ desktop_file_path = None
226+
227+ def setUp(self):
228+ super(QMLFileAppTestCase, self).setUp()
229+ self.pointing_device = Pointer(self.input_device_class.create())
230+ self.launch_application()
231+
232+ def launch_application(self):
233+ desktop_file_path = self._get_desktop_file_path()
234+ self.app = self.launch_test_application(
235+ 'qmlscene',
236+ "-I" + _get_module_include_path(),
237+ self.test_qml_file_path,
238+ '--desktop_file_hint={0}'.format(desktop_file_path),
239+ emulator_base=emulators.UbuntuUIToolkitEmulatorBase,
240+ app_type='qt')
241+
242+ self.assertThat(
243+ self.main_view.visible, Eventually(Equals(True)))
244+
245+ def _get_desktop_file_path(self):
246+ if self.desktop_file_path is None:
247+ desktop_file_path = _write_test_desktop_file()
248+ self.addCleanup(os.remove, desktop_file_path)
249+ return desktop_file_path
250+ else:
251+ self.desktop_file_path
252
253 @property
254 def main_view(self):
255
256=== modified file 'tests/autopilot/ubuntuuitoolkit/tests/gallery/test_gallery.py'
257--- tests/autopilot/ubuntuuitoolkit/tests/gallery/test_gallery.py 2013-09-03 10:12:39 +0000
258+++ tests/autopilot/ubuntuuitoolkit/tests/gallery/test_gallery.py 2013-09-19 14:22:51 +0000
259@@ -17,8 +17,7 @@
260 """Tests for the Ubuntu UI Toolkit Gallery"""
261
262 import os
263-
264-import testscenarios
265+import shutil
266
267 from autopilot.matchers import Eventually
268 from testtools.matchers import Is, Not, Equals
269@@ -26,23 +25,64 @@
270 from ubuntuuitoolkit import tests
271
272
273-class GalleryTestCase(tests.UbuntuUiToolkitTestCase):
274+class GalleryTestCase(tests.QMLFileAppTestCase):
275 """Base class for gallery test cases."""
276
277- # Support both running from system and in the source directory
278- runPath = os.path.dirname(os.path.realpath(__file__))
279- localSourceFile = (
280- runPath +
281- "/../../../../../examples/ubuntu-ui-toolkit-gallery/"
282- "ubuntu-ui-toolkit-gallery.qml")
283- if (os.path.isfile(localSourceFile)):
284- print "Using local source directory"
285- test_qml_file = localSourceFile
286- else:
287- print "Using system QML file"
288- test_qml_file = (
289- "/usr/lib/ubuntu-ui-toolkit/examples/ubuntu-ui-toolkit-gallery/"
290- "ubuntu-ui-toolkit-gallery.qml")
291+ local_desktop_file_path = None
292+
293+ def setUp(self):
294+ self.app_qml_source_path = os.path.join(
295+ self._get_path_to_gallery_source(),
296+ 'ubuntu-ui-toolkit-gallery.qml')
297+ self.test_qml_file_path = self._get_test_qml_file_path()
298+ self.desktop_file_path = self._get_desktop_file_path()
299+ super(GalleryTestCase, self).setUp()
300+
301+ def _get_path_to_gallery_source(self):
302+ return os.path.join(
303+ tests.get_path_to_source_root(), 'examples',
304+ 'ubuntu-ui-toolkit-gallery')
305+
306+ def _application_source_exists(self):
307+ return os.path.exists(self.app_qml_source_path)
308+
309+ def _get_test_qml_file_path(self):
310+ if self._application_source_exists():
311+ return self.app_qml_source_path
312+ else:
313+ return os.path.join(
314+ self._get_path_to_installed_gallery(),
315+ 'ubuntu-ui-toolkit-gallery.qml')
316+
317+ def _get_path_to_installed_gallery(self):
318+ return '/usr/lib/ubuntu-ui-toolkit/examples/ubuntu-ui-toolkit-gallery'
319+
320+ def _get_desktop_file_path(self):
321+ if self._application_source_exists():
322+ local_desktop_file_dir = tests.get_local_desktop_file_directory()
323+ if not os.path.exists(local_desktop_file_dir):
324+ os.makedirs(local_desktop_file_dir)
325+ source_desktop_file_path = os.path.join(
326+ self._get_path_to_gallery_source(),
327+ 'ubuntu-ui-toolkit-gallery.desktop')
328+ local_desktop_file_path = os.path.join(
329+ local_desktop_file_dir, 'ubuntu-ui-toolkit-gallery.desktop')
330+ shutil.copy(source_desktop_file_path, local_desktop_file_path)
331+ # We can't delete the desktop file before we close the application,
332+ # so we save it on an attribute to be deleted on tear down.
333+ self.local_desktop_file_path = local_desktop_file_path
334+ return local_desktop_file_path
335+ else:
336+ return os.path.join(
337+ self._get_path_to_installed_gallery(),
338+ 'ubuntu-ui-toolkit-gallery.desktop')
339+
340+ def tearDown(self):
341+ super(GalleryTestCase, self).tearDown()
342+ # We can't delete the desktop file before we close the application,
343+ # so we save it on an attribute to be deleted on tear down.
344+ if self.local_desktop_file_path is not None:
345+ os.remove(self.local_desktop_file_path)
346
347
348 class GenericTests(GalleryTestCase):
349@@ -192,27 +232,26 @@
350
351 class ButtonsTestCase(GalleryTestCase):
352
353- scenarios = testscenarios.multiply_scenarios(
354- tests.get_input_device_scenarios(),
355- [('standard button', dict(
356+ scenarios = [
357+ ('standard button', dict(
358 button_name="button_text", is_enabled=True, color=None, icon=None,
359 text="Call")),
360- ('button with color', dict(
361- button_name="button_color", is_enabled=True,
362- color=[0, 0, 0, 255], icon=None, text="Call")),
363- ('button with icon', dict(
364- button_name="button_iconsource", is_enabled=True, color=None,
365- icon="call.png", text=None)),
366- ('button with icon on the right', dict(
367- button_name="button_iconsource_right_text", is_enabled=True,
368- color=None, icon="call.png", text="Call")),
369- ('button with icon on the left', dict(
370- button_name="button_iconsource_left_text", is_enabled=True,
371- color=None, icon="call.png", text="Call")),
372- ('disabled button', dict(
373- button_name="button_text_disabled", is_enabled=False, color=None,
374- icon=None, text="Call"))]
375- )
376+ ('button with color', dict(
377+ button_name="button_color", is_enabled=True,
378+ color=[0, 0, 0, 255], icon=None, text="Call")),
379+ ('button with icon', dict(
380+ button_name="button_iconsource", is_enabled=True, color=None,
381+ icon="call.png", text=None)),
382+ ('button with icon on the right', dict(
383+ button_name="button_iconsource_right_text", is_enabled=True,
384+ color=None, icon="call.png", text="Call")),
385+ ('button with icon on the left', dict(
386+ button_name="button_iconsource_left_text", is_enabled=True,
387+ color=None, icon="call.png", text="Call")),
388+ ('disabled button', dict(
389+ button_name="button_text_disabled", is_enabled=False, color=None,
390+ icon=None, text="Call"))
391+ ]
392
393 def test_buttons(self):
394 item = "Buttons"
395
396=== added file 'tests/autopilot/ubuntuuitoolkit/tests/test_base.py'
397--- tests/autopilot/ubuntuuitoolkit/tests/test_base.py 1970-01-01 00:00:00 +0000
398+++ tests/autopilot/ubuntuuitoolkit/tests/test_base.py 2013-09-19 14:22:51 +0000
399@@ -0,0 +1,42 @@
400+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
401+#
402+# Copyright (C) 2013 Canonical Ltd.
403+#
404+# This program is free software; you can redistribute it and/or modify
405+# it under the terms of the GNU Lesser General Public License as published by
406+# the Free Software Foundation; version 3.
407+#
408+# This program is distributed in the hope that it will be useful,
409+# but WITHOUT ANY WARRANTY; without even the implied warranty of
410+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
411+# GNU Lesser General Public License for more details.
412+#
413+# You should have received a copy of the GNU Lesser General Public License
414+# along with this program. If not, see <http://www.gnu.org/licenses/>.
415+
416+import testtools
417+from autopilot import input, platform
418+
419+from ubuntuuitoolkit import base
420+
421+
422+class AppTestCase(base.UbuntuUIToolkitAppTestCase):
423+ """Empty test case to be used by other tests."""
424+
425+ def _runTest(self):
426+ pass
427+
428+
429+class TestUbuntuUIToolkitAppTestCase(testtools.TestCase):
430+
431+ @testtools.skipIf(platform.model() != 'Desktop', 'Desktop only')
432+ def test_desktop_input_device_class(self):
433+ test = AppTestCase('_runTest')
434+ test.setUp()
435+ self.assertIs(test.input_device_class, input.Mouse)
436+
437+ @testtools.skipIf(platform.model() == 'Desktop', 'Phablet only')
438+ def test_phablet_input_device_class(self):
439+ test = AppTestCase('_runTest')
440+ test.setUp()
441+ self.assertIs(test.input_device_class, input.Touch)
442
443=== modified file 'tests/autopilot/ubuntuuitoolkit/tests/test_emulators.py'
444--- tests/autopilot/ubuntuuitoolkit/tests/test_emulators.py 2013-09-03 20:31:00 +0000
445+++ tests/autopilot/ubuntuuitoolkit/tests/test_emulators.py 2013-09-19 14:22:51 +0000
446@@ -46,7 +46,7 @@
447 self.assertIsInstance(keyword_args['device'], input.Touch)
448
449
450-class MainViewTestCase(tests.UbuntuUiToolkitTestCase):
451+class MainViewTestCase(tests.QMLStringAppTestCase):
452
453 test_qml = ("""
454 import QtQuick 2.0
455@@ -84,7 +84,7 @@
456 error.message, 'The MainView has no Tabs.')
457
458
459-class PageTestCase(tests.UbuntuUiToolkitTestCase):
460+class PageTestCase(tests.QMLStringAppTestCase):
461
462 test_qml = ("""
463 import QtQuick 2.0
464@@ -107,7 +107,7 @@
465 self.assertEqual(header.title, "Test title")
466
467
468-class ToolbarTestCase(tests.UbuntuUiToolkitTestCase):
469+class ToolbarTestCase(tests.QMLStringAppTestCase):
470
471 test_qml = ("""
472 import QtQuick 2.0
473@@ -184,7 +184,7 @@
474 error.message, 'Button with objectName "unexisting" not found.')
475
476
477-class TabsTestCase(tests.UbuntuUiToolkitTestCase):
478+class TabsTestCase(tests.QMLStringAppTestCase):
479
480 test_qml = ("""
481 import QtQuick 2.0
482@@ -308,7 +308,7 @@
483 error.message, 'Tab with objectName "unexisting" not found.')
484
485
486-class ActionSelectionPopoverTestCase(tests.UbuntuUiToolkitTestCase):
487+class ActionSelectionPopoverTestCase(tests.QMLStringAppTestCase):
488
489 test_qml = ("""
490 import QtQuick 2.0

Subscribers

People subscribed via source and target branches

to status/vote changes: