• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1from __future__ import absolute_import
2import os
3import subprocess
4import sys
5
6import lit.Test
7import lit.TestRunner
8import lit.util
9from .base import TestFormat
10
11kIsWindows = sys.platform in ['win32', 'cygwin']
12
13class GoogleTest(TestFormat):
14    def __init__(self, test_sub_dirs, test_suffix):
15        self.test_sub_dirs = str(test_sub_dirs).split(';')
16
17        # On Windows, assume tests will also end in '.exe'.
18        exe_suffix = str(test_suffix)
19        if kIsWindows:
20            exe_suffix += '.exe'
21
22        # Also check for .py files for testing purposes.
23        self.test_suffixes = {exe_suffix, test_suffix + '.py'}
24
25    def getGTestTests(self, path, litConfig, localConfig):
26        """getGTestTests(path) - [name]
27
28        Return the tests available in gtest executable.
29
30        Args:
31          path: String path to a gtest executable
32          litConfig: LitConfig instance
33          localConfig: TestingConfig instance"""
34
35        list_test_cmd = self.maybeAddPythonToCmd([path, '--gtest_list_tests'])
36
37        try:
38            output = subprocess.check_output(list_test_cmd,
39                                             env=localConfig.environment)
40        except subprocess.CalledProcessError as exc:
41            litConfig.warning(
42                "unable to discover google-tests in %r: %s. Process output: %s"
43                % (path, sys.exc_info()[1], exc.output))
44            # This doesn't look like a valid gtest file.  This can
45            # have a number of causes, none of them good.  For
46            # instance, we could have created a broken executable.
47            # Alternatively, someone has cruft in their test
48            # directory.  If we don't return a test here, then no
49            # failures will get reported, so return a dummy test name
50            # so that the failure is reported later.
51            yield 'failed_to_discover_tests_from_gtest'
52            return
53
54        nested_tests = []
55        for ln in output.splitlines(False):  # Don't keep newlines.
56            ln = lit.util.to_string(ln)
57
58            if 'Running main() from gtest_main.cc' in ln:
59                # Upstream googletest prints this to stdout prior to running
60                # tests. LLVM removed that print statement in r61540, but we
61                # handle it here in case upstream googletest is being used.
62                continue
63
64            # The test name list includes trailing comments beginning with
65            # a '#' on some lines, so skip those. We don't support test names
66            # that use escaping to embed '#' into their name as the names come
67            # from C++ class and method names where such things are hard and
68            # uninteresting to support.
69            ln = ln.split('#', 1)[0].rstrip()
70            if not ln.lstrip():
71                continue
72
73            index = 0
74            while ln[index*2:index*2+2] == '  ':
75                index += 1
76            while len(nested_tests) > index:
77                nested_tests.pop()
78
79            ln = ln[index*2:]
80            if ln.endswith('.'):
81                nested_tests.append(ln)
82            elif any([name.startswith('DISABLED_')
83                      for name in nested_tests + [ln]]):
84                # Gtest will internally skip these tests. No need to launch a
85                # child process for it.
86                continue
87            else:
88                yield ''.join(nested_tests) + ln
89
90    def getTestsInDirectory(self, testSuite, path_in_suite,
91                            litConfig, localConfig):
92        source_path = testSuite.getSourcePath(path_in_suite)
93        for subdir in self.test_sub_dirs:
94            dir_path = os.path.join(source_path, subdir)
95            if not os.path.isdir(dir_path):
96                continue
97            for fn in lit.util.listdir_files(dir_path,
98                                             suffixes=self.test_suffixes):
99                # Discover the tests in this executable.
100                execpath = os.path.join(source_path, subdir, fn)
101                testnames = self.getGTestTests(execpath, litConfig, localConfig)
102                for testname in testnames:
103                    testPath = path_in_suite + (subdir, fn, testname)
104                    yield lit.Test.Test(testSuite, testPath, localConfig,
105                                        file_path=execpath)
106
107    def execute(self, test, litConfig):
108        testPath,testName = os.path.split(test.getSourcePath())
109        while not os.path.exists(testPath):
110            # Handle GTest parametrized and typed tests, whose name includes
111            # some '/'s.
112            testPath, namePrefix = os.path.split(testPath)
113            testName = namePrefix + '/' + testName
114
115        cmd = [testPath, '--gtest_filter=' + testName]
116        cmd = self.maybeAddPythonToCmd(cmd)
117        if litConfig.useValgrind:
118            cmd = litConfig.valgrindArgs + cmd
119
120        if litConfig.noExecute:
121            return lit.Test.PASS, ''
122
123        try:
124            out, err, exitCode = lit.util.executeCommand(
125                cmd, env=test.config.environment,
126                timeout=litConfig.maxIndividualTestTime)
127        except lit.util.ExecuteCommandTimeoutException:
128            return (lit.Test.TIMEOUT,
129                    'Reached timeout of {} seconds'.format(
130                        litConfig.maxIndividualTestTime)
131                   )
132
133        if exitCode:
134            return lit.Test.FAIL, out + err
135
136        passing_test_line = '[  PASSED  ] 1 test.'
137        if passing_test_line not in out:
138            msg = ('Unable to find %r in gtest output:\n\n%s%s' %
139                   (passing_test_line, out, err))
140            return lit.Test.UNRESOLVED, msg
141
142        return lit.Test.PASS,''
143
144    def maybeAddPythonToCmd(self, cmd):
145        """Insert the python exe into the command if cmd[0] ends in .py
146
147        We cannot rely on the system to interpret shebang lines for us on
148        Windows, so add the python executable to the command if this is a .py
149        script.
150        """
151        if cmd[0].endswith('.py'):
152            return [sys.executable] + cmd
153        return cmd
154