blob: 9206427b451a4ecd06709c21b7eba7f7bcd7fd1e [file] [log] [blame]
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +00001import errno
Daniel Dunbar69e07a72009-06-17 21:33:37 +00002import os
Daniel Dunbar8fe83f12009-07-25 09:42:24 +00003import platform
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +00004import re
Daniel Dunbar69e07a72009-06-17 21:33:37 +00005import signal
6import subprocess
7import sys
8
Daniel Dunbar1db467f2009-07-31 05:54:17 +00009import Util
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +000010
Daniel Dunbar8fe83f12009-07-25 09:42:24 +000011kSystemName = platform.system()
12
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +000013class TestStatus:
14 Pass = 0
15 XFail = 1
16 Fail = 2
17 XPass = 3
Daniel Dunbar8bf0ccd2009-07-25 12:47:38 +000018 Invalid = 4
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +000019
Daniel Dunbar8bf0ccd2009-07-25 12:47:38 +000020 kNames = ['Pass','XFail','Fail','XPass','Invalid']
Daniel Dunbarfbbb1e72009-07-02 23:58:07 +000021 @staticmethod
Daniel Dunbar8afede22009-07-02 23:56:37 +000022 def getName(code):
23 return TestStatus.kNames[code]
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +000024
Daniel Dunbar5928ccd2009-08-01 04:06:02 +000025def executeScript(cfg, script, commands, cwd):
Daniel Dunbar10aebbb2009-07-25 15:26:08 +000026 # Write script file
27 f = open(script,'w')
28 if kSystemName == 'Windows':
29 f.write('\nif %ERRORLEVEL% NEQ 0 EXIT\n'.join(commands))
30 else:
31 f.write(' &&\n'.join(commands))
32 f.write('\n')
33 f.close()
34
35 if kSystemName == 'Windows':
36 command = ['cmd','/c', script]
37 else:
38 command = ['/bin/sh', script]
Daniel Dunbar5928ccd2009-08-01 04:06:02 +000039 if cfg.useValgrind:
Daniel Dunbar9a676b72009-07-29 02:57:25 +000040 # FIXME: Running valgrind on sh is overkill. We probably could just
Daniel Dunbar5928ccd2009-08-01 04:06:02 +000041 # run on clang with no real loss.
Daniel Dunbar9a676b72009-07-29 02:57:25 +000042 command = ['valgrind', '-q',
43 '--tool=memcheck', '--leak-check=no', '--trace-children=yes',
44 '--error-exitcode=123'] + command
Daniel Dunbar10aebbb2009-07-25 15:26:08 +000045
46 p = subprocess.Popen(command, cwd=cwd,
47 stdin=subprocess.PIPE,
48 stdout=subprocess.PIPE,
49 stderr=subprocess.PIPE,
Daniel Dunbar1db467f2009-07-31 05:54:17 +000050 env=cfg.environment)
Daniel Dunbar10aebbb2009-07-25 15:26:08 +000051 out,err = p.communicate()
52 exitCode = p.wait()
53
54 # Detect Ctrl-C in subprocess.
55 if exitCode == -signal.SIGINT:
56 raise KeyboardInterrupt
57
58 return out, err, exitCode
59
Daniel Dunbara957d992009-07-25 14:46:05 +000060import StringIO
Daniel Dunbar5928ccd2009-08-01 04:06:02 +000061def runOneTest(cfg, testPath, tmpBase):
Daniel Dunbara957d992009-07-25 14:46:05 +000062 # Make paths absolute.
63 tmpBase = os.path.abspath(tmpBase)
64 testPath = os.path.abspath(testPath)
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +000065
66 # Create the output directory if it does not already exist.
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +000067
Daniel Dunbar1db467f2009-07-31 05:54:17 +000068 Util.mkdir_p(os.path.dirname(tmpBase))
Daniel Dunbara957d992009-07-25 14:46:05 +000069 script = tmpBase + '.script'
Daniel Dunbar8fe83f12009-07-25 09:42:24 +000070 if kSystemName == 'Windows':
Daniel Dunbara957d992009-07-25 14:46:05 +000071 script += '.bat'
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +000072
Daniel Dunbara957d992009-07-25 14:46:05 +000073 substitutions = [('%s', testPath),
74 ('%S', os.path.dirname(testPath)),
75 ('%t', tmpBase + '.tmp'),
Daniel Dunbar5928ccd2009-08-01 04:06:02 +000076 (' clang ', ' ' + cfg.clang + ' '),
77 (' clang-cc ', ' ' + cfg.clangcc + ' ')]
Daniel Dunbar025f80d2009-07-25 11:27:37 +000078
79 # Collect the test lines from the script.
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +000080 scriptLines = []
81 xfailLines = []
Daniel Dunbara957d992009-07-25 14:46:05 +000082 for ln in open(testPath):
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +000083 if 'RUN:' in ln:
Daniel Dunbar025f80d2009-07-25 11:27:37 +000084 # Isolate the command to run.
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +000085 index = ln.index('RUN:')
86 ln = ln[index+4:]
Daniel Dunbar025f80d2009-07-25 11:27:37 +000087
Daniel Dunbar322f7892009-07-25 12:23:35 +000088 # Strip trailing newline.
89 scriptLines.append(ln)
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +000090 elif 'XFAIL' in ln:
91 xfailLines.append(ln)
Daniel Dunbar025f80d2009-07-25 11:27:37 +000092
93 # FIXME: Support something like END, in case we need to process large
94 # files.
Daniel Dunbara957d992009-07-25 14:46:05 +000095
96 # Verify the script contains a run line.
97 if not scriptLines:
98 return (TestStatus.Fail, "Test has no run line!")
Daniel Dunbar322f7892009-07-25 12:23:35 +000099
100 # Apply substitutions to the script.
101 def processLine(ln):
102 # Apply substitutions
103 for a,b in substitutions:
104 ln = ln.replace(a,b)
105
Daniel Dunbar322f7892009-07-25 12:23:35 +0000106 # Strip the trailing newline and any extra whitespace.
107 return ln.strip()
108 scriptLines = map(processLine, scriptLines)
Daniel Dunbar025f80d2009-07-25 11:27:37 +0000109
110 # Validate interior lines for '&&', a lovely historical artifact.
111 for i in range(len(scriptLines) - 1):
112 ln = scriptLines[i]
113
114 if not ln.endswith('&&'):
Daniel Dunbara957d992009-07-25 14:46:05 +0000115 return (TestStatus.Fail,
Daniel Dunbar67796472009-07-27 19:01:13 +0000116 ("MISSING \'&&\': %s\n" +
117 "FOLLOWED BY : %s\n") % (ln, scriptLines[i + 1]))
Daniel Dunbar025f80d2009-07-25 11:27:37 +0000118
119 # Strip off '&&'
120 scriptLines[i] = ln[:-2]
Daniel Dunbar025f80d2009-07-25 11:27:37 +0000121
Daniel Dunbar1db467f2009-07-31 05:54:17 +0000122 out, err, exitCode = executeScript(cfg, script, scriptLines,
Daniel Dunbar5928ccd2009-08-01 04:06:02 +0000123 os.path.dirname(testPath))
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +0000124 if xfailLines:
Daniel Dunbara957d992009-07-25 14:46:05 +0000125 ok = exitCode != 0
126 status = (TestStatus.XPass, TestStatus.XFail)[ok]
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +0000127 else:
Daniel Dunbara957d992009-07-25 14:46:05 +0000128 ok = exitCode == 0
129 status = (TestStatus.Fail, TestStatus.Pass)[ok]
130
131 if ok:
132 return (status,'')
133
134 output = StringIO.StringIO()
135 print >>output, "Script:"
136 print >>output, "--"
137 print >>output, '\n'.join(scriptLines)
138 print >>output, "--"
139 print >>output, "Exit Code: %r" % exitCode
140 print >>output, "Command Output (stdout):"
141 print >>output, "--"
142 output.write(out)
143 print >>output, "--"
144 print >>output, "Command Output (stderr):"
145 print >>output, "--"
146 output.write(err)
147 print >>output, "--"
148 return (status, output.getvalue())
Daniel Dunbar69e07a72009-06-17 21:33:37 +0000149
150def capture(args):
Daniel Dunbar8fe83f12009-07-25 09:42:24 +0000151 p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Daniel Dunbar69e07a72009-06-17 21:33:37 +0000152 out,_ = p.communicate()
153 return out
154
Daniel Dunbar1db467f2009-07-31 05:54:17 +0000155def inferClang(cfg):
Daniel Dunbar69e07a72009-06-17 21:33:37 +0000156 # Determine which clang to use.
157 clang = os.getenv('CLANG')
158
159 # If the user set clang in the environment, definitely use that and don't
160 # try to validate.
161 if clang:
162 return clang
163
164 # Otherwise look in the path.
Daniel Dunbar1db467f2009-07-31 05:54:17 +0000165 clang = Util.which('clang', cfg.environment['PATH'])
Daniel Dunbar69e07a72009-06-17 21:33:37 +0000166
167 if not clang:
168 print >>sys.stderr, "error: couldn't find 'clang' program, try setting CLANG in your environment"
169 sys.exit(1)
170
171 return clang
172
Daniel Dunbar1db467f2009-07-31 05:54:17 +0000173def inferClangCC(cfg, clang):
Daniel Dunbar69e07a72009-06-17 21:33:37 +0000174 clangcc = os.getenv('CLANGCC')
175
176 # If the user set clang in the environment, definitely use that and don't
177 # try to validate.
178 if clangcc:
179 return clangcc
180
181 # Otherwise try adding -cc since we expect to be looking in a build
182 # directory.
Daniel Dunbar8fe83f12009-07-25 09:42:24 +0000183 if clang.endswith('.exe'):
184 clangccName = clang[:-4] + '-cc.exe'
185 else:
186 clangccName = clang + '-cc'
Daniel Dunbar1db467f2009-07-31 05:54:17 +0000187 clangcc = Util.which(clangccName, cfg.environment['PATH'])
Daniel Dunbar69e07a72009-06-17 21:33:37 +0000188 if not clangcc:
189 # Otherwise ask clang.
190 res = capture([clang, '-print-prog-name=clang-cc'])
191 res = res.strip()
192 if res and os.path.exists(res):
193 clangcc = res
194
195 if not clangcc:
196 print >>sys.stderr, "error: couldn't find 'clang-cc' program, try setting CLANGCC in your environment"
197 sys.exit(1)
198
199 return clangcc
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +0000200
Daniel Dunbardf084892009-07-25 09:53:43 +0000201def getTestOutputBase(dir, testpath):
Daniel Dunbarfecdd002009-07-25 12:05:55 +0000202 """getTestOutputBase(dir, testpath) - Get the full path for temporary files
Daniel Dunbardf084892009-07-25 09:53:43 +0000203 corresponding to the given test path."""
204
205 # Form the output base out of the test parent directory name and the test
206 # name. FIXME: Find a better way to organize test results.
207 return os.path.join(dir,
208 os.path.basename(os.path.dirname(testpath)),
209 os.path.basename(testpath))