blob: 23038b3e2166cd7f8105fa65285058d630c8fafd [file] [log] [blame]
Daniel Dunbar69e07a72009-06-17 21:33:37 +00001import os
Daniel Dunbar8fe83f12009-07-25 09:42:24 +00002import platform
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +00003import re
Daniel Dunbar69e07a72009-06-17 21:33:37 +00004import signal
5import subprocess
6import sys
7
Daniel Dunbar1db467f2009-07-31 05:54:17 +00008import Util
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +00009
Daniel Dunbar8fe83f12009-07-25 09:42:24 +000010kSystemName = platform.system()
11
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +000012class TestStatus:
13 Pass = 0
14 XFail = 1
15 Fail = 2
16 XPass = 3
Daniel Dunbar8bf0ccd2009-07-25 12:47:38 +000017 Invalid = 4
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +000018
Daniel Dunbar8bf0ccd2009-07-25 12:47:38 +000019 kNames = ['Pass','XFail','Fail','XPass','Invalid']
Daniel Dunbarfbbb1e72009-07-02 23:58:07 +000020 @staticmethod
Daniel Dunbar8afede22009-07-02 23:56:37 +000021 def getName(code):
22 return TestStatus.kNames[code]
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +000023
Daniel Dunbar5928ccd2009-08-01 04:06:02 +000024def executeScript(cfg, script, commands, cwd):
Daniel Dunbar10aebbb2009-07-25 15:26:08 +000025 # Write script file
26 f = open(script,'w')
27 if kSystemName == 'Windows':
28 f.write('\nif %ERRORLEVEL% NEQ 0 EXIT\n'.join(commands))
29 else:
30 f.write(' &&\n'.join(commands))
31 f.write('\n')
32 f.close()
33
34 if kSystemName == 'Windows':
35 command = ['cmd','/c', script]
36 else:
37 command = ['/bin/sh', script]
Daniel Dunbar5928ccd2009-08-01 04:06:02 +000038 if cfg.useValgrind:
Daniel Dunbar9a676b72009-07-29 02:57:25 +000039 # FIXME: Running valgrind on sh is overkill. We probably could just
Daniel Dunbar5928ccd2009-08-01 04:06:02 +000040 # run on clang with no real loss.
Daniel Dunbar9a676b72009-07-29 02:57:25 +000041 command = ['valgrind', '-q',
42 '--tool=memcheck', '--leak-check=no', '--trace-children=yes',
43 '--error-exitcode=123'] + command
Daniel Dunbar10aebbb2009-07-25 15:26:08 +000044
45 p = subprocess.Popen(command, cwd=cwd,
46 stdin=subprocess.PIPE,
47 stdout=subprocess.PIPE,
48 stderr=subprocess.PIPE,
Daniel Dunbar1db467f2009-07-31 05:54:17 +000049 env=cfg.environment)
Daniel Dunbar10aebbb2009-07-25 15:26:08 +000050 out,err = p.communicate()
51 exitCode = p.wait()
52
53 # Detect Ctrl-C in subprocess.
54 if exitCode == -signal.SIGINT:
55 raise KeyboardInterrupt
56
57 return out, err, exitCode
58
Daniel Dunbara957d992009-07-25 14:46:05 +000059import StringIO
Daniel Dunbar5928ccd2009-08-01 04:06:02 +000060def runOneTest(cfg, testPath, tmpBase):
Daniel Dunbara957d992009-07-25 14:46:05 +000061 # Make paths absolute.
62 tmpBase = os.path.abspath(tmpBase)
63 testPath = os.path.abspath(testPath)
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +000064
65 # Create the output directory if it does not already exist.
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +000066
Daniel Dunbar1db467f2009-07-31 05:54:17 +000067 Util.mkdir_p(os.path.dirname(tmpBase))
Daniel Dunbara957d992009-07-25 14:46:05 +000068 script = tmpBase + '.script'
Daniel Dunbar8fe83f12009-07-25 09:42:24 +000069 if kSystemName == 'Windows':
Daniel Dunbara957d992009-07-25 14:46:05 +000070 script += '.bat'
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +000071
Daniel Dunbara957d992009-07-25 14:46:05 +000072 substitutions = [('%s', testPath),
73 ('%S', os.path.dirname(testPath)),
74 ('%t', tmpBase + '.tmp'),
Daniel Dunbar5928ccd2009-08-01 04:06:02 +000075 (' clang ', ' ' + cfg.clang + ' '),
76 (' clang-cc ', ' ' + cfg.clangcc + ' ')]
Daniel Dunbar025f80d2009-07-25 11:27:37 +000077
78 # Collect the test lines from the script.
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +000079 scriptLines = []
80 xfailLines = []
Daniel Dunbara957d992009-07-25 14:46:05 +000081 for ln in open(testPath):
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +000082 if 'RUN:' in ln:
Daniel Dunbar025f80d2009-07-25 11:27:37 +000083 # Isolate the command to run.
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +000084 index = ln.index('RUN:')
85 ln = ln[index+4:]
Daniel Dunbar025f80d2009-07-25 11:27:37 +000086
Daniel Dunbar322f7892009-07-25 12:23:35 +000087 # Strip trailing newline.
88 scriptLines.append(ln)
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +000089 elif 'XFAIL' in ln:
90 xfailLines.append(ln)
Daniel Dunbar025f80d2009-07-25 11:27:37 +000091
92 # FIXME: Support something like END, in case we need to process large
93 # files.
Daniel Dunbara957d992009-07-25 14:46:05 +000094
95 # Verify the script contains a run line.
96 if not scriptLines:
97 return (TestStatus.Fail, "Test has no run line!")
Daniel Dunbar322f7892009-07-25 12:23:35 +000098
99 # Apply substitutions to the script.
100 def processLine(ln):
101 # Apply substitutions
102 for a,b in substitutions:
103 ln = ln.replace(a,b)
104
Daniel Dunbar322f7892009-07-25 12:23:35 +0000105 # Strip the trailing newline and any extra whitespace.
106 return ln.strip()
107 scriptLines = map(processLine, scriptLines)
Daniel Dunbar025f80d2009-07-25 11:27:37 +0000108
109 # Validate interior lines for '&&', a lovely historical artifact.
110 for i in range(len(scriptLines) - 1):
111 ln = scriptLines[i]
112
113 if not ln.endswith('&&'):
Daniel Dunbara957d992009-07-25 14:46:05 +0000114 return (TestStatus.Fail,
Daniel Dunbar67796472009-07-27 19:01:13 +0000115 ("MISSING \'&&\': %s\n" +
116 "FOLLOWED BY : %s\n") % (ln, scriptLines[i + 1]))
Daniel Dunbar025f80d2009-07-25 11:27:37 +0000117
118 # Strip off '&&'
119 scriptLines[i] = ln[:-2]
Daniel Dunbar025f80d2009-07-25 11:27:37 +0000120
Daniel Dunbar1db467f2009-07-31 05:54:17 +0000121 out, err, exitCode = executeScript(cfg, script, scriptLines,
Daniel Dunbar5928ccd2009-08-01 04:06:02 +0000122 os.path.dirname(testPath))
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +0000123 if xfailLines:
Daniel Dunbara957d992009-07-25 14:46:05 +0000124 ok = exitCode != 0
125 status = (TestStatus.XPass, TestStatus.XFail)[ok]
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +0000126 else:
Daniel Dunbara957d992009-07-25 14:46:05 +0000127 ok = exitCode == 0
128 status = (TestStatus.Fail, TestStatus.Pass)[ok]
129
130 if ok:
131 return (status,'')
132
133 output = StringIO.StringIO()
134 print >>output, "Script:"
135 print >>output, "--"
136 print >>output, '\n'.join(scriptLines)
137 print >>output, "--"
138 print >>output, "Exit Code: %r" % exitCode
139 print >>output, "Command Output (stdout):"
140 print >>output, "--"
141 output.write(out)
142 print >>output, "--"
143 print >>output, "Command Output (stderr):"
144 print >>output, "--"
145 output.write(err)
146 print >>output, "--"
147 return (status, output.getvalue())
Daniel Dunbar69e07a72009-06-17 21:33:37 +0000148
149def capture(args):
Daniel Dunbar8fe83f12009-07-25 09:42:24 +0000150 p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Daniel Dunbar69e07a72009-06-17 21:33:37 +0000151 out,_ = p.communicate()
152 return out
153
Daniel Dunbar1db467f2009-07-31 05:54:17 +0000154def inferClang(cfg):
Daniel Dunbar69e07a72009-06-17 21:33:37 +0000155 # Determine which clang to use.
156 clang = os.getenv('CLANG')
157
158 # If the user set clang in the environment, definitely use that and don't
159 # try to validate.
160 if clang:
161 return clang
162
163 # Otherwise look in the path.
Daniel Dunbar1db467f2009-07-31 05:54:17 +0000164 clang = Util.which('clang', cfg.environment['PATH'])
Daniel Dunbar69e07a72009-06-17 21:33:37 +0000165
166 if not clang:
167 print >>sys.stderr, "error: couldn't find 'clang' program, try setting CLANG in your environment"
168 sys.exit(1)
169
170 return clang
171
Daniel Dunbar1db467f2009-07-31 05:54:17 +0000172def inferClangCC(cfg, clang):
Daniel Dunbar69e07a72009-06-17 21:33:37 +0000173 clangcc = os.getenv('CLANGCC')
174
175 # If the user set clang in the environment, definitely use that and don't
176 # try to validate.
177 if clangcc:
178 return clangcc
179
180 # Otherwise try adding -cc since we expect to be looking in a build
181 # directory.
Daniel Dunbar8fe83f12009-07-25 09:42:24 +0000182 if clang.endswith('.exe'):
183 clangccName = clang[:-4] + '-cc.exe'
184 else:
185 clangccName = clang + '-cc'
Daniel Dunbar1db467f2009-07-31 05:54:17 +0000186 clangcc = Util.which(clangccName, cfg.environment['PATH'])
Daniel Dunbar69e07a72009-06-17 21:33:37 +0000187 if not clangcc:
188 # Otherwise ask clang.
189 res = capture([clang, '-print-prog-name=clang-cc'])
190 res = res.strip()
191 if res and os.path.exists(res):
192 clangcc = res
193
194 if not clangcc:
195 print >>sys.stderr, "error: couldn't find 'clang-cc' program, try setting CLANGCC in your environment"
196 sys.exit(1)
197
198 return clangcc
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +0000199
Daniel Dunbardf084892009-07-25 09:53:43 +0000200def getTestOutputBase(dir, testpath):
Daniel Dunbarfecdd002009-07-25 12:05:55 +0000201 """getTestOutputBase(dir, testpath) - Get the full path for temporary files
Daniel Dunbardf084892009-07-25 09:53:43 +0000202 corresponding to the given test path."""
203
204 # Form the output base out of the test parent directory name and the test
205 # name. FIXME: Find a better way to organize test results.
206 return os.path.join(dir,
207 os.path.basename(os.path.dirname(testpath)),
208 os.path.basename(testpath))