]> git.cworth.org Git - apitrace-tests/blob - driver.py
Specify the api to trace.
[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     def trace(self):
91         if self.trace_file is None:
92             self.trace_file = os.path.abspath(os.path.join(self.results, self.name + '.trace'))
93         if os.path.exists(self.trace_file):
94             os.remove(self.trace_file)
95         else:
96             trace_dir = os.path.dirname(self.trace_file)
97             if not os.path.exists(trace_dir):
98                 os.makedirs(trace_dir)
99
100         cmd = self.args
101         env = os.environ.copy()
102         
103         system = platform.system()
104         local_wrapper = None
105         if system == 'Windows':
106             wrapper = _get_build_path('wrappers/opengl32.dll')
107             local_wrapper = os.path.join(os.path.dirname(self.args[0]), os.path.basename(wrapper))
108             shutil.copy(wrapper, local_wrapper)
109             env['TRACE_FILE'] = self.trace_file
110         else:
111             apitrace = _get_build_program('apitrace')
112             cmd = [
113                 apitrace, 'trace', 
114                 '--api', self.api,
115                 '--output', self.trace_file,
116                 '--'
117             ] + cmd
118         if self.max_frames is not None:
119             env['TRACE_FRAMES'] = str(self.max_frames)
120
121         try:
122             p = popen(cmd, env=env, cwd=self.cwd)
123             p.wait()
124         finally:
125             if local_wrapper is not None:
126                 os.remove(local_wrapper)
127
128         if not os.path.exists(self.trace_file):
129             self.fail('no trace file generated\n')
130     
131     call_re = re.compile(r'^([0-9]+) (\w+)\(')
132
133     def dump(self):
134
135         cmd = [_get_build_program('apitrace'), 'dump', '--color=never', self.trace_file]
136         p = popen(cmd, stdout=subprocess.PIPE)
137
138         swapbuffers = 0
139         flushes = 0
140
141         ref_line = ''
142         src_lines = []
143         if self.ref_dump is not None:
144             ref = open(self.ref_dump, 'rt')
145             ref_line = ref.readline().rstrip()
146         for line in p.stdout:
147             line = line.rstrip()
148             mo = self.call_re.match(line)
149             assert mo
150             if mo:
151                 call_no = int(mo.group(1))
152                 function_name = mo.group(2)
153                 if function_name == 'glXSwapBuffers':
154                     swapbuffers += 1
155                 if function_name in ('glFlush', 'glFinish'):
156                     flushes += 1
157                 src_line = line[mo.start(2):]
158                 if ref_line:
159                     if src_line == ref_line:
160                         sys.stdout.write(src_line + '\n')
161                         ref_line = ref.readline().rstrip()
162                         src_lines = []
163                     else:
164                         src_lines.append(src_line)
165
166         p.wait()
167         if p.returncode != 0:
168             self.fail('`apitrace dump` returned code %i' % p.returncode)
169         if ref_line:
170             if src_lines:
171                 self.fail('missing call `%s` (found `%s`)' % (ref_line, src_lines[0]))
172             else:
173                 self.fail('missing call %s' % ref_line)
174
175     def run(self):
176         self.standalone()
177         self.trace()
178         self.dump()
179
180         self.pass_()
181         return
182
183         ref_prefix = os.path.abspath(os.path.join(self.results, self.name + '.ref.'))
184         src_prefix = os.path.join(self.results, self.name + '.src.')
185         diff_prefix = os.path.join(self.results, self.name + '.diff.')
186
187
188         if not os.path.isfile(trace):
189             sys.stdout.write('SKIP (no trace)\n')
190             return
191         args = [_get_build_path('glretrace')]
192         if swapbuffers:
193             args += ['-db']
194             frames = swapbuffers
195         else:
196             args += ['-sb']
197             frames = flushes
198         args += ['-s', src_prefix]
199         args += [trace]
200         p = popen(args, stdout=subprocess.PIPE)
201         image_re = re.compile(r'^Wrote (.*\.png)$')
202         images = []
203         for line in p.stdout:
204             line = line.rstrip()
205             mo = image_re.match(line)
206             if mo:
207                 image = mo.group(1)
208                 if image.startswith(src_prefix):
209                     image = image[len(src_prefix):]
210                     images.append(image)
211         p.wait()
212         if p.returncode != 0:
213             sys.stdout.write('FAIL (glretrace)\n')
214             return
215
216         for image in images:
217             ref_image = ref_prefix + image
218             src_image = src_prefix + image
219             diff_image = diff_prefix + image
220             
221             if not os.path.isfile(ref_image):
222                 continue
223             assert os.path.isfile(src_image)
224
225             comparer = Comparer(ref_image, src_image)
226             match = comparer.ae()
227             sys.stdout.write('%s: %s bits\n' % (image, comparer.precision()))
228             if not match:
229                 comparer.write_diff(diff_image)
230                 #report.add_snapshot(ref_image, src_image, diff_image)
231                 sys.stdout.write('FAIL (snapshot)\n')
232                 return
233
234     def fail(self, reason=None):
235         self._exit('FAIL', 1, reason)
236
237     def skip(self, reason=None):
238         self._exit('SKIP', 0, reason)
239
240     def pass_(self, reason=None):
241         self._exit('PASS', 0, reason)
242
243     def _exit(self, status, code, reason=None):
244         if reason is None:
245             reason = ''
246         else:
247             reason = ' (%s)' % reason
248         sys.stdout.write('%s%s\n' % (status, reason))
249         sys.exit(code)
250
251
252
253 def main():
254     global options
255
256     # Parse command line options
257     optparser = optparse.OptionParser(
258         usage='\n\t%prog [options] -- program [args] ...',
259         version='%%prog')
260     optparser.add_option(
261         '-a', '--api', metavar='API',
262         type='string', dest='api', default='gl',
263         help='api to trace')
264     optparser.add_option(
265         '-B', '--build', metavar='PATH',
266         type='string', dest='build', default='..',
267         help='path to apitrace build')
268     optparser.add_option(
269         '-C', '--directory', metavar='PATH',
270         type='string', dest='cwd', default=None,
271         help='change to directory')
272     optparser.add_option(
273         '-R', '--results', metavar='PATH',
274         type='string', dest='results', default='.',
275         help='results directory [default=%default]')
276     optparser.add_option(
277         '--ref-dump', metavar='PATH',
278         type='string', dest='ref_dump', default=None,
279         help='reference dump')
280
281     (options, args) = optparser.parse_args(sys.argv[1:])
282     if not args:
283         optparser.error('program must be specified')
284
285     test = TestCase(
286         name = os.path.basename(args[0]), 
287         args = args,
288         cwd = options.cwd,
289         build = options.build,
290         results = options.results,
291     )
292     test.api = options.api
293     test.ref_dump = options.ref_dump
294
295     test.run()
296
297
298 if __name__ == '__main__':
299     main()