Merge lp:~tpeeters/ubuntu-ui-toolkit/ap-toolbar-open into lp:ubuntu-ui-toolkit

Proposed by Tim Peeters
Status: Merged
Approved by: Florian Boucault
Approved revision: 837
Merged at revision: 826
Proposed branch: lp:~tpeeters/ubuntu-ui-toolkit/ap-toolbar-open
Merge into: lp:ubuntu-ui-toolkit
Diff against target: 195 lines (+80/-37)
2 files modified
tests/autopilot/ubuntuuitoolkit/emulators.py (+53/-30)
tests/autopilot/ubuntuuitoolkit/tests/test_emulators.py (+27/-7)
To merge this branch: bzr merge lp:~tpeeters/ubuntu-ui-toolkit/ap-toolbar-open
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Approve
Leo Arias (community) code review, test analysis, ran tests Approve
Ubuntu SDK team Pending
Review via email: mp+194122@code.launchpad.net

Description of the change

Ensure the toolbar is opened when clicking a toolbar in autopilot tests

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Leo Arias (elopio) wrote :

42 + return self.get_toolbar().close()

the close method doesn't return anything, so you can remove the 'return' from there.

50 + def open(self):
72 + def close(self):

Now we are missing tests for those two new public methods. It would be basically to copy the existing tests, but calling get_toolbar(). With the existing tests for the open and close methods of the main view, you can leave them as they are, or you could use a mock just to check that the new open and close methods were called.

95 + # ensure the toolbar is open
96 + self.open()

I find it weird that the click_button method opens the toolbar if it's not open. I think I would prefer to raise an exception if when called click_button the toolbar is not opened.

98 + # ensure the toolbar is still open (may have closed due to timeout)
99 + self.open()

I do like this other open. I think it's just how a real use would act.

119 +# def test_click_unexisting_button(self):

This test is failing for you probably because you are not using autopilot 1.4.

With this branch, we would be missing also a test that checks that the button is clicked even if the toolbar was closed during the method call. I think you can reproduce that condition changing self.pointing_device.move_to_object(button) for a mock that just sleeps until the toolbar is closed.

Thank you for working on this!

Revision history for this message
Leo Arias (elopio) :
review: Needs Fixing
Revision history for this message
Olivier Tilloy (osomon) wrote :

Copying my comments from bug #1248487:

 « I’m not proposing that open_toolbar close it first, I’m proposing that if it’s already open the emulator does some sort of interaction with it (details to be figured out) to ensure it won’t autohide.

   Your proposal works for click_button(), but what if I want to interact differently with the toolbar. E.g., what if the toolbar contains a TextField, or some other widget? I will still want to ensure that when I call open_toolbar(), it remains open. »

Revision history for this message
Tim Peeters (tpeeters) wrote :

> Copying my comments from bug #1248487:
>
> « I’m not proposing that open_toolbar close it first, I’m proposing that if
> it’s already open the emulator does some sort of interaction with it (details
> to be figured out) to ensure it won’t autohide.
>
> Your proposal works for click_button(), but what if I want to interact
> differently with the toolbar. E.g., what if the toolbar contains a TextField,
> or some other widget? I will still want to ensure that when I call
> open_toolbar(), it remains open. »

The current design specs say that if it is not interacted with for 5 seconds it should close. So I don't know how to incorporate that with your requirement.

Do you currently have a use case?

Revision history for this message
Olivier Tilloy (osomon) wrote :

> > Copying my comments from bug #1248487:
> >
> > « I’m not proposing that open_toolbar close it first, I’m proposing that if
> > it’s already open the emulator does some sort of interaction with it
> (details
> > to be figured out) to ensure it won’t autohide.
> >
> > Your proposal works for click_button(), but what if I want to interact
> > differently with the toolbar. E.g., what if the toolbar contains a
> TextField,
> > or some other widget? I will still want to ensure that when I call
> > open_toolbar(), it remains open. »
>
> The current design specs say that if it is not interacted with for 5 seconds
> it should close. So I don't know how to incorporate that with your
> requirement.

So even if it was manually revealed, it will auto-hide after 5 seconds of inactivity?

Revision history for this message
Tim Peeters (tpeeters) wrote :

Yes, it will always hide after 5 seconds.

For most apps this is not a problem, because clicking a button takes virtually no time. But I realize that for the browser, the user may be entering a URL in the address bar for >5s. So for that case, I would have to detect when the user starts and stops interacting with items inside the toolbar, and stop the timer during interaction (and reset afterwards).

