blob: aeb6b2cd3907a7d7e7ed8a83bc7c77360781cd0a [file] [log] [blame]
# -*- encoding: utf-8 -*-
# Copyright 2017 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.
"""A factory test for checking touch uniformity.
This test is intended to be run during run-in without a fixture or operator.
The test recalibrates the touch device then reads raw reference (baseline) data.
Each value must fall within a specified max and min range. Delta values (the
baseline - current reading) are also checked.
Sample test_list entry::
{
"pytest_name": "touch_uniformity",
"args": {
"check_list": [
[0, "i18n! References", 23400, 25100, 0, 0],
[1, "i18n! Deltas", -30, 40, 0, 0]
]
}
}
The args thresholds in need to be experimentally determined by checking
a set of machines. The test logs the actual max and min values found.
"""
import collections
import logging
import factory_common # pylint: disable=unused-import
from cros.factory.device import device_utils
from cros.factory.test import event_log # TODO(chuntsen): Deprecate event log.
from cros.factory.test.i18n import _
from cros.factory.test import test_case
from cros.factory.testlog import testlog
from cros.factory.utils.arg_utils import Arg
from cros.factory.utils import file_utils
from cros.factory.external import numpy
_LABEL_PASS = ['<span class="test-status-passed">', _('PASS'), '</span>']
_LABEL_FAIL = ['<span class="test-status-failed">', _('FAIL'), '</span>']
_MESSAGE_DELAY_SECS = 1
CheckItem = collections.namedtuple(
'CheckItem', ['frame_idx', 'label', 'min_val', 'max_val', 'rows', 'cols'])
class TouchUniformity(test_case.TestCase):
ARGS = [
Arg('device_index', int, 'Index of touch device to test.', default=0),
Arg('check_list', list,
'A list of sequence. Each sequence consists of six elements: '
'frame_idx, label, min_val, max_val, rows, cols.\n'
'frame_idx: Index of frame to check.\n'
'label: A i18n translation dictionary of frame label.\n'
'min_val: Lower bound for values in this frame.\n'
'max_val: Upper bound for values in this frame.\n'
'rows: Number of rows from top to check, or zero to check all.\n'
'cols: Number of columns from left to check, or zero to check all.'),
Arg('keep_raw_logs', bool, 'Whether to attach the log by Testlog',
default=True)]
def setUp(self):
self.dut = device_utils.CreateDUTInterface()
self.controller = self.dut.touch.GetController(self.args.device_index)
self.check_list = [CheckItem(*item) for item in self.args.check_list]
self.ui.ToggleTemplateClass('font-large', True)
# Group checker for Testlog.
self.group_checker = testlog.GroupParam(
'data', ['frame_idx', 'min_value', 'max_value', 'standard_deviation'])
def runTest(self):
self.CheckInterface()
self.Calibrate()
self.CheckRawData()
def CheckInterface(self):
if not self.controller.CheckInterface():
self.ui.SetState([
'<span class="test-status-failed">',
_('ERROR: Touch device not found'), '</span>'
])
self.Sleep(_MESSAGE_DELAY_SECS)
self.FailTask('Touch controller not found.')
def Calibrate(self):
self.ui.SetState(_('Calibrating Touch device'))
if not self.controller.Calibrate():
self.ui.SetState(_LABEL_FAIL, append=True)
self.Sleep(_MESSAGE_DELAY_SECS)
self.FailTask('Touch device calibration failed.')
def _CheckSingleRawData(self, check_item, data):
"""Checks that data is within bounds.
Returns:
True if the data is in bounds.
"""
logging.info('Checking values from frame %d are between %s and %s',
check_item.frame_idx, check_item.min_val, check_item.max_val)
check_passed = True
for row_index in xrange(check_item.rows or len(data)):
for col_index in xrange(check_item.cols or len(data[0])):
val = data[row_index][col_index]
if not check_item.min_val <= val <= check_item.max_val:
logging.info(
'Raw data out of range: [%d, %d] = %s', row_index, col_index, val)
check_passed = False
merged_data = sum(data, [])
actual_min_val = min(merged_data)
actual_max_val = max(merged_data)
standard_deviation = float(numpy.std(merged_data))
logging.info('Lowest value: %s', actual_min_val)
logging.info('Highest value: %s', actual_max_val)
logging.info('Standard deviation %f', standard_deviation)
event_log.Log('touch_%d_stats' % check_item.frame_idx,
allowed_min_val=check_item.min_val,
allowed_max_val=check_item.max_val,
actual_min_val=actual_min_val,
actual_max_val=actual_max_val,
standard_deviation=standard_deviation,
test_passed=check_passed)
with self.group_checker:
testlog.LogParam('frame_idx', check_item.frame_idx)
testlog.LogParam('standard_deviation', standard_deviation)
testlog.CheckNumericParam(
'min_value', actual_min_val, min=check_item.min_val)
testlog.CheckNumericParam(
'max_value', actual_max_val, max=check_item.max_val)
return check_passed
def CheckRawData(self):
matrices = self.controller.GetMatrices(
[item.frame_idx for item in self.check_list])
fails = []
to_log = []
self.ui.SetState('')
for item, matrix in zip(self.check_list, matrices):
status = None
if self._CheckSingleRawData(item, matrix):
status = _LABEL_PASS
to_log.append([dict(item._asdict()), 'PASS', matrix])
else:
status = _LABEL_FAIL
fails.append(item.frame_idx)
to_log.append([dict(item._asdict()), 'FAIL', matrix])
self.ui.SetState(
['<div>',
_('Testing {item}...', item=item.label), status, '</div>'],
append=True)
self.Sleep(_MESSAGE_DELAY_SECS)
if self.args.keep_raw_logs:
with file_utils.UnopenedTemporaryFile() as temp_path:
with open(temp_path, 'w') as f:
for obj in to_log:
f.write('%r\n' % obj)
testlog.AttachFile(
path=temp_path,
name='touch_uniformity.log',
mime_type='text/plain',
description='plain text log of touch_uniformity')
if fails:
self.FailTask('Uniformity check failed on frame %s.' % fails)