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