Revision history for this message
Olivier Tilloy (osomon) wrote :

> Yes, it will always hide after 5 seconds.

Ok. I hadn’t understood this. I thought it would autohide only when automatically shown.
So I guess the only solution for autopilot tests is to always ensure the toolbar is first hidden by interacting with the application’s contents, then manually reveal it and interact with it (e.g. click a button).
Otherwise we’ll always run into race conditions where the toolbar auto-hides while the cursor is moving towards one of its buttons.
Either that, or your proposal of ensuring the toolbar is shown in click_button(…) before actually clicking the button.

> For most apps this is not a problem, because clicking a button takes virtually
> no time. But I realize that for the browser, the user may be entering a URL in
> the address bar for >5s. So for that case, I would have to detect when the
> user starts and stops interacting with items inside the toolbar, and stop the
> timer during interaction (and reset afterwards).

No, this is not a problem in the browser, as it uses a custom toolbar, not the default one.

Revision history for this message
Tim Peeters (tpeeters) wrote :

> 95 + # ensure the toolbar is open
> 96 + self.open()
>
> I find it weird that the click_button method opens the toolbar if it's not
> open. I think I would prefer to raise an exception if when called click_button
> the toolbar is not opened.

You cannot really know whether the toolbar is open when clicking.
When you call toolbar_open(), the toolbar opens, but things can happen between that and the actual click on the button so that the toolbar closes again. For example, a timeout can occur. If the toolbar was opened initially, it will close after 5s. But in a test it is possible that only after 4.5s toolbar.open() is called (which does nothing because it was open already), and then it takes 1s to move the cursor to the button to click it, but in the meantime the toolbar closed already.

I think it is a matter of choice whether we want click_button() to open the toolbar for you, or if we want always toolbar_open() to be called before click_button(). Note that you would *always* call toolbar_open() (or toolbar.open()) before click_button, so why not integrate it in that function?

Revision history for this message
Tim Peeters (tpeeters) wrote :

> 119 +# def test_click_unexisting_button(self):
>
> This test is failing for you probably because you are not using autopilot 1.4.

I know. Before merging this, I will put the test back in. I just uncommented it now to make it easier to test on my laptop.

> Thank you for working on this!

No problem :) thanks for assisting me :)

827. By Tim Peeters

remove return in close() call

828. By Tim Peeters

additional documentation for click_button

829. By Tim Peeters

use new/updated functions in test_emulators

Revision history for this message
Tim Peeters (tpeeters) wrote :

> Yes, it will always hide after 5 seconds.
>
> For most apps this is not a problem, because clicking a button takes virtually
> no time. But I realize that for the browser, the user may be entering a URL in
> the address bar for >5s. So for that case, I would have to detect when the
> user starts and stops interacting with items inside the toolbar, and stop the
> timer during interaction (and reset afterwards).

This is no problem for the browser, because it uses its own custom toolbar.
However, if applications use the toolbar as in the browser, this can cause problems.

I reported this bug to fix it: https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1249031

830. By Tim Peeters

require toolbar to be opened before calling click_button

831. By Tim Peeters

put test_click_unexisting_button() back in. should work with AP 1.4

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
832. By Tim Peeters

fix too long line in python

833. By Tim Peeters

revert fix, is fixed in upcoming merge

834. By Tim Peeters

Added missing tests and fixed pep257. Thanks elopio.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
835. By Tim Peeters

empty

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

62 + return self

The failure is because that line is overindented. You should return the toolbar even if it is hidden.

Thank you for merging my changes.

review: Approve (code review, test analysis, ran tests)
836. By Tim Peeters

fix indentation

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:836
http://jenkins.qa.ubuntu.com/job/ubuntu-ui-toolkit-ci/1199/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-trusty/643
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-trusty-touch/628/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-ui-toolkit-trusty-amd64-ci/147
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-ui-toolkit-trusty-armhf-ci/147
        deb: http://jenkins.qa.ubuntu.com/job/ubuntu-ui-toolkit-trusty-armhf-ci/147/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-trusty/584
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/643
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/643/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/628
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/628/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-runner-maguro/3047
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-runner-mako/3261/console
    SUCCESS: http://10.97.0.26:8080/job/touch-flash-device/1311
    SUCCESS: http://10.97.0.26:8080/job/touch-flash-device/1312

Click here to trigger a rebuild:
http://10.97.0.26:8080/job/ubuntu-ui-toolkit-ci/1199/rebuild

