Merge lp:~canonical-platform-qa/ubuntu-ui-toolkit/textfield-use-osk into lp:ubuntu-ui-toolkit/staging

Proposed by Leo Arias
Status: Merged
Approved by: Cris Dywan
Approved revision: 1215
Merged at revision: 1537
Proposed branch: lp:~canonical-platform-qa/ubuntu-ui-toolkit/textfield-use-osk
Merge into: lp:ubuntu-ui-toolkit/staging
Diff against target: 432 lines (+235/-61)
7 files modified
debian/control (+1/-0)
tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_common.py (+141/-3)
tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_textarea.py (+28/-5)
tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_textfield.py (+58/-40)
tests/autopilot/ubuntuuitoolkit/tests/components/test_textinput.py (+2/-0)
tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_textarea.py (+2/-11)
tests/autopilot/ubuntuuitoolkit/tests/gallery/test_textinput.py (+3/-2)
To merge this branch: bzr merge lp:~canonical-platform-qa/ubuntu-ui-toolkit/textfield-use-osk
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Approve
Cris Dywan Approve
Leo Arias Pending
Review via email: mp+261098@code.launchpad.net

This proposal supersedes a proposal from 2015-06-03.

Commit message

Use the real OSK for keyboard input

Description of the change

Use the OSK in text fields to enter text.

This change will restart maliit-server with testability enabled if it is not already done. The keyboard type returned from get_keyboard() is based on whether maliit-server is running or not. Some project tests will stop maliit-server and use simulated key events, so this change will not affect that behaviour.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
1200. By Leo Arias

Merged with staging.

1201. By Leo Arias

Reverted the po changes.

1202. By Leo Arias

Reverted the po changes.

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)
1203. By Richard Huddie

Delay import in _go_to_end() until it is needed.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1204. By Richard Huddie

Use activeFocus instead of focus property.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1205. By Richard Huddie

Fix flake8 by using ToolkitException.

1206. By Richard Huddie

Docstring fix.

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

There are several failures in the test results where it is claimed that the OSK is not displayed, but the failure screenshots show that it is. I have raised a bug on ubuntu-keyboard for this: https://bugs.launchpad.net/ubuntu-keyboard/+bug/1463906

1207. By Richard Huddie

Fix test_textinput tests to use correct textfield.write() method rather than directly calling keyboard.type().

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1208. By Richard Huddie

Only run test which uses shift+left key combination on desktop.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Sergio Cazzolato (sergio-j-cazzolato) wrote :

Seems to be ok +1. Nice job.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1209. By Richard Huddie

Merge with staging branch.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1210. By Richard Huddie

Fix for textarea and test_textinput failing tests.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1211. By Richard Huddie

Fixes for failing tests. Also wait 5 seconds after re-launching maliit-server to allow it to be ready.

1212. By Richard Huddie

Merge from staging branch.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Cris Dywan (kalikiana) wrote :

+ if is_maliit_process_running():
+ configure_osk_settings()
+ restart_maliit_with_testability()
+ return input.Keyboard.create('OSK')
+ else:
+ return input.Keyboard.create()
+
+
+def restart_maliit_with_testability():
+ """Restart maliit-server with testability enabled."""
+ if is_maliit_process_running():

The if..running is redundant given that restart only occurs if that's already the case. Also the function name inherently requires that.

+ # This is needed to work around launchpad.net/bugs/1248913
+ time.sleep(5)

Please use https so bug links can actually be opened.

review: Needs Fixing
1213. By Richard Huddie

Fix review comments. Also don't enforce en language, re-enable test_clear_with_clear_button as prediction is now set off.

1214. By Richard Huddie

Merge from staging branch.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Richard Huddie (rhuddie) wrote :

> + if is_maliit_process_running():
> + configure_osk_settings()
> + restart_maliit_with_testability()
> + return input.Keyboard.create('OSK')
> + else:
> + return input.Keyboard.create()
> +
> +
> +def restart_maliit_with_testability():
> + """Restart maliit-server with testability enabled."""
> + if is_maliit_process_running():
>
> The if..running is redundant given that restart only occurs if that's already
> the case. Also the function name inherently requires that.
>
> + # This is needed to work around launchpad.net/bugs/1248913
> + time.sleep(5)
>
> Please use https so bug links can actually be opened.

