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