review: Needs Fixing (continuous-integration)
837. By Tim Peeters

push trunk

Revision history for this message
Tim Peeters (tpeeters) wrote :

^"push trunk" should be "merge trunk" (just to clarify that nothing was pushed to trunk from here)

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

PASSED: Continuous integration, rev:837
http://jenkins.qa.ubuntu.com/job/ubuntu-ui-toolkit-ci/1201/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-trusty/647
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-trusty-touch/632
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-ui-toolkit-trusty-amd64-ci/149
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-ui-toolkit-trusty-armhf-ci/149
        deb: http://jenkins.qa.ubuntu.com/job/ubuntu-ui-toolkit-trusty-armhf-ci/149/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-trusty/588
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/647
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/647/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/632
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-armhf/632/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-runner-maguro/3051
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-runner-mako/3265
    SUCCESS: http://10.97.0.26:8080/job/touch-flash-device/1320
    SUCCESS: http://10.97.0.26:8080/job/touch-flash-device/1319

Click here to trigger a rebuild:
http://10.97.0.26:8080/job/ubuntu-ui-toolkit-ci/1201/rebuild

review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'tests/autopilot/ubuntuuitoolkit/emulators.py'
2--- tests/autopilot/ubuntuuitoolkit/emulators.py 2013-11-06 16:38:23 +0000
3+++ tests/autopilot/ubuntuuitoolkit/emulators.py 2013-11-08 17:15:25 +0000
4@@ -88,39 +88,11 @@
5 :return: The toolbar.
6
7 """
8- toolbar = self.get_toolbar()
9- toolbar.animating.wait_for(False)
10- if not toolbar.opened:
11- self._drag_to_open_toolbar()
12- toolbar.opened.wait_for(True)
13- toolbar.animating.wait_for(False)
14-
15- return toolbar
16-
17- def _drag_to_open_toolbar(self):
18- x, y, _, _ = self.globalRect
19- line_x = x + self.width * 0.50
20- start_y = y + self.height - 1
21- stop_y = y + self.height - self.get_toolbar().height
22-
23- self.pointing_device.drag(line_x, start_y, line_x, stop_y)
24+ return self.get_toolbar().open()
25
26 def close_toolbar(self):
27 """Close the toolbar if it's opened."""
28- toolbar = self.get_toolbar()
29- toolbar.animating.wait_for(False)
30- if toolbar.opened:
31- self._drag_to_close_toolbar()
32- toolbar.opened.wait_for(False)
33- toolbar.animating.wait_for(False)
34-
35- def _drag_to_close_toolbar(self):
36- x, y, _, _ = self.globalRect
37- line_x = x + self.width * 0.50
38- start_y = y + self.height - self.get_toolbar().height
39- stop_y = y + self.height - 1
40-
41- self.pointing_device.drag(line_x, start_y, line_x, stop_y)
42+ self.get_toolbar().close()
43
44 def get_tabs(self):
45 """Return the Tabs emulator of the MainView.
46@@ -244,9 +216,52 @@
47 class Toolbar(UbuntuUIToolkitEmulatorBase):
48 """Toolbar Autopilot emulator."""
49
50+ def open(self):
51+ """Open the toolbar if it's not already opened.
52+
53+ :return: The toolbar.
54+
55+ """
56+ self.animating.wait_for(False)
57+ if not self.opened:
58+ self._drag_to_open()
59+ self.opened.wait_for(True)
60+ self.animating.wait_for(False)
61+
62+ return self
63+
64+ def _drag_to_open(self):
65+ x, y, _, _ = self.globalRect
66+ line_x = x + self.width * 0.50
67+ start_y = y + self.height - 1
68+ stop_y = y
69+
70+ self.pointing_device.drag(line_x, start_y, line_x, stop_y)
71+
72+ def close(self):
73+ """Close the toolbar if it's opened."""
74+ self.animating.wait_for(False)
75+ if self.opened:
76+ self._drag_to_close()
77+ self.opened.wait_for(False)
78+ self.animating.wait_for(False)
79+
80+ def _drag_to_close(self):
81+ x, y, _, _ = self.globalRect
82+ line_x = x + self.width * 0.50
83+ start_y = y
84+ stop_y = y + self.height - 1
85+
86+ self.pointing_device.drag(line_x, start_y, line_x, stop_y)
87+
88 def click_button(self, object_name):
89 """Click a button of the toolbar.
90
91+ The toolbar should be opened before clicking the button, or an
92+ exception will be raised. If the toolbar is closed for some reason
93+ (e.g., timer finishes) after moving the mouse cursor and before
94+ clicking the button, it is re-opened automatically by this function.
95+
96 :parameter object_name: The QML objectName property of the button.
97 :raise ToolkitEmulatorException: If there is no button with that object
98 name.
99@@ -257,6 +272,14 @@
100 except dbus.StateNotFoundError:
101 raise ToolkitEmulatorException(
102 'Button with objectName "{0}" not found.'.format(object_name))
103+ # ensure the toolbar is open
104+ if not self.opened:
105+ raise ToolkitEmulatorException(
106+ 'Toolbar must be opened before calling click_button().')
107+ self.pointing_device.move_to_object(button)
108+ # ensure the toolbar is still open (may have closed due to timeout)
109+ self.open()
110+ # click the button
111 self.pointing_device.click_object(button)
112
113 def _get_button(self, object_name):
114
115=== modified file 'tests/autopilot/ubuntuuitoolkit/tests/test_emulators.py'
116--- tests/autopilot/ubuntuuitoolkit/tests/test_emulators.py 2013-11-06 16:45:12 +0000
117+++ tests/autopilot/ubuntuuitoolkit/tests/test_emulators.py 2013-11-08 17:15:25 +0000
118@@ -87,6 +87,18 @@
119 toolbar = self.main_view.get_toolbar()
120 self.assertIsInstance(toolbar, emulators.Toolbar)
121
122+ def test_open_toolbar(self):
123+ with mock.patch.object(emulators.Toolbar, 'open') as mock_open:
124+ self.main_view.open_toolbar()
125+
126+ mock_open.assert_called_once_with()
127+
128+ def test_close_toolbar(self):
129+ with mock.patch.object(emulators.Toolbar, 'close') as mock_close:
130+ self.main_view.close_toolbar()
131+
132+ mock_close.assert_called_once_with()
133+
134 def test_open_toolbar_returns_the_toolbar(self):
135 toolbar = self.main_view.open_toolbar()
136 self.assertIsInstance(toolbar, emulators.Toolbar)
137@@ -171,29 +183,29 @@
138 self.assertFalse(self.toolbar.opened)
139
140 def test_open_toolbar(self):
141- self.main_view.open_toolbar()
142+ self.toolbar.open()
143 self.assertTrue(self.toolbar.opened)
144 self.assertFalse(self.toolbar.animating)
145
146 def test_opened_toolbar_is_not_opened_again(self):
147- self.main_view.open_toolbar()
148+ self.toolbar.open()
149 with mock.patch.object(
150 self.main_view.pointing_device, 'drag') as mock_drag:
151- self.main_view.open_toolbar()
152+ self.toolbar.open()
153
154 self.assertFalse(mock_drag.called)
155 self.assertTrue(self.toolbar.opened)
156
157 def test_close_toolbar(self):
158- self.main_view.open_toolbar()
159- self.main_view.close_toolbar()
160+ self.toolbar.open()
161+ self.toolbar.close()
162 self.assertFalse(self.toolbar.opened)
163 self.assertFalse(self.toolbar.animating)
164
165 def test_closed_toolbar_is_not_closed_again(self):
166 with mock.patch.object(
167 self.main_view.pointing_device, 'drag') as mock_drag:
168- self.main_view.close_toolbar()
169+ self.toolbar.close()
170
171 self.assertFalse(mock_drag.called)
172 self.assertFalse(self.toolbar.opened)
173@@ -201,7 +213,7 @@
174 def test_click_toolbar_button(self):
175 label = self.app.select_single('Label', objectName='clicked_label')
176 self.assertNotEqual(label.text, 'Button clicked.')
177- self.main_view.open_toolbar()
178+ self.toolbar.open()
179 self.toolbar.click_button('buttonName')
180 self.assertEqual(label.text, 'Button clicked.')
181
182@@ -213,6 +225,14 @@
183 self.assertEqual(
184 error.message, 'Button with objectName "unexisting" not found.')
185
186+ def test_click_button_on_closed_toolbar(self):
187+ error = self.assertRaises(
188+ emulators.ToolkitEmulatorException, self.toolbar.click_button,
189+ 'buttonName')
190+ self.assertEqual(
191+ error.message,
192+ 'Toolbar must be opened before calling click_button().')
193+
194
195 class TabsTestCase(tests.QMLStringAppTestCase):
196

Subscribers

People subscribed via source and target branches

to status/vote changes: