1# Copyright 2017 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 test
8from autotest_lib.client.common_lib import error
9from autotest_lib.client.common_lib import utils
10
11
12class detachablebase_TriggerHammerd(test.test):
13    """Hammerd smoke test.
14
15    Hammerd upstart job should be invoked on boot. The job should exit normally.
16
17    Also, checks that hammerd enables USB autosuspend on the hammer device.
18    """
19    version = 1
20
21    # The upstart job's name.
22    PROCESS_NAME = 'hammerd'
23
24    # Path of the system log.
25    MESSAGE_PATH = '/var/log/messages'
26
27    # Message to find the start line of each boot. The boot timing is usually
28    # [    0.000000], but in case the boot process has delay, we accept any
29    # timing within 1 second.
30    BOOT_START_LINE_MSG = 'kernel: \[    0\.[0-9]\{6\}\] Linux version'
31
32    # Message that is printed when hammerd is triggered on boot.
33    # It is defined at `src/platform2/hammerd/init/hammerd-at-boot.sh`.
34    TRIGGER_ON_BOOT_MSG = 'Force trigger hammerd at boot.'
35
36    # Message that is printed when the hammerd job failed to terminated
37    # normally.
38    PROCESS_FAILED_MSG = '%s main process ([0-9]\+) terminated' % PROCESS_NAME
39
40    # Hammerd writes path to USB device in that file.
41    WRITE_SYSFS_PATH = '/run/metrics/external/hammer/hammer_sysfs_path'
42
43    # Autosuspend control sysfs PATH (rooted at USB device sysfs path)
44    SYSFS_POWER_CONTROL_PATH = 'power/control'
45
46    def run_once(self):
47        # Get the start line of message belonging to this current boot.
48        start_line = utils.run(
49                'grep -ni "%s" "%s" | tail -n 1 | cut -d ":" -f 1' %
50                (self.BOOT_START_LINE_MSG, self.MESSAGE_PATH),
51                ignore_status=True).stdout.strip()
52        logging.info('Start line: %s', start_line)
53        if not start_line:
54            raise error.TestFail('Start line of boot is not found.')
55
56        def _grep_messages(pattern):
57            return utils.run('tail -n +%s %s | grep "%s"' %
58                             (start_line, self.MESSAGE_PATH, pattern),
59                             ignore_status=True).stdout
60
61        # Check hammerd is triggered on boot.
62        if not _grep_messages(self.TRIGGER_ON_BOOT_MSG):
63            raise error.TestFail('hammerd is not triggered on boot.')
64        # Check hammerd is terminated normally.
65        if _grep_messages(self.PROCESS_FAILED_MSG):
66            hammerd_log = _grep_messages(self.PROCESS_NAME)
67            logging.error('Hammerd log: %s', hammerd_log)
68            raise error.TestFail('hammerd terminated with non-zero value')
69
70        # Check that hammerd wrote to WRITE_SYSFS_PATH
71        sysfspath = utils.read_one_line(self.WRITE_SYSFS_PATH)
72
73        if not sysfspath:
74            raise error.TestFail('%s is empty' % (self.WRITE_SYSFS_PATH))
75
76        # Check that autosuspend is enabled
77        power = utils.read_one_line(sysfspath + '/' +
78                                    self.SYSFS_POWER_CONTROL_PATH)
79        if power != 'auto':
80            raise error.TestFail('Autosuspend not enabled on hammer port')
81