OLD | NEW |
1 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 1 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 | 5 |
6 # DESCRIPTION : | 6 # DESCRIPTION : |
7 # | 7 # |
8 # Intended for use during manufacturing to validate that all keyboard | 8 # Intended for use during manufacturing to validate that all keyboard |
9 # keys function properly. This program will display a keyboard image | 9 # keys function properly. This program will display a keyboard image |
10 # and keys will be highlighted as they are pressed and released. | 10 # and keys will be highlighted as they are pressed and released. |
11 # After the first key is hit, a countdown will begin. If not all keys | 11 # After the first key is hit, a countdown will begin. If not all keys |
12 # are used in time, the test will fail. | 12 # are used in time, the test will fail. |
13 | 13 |
14 | 14 |
| 15 import cairo |
| 16 import gobject |
| 17 import gtk |
| 18 import logging |
| 19 import pango |
| 20 import time |
| 21 import os |
| 22 import sys |
| 23 |
| 24 from autotest_lib.client.bin import factory |
| 25 from autotest_lib.client.bin import factory_ui_lib as ful |
15 from autotest_lib.client.bin import test | 26 from autotest_lib.client.bin import test |
16 from autotest_lib.client.common_lib import error | 27 from autotest_lib.client.common_lib import error |
17 from autotest_lib.client.common_lib import factory_test | |
18 | 28 |
19 import cairo | |
20 import os | |
21 | 29 |
22 import KeyboardTest | 30 # How long keyboard_test allows in seconds from the first keypress |
| 31 # until defaulting to the failure condition. |
| 32 _TIMEOUT = 50 |
| 33 _PASS_TIMEOUT = 0.4 |
| 34 |
| 35 # Highlight color and alpha to indicate activated keys. |
| 36 _RGBA_PRESS_AND_RELEASE = ( 0, 0.5, 0, 0.6) |
| 37 _RGBA_PRESS_ONLY = (0.6, 0.6, 0, 0.6) |
| 38 |
| 39 |
| 40 class KeyboardTest: |
| 41 |
| 42 def __init__(self, kbd_image, bindings, ft_state): |
| 43 self._kbd_image = kbd_image |
| 44 self._bindings = bindings |
| 45 self._ft_state = ft_state |
| 46 self._pressed_keys = set() |
| 47 self._successful_keys = set() |
| 48 self._deadline = None |
| 49 self._success = False |
| 50 |
| 51 def show_countdown(self, widget, context): |
| 52 countdown = self._deadline - int(time.time()) |
| 53 width, height = widget.get_size_request() |
| 54 text = '%3d' % countdown |
| 55 context.save() |
| 56 context.translate(width - 60, height) |
| 57 context.set_source_rgb(0.5, 0.5, 0.5) |
| 58 context.select_font_face( |
| 59 'Courier New', cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) |
| 60 context.set_font_size(20) |
| 61 x_bearing, y_bearing = context.text_extents('000')[:2] |
| 62 context.move_to(x_bearing, y_bearing) |
| 63 context.show_text(text) |
| 64 context.restore() |
| 65 |
| 66 def timer_event(self, window): |
| 67 if not self._deadline: |
| 68 # Ignore timer events with no countdown in progress. |
| 69 return True |
| 70 if self._deadline <= time.time(): |
| 71 factory.log('deadline reached') |
| 72 gtk.main_quit() |
| 73 window.queue_draw() |
| 74 return True |
| 75 |
| 76 def expose_event(self, widget, event): |
| 77 context = widget.window.cairo_create() |
| 78 |
| 79 # Show keyboard image as the background. |
| 80 context.set_source_surface(self._kbd_image, 0, 0) |
| 81 context.paint() |
| 82 |
| 83 for key in self._successful_keys: |
| 84 coords = self._bindings[key] |
| 85 context.rectangle(*coords) |
| 86 context.set_source_rgba(*_RGBA_PRESS_AND_RELEASE) |
| 87 context.fill() |
| 88 for key in self._pressed_keys: |
| 89 coords = self._bindings[key] |
| 90 context.rectangle(*coords) |
| 91 context.set_source_rgba(*_RGBA_PRESS_ONLY) |
| 92 context.fill() |
| 93 if self._deadline: |
| 94 self.show_countdown(widget, context) |
| 95 |
| 96 return True |
| 97 |
| 98 def key_press_event(self, widget, event): |
| 99 if self._ft_state.exit_on_trigger(event): |
| 100 return True |
| 101 if ('GDK_MOD1_MASK' in event.state.value_names |
| 102 and event.keyval == gtk.keysyms.q): |
| 103 # Alt-q for early exit. |
| 104 gtk.main_quit() |
| 105 return True |
| 106 if event.keyval in self._successful_keys: |
| 107 # Ignore keys already found to work successfully. |
| 108 return True |
| 109 if event.state != 0: |
| 110 factory.log('key (0x%x) ignored because modifier applied (state=%d)' |
| 111 % (event.keyval, event.state)) |
| 112 return True |
| 113 if event.keyval not in self._bindings: |
| 114 factory.log('key (0x%x) ignored because not in bindings' |
| 115 % event.keyval) |
| 116 return True |
| 117 |
| 118 self._pressed_keys.add(event.keyval) |
| 119 widget.queue_draw() |
| 120 |
| 121 # The first keypress starts test countdown. |
| 122 if self._deadline is None: |
| 123 self._deadline = int(time.time()) + _TIMEOUT |
| 124 |
| 125 return True |
| 126 |
| 127 def key_release_event(self, widget, event): |
| 128 if event.keyval not in self._pressed_keys: |
| 129 # Ignore releases for keys not previously accepted as pressed. |
| 130 return False |
| 131 self._pressed_keys.remove(event.keyval) |
| 132 self._successful_keys.add(event.keyval) |
| 133 if not (set(self._bindings) - self._successful_keys): |
| 134 self._success = True |
| 135 self._deadline = int(time.time()) + _PASS_TIMEOUT |
| 136 widget.queue_draw() |
| 137 return True |
| 138 |
| 139 def register_callbacks(self, window): |
| 140 window.connect('key-press-event', self.key_press_event) |
| 141 window.connect('key-release-event', self.key_release_event) |
| 142 window.add_events(gtk.gdk.KEY_PRESS_MASK | gtk.gdk.KEY_RELEASE_MASK) |
23 | 143 |
24 | 144 |
25 class factory_Keyboard(test.test): | 145 class factory_Keyboard(test.test): |
26 version = 1 | 146 version = 1 |
27 preserve_srcdir = True | 147 preserve_srcdir = True |
28 | 148 |
29 def run_once(self, test_widget_size=None, trigger_set=None, | 149 def run_once(self, |
30 result_file_path=None, layout=None): | 150 test_widget_size=None, |
| 151 trigger_set=None, |
| 152 result_file_path=None, |
| 153 layout=None): |
31 | 154 |
32 factory_test.XXX_log('factory_Keyboard') | 155 factory.log('%s run_once' % self.__class__) |
33 | 156 |
34 # XXX Why can this not be run from the UI code? | 157 # XXX Why can this not be run from the UI code? |
35 xset_status = os.system('xset r off') | 158 xset_status = os.system('xset r off') |
36 xmm_status = os.system('xmodmap -e "clear Lock"') | 159 xmm_status = os.system('xmodmap -e "clear Lock"') |
37 if xset_status or xmm_status: | 160 if xset_status or xmm_status: |
38 raise TestFail('ERROR: disabling key repeat or caps lock') | 161 raise TestFail('ERROR: disabling key repeat or caps lock') |
39 | 162 |
40 factory_test.init(trigger_set=trigger_set, | 163 ft_state = ful.State( |
41 result_file_path=result_file_path) | 164 trigger_set=trigger_set, |
| 165 result_file_path=result_file_path) |
42 | 166 |
43 os.chdir(self.srcdir) | 167 os.chdir(self.srcdir) |
| 168 |
44 kbd_image = cairo.ImageSurface.create_from_png('%s.png' % layout) | 169 kbd_image = cairo.ImageSurface.create_from_png('%s.png' % layout) |
| 170 image_size = (kbd_image.get_width(), kbd_image.get_height()) |
| 171 |
45 with open('%s.bindings' % layout, 'r') as file: | 172 with open('%s.bindings' % layout, 'r') as file: |
46 bindings = eval(file.read()) | 173 bindings = eval(file.read()) |
47 | 174 |
48 test_widget, wr_cb = KeyboardTest.make_test_widget(kbd_image, bindings) | 175 test = KeyboardTest(kbd_image, bindings, ft_state) |
49 | 176 |
50 factory_test.run_test_widget( | 177 drawing_area = gtk.DrawingArea() |
51 test_widget=test_widget, | 178 drawing_area.set_size_request(*image_size) |
| 179 drawing_area.connect('expose_event', test.expose_event) |
| 180 drawing_area.add_events(gtk.gdk.EXPOSURE_MASK) |
| 181 gobject.timeout_add(1000, test.timer_event, drawing_area) |
| 182 |
| 183 ft_state.run_test_widget( |
| 184 test_widget=drawing_area, |
52 test_widget_size=test_widget_size, | 185 test_widget_size=test_widget_size, |
53 window_registration_callback=wr_cb) | 186 window_registration_callback=test.register_callbacks) |
| 187 |
| 188 factory.log('%s run_once finished' % self.__class__) |
OLD | NEW |