added support for forward references
[swftools.git] / lib / as3 / test
1 #!/usr/bin/python
2 #
3 # test.py
4 #
5 # Run compiler unit tests
6 #
7 # Copyright (c) 2008/2009 Matthias Kramm <kramm@quiss.org>
8 #
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.
13 #
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.
18 #
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 */
22
23 import sys
24 import os
25 import time
26 import subprocess
27 import marshal
28 import select
29 from optparse import OptionParser
30
31 def check(s):
32     row = None
33     ok = 0
34     for line in s.split("\n"):
35         if line.startswith("[") and line.endswith("]"):
36             continue
37         if not line.strip():
38             continue
39         if not line.startswith("ok"):
40             return 0
41         if line.startswith("ok "):
42             if "/" not in line:
43                 return 0
44             i = line.index('/')
45             try:
46                 nr,len = int(line[3:i]),int(line[i+1:])
47             except ValueError:
48                 return 0
49             if nr<1 or nr>len:
50                 return 0
51             if not row:
52                 row = [0]*len
53             if row[nr-1]:
54                 return 0
55             row[nr-1] = 1
56         elif line == "ok":
57             ok = 1
58     if ok:
59         return not row
60     if row:
61         return 0 not in row
62     return 0
63
64 def runcmd(cmd,args,wait):
65     #fo = os.tmpfile()
66     fi,fo = os.pipe()
67     fo = os.fdopen(fo, "wb")
68     p = subprocess.Popen([cmd] + args, executable=cmd, stdout=fo, stderr=fo)
69     ret = -1
70     output = ""
71     for i in range(wait*10):
72         if fi in select.select([fi],[],[], 0.01)[0]:
73             output += os.read(fi, 8192)
74             if "[exit]" in output:
75                 break
76             if "rror" in output:
77                 break
78         ret = p.poll()
79         if ret is not None:
80             break
81         time.sleep(0.1)
82     else:
83         os.kill(p.pid, 9)
84         os.system("killall -9 %s >/dev/null 2>/dev/null" % cmd)
85     fo.close()
86    
87     if fi in select.select([fi],[],[], 0.01)[0]:
88         output += os.read(fi, 8192)
89
90     os.close(fi)
91     return ret,output
92
93 class Cache:
94     def __init__(self, filename):
95         self.filename = filename
96         self.filename_milestone = filename+"_milestone"
97         try:
98             self.filename2status = marshal.load(open(self.filename, "rb"))
99         except IOError:
100             self.filename2status = {}
101         try:
102             self.milestone = marshal.load(open(self.filename_milestone, "rb"))
103         except IOError:
104             self.milstone = {}
105
106     def parse_args(self):
107         parser = OptionParser()
108         parser.add_option("-d", "--diff", dest="diff", help="Only run tests that failed the last time",action="store_true")
109         parser.add_option("-a", "--all", dest="all", help="Run all tests (also tests expected fail)",action="store_true")
110         parser.add_option("-t", "--tag", dest="tag", help="Mark the current pass/fail statistic as milestone",action="store_true")
111         (options, args) = parser.parse_args()
112
113         if args and args[0]=="add":
114             self.all = 1
115             self.tag = 1
116             self.milestone[args[1]] = "ok"
117             self.filename2status = self.milestone
118             self.save()
119             sys.exit(0)
120
121         self.__dict__.update(options.__dict__)
122         self.runtime = 1
123         if self.tag: 
124             self.all = 1
125             self.runtime = 5 # allow more time if we're tagging this state
126
127         self.checknum=-1
128         if len(args):
129             self.checknum = int(args[0])
130
131     @staticmethod
132     def load(filename):
133         return Cache(filename)
134
135     def save(self):
136         fi = open(self.filename, "wb")
137         marshal.dump(self.filename2status, fi)
138         fi.close()
139         if self.tag:
140             assert(self.all)
141             fi = open(self.filename_milestone, "wb")
142             marshal.dump(self.filename2status, fi)
143             fi.close()
144
145     def highlight(self, nr, filename):
146         return self.checknum==nr
147
148     def skip_file(self, nr, filename):
149         if self.checknum>=0 and nr!=self.checknum:
150             return 1
151         if not self.all and self.milestone.get(filename,"new")!="ok":
152             return 1
153         if self.diff and self.filename2status(filename,"new")=="ok":
154             return 1
155         return 0
156
157     def file_status(self, filename, status):
158         self.filename2status[filename] = status
159
160 class TestBase:
161     def __init__(self, cache, nr, file, run):
162         self.cache = cache
163         self.nr = nr
164         self.dorun = run
165         self.file = file
166         self.flash_output = None
167         self.flash_error = None
168         self.compile_output = None
169         self.compile_error = None
170
171     def compile(self):
172         try: os.unlink("abc.swf");
173         except: pass
174         ret,output = runcmd("./parser",[self.file],wait=cache.runtime)
175         self.compile_error = 0
176         self.compile_output = output
177         self.exit_status = 0
178         if ret:
179             self.compile_output += "\nExit status %d" % (-ret)
180             self.exit_status = -ret
181             self.compile_error = 1
182             return 0
183         if not os.path.isfile("abc.swf"):
184             self.compile_error = 1
185             return 0
186         return 1
187
188     def run(self):
189         ret,output = runcmd("flashplayer",["abc.swf"],wait=cache.runtime)
190         os.system("killall flashplayer")
191         self.flash_output = output
192         
193         if not check(self.flash_output):
194             self.flash_error = 1
195             return 0
196         return 1
197
198     def doprint(self):
199         print self.r(str(self.nr),3)," ",
200         if self.compile_error:
201             if self.dorun:
202                 if self.exit_status == 11:
203                     print "crash"," - ",
204                 else:
205                     print "err  "," - ",
206             else:
207                 print "err  ","   ",
208         else:
209             print "ok   ",
210             if self.dorun:
211                 if not self.flash_error:
212                     print "ok ",
213                 else:
214                     print "err",
215             else:
216                 print "   ",
217         print " ",
218         print self.file
219
220     def doprintlong(self):
221         print self.nr, self.file
222         print "================================"
223         print "compile:", (self.compile_error and "error" or "ok")
224         print self.compile_output
225         if not self.dorun:
226             return
227         print "================================"
228         print "run:", (self.flash_error and "error" or "ok")
229         print self.flash_output
230         print "================================"
231
232     def r(self,s,l):
233         if(len(s)>=l):
234             return s
235         return (" "*(l-len(s))) + s
236     def l(self,s,l):
237         if(len(s)>=l):
238             return s
239         return s + (" "*(l-len(s)))
240
241 class Test(TestBase):
242     def __init__(self, cache, nr, file):
243         TestBase.__init__(self, cache, nr, file, run=1)
244         if self.compile() and self.run():
245             cache.file_status(file, "ok")
246         else:
247             cache.file_status(file, "error")
248
249 class ErrTest(TestBase):
250     def __init__(self, cache, nr, file):
251         TestBase.__init__(self, cache, nr, file, run=0)
252         if self.compile():
253             cache.file_status(file, "error")
254             self.compile_error = True
255         else:
256             cache.file_status(file, "ok")
257             self.compile_error = False
258
259 class Suite:
260     def __init__(self, cache, dir):
261         self.dir = dir
262         self.cache = cache
263         self.errtest = "err" in dir
264     def run(self, nr):
265         print "-"*40,"tests \""+self.dir+"\"","-"*40
266         for file in sorted(os.listdir(self.dir)):
267             if not file.endswith(".as"):
268                 continue
269             nr = nr + 1
270             file = os.path.join(self.dir, file)
271
272             if cache.skip_file(nr, file):
273                 continue
274
275             if self.errtest:
276                 test = ErrTest(cache, nr, file)
277             else:
278                 test = Test(cache, nr, file)
279
280             if not cache.highlight(nr, file):
281                 test.doprint()
282             else:
283                 test.doprintlong()
284         return nr
285
286 cache = Cache.load(".tests.cache")
287 cache.parse_args()
288
289 nr = 0
290 nr = Suite(cache, "err").run(nr)
291 nr = Suite(cache, "ok").run(nr)
292
293 cache.save()