blob: 535b38f9af3a6ae8df5a514b18147ce8bdc9cf5f [file] [log] [blame]
# Copyright 2016 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import unittest
import factory_common # pylint: disable=unused-import
from cros.factory.external import evdev
from cros.factory.test.i18n import test_ui as i18n_test_ui
from cros.factory.test import test_ui
from cros.factory.test import ui_templates
from cros.factory.test.utils import evdev_utils
from cros.factory.utils.arg_utils import Arg
_ID_CANVAS = 'stylus-test-canvas'
_HTML = '<canvas id="%s" style="display: none"></canvas>' % _ID_CANVAS
_MSG_PROMPT_CSS_CLASS = 'stylus-test-css-class'
_MSG_PROMPT_CSS = '.%s { font-size: 2em; }' % _MSG_PROMPT_CSS_CLASS
_MSG_PROMPT = i18n_test_ui.MakeI18nLabelWithClass(
'Please draw a line with stylus from bottom left corner to top right '
'corner. Stay between the two red lines.<br>'
'Press SPACE to start; Esc to fail.', _MSG_PROMPT_CSS_CLASS)
class _AbsInfo(object):
def __init__(self, absinfo):
self.value = absinfo.value
self.offset = absinfo.min
self.range = float(absinfo.max - absinfo.min)
def Update(self, value):
self.value = value
def GetNormalizedValue(self):
return min(max(0.0, (self.value - self.offset) / self.range), 1.0)
class StylusTest(unittest.TestCase):
"""Stylus factory test."""
ARGS = [
Arg('stylus_event_id', int, 'Stylus input event id.', optional=True),
Arg('error_margin', int,
'Maximum tolerable distance to the diagonal line (in pixel).',
default=25),
Arg('begin_position', float,
'The beginning position of the diagnoal line segment to check. '
'Should be in (0, 1).',
default=0.01),
Arg('end_position', float,
'The ending position of the diagnoal line segment to check. '
'Should be in (0, 1).',
default=0.99),
Arg('step_size', float,
'If the distance between an input event to the latest accepted '
'input event is larger than this size, it would be ignored. '
'Should be in (0, 1).',
default=0.01)]
def setUp(self):
if self.args.stylus_event_id is not None:
self._device = evdev.InputDevice(
'/dev/input/event%d' % self.args.stylus_event_id)
else:
candidates = evdev_utils.GetStylusDevices()
assert len(candidates) == 1, 'Not having exactly one candidate.'
self._device = candidates[0]
abscaps = {
pair[0]: pair[1]
for pair in self._device.capabilities()[evdev.ecodes.EV_ABS]}
self._x = _AbsInfo(abscaps[evdev.ecodes.ABS_X])
self._y = _AbsInfo(abscaps[evdev.ecodes.ABS_Y])
self._touch = False
self._dispatcher = None
assert self.args.error_margin >= 0
assert 0 < self.args.begin_position < self.args.end_position < 1
assert 0 < self.args.step_size < 1
self._ui = test_ui.UI()
self._template = ui_templates.OneSection(self._ui)
self._ui.AppendCSS(_MSG_PROMPT_CSS)
self._template.SetState(_MSG_PROMPT)
def _Handler(self, event):
if event.type == evdev.ecodes.EV_ABS:
if event.code == evdev.ecodes.ABS_X:
self._x.Update(event.value)
elif event.code == evdev.ecodes.ABS_Y:
self._y.Update(event.value)
elif event.type == evdev.ecodes.EV_KEY:
if event.code == evdev.ecodes.BTN_TOUCH:
self._touch = event.value != 0
elif event.type == evdev.ecodes.EV_SYN:
if self._touch:
self._ui.CallJSFunction(
'handler',
self._x.GetNormalizedValue(),
self._y.GetNormalizedValue())
def _StartTest(self, _):
self._ui.SetHTML(_HTML)
self._ui.CallJSFunction('setupStylusTest',
_ID_CANVAS,
self.args.error_margin,
self.args.begin_position,
self.args.end_position,
self.args.step_size)
self._ui.CallJSFunction('startTest')
self._dispatcher = evdev_utils.InputDeviceDispatcher(self._device,
self._Handler)
self._dispatcher.StartDaemon()
def tearDown(self):
if self._dispatcher is not None:
self._dispatcher.close()
def runTest(self):
self._ui.BindKeyJS(test_ui.ESCAPE_KEY, 'failTest();')
self._ui.BindKey(test_ui.SPACE_KEY, self._StartTest)
self._ui.Run()