fixed off-by-one bug
[swftools.git] / lib / as3 / test
blob110099637f150475041c51a9ce9350a37f110adc
1 #!/usr/bin/python
3 # test.py
5 # Run compiler unit tests
7 # Copyright (c) 2008/2009 Matthias Kramm <kramm@quiss.org>
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2 of the License, or
12 # (at your option) any later version.
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this program; if not, write to the Free Software
21 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
23 import sys
24 import os
25 import time
26 import subprocess
27 import marshal
28 import select
29 from optparse import OptionParser
31 CMD_ARGS=[]
32 CMD = "./parser"
33 #CMD="as3compile"
34 CMD_ARGS=["-o","abc.swf"]
36 def check(s):
37 row = None
38 ok = 0
39 for line in s.split("\n"):
40 if line.startswith("[") and line.endswith("]"):
41 continue
42 if not line.strip():
43 continue
44 if not line.startswith("ok"):
45 return 0
46 if line.startswith("ok "):
47 if "/" not in line:
48 return 0
49 i = line.index('/')
50 try:
51 nr,l = int(line[3:i]),int(line[i+1:])
52 except ValueError:
53 return 0
54 if nr<1 or nr>l:
55 return 0
56 if not row:
57 row = [0]*l
58 elif l != len(row):
59 return 0
60 if row[nr-1]:
61 return 0
62 row[nr-1] = 1
63 elif line == "ok":
64 ok = 1
65 if ok:
66 return not row
67 if row:
68 return 0 not in row
69 return 0
71 def runcmd(cmd,args,wait):
72 #fo = os.tmpfile()
73 fi,fo = os.pipe()
74 fo = os.fdopen(fo, "wb")
75 p = subprocess.Popen([cmd] + args, executable=cmd, stdout=fo, stderr=fo)
76 ret = -1
77 output = ""
78 for i in range(wait*10):
79 if fi in select.select([fi],[],[], 0.01)[0]:
80 output += os.read(fi, 8192)
81 if "[exit]" in output:
82 break
83 if "== by" in output:
84 ret = -33
85 break
86 ret = p.poll()
87 if ret is not None:
88 if cmd == "valgrind":
89 # valgrind never returns true
90 ret = 0
91 break
92 time.sleep(0.1)
93 else:
94 os.kill(p.pid, 9)
95 os.system("killall -9 %s >/dev/null 2>/dev/null" % cmd)
96 ret = -1
97 fo.close()
99 if fi in select.select([fi],[],[], 0.01)[0]:
100 output += os.read(fi, 8192)
102 os.close(fi)
103 return ret,output
105 class Cache:
106 def __init__(self, filename):
107 self.filename = filename
108 self.filename_milestone = filename+"_milestone"
109 try:
110 self.filename2status = marshal.load(open(self.filename, "rb"))
111 except IOError:
112 self.filename2status = {}
113 try:
114 self.milestone = marshal.load(open(self.filename_milestone, "rb"))
115 except IOError:
116 self.milestone = {}
118 def parse_args(self):
119 parser = OptionParser()
120 parser.add_option("-d", "--diff", dest="diff", help="Only run tests that failed the last time",action="store_true")
121 parser.add_option("-a", "--all", dest="all", help="Run all tests (also tests expected to fail)",action="store_true")
122 parser.add_option("-t", "--tag", dest="tag", help="Mark the current pass/fail statistic as milestone",action="store_true")
123 parser.add_option("-m", "--valgrind", dest="valgrind", help="Run compiler through valgrind",action="store_true")
124 (options, args) = parser.parse_args()
126 if args and args[0]=="add":
127 self.all = 1
128 self.tag = 1
129 self.milestone[args[1]] = "ok"
130 self.filename2status = self.milestone
131 self.save()
132 sys.exit(0)
134 self.__dict__.update(options.__dict__)
135 self.runtime = 1
136 if self.tag:
137 self.all = 1
138 self.runtime = 3 # allow more time if we're tagging this state
140 if self.valgrind:
141 global CMD,CMD_ARGS
142 CMD_ARGS = [CMD] + CMD_ARGS
143 CMD = "valgrind"
144 self.runtime = 20 # allow even more time for valgrind
146 self.checknum=-1
147 self.checkfile=None
148 if len(args):
149 try:
150 self.checknum = int(args[0])
151 except ValueError:
152 self.checkfile = args[0]
154 @staticmethod
155 def load(filename):
156 return Cache(filename)
158 def save(self):
159 fi = open(self.filename, "wb")
160 marshal.dump(self.filename2status, fi)
161 fi.close()
162 if self.tag:
163 assert(self.all)
164 fi = open(self.filename_milestone, "wb")
165 marshal.dump(self.filename2status, fi)
166 fi.close()
168 def highlight(self, nr, filename):
169 if self.checkfile and filename==self.checkfile:
170 return 1
171 return self.checknum==nr
173 def skip_file(self, nr, filename):
174 if self.checknum>=0 and nr!=self.checknum:
175 return 1
176 if self.checkfile and filename!=self.checkfile:
177 return 1
178 if not self.all and self.milestone.get(filename,"new")!="ok":
179 return 1
180 if self.diff and self.filename2status(filename,"new")=="ok":
181 return 1
182 return 0
184 def file_status(self, filename, status):
185 self.filename2status[filename] = status
187 class TestBase:
188 def __init__(self, cache, nr, file, run):
189 self.cache = cache
190 self.nr = nr
191 self.dorun = run
192 self.file = file
193 self.flash_output = None
194 self.flash_error = None
195 self.compile_output = None
196 self.compile_error = None
198 def compile(self):
199 try: os.unlink("abc.swf");
200 except: pass
201 ret,output = runcmd(CMD,CMD_ARGS+[self.file],wait=cache.runtime)
202 self.compile_error = 0
203 self.compile_output = output
204 self.exit_status = 0
205 if ret:
206 self.compile_output += "\nExit status %d" % (-ret)
207 self.exit_status = -ret
208 self.compile_error = 1
209 return 0
210 if not os.path.isfile("abc.swf"):
211 self.compile_error = 1
212 return 0
213 return 1
215 def run(self):
216 ret,output = runcmd("flashplayer",[os.path.join(os.getcwd(),"abc.swf")],wait=cache.runtime)
217 os.system("killall flashplayer")
218 self.flash_output = output
220 if not check(self.flash_output):
221 self.flash_error = 1
222 return 0
223 return 1
225 def doprint(self):
226 print self.r(str(self.nr),3)," ",
227 if self.compile_error:
228 if self.dorun:
229 if self.exit_status == 11:
230 print "crash"," - ",
231 else:
232 print "err "," - ",
233 else:
234 print "err "," ",
235 else:
236 print "ok ",
237 if self.dorun:
238 if not self.flash_error:
239 print "ok ",
240 else:
241 print "err",
242 else:
243 print " ",
244 print " ",
245 print self.file
247 def doprintlong(self):
248 print self.nr, self.file
249 print "================================"
250 print "compile:", (self.compile_error and "error" or "ok")
251 print self.compile_output
252 if not self.dorun:
253 return
254 print "================================"
255 print "run:", (self.flash_error and "error" or "ok")
256 print self.flash_output
257 print "================================"
259 def r(self,s,l):
260 if(len(s)>=l):
261 return s
262 return (" "*(l-len(s))) + s
263 def l(self,s,l):
264 if(len(s)>=l):
265 return s
266 return s + (" "*(l-len(s)))
268 class Test(TestBase):
269 def __init__(self, cache, nr, file):
270 TestBase.__init__(self, cache, nr, file, run=1)
271 if self.compile() and self.run():
272 cache.file_status(file, "ok")
273 else:
274 cache.file_status(file, "error")
276 class ErrTest(TestBase):
277 def __init__(self, cache, nr, file):
278 TestBase.__init__(self, cache, nr, file, run=0)
279 if self.compile():
280 cache.file_status(file, "error")
281 self.compile_error = True
282 else:
283 cache.file_status(file, "ok")
284 self.compile_error = False
286 class Suite:
287 def __init__(self, cache, dir):
288 self.dir = dir
289 self.cache = cache
290 self.errtest = "err" in dir
291 def run(self, nr):
292 print "-"*40,"tests \""+self.dir+"\"","-"*40
293 for file in sorted(os.listdir(self.dir)):
294 if not file.endswith(".as"):
295 continue
296 nr = nr + 1
297 file = os.path.join(self.dir, file)
299 if cache.skip_file(nr, file):
300 continue
302 if self.errtest:
303 test = ErrTest(cache, nr, file)
304 else:
305 test = Test(cache, nr, file)
307 if not cache.highlight(nr, file):
308 test.doprint()
309 else:
310 test.doprintlong()
311 return nr
313 cache = Cache.load(".tests.cache")
314 cache.parse_args()
316 nr = 0
317 nr = Suite(cache, "err").run(nr)
318 nr = Suite(cache, "ok").run(nr)
320 cache.save()