1# Copyright 2018 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
5from __future__ import print_function
6
7import difflib
8import logging
9
10from autotest_lib.client.common_lib import error
11from autotest_lib.server.cros.faft.firmware_test import FirmwareTest
12
13
14class servo_ConsoleStress(FirmwareTest):
15    """Verify the given console by running the same command many times.
16
17    Use the given command to verify the specified console. This command should
18    have output that doesn't change. This test will fail if the command output
19    changes at all or the control fails to run.
20    """
21    version = 1
22
23    # Give the EC some time to enter/resume from hibernate
24    EC_SETTLE_TIME = 10
25
26    def _get_servo_cmd_output(self, cmd):
27        """Get the output from the specified servo control"""
28        return self.servo.get(cmd).strip()
29
30
31    def _get_console_cmd_output(self, cmd):
32        """Run the command on the console specified by the test args."""
33        return self._test_console_obj.send_command_get_output(cmd,
34                ['%s.*>' % cmd])[0].strip()
35
36    def cleanup(self):
37        """Restore the chan settings"""
38        if hasattr(self, '_test_console_obj'):
39            self._test_console_obj.send_command('chan restore')
40        super(servo_ConsoleStress, self).cleanup()
41
42
43    def run_once(self, attempts, cmd_type, cmd):
44        """Make sure cmd output doesn't change during any of the runs."""
45        if cmd_type == 'servo':
46            self._get_test_cmd_output = self._get_servo_cmd_output
47        elif cmd_type in ['ec', 'cr50']:
48            self._test_console_obj = getattr(self, cmd_type)
49            # Set chan to 0, so only console task output will print. This will
50            # prevent other task output from corrupting the command output.
51            self._test_console_obj.send_command('chan save')
52            self._test_console_obj.send_command('chan 0')
53            self._get_test_cmd_output = self._get_console_cmd_output
54        else:
55            raise error.TestError('Invalid cmd type %r' % cmd_type)
56
57        start = self._get_test_cmd_output(cmd)
58        if not start:
59            raise error.TestError('Could not get %s %s output' % (cmd_type,
60                    cmd))
61        logging.info('start output: %r', start)
62
63        # Run the command for the given number of tries. If the output changes
64        # or the command fails to run raise an error.
65        for i in range(attempts):
66            try:
67                output = self._get_test_cmd_output(cmd)
68            except Exception as e:
69                raise error.TestFail('failed to get %s %r during run %d' %
70                        (cmd_type, cmd, i))
71
72            # The command will be run hundreds of times. Print the run number
73            # command output once every hundred runs, so you can tell the test
74            # is progressing but don't spam too much output.
75            if (i % 100) == 0:
76                logging.info('run %d %r', i, start)
77
78            if start != output:
79                logging.info('MISMATCH:\n %s', '\n'.join(difflib.unified_diff(
80                        start.splitlines(), output.splitlines())))
81                raise error.TestFail('%s %r output changed after %d runs' %
82                                     (cmd_type, cmd, i))
83