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