1# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import logging
6import os
7import re
8import subprocess
9import tempfile
10import time
11
12from autotest_lib.client.bin import test
13from autotest_lib.client.common_lib import error
14from autotest_lib.client.cros.graphics import graphics_utils
15
16
17class hardware_TouchScreenPowerCycles(test.test):
18    """Check if there are any spurious contacts when power is cycled."""
19    version = 1
20
21    SCREEN_ON = 1
22    SCREEN_OFF = 0
23
24    def initialize(self):
25        self.touch_screen_device = self._probe_touch_screen_device()
26        logging.info('Touchscreen device: %s', self.touch_screen_device)
27        if self.touch_screen_device is None:
28            raise error.TestError('No touch screen device is found.')
29
30        # Make sure that the screen is turned on before conducting the test.
31        self._wakeup_screen()
32        self.touch_screen_status = self.SCREEN_ON
33
34    def _wakeup_screen(self):
35        """Wake up the screen if it is dark."""
36        graphics_utils.screen_wakeup()
37        time.sleep(2)
38
39    def _touch_screen_on(self, interval):
40        """Turn the touch screen on."""
41        graphics_utils.switch_screen_on(on=1)
42        self.touch_screen_status = self.SCREEN_ON
43        logging.info('Touchscreen is turned on')
44        time.sleep(interval)
45
46    def _touch_screen_off(self, interval):
47        """Turn the touch screen off."""
48        graphics_utils.switch_screen_on(on=0)
49        self.touch_screen_status = self.SCREEN_OFF
50        logging.info('Touchscreen is turned off')
51        time.sleep(interval)
52
53    def _probe_touch_screen_device(self):
54        """Probe the touch screen device file."""
55        device_info_file = '/proc/bus/input/devices'
56        if not os.path.exists(device_info_file):
57            return None
58        with open(device_info_file) as f:
59            device_info = f.read()
60
61        touch_screen_pattern = re.compile('name=.+%s' % 'Touchscreen', re.I)
62        event_pattern = re.compile('handlers=.*event(\d+)', re.I)
63        found_touch_screen = False
64        touch_screen_device_file = None
65        for line in device_info.splitlines():
66            if (not found_touch_screen and
67                touch_screen_pattern.search(line) is not None):
68                found_touch_screen = True
69            elif found_touch_screen:
70                result = event_pattern.search(line)
71                if result is not None:
72                    event_no = int(result.group(1))
73                    device_file = '/dev/input/event%d' % event_no
74                    if os.path.exists(device_file):
75                        touch_screen_device_file = device_file
76                    break
77        return touch_screen_device_file
78
79    def _begin_recording(self):
80        """Begin a recording process."""
81        record_program = 'evemu-record'
82        record_cmd = '%s %s -1' % (record_program, self.touch_screen_device)
83        self.event_file = tempfile.TemporaryFile()
84        self.rec_proc = subprocess.Popen(record_cmd.split(),
85                                         stdout=self.event_file)
86
87    def _end_recording(self):
88        """Terminate recording process, and read/close the temp event file."""
89        self.rec_proc.terminate()
90        self.rec_proc.wait()
91        self.event_file.seek(0)
92        self.events = self.event_file.readlines()
93        self.event_file.close()
94
95    def _get_timestamp(self, event):
96        """Get the timestamp of an event.
97
98        A device event looks like: "E: 1344225607.043493 0003 0036 202"
99        """
100        result = re.search('E:\s*(\d+(\.\d*)?|\.\d+)', event)
101        timestamp = float(result.group(1)) if result else None
102        return timestamp
103
104    def _get_number_touch_contacts(self):
105        """Get the number of touch contacts.
106
107        Count ABS_MT_TRACKING_ID with a positive ID number but not -1
108        For example:
109            count this event:          "E: 1365999572.107771 0003 0039 405"
110            do not count this event:   "E: 1365999572.107771 0003 0039 -1"
111        """
112        touch_pattern = re.compile('^E:.*\s*0003\s*0039\s*\d+')
113        count_contacts = len(filter(touch_pattern.search, self.events))
114        return count_contacts
115
116    def run_once(self, repeated_times=5, interval=30):
117        """Run through power cycles and check spurious contacts.
118
119        @param repeated_times: the number of power on/off cycles to check.
120        @param interval: the power on/off duration in seconds.
121
122        Turn the power on for 30 seconds, and then turn it off for another
123        30 seconds. Repeat it for 5 times.
124        """
125        count_contacts_list = []
126        count_rounds = 0
127        for _ in range(repeated_times):
128            self._begin_recording()
129            self._touch_screen_off(interval)
130            self._touch_screen_on(interval)
131            self._end_recording()
132            count_contacts = self._get_number_touch_contacts()
133            count_contacts_list.append(count_contacts)
134            if count_contacts > 0:
135                count_rounds += 1
136
137        if count_rounds > 0:
138            msg1 = ('Spurious contacts detected %d out of %d iterations.' %
139                    (count_rounds, repeated_times))
140            msg2 = 'Count of touch contacts: %s' % str(count_contacts_list)
141            ave = float(sum(count_contacts_list)) / len(count_contacts_list)
142            msg3 = 'Average count of touch contacts: %.2f' % ave
143            raise error.TestFail('\n'.join(['', msg1, msg2, msg3]))
144