1# Copyright 2015 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 collections import namedtuple
6import logging
7import os
8
9import common
10from autotest_lib.client.common_lib import error
11from autotest_lib.server import site_gtest_runner
12from autotest_lib.server import test
13
14
15NATIVE_TESTS_PATH = '/data/nativetest'
16WHITELIST_FILE = '/data/nativetest/tests.txt'
17LIST_TEST_BINARIES_TEMPLATE = (
18        'find %(path)s -type f -mindepth 2 -maxdepth 2 '
19        '\( -perm -100 -o -perm -010 -o -perm -001 \)')
20
21GtestSuite = namedtuple('GtestSuite', ['path', 'run_as_root'])
22
23class brillo_Gtests(test.test):
24    """Run one or more native gTest Suites."""
25    version = 1
26
27
28    def _get_whitelisted_tests(self, whitelist_path):
29        """Return the list of whitelisted tests.
30
31        The whitelist is expected to be a two column CSV file containing the
32        test name and "yes" or "no" whether the test should be run as root or
33        not.
34        Anything after a # on a line is considered to be a comment and  ignored.
35
36        @param whitelist_path: Path to the whitelist.
37
38        @return a list of GtestSuite tuples.
39        """
40        suites = []
41        for line in self.host.run_output(
42                'cat %s' % whitelist_path).splitlines():
43            # Remove anything after the first # (comments).
44            line = line.split('#')[0]
45            if line.strip() == '':
46                continue
47
48            parts = line.split(',')
49            if len(parts) != 2:
50                logging.error('badly formatted line in %s: %s', whitelist_path,
51                              line)
52                continue
53
54            name = parts[0].strip()
55            path = os.path.join(NATIVE_TESTS_PATH, name, name)
56            suites.append(GtestSuite(path, parts[1].strip() == 'yes'))
57        return suites
58
59
60    def _find_all_gtestsuites(self, use_whitelist=False):
61        """Find all the gTest Suites installed on the DUT.
62
63        @param use_whitelist: Only whitelisted tests found on the system will
64                              be used.
65        """
66        list_cmd = LIST_TEST_BINARIES_TEMPLATE % {'path': NATIVE_TESTS_PATH}
67        gtest_suites_path = self.host.run_output(list_cmd).splitlines()
68        gtest_suites = [GtestSuite(path, True) for path in gtest_suites_path]
69
70        if use_whitelist:
71            try:
72                whitelisted = self._get_whitelisted_tests(WHITELIST_FILE)
73                gtest_suites = [t for t in whitelisted
74                                if t.path in gtest_suites_path]
75            except error.AutoservRunError:
76                logging.error('Failed to read whitelist %s', WHITELIST_FILE)
77
78        if not gtest_suites:
79            raise error.TestWarn('No test executables found on the DUT')
80        logging.debug('Test executables found:\n%s',
81                      '\n'.join([str(t) for t in gtest_suites]))
82        return gtest_suites
83
84
85    def run_gtestsuite(self, gtestSuite):
86        """Run a gTest Suite.
87
88        @param gtestSuite: GtestSuite tuple.
89
90        @return True if the all the tests in the gTest Suite pass. False
91                otherwise.
92        """
93        # Make sure the gTest Suite exists.
94        result = self.host.run('test -e %s' % gtestSuite.path,
95                               ignore_status=True)
96        if not result.exit_status == 0:
97            logging.error('Unable to find %s', gtestSuite.path)
98            return False
99
100        result = self.host.run('test -x %s' % gtestSuite.path,
101                               ignore_status=True)
102        if not result.exit_status == 0:
103            self.host.run('chmod +x %s' % gtestSuite.path)
104
105        logging.debug('Running: %s', gtestSuite)
106        command = gtestSuite.path
107        if not gtestSuite.run_as_root:
108          command = 'su shell %s' % command
109
110        result = self.host.run(command, ignore_status=True)
111        logging.debug(result.stdout)
112
113        parser = site_gtest_runner.gtest_parser()
114        for line in result.stdout.splitlines():
115            parser.ProcessLogLine(line)
116        passed_tests = parser.PassedTests()
117        if passed_tests:
118            logging.debug('Passed Tests: %s', passed_tests)
119        failed_tests = parser.FailedTests(include_fails=True,
120                                          include_flaky=True)
121        if failed_tests:
122            logging.error('Failed Tests: %s', failed_tests)
123            for test in failed_tests:
124                logging.error('Test %s failed:\n%s', test,
125                              parser.FailureDescription(test))
126            return False
127        if result.exit_status != 0:
128            logging.error('%s exited with exit code: %s',
129                          gtestSuite, result.exit_status)
130            return False
131        return True
132
133
134    def run_once(self, host=None, gtest_suites=None, use_whitelist=False):
135        """Run gTest Suites on the DUT.
136
137        @param host: host object representing the device under test.
138        @param gtest_suites: List of gTest suites to run. Default is to run
139                             every gTest suite on the host.
140        @param use_whitelist: If gTestSuites is not passed in and use_whitelist
141                              is true, only whitelisted tests found on the
142                              system will be used.
143
144        @raise TestFail: The test failed.
145        """
146        self.host = host
147        if not gtest_suites:
148            gtest_suites = self._find_all_gtestsuites(
149                    use_whitelist=use_whitelist)
150
151        failed_gtest_suites = []
152        for gtestSuite in gtest_suites:
153            if not self.run_gtestsuite(gtestSuite):
154                failed_gtest_suites.append(gtestSuite)
155
156        if failed_gtest_suites:
157            logging.error(
158                    'The following gTest Suites failed: \n %s',
159                    '\n'.join([str(t) for t in failed_gtest_suites]))
160            raise error.TestFail(
161                    'Not all gTest Suites completed successfully. '
162                    '%s out of %s suites failed. '
163                    'Failed Suites: %s'
164                    % (len(failed_gtest_suites),
165                       len(gtest_suites),
166                       failed_gtest_suites))
167