| # Copyright 2014 The ChromiumOS Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Tests keyboard pin connectivity in SMT factory test. |
| |
| Description |
| ----------- |
| Unlike keyboard test, it only expects a key sequence where keys are the keyboard |
| scan lines' row-column crossing points. It also can trigger a SMT testing |
| fixture to send out signals to simulate key presses on the key sequence. |
| |
| Test Procedure |
| -------------- |
| - A ``keycode_sequence`` is required for the expected key sequence. |
| - If ``bft_fixture`` is set the test calls the fixture to simulate key press |
| events. |
| - Listen to keyboard events, the received keycode must match the expected |
| sequence. |
| |
| Dependency |
| ---------- |
| Depends on 'evdev' module to monitor key presses. |
| |
| Examples |
| -------- |
| Here is an example:: |
| |
| { |
| "pytest_name": "keyboard_smt", |
| "args": { |
| "keycode_sequence": [1, 61, 19, 68, 27, 22, 42, 12, 67, 56, 57, 106, 29], |
| "timeout_secs": 10, |
| "bft_fixture": { ... } |
| } |
| } |
| """ |
| |
| from cros.factory.test.fixture import bft_fixture |
| from cros.factory.test import session |
| from cros.factory.test import test_case |
| from cros.factory.test.utils import evdev_utils |
| from cros.factory.utils.arg_utils import Arg |
| |
| from cros.factory.external.py_lib import evdev |
| |
| |
| class KeyboardSMTTest(test_case.TestCase): |
| """Tests each keyboard scan lines are connected. |
| |
| It triggers a keyboard scan module by sending 0xC1 to fixture via RS-232. |
| The keyboard scan module will send a sequence of keycodes. This test checks |
| if the upcoming keyup events match the expected keycode sequence. |
| """ |
| related_components = (test_case.TestCategory.KEYBOARD, ) |
| |
| ARGS = [ |
| Arg('device_filter', (int, str), |
| 'Keyboard input event id or evdev name.', |
| default=None), |
| Arg('timeout_secs', int, 'Timeout for the test.', default=30), |
| Arg('keycode_sequence', list, |
| 'Expected keycode sequence generated by a keyboard scan module in ' |
| 'the fixture.'), |
| Arg('bft_fixture', dict, bft_fixture.TEST_ARG_HELP, default=None), |
| Arg('debug', bool, |
| 'True to disable timeout and never fail. Used to observe keystrokes.', |
| default=False) |
| ] |
| |
| def setUp(self): |
| # yapf: disable |
| self.debug = self.args.debug # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long |
| # yapf: enable |
| # yapf: disable |
| self.expected_sequence = self.args.keycode_sequence # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long |
| # yapf: enable |
| self.received_sequence = [] |
| |
| self.fixture = None |
| # yapf: disable |
| if self.args.bft_fixture: # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long |
| # yapf: enable |
| # yapf: disable |
| self.fixture = bft_fixture.CreateBFTFixture(**self.args.bft_fixture) # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long |
| # yapf: enable |
| |
| # Get the keyboard input device. |
| # yapf: disable |
| self.event_dev = evdev_utils.FindDevice(self.args.device_filter, # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long |
| # yapf: enable |
| evdev_utils.IsKeyboardDevice) |
| |
| # Monitor keyboard event within specified time period. |
| self.event_dev.grab() |
| self.dispatcher = evdev_utils.InputDeviceDispatcher( |
| # yapf: disable |
| self.event_dev, self.event_loop.CatchException(self.HandleEvdevEvent)) # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long |
| # yapf: enable |
| self.dispatcher.StartDaemon() |
| self.UpdateUI() |
| |
| def tearDown(self): |
| self.dispatcher.Close() |
| self.event_dev.ungrab() |
| |
| def UpdateUI(self): |
| expected_sequence = self.expected_sequence |
| # yapf: disable |
| if not self.debug: # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long |
| # yapf: enable |
| expected_sequence = expected_sequence[len(self.received_sequence):] |
| |
| # yapf: disable |
| self.ui.CallJSFunction('setMatchedSequence', self.received_sequence) # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long |
| # yapf: enable |
| # yapf: disable |
| self.ui.CallJSFunction('setExpectedSequence', expected_sequence) # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long |
| # yapf: enable |
| |
| def HandleEvdevEvent(self, event): |
| """Handles evdev event. |
| |
| Args: |
| event: evdev event. |
| """ |
| # yapf: disable |
| if event.type == evdev.ecodes.EV_KEY and event.value == 0: # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long |
| # yapf: enable |
| self.HandleKey(event.code) |
| |
| def HandleKey(self, key): |
| """Handles keyup event.""" |
| # yapf: disable |
| if self.debug: # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long |
| # yapf: enable |
| session.console.info('keycode: %s', key) |
| self.received_sequence.append(key) |
| else: |
| self.received_sequence.append(key) |
| if key != self.expected_sequence[len(self.received_sequence) - 1]: |
| self.FailTask( |
| f'Keycode sequence mismatches. expected: ' |
| f'{self.expected_sequence!r}, actual: {self.received_sequence!r}.') |
| if self.received_sequence == self.expected_sequence: |
| self.PassTask() |
| self.UpdateUI() |
| |
| def runTest(self): |
| # yapf: disable |
| if not self.debug: # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long |
| # yapf: enable |
| self.ui.StartFailingCountdownTimer(self.args.timeout_secs) |
| if self.fixture: |
| self.fixture.SimulateKeystrokes() |
| self.WaitTaskEnd() |