]> git.cworth.org Git - apitrace-tests/blob - driver.py
Do a trial (e)gltrace run as part of the test.
[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     api = 'gl'
69     max_frames = None
70     trace_file = None
71
72     def __init__(self, name, args, cwd=None, build=None, results = '.'):
73         self.name = name
74         self.args = args
75         self.cwd = cwd
76         self.build = build
77         self.results = results
78
79         if not os.path.exists(results):
80             os.makedirs(results)
81
82     expected_dump = None
83
84     def standalone(self):
85         p = popen(self.args, cwd=self.cwd)
86         p.wait()
87         if p.returncode:
88             self.skip('application returned code %i' % p.returncode)
89
90     api_map = {
91         'gl': 'gl',
92         'egl_gl': 'egl',
93         'egl_gles1': 'egl',
94         'egl_gles2': 'egl',
95     }
96
97     def trace(self):
98         if self.trace_file is None:
99             self.trace_file = os.path.abspath(os.path.join(self.results, self.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.args
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.args[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             print line
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] -- program [args] ...',
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('program must be specified')
305
306     test = TestCase(
307         name = os.path.basename(args[0]), 
308         args = args,
309         cwd = options.cwd,
310         build = options.build,
311         results = options.results,
312     )
313     test.api = options.api
314     test.ref_dump = options.ref_dump
315
316     test.run()
317
318
319 if __name__ == '__main__':
320     main()