1from __future__ import absolute_import 2import os 3import subprocess 4import sys 5 6import lit10.Test 7import lit10.TestRunner 8import lit10.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 = os.path.normcase(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 return 45 46 nested_tests = [] 47 for ln in output.splitlines(False): # Don't keep newlines. 48 ln = lit10.util.to_string(ln) 49 50 if 'Running main() from gtest_main.cc' in ln: 51 # Upstream googletest prints this to stdout prior to running 52 # tests. LLVM removed that print statement in r61540, but we 53 # handle it here in case upstream googletest is being used. 54 continue 55 56 # The test name list includes trailing comments beginning with 57 # a '#' on some lines, so skip those. We don't support test names 58 # that use escaping to embed '#' into their name as the names come 59 # from C++ class and method names where such things are hard and 60 # uninteresting to support. 61 ln = ln.split('#', 1)[0].rstrip() 62 if not ln.lstrip(): 63 continue 64 65 index = 0 66 while ln[index*2:index*2+2] == ' ': 67 index += 1 68 while len(nested_tests) > index: 69 nested_tests.pop() 70 71 ln = ln[index*2:] 72 if ln.endswith('.'): 73 nested_tests.append(ln) 74 elif any([name.startswith('DISABLED_') 75 for name in nested_tests + [ln]]): 76 # Gtest will internally skip these tests. No need to launch a 77 # child process for it. 78 continue 79 else: 80 yield ''.join(nested_tests) + ln 81 82 def getTestsInDirectory(self, testSuite, path_in_suite, 83 litConfig, localConfig): 84 source_path = testSuite.getSourcePath(path_in_suite) 85 for subdir in self.test_sub_dirs: 86 dir_path = os.path.join(source_path, subdir) 87 if not os.path.isdir(dir_path): 88 continue 89 for fn in lit10.util.listdir_files(dir_path, 90 suffixes=self.test_suffixes): 91 # Discover the tests in this executable. 92 execpath = os.path.join(source_path, subdir, fn) 93 testnames = self.getGTestTests(execpath, litConfig, localConfig) 94 for testname in testnames: 95 testPath = path_in_suite + (subdir, fn, testname) 96 yield lit10.Test.Test(testSuite, testPath, localConfig, 97 file_path=execpath) 98 99 def execute(self, test, litConfig): 100 testPath,testName = os.path.split(test.getSourcePath()) 101 while not os.path.exists(testPath): 102 # Handle GTest parametrized and typed tests, whose name includes 103 # some '/'s. 104 testPath, namePrefix = os.path.split(testPath) 105 testName = namePrefix + '/' + testName 106 107 cmd = [testPath, '--gtest_filter=' + testName] 108 cmd = self.maybeAddPythonToCmd(cmd) 109 if litConfig.useValgrind: 110 cmd = litConfig.valgrindArgs + cmd 111 112 if litConfig.noExecute: 113 return lit10.Test.PASS, '' 114 115 try: 116 out, err, exitCode = lit10.util.executeCommand( 117 cmd, env=test.config.environment, 118 timeout=litConfig.maxIndividualTestTime) 119 except lit10.util.ExecuteCommandTimeoutException: 120 return (lit10.Test.TIMEOUT, 121 'Reached timeout of {} seconds'.format( 122 litConfig.maxIndividualTestTime) 123 ) 124 125 if exitCode: 126 return lit10.Test.FAIL, out + err 127 128 passing_test_line = '[ PASSED ] 1 test.' 129 if passing_test_line not in out: 130 msg = ('Unable to find %r in gtest output:\n\n%s%s' % 131 (passing_test_line, out, err)) 132 return lit10.Test.UNRESOLVED, msg 133 134 return lit10.Test.PASS,'' 135 136 def maybeAddPythonToCmd(self, cmd): 137 """Insert the python exe into the command if cmd[0] ends in .py 138 139 We cannot rely on the system to interpret shebang lines for us on 140 Windows, so add the python executable to the command if this is a .py 141 script. 142 """ 143 if cmd[0].endswith('.py'): 144 return [sys.executable] + cmd 145 return cmd 146