| Daniel Dunbar | be7ada7 | 2009-09-08 05:31:18 +0000 | [diff] [blame] | 1 | import os, signal, subprocess, sys | 
|  | 2 | import StringIO | 
|  | 3 |  | 
|  | 4 | import ShUtil | 
|  | 5 | import Test | 
|  | 6 | import Util | 
|  | 7 |  | 
| Daniel Dunbar | 58c661c | 2009-09-22 04:44:37 +0000 | [diff] [blame] | 8 | import platform | 
| Daniel Dunbar | 5a461dd | 2009-09-22 09:50:38 +0000 | [diff] [blame] | 9 | import tempfile | 
| Daniel Dunbar | 58c661c | 2009-09-22 04:44:37 +0000 | [diff] [blame] | 10 |  | 
| Daniel Dunbar | 6bd2b2e | 2009-09-22 04:44:26 +0000 | [diff] [blame] | 11 | class InternalShellError(Exception): | 
|  | 12 | def __init__(self, command, message): | 
|  | 13 | self.command = command | 
|  | 14 | self.message = message | 
|  | 15 |  | 
| Daniel Dunbar | 58c661c | 2009-09-22 04:44:37 +0000 | [diff] [blame] | 16 | # Don't use close_fds on Windows. | 
|  | 17 | kUseCloseFDs = platform.system() != 'Windows' | 
| Daniel Dunbar | 6efba21 | 2009-10-25 01:37:26 +0000 | [diff] [blame] | 18 |  | 
|  | 19 | # Use temporary files to replace /dev/null on Windows. | 
|  | 20 | kAvoidDevNull = platform.system() == 'Windows' | 
|  | 21 |  | 
| Daniel Dunbar | be7ada7 | 2009-09-08 05:31:18 +0000 | [diff] [blame] | 22 | def executeCommand(command, cwd=None, env=None): | 
|  | 23 | p = subprocess.Popen(command, cwd=cwd, | 
|  | 24 | stdin=subprocess.PIPE, | 
|  | 25 | stdout=subprocess.PIPE, | 
|  | 26 | stderr=subprocess.PIPE, | 
|  | 27 | env=env) | 
|  | 28 | out,err = p.communicate() | 
|  | 29 | exitCode = p.wait() | 
|  | 30 |  | 
|  | 31 | # Detect Ctrl-C in subprocess. | 
|  | 32 | if exitCode == -signal.SIGINT: | 
|  | 33 | raise KeyboardInterrupt | 
|  | 34 |  | 
|  | 35 | return out, err, exitCode | 
|  | 36 |  | 
|  | 37 | def executeShCmd(cmd, cfg, cwd, results): | 
|  | 38 | if isinstance(cmd, ShUtil.Seq): | 
|  | 39 | if cmd.op == ';': | 
|  | 40 | res = executeShCmd(cmd.lhs, cfg, cwd, results) | 
|  | 41 | return executeShCmd(cmd.rhs, cfg, cwd, results) | 
|  | 42 |  | 
|  | 43 | if cmd.op == '&': | 
|  | 44 | raise NotImplementedError,"unsupported test command: '&'" | 
|  | 45 |  | 
|  | 46 | if cmd.op == '||': | 
|  | 47 | res = executeShCmd(cmd.lhs, cfg, cwd, results) | 
|  | 48 | if res != 0: | 
|  | 49 | res = executeShCmd(cmd.rhs, cfg, cwd, results) | 
|  | 50 | return res | 
|  | 51 | if cmd.op == '&&': | 
|  | 52 | res = executeShCmd(cmd.lhs, cfg, cwd, results) | 
|  | 53 | if res is None: | 
|  | 54 | return res | 
|  | 55 |  | 
|  | 56 | if res == 0: | 
|  | 57 | res = executeShCmd(cmd.rhs, cfg, cwd, results) | 
|  | 58 | return res | 
|  | 59 |  | 
|  | 60 | raise ValueError,'Unknown shell command: %r' % cmd.op | 
|  | 61 |  | 
|  | 62 | assert isinstance(cmd, ShUtil.Pipeline) | 
|  | 63 | procs = [] | 
|  | 64 | input = subprocess.PIPE | 
| Daniel Dunbar | 5a461dd | 2009-09-22 09:50:38 +0000 | [diff] [blame] | 65 | stderrTempFiles = [] | 
|  | 66 | # To avoid deadlock, we use a single stderr stream for piped | 
|  | 67 | # output. This is null until we have seen some output using | 
|  | 68 | # stderr. | 
|  | 69 | for i,j in enumerate(cmd.commands): | 
| Daniel Dunbar | a3f85d2 | 2009-10-24 20:32:49 +0000 | [diff] [blame] | 70 | # Apply the redirections, we use (N,) as a sentinal to indicate stdin, | 
|  | 71 | # stdout, stderr for N equal to 0, 1, or 2 respectively. Redirects to or | 
|  | 72 | # from a file are represented with a list [file, mode, file-object] | 
|  | 73 | # where file-object is initially None. | 
| Daniel Dunbar | be7ada7 | 2009-09-08 05:31:18 +0000 | [diff] [blame] | 74 | redirects = [(0,), (1,), (2,)] | 
|  | 75 | for r in j.redirects: | 
|  | 76 | if r[0] == ('>',2): | 
|  | 77 | redirects[2] = [r[1], 'w', None] | 
| Daniel Dunbar | a3f85d2 | 2009-10-24 20:32:49 +0000 | [diff] [blame] | 78 | elif r[0] == ('>>',2): | 
|  | 79 | redirects[2] = [r[1], 'a', None] | 
| Daniel Dunbar | be7ada7 | 2009-09-08 05:31:18 +0000 | [diff] [blame] | 80 | elif r[0] == ('>&',2) and r[1] in '012': | 
|  | 81 | redirects[2] = redirects[int(r[1])] | 
|  | 82 | elif r[0] == ('>&',) or r[0] == ('&>',): | 
|  | 83 | redirects[1] = redirects[2] = [r[1], 'w', None] | 
|  | 84 | elif r[0] == ('>',): | 
|  | 85 | redirects[1] = [r[1], 'w', None] | 
| Daniel Dunbar | a3f85d2 | 2009-10-24 20:32:49 +0000 | [diff] [blame] | 86 | elif r[0] == ('>>',): | 
|  | 87 | redirects[1] = [r[1], 'a', None] | 
| Daniel Dunbar | be7ada7 | 2009-09-08 05:31:18 +0000 | [diff] [blame] | 88 | elif r[0] == ('<',): | 
|  | 89 | redirects[0] = [r[1], 'r', None] | 
|  | 90 | else: | 
|  | 91 | raise NotImplementedError,"Unsupported redirect: %r" % (r,) | 
|  | 92 |  | 
| Daniel Dunbar | a3f85d2 | 2009-10-24 20:32:49 +0000 | [diff] [blame] | 93 | # Map from the final redirections to something subprocess can handle. | 
| Daniel Dunbar | be7ada7 | 2009-09-08 05:31:18 +0000 | [diff] [blame] | 94 | final_redirects = [] | 
|  | 95 | for index,r in enumerate(redirects): | 
|  | 96 | if r == (0,): | 
|  | 97 | result = input | 
|  | 98 | elif r == (1,): | 
|  | 99 | if index == 0: | 
|  | 100 | raise NotImplementedError,"Unsupported redirect for stdin" | 
|  | 101 | elif index == 1: | 
|  | 102 | result = subprocess.PIPE | 
|  | 103 | else: | 
|  | 104 | result = subprocess.STDOUT | 
|  | 105 | elif r == (2,): | 
|  | 106 | if index != 2: | 
|  | 107 | raise NotImplementedError,"Unsupported redirect on stdout" | 
|  | 108 | result = subprocess.PIPE | 
|  | 109 | else: | 
|  | 110 | if r[2] is None: | 
| Daniel Dunbar | 6efba21 | 2009-10-25 01:37:26 +0000 | [diff] [blame] | 111 | if kAvoidDevNull and r[0] == '/dev/null': | 
|  | 112 | r[2] = tempfile.TemporaryFile(mode=r[1]) | 
|  | 113 | else: | 
|  | 114 | r[2] = open(r[0], r[1]) | 
| Daniel Dunbar | 474f0df | 2009-11-08 03:43:06 +0000 | [diff] [blame] | 115 | # Workaround a Win32 and/or subprocess bug when appending. | 
|  | 116 | if r[1] == 'a': | 
| Daniel Dunbar | bf477df | 2009-11-08 09:07:33 +0000 | [diff] [blame] | 117 | r[2].seek(0, 2) | 
| Daniel Dunbar | be7ada7 | 2009-09-08 05:31:18 +0000 | [diff] [blame] | 118 | result = r[2] | 
|  | 119 | final_redirects.append(result) | 
|  | 120 |  | 
|  | 121 | stdin, stdout, stderr = final_redirects | 
|  | 122 |  | 
|  | 123 | # If stderr wants to come from stdout, but stdout isn't a pipe, then put | 
|  | 124 | # stderr on a pipe and treat it as stdout. | 
|  | 125 | if (stderr == subprocess.STDOUT and stdout != subprocess.PIPE): | 
|  | 126 | stderr = subprocess.PIPE | 
|  | 127 | stderrIsStdout = True | 
|  | 128 | else: | 
|  | 129 | stderrIsStdout = False | 
| Daniel Dunbar | 6bd2b2e | 2009-09-22 04:44:26 +0000 | [diff] [blame] | 130 |  | 
| Daniel Dunbar | 5a461dd | 2009-09-22 09:50:38 +0000 | [diff] [blame] | 131 | # Don't allow stderr on a PIPE except for the last | 
|  | 132 | # process, this could deadlock. | 
|  | 133 | # | 
|  | 134 | # FIXME: This is slow, but so is deadlock. | 
|  | 135 | if stderr == subprocess.PIPE and j != cmd.commands[-1]: | 
|  | 136 | stderr = tempfile.TemporaryFile(mode='w+b') | 
|  | 137 | stderrTempFiles.append((i, stderr)) | 
|  | 138 |  | 
| Daniel Dunbar | 6bd2b2e | 2009-09-22 04:44:26 +0000 | [diff] [blame] | 139 | # Resolve the executable path ourselves. | 
|  | 140 | args = list(j.args) | 
|  | 141 | args[0] = Util.which(args[0], cfg.environment['PATH']) | 
|  | 142 | if not args[0]: | 
|  | 143 | raise InternalShellError(j, '%r: command not found' % j.args[0]) | 
|  | 144 |  | 
| Daniel Dunbar | 4b78aa3 | 2009-09-22 06:09:13 +0000 | [diff] [blame] | 145 | procs.append(subprocess.Popen(args, cwd=cwd, | 
| Daniel Dunbar | be7ada7 | 2009-09-08 05:31:18 +0000 | [diff] [blame] | 146 | stdin = stdin, | 
|  | 147 | stdout = stdout, | 
|  | 148 | stderr = stderr, | 
|  | 149 | env = cfg.environment, | 
| Daniel Dunbar | 58c661c | 2009-09-22 04:44:37 +0000 | [diff] [blame] | 150 | close_fds = kUseCloseFDs)) | 
| Daniel Dunbar | be7ada7 | 2009-09-08 05:31:18 +0000 | [diff] [blame] | 151 |  | 
|  | 152 | # Immediately close stdin for any process taking stdin from us. | 
|  | 153 | if stdin == subprocess.PIPE: | 
|  | 154 | procs[-1].stdin.close() | 
|  | 155 | procs[-1].stdin = None | 
|  | 156 |  | 
|  | 157 | # Update the current stdin source. | 
|  | 158 | if stdout == subprocess.PIPE: | 
|  | 159 | input = procs[-1].stdout | 
|  | 160 | elif stderrIsStdout: | 
|  | 161 | input = procs[-1].stderr | 
|  | 162 | else: | 
|  | 163 | input = subprocess.PIPE | 
|  | 164 |  | 
| Daniel Dunbar | 5a461dd | 2009-09-22 09:50:38 +0000 | [diff] [blame] | 165 | # FIXME: There is probably still deadlock potential here. Yawn. | 
| Daniel Dunbar | be7ada7 | 2009-09-08 05:31:18 +0000 | [diff] [blame] | 166 | procData = [None] * len(procs) | 
|  | 167 | procData[-1] = procs[-1].communicate() | 
| Daniel Dunbar | 5a461dd | 2009-09-22 09:50:38 +0000 | [diff] [blame] | 168 |  | 
| Daniel Dunbar | be7ada7 | 2009-09-08 05:31:18 +0000 | [diff] [blame] | 169 | for i in range(len(procs) - 1): | 
|  | 170 | if procs[i].stdout is not None: | 
|  | 171 | out = procs[i].stdout.read() | 
|  | 172 | else: | 
|  | 173 | out = '' | 
|  | 174 | if procs[i].stderr is not None: | 
|  | 175 | err = procs[i].stderr.read() | 
|  | 176 | else: | 
|  | 177 | err = '' | 
|  | 178 | procData[i] = (out,err) | 
| Daniel Dunbar | 5a461dd | 2009-09-22 09:50:38 +0000 | [diff] [blame] | 179 |  | 
|  | 180 | # Read stderr out of the temp files. | 
|  | 181 | for i,f in stderrTempFiles: | 
|  | 182 | f.seek(0, 0) | 
|  | 183 | procData[i] = (procData[i][0], f.read()) | 
| Daniel Dunbar | be7ada7 | 2009-09-08 05:31:18 +0000 | [diff] [blame] | 184 |  | 
|  | 185 | exitCode = None | 
|  | 186 | for i,(out,err) in enumerate(procData): | 
|  | 187 | res = procs[i].wait() | 
|  | 188 | # Detect Ctrl-C in subprocess. | 
|  | 189 | if res == -signal.SIGINT: | 
|  | 190 | raise KeyboardInterrupt | 
|  | 191 |  | 
|  | 192 | results.append((cmd.commands[i], out, err, res)) | 
|  | 193 | if cmd.pipe_err: | 
|  | 194 | # Python treats the exit code as a signed char. | 
|  | 195 | if res < 0: | 
|  | 196 | exitCode = min(exitCode, res) | 
|  | 197 | else: | 
|  | 198 | exitCode = max(exitCode, res) | 
|  | 199 | else: | 
|  | 200 | exitCode = res | 
|  | 201 |  | 
|  | 202 | if cmd.negate: | 
|  | 203 | exitCode = not exitCode | 
|  | 204 |  | 
|  | 205 | return exitCode | 
|  | 206 |  | 
|  | 207 | def executeScriptInternal(test, litConfig, tmpBase, commands, cwd): | 
|  | 208 | ln = ' &&\n'.join(commands) | 
|  | 209 | try: | 
|  | 210 | cmd = ShUtil.ShParser(ln, litConfig.isWindows).parse() | 
|  | 211 | except: | 
|  | 212 | return (Test.FAIL, "shell parser error on: %r" % ln) | 
|  | 213 |  | 
|  | 214 | results = [] | 
| Daniel Dunbar | 6bd2b2e | 2009-09-22 04:44:26 +0000 | [diff] [blame] | 215 | try: | 
|  | 216 | exitCode = executeShCmd(cmd, test.config, cwd, results) | 
|  | 217 | except InternalShellError,e: | 
|  | 218 | out = '' | 
|  | 219 | err = e.message | 
|  | 220 | exitCode = 255 | 
| Daniel Dunbar | be7ada7 | 2009-09-08 05:31:18 +0000 | [diff] [blame] | 221 |  | 
|  | 222 | out = err = '' | 
|  | 223 | for i,(cmd, cmd_out,cmd_err,res) in enumerate(results): | 
|  | 224 | out += 'Command %d: %s\n' % (i, ' '.join('"%s"' % s for s in cmd.args)) | 
|  | 225 | out += 'Command %d Result: %r\n' % (i, res) | 
|  | 226 | out += 'Command %d Output:\n%s\n\n' % (i, cmd_out) | 
|  | 227 | out += 'Command %d Stderr:\n%s\n\n' % (i, cmd_err) | 
|  | 228 |  | 
|  | 229 | return out, err, exitCode | 
|  | 230 |  | 
|  | 231 | def executeTclScriptInternal(test, litConfig, tmpBase, commands, cwd): | 
|  | 232 | import TclUtil | 
|  | 233 | cmds = [] | 
|  | 234 | for ln in commands: | 
|  | 235 | # Given the unfortunate way LLVM's test are written, the line gets | 
|  | 236 | # backslash substitution done twice. | 
|  | 237 | ln = TclUtil.TclLexer(ln).lex_unquoted(process_all = True) | 
|  | 238 |  | 
|  | 239 | try: | 
|  | 240 | tokens = list(TclUtil.TclLexer(ln).lex()) | 
|  | 241 | except: | 
|  | 242 | return (Test.FAIL, "Tcl lexer error on: %r" % ln) | 
|  | 243 |  | 
|  | 244 | # Validate there are no control tokens. | 
|  | 245 | for t in tokens: | 
|  | 246 | if not isinstance(t, str): | 
|  | 247 | return (Test.FAIL, | 
|  | 248 | "Invalid test line: %r containing %r" % (ln, t)) | 
|  | 249 |  | 
|  | 250 | try: | 
|  | 251 | cmds.append(TclUtil.TclExecCommand(tokens).parse_pipeline()) | 
|  | 252 | except: | 
| Daniel Dunbar | 2c0a49c | 2009-09-08 05:37:51 +0000 | [diff] [blame] | 253 | return (Test.FAIL, "Tcl 'exec' parse error on: %r" % ln) | 
| Daniel Dunbar | be7ada7 | 2009-09-08 05:31:18 +0000 | [diff] [blame] | 254 |  | 
|  | 255 | cmd = cmds[0] | 
|  | 256 | for c in cmds[1:]: | 
|  | 257 | cmd = ShUtil.Seq(cmd, '&&', c) | 
|  | 258 |  | 
| Daniel Dunbar | 7723d45 | 2009-10-19 03:54:21 +0000 | [diff] [blame] | 259 | # FIXME: This is lame, we shouldn't need bash. See PR5240. | 
|  | 260 | bashPath = litConfig.getBashPath() | 
|  | 261 | if litConfig.useTclAsSh and bashPath: | 
| Daniel Dunbar | be7ada7 | 2009-09-08 05:31:18 +0000 | [diff] [blame] | 262 | script = tmpBase + '.script' | 
|  | 263 |  | 
|  | 264 | # Write script file | 
|  | 265 | f = open(script,'w') | 
|  | 266 | print >>f, 'set -o pipefail' | 
|  | 267 | cmd.toShell(f, pipefail = True) | 
|  | 268 | f.close() | 
|  | 269 |  | 
|  | 270 | if 0: | 
|  | 271 | print >>sys.stdout, cmd | 
|  | 272 | print >>sys.stdout, open(script).read() | 
|  | 273 | print >>sys.stdout | 
|  | 274 | return '', '', 0 | 
|  | 275 |  | 
| Daniel Dunbar | 7723d45 | 2009-10-19 03:54:21 +0000 | [diff] [blame] | 276 | command = [litConfig.getBashPath(), script] | 
| Daniel Dunbar | be7ada7 | 2009-09-08 05:31:18 +0000 | [diff] [blame] | 277 | out,err,exitCode = executeCommand(command, cwd=cwd, | 
|  | 278 | env=test.config.environment) | 
|  | 279 |  | 
|  | 280 | # Tcl commands fail on standard error output. | 
|  | 281 | if err: | 
|  | 282 | exitCode = 1 | 
|  | 283 | out = 'Command has output on stderr!\n\n' + out | 
|  | 284 |  | 
|  | 285 | return out,err,exitCode | 
|  | 286 | else: | 
|  | 287 | results = [] | 
| Daniel Dunbar | 6bd2b2e | 2009-09-22 04:44:26 +0000 | [diff] [blame] | 288 | try: | 
|  | 289 | exitCode = executeShCmd(cmd, test.config, cwd, results) | 
|  | 290 | except InternalShellError,e: | 
|  | 291 | results.append((e.command, '', e.message + '\n', 255)) | 
|  | 292 | exitCode = 255 | 
| Daniel Dunbar | be7ada7 | 2009-09-08 05:31:18 +0000 | [diff] [blame] | 293 |  | 
|  | 294 | out = err = '' | 
|  | 295 |  | 
|  | 296 | # Tcl commands fail on standard error output. | 
|  | 297 | if [True for _,_,err,res in results if err]: | 
|  | 298 | exitCode = 1 | 
|  | 299 | out += 'Command has output on stderr!\n\n' | 
|  | 300 |  | 
|  | 301 | for i,(cmd, cmd_out, cmd_err, res) in enumerate(results): | 
|  | 302 | out += 'Command %d: %s\n' % (i, ' '.join('"%s"' % s for s in cmd.args)) | 
|  | 303 | out += 'Command %d Result: %r\n' % (i, res) | 
|  | 304 | out += 'Command %d Output:\n%s\n\n' % (i, cmd_out) | 
|  | 305 | out += 'Command %d Stderr:\n%s\n\n' % (i, cmd_err) | 
|  | 306 |  | 
|  | 307 | return out, err, exitCode | 
|  | 308 |  | 
|  | 309 | def executeScript(test, litConfig, tmpBase, commands, cwd): | 
|  | 310 | script = tmpBase + '.script' | 
|  | 311 | if litConfig.isWindows: | 
|  | 312 | script += '.bat' | 
|  | 313 |  | 
|  | 314 | # Write script file | 
|  | 315 | f = open(script,'w') | 
|  | 316 | if litConfig.isWindows: | 
|  | 317 | f.write('\nif %ERRORLEVEL% NEQ 0 EXIT\n'.join(commands)) | 
|  | 318 | else: | 
|  | 319 | f.write(' &&\n'.join(commands)) | 
|  | 320 | f.write('\n') | 
|  | 321 | f.close() | 
|  | 322 |  | 
|  | 323 | if litConfig.isWindows: | 
|  | 324 | command = ['cmd','/c', script] | 
|  | 325 | else: | 
|  | 326 | command = ['/bin/sh', script] | 
|  | 327 | if litConfig.useValgrind: | 
|  | 328 | # FIXME: Running valgrind on sh is overkill. We probably could just | 
|  | 329 | # run on clang with no real loss. | 
|  | 330 | valgrindArgs = ['valgrind', '-q', | 
|  | 331 | '--tool=memcheck', '--trace-children=yes', | 
|  | 332 | '--error-exitcode=123'] | 
|  | 333 | valgrindArgs.extend(litConfig.valgrindArgs) | 
|  | 334 |  | 
|  | 335 | command = valgrindArgs + command | 
|  | 336 |  | 
|  | 337 | return executeCommand(command, cwd=cwd, env=test.config.environment) | 
|  | 338 |  | 
| Daniel Dunbar | 42543b7 | 2009-11-03 07:26:38 +0000 | [diff] [blame] | 339 | def isExpectedFail(xfails, xtargets, target_triple): | 
|  | 340 | # Check if any xfail matches this target. | 
|  | 341 | for item in xfails: | 
|  | 342 | if item == '*' or item in target_triple: | 
|  | 343 | break | 
|  | 344 | else: | 
|  | 345 | return False | 
|  | 346 |  | 
|  | 347 | # If so, see if it is expected to pass on this target. | 
|  | 348 | # | 
|  | 349 | # FIXME: Rename XTARGET to something that makes sense, like XPASS. | 
|  | 350 | for item in xtargets: | 
|  | 351 | if item == '*' or item in target_triple: | 
|  | 352 | return False | 
|  | 353 |  | 
|  | 354 | return True | 
|  | 355 |  | 
| Daniel Dunbar | ee504b8 | 2009-11-08 09:07:13 +0000 | [diff] [blame] | 356 | def parseIntegratedTestScript(test): | 
| Daniel Dunbar | be7ada7 | 2009-09-08 05:31:18 +0000 | [diff] [blame] | 357 | """parseIntegratedTestScript - Scan an LLVM/Clang style integrated test | 
|  | 358 | script and extract the lines to 'RUN' as well as 'XFAIL' and 'XTARGET' | 
|  | 359 | information. The RUN lines also will have variable substitution performed. | 
|  | 360 | """ | 
|  | 361 |  | 
|  | 362 | # Get the temporary location, this is always relative to the test suite | 
|  | 363 | # root, not test source root. | 
|  | 364 | # | 
|  | 365 | # FIXME: This should not be here? | 
|  | 366 | sourcepath = test.getSourcePath() | 
|  | 367 | execpath = test.getExecPath() | 
|  | 368 | execdir,execbase = os.path.split(execpath) | 
|  | 369 | tmpBase = os.path.join(execdir, 'Output', execbase) | 
|  | 370 |  | 
|  | 371 | # We use #_MARKER_# to hide %% while we do the other substitutions. | 
|  | 372 | substitutions = [('%%', '#_MARKER_#')] | 
|  | 373 | substitutions.extend(test.config.substitutions) | 
|  | 374 | substitutions.extend([('%s', sourcepath), | 
|  | 375 | ('%S', os.path.dirname(sourcepath)), | 
|  | 376 | ('%p', os.path.dirname(sourcepath)), | 
|  | 377 | ('%t', tmpBase + '.tmp'), | 
| Daniel Dunbar | 5110609 | 2009-09-13 01:39:50 +0000 | [diff] [blame] | 378 | # FIXME: Remove this once we kill DejaGNU. | 
|  | 379 | ('%abs_tmp', tmpBase + '.tmp'), | 
| Daniel Dunbar | be7ada7 | 2009-09-08 05:31:18 +0000 | [diff] [blame] | 380 | ('#_MARKER_#', '%')]) | 
|  | 381 |  | 
|  | 382 | # Collect the test lines from the script. | 
|  | 383 | script = [] | 
|  | 384 | xfails = [] | 
|  | 385 | xtargets = [] | 
|  | 386 | for ln in open(sourcepath): | 
|  | 387 | if 'RUN:' in ln: | 
|  | 388 | # Isolate the command to run. | 
|  | 389 | index = ln.index('RUN:') | 
|  | 390 | ln = ln[index+4:] | 
|  | 391 |  | 
|  | 392 | # Trim trailing whitespace. | 
|  | 393 | ln = ln.rstrip() | 
|  | 394 |  | 
|  | 395 | # Collapse lines with trailing '\\'. | 
|  | 396 | if script and script[-1][-1] == '\\': | 
|  | 397 | script[-1] = script[-1][:-1] + ln | 
|  | 398 | else: | 
|  | 399 | script.append(ln) | 
| Daniel Dunbar | 42543b7 | 2009-11-03 07:26:38 +0000 | [diff] [blame] | 400 | elif 'XFAIL:' in ln: | 
| Daniel Dunbar | be7ada7 | 2009-09-08 05:31:18 +0000 | [diff] [blame] | 401 | items = ln[ln.index('XFAIL:') + 6:].split(',') | 
|  | 402 | xfails.extend([s.strip() for s in items]) | 
| Daniel Dunbar | be7ada7 | 2009-09-08 05:31:18 +0000 | [diff] [blame] | 403 | elif 'XTARGET:' in ln: | 
|  | 404 | items = ln[ln.index('XTARGET:') + 8:].split(',') | 
|  | 405 | xtargets.extend([s.strip() for s in items]) | 
|  | 406 | elif 'END.' in ln: | 
|  | 407 | # Check for END. lines. | 
|  | 408 | if ln[ln.index('END.'):].strip() == 'END.': | 
|  | 409 | break | 
|  | 410 |  | 
|  | 411 | # Apply substitutions to the script. | 
|  | 412 | def processLine(ln): | 
|  | 413 | # Apply substitutions | 
|  | 414 | for a,b in substitutions: | 
|  | 415 | ln = ln.replace(a,b) | 
|  | 416 |  | 
|  | 417 | # Strip the trailing newline and any extra whitespace. | 
|  | 418 | return ln.strip() | 
|  | 419 | script = map(processLine, script) | 
|  | 420 |  | 
|  | 421 | # Verify the script contains a run line. | 
|  | 422 | if not script: | 
|  | 423 | return (Test.UNRESOLVED, "Test has no run line!") | 
|  | 424 |  | 
|  | 425 | if script[-1][-1] == '\\': | 
|  | 426 | return (Test.UNRESOLVED, "Test has unterminated run lines (with '\\')") | 
|  | 427 |  | 
| Daniel Dunbar | 42543b7 | 2009-11-03 07:26:38 +0000 | [diff] [blame] | 428 | isXFail = isExpectedFail(xfails, xtargets, test.suite.config.target_triple) | 
|  | 429 | return script,isXFail,tmpBase,execdir | 
| Daniel Dunbar | be7ada7 | 2009-09-08 05:31:18 +0000 | [diff] [blame] | 430 |  | 
|  | 431 | def formatTestOutput(status, out, err, exitCode, script): | 
|  | 432 | output = StringIO.StringIO() | 
|  | 433 | print >>output, "Script:" | 
|  | 434 | print >>output, "--" | 
|  | 435 | print >>output, '\n'.join(script) | 
|  | 436 | print >>output, "--" | 
|  | 437 | print >>output, "Exit Code: %r" % exitCode | 
|  | 438 | print >>output, "Command Output (stdout):" | 
|  | 439 | print >>output, "--" | 
|  | 440 | output.write(out) | 
|  | 441 | print >>output, "--" | 
|  | 442 | print >>output, "Command Output (stderr):" | 
|  | 443 | print >>output, "--" | 
|  | 444 | output.write(err) | 
|  | 445 | print >>output, "--" | 
|  | 446 | return (status, output.getvalue()) | 
|  | 447 |  | 
|  | 448 | def executeTclTest(test, litConfig): | 
|  | 449 | if test.config.unsupported: | 
|  | 450 | return (Test.UNSUPPORTED, 'Test is unsupported') | 
|  | 451 |  | 
| Daniel Dunbar | ee504b8 | 2009-11-08 09:07:13 +0000 | [diff] [blame] | 452 | res = parseIntegratedTestScript(test) | 
| Daniel Dunbar | be7ada7 | 2009-09-08 05:31:18 +0000 | [diff] [blame] | 453 | if len(res) == 2: | 
|  | 454 | return res | 
|  | 455 |  | 
| Daniel Dunbar | 42543b7 | 2009-11-03 07:26:38 +0000 | [diff] [blame] | 456 | script, isXFail, tmpBase, execdir = res | 
| Daniel Dunbar | be7ada7 | 2009-09-08 05:31:18 +0000 | [diff] [blame] | 457 |  | 
|  | 458 | if litConfig.noExecute: | 
|  | 459 | return (Test.PASS, '') | 
|  | 460 |  | 
|  | 461 | # Create the output directory if it does not already exist. | 
|  | 462 | Util.mkdir_p(os.path.dirname(tmpBase)) | 
|  | 463 |  | 
|  | 464 | res = executeTclScriptInternal(test, litConfig, tmpBase, script, execdir) | 
|  | 465 | if len(res) == 2: | 
|  | 466 | return res | 
|  | 467 |  | 
| Daniel Dunbar | be7ada7 | 2009-09-08 05:31:18 +0000 | [diff] [blame] | 468 | out,err,exitCode = res | 
|  | 469 | if isXFail: | 
|  | 470 | ok = exitCode != 0 | 
|  | 471 | status = (Test.XPASS, Test.XFAIL)[ok] | 
|  | 472 | else: | 
|  | 473 | ok = exitCode == 0 | 
|  | 474 | status = (Test.FAIL, Test.PASS)[ok] | 
|  | 475 |  | 
|  | 476 | if ok: | 
|  | 477 | return (status,'') | 
|  | 478 |  | 
|  | 479 | return formatTestOutput(status, out, err, exitCode, script) | 
|  | 480 |  | 
| Daniel Dunbar | ee504b8 | 2009-11-08 09:07:13 +0000 | [diff] [blame] | 481 | def executeShTest(test, litConfig, useExternalSh): | 
| Daniel Dunbar | be7ada7 | 2009-09-08 05:31:18 +0000 | [diff] [blame] | 482 | if test.config.unsupported: | 
|  | 483 | return (Test.UNSUPPORTED, 'Test is unsupported') | 
|  | 484 |  | 
| Daniel Dunbar | ee504b8 | 2009-11-08 09:07:13 +0000 | [diff] [blame] | 485 | res = parseIntegratedTestScript(test) | 
| Daniel Dunbar | be7ada7 | 2009-09-08 05:31:18 +0000 | [diff] [blame] | 486 | if len(res) == 2: | 
|  | 487 | return res | 
|  | 488 |  | 
| Daniel Dunbar | 42543b7 | 2009-11-03 07:26:38 +0000 | [diff] [blame] | 489 | script, isXFail, tmpBase, execdir = res | 
| Daniel Dunbar | be7ada7 | 2009-09-08 05:31:18 +0000 | [diff] [blame] | 490 |  | 
|  | 491 | if litConfig.noExecute: | 
|  | 492 | return (Test.PASS, '') | 
|  | 493 |  | 
|  | 494 | # Create the output directory if it does not already exist. | 
|  | 495 | Util.mkdir_p(os.path.dirname(tmpBase)) | 
|  | 496 |  | 
|  | 497 | if useExternalSh: | 
|  | 498 | res = executeScript(test, litConfig, tmpBase, script, execdir) | 
|  | 499 | else: | 
|  | 500 | res = executeScriptInternal(test, litConfig, tmpBase, script, execdir) | 
|  | 501 | if len(res) == 2: | 
|  | 502 | return res | 
|  | 503 |  | 
|  | 504 | out,err,exitCode = res | 
| Daniel Dunbar | 42543b7 | 2009-11-03 07:26:38 +0000 | [diff] [blame] | 505 | if isXFail: | 
| Daniel Dunbar | be7ada7 | 2009-09-08 05:31:18 +0000 | [diff] [blame] | 506 | ok = exitCode != 0 | 
|  | 507 | status = (Test.XPASS, Test.XFAIL)[ok] | 
|  | 508 | else: | 
|  | 509 | ok = exitCode == 0 | 
|  | 510 | status = (Test.FAIL, Test.PASS)[ok] | 
|  | 511 |  | 
|  | 512 | if ok: | 
|  | 513 | return (status,'') | 
|  | 514 |  | 
|  | 515 | return formatTestOutput(status, out, err, exitCode, script) |