| # 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 the Validators used by the FuzzyCheck class. |
| # The validators mak use of the FuzzyComparator class that allows validations |
| # to be made in a fuzzy way that leads to a score instead of a true/false |
| # decision. |
| import math |
| import re |
| |
| from fuzzy_check import FuzzyComparator |
| |
| def Disabled(fn): |
| """ |
| Function decorator to disable a test case. |
| |
| @Disabled |
| def Validate(): |
| [...] |
| """ |
| fn.disabled = True |
| return fn |
| |
| def FingerSize(*args): |
| """ |
| Function decorator to define finger sizes on the robot instruction method. |
| |
| @FingerSize(1) |
| def InstructRobot(robot): |
| [...] |
| """ |
| def Wrapper(fn): |
| fn.finger_size = args |
| return fn |
| return Wrapper |
| |
| def CustomFinger(fn): |
| """ |
| Function decorator to enforce custom finger setup for an robot instruction |
| method. The robot will start with no finger tips attached and this method |
| has to take care of setting up finger tips as well as cleaning them up again. |
| |
| @CustomFinger |
| def InstructRobot(robot): |
| [...] |
| """ |
| fn.finger_size = None |
| return fn |
| |
| class AbstractValidator(object): |
| """ |
| Base class provides the possibility to Validate start/end times of |
| gestures. |
| """ |
| def __init__(self, start=None, end=None): |
| if start: |
| self._start_check = FuzzyComparator(start) |
| else: |
| self._start_check = None |
| if end: |
| self._end_check = FuzzyComparator(end) |
| else: |
| self._end_check = None |
| |
| self._start = 0 |
| self._end = 0 |
| |
| def Accept(self, event): |
| """ |
| This method is used to determine if this validator is able to validate a |
| certain event. If it cannot validate the event it will be handled by |
| the next validator or is considered as an unexpected event. |
| """ |
| return False |
| |
| def Validate(self, event): |
| """ |
| While this validator is active, this method is called for each gesture that |
| it accepts (i.e. Accept(event) returns True). |
| """ |
| pass |
| |
| def TypeStr(self): |
| """ |
| Returns a shortened string representation used for presentation. It should |
| not contain any validation information but only show which kind of |
| gestures are accepted. |
| """ |
| pass |
| |
| def _ValidateTimestamps(self, event): |
| if self._start == 0: |
| self._start = float(event.start) |
| self._end = float(event.end) |
| |
| def _ScoreTimestamps(self): |
| score = 1 |
| if self._start_check: |
| score = score * self._start_check.Compare(self._start) |
| if self._end_check: |
| score = score * self._end_check.Compare(self._end) |
| return score |
| |
| |
| class AbstractCountValidator(AbstractValidator): |
| """ |
| Base class for buttons. Allows to Validate if a certain number of |
| button events are present. |
| """ |
| |
| def __init__(self, count_match="== 1 ~ 1", start=None, end=None): |
| AbstractValidator.__init__(self, start, end) |
| self._count_match = FuzzyComparator(count_match) |
| self._count = 0 |
| self.score = self._count_match.Compare(self._count) |
| |
| def Accept(self, event): |
| return event.type == self.__class__.event_type |
| |
| def Validate(self, event): |
| self._count = self._count + 1 |
| self.score = self._count_match.Compare(self._count) |
| self._ValidateTimestamps(event) |
| self.score = self.score * self._ScoreTimestamps() |
| |
| def TypeString(self): |
| return self.__class__.event_type |
| |
| def __str__(self): |
| format_str = "{0} c:{1}{2}" |
| return format_str.format(self.TypeString(), self._count, self._count_match) |
| |
| |
| class FlingStopValidator(AbstractCountValidator): |
| event_type = "FlingStop" |
| |
| class SwipeLiftValidator(AbstractCountValidator): |
| event_type = "SwipeLift" |
| |
| class FourFingerSwipeLiftValidator(AbstractCountValidator): |
| event_type = "FourFingerSwipeLift" |
| |
| |
| class AbstractButtonValidator(AbstractCountValidator): |
| """ |
| Base class for buttons. Allows to Validate if a certain number of |
| button events are present. |
| """ |
| |
| def __init__(self, button, count_match="== 1 ~ 1", start=None, end=None): |
| AbstractCountValidator.__init__(self, count_match, start, end) |
| self._button = button |
| |
| def Accept(self, event): |
| return event.type == self.__class__.event_type and \ |
| event.button == self._button |
| |
| def TypeString(self): |
| return self.__class__.event_type + "(" + str(self._button) + ")" |
| |
| |
| class ButtonDownValidator(AbstractButtonValidator): |
| event_type = "ButtonDown" |
| |
| |
| class ButtonUpValidator(AbstractButtonValidator): |
| event_type = "ButtonUp" |
| |
| |
| class AbstractAxisValidator(AbstractValidator): |
| """ |
| Allows to Validate movement on a 2 dimensional axis (scroll, cursor movement, |
| or fling. x and y movement can be validateValidated separately, as well as the |
| overall distance traveled. |
| Next to the overall movement, the Roughness measure of the movement can also |
| be validateValidated. |
| """ |
| def __init__(self, d=None, x=None, y=None, z=None, start=None, end=None, |
| roughness=None, speed=None, merge=False, integralPixels=False): |
| AbstractValidator.__init__(self, start, end) |
| if d: |
| self._distance_check = FuzzyComparator(d) |
| else: |
| self._distance_check = None |
| if x: |
| self._x_check = FuzzyComparator(x) |
| else: |
| self._x_check = None |
| if y: |
| self._y_check = FuzzyComparator(y) |
| else: |
| self._y_check = None |
| if z: |
| self._z_check = FuzzyComparator(z) |
| else: |
| self._z_check = None |
| |
| if speed: |
| self._speed_check = FuzzyComparator(speed) |
| else: |
| self._speed_check = None |
| |
| if roughness: |
| self._roughness_check = FuzzyComparator(roughness) |
| else: |
| self._roughness_check = None |
| |
| self._distance = 0 |
| self._x = 0 |
| self._y = 0 |
| self._z = 0 |
| self._distance_remainder = 0.0 |
| self._roughnesses = [] |
| self._speeds = [] |
| self._UpdateScore() |
| self._merge = merge |
| self._integralPixels = integralPixels |
| |
| def _Speed(self): |
| if self._speeds: |
| return sum(self._speeds) / len(self._speeds) |
| return 0 |
| |
| def Accept(self, event): |
| return (event.type == self.event_type and |
| ((float(event.start) - self._end) < 0.1 or |
| self._end <= 0.0 or |
| self._merge)) |
| |
| def _UpdateScore(self): |
| self.score = 1 |
| if self._distance_check: |
| self.score = self.score * self._distance_check.Compare(self._distance) |
| if self._x_check: |
| self.score = self.score * self._x_check.Compare(self._x) |
| if self._y_check: |
| self.score = self.score * self._y_check.Compare(self._y) |
| if self._z_check: |
| self.score = self.score * self._z_check.Compare(self._z) |
| if self._roughness_check: |
| roughness = sum(self._roughnesses) |
| self.score = self.score * self._roughness_check.Compare(roughness) |
| if self._speed_check: |
| self.score = self.score * self._speed_check.Compare(self._Speed()) |
| self.score = self.score * self._ScoreTimestamps() |
| if self.score == 0: |
| self.score = False |
| |
| def _AddDistance(self, event): |
| if self._integralPixels: |
| distance = self._distance + event.distance + self._distance_remainder |
| self._distance = int(distance) |
| self._distance_remainder = distance - self._distance |
| else: |
| self._distance = self._distance + event.distance |
| |
| def Validate(self, event): |
| self._roughnesses.append(event.Roughness()) |
| self._speeds.append(event.Speed()) |
| self._AddDistance(event) |
| self._x = self._x + event.dx |
| self._y = self._y + event.dy |
| self._z = self._z + event.dz |
| self._ValidateTimestamps(event) |
| self._UpdateScore() |
| |
| def TypeString(self): |
| return self.event_type |
| |
| def __str__(self): |
| valuators = [] |
| if self._distance_check: |
| valuators.append("d:{0:.4g}".format(self._distance) + |
| str(self._distance_check)) |
| if self._x_check: |
| valuators.append("x:{0:.4g}".format(self._x) + str(self._x_check)) |
| if self._y_check: |
| valuators.append("y:{0:.4g}".format(self._y) + str(self._y_check)) |
| if self._z_check: |
| valuators.append("z:{0:.4g}".format(self._z) + str(self._z_check)) |
| if self._roughness_check: |
| valuators.append("r:{0:.4g}".format(sum(self._roughnesses)) + |
| str(self._roughness_check)) |
| if self._speed_check: |
| valuators.append("s:{0:.4g}".format(self._Speed()) + |
| str(self._speed_check)) |
| if self._start_check: |
| valuators.append("start:" + str(self._start) + str(self._start_check)) |
| if self._end_check: |
| valuators.append("end:" + str(self._end) + str(self._end_check)) |
| return self.event_type + " " + (" ".join(valuators)) |
| |
| |
| class FlingValidator(AbstractAxisValidator): |
| event_type = "Fling" |
| |
| |
| class ScrollValidator(AbstractAxisValidator): |
| event_type = "Scroll" |
| |
| |
| class PinchValidator(AbstractAxisValidator): |
| event_type = "Pinch" |
| |
| |
| class SwipeValidator(AbstractAxisValidator): |
| event_type = "Swipe" |
| |
| |
| class FourFingerSwipeValidator(AbstractAxisValidator): |
| event_type = "FourFingerSwipe" |
| |
| |
| class MotionValidator(AbstractAxisValidator): |
| event_type = "Motion" |
| |
| |
| class AnythingButValidator(object): |
| """ |
| Allows to 'wait' for a certain gesture to be performed. It will accept all |
| events exept for the events accepted by the validators passed as arguments. |
| The score of this validator is always 1. |
| """ |
| def __init__(self, *but): |
| self._but = but |
| self.score = 1 |
| |
| def Accept(self, event): |
| for validator in self._but: |
| if validator.Accept(event): |
| return False |
| return True |
| |
| def Validate(self, event): |
| pass |
| |
| def __str__(self): |
| types = map(lambda x: str(x.TypeString()), self._but) |
| return "not(" + " or ".join(types) + ")" |