Index: client/site_tests/factory_Keyboard/factory_Keyboard.py |
diff --git a/client/site_tests/factory_Keyboard/factory_Keyboard.py b/client/site_tests/factory_Keyboard/factory_Keyboard.py |
index a13af34fecf2542567e6e2665d22fe91ad659c5f..ffb881b47375b8affe4732c2cf3781c6898a666d 100644 |
--- a/client/site_tests/factory_Keyboard/factory_Keyboard.py |
+++ b/client/site_tests/factory_Keyboard/factory_Keyboard.py |
@@ -12,24 +12,147 @@ |
# are used in time, the test will fail. |
+import cairo |
+import gobject |
+import gtk |
+import logging |
+import pango |
+import time |
+import os |
+import sys |
+ |
+from autotest_lib.client.bin import factory |
+from autotest_lib.client.bin import factory_ui_lib as ful |
from autotest_lib.client.bin import test |
from autotest_lib.client.common_lib import error |
-from autotest_lib.client.common_lib import factory_test |
-import cairo |
-import os |
-import KeyboardTest |
+# How long keyboard_test allows in seconds from the first keypress |
+# until defaulting to the failure condition. |
+_TIMEOUT = 50 |
+_PASS_TIMEOUT = 0.4 |
+ |
+# Highlight color and alpha to indicate activated keys. |
+_RGBA_PRESS_AND_RELEASE = ( 0, 0.5, 0, 0.6) |
+_RGBA_PRESS_ONLY = (0.6, 0.6, 0, 0.6) |
+ |
+ |
+class KeyboardTest: |
+ |
+ def __init__(self, kbd_image, bindings, ft_state): |
+ self._kbd_image = kbd_image |
+ self._bindings = bindings |
+ self._ft_state = ft_state |
+ self._pressed_keys = set() |
+ self._successful_keys = set() |
+ self._deadline = None |
+ self._success = False |
+ |
+ def show_countdown(self, widget, context): |
+ countdown = self._deadline - int(time.time()) |
+ width, height = widget.get_size_request() |
+ text = '%3d' % countdown |
+ context.save() |
+ context.translate(width - 60, height) |
+ context.set_source_rgb(0.5, 0.5, 0.5) |
+ context.select_font_face( |
+ 'Courier New', cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) |
+ context.set_font_size(20) |
+ x_bearing, y_bearing = context.text_extents('000')[:2] |
+ context.move_to(x_bearing, y_bearing) |
+ context.show_text(text) |
+ context.restore() |
+ |
+ def timer_event(self, window): |
+ if not self._deadline: |
+ # Ignore timer events with no countdown in progress. |
+ return True |
+ if self._deadline <= time.time(): |
+ factory.log('deadline reached') |
+ gtk.main_quit() |
+ window.queue_draw() |
+ return True |
+ |
+ def expose_event(self, widget, event): |
+ context = widget.window.cairo_create() |
+ |
+ # Show keyboard image as the background. |
+ context.set_source_surface(self._kbd_image, 0, 0) |
+ context.paint() |
+ |
+ for key in self._successful_keys: |
+ coords = self._bindings[key] |
+ context.rectangle(*coords) |
+ context.set_source_rgba(*_RGBA_PRESS_AND_RELEASE) |
+ context.fill() |
+ for key in self._pressed_keys: |
+ coords = self._bindings[key] |
+ context.rectangle(*coords) |
+ context.set_source_rgba(*_RGBA_PRESS_ONLY) |
+ context.fill() |
+ if self._deadline: |
+ self.show_countdown(widget, context) |
+ |
+ return True |
+ |
+ def key_press_event(self, widget, event): |
+ if self._ft_state.exit_on_trigger(event): |
+ return True |
+ if ('GDK_MOD1_MASK' in event.state.value_names |
+ and event.keyval == gtk.keysyms.q): |
+ # Alt-q for early exit. |
+ gtk.main_quit() |
+ return True |
+ if event.keyval in self._successful_keys: |
+ # Ignore keys already found to work successfully. |
+ return True |
+ if event.state != 0: |
+ factory.log('key (0x%x) ignored because modifier applied (state=%d)' |
+ % (event.keyval, event.state)) |
+ return True |
+ if event.keyval not in self._bindings: |
+ factory.log('key (0x%x) ignored because not in bindings' |
+ % event.keyval) |
+ return True |
+ |
+ self._pressed_keys.add(event.keyval) |
+ widget.queue_draw() |
+ |
+ # The first keypress starts test countdown. |
+ if self._deadline is None: |
+ self._deadline = int(time.time()) + _TIMEOUT |
+ |
+ return True |
+ |
+ def key_release_event(self, widget, event): |
+ if event.keyval not in self._pressed_keys: |
+ # Ignore releases for keys not previously accepted as pressed. |
+ return False |
+ self._pressed_keys.remove(event.keyval) |
+ self._successful_keys.add(event.keyval) |
+ if not (set(self._bindings) - self._successful_keys): |
+ self._success = True |
+ self._deadline = int(time.time()) + _PASS_TIMEOUT |
+ widget.queue_draw() |
+ return True |
+ |
+ def register_callbacks(self, window): |
+ window.connect('key-press-event', self.key_press_event) |
+ window.connect('key-release-event', self.key_release_event) |
+ window.add_events(gtk.gdk.KEY_PRESS_MASK | gtk.gdk.KEY_RELEASE_MASK) |
class factory_Keyboard(test.test): |
version = 1 |
preserve_srcdir = True |
- def run_once(self, test_widget_size=None, trigger_set=None, |
- result_file_path=None, layout=None): |
+ def run_once(self, |
+ test_widget_size=None, |
+ trigger_set=None, |
+ result_file_path=None, |
+ layout=None): |
- factory_test.XXX_log('factory_Keyboard') |
+ factory.log('%s run_once' % self.__class__) |
# XXX Why can this not be run from the UI code? |
xset_status = os.system('xset r off') |
@@ -37,17 +160,29 @@ class factory_Keyboard(test.test): |
if xset_status or xmm_status: |
raise TestFail('ERROR: disabling key repeat or caps lock') |
- factory_test.init(trigger_set=trigger_set, |
- result_file_path=result_file_path) |
+ ft_state = ful.State( |
+ trigger_set=trigger_set, |
+ result_file_path=result_file_path) |
os.chdir(self.srcdir) |
+ |
kbd_image = cairo.ImageSurface.create_from_png('%s.png' % layout) |
+ image_size = (kbd_image.get_width(), kbd_image.get_height()) |
+ |
with open('%s.bindings' % layout, 'r') as file: |
bindings = eval(file.read()) |
- test_widget, wr_cb = KeyboardTest.make_test_widget(kbd_image, bindings) |
+ test = KeyboardTest(kbd_image, bindings, ft_state) |
+ |
+ drawing_area = gtk.DrawingArea() |
+ drawing_area.set_size_request(*image_size) |
+ drawing_area.connect('expose_event', test.expose_event) |
+ drawing_area.add_events(gtk.gdk.EXPOSURE_MASK) |
+ gobject.timeout_add(1000, test.timer_event, drawing_area) |
- factory_test.run_test_widget( |
- test_widget=test_widget, |
+ ft_state.run_test_widget( |
+ test_widget=drawing_area, |
test_widget_size=test_widget_size, |
- window_registration_callback=wr_cb) |
+ window_registration_callback=test.register_callbacks) |
+ |
+ factory.log('%s run_once finished' % self.__class__) |