blob: ab893e98f677620e581e452c534b62c04e73ac50 [file] [log] [blame]
# Copyright (c) 2012 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.
#
"""This module contains unit tests for the classes in the validators module."""
import os.path
import unittest
import common_unittest_utils
import common_util
import test_conf as conf
from common_unittest_utils import MockTouchpadDevice, parse_tests_data
from firmware_constants import GV
from validators import (CountPacketsValidator,
CountTrackingIDValidator,
DrumrollValidator,
LinearityValidator,
NoGapValidator,
NoLevelJumpValidator,
NoReversedMotionValidator,
PhysicalClickValidator,
PinchValidator,
RangeValidator,
ReportRateValidator,
StationaryFingerValidator,
)
class BaseValidatorTest(unittest.TestCase):
"""A base class for all ValidatorTest classes."""
def setUp(self):
"""Set up mocked devices for various test boards."""
# define supported platforms
self.ALEX= 'alex'
self.LUMPY = 'lumpy'
self.LINK = 'link'
self.supported_platforms = [self.ALEX, self.LUMPY, self.LINK]
self.mock_device = {}
description_path = common_unittest_utils.get_device_description_path()
for platform in self.supported_platforms:
description_filename = '%s.device' % platform
description_filepath = os.path.join(description_path,
description_filename)
if not os.path.isfile(description_filepath):
self.mock_device[platform] = None
warn_msg = 'Warning: device description file %s does not exist'
print msg % description_filepath
continue
query_cmd = 'cat %s' % description_filepath
device_description = common_util.simple_system_output(query_cmd)
self.mock_device[platform] = MockTouchpadDevice(device_description)
class CountTrackingIDValidatorTest(BaseValidatorTest):
"""Unit tests for CountTrackingIDValidator class."""
def setUp(self):
super(CountTrackingIDValidatorTest, self).setUp()
def _test_count_tracking_id(self, filename, criteria, device):
packets = parse_tests_data(filename)
validator = CountTrackingIDValidator(criteria, device=device)
vlog = validator.check(packets)
return vlog.get_score()
def test_two_finger_id_change(self):
"""Two two fingers id change.
Issue 7867: Cyapa : Two finger scroll, tracking ids change
"""
filename = 'two_finger_id_change.dat'
score = self._test_count_tracking_id(filename, '== 2',
self.mock_device[self.LUMPY])
self.assertTrue(score == 0)
def test_one_finger_fast_swipe_id_split(self):
"""One finger fast swipe resulting in IDs split.
Issue: 7869: Lumpy: Tracking ID reassigned during quick-2F-swipe
"""
filename = 'one_finger_fast_swipe_id_split.dat'
score = self._test_count_tracking_id(filename, '== 1',
self.mock_device[self.LUMPY])
self.assertTrue(score == 0)
def test_two_fingers_fast_flick_id_split(self):
"""Two figners fast flick resulting in IDs split.
Issue: 7869: Lumpy: Tracking ID reassigned during quick-2F-swipe
"""
filename = 'two_finger_fast_flick_id_split.dat'
score = self._test_count_tracking_id(filename, '== 2',
self.mock_device[self.LUMPY])
self.assertTrue(score == 0)
class DrumrollValidatorTest(BaseValidatorTest):
"""Unit tests for DrumrollValidator class."""
def setUp(self):
super(DrumrollValidatorTest, self).setUp()
self.criteria = conf.drumroll_criteria
def _test_drumroll(self, filename, criteria, device):
packets = parse_tests_data(filename)
validator = DrumrollValidator(criteria, device=device)
vlog = validator.check(packets)
return vlog.get_score()
def test_drumroll_lumpy(self):
"""Should catch the drumroll on lumpy.
Issue 7809: Lumpy: Drumroll bug in firmware
"""
filename = 'drumroll_lumpy.dat'
score = self._test_drumroll(filename, self.criteria,
self.mock_device[self.LUMPY])
self.assertTrue(score == 0)
def test_drumroll_lumpy_1(self):
"""Should catch the drumroll on lumpy.
Issue 7809: Lumpy: Drumroll bug in firmware
"""
filename = 'drumroll_lumpy_1.dat'
score = self._test_drumroll(filename, self.criteria,
self.mock_device[self.LUMPY])
self.assertTrue(score == 0)
def test_no_drumroll_link(self):
"""Should pass (score == 1) when there is no drumroll.
Issue 7809: Lumpy: Drumroll bug in firmware
"""
filename = 'no_drumroll_link.dat'
score = self._test_drumroll(filename, self.criteria,
self.mock_device[self.LINK])
self.assertTrue(score == 1)
class LinearityValidatorTest(BaseValidatorTest):
"""Unit tests for LinearityValidator class."""
def setUp(self):
super(LinearityValidatorTest, self).setUp()
self.criteria = conf.linearity_criteria
def _test_linearity_criteria(self, criteria_str, slots, device):
filename = '2f_scroll_diagonal.dat'
direction = GV.DIAGONAL
packets = parse_tests_data(filename)
scores = {}
for slot in slots:
validator = LinearityValidator(criteria_str, device=device,
slot=slot)
scores[slot] = validator.check(packets, direction).get_score()
return scores
def test_linearity_criteria0(self):
"""The scores are 0s due to strict criteria."""
criteria_str = '<= 0.01, ~ +0.01'
scores = self._test_linearity_criteria(criteria_str, (0, 1),
self.mock_device[self.ALEX])
self.assertTrue(scores[0] == 0)
self.assertTrue(scores[1] == 0)
def test_linearity_criteria1(self):
"""The validator gets score betwee 0 and 1."""
criteria_str = '<= 0.01, ~ +3.0'
scores = self._test_linearity_criteria(criteria_str, (0, 1),
self.mock_device[self.ALEX])
self.assertTrue(scores[0] > 0 and scores[0] < 1)
self.assertTrue(scores[1] > 0 and scores[1] < 1)
def test_linearity_criteria2(self):
"""The validator gets score of 1 due to very relaxed criteria."""
criteria_str = '<= 10, ~ +10'
scores = self._test_linearity_criteria(criteria_str, (0, 1),
self.mock_device[self.ALEX])
self.assertTrue(scores[0] == 1)
self.assertTrue(scores[1] == 1)
def _test_linearity_validator(self, filename, criteria, slots, device,
direction):
packets = parse_tests_data(filename)
scores = {}
if isinstance(slots, int):
slots = (slots,)
for slot in slots:
validator = LinearityValidator(criteria, device=device, slot=slot)
scores[slot] = validator.check(packets, direction).get_score()
return scores
def test_two_finger_jagged_lines(self):
"""Test two-finger jagged lines."""
filename = 'two_finger_tracking.diagonal.slow.dat'
scores = self._test_linearity_validator(filename, self.criteria, (0, 1),
self.mock_device[self.LUMPY], GV.DIAGONAL)
self.assertTrue(scores[0] < 0.7)
self.assertTrue(scores[1] < 0.7)
def test_stationary_finger_fat_finger_wobble(self):
"""Test fat finger horizontal move with a stationary resting finger
results in a wobble.
Issue 7551: Fat finger horizontal move with a stationary resting
finger results in a wobble.
"""
filename = 'stationary_finger_fat_finger_wobble.dat'
scores = self._test_linearity_validator(filename, self.criteria, 1,
self.mock_device[self.LUMPY], GV.HORIZONTAL)
self.assertTrue(scores[1] <= 0.1)
def test_thumb_edge(self):
"""Test thumb edge wobble.
Issue 7554: thumb edge behavior.
"""
filename = 'thumb_edge_wobble.dat'
scores = self._test_linearity_validator(filename, self.criteria, 0,
self.mock_device[self.LUMPY], GV.HORIZONTAL)
self.assertTrue(scores[0] < 0.5)
def test_two_close_fingers_merging_changed_ids_gaps(self):
"""Test close finger merging - causes id changes
Issue 7555: close finger merging - causes id changes.
"""
filename = 'two_close_fingers_merging_changed_ids_gaps.dat'
scores = self._test_linearity_validator(filename, self.criteria, 0,
self.mock_device[self.LUMPY], GV.VERTICAL)
self.assertTrue(scores[0] < 0.3)
def test_jagged_two_finger_scroll(self):
"""Test jagged two finger scroll.
Issue 7650: Cyapa : poor two fat fingers horizontal scroll performance -
jagged lines
"""
filename = 'jagged_two_finger_scroll_horizontal.dat'
scores = self._test_linearity_validator(filename, self.criteria, (0, 1),
self.mock_device[self.LUMPY], GV.HORIZONTAL)
self.assertTrue(scores[0] < 0.3)
self.assertTrue(scores[1] < 0.3)
def test_first_point_jump(self):
"""Test the first point jump
At slot 0, the positions of (x, y) looks like
x: 208, 241, 242, 245, 246, ...
y: 551, 594, 595, 597, 598, ...
Note that the the first y position is a jump.
"""
filename = 'two_finger_tracking.bottom_left_to_top_right.slow.dat'
scores = self._test_linearity_validator(filename, self.criteria, 0,
self.mock_device[self.LUMPY], GV.DIAGONAL)
self.assertTrue(scores[0] < 0.3)
def test_simple_linear_regression0(self):
device = self.mock_device[self.LUMPY]
validator = LinearityValidator('<= 0.2, ~ +0.3', device=device, slot=0)
validator.init_check()
# A perfect line from bottom left to top right
list_x = [1, 2, 3, 4, 5, 6, 7, 8]
list_y = [20, 40, 60, 80, 100, 120, 140, 160]
spmse = validator._simple_linear_regression(list_x, list_y)
self.assertEqual(spmse, 0)
def test_simple_linear_regression1(self):
device = self.mock_device[self.LUMPY]
validator = LinearityValidator('<= 0.2, ~ +0.3', device=device, slot=0)
validator.init_check()
# Another perfect line from top left to bottom right
list_x = [1, 2, 3, 4, 5, 6, 7, 8]
list_y = [160, 140, 120, 100, 80, 60, 40, 20]
spmse = validator._simple_linear_regression(list_x, list_y)
self.assertEqual(spmse, 0)
def test_simple_linear_regression2(self):
device = self.mock_device[self.LUMPY]
validator = LinearityValidator('<= 0.2, ~ +0.3', device=device, slot=0)
validator.init_check()
# An outlier in y axis
list_x = [1, 2, 3, 4, 5, 6, 7, 8]
list_y = [20, 40, 60, 70, 100, 120, 140, 160]
spmse = validator._simple_linear_regression(list_x, list_y)
self.assertTrue(spmse > 0)
def test_simple_linear_regression3(self):
device = self.mock_device[self.LUMPY]
validator = LinearityValidator('<= 0.2, ~ +0.3', device=device, slot=0)
validator.init_check()
# Repeated values in x axis
list_x = [1, 2, 2, 4, 5, 6, 7, 8]
list_y = [20, 40, 60, 80, 100, 120, 140, 160]
spmse = validator._simple_linear_regression(list_x, list_y)
self.assertTrue(spmse > 0)
class NoGapValidatorTest(BaseValidatorTest):
"""Unit tests for NoGapValidator class."""
GAPS_SUBDIR = 'gaps'
def setUp(self):
super(NoGapValidatorTest, self).setUp()
self.criteria = conf.no_gap_criteria
def _test_no_gap(self, filename, criteria, device, slot):
file_subpath = os.path.join(self.GAPS_SUBDIR, filename)
packets = parse_tests_data(file_subpath)
validator = NoGapValidator(criteria, device=device, slot=slot)
vlog = validator.check(packets)
return vlog.get_score()
def test_two_finger_scroll_gaps(self):
"""Test that there are gaps in the two finger scroll gesture.
Issue 7552: Cyapa : two finger scroll motion produces gaps in tracking
"""
filename = 'two_finger_gaps.horizontal.dat'
mock_device = self.mock_device[self.LUMPY]
score0 = self._test_no_gap(filename, self.criteria, mock_device, 0)
score1 = self._test_no_gap(filename, self.criteria, mock_device, 1)
self.assertTrue(score0 <= 0.1)
self.assertTrue(score1 <= 0.1)
def test_gap_new_finger_arriving_or_departing(self):
"""Test gap when new finger arriving or departing.
Issue: 8005: Cyapa : gaps appear when new finger arrives or departs
"""
filename = 'gap_new_finger_arriving_or_departing.dat'
mock_device = self.mock_device[self.LUMPY]
score = self._test_no_gap(filename, self.criteria, mock_device, 0)
self.assertTrue(score <= 0.3)
def test_one_stationary_finger_2nd_finger_moving_gaps(self):
"""Test one stationary finger resulting in 2nd finger moving gaps."""
filename = 'one_stationary_finger_2nd_finger_moving_gaps.dat'
mock_device = self.mock_device[self.LUMPY]
score = self._test_no_gap(filename, self.criteria, mock_device, 1)
self.assertTrue(score <= 0.1)
def test_resting_finger_2nd_finger_moving_gaps(self):
"""Test resting finger resulting in 2nd finger moving gaps.
Issue 7648: Cyapa : Resting finger plus one finger move generates a gap
"""
filename = 'resting_finger_2nd_finger_moving_gaps.dat'
mock_device = self.mock_device[self.LUMPY]
score = self._test_no_gap(filename, self.criteria, mock_device, 1)
self.assertTrue(score <= 0.3)
class StationaryFingerValidatorTest(BaseValidatorTest):
"""Unit tests for LinearityValidator class."""
def setUp(self):
super(StationaryFingerValidatorTest, self).setUp()
self.criteria = conf.stationary_finger_criteria
def _test_stationary_finger(self, filename, criteria, device):
packets = parse_tests_data(filename)
validator = StationaryFingerValidator(criteria, device=device)
vlog = validator.check(packets)
return vlog.get_score()
def test_stationary_finger_shift(self):
"""Test that the stationary shift due to 2nd finger tapping.
Issue 7442: Cyapa : Second finger tap events influence stationary finger
position
"""
filename = 'stationary_finger_shift_with_2nd_finger_tap.dat'
device = self.mock_device[self.LUMPY]
score = self._test_stationary_finger(filename, self.criteria, device)
self.assertTrue(score <= 0.1)
def test_stationary_strongly_affected_by_2nd_moving_finger(self):
"""Test stationary finger strongly affected by 2nd moving finger with
gaps.
Issue 5812: [Cypress] reported positions of stationary finger strongly
affected by nearby moving finger
"""
filename = ('stationary_finger_strongly_affected_by_2nd_moving_finger_'
'with_gaps.dat')
device = self.mock_device[self.LUMPY]
score = self._test_stationary_finger(filename, self.criteria, device)
self.assertTrue(score <= 0.1)
class NoLevelJumpValidatorTest(BaseValidatorTest):
"""Unit tests for NoLevelJumpValidator class."""
def setUp(self):
super(NoLevelJumpValidatorTest, self).setUp()
self.criteria = conf.no_level_jump_criteria
self.gesture_dir = 'drag_edge_thumb'
def _get_score(self, filename, device):
validator = NoLevelJumpValidator(self.criteria, device=device,
slots=[0,])
packets = parse_tests_data(filename, gesture_dir=self.gesture_dir)
vlog = validator.check(packets)
score = vlog.get_score()
return score
def test_level_jumps(self):
"""Test files with level jumps."""
filenames = [
'drag_edge_thumb.horizontal.dat',
'drag_edge_thumb.horizontal_2.dat',
'drag_edge_thumb.horizontal_3.no_points.dat',
'drag_edge_thumb.vertical.dat',
'drag_edge_thumb.vertical_2.dat',
'drag_edge_thumb.diagonal.dat',
]
device = self.mock_device[self.LUMPY]
for filename in filenames:
self.assertTrue(self._get_score(filename, device) <= 0.6)
def test_no_level_jumps(self):
"""Test files without level jumps."""
filenames = [
'drag_edge_thumb.horizontal.curvy.dat',
'drag_edge_thumb.horizontal_2.curvy.dat',
'drag_edge_thumb.vertical.curvy.dat',
'drag_edge_thumb.vertical_2.curvy.dat',
]
device = self.mock_device[self.LUMPY]
for filename in filenames:
self.assertTrue(self._get_score(filename, device) == 1.0)
class ReportRateValidatorTest(BaseValidatorTest):
"""Unit tests for ReportRateValidator class."""
def setUp(self):
super(ReportRateValidatorTest, self).setUp()
self.criteria = conf.report_rate_criteria
def _get_score(self, filename, device):
validator = ReportRateValidator(self.criteria, device=device)
packets = parse_tests_data(filename)
vlog = validator.check(packets)
score = vlog.get_score()
return score
def test_level_jumps(self):
"""Test files with level jumps."""
lumpy = self.mock_device[self.LUMPY]
filename = '2f_scroll_diagonal.dat'
self.assertTrue(self._get_score(filename, device=lumpy) <= 0.5)
filename = 'one_finger_with_slot_0.dat'
self.assertTrue(self._get_score(filename, device=lumpy) >= 0.9)
filename = 'two_close_fingers_merging_changed_ids_gaps.dat'
self.assertTrue(self._get_score(filename, device=lumpy) <= 0.5)
if __name__ == '__main__':
unittest.main()