1# Copyright (c) 2014 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
6
7from autotest_lib.client.bin import utils
8from autotest_lib.client.common_lib import error
9from autotest_lib.client.cros import touch_playback_test_base
10
11
12class touch_UpdateErrors(touch_playback_test_base.touch_playback_test_base):
13    """Check that touch update is tried and that there are no update errors."""
14    version = 1
15
16    # Older devices with Synaptics touchpads do not report firmware updates.
17    _INVALID_BOARDS = ['x86-alex', 'x86-alex_he', 'x86-zgb', 'x86-zgb_he',
18                       'x86-mario', 'stout']
19
20    # Devices which have errors in older builds but not newer ones.
21    _IGNORE_OLDER_LOGS = ['expresso', 'enguarde', 'cyan']
22
23    def _find_logs_start_line(self):
24        """Find where in /var/log/messages this build's logs start.
25
26        Prevent bugs such as crosbug.com/p/31012, where unfixable errors from
27        FSI builds remain in the logs.
28
29        For devices where this applies, split the logs by Linux version.  Since
30        this line can repeat, find the last chunk of logs where the version is
31        all the same - all for the build under test.
32
33        @returns: string of the line number to start looking at logs
34
35        """
36        if self._platform not in self._IGNORE_OLDER_LOGS:
37            return '0'
38
39        log_cmd = 'grep -ni "Linux version " /var/log/messages'
40
41        version_entries = utils.run(log_cmd).stdout.strip().split('\n')
42
43        # Separate the line number and the version date (i.e. remove timestamp).
44        lines, dates = [], []
45        for entry in version_entries:
46            lines.append(entry[:entry.find(':')])
47            dates.append(entry[entry.find('Linux version '):])
48        latest = dates[-1]
49        start_line = lines[-1]
50
51        # Find where logs from this build start by checking backwards for the
52        # first change in build.  Some of these dates may be duplicated.
53        for i in xrange(len(lines)-1, -1, -1):
54            if dates[i] != latest:
55                break
56            start_line = lines[i]
57
58        return start_line
59
60    def _check_updates(self, input_type):
61        """Fail the test if device has problems with touch firmware update.
62
63        @param input_type: string of input type, e.g. 'touchpad'
64
65        @raises: TestFail if no update attempt occurs or if there is an error.
66
67        """
68        hw_id = self.player.devices[input_type].hw_id
69        if not hw_id:
70            raise error.TestError('%s has no valid hw_id!' % input_type)
71
72        start_line = self._find_logs_start_line()
73        log_cmd = 'tail -n +%s /var/log/messages | grep -i touch' % start_line
74
75        pass_terms = ['touch-firmware-update',
76                      '"Product[^a-z0-9]ID[^a-z0-9]*%s"' % hw_id]
77        fail_terms = ['error[^s]', 'err[^a-z]']
78
79        # Check for key terms in touch logs.
80        for term in pass_terms + fail_terms:
81            search_cmd = '%s | grep -i %s' % (log_cmd, term)
82            log_entries = utils.run(search_cmd, ignore_status=True).stdout
83            if term in fail_terms and len(log_entries) > 0:
84                error_msg = log_entries.split('\n')[0]
85                error_msg = error_msg[error_msg.find(term)+len(term):].strip()
86                raise error.TestFail(error_msg)
87            if term in pass_terms and len(log_entries) == 0:
88                logging.info('Did not find "%s"!', term)
89                raise error.TestFail('Touch firmware did not attempt update.')
90
91    def run_once(self, input_type='touchpad'):
92        """Entry point of this test."""
93        if not self.player.has(input_type):
94            raise error.TestError('No %s found on this device!' % input_type)
95
96        # Skip run on invalid touch inputs.
97        if self._platform in self._INVALID_BOARDS:
98            logging.info('This touchpad is not supported for this test.')
99            return
100
101        self._check_updates(input_type)
102