Merge lp:~nskaggs/ubuntu-clock-app/update-test-layout into lp:ubuntu-clock-app

Proposed by Nicholas Skaggs
Status: Merged
Approved by: Nicholas Skaggs
Approved revision: 291
Merged at revision: 284
Proposed branch: lp:~nskaggs/ubuntu-clock-app/update-test-layout
Merge into: lp:ubuntu-clock-app
Diff against target: 1516 lines (+750/-601)
8 files modified
README.autopilot (+13/-10)
debian/changelog (+6/-0)
tests/autopilot/ubuntu_clock_app/CMakePluginParser.py (+119/-0)
tests/autopilot/ubuntu_clock_app/__init__.py (+545/-3)
tests/autopilot/ubuntu_clock_app/emulators.py (+0/-546)
tests/autopilot/ubuntu_clock_app/tests/__init__.py (+60/-35)
tests/autopilot/ubuntu_clock_app/tests/test_alarm.py (+3/-3)
tests/autopilot/ubuntu_clock_app/tests/test_clock.py (+4/-4)
To merge this branch: bzr merge lp:~nskaggs/ubuntu-clock-app/update-test-layout
Reviewer Review Type Date Requested Status
Nekhelesh Ramananthan Approve
Ubuntu Phone Apps Jenkins Bot continuous-integration Approve
Review via email: mp+262271@code.launchpad.net

Commit message

Update testing layout

Description of the change

Update testing layout. This conforms more with the best practices we have today by removing the older naming of emulators. It also should now properly detect a cmake build, and not require you to build in a certain folder.

To post a comment you must log in.
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Nekhelesh Ramananthan (nik90) wrote :

Thanks Nicholas for the MP. Just a couple of minor things,

In your MP description you mention that "It also should now properly detect a cmake build, and not require you to build in a certain folder." .. I suppose you're talking about the lack of need to create builddir like it was mandatory to do before. However when I tried running the tests without creating a separate build dir, I wasn't able to run autopilot tests. The errors can be found at http://paste.ubuntu.com/11737058/.

On creating a builddir, cmake .. && make and then running the test, everything worked correctly. The readme still informs the user to create a builddir, so w.r.t everything is good.

Is this what you expected?

review: Needs Information
Revision history for this message
Nekhelesh Ramananthan (nik90) wrote :

Can you update the debian changelog pls?

review: Needs Fixing
285. By Nicholas Skaggs

Update testing layout. No longer require specific cmake build directory to run tests

286. By Nicholas Skaggs

bzr revert

287. By Nicholas Skaggs

remerge trunk

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
288. By Nicholas Skaggs

fix pathing for launch

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
289. By Nicholas Skaggs

fix cmake parser defaults

290. By Nicholas Skaggs

flake8

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
291. By Nicholas Skaggs

force builddir

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Nekhelesh Ramananthan (nik90) wrote :

