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