dc79c9048d2c0dcf6826428312085ca6c3636e8d
[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         self.checkfile=None
134         if len(args):
135             try:
136                 self.checknum = int(args[0])
137             except ValueError:
138                 self.checkfile = args[0]
139
140     @staticmethod
141     def load(filename):
142         return Cache(filename)
143
144     def save(self):
145         fi = open(self.filename, "wb")
146         marshal.dump(self.filename2status, fi)
147         fi.close()
148         if self.tag:
149             assert(self.all)
150             fi = open(self.filename_milestone, "wb")
151             marshal.dump(self.filename2status, fi)
152             fi.close()
153
154     def highlight(self, nr, filename):
155         if self.checkfile and filename==self.checkfile:
156             return 1
157         return self.checknum==nr
158
159     def skip_file(self, nr, filename):
160         if self.checknum>=0 and nr!=self.checknum:
161             return 1
162         if self.checkfile and filename!=self.checkfile:
163             return 1
164         if not self.all and self.milestone.get(filename,"new")!="ok":
165             return 1
166         if self.diff and self.filename2status(filename,"new")=="ok":
167             return 1
168         return 0
169
170     def file_status(self, filename, status):
171         self.filename2status[filename] = status
172
173 class TestBase:
174     def __init__(self, cache, nr, file, run):
175         self.cache = cache
176         self.nr = nr
177         self.dorun = run
178         self.file = file
179         self.flash_output = None
180         self.flash_error = None
181         self.compile_output = None
182         self.compile_error = None
183
184     def compile(self):
185         try: os.unlink("abc.swf");
186         except: pass
187         ret,output = runcmd(CMD,CMD_ARGS+[self.file],wait=cache.runtime)
188         self.compile_error = 0
189         self.compile_output = output
190         self.exit_status = 0
191         if ret:
192             self.compile_output += "\nExit status %d" % (-ret)
193             self.exit_status = -ret
194             self.compile_error = 1
195             return 0
196         if not os.path.isfile("abc.swf"):
197             self.compile_error = 1
198             return 0
199         return 1
200
201     def run(self):
202         ret,output = runcmd("flashplayer",["abc.swf"],wait=cache.runtime)
203         os.system("killall flashplayer")
204         self.flash_output = output
205         
206         if not check(self.flash_output):
207             self.flash_error = 1
208             return 0
209         return 1
210
211     def doprint(self):
212         print self.r(str(self.nr),3)," ",
213         if self.compile_error:
214             if self.dorun:
215                 if self.exit_status == 11:
216                     print "crash"," - ",
217                 else:
218                     print "err  "," - ",
219             else:
220                 print "err  ","   ",
221         else:
222             print "ok   ",
223             if self.dorun:
224                 if not self.flash_error:
225                     print "ok ",
226                 else:
227                     print "err",
228             else:
229                 print "   ",
230         print " ",
231         print self.file
232
233     def doprintlong(self):
234         print self.nr, self.file
235         print "================================"
236         print "compile:", (self.compile_error and "error" or "ok")
237         print self.compile_output
238         if not self.dorun:
239             return
240         print "================================"
241         print "run:", (self.flash_error and "error" or "ok")
242         print self.flash_output
243         print "================================"
244
245     def r(self,s,l):
246         if(len(s)>=l):
247             return s
248         return (" "*(l-len(s))) + s
249     def l(self,s,l):
250         if(len(s)>=l):
251             return s
252         return s + (" "*(l-len(s)))
253
254 class Test(TestBase):
255     def __init__(self, cache, nr, file):
256         TestBase.__init__(self, cache, nr, file, run=1)
257         if self.compile() and self.run():
258             cache.file_status(file, "ok")
259         else:
260             cache.file_status(file, "error")
261
262 class ErrTest(TestBase):
263     def __init__(self, cache, nr, file):
264         TestBase.__init__(self, cache, nr, file, run=0)
265         if self.compile():
266             cache.file_status(file, "error")
267             self.compile_error = True
268         else:
269             cache.file_status(file, "ok")
270             self.compile_error = False
271
272 class Suite:
273     def __init__(self, cache, dir):
274         self.dir = dir
275         self.cache = cache
276         self.errtest = "err" in dir
277     def run(self, nr):
278         print "-"*40,"tests \""+self.dir+"\"","-"*40
279         for file in sorted(os.listdir(self.dir)):
280             if not file.endswith(".as"):
281                 continue
282             nr = nr + 1
283             file = os.path.join(self.dir, file)
284
285             if cache.skip_file(nr, file):
286                 continue
287
288             if self.errtest:
289                 test = ErrTest(cache, nr, file)
290             else:
291                 test = Test(cache, nr, file)
292
293             if not cache.highlight(nr, file):
294                 test.doprint()
295             else:
296                 test.doprintlong()
297         return nr
298
299 cache = Cache.load(".tests.cache")
300 cache.parse_args()
301
302 nr = 0
303 nr = Suite(cache, "err").run(nr)
304 nr = Suite(cache, "ok").run(nr)
305
306 cache.save()