So on testing this morning, I observed the following,
- Creating a specific builddir and then running the tests works as expected
- Building using the Ubuntu SDK and then running the tests works as expected (improvement compared to what we have in trunk)
- Building in the root app directory and running the tests fail (http://paste.ubuntu.com/11739768/)

This is fine by me. If this accomplishes what you expected, feel free to top-approve.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'README.autopilot'
2--- README.autopilot 2014-08-14 20:16:14 +0000
3+++ README.autopilot 2015-06-18 22:50:34 +0000
4@@ -2,31 +2,34 @@
5
6 Ubuntu Clock App follows a test driven development where autopilot tests are run before every merge into trunk. If you are submitting your bugfix/patch to the clock app, please follow the following steps below to ensure that all tests pass before proposing a merge request.
7
8-If you are looking for more info about Autopilot or writing AP tests for the clock app, here are some useful links to help you,
9-
10-- http://developer.ubuntu.com/api/devel/ubuntu-14.10/python/autopilot/
11-- http://developer.ubuntu.com/api/devel/ubuntu-14.10/python/autopilot-emulator/
12+If you are looking for more info about Autopilot or writing AP tests for the clock app, here are some useful links to help you:
13+
14+- http://developer.ubuntu.com/start/quality
15+- https://developer.ubuntu.com/api/autopilot/python/1.5.0/
16+
17+For help and options on running tests, see:
18+
19+- https://developer.ubuntu.com/en/start/platform/guides/running-autopilot-tests/
20
21 ## Prerequisites
22
23 Install the following autopilot packages required to run the tests,
24-$ sudo apt-get install python-autopilot libautopilot-qt ubuntu-ui-toolkit-autopilot python3-autopilot-vis
25-
26+$ sudo apt-get install python3-autopilot libautopilot-qt ubuntu-ui-toolkit-autopilot python3-autopilot-vis
27
28 ## Running tests on the desktop
29
30 Using terminal:
31
32 * Branch the clock app code,
33- $ bzr branch lp:ubuntu-clock-app/reboot
34-
35+ $ bzr branch lp:ubuntu-clock-app
36+
37 * Build the clock app,
38 $ mkdir builddir && cd builddir
39 $ cmake .. && make
40 $ cd ..
41
42 * Navigate to the tests/autopilot directory.
43- $ cd reboot/tests/autopilot
44+ $ cd tests/autopilot
45
46 * run all tests.
47 $ autopilot3 run -vv ubuntu_clock_app
48@@ -47,4 +50,4 @@
49 Using autopkg:
50
51 1. navigate to the directory where the ubuntu-clock-app branch is and run:
52- $ adt-run ubuntu-clock-app --click=com.ubuntu.clock.devel --- ssh -s adb
53+ $ adt-run ubuntu-clock-app --click=com.ubuntu.clock.devel --- ssh -s adb -p YOURPASSWORD
54
55=== modified file 'debian/changelog'
56--- debian/changelog 2015-06-18 20:01:54 +0000
57+++ debian/changelog 2015-06-18 22:50:34 +0000
58@@ -1,3 +1,9 @@
59+ubuntu-clock-app (3.4ubuntu1) UNRELEASED; urgency=medium
60+
61+ * Update testing layout.
62+
63+ -- Nicholas Skaggs <nicholas.skaggs@canonical.com> Thu, 18 Jun 2015 15:48:22 -0400
64+
65 ubuntu-clock-app (3.4) vivid; urgency=medium
66
67 [Nekhelesh Ramananthan]
68
69=== added file 'tests/autopilot/ubuntu_clock_app/CMakePluginParser.py'
70--- tests/autopilot/ubuntu_clock_app/CMakePluginParser.py 1970-01-01 00:00:00 +0000
71+++ tests/autopilot/ubuntu_clock_app/CMakePluginParser.py 2015-06-18 22:50:34 +0000
72@@ -0,0 +1,119 @@
73+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
74+#
75+# Copyright (C) 2014 Canonical Ltd.
76+#
77+# This program is free software; you can redistribute it and/or modify
78+# it under the terms of the GNU Lesser General Public License as published by
79+# the Free Software Foundation; version 3.
80+#
81+# This program is distributed in the hope that it will be useful,
82+# but WITHOUT ANY WARRANTY; without even the implied warranty of
83+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
84+# GNU Lesser General Public License for more details.
85+#
86+# You should have received a copy of the GNU Lesser General Public License
87+# along with this program. If not, see <http://www.gnu.org/licenses/>.
88+#
89+# Author:
90+# David Planella <david.planella@ubuntu.com>
91+
92+"""
93+This module parses a configuration file from the Qt Creator's CMake plugin and
94+enables programmatical read-only access to several of its configuration options
95+"""
96+
97+from lxml import etree
98+
99+
100+class CMakePluginParseError(Exception):
101+ """
102+ Custom exception for errors during the parsing of a
103+ CMakeLists.txt.user file
104+ """
105+ def __init__(self, message):
106+ Exception.__init__(self, message)
107+
108+
109+class CMakePluginParser(object):
110+ """
111+ Parses a CMake plugin's config file and provides R/O access to its
112+ configuration options """
113+
114+ def __init__(self, cmakelists_usr_file='CMakeLists.txt.user'):
115+ self.usr_file = cmakelists_usr_file
116+
117+ try:
118+ self.info = etree.parse(self.usr_file)
119+ except:
120+ raise CMakePluginParseError("Could not open the given " +
121+ "CMakeLists.txt.user file: " +
122+ self.info)
123+
124+ def _get_active_build_target(self):
125+ """
126+ Return the active build target from the current project in Qt Creator
127+ """
128+
129+ try:
130+ active_build_target_nr = self.info.xpath(
131+ "./data/variable" +
132+ "[text()='ProjectExplorer.Project.ActiveTarget']" +
133+ "/../value")[0].text
134+ except:
135+ raise CMakePluginParseError("Could not find the active build " +
136+ "target in the CMake plugin's config")
137+
138+ active_build_target = "ProjectExplorer.Project.Target." + \
139+ active_build_target_nr
140+
141+ return active_build_target
142+
143+ def _get_active_build_config(self, active_build_target):
144+ """Return the active build config from the active build targed"""
145+
146+ try:
147+ active_build_config_nr = self.info.xpath(
148+ "./data/variable[text()='{0}']".format(active_build_target) +
149+ "/..//value[@key="
150+ "'ProjectExplorer.Target.ActiveBuildConfiguration']")[0].text
151+ except:
152+ raise CMakePluginParseError("Could not find the active build " +
153+ "target's active build config " +
154+ "in the CMake plugin's config")
155+
156+ active_build_config = "ProjectExplorer.Target.BuildConfiguration." + \
157+ active_build_config_nr
158+
159+ return active_build_config
160+
161+ def _get_active_build_config_path(self):
162+ """Return the active build config's absolute path"""
163+
164+ active_build_target = self._get_active_build_target()
165+ active_build_config = \
166+ self._get_active_build_config(active_build_target)
167+
168+ try:
169+ active_build_config_node = self.info.xpath(
170+ "./data/variable[text()='{0}']".format(active_build_target) +
171+ "/..//valuemap[@key='{0}']".format(active_build_config))[0]
172+ except:
173+ raise CMakePluginParseError("Could not find the active " +
174+ "build config's node " +
175+ "in the CMake plugin's config")
176+
177+ try:
178+ active_build_config_path = active_build_config_node.xpath(
179+ "./value[@key=" +
180+ "'ProjectExplorer.BuildConfiguration.BuildDirectory']")[0].text
181+ except:
182+ raise CMakePluginParseError("Could not find the active build " +
183+ "directory in the CMake plugin's " +
184+ "config")
185+
186+ return active_build_config_path
187+
188+ @property
189+ def active_build_dir(self):
190+ """Return the active build config's directory as an absolute path"""
191+ return self._get_active_build_config_path()
192
193=== modified file 'tests/autopilot/ubuntu_clock_app/__init__.py'
194--- tests/autopilot/ubuntu_clock_app/__init__.py 2014-08-02 12:57:23 +0000
195+++ tests/autopilot/ubuntu_clock_app/__init__.py 2015-06-18 22:50:34 +0000
196@@ -1,4 +1,6 @@
197-# Copyright (C) 2014 Canonical Ltd
198+# -#- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -#-
199+#
200+# Copyright (C) 2014-2015 Canonical Ltd
201 #
202 # This file is part of Ubuntu Clock App
203 #
204@@ -14,5 +16,545 @@
205 # You should have received a copy of the GNU General Public License
206 # along with this program. If not, see <http://www.gnu.org/licenses/>.
207
208-
209-"""clock-app autopilot tests and emulators - top level package."""
210+import logging
211+
212+from autopilot import logging as autopilot_logging
213+from autopilot.introspection import dbus
214+from testtools.matchers import GreaterThan
215+
216+from ubuntuuitoolkit import pickers
217+import ubuntuuitoolkit
218+
219+logger = logging.getLogger(__name__)
220+
221+
222+class ClockEmulatorException(ubuntuuitoolkit.ToolkitException):
223+
224+ """Exception raised when there is an error with the emulator."""
225+
226+
227+class ClockApp(object):
228+
229+ """Autopilot helper object for clock."""
230+
231+ def __init__(self, app_proxy, test_type):
232+ self.app = app_proxy
233+ self.test_type = test_type
234+ self.main_view = self.app.wait_select_single(MainView)
235+
236+ @property
237+ def pointing_device(self):
238+ return self.app.pointing_device
239+
240+
241+class MainView(ubuntuuitoolkit.MainView):
242+
243+ @autopilot_logging.log_action(logger.info)
244+ def open_clock(self):
245+ """Open the Clock Page.
246+
247+ :return the Clock Page
248+
249+ """
250+ return self.wait_select_single(ClockPage)
251+
252+ @autopilot_logging.log_action(logger.info)
253+ def open_alarm(self):
254+ """Open the Alarm Page.
255+
256+ :return: the Alarm Page.
257+
258+ """
259+ clockPage = self.open_clock()
260+ clockPage.reveal_bottom_edge_page()
261+ self.get_header().visible.wait_for(True)
262+ return self.wait_select_single(Page11)
263+
264+ def get_AlarmList(self):
265+ """ Get the AlarmList object. """
266+ return AlarmList.select(self)
267+
268+ @autopilot_logging.log_action(logger.info)
269+ def get_worldCityList(self):
270+ """ Return the World City List page """
271+ return self.wait_select_single("WorldCityList",
272+ objectName="worldCityList")
273+
274+
275+class Page(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
276+
277+ """Autopilot helper for Pages."""
278+
279+ def __init__(self, *args):
280+ super(Page, self).__init__(*args)
281+ # XXX we need a better way to keep reference to the main view.
282+ # --elopio - 2014-01-31
283+ self.main_view = self.get_root_instance().select_single(MainView)
284+
285+
286+class PageWithBottomEdge(MainView):
287+ """
288+ An emulator class that makes it easy to interact with the bottom edge
289+ swipe page
290+ """
291+ def __init__(self, *args):
292+ super(PageWithBottomEdge, self).__init__(*args)
293+
294+ def reveal_bottom_edge_page(self):
295+ """Bring the bottom edge page to the screen"""
296+ self.bottomEdgePageLoaded.wait_for(True)
297+ try:
298+ action_item = self.wait_select_single(objectName='bottomEdgeTip')
299+ action_item.visible.wait_for(True)
300+ action_item.isAnimating.wait_for(False)
301+ start_x = (action_item.globalRect.x +
302+ (action_item.globalRect.width * 0.5))
303+ start_y = (action_item.globalRect.y +
304+ (action_item.height * 0.5))
305+ stop_y = start_y - (self.height * 0.7)
306+ self.pointing_device.drag(start_x, start_y,
307+ start_x, stop_y, rate=2)
308+ self.isReady.wait_for(True)
309+ except dbus.StateNotFoundError:
310+ logger.error('BottomEdge element not found.')
311+ raise
312+
313+
314+class ClockPage(PageWithBottomEdge):
315+ """Autopilot helper for the Clock page."""
316+
317+ @autopilot_logging.log_action(logger.info)
318+ def click_addCity_to_open_worldCityList(self):
319+ """Swipe to reveal WorldCityList"""
320+
321+ addWorldCityButton = self.wait_select_single(
322+ "AbstractButton", objectName="addWorldCityButton")
323+ self.pointing_device.click_object(addWorldCityButton)
324+
325+ def get_num_of_saved_cities(self):
326+ """Return the number of saved world cities"""
327+ return int(self._get_saved_world_city_list().count)
328+
329+ def _get_saved_world_city_list(self):
330+ """Return the saved world city listview object"""
331+ return self.wait_select_single(
332+ "QQuickRepeater", objectName='userWorldCityRepeater')
333+
334+ @autopilot_logging.log_action(logger.info)
335+ def delete_added_world_city(self, city_Name, country_Name):
336+ """ Delete added world city from user world city list """
337+ old_cities_count = self.get_num_of_saved_cities()
338+ index = 0
339+ for index in range(old_cities_count):
340+ world_city_item = self.wait_select_single(
341+ objectName='userWorldCityItem{}'.format(index))
342+ city_name_label = world_city_item.wait_select_single(
343+ 'Label', objectName='userCityNameText')
344+ country_name_label = world_city_item.wait_select_single(
345+ 'Label', objectName='userCountryNameText')
346+ if (city_name_label.text == city_Name and
347+ country_name_label.text == country_Name):
348+ self._delete_userWorldCityItem(index)
349+
350+ # FIXME -----------------------------------------------------------------
351+ # Commenting the following lines as deleting a world city when there is
352+ # only one in the user world city list does not decrease counter to 0 but
353+ # leaves it at 1 so the test fails
354+ # Reported bug #1368393
355+ # Discovered that deleting world city clock deletes the city from the clock
356+ # app, but if you look with autopilot vis the world city is still there
357+ # added a comment to bug #1368393
358+ #
359+ # try:
360+ # self._get_saved_world_city_list().count.wait_for(
361+ # old_cities_count - 1)
362+ # except AssertionError:
363+ # raise ClockEmulatorException('Error deleting city.')
364+ # -------------------------------------------------------------------------
365+
366+ def _delete_userWorldCityItem(self, index):
367+ cityItem = self.wait_select_single(
368+ objectName='userWorldCityItem{}'.format(index))
369+ self._swipe_to_delete(cityItem)
370+ self._confirm_removal(cityItem)
371+
372+ def _swipe_to_delete(self, cityItem):
373+ x, y, width, height = cityItem.globalRect
374+ start_x = x + (width * 0.2)
375+ stop_x = x + (width * 0.8)
376+ start_y = stop_y = y + (height // 2)
377+
378+ self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
379+
380+ def _confirm_removal(self, cityItem):
381+ deleteButton = cityItem.wait_select_single(name='delete')
382+ self.pointing_device.click_object(deleteButton)
383+
384+
385+class Page11(Page):
386+ """Autopilot helper for the Alarm page."""
387+
388+ @autopilot_logging.log_action(logger.info)
389+ def add_single_alarm(self, name, days, time_to_set, test_sound_name):
390+ """Add a single type alarm
391+
392+ :param name: name of alarm
393+ :param days: days on which the alarm should be triggered
394+ :param time_to_set: time to set alarm to
395+ :param test_sound_name: sound to set in alarm
396+
397+ """
398+ alarmListPage = AlarmList.select(self.main_view)
399+ old_alarm_count = alarmListPage.get_num_of_alarms()
400+
401+ edit_alarm_page = self._click_add_alarm_button()
402+ edit_alarm_page.set_alarm_time(time_to_set)
403+
404+ alarm_repeat_page = edit_alarm_page.open_alarmRepeat_page()
405+ alarm_repeat_page.set_alarm_days(days)
406+ self._click_header_backButton()
407+
408+ alarm_label_page = edit_alarm_page.open_alarmLabel_page()
409+ alarm_label_page.set_alarm_label(name)
410+ self._click_header_customBackButton()
411+
412+ alarm_sound_page = edit_alarm_page.open_alarmSound_page()
413+ alarm_sound_page.set_alarm_sound(test_sound_name)
414+ self._click_header_customBackButton()
415+ edit_alarm_page._check_sound_changed(test_sound_name)
416+
417+ self._click_save()
418+ self._confirm_alarm_creation(old_alarm_count)
419+
420+ def _click_add_alarm_button(self):
421+ """Click the add alarm header button."""
422+ header = self.main_view.get_header()
423+ header.click_action_button('addAlarmAction')
424+ return self.main_view.wait_select_single(EditAlarmPage)
425+
426+ def _click_header_customBackButton(self):
427+ """Click the header button: 'customBackButton' """
428+ header = self.main_view.get_header()
429+ header.click_custom_back_button()
430+
431+ def _click_header_backButton(self):
432+ """Click the header button: 'backButton' """
433+ header = self.main_view.get_header()
434+ header.click_back_button()
435+
436+ def _click_save(self):
437+ """Click the save timer header button"""
438+ header = self.main_view.get_header()
439+ header.click_action_button('saveAlarmAction')
440+
441+ def _confirm_alarm_creation(self, count):
442+ """Confirm creation of alarm
443+
444+ :param count: alarm count before alarm creation
445+
446+ """
447+ try:
448+ AlarmList.select(self.main_view)._get_saved_alarms_list().\
449+ count.wait_for(count + 1)
450+ except AssertionError:
451+ raise ClockEmulatorException('Error creating alarm.')
452+
453+
454+class WorldCityList(Page):
455+ """Autopilot helper for World City List page."""
456+
457+ @autopilot_logging.log_action(logger.info)
458+ def add_world_city_from_list(self, city_Name, country_Name):
459+ """Add world city from list
460+
461+ :param city_Name: world city name to add
462+ :param country_Name: country city name belongs to (same city name could
463+ be found in more countries)
464+ """
465+ self.wait_select_single("ActivityIndicator").running.wait_for(False)
466+ cityList = self.wait_select_single("QQuickListView",
467+ objectName="cityList")
468+
469+ cityList.count.wait_for(GreaterThan(0))
470+
471+ for index in range(int(cityList.count)):
472+ world_city_item = self.wait_select_single(
473+ objectName='defaultWorldCityItem{}'.format(index))
474+ city_name_label = world_city_item.wait_select_single(
475+ 'Label', objectName='defaultCityNameText')
476+ country_name_label = world_city_item.wait_select_single(
477+ 'Label', objectName='defaultCountryNameText')
478+ if (city_name_label.text == city_Name and
479+ country_name_label.text == country_Name):
480+ cityList.click_element(
481+ 'defaultWorldCityItem{}'.format(index), direction=None)
482+ break
483+
484+ @autopilot_logging.log_action(logger.info)
485+ def search_world_city_(self, city_Name, country_Name):
486+ """Add world city by searching the world city name
487+
488+ :param city_Name: world city name to add
489+
490+ """
491+ header = self.main_view.get_header()
492+ header.click_action_button("searchButton")
493+ self._search_world_city(city_Name, country_Name)
494+
495+ def _search_world_city(self, city_Name, country_Name):
496+ header = self.main_view.get_header()
497+ searchTextfield = header.wait_select_single(
498+ "TextField", objectName='searchField')
499+ searchTextfield.visible.wait_for(True)
500+ searchTextfield.write(city_Name)
501+
502+
503+class EditAlarmPage(Page):
504+ """Autopilot helper for the Add Alarm page."""
505+
506+ @autopilot_logging.log_action(logger.info)
507+ def set_alarm_time(self, time_to_set):
508+ """Set alarm time on datepicker.
509+
510+ :param time_to_set: time to set on datepicker
511+
512+ """
513+ PickerRow_HoursPicker = self.wait_select_single(
514+ "Picker", objectName="PickerRow_HoursPicker")
515+ self._set_picker(PickerRow_HoursPicker, 'time', time_to_set)
516+
517+ def _set_picker(self, field, mode, value):
518+ # open picker
519+ self.pointing_device.click_object(field)
520+ # valid options are date or time; assume date if invalid/no option
521+ mode_value = 'Hours|Minutes'
522+ picker = self.wait_select_single(
523+ pickers.DatePicker, mode=mode_value, visible=True)
524+ picker.pick_time(value)
525+ # close picker
526+ self.pointing_device.click_object(field)
527+
528+ @autopilot_logging.log_action(logger.info)
529+ def open_alarmRepeat_page(self):
530+ """ Open the alarmRepeat page """
531+
532+ alarmRepeatItem = self.wait_select_single(
533+ "SubtitledListItem", objectName="alarmRepeat")
534+ self.pointing_device.click_object(alarmRepeatItem)
535+ return self.main_view.wait_select_single(AlarmRepeat)
536+
537+ @autopilot_logging.log_action(logger.info)
538+ def open_alarmLabel_page(self):
539+ """ Open the alarmLabel page """
540+
541+ alarmLabelItem = self.wait_select_single(
542+ "SubtitledListItem", objectName="alarmLabel")
543+ self.pointing_device.click_object(alarmLabelItem)
544+ return AlarmLable.select(self.main_view)
545+
546+ @autopilot_logging.log_action(logger.info)
547+ def open_alarmSound_page(self):
548+ """ Open the alarmSound page """
549+
550+ alarmSoundItem = self.wait_select_single(
551+ "SubtitledListItem", objectName="alarmSound")
552+ self.pointing_device.click_object(alarmSoundItem)
553+ return self.main_view.wait_select_single(AlarmSound)
554+
555+ def _check_sound_changed(self, test_sound_name):
556+ """ function to check that sound has changed.
557+
558+ :param test_sound_name = new sound name
559+
560+ """
561+ try:
562+ self.wait_select_single(
563+ "SubtitledListItem", objectName="alarmSound").subText.wait_for(
564+ test_sound_name)
565+ except AssertionError:
566+ raise ClockEmulatorException('Error! Incorrect alarm sound')
567+
568+
569+class AlarmRepeat(Page):
570+ """Autopilot helper for the AlarmRepeat page."""
571+
572+ @autopilot_logging.log_action(logger.info)
573+ def set_alarm_days(self, days):
574+ """Set the alarm days of the alarm.
575+
576+ :param days: days on which alarm is triggered
577+
578+ """
579+ self.unselect_selected_days()
580+ index = 0
581+ for index in range(len(days)):
582+ for index2 in range(self._get_num_of_days()):
583+ if self.wait_select_single(
584+ 'Label', objectName='alarmDay{}'.format(index2)).text\
585+ == days[index]:
586+ self._select_single_alarm_day(index2)
587+ break
588+
589+ def _get_num_of_days(self):
590+ return int(self.wait_select_single(
591+ 'QQuickRepeater', objectName='alarmDays').count)
592+
593+ def _select_single_alarm_day(self, index):
594+ """ function for selecting the day passed to the function.
595+
596+ :param index: the day to be selected
597+
598+ """
599+ dayCheckbox = self.wait_select_single(
600+ 'CheckBox', objectName='daySwitch{}'.format(index))
601+ dayCheckbox.check()
602+
603+ @autopilot_logging.log_action(logger.info)
604+ def unselect_selected_days(self):
605+ """ function for unselecting already selected days. """
606+ for index in range(self._get_num_of_days()):
607+ dayCheckbox = self.wait_select_single(
608+ 'CheckBox', objectName='daySwitch{}'.format(index))
609+ dayCheckbox.uncheck()
610+
611+
612+class AlarmSound(Page):
613+ """Autopilot helper for the AlarmSound page."""
614+
615+ @autopilot_logging.log_action(logger.info)
616+ def set_alarm_sound(self, test_sound_name):
617+ """Set alarm sound.
618+
619+ :param test_sound_name: sound to set for alarm
620+
621+ """
622+ for index in range(self._get_num_of_sounds()):
623+ if self.wait_select_single(
624+ 'Label', objectName='soundName{}'.format(index)).\
625+ text == test_sound_name:
626+ self._select_alarm_sound(index)
627+ break
628+
629+ def _get_num_of_sounds(self):
630+ return int(self.wait_select_single(
631+ 'QQuickRepeater', objectName='alarmSounds').count)
632+
633+ def _select_alarm_sound(self, index):
634+ """ function for selecting the sound passed to the function.
635+
636+ :param index: the sound to be selected
637+
638+ """
639+ soundCheckbox = self.wait_select_single(
640+ 'CheckBox', objectName='soundStatus{}'.format(index))
641+ soundCheckbox.check()
642+
643+
644+class AlarmLable(object):
645+ """Autopilot helper for the AlarmLabel page."""
646+
647+ def __init__(self, proxy_object):
648+ super(AlarmLable, self).__init__()
649+ self.proxy_object = proxy_object
650+
651+ @classmethod
652+ def select(cls, main_view):
653+ proxy_object = main_view.wait_select_single(
654+ objectName='alarmLabelPage')
655+ proxy_object.visible.wait_for(True)
656+ return cls(proxy_object)
657+
658+ @autopilot_logging.log_action(logger.info)
659+ def set_alarm_label(self, name):
660+ """Set alarm label.
661+
662+ :param name: label for alarm to set
663+
664+ """
665+ alarmTextfield = self.proxy_object.wait_select_single(
666+ "TextField", objectName='labelEntry')
667+ # TODO: This wait to ensure that the textfield is visible before
668+ # entering text should be part of the SDK emulator. Until then, it has
669+ # been added here. http://pad.lv/1289616 --nik90 2014-03-06
670+ alarmTextfield.visible.wait_for(True)
671+ alarmTextfield.write(name)
672+
673+
674+class AlarmList(object):
675+ """Autopilot helper for the AlarmList."""
676+
677+ def __init__(self, proxy_object):
678+ super(AlarmList, self).__init__()
679+ self.proxy_object = proxy_object
680+
681+ @classmethod
682+ def select(cls, main_view):
683+ proxy_object = main_view.wait_select_single(
684+ 'AlarmList', objectName='alarmListView')
685+ proxy_object.visible.wait_for(True)
686+ return cls(proxy_object)
687+
688+ def get_num_of_alarms(self):
689+ """Return the number of saved alarms."""
690+ return int(self._get_saved_alarms_list().count)
691+
692+ def _get_saved_alarms_list(self):
693+ """Return the saved alarm list"""
694+ return self.proxy_object
695+
696+ def get_saved_alarms(self):
697+ """Return a list with the information of the saved alarms.
698+
699+ Each item of the returned list is a tuple of
700+ (name, recurrence, time, enabled).
701+
702+ """
703+ alarms = []
704+ for index in range(self.get_num_of_alarms()):
705+ name = self.proxy_object.wait_select_single(
706+ 'Label', objectName='listAlarmLabel{}'.format(index)).text
707+ recurrence = self.proxy_object.wait_select_single(
708+ 'Label', objectName='listAlarmSubtitle{}'.format(index)).text
709+ time = self.proxy_object.wait_select_single(
710+ 'Label', objectName='listAlarmTime{}'.format(index)).text
711+ enabled = self.proxy_object.wait_select_single(
712+ ubuntuuitoolkit.CheckBox,
713+ objectName='listAlarmStatus{}'.format(index)).checked
714+ alarms.append((name, recurrence, enabled, time))
715+ return alarms
716+
717+ @autopilot_logging.log_action(logger.info)
718+ def delete_alarm(self, index):
719+ """Delete an alarm at the specified index."""
720+ old_alarm_count = self.get_num_of_alarms()
721+ alarm = self.proxy_object.wait_select_single(
722+ objectName='alarm{}'.format(index))
723+
724+ alarm.swipe_to_delete()
725+ alarm.confirm_removal()
726+ try:
727+ self._get_saved_alarms_list().count.wait_for(old_alarm_count - 1)
728+ except AssertionError:
729+ raise ClockEmulatorException('Error deleting alarm.')
730+
731+
732+class ListItemWithActions(
733+ ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
734+
735+ def swipe_to_delete(self):
736+ x, y, width, height = self.globalRect
737+ start_x = x + (width * 0.2)
738+ stop_x = x + (width * 0.8)
739+ start_y = stop_y = y + (height // 2)
740+
741+ self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
742+
743+ def confirm_removal(self):
744+ deleteButton = self.wait_select_single(name='delete')
745+ self.pointing_device.click_object(deleteButton)
746+
747+
748+class AlarmDelegate(ListItemWithActions):
749+
750+ def __init__(self, *args):
751+ super(AlarmDelegate, self).__init__(*args)
752
753=== removed file 'tests/autopilot/ubuntu_clock_app/emulators.py'
754--- tests/autopilot/ubuntu_clock_app/emulators.py 2014-11-25 15:17:21 +0000
755+++ tests/autopilot/ubuntu_clock_app/emulators.py 1970-01-01 00:00:00 +0000
756@@ -1,546 +0,0 @@
757-# -#- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -#-
758-#
759-# Copyright (C) 2014 Canonical Ltd
760-#
761-# This file is part of Ubuntu Clock App
762-#
763-# Ubuntu Clock App is free software: you can redistribute it and/or modify
764-# it under the terms of the GNU General Public License version 3 as
765-# published by the Free Software Foundation.
766-#
767-# Ubuntu Clock App is distributed in the hope that it will be useful,
768-# but WITHOUT ANY WARRANTY; without even the implied warranty of
769-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
770-# GNU General Public License for more details.
771-#
772-# You should have received a copy of the GNU General Public License
773-# along with this program. If not, see <http://www.gnu.org/licenses/>.
774-
775-import logging
776-
777-from autopilot import logging as autopilot_logging
778-from autopilot.introspection import dbus
779-from testtools.matchers import GreaterThan
780-
781-from ubuntuuitoolkit import pickers
782-import ubuntuuitoolkit
783-
784-logger = logging.getLogger(__name__)
785-
786-
787-class ClockEmulatorException(ubuntuuitoolkit.ToolkitException):
788-
789- """Exception raised when there is an error with the emulator."""
790-
791-
792-class MainView(ubuntuuitoolkit.MainView):
793-
794- @autopilot_logging.log_action(logger.info)
795- def open_clock(self):
796- """Open the Clock Page.
797-
798- :return the Clock Page
799-
800- """
801- return self.wait_select_single(ClockPage)
802-
803- @autopilot_logging.log_action(logger.info)
804- def open_alarm(self):
805- """Open the Alarm Page.
806-
807- :return: the Alarm Page.
808-
809- """
810- clockPage = self.open_clock()
811- clockPage.reveal_bottom_edge_page()
812- self.get_header().visible.wait_for(True)
813- return self.wait_select_single(Page11)
814-
815- def get_AlarmList(self):
816- """ Get the AlarmList object. """
817- return AlarmList.select(self)
818-
819- @autopilot_logging.log_action(logger.info)
820- def get_worldCityList(self):
821- """ Return the World City List page """
822- return self.wait_select_single("WorldCityList",
823- objectName="worldCityList")
824-
825-
826-class Page(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
827-
828- """Autopilot helper for Pages."""
829-
830- def __init__(self, *args):
831- super(Page, self).__init__(*args)
832- # XXX we need a better way to keep reference to the main view.
833- # --elopio - 2014-01-31
834- self.main_view = self.get_root_instance().select_single(MainView)
835-
836-
837-class PageWithBottomEdge(MainView):
838- """
839- An emulator class that makes it easy to interact with the bottom edge
840- swipe page
841- """
842- def __init__(self, *args):
843- super(PageWithBottomEdge, self).__init__(*args)
844-
845- def reveal_bottom_edge_page(self):
846- """Bring the bottom edge page to the screen"""
847- self.bottomEdgePageLoaded.wait_for(True)
848- try:
849- action_item = self.wait_select_single(objectName='bottomEdgeTip')
850- action_item.visible.wait_for(True)
851- action_item.isAnimating.wait_for(False)
852- start_x = (action_item.globalRect.x +
853- (action_item.globalRect.width * 0.5))
854- start_y = (action_item.globalRect.y +
855- (action_item.height * 0.5))
856- stop_y = start_y - (self.height * 0.7)
857- self.pointing_device.drag(start_x, start_y,
858- start_x, stop_y, rate=2)
859- self.isReady.wait_for(True)
860- except dbus.StateNotFoundError:
861- logger.error('BottomEdge element not found.')
862- raise
863-
864-
865-class ClockPage(PageWithBottomEdge):
866- """Autopilot helper for the Clock page."""
867-
868- @autopilot_logging.log_action(logger.info)
869- def click_addCity_to_open_worldCityList(self):
870- """Swipe to reveal WorldCityList"""
871-
872- addWorldCityButton = self.wait_select_single(
873- "AbstractButton", objectName="addWorldCityButton")
874- self.pointing_device.click_object(addWorldCityButton)
875-
876- def get_num_of_saved_cities(self):
877- """Return the number of saved world cities"""
878- return int(self._get_saved_world_city_list().count)
879-
880- def _get_saved_world_city_list(self):
881- """Return the saved world city listview object"""
882- return self.wait_select_single(
883- "QQuickRepeater", objectName='userWorldCityRepeater')
884-
885- @autopilot_logging.log_action(logger.info)
886- def delete_added_world_city(self, city_Name, country_Name):
887- """ Delete added world city from user world city list """
888- old_cities_count = self.get_num_of_saved_cities()
889- index = 0
890- for index in range(old_cities_count):
891- world_city_item = self.wait_select_single(
892- objectName='userWorldCityItem{}'.format(index))
893- city_name_label = world_city_item.wait_select_single(
894- 'Label', objectName='userCityNameText')
895- country_name_label = world_city_item.wait_select_single(
896- 'Label', objectName='userCountryNameText')
897- if (city_name_label.text == city_Name and
898- country_name_label.text == country_Name):
899- self._delete_userWorldCityItem(index)
900-
901- # FIXME -----------------------------------------------------------------
902- # Commenting the following lines as deleting a world city when there is
903- # only one in the user world city list does not decrease counter to 0 but
904- # leaves it at 1 so the test fails
905- # Reported bug #1368393
906- # Discovered that deleting world city clock deletes the city from the clock
907- # app, but if you look with autopilot vis the world city is still there
908- # added a comment to bug #1368393
909- #
910- # try:
911- # self._get_saved_world_city_list().count.wait_for(
912- # old_cities_count - 1)
913- # except AssertionError:
914- # raise ClockEmulatorException('Error deleting city.')
915- # -------------------------------------------------------------------------
916-
917- def _delete_userWorldCityItem(self, index):
918- cityItem = self.wait_select_single(
919- objectName='userWorldCityItem{}'.format(index))
920- self._swipe_to_delete(cityItem)
921- self._confirm_removal(cityItem)
922-
923- def _swipe_to_delete(self, cityItem):
924- x, y, width, height = cityItem.globalRect
925- start_x = x + (width * 0.2)
926- stop_x = x + (width * 0.8)
927- start_y = stop_y = y + (height // 2)
928-
929- self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
930-
931- def _confirm_removal(self, cityItem):
932- deleteButton = cityItem.wait_select_single(name='delete')
933- self.pointing_device.click_object(deleteButton)
934-
935-
936-class Page11(Page):
937- """Autopilot helper for the Alarm page."""
938-
939- @autopilot_logging.log_action(logger.info)
940- def add_single_alarm(self, name, days, time_to_set, test_sound_name):
941- """Add a single type alarm
942-
943- :param name: name of alarm
944- :param days: days on which the alarm should be triggered
945- :param time_to_set: time to set alarm to
946- :param test_sound_name: sound to set in alarm
947-
948- """
949- alarmListPage = AlarmList.select(self.main_view)
950- old_alarm_count = alarmListPage.get_num_of_alarms()
951-
952- edit_alarm_page = self._click_add_alarm_button()
953- edit_alarm_page.set_alarm_time(time_to_set)
954-
955- alarm_repeat_page = edit_alarm_page.open_alarmRepeat_page()
956- alarm_repeat_page.set_alarm_days(days)
957- self._click_header_backButton()
958-
959- alarm_label_page = edit_alarm_page.open_alarmLabel_page()
960- alarm_label_page.set_alarm_label(name)
961- self._click_header_customBackButton()
962-
963- alarm_sound_page = edit_alarm_page.open_alarmSound_page()
964- alarm_sound_page.set_alarm_sound(test_sound_name)
965- self._click_header_customBackButton()
966- edit_alarm_page._check_sound_changed(test_sound_name)
967-
968- self._click_save()
969- self._confirm_alarm_creation(old_alarm_count)
970-
971- def _click_add_alarm_button(self):
972- """Click the add alarm header button."""
973- header = self.main_view.get_header()
974- header.click_action_button('addAlarmAction')
975- return self.main_view.wait_select_single(EditAlarmPage)
976-
977- def _click_header_customBackButton(self):
978- """Click the header button: 'customBackButton' """
979- header = self.main_view.get_header()
980- header.click_custom_back_button()
981-
982- def _click_header_backButton(self):
983- """Click the header button: 'backButton' """
984- header = self.main_view.get_header()
985- header.click_back_button()
986-
987- def _click_save(self):
988- """Click the save timer header button"""
989- header = self.main_view.get_header()
990- header.click_action_button('saveAlarmAction')
991-
992- def _confirm_alarm_creation(self, count):
993- """Confirm creation of alarm
994-
995- :param count: alarm count before alarm creation
996-
997- """
998- try:
999- AlarmList.select(self.main_view)._get_saved_alarms_list().\
1000- count.wait_for(count + 1)
1001- except AssertionError:
1002- raise ClockEmulatorException('Error creating alarm.')
1003-
1004-
1005-class WorldCityList(Page):
1006- """Autopilot helper for World City List page."""
1007-
1008- @autopilot_logging.log_action(logger.info)
1009- def add_world_city_from_list(self, city_Name, country_Name):
1010- """Add world city from list
1011-
1012- :param city_Name: world city name to add
1013- :param country_Name: country city name belongs to (same city name could
1014- be found in more countries)
1015- """
1016- self.wait_select_single("ActivityIndicator").running.wait_for(False)
1017- cityList = self.wait_select_single("QQuickListView",
1018- objectName="cityList")
1019-
1020- cityList.count.wait_for(GreaterThan(0))
1021-
1022- for index in range(int(cityList.count)):
1023- world_city_item = self.wait_select_single(
1024- objectName='defaultWorldCityItem{}'.format(index))
1025- city_name_label = world_city_item.wait_select_single(
1026- 'Label', objectName='defaultCityNameText')
1027- country_name_label = world_city_item.wait_select_single(
1028- 'Label', objectName='defaultCountryNameText')
1029- if (city_name_label.text == city_Name and
1030- country_name_label.text == country_Name):
1031- cityList.click_element(
1032- 'defaultWorldCityItem{}'.format(index), direction=None)
1033- break
1034-
1035- @autopilot_logging.log_action(logger.info)
1036- def search_world_city_(self, city_Name, country_Name):
1037- """Add world city by searching the world city name
1038-
1039- :param city_Name: world city name to add
1040-
1041- """
1042- header = self.main_view.get_header()
1043- header.click_action_button("searchButton")
1044- self._search_world_city(city_Name, country_Name)
1045-
1046- def _search_world_city(self, city_Name, country_Name):
1047- header = self.main_view.get_header()
1048- searchTextfield = header.wait_select_single(
1049- "TextField", objectName='searchField')
1050- searchTextfield.visible.wait_for(True)
1051- searchTextfield.write(city_Name)
1052-
1053-
1054-class EditAlarmPage(Page):
1055- """Autopilot helper for the Add Alarm page."""
1056-
1057- @autopilot_logging.log_action(logger.info)
1058- def set_alarm_time(self, time_to_set):
1059- """Set alarm time on datepicker.
1060-
1061- :param time_to_set: time to set on datepicker
1062-
1063- """
1064- PickerRow_HoursPicker = self.wait_select_single(
1065- "Picker", objectName="PickerRow_HoursPicker")
1066- self._set_picker(PickerRow_HoursPicker, 'time', time_to_set)
1067-
1068- def _set_picker(self, field, mode, value):
1069- # open picker
1070- self.pointing_device.click_object(field)
1071- # valid options are date or time; assume date if invalid/no option
1072- mode_value = 'Hours|Minutes'
1073- picker = self.wait_select_single(
1074- pickers.DatePicker, mode=mode_value, visible=True)
1075- picker.pick_time(value)
1076- # close picker
1077- self.pointing_device.click_object(field)
1078-
1079- @autopilot_logging.log_action(logger.info)
1080- def open_alarmRepeat_page(self):
1081- """ Open the alarmRepeat page """
1082-
1083- alarmRepeatItem = self.wait_select_single(
1084- "SubtitledListItem", objectName="alarmRepeat")
1085- self.pointing_device.click_object(alarmRepeatItem)
1086- return self.main_view.wait_select_single(AlarmRepeat)
1087-
1088- @autopilot_logging.log_action(logger.info)
1089- def open_alarmLabel_page(self):
1090- """ Open the alarmLabel page """
1091-
1092- alarmLabelItem = self.wait_select_single(
1093- "SubtitledListItem", objectName="alarmLabel")
1094- self.pointing_device.click_object(alarmLabelItem)
1095- return AlarmLable.select(self.main_view)
1096-
1097- @autopilot_logging.log_action(logger.info)
1098- def open_alarmSound_page(self):
1099- """ Open the alarmSound page """
1100-
1101- alarmSoundItem = self.wait_select_single(
1102- "SubtitledListItem", objectName="alarmSound")
1103- self.pointing_device.click_object(alarmSoundItem)
1104- return self.main_view.wait_select_single(AlarmSound)
1105-
1106- def _check_sound_changed(self, test_sound_name):
1107- """ function to check that sound has changed.
1108-
1109- :param test_sound_name = new sound name
1110-
1111- """
1112- try:
1113- self.wait_select_single(
1114- "SubtitledListItem", objectName="alarmSound").subText.wait_for(
1115- test_sound_name)
1116- except AssertionError:
1117- raise ClockEmulatorException('Error! Incorrect alarm sound')
1118-
1119-
1120-class AlarmRepeat(Page):
1121- """Autopilot helper for the AlarmRepeat page."""
1122-
1123- @autopilot_logging.log_action(logger.info)
1124- def set_alarm_days(self, days):
1125- """Set the alarm days of the alarm.
1126-
1127- :param days: days on which alarm is triggered
1128-
1129- """
1130- self.unselect_selected_days()
1131- index = 0
1132- for index in range(len(days)):
1133- for index2 in range(self._get_num_of_days()):
1134- if self.wait_select_single(
1135- 'Label', objectName='alarmDay{}'.format(index2)).text\
1136- == days[index]:
1137- self._select_single_alarm_day(index2)
1138- break
1139-
1140- def _get_num_of_days(self):
1141- return int(self.wait_select_single(
1142- 'QQuickRepeater', objectName='alarmDays').count)
1143-
1144- def _select_single_alarm_day(self, index):
1145- """ function for selecting the day passed to the function.
1146-
1147- :param index: the day to be selected
1148-
1149- """
1150- dayCheckbox = self.wait_select_single(
1151- 'CheckBox', objectName='daySwitch{}'.format(index))
1152- dayCheckbox.check()
1153-
1154- @autopilot_logging.log_action(logger.info)
1155- def unselect_selected_days(self):
1156- """ function for unselecting already selected days. """
1157- for index in range(self._get_num_of_days()):
1158- dayCheckbox = self.wait_select_single(
1159- 'CheckBox', objectName='daySwitch{}'.format(index))
1160- dayCheckbox.uncheck()
1161-
1162-
1163-class AlarmSound(Page):
1164- """Autopilot helper for the AlarmSound page."""
1165-
1166- @autopilot_logging.log_action(logger.info)
1167- def set_alarm_sound(self, test_sound_name):
1168- """Set alarm sound.
1169-
1170- :param test_sound_name: sound to set for alarm
1171-
1172- """
1173- for index in range(self._get_num_of_sounds()):
1174- if self.wait_select_single(
1175- 'Label', objectName='soundName{}'.format(index)).\
1176- text == test_sound_name:
1177- self._select_alarm_sound(index)
1178- break
1179-
1180- def _get_num_of_sounds(self):
1181- return int(self.wait_select_single(
1182- 'QQuickRepeater', objectName='alarmSounds').count)
1183-
1184- def _select_alarm_sound(self, index):
1185- """ function for selecting the sound passed to the function.
1186-
1187- :param index: the sound to be selected
1188-
1189- """
1190- soundCheckbox = self.wait_select_single(
1191- 'CheckBox', objectName='soundStatus{}'.format(index))
1192- soundCheckbox.check()
1193-
1194-
1195-class AlarmLable(object):
1196- """Autopilot helper for the AlarmLabel page."""
1197-
1198- def __init__(self, proxy_object):
1199- super(AlarmLable, self).__init__()
1200- self.proxy_object = proxy_object
1201-
1202- @classmethod
1203- def select(cls, main_view):
1204- proxy_object = main_view.wait_select_single(
1205- objectName='alarmLabelPage')
1206- proxy_object.visible.wait_for(True)
1207- return cls(proxy_object)
1208-
1209- @autopilot_logging.log_action(logger.info)
1210- def set_alarm_label(self, name):
1211- """Set alarm label.
1212-
1213- :param name: label for alarm to set
1214-
1215- """
1216- alarmTextfield = self.proxy_object.wait_select_single(
1217- "TextField", objectName='labelEntry')
1218- # TODO: This wait to ensure that the textfield is visible before
1219- # entering text should be part of the SDK emulator. Until then, it has
1220- # been added here. http://pad.lv/1289616 --nik90 2014-03-06
1221- alarmTextfield.visible.wait_for(True)
1222- alarmTextfield.write(name)
1223-
1224-
1225-class AlarmList(object):
1226- """Autopilot helper for the AlarmList."""
1227-
1228- def __init__(self, proxy_object):
1229- super(AlarmList, self).__init__()
1230- self.proxy_object = proxy_object
1231-
1232- @classmethod
1233- def select(cls, main_view):
1234- proxy_object = main_view.wait_select_single(
1235- 'AlarmList', objectName='alarmListView')
1236- proxy_object.visible.wait_for(True)
1237- return cls(proxy_object)
1238-
1239- def get_num_of_alarms(self):
1240- """Return the number of saved alarms."""
1241- return int(self._get_saved_alarms_list().count)
1242-
1243- def _get_saved_alarms_list(self):
1244- """Return the saved alarm list"""
1245- return self.proxy_object
1246-
1247- def get_saved_alarms(self):
1248- """Return a list with the information of the saved alarms.
1249-
1250- Each item of the returned list is a tuple of
1251- (name, recurrence, time, enabled).
1252-
1253- """
1254- alarms = []
1255- for index in range(self.get_num_of_alarms()):
1256- name = self.proxy_object.wait_select_single(
1257- 'Label', objectName='listAlarmLabel{}'.format(index)).text
1258- recurrence = self.proxy_object.wait_select_single(
1259- 'Label', objectName='listAlarmSubtitle{}'.format(index)).text
1260- time = self.proxy_object.wait_select_single(
1261- 'Label', objectName='listAlarmTime{}'.format(index)).text
1262- enabled = self.proxy_object.wait_select_single(
1263- ubuntuuitoolkit.CheckBox,
1264- objectName='listAlarmStatus{}'.format(index)).checked
1265- alarms.append((name, recurrence, enabled, time))
1266- return alarms
1267-
1268- @autopilot_logging.log_action(logger.info)
1269- def delete_alarm(self, index):
1270- """Delete an alarm at the specified index."""
1271- old_alarm_count = self.get_num_of_alarms()
1272- alarm = self.proxy_object.wait_select_single(
1273- objectName='alarm{}'.format(index))
1274-
1275- alarm.swipe_to_delete()
1276- alarm.confirm_removal()
1277- try:
1278- self._get_saved_alarms_list().count.wait_for(old_alarm_count - 1)
1279- except AssertionError:
1280- raise ClockEmulatorException('Error deleting alarm.')
1281-
1282-
1283-class ListItemWithActions(
1284- ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
1285-
1286- def swipe_to_delete(self):
1287- x, y, width, height = self.globalRect
1288- start_x = x + (width * 0.2)
1289- stop_x = x + (width * 0.8)
1290- start_y = stop_y = y + (height // 2)
1291-
1292- self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
1293-
1294- def confirm_removal(self):
1295- deleteButton = self.wait_select_single(name='delete')
1296- self.pointing_device.click_object(deleteButton)
1297-
1298-
1299-class AlarmDelegate(ListItemWithActions):
1300-
1301- def __init__(self, *args):
1302- super(AlarmDelegate, self).__init__(*args)
1303
1304=== modified file 'tests/autopilot/ubuntu_clock_app/tests/__init__.py'
1305--- tests/autopilot/ubuntu_clock_app/tests/__init__.py 2014-11-27 09:12:00 +0000
1306+++ tests/autopilot/ubuntu_clock_app/tests/__init__.py 2015-06-18 22:50:34 +0000
1307@@ -18,56 +18,68 @@
1308
1309 import os.path
1310 import os
1311+import sys
1312 import shutil
1313 import glob
1314 import logging
1315 import fixtures
1316
1317 from autopilot import logging as autopilot_logging
1318-from ubuntuuitoolkit import (
1319- base,
1320- emulators as toolkit_emulators
1321-)
1322+from autopilot.testcase import AutopilotTestCase
1323+import ubuntuuitoolkit
1324+from ubuntuuitoolkit import base
1325
1326-from ubuntu_clock_app import emulators, fixture_setup
1327+import ubuntu_clock_app
1328+from ubuntu_clock_app import fixture_setup, CMakePluginParser
1329
1330 logger = logging.getLogger(__name__)
1331
1332
1333-class ClockAppTestCase(base.UbuntuUIToolkitAppTestCase):
1334+class ClockAppTestCase(AutopilotTestCase):
1335
1336 """A common test case class that provides several useful methods for
1337 clock-app tests.
1338
1339 """
1340
1341- local_location = os.path.dirname(os.path.dirname(os.getcwd()))
1342- local_location_qml = os.path.join(local_location,
1343- 'app/ubuntu-clock-app.qml')
1344- local_location_backend = os.path.join(local_location, 'builddir/backend')
1345- installed_location_backend = ""
1346- if glob.glob('/usr/lib/*/qt5/qml/ClockApp'):
1347- installed_location_backend = \
1348- glob.glob('/usr/lib/*/qt5/qml/ClockApp')[0]
1349- installed_location_qml = \
1350- '/usr/share/ubuntu-clock-app/ubuntu-clock-app.qml'
1351-
1352- sqlite_dir = os.path.expanduser(
1353- "~/.local/share/com.ubuntu.clock")
1354- backup_dir = sqlite_dir + ".backup"
1355-
1356 def setUp(self):
1357+ # setup paths
1358+ self.binary = 'ubuntu-clock-app'
1359+ self.source_dir = os.path.dirname(
1360+ os.path.dirname(os.path.abspath('.')))
1361+ self.build_dir = self._get_build_dir()
1362+
1363+ self.local_location = self.source_dir
1364+ self.local_location_qml = os.path.join(self.source_dir,
1365+ 'app', self.binary + '.qml')
1366+
1367+ self.local_location_backend = os.path.join(self.build_dir, 'backend')
1368+
1369+ self.installed_location_backend = ""
1370+ if glob.glob('/usr/lib/*/qt5/qml/ClockApp'):
1371+ self.installed_location_backend = \
1372+ glob.glob('/usr/lib/*/qt5/qml/ClockApp')[0]
1373+ self.installed_location_qml = \
1374+ '/usr/share/ubuntu-clock-app/ubuntu-clock-app.qml'
1375+
1376+ self.sqlite_dir = os.path.expanduser(
1377+ "~/.local/share/com.ubuntu.clock")
1378+ self.backup_dir = self.sqlite_dir + ".backup"
1379+
1380 # backup and wipe db's before testing
1381 self.temp_move_sqlite_db()
1382 self.addCleanup(self.restore_sqlite_db)
1383+
1384+ # setup fixtures and launcher
1385 self.useFixture(fixture_setup.LocationServiceTestEnvironment())
1386- launch, self.test_type = self.get_launcher_method_and_type()
1387 self.useFixture(fixtures.EnvironmentVariable('LC_ALL', newvalue='C'))
1388+ self.launcher, self.test_type = self.get_launcher_and_type()
1389+
1390+ # launch application under introspection
1391 super(ClockAppTestCase, self).setUp()
1392-
1393- launch()
1394-
1395- def get_launcher_method_and_type(self):
1396+ self.app = ubuntu_clock_app.ClockApp(self.launcher(), self.test_type)
1397+
1398+ def get_launcher_and_type(self):
1399 if os.path.exists(self.local_location_backend):
1400 launcher = self.launch_test_local
1401 test_type = 'local'
1402@@ -81,27 +93,27 @@
1403
1404 @autopilot_logging.log_action(logger.info)
1405 def launch_test_local(self):
1406- self.app = self.launch_test_application(
1407+ return self.launch_test_application(
1408 base.get_qmlscene_launch_command(),
1409 self.local_location_qml,
1410 "-I", self.local_location_backend,
1411 app_type='qt',
1412- emulator_base=toolkit_emulators.UbuntuUIToolkitEmulatorBase)
1413+ emulator_base=ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase)
1414
1415 @autopilot_logging.log_action(logger.info)
1416 def launch_test_installed(self):
1417- self.app = self.launch_test_application(
1418+ return self.launch_test_application(
1419 base.get_qmlscene_launch_command(),
1420 self.installed_location_qml,
1421 "-I", self.installed_location_backend,
1422 app_type='qt',
1423- emulator_base=toolkit_emulators.UbuntuUIToolkitEmulatorBase)
1424+ emulator_base=ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase)
1425
1426 @autopilot_logging.log_action(logger.info)
1427 def launch_test_click(self):
1428- self.app = self.launch_click_package(
1429+ return self.launch_click_package(
1430 "com.ubuntu.clock",
1431- emulator_base=toolkit_emulators.UbuntuUIToolkitEmulatorBase)
1432+ emulator_base=ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase)
1433
1434 def temp_move_sqlite_db(self):
1435 try:
1436@@ -132,6 +144,19 @@
1437 except:
1438 logger.error("Failed to restore database")
1439
1440- @property
1441- def main_view(self):
1442- return self.app.wait_select_single(emulators.MainView)
1443+ def _get_build_dir(self):
1444+ """
1445+ Returns the build dir after having parsed the CMake config file
1446+ generated by Qt Creator. If it cannot find it or it cannot be parsed,
1447+ an in-tree build is assumed and thus returned.
1448+ """
1449+ try:
1450+ cmake_config = CMakePluginParser.CMakePluginParser(os.path.join(
1451+ self.source_dir, 'CMakeLists.txt.user'))
1452+ build_dir = cmake_config.active_build_dir
1453+ except:
1454+ logger.error("Error parsing CMakeLists.txt.user %s",
1455+ sys.exc_info()[0])
1456+ build_dir = os.path.join(self.source_dir, 'builddir')
1457+
1458+ return build_dir
1459
1460=== modified file 'tests/autopilot/ubuntu_clock_app/tests/test_alarm.py'
1461--- tests/autopilot/ubuntu_clock_app/tests/test_alarm.py 2015-03-19 23:58:06 +0000
1462+++ tests/autopilot/ubuntu_clock_app/tests/test_alarm.py 2015-06-18 22:50:34 +0000
1463@@ -65,9 +65,9 @@
1464 """
1465 super(TestAlarm, self).setUp()
1466 self.assertThat(
1467- self.main_view.visible, Eventually(Equals(True)))
1468+ self.app.main_view.visible, Eventually(Equals(True)))
1469
1470- self.page = self.main_view.open_alarm()
1471+ self.page = self.app.main_view.open_alarm()
1472
1473 def test_add_recurring_type_alarm_must_add_to_alarm_list(self):
1474 """Test to check if alarms are saved properly
1475@@ -84,7 +84,7 @@
1476 self.page.add_single_alarm(
1477 self.alarm_name, self.days, time_to_set, self.test_sound_name)
1478
1479- alarmlistPage = self.main_view.get_AlarmList()
1480+ alarmlistPage = self.app.main_view.get_AlarmList()
1481 saved_alarms = alarmlistPage.get_saved_alarms()
1482 self.assertIn(expected_alarm_info, saved_alarms)
1483
1484
1485=== modified file 'tests/autopilot/ubuntu_clock_app/tests/test_clock.py'
1486--- tests/autopilot/ubuntu_clock_app/tests/test_clock.py 2014-09-12 19:44:18 +0000
1487+++ tests/autopilot/ubuntu_clock_app/tests/test_clock.py 2015-06-18 22:50:34 +0000
1488@@ -39,9 +39,9 @@
1489
1490 super(TestClock, self).setUp()
1491 self.assertThat(
1492- self.main_view.visible, Eventually(Equals(True)))
1493+ self.app.main_view.visible, Eventually(Equals(True)))
1494
1495- self.page = self.main_view.open_clock()
1496+ self.page = self.app.main_view.open_clock()
1497
1498 def test_add_city_from_list_must_add_world_city(self):
1499 """Test if adding a world city chosing it from the list works"""
1500@@ -52,7 +52,7 @@
1501 old_city_count = self.page.get_num_of_saved_cities()
1502
1503 self.page.click_addCity_to_open_worldCityList()
1504- worldCityList = self.main_view.get_worldCityList()
1505+ worldCityList = self.app.main_view.get_worldCityList()
1506 worldCityList.add_world_city_from_list(city_Name, country_Name)
1507
1508 # Confirm that the city has been added
1509@@ -72,7 +72,7 @@
1510 old_city_count = self.page.get_num_of_saved_cities()
1511
1512 self.page.click_addCity_to_open_worldCityList()
1513- worldCityList = self.main_view.get_worldCityList()
1514+ worldCityList = self.app.main_view.get_worldCityList()
1515 worldCityList.search_world_city_(city_Name, country_Name)
1516 worldCityList.add_world_city_from_list(city_Name, country_Name)
1517

Subscribers

People subscribed via source and target branches