Thanks for review, I've made those updates.

1215. By Richard Huddie

Set the osk language to en.

Revision history for this message
Richard Huddie (rhuddie) wrote :

I've discovered that some of the key names differ between simulated keyboard and osk.
E.g. The webbrowser tests use the key 'Enter' to navigate to a url, but on the osk the key is called 'Return'. This will cause test failures unless the key names are aligned fully.

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

See bug report for improving compatibility of OSK with simulated keyboard: https://bugs.launchpad.net/ubuntu-keyboard/+bug/1467449

Revision history for this message
Cris Dywan (kalikiana) wrote :

Thank you, looking good now (the failures in CI are false positives)!

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/control'
2--- debian/control 2015-06-02 17:18:53 +0000
3+++ debian/control 2015-06-19 14:49:08 +0000
4@@ -147,6 +147,7 @@
5 python3-autopilot (>= 1.4),
6 python3-autopilot-trace,
7 qttestability-autopilot,
8+ ubuntu-keyboard-autopilot,
9 ubuntu-ui-toolkit-examples (>= ${source:Version}),
10 upstart,
11 url-dispatcher-tools,
12
13=== modified file 'tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_common.py'
14--- tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_common.py 2015-04-14 21:02:06 +0000
15+++ tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_common.py 2015-06-19 14:49:08 +0000
16@@ -17,7 +17,10 @@
17 """Common helpers for Ubuntu UI Toolkit Autopilot custom proxy objects."""
18
19 import logging
20+import subprocess
21+import time
22 from distutils import version
23+from gi.repository import Gio
24
25 import autopilot
26 from autopilot import (
27@@ -27,9 +30,10 @@
28 )
29 from autopilot.introspection import dbus
30
31-
32 logger = logging.getLogger(__name__)
33
34+MALIIT = 'maliit-server'
35+
36
37 class ToolkitException(Exception):
38 """Exception raised when there is an error with the custom proxy object."""
39@@ -51,8 +55,142 @@
40
41 def get_keyboard():
42 """Return the keyboard device."""
43- # TODO return the OSK if we are on the phone. --elopio - 2014-01-13
44- return input.Keyboard.create()
45+ if is_maliit_process_running():
46+ configure_osk_settings()
47+ restart_maliit_with_testability()
48+ return input.Keyboard.create('OSK')
49+ else:
50+ return input.Keyboard.create()
51+
52+
53+def restart_maliit_with_testability():
54+ """Restart maliit-server with testability enabled."""
55+ pid = get_process_pid(MALIIT)
56+ if _is_testability_enabled_for_process(pid):
57+ return
58+ _stop_process(MALIIT)
59+ _start_process(MALIIT, 'QT_LOAD_TESTABILITY=1')
60+ # This is needed to work around https://launchpad.net/bugs/1248913
61+ time.sleep(5)
62+
63+
64+def configure_osk_settings():
65+ """Configure OSK ready for testing by turning off all helpers."""
66+ gsettings = Gio.Settings.new("com.canonical.keyboard.maliit")
67+ gsettings.set_string("active-language", "en")
68+ gsettings.set_boolean("auto-capitalization", False)
69+ gsettings.set_boolean("auto-completion", False)
70+ gsettings.set_boolean("predictive-text", False)
71+ gsettings.set_boolean("spell-checking", False)
72+ gsettings.set_boolean("double-space-full-stop", False)
73+
74+
75+def _is_testability_enabled_for_process(pid):
76+ """Return True if testability is enabled for specified process."""
77+ proc_env = '/proc/{pid}/environ'.format(pid=pid)
78+ command = ['cat', proc_env]
79+ try:
80+ output = subprocess.check_output(
81+ command,
82+ stderr=subprocess.STDOUT,
83+ universal_newlines=True,
84+ )
85+ except subprocess.CalledProcessError as e:
86+ e.args += ('Failed to get environment for pid {}: {}.'.format(
87+ pid, e.output),)
88+ raise
89+ return output.find('QT_LOAD_TESTABILITY=1') >= 0
90+
91+
92+def _stop_process(proc_name):
93+ """Stop process with name proc_name."""
94+ logger.info('Stoping process {}.'.format(proc_name))
95+ command = ['/sbin/initctl', 'stop', proc_name]
96+ try:
97+ output = subprocess.check_output(
98+ command,
99+ stderr=subprocess.STDOUT,
100+ universal_newlines=True,
101+ )
102+ logger.info(output)
103+ except subprocess.CalledProcessError as e:
104+ e.args += ('Failed to stop {}: {}.'.format(proc_name, e.output),)
105+ raise
106+
107+
108+def _start_process(proc_name, *args):
109+ """Start a process.
110+
111+ :param proc_name: The name of the process.
112+ :param args: The arguments to be used when starting the job.
113+ :return: The process id of the started job.
114+ :raises CalledProcessError: if the job failed to start.
115+
116+ """
117+ logger.info('Starting job {} with arguments {}.'.format(proc_name, args))
118+ command = ['/sbin/initctl', 'start', proc_name] + list(args)
119+ try:
120+ output = subprocess.check_output(
121+ command,
122+ stderr=subprocess.STDOUT,
123+ universal_newlines=True,
124+ )
125+ logger.info(output)
126+ pid = get_process_pid(proc_name)
127+ except subprocess.CalledProcessError as e:
128+ e.args += ('Failed to start {}: {}.'.format(proc_name, e.output),)
129+ raise
130+ else:
131+ return pid
132+
133+
134+def get_process_pid(proc_name):
135+ """Return the process id of a running job.
136+
137+ :param str name: The name of the job.
138+ :raises ToolkitException: if the job is not running.
139+
140+ """
141+ status = get_process_status(proc_name)
142+ if "start/" not in status:
143+ raise ToolkitException(
144+ '{} is not in the running state.'.format(proc_name))
145+ return int(status.split()[-1])
146+
147+
148+def get_process_status(name):
149+ """
150+ Return the status of a process.
151+
152+ :param str name: The name of the process.
153+ :raises ToolkitException: if it's not possible to get status of the job.
154+
155+ """
156+ try:
157+ return subprocess.check_output([
158+ '/sbin/initctl',
159+ 'status',
160+ name
161+ ], universal_newlines=True)
162+ except subprocess.CalledProcessError as error:
163+ raise ToolkitException(
164+ "Unable to get {}'s status: {}".format(name, error)
165+ )
166+
167+
168+def is_process_running(name):
169+ """Return True if the process is running. Otherwise, False.
170+
171+ :param str name: The name of the process.
172+ :raises ToolkitException: if not possible to get status of the process.
173+
174+ """
175+ return 'start/' in get_process_status(name)
176+
177+
178+def is_maliit_process_running():
179+ """Return True if malitt-server process is running, False otherwise."""
180+ return is_process_running(MALIIT)
181
182
183 def check_autopilot_version():
184
185=== modified file 'tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_textarea.py'
186--- tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_textarea.py 2014-08-20 20:11:35 +0000
187+++ tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_textarea.py 2015-06-19 14:49:08 +0000
188@@ -22,12 +22,35 @@
189
190 def clear(self):
191 """Clear the text area."""
192+ self._ensure_focused()
193 if not self.is_empty():
194 self._clear_with_keys()
195 self.text.wait_for('')
196
197- def _go_to_end(self):
198- # We override this because the text areas can have more than one line.
199- # XXX Here we are cheating because the on-screen keyboard doesn't have
200- # CTRL nor END keys. --elopio - 2014-08-20
201- self.keyboard.press_and_release('Ctrl+End')
202+ def _go_to_end(self, select_text=False):
203+ # We override this because the text areas can have more than one line.
204+ key = 'Ctrl+End'
205+ if select_text:
206+ key = 'Shift+' + key
207+ if self._is_keyboard_osk():
208+ # XXX Here we are cheating because the on-screen keyboard doesn't
209+ # have an END key.
210+ from autopilot.input import Keyboard
211+ keyboard = Keyboard.create()
212+ else:
213+ keyboard = self.keyboard
214+ keyboard.press_and_release(key)
215+
216+ def _go_to_start(self, select_text=False):
217+ # We override this because the text areas can have more than one line.
218+ key = 'Ctrl+Home'
219+ if select_text:
220+ key = 'Shift+' + key
221+ if self._is_keyboard_osk():
222+ # XXX Here we are cheating because the on-screen keyboard doesn't
223+ # have a HOME key.
224+ from autopilot.input import Keyboard
225+ keyboard = Keyboard.create()
226+ else:
227+ keyboard = self.keyboard
228+ keyboard.press_and_release(key)
229
230=== modified file 'tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_textfield.py'
231--- tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_textfield.py 2015-04-14 21:02:06 +0000
232+++ tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_textfield.py 2015-06-19 14:49:08 +0000
233@@ -16,16 +16,9 @@
234
235 import logging
236
237-from autopilot import (
238- logging as autopilot_logging,
239- platform
240-)
241-
242-from ubuntuuitoolkit._custom_proxy_objects import (
243- _common,
244- _mainview
245-)
246-
247+from autopilot import logging as autopilot_logging
248+
249+from ubuntuuitoolkit._custom_proxy_objects import _common
250
251 logger = logging.getLogger(__name__)
252
253@@ -47,18 +40,18 @@
254 of the text field. Default is True.
255
256 """
257- with self.keyboard.focused_type(self):
258- self.focus.wait_for(True)
259- if clear:
260- self.clear()
261- else:
262- if not self.is_empty():
263- self.keyboard.press_and_release('End')
264- self.keyboard.type(text)
265+ self._ensure_focused()
266+ if clear:
267+ self.clear()
268+ else:
269+ if not self.is_empty():
270+ self._go_to_end()
271+ self.keyboard.type(text)
272
273 @autopilot_logging.log_action(logger.info)
274 def clear(self):
275 """Clear the text field."""
276+ self._ensure_focused()
277 if not self.is_empty():
278 if self.hasClearButton:
279 self._click_clear_button()
280@@ -80,44 +73,69 @@
281
282 @autopilot_logging.log_action(logger.debug)
283 def _clear_with_keys(self):
284- if platform.model() == 'Desktop':
285- self._select_all()
286- self.keyboard.press_and_release('BackSpace')
287- else:
288+ if self._is_keyboard_osk():
289 # Touch tap currently doesn't have a press_duration parameter, so
290 # we can't select all the text.
291 # Reported as bug http://pad.lv/1268782 --elopio - 2014-01-13
292 self._go_to_end()
293- while self.cursorPosition != 0:
294- self._delete_one_character()
295+ while self.text != '':
296+ self._delete_one_character_using_osk()
297+ else:
298+ self._select_all()
299+ self.keyboard.press_and_release('BackSpace')
300 if not self.is_empty():
301 raise _common.ToolkitException('Failed to clear the text field.')
302
303 @autopilot_logging.log_action(logger.debug)
304 def _select_all(self):
305 if not self._is_all_text_selected():
306- # right click is needed
307- self.pointing_device.click_object(self, button=3)
308- root = self.get_root_instance()
309- main_view = root.select_single(_mainview.MainView)
310- popover = main_view.get_text_input_context_menu(
311- 'text_input_contextmenu')
312- popover.click_option_by_text('Select All')
313+ self._go_to_start()
314+ self._go_to_end(select_text=True)
315
316 def _is_all_text_selected(self):
317 return self.text == self.selectedText
318
319- @autopilot_logging.log_action(logger.debug)
320- def _go_to_end(self):
321- # XXX Here we are cheating because the on-screen keyboard doesn't have
322- # an END key. --elopio - 2014-08-20
323- self.keyboard.press_and_release('End')
324-
325- @autopilot_logging.log_action(logger.debug)
326- def _delete_one_character(self):
327+ def _is_keyboard_osk(self):
328+ """Return True if the keyboard instance is the OSK."""
329+ return _common.is_maliit_process_running()
330+
331+ @autopilot_logging.log_action(logger.debug)
332+ def _go_to_end(self, select_text=False):
333+ key = 'End'
334+ if select_text:
335+ key = 'Shift+' + key
336+ if self._is_keyboard_osk():
337+ # XXX Here we are cheating because the on-screen keyboard doesn't
338+ # have an END key. --elopio - 2014-08-20
339+ from autopilot.input import Keyboard
340+ keyboard = Keyboard.create()
341+ else:
342+ keyboard = self.keyboard
343+ keyboard.press_and_release(key)
344+
345+ @autopilot_logging.log_action(logger.debug)
346+ def _go_to_start(self, select_text=False):
347+ key = 'Home'
348+ if select_text:
349+ key = 'Shift+' + key
350+ if self._is_keyboard_osk():
351+ from autopilot.input import Keyboard
352+ keyboard = Keyboard.create()
353+ else:
354+ keyboard = self.keyboard
355+ keyboard.press_and_release(key)
356+
357+ @autopilot_logging.log_action(logger.debug)
358+ def _delete_one_character_using_osk(self):
359 original_text = self.text
360 # We delete with backspace because the on-screen keyboard has
361 # that key.
362- self.keyboard.press_and_release('BackSpace')
363+ self.keyboard.press_and_release('backspace')
364 if len(self.text) != len(original_text) - 1:
365 raise _common.ToolkitException('Failed to delete one character.')
366+
367+ @autopilot_logging.log_action(logger.debug)
368+ def _ensure_focused(self):
369+ if not self.activeFocus:
370+ self.pointing_device.click_object(self)
371+ self.activeFocus.wait_for(True)
372
373=== modified file 'tests/autopilot/ubuntuuitoolkit/tests/components/test_textinput.py'
374--- tests/autopilot/ubuntuuitoolkit/tests/components/test_textinput.py 2015-06-03 15:09:52 +0000
375+++ tests/autopilot/ubuntuuitoolkit/tests/components/test_textinput.py 2015-06-19 14:49:08 +0000
376@@ -97,6 +97,8 @@
377 self.pointing_device.click_object(self.textfield)
378 self.select_cursor('cursorPosition')
379
380+ @testtools.skipUnless(platform.model() == 'Desktop',
381+ 'Desktop only due to non-OSK key used.')
382 def test_caret_visible_after_selecting(self):
383 self.test_caret_hide_while_typing()
384 # Select a character
385
386=== modified file 'tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_textarea.py'
387--- tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_textarea.py 2015-04-14 21:02:06 +0000
388+++ tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_textarea.py 2015-06-19 14:49:08 +0000
389@@ -14,9 +14,6 @@
390 # You should have received a copy of the GNU Lesser General Public License
391 # along with this program. If not, see <http://www.gnu.org/licenses/>.
392
393-from unittest import mock
394-
395-from autopilot import platform
396 from testtools.matchers import GreaterThan
397
398 import ubuntuuitoolkit
399@@ -56,17 +53,11 @@
400 self.simple_text_area.clear()
401 self.assertEqual(self.simple_text_area.text, '')
402
403- def test_clear_with_multiple_lines_on_touch(self):
404+ def test_clear_with_multiple_lines(self):
405 # This is a regrestion test for http://pad.lv/1359167
406 self.simple_text_area.write(
407 'Long text that will make it wrap into multiple lines.')
408 self.assertThat(self.simple_text_area.lineCount, GreaterThan(1))
409- self.simple_text_area.keyboard.press_and_release('Ctrl+Home')
410- if platform.model() == 'Desktop':
411- # Use a touch mock.
412- patcher = mock.patch('autopilot.platform.model')
413- mock_model = patcher.start()
414- self.addCleanup(patcher.stop)
415- mock_model.return_value = 'not desktop'
416+ self.simple_text_area._go_to_start()
417 self.simple_text_area.clear()
418 self.assertEqual(self.simple_text_area.text, '')
419
420=== modified file 'tests/autopilot/ubuntuuitoolkit/tests/gallery/test_textinput.py'
421--- tests/autopilot/ubuntuuitoolkit/tests/gallery/test_textinput.py 2015-04-14 21:02:06 +0000
422+++ tests/autopilot/ubuntuuitoolkit/tests/gallery/test_textinput.py 2015-06-19 14:49:08 +0000
423@@ -92,6 +92,7 @@
424 self.assertFalse(textfield_disabled.enabled)
425
426 disabled_text = textfield_disabled.text
427- self.pointing_device.click_object(textfield_disabled)
428- textfield_disabled.keyboard.type('This should not be written')
429+ self.assertRaises(AssertionError,
430+ textfield_disabled.write,
431+ 'This should not be written')
432 self.assertEqual(disabled_text, textfield_disabled.text)

Subscribers

People subscribed via source and target branches