blob: 6212f35572287394a231e1809eafe9b84a9f036d [file] [log] [blame]
Daniel Dunbar6fc0bdf2009-03-06 22:20:40 +00001#!/usr/bin/python
2#
3# TestRunner.py - This script is used to run arbitrary unit tests. Unit
4# tests must contain the command used to run them in the input file, starting
5# immediately after a "RUN:" string.
6#
7# This runner recognizes and replaces the following strings in the command:
8#
9# %s - Replaced with the input name of the program, or the program to
10# execute, as appropriate.
11# %llvmgcc - llvm-gcc command
12# %llvmgxx - llvm-g++ command
13# %prcontext - prcontext.tcl script
14# %t - temporary file name (derived from testcase name)
15#
16
17import os
18import sys
19import subprocess
20import errno
21import re
22
23class TestStatus:
24 Pass = 0
25 XFail = 1
26 Fail = 2
27 XPass = 3
28 NoRunLine = 4
29 Invalid = 5
30
31 kNames = ['Pass','XFail','Fail','XPass','NoRunLine','Invalid']
32 @staticmethod
33 def getName(code): return TestStatus.kNames[code]
34
35def mkdir_p(path):
36 if not path:
37 pass
38 elif os.path.exists(path):
39 pass
40 else:
41 parent = os.path.dirname(path)
42 if parent != path:
43 mkdir_p(parent)
44 try:
45 os.mkdir(path)
46 except OSError,e:
47 if e.errno != errno.EEXIST:
48 raise
49
50def remove(path):
51 try:
52 os.remove(path)
53 except OSError:
54 pass
55
56def cat(path, output):
57 f = open(path)
58 output.writelines(f)
59 f.close()
60
61def runOneTest(FILENAME, SUBST, OUTPUT, TESTNAME, CLANG,
62 useValgrind=False,
63 useDGCompat=False,
64 useScript=None,
65 output=sys.stdout):
66 if useValgrind:
67 VG_OUTPUT = '%s.vg'%(OUTPUT,)
68 if os.path.exists:
69 remove(VG_OUTPUT)
70 CLANG = 'valgrind --leak-check=full --quiet --log-file=%s %s'%(VG_OUTPUT, CLANG)
71
72 # Create the output directory if it does not already exist.
73 mkdir_p(os.path.dirname(OUTPUT))
74
75 # FIXME
76 #ulimit -t 40
77
78 # FIXME: Load script once
79 # FIXME: Support "short" script syntax
80
81 if useScript:
82 scriptFile = useScript
83 else:
84 # See if we have a per-dir test script.
85 dirScriptFile = os.path.join(os.path.dirname(FILENAME), 'test.script')
86 if os.path.exists(dirScriptFile):
87 scriptFile = dirScriptFile
88 else:
89 scriptFile = FILENAME
90
91 # Verify the script contains a run line.
92 for ln in open(scriptFile):
93 if 'RUN:' in ln:
94 break
95 else:
96 print >>output, "******************** TEST '%s' HAS NO RUN LINE! ********************"%(TESTNAME,)
97 output.flush()
98 return TestStatus.NoRunLine
99
100 OUTPUT = os.path.abspath(OUTPUT)
101 FILENAME = os.path.abspath(FILENAME)
102 SCRIPT = OUTPUT + '.script'
103 TEMPOUTPUT = OUTPUT + '.tmp'
104
105 substitutions = [('%s',SUBST),
106 ('%llvmgcc','llvm-gcc -emit-llvm -w'),
107 ('%llvmgxx','llvm-g++ -emit-llvm -w'),
108 ('%prcontext','prcontext.tcl'),
109 ('%t',TEMPOUTPUT),
110 ('clang',CLANG)]
111 scriptLines = []
112 xfailLines = []
113 for ln in open(scriptFile):
114 if 'RUN:' in ln:
115 # Isolate run parameters
116 index = ln.index('RUN:')
117 ln = ln[index+4:]
118
119 # Apply substitutions
120 for a,b in substitutions:
121 ln = ln.replace(a,b)
122
123 if useDGCompat:
124 ln = re.sub(r'\{(.*)\}', r'"\1"', ln)
125 scriptLines.append(ln)
126 elif 'XFAIL' in ln:
127 xfailLines.append(ln)
128
129 if xfailLines:
130 print >>output, "XFAILED '%s':"%(TESTNAME,)
131 output.writelines(xfailLines)
132
133 # Write script file
134 f = open(SCRIPT,'w')
135 f.write(''.join(scriptLines))
136 f.close()
137
138 outputFile = open(OUTPUT,'w')
139 p = None
140 try:
141 p = subprocess.Popen(["/bin/sh",SCRIPT],
142 cwd=os.path.dirname(FILENAME),
143 stdout=subprocess.PIPE,
144 stderr=subprocess.PIPE)
145 out,err = p.communicate()
146 outputFile.write(out)
147 outputFile.write(err)
148 SCRIPT_STATUS = p.wait()
149 except KeyboardInterrupt:
150 if p is not None:
151 os.kill(p.pid)
152 raise
153 outputFile.close()
154
155 if xfailLines:
156 SCRIPT_STATUS = not SCRIPT_STATUS
157
158 if useValgrind:
159 VG_STATUS = len(list(open(VG_OUTPUT)))
160 else:
161 VG_STATUS = 0
162
163 if SCRIPT_STATUS or VG_STATUS:
164 print >>output, "******************** TEST '%s' FAILED! ********************"%(TESTNAME,)
165 print >>output, "Command: "
166 output.writelines(scriptLines)
167 if not SCRIPT_STATUS:
168 print >>output, "Output:"
169 else:
170 print >>output, "Incorrect Output:"
171 cat(OUTPUT, output)
172 if VG_STATUS:
173 print >>output, "Valgrind Output:"
174 cat(VG_OUTPUT, output)
175 print >>output, "******************** TEST '%s' FAILED! ********************"%(TESTNAME,)
176 output.flush()
177 if xfailLines:
178 return TestStatus.XPass
179 else:
180 return TestStatus.Fail
181
182 if xfailLines:
183 return TestStatus.XFail
184 else:
185 return TestStatus.Pass
186
187def main():
188 _,path = sys.argv
189 command = path
190 # Use hand concatentation here because we want to override
191 # absolute paths.
192 output = 'Output/' + path + '.out'
193 testname = path
194
195 # Determine which clang to use.
196 CLANG = os.getenv('CLANG')
197 if not CLANG:
198 CLANG = 'clang'
199
200 res = runOneTest(path, command, output, testname, CLANG,
201 useValgrind=bool(os.getenv('VG')),
202 useDGCompat=bool(os.getenv('DG_COMPAT')),
203 useScript=os.getenv("TEST_SCRIPT"))
204
205 sys.exit(res == TestStatus.Fail or res == TestStatus.XPass)
206
207if __name__=='__main__':
208 main()