1from __future__ import absolute_import 2import os 3import tempfile 4import subprocess 5import sys 6 7import lit.Test 8import lit.TestRunner 9import lit.util 10from lit.formats.base import TestFormat 11 12def getBuildDir(cmd): 13 found = False 14 for arg in cmd: 15 if found: 16 return arg 17 if arg == '--build-dir': 18 found = True 19 return None 20 21def mkdir_p(path): 22 import errno 23 try: 24 os.makedirs(path) 25 except OSError as e: 26 if e.errno != errno.EEXIST: 27 raise 28 if not os.path.isdir(path): 29 raise OSError(errno.ENOTDIR, "%s is not a directory"%path) 30 31class LLDBTest(TestFormat): 32 def __init__(self, dotest_cmd): 33 self.dotest_cmd = dotest_cmd 34 35 def getTestsInDirectory(self, testSuite, path_in_suite, litConfig, 36 localConfig): 37 source_path = testSuite.getSourcePath(path_in_suite) 38 for filename in os.listdir(source_path): 39 # Ignore dot files and excluded tests. 40 if (filename.startswith('.') or filename in localConfig.excludes): 41 continue 42 43 # Ignore files that don't start with 'Test'. 44 if not filename.startswith('Test'): 45 continue 46 47 filepath = os.path.join(source_path, filename) 48 if not os.path.isdir(filepath): 49 base, ext = os.path.splitext(filename) 50 if ext in localConfig.suffixes: 51 yield lit.Test.Test(testSuite, path_in_suite + 52 (filename, ), localConfig) 53 54 def execute(self, test, litConfig): 55 if litConfig.noExecute: 56 return lit.Test.PASS, '' 57 58 if not test.config.lldb_enable_python: 59 return (lit.Test.UNSUPPORTED, 'Python module disabled') 60 61 if test.config.unsupported: 62 return (lit.Test.UNSUPPORTED, 'Test is unsupported') 63 64 testPath, testFile = os.path.split(test.getSourcePath()) 65 # On Windows, the system does not always correctly interpret 66 # shebang lines. To make sure we can execute the tests, add 67 # python exe as the first parameter of the command. 68 cmd = [sys.executable] + self.dotest_cmd + [testPath, '-p', testFile] 69 70 builddir = getBuildDir(cmd) 71 mkdir_p(builddir) 72 73 # The macOS system integrity protection (SIP) doesn't allow injecting 74 # libraries into system binaries, but this can be worked around by 75 # copying the binary into a different location. 76 if 'DYLD_INSERT_LIBRARIES' in test.config.environment and \ 77 (sys.executable.startswith('/System/') or \ 78 sys.executable.startswith('/usr/bin/')): 79 copied_python = os.path.join(builddir, 'copied-system-python') 80 if not os.path.isfile(copied_python): 81 import shutil, subprocess 82 python = subprocess.check_output([ 83 sys.executable, 84 '-c', 85 'import sys; print(sys.executable)' 86 ]).decode('utf-8').strip() 87 shutil.copy(python, copied_python) 88 cmd[0] = copied_python 89 90 if 'lldb-repro-capture' in test.config.available_features or \ 91 'lldb-repro-replay' in test.config.available_features: 92 reproducer_root = os.path.join(builddir, 'reproducers') 93 mkdir_p(reproducer_root) 94 reproducer_path = os.path.join(reproducer_root, testFile) 95 if 'lldb-repro-capture' in test.config.available_features: 96 cmd.extend(['--capture-path', reproducer_path]) 97 else: 98 cmd.extend(['--replay-path', reproducer_path]) 99 100 timeoutInfo = None 101 try: 102 out, err, exitCode = lit.util.executeCommand( 103 cmd, 104 env=test.config.environment, 105 timeout=litConfig.maxIndividualTestTime) 106 except lit.util.ExecuteCommandTimeoutException as e: 107 out = e.out 108 err = e.err 109 exitCode = e.exitCode 110 timeoutInfo = 'Reached timeout of {} seconds'.format( 111 litConfig.maxIndividualTestTime) 112 113 output = """Script:\n--\n%s\n--\nExit Code: %d\n""" % ( 114 ' '.join(cmd), exitCode) 115 if timeoutInfo is not None: 116 output += """Timeout: %s\n""" % (timeoutInfo,) 117 output += "\n" 118 119 if out: 120 output += """Command Output (stdout):\n--\n%s\n--\n""" % (out,) 121 if err: 122 output += """Command Output (stderr):\n--\n%s\n--\n""" % (err,) 123 124 if timeoutInfo: 125 return lit.Test.TIMEOUT, output 126 127 if exitCode: 128 if 'XPASS:' in out or 'XPASS:' in err: 129 return lit.Test.XPASS, output 130 131 # Otherwise this is just a failure. 132 return lit.Test.FAIL, output 133 134 has_unsupported_tests = 'UNSUPPORTED:' in out or 'UNSUPPORTED:' in err 135 has_passing_tests = 'PASS:' in out or 'PASS:' in err 136 if has_unsupported_tests and not has_passing_tests: 137 return lit.Test.UNSUPPORTED, output 138 139 passing_test_line = 'RESULT: PASSED' 140 if passing_test_line not in out and passing_test_line not in err: 141 return lit.Test.UNRESOLVED, output 142 143 return lit.Test.PASS, output 144