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

Proposed by Leo Arias on 2015-06-04
Status: Merged
Approved by: Christian Dywan on 2015-06-22
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 2015-06-04 Approve on 2015-06-22
Christian Dywan 2015-06-04 Approve on 2015-06-22
Leo Arias 2015-06-10 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.
1200. By Leo Arias on 2015-06-04

Merged with staging.

1201. By Leo Arias on 2015-06-04

Reverted the po changes.

1202. By Leo Arias on 2015-06-04

Reverted the po changes.

1203. By Richard Huddie on 2015-06-05

Delay import in _go_to_end() until it is needed.

1204. By Richard Huddie on 2015-06-05

Use activeFocus instead of focus property.

1205. By Richard Huddie on 2015-06-08

Fix flake8 by using ToolkitException.

1206. By Richard Huddie on 2015-06-08

Docstring fix.

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 on 2015-06-11

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

1208. By Richard Huddie on 2015-06-11

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

Seems to be ok +1. Nice job.

1209. By Richard Huddie on 2015-06-12

Merge with staging branch.

1210. By Richard Huddie on 2015-06-12

Fix for textarea and test_textinput failing tests.

1211. By Richard Huddie on 2015-06-17

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

1212. By Richard Huddie on 2015-06-17

Merge from staging branch.

Christian 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 on 2015-06-18

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 on 2015-06-18

Merge from staging branch.

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 on 2015-06-19

Set the osk language to en.

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.

Richard Huddie (rhuddie) wrote :

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

Christian Dywan (kalikiana) wrote :

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

review: Approve
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