]> git.cworth.org Git - apitrace-tests/blob - driver.py
60699099e2b9623693131839c292f44e26df989a
[apitrace-tests] / driver.py
1 #!/usr/bin/env python
2 ##########################################################################
3 #
4 # Copyright 2011 Jose Fonseca
5 # All Rights Reserved.
6 #
7 # Permission is hereby granted, free of charge, to any person obtaining a copy
8 # of this software and associated documentation files (the "Software"), to deal
9 # in the Software without restriction, including without limitation the rights
10 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 # copies of the Software, and to permit persons to whom the Software is
12 # furnished to do so, subject to the following conditions:
13 #
14 # The above copyright notice and this permission notice shall be included in
15 # all copies or substantial portions of the Software.
16 #
17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 # THE SOFTWARE.
24 #
25 ##########################################################################/
26
27 '''Main test driver.'''
28
29
30 import optparse
31 import os.path
32 import platform
33 import re
34 import shutil
35 import subprocess
36 import sys
37 import time
38
39
40 def popen(command, *args, **kwargs):
41     if kwargs.get('cwd', None) is not None:
42         sys.stdout.write('cd %s && ' % kwargs['cwd'])
43     if 'env' in kwargs:
44         for name, value in kwargs['env'].iteritems():
45             if value != os.environ.get(name, None):
46                 sys.stdout.write('%s=%s ' % (name, value))
47     sys.stdout.write(' '.join(command) + '\n')
48     sys.stdout.flush()
49     return subprocess.Popen(command, *args, **kwargs)
50
51
52 def _get_build_path(path):
53     if options.build is not None:
54         path = os.path.abspath(os.path.join(options.build, path))
55     if not os.path.exists(path):
56         sys.stderr.write('error: %s does not exist\n' % path)
57         sys.exit(1)
58     return path
59
60 def _get_build_program(program):
61     if platform.system() == 'Windows':
62         program += '.exe'
63     return _get_build_path(program)
64
65
66 class TestCase:
67
68     cmd = None
69     cwd = None
70
71     api = 'gl'
72     max_frames = None
73     trace_file = None
74
75     expected_dump = None
76
77     def standalone(self):
78         if not self.cmd:
79             return
80
81         p = popen(self.cmd, cwd=self.cwd)
82         p.wait()
83         if p.returncode:
84             self.skip('application returned code %i' % p.returncode)
85
86     api_map = {
87         'gl': 'gl',
88         'egl_gl': 'egl',
89         'egl_gles1': 'egl',
90         'egl_gles2': 'egl',
91     }
92
93     def trace(self):
94         if not self.cmd:
95             return
96
97         if self.trace_file is None:
98             name = os.path.basename(self.cmd[0])
99             self.trace_file = os.path.abspath(os.path.join(self.results, name + '.trace'))
100         if os.path.exists(self.trace_file):
101             os.remove(self.trace_file)
102         else:
103             trace_dir = os.path.dirname(self.trace_file)
104             if not os.path.exists(trace_dir):
105                 os.makedirs(trace_dir)
106
107         cmd = self.cmd
108         env = os.environ.copy()
109         
110         system = platform.system()
111         local_wrapper = None
112         if system == 'Windows':
113             wrapper = _get_build_path('wrappers/opengl32.dll')
114             local_wrapper = os.path.join(os.path.dirname(self.cmd[0]), os.path.basename(wrapper))
115             shutil.copy(wrapper, local_wrapper)
116             env['TRACE_FILE'] = self.trace_file
117         else:
118             apitrace = _get_build_program('apitrace')
119             cmd = [
120                 apitrace, 'trace', 
121                 '--api', self.api_map[self.api],
122                 '--output', self.trace_file,
123                 '--'
124             ] + cmd
125         if self.max_frames is not None:
126             env['TRACE_FRAMES'] = str(self.max_frames)
127
128         try:
129             p = popen(cmd, env=env, cwd=self.cwd)
130             p.wait()
131         finally:
132             if local_wrapper is not None:
133                 os.remove(local_wrapper)
134
135         if not os.path.exists(self.trace_file):
136             self.fail('no trace file generated\n')
137     
138     call_re = re.compile(r'^([0-9]+) (\w+)\(')
139
140     def dump(self):
141
142         cmd = [_get_build_program('apitrace'), 'dump', '--color=never', self.trace_file]
143         p = popen(cmd, stdout=subprocess.PIPE)
144
145         swapbuffers = 0
146         flushes = 0
147
148         ref_line = ''
149         src_lines = []
150         if self.ref_dump is not None:
151             ref = open(self.ref_dump, 'rt')
152             ref_line = ref.readline().rstrip()
153         for line in p.stdout:
154             line = line.rstrip()
155             sys.stdout.write(line + '\n')
156             mo = self.call_re.match(line)
157             if mo:
158                 call_no = int(mo.group(1))
159                 function_name = mo.group(2)
160                 if function_name == 'glXSwapBuffers':
161                     swapbuffers += 1
162                 if function_name in ('glFlush', 'glFinish'):
163                     flushes += 1
164                 src_line = line[mo.start(2):]
165             else:
166                 src_line = line
167             if ref_line:
168                 if src_line == ref_line:
169                     sys.stdout.write(src_line + '\n')
170                     ref_line = ref.readline().rstrip()
171                     src_lines = []
172                 else:
173                     src_lines.append(src_line)
174
175         p.wait()
176         if p.returncode != 0:
177             self.fail('`apitrace dump` returned code %i' % p.returncode)
178         if ref_line:
179             if src_lines:
180                 self.fail('missing call `%s` (found `%s`)' % (ref_line, src_lines[0]))
181             else:
182                 self.fail('missing call %s' % ref_line)
183
184     def retrace(self):
185         retrace = self.api_map[self.api] + 'retrace'
186         args = [_get_build_path(retrace)]
187         args += [self.trace_file]
188         p = popen(args, stdout=subprocess.PIPE)
189         p.wait()
190         if p.returncode != 0:
191             self.fail('`%s` returned code %i' % (retrace, p.returncode))
192
193     def run(self):
194         self.standalone()
195         self.trace()
196         self.dump()
197         self.retrace()
198
199         self.pass_()
200         return
201
202         ref_prefix = os.path.abspath(os.path.join(self.results, self.name + '.ref.'))
203         src_prefix = os.path.join(self.results, self.name + '.src.')
204         diff_prefix = os.path.join(self.results, self.name + '.diff.')
205
206
207         if not os.path.isfile(trace):
208             sys.stdout.write('SKIP (no trace)\n')
209             return
210
211         retrace = self.api_map[self.api] + 'retrace'
212         args = [_get_build_path(retrace)]
213         if swapbuffers:
214             args += ['-db']
215             frames = swapbuffers
216         else:
217             args += ['-sb']
218             frames = flushes
219         args += ['-s', src_prefix]
220         args += [trace]
221         p = popen(args, stdout=subprocess.PIPE)
222         image_re = re.compile(r'^Wrote (.*\.png)$')
223         images = []
224         for line in p.stdout:
225             line = line.rstrip()
226             mo = image_re.match(line)
227             if mo:
228                 image = mo.group(1)
229                 if image.startswith(src_prefix):
230                     image = image[len(src_prefix):]
231                     images.append(image)
232         p.wait()
233         if p.returncode != 0:
234             sys.stdout.write('FAIL (glretrace)\n')
235             return
236
237         for image in images:
238             ref_image = ref_prefix + image
239             src_image = src_prefix + image
240             diff_image = diff_prefix + image
241             
242             if not os.path.isfile(ref_image):
243                 continue
244             assert os.path.isfile(src_image)
245
246             comparer = Comparer(ref_image, src_image)
247             match = comparer.ae()
248             sys.stdout.write('%s: %s bits\n' % (image, comparer.precision()))
249             if not match:
250                 comparer.write_diff(diff_image)
251                 #report.add_snapshot(ref_image, src_image, diff_image)
252                 sys.stdout.write('FAIL (snapshot)\n')
253                 return
254
255     def fail(self, reason=None):
256         self._exit('FAIL', 1, reason)
257
258     def skip(self, reason=None):
259         self._exit('SKIP', 0, reason)
260
261     def pass_(self, reason=None):
262         self._exit('PASS', 0, reason)
263
264     def _exit(self, status, code, reason=None):
265         if reason is None:
266             reason = ''
267         else:
268             reason = ' (%s)' % reason
269         sys.stdout.write('%s%s\n' % (status, reason))
270         sys.exit(code)
271
272
273
274 def main():
275     global options
276
277     # Parse command line options
278     optparser = optparse.OptionParser(
279         usage='\n\t%prog [options] -- [TRACE|PROGRAM] ...',
280         version='%%prog')
281     optparser.add_option(
282         '-a', '--api', metavar='API',
283         type='string', dest='api', default='gl',
284         help='api to trace')
285     optparser.add_option(
286         '-B', '--build', metavar='PATH',
287         type='string', dest='build', default='..',
288         help='path to apitrace build')
289     optparser.add_option(
290         '-C', '--directory', metavar='PATH',
291         type='string', dest='cwd', default=None,
292         help='change to directory')
293     optparser.add_option(
294         '-R', '--results', metavar='PATH',
295         type='string', dest='results', default='.',
296         help='results directory [default=%default]')
297     optparser.add_option(
298         '--ref-dump', metavar='PATH',
299         type='string', dest='ref_dump', default=None,
300         help='reference dump')
301
302     (options, args) = optparser.parse_args(sys.argv[1:])
303     if not args:
304         optparser.error('an argument must be specified')
305
306     if not os.path.exists(options.results):
307         os.makedirs(options.results)
308
309     test = TestCase()
310
311     if args[0].endswith('.trace'):
312         test.trace_file = args[0]
313     else:
314         test.cmd = args
315     test.cwd = options.cwd
316     test.api = options.api
317     test.ref_dump = options.ref_dump
318     test.results = options.results
319
320     test.run()
321
322
323 if __name__ == '__main__':
324     main()