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