Merge lp:~nskaggs/ubuntu-clock-app/update-test-layout into lp:ubuntu-clock-app
- update-test-layout
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Nekhelesh Ramananthan | Approve | ||
Ubuntu Phone Apps Jenkins Bot | continuous-integration | Approve | |
Review via email:
|
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.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
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://
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?
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Nekhelesh Ramananthan (nik90) wrote : | # |
Can you update the debian changelog pls?
- 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
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:285
http://
Executed test runs:
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:287
http://
Executed test runs:
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
- 288. By Nicholas Skaggs
-
fix pathing for launch
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
FAILED: Continuous integration, rev:288
http://
Executed test runs:
FAILURE: http://
Click here to trigger a rebuild:
http://
- 289. By Nicholas Skaggs
-
fix cmake parser defaults
- 290. By Nicholas Skaggs
-
flake8
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:290
http://
Executed test runs:
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
- 291. By Nicholas Skaggs
-
force builddir
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:291
http://
Executed test runs:
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
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://
This is fine by me. If this accomplishes what you expected, feel free to top-approve.
Preview Diff
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 |
PASSED: Continuous integration, rev:284 91.189. 93.70:8080/ job/ubuntu- clock-app- ci/668/ 91.189. 93.70:8080/ job/ubuntu- clock-app- vivid-amd64- ci/14 91.189. 93.70:8080/ job/ubuntu- clock-app- vivid-amd64- ci/14/artifact/ work/output/ *zip*/output. zip
http://
Executed test runs:
SUCCESS: http://
deb: http://
Click here to trigger a rebuild: 91.189. 93.70:8080/ job/ubuntu- clock-app- ci/668/ rebuild
http://