1from __future__ import absolute_import 2import os 3import tempfile 4import subprocess 5import sys 6import platform 7 8import lit.Test 9import lit.TestRunner 10import lit.util 11from lit.formats.base import TestFormat 12 13def getBuildDir(cmd): 14 found = False 15 for arg in cmd: 16 if found: 17 return arg 18 if arg == '--build-dir': 19 found = True 20 return None 21 22def mkdir_p(path): 23 import errno 24 try: 25 os.makedirs(path) 26 except OSError as e: 27 if e.errno != errno.EEXIST: 28 raise 29 if not os.path.isdir(path): 30 raise OSError(errno.ENOTDIR, "%s is not a directory"%path) 31 32class LLDBTest(TestFormat): 33 def __init__(self, dotest_cmd): 34 self.dotest_cmd = dotest_cmd 35 36 def getTestsInDirectory(self, testSuite, path_in_suite, litConfig, 37 localConfig): 38 source_path = testSuite.getSourcePath(path_in_suite) 39 for filename in os.listdir(source_path): 40 # Ignore dot files and excluded tests. 41 if (filename.startswith('.') or filename in localConfig.excludes): 42 continue 43 44 # Ignore files that don't start with 'Test'. 45 if not filename.startswith('Test'): 46 continue 47 48 filepath = os.path.join(source_path, filename) 49 if not os.path.isdir(filepath): 50 base, ext = os.path.splitext(filename) 51 if ext in localConfig.suffixes: 52 yield lit.Test.Test(testSuite, path_in_suite + 53 (filename, ), localConfig) 54 55 def execute(self, test, litConfig): 56 if litConfig.noExecute: 57 return lit.Test.PASS, '' 58 59 if not test.config.lldb_enable_python: 60 return (lit.Test.UNSUPPORTED, 'Python module disabled') 61 62 if test.config.unsupported: 63 return (lit.Test.UNSUPPORTED, 'Test is unsupported') 64 65 testPath, testFile = os.path.split(test.getSourcePath()) 66 67 # The Python used to run lit can be different from the Python LLDB was 68 # build with. 69 executable = test.config.python_executable 70 71 # On Windows, the system does not always correctly interpret 72 # shebang lines. To make sure we can execute the tests, add 73 # python exe as the first parameter of the command. 74 cmd = [executable] + self.dotest_cmd + [testPath, '-p', testFile] 75 76 builddir = getBuildDir(cmd) 77 mkdir_p(builddir) 78 79 # On macOS, we can't do the DYLD_INSERT_LIBRARIES trick with a shim 80 # python binary as the ASan interceptors get loaded too late. Also, 81 # when SIP is enabled, we can't inject libraries into system binaries 82 # at all, so we need a copy of the "real" python to work with. 83 # 84 # Find the "real" python binary, copy it, and invoke it. 85 if 'DYLD_INSERT_LIBRARIES' in test.config.environment and \ 86 platform.system() == 'Darwin': 87 copied_python = os.path.join(builddir, 'copied-system-python') 88 if not os.path.isfile(copied_python): 89 import shutil, subprocess 90 python = subprocess.check_output([ 91 executable, 92 os.path.join(os.path.dirname(os.path.realpath(__file__)), 93 'get_darwin_real_python.py') 94 ]).decode('utf-8').strip() 95 shutil.copy(python, copied_python) 96 cmd[0] = copied_python 97 98 if 'lldb-repro-capture' in test.config.available_features or \ 99 'lldb-repro-replay' in test.config.available_features: 100 reproducer_root = os.path.join(builddir, 'reproducers') 101 mkdir_p(reproducer_root) 102 reproducer_path = os.path.join(reproducer_root, testFile) 103 if 'lldb-repro-capture' in test.config.available_features: 104 cmd.extend(['--capture-path', reproducer_path]) 105 else: 106 cmd.extend(['--replay-path', reproducer_path]) 107 108 timeoutInfo = None 109 try: 110 out, err, exitCode = lit.util.executeCommand( 111 cmd, 112 env=test.config.environment, 113 timeout=litConfig.maxIndividualTestTime) 114 except lit.util.ExecuteCommandTimeoutException as e: 115 out = e.out 116 err = e.err 117 exitCode = e.exitCode 118 timeoutInfo = 'Reached timeout of {} seconds'.format( 119 litConfig.maxIndividualTestTime) 120 121 if sys.version_info.major == 2: 122 # In Python 2, string objects can contain Unicode characters. Use 123 # the non-strict 'replace' decoding mode. We cannot use the strict 124 # mode right now because lldb's StringPrinter facility and the 125 # Python utf8 decoder have different interpretations of which 126 # characters are "printable". This leads to Python utf8 decoding 127 # exceptions even though lldb is behaving as expected. 128 out = out.decode('utf-8', 'replace') 129 err = err.decode('utf-8', 'replace') 130 131 output = """Script:\n--\n%s\n--\nExit Code: %d\n""" % ( 132 ' '.join(cmd), exitCode) 133 if timeoutInfo is not None: 134 output += """Timeout: %s\n""" % (timeoutInfo,) 135 output += "\n" 136 137 if out: 138 output += """Command Output (stdout):\n--\n%s\n--\n""" % (out,) 139 if err: 140 output += """Command Output (stderr):\n--\n%s\n--\n""" % (err,) 141 142 if timeoutInfo: 143 return lit.Test.TIMEOUT, output 144 145 if exitCode: 146 if 'XPASS:' in out or 'XPASS:' in err: 147 return lit.Test.XPASS, output 148 149 # Otherwise this is just a failure. 150 return lit.Test.FAIL, output 151 152 has_unsupported_tests = 'UNSUPPORTED:' in out or 'UNSUPPORTED:' in err 153 has_passing_tests = 'PASS:' in out or 'PASS:' in err 154 if has_unsupported_tests and not has_passing_tests: 155 return lit.Test.UNSUPPORTED, output 156 157 passing_test_line = 'RESULT: PASSED' 158 if passing_test_line not in out and passing_test_line not in err: 159 return lit.Test.UNRESOLVED, output 160 161 return lit.Test.PASS, output 162