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