]> git.cworth.org Git - apitrace-tests/blob - driver.py
Ensure trace are cleaned up.
[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 subprocess
35 import sys
36 import time
37
38
39 def popen(command, *args, **kwargs):
40     if kwargs.get('cwd', None) is not None:
41         sys.stdout.write('cd %s && ' % kwargs['cwd'])
42     if 'env' in kwargs:
43         for name, value in kwargs['env'].iteritems():
44             if value != os.environ.get(name, None):
45                 sys.stdout.write('%s=%s ' % (name, value))
46     sys.stdout.write(' '.join(command) + '\n')
47     sys.stdout.flush()
48     return subprocess.Popen(command, *args, **kwargs)
49
50
51 def _get_build_path(path):
52     if options.build is not None:
53         path = os.path.abspath(os.path.join(options.build, path))
54     if not os.path.exists(path):
55         sys.stderr.write('error: %s does not exist\n' % path)
56         sys.exit(1)
57     return path
58
59
60 class TestCase:
61
62     max_frames = None
63
64     trace_file = None
65
66     def __init__(self, name, args, cwd=None, build=None, results = '.'):
67         self.name = name
68         self.args = args
69         self.cwd = cwd
70         self.build = build
71         self.results = results
72
73         if not os.path.exists(results):
74             os.makedirs(results)
75
76     expected_dump = None
77
78     def standalone(self):
79         p = popen(self.args, cwd=self.cwd)
80         p.wait()
81         if p.returncode:
82             self.skip('application returned code %i' % p.returncode)
83
84     def trace(self):
85         if self.trace_file is None:
86             self.trace_file = os.path.abspath(os.path.join(self.results, self.name + '.trace'))
87         if os.path.exists(self.trace_file):
88             os.remove(self.trace_file)
89         else:
90             trace_dir = os.path.dirname(self.trace_file)
91             if not os.path.exists(trace_dir):
92                 os.makedirs(trace_dir)
93
94         env = os.environ.copy()
95         
96         system = platform.system()
97         if system == 'Windows':
98             # TODO
99             self.skip('tracing not supported on Windows')
100             wrapper = _get_build_path('wrappers/opengl32.dll')
101         elif system == 'Darwin':
102             wrapper = _get_build_path('wrappers/OpenGL')
103             env['DYLD_LIBRARY_PATH'] = os.path.dirname(wrapper)
104         else:
105             wrapper = _get_build_path('glxtrace.so')
106             env['LD_PRELOAD'] = wrapper
107
108         env['TRACE_FILE'] = self.trace_file
109         if self.max_frames is not None:
110             env['TRACE_FRAMES'] = str(self.max_frames)
111
112         p = popen(self.args, env=env, cwd=self.cwd)
113         p.wait()
114
115         if not os.path.exists(self.trace_file):
116             self.fail('no trace file generated\n')
117     
118     call_re = re.compile(r'^([0-9]+) (\w+)\(')
119
120     def dump(self):
121
122         cmd = [_get_build_path('tracedump'), '--color=never', self.trace_file]
123         p = popen(cmd, stdout=subprocess.PIPE)
124
125         swapbuffers = 0
126         flushes = 0
127
128         ref_line = ''
129         if self.ref_dump is not None:
130             ref = open(self.ref_dump, 'rt')
131             ref_line = ref.readline().rstrip()
132         for line in p.stdout:
133             line = line.rstrip()
134             mo = self.call_re.match(line)
135             assert mo
136             if mo:
137                 call_no = int(mo.group(1))
138                 function_name = mo.group(2)
139                 if function_name == 'glXSwapBuffers':
140                     swapbuffers += 1
141                 if function_name in ('glFlush', 'glFinish'):
142                     flushes += 1
143                 src_line = line[mo.start(2):]
144                 sys.stdout.write(src_line + '\n')
145                 if ref_line:
146                     if src_line == ref_line:
147                         ref_line = ref.readline().rstrip()
148         p.wait()
149         if p.returncode != 0:
150             self.fail('tracedump returned code %i' % p.returncode)
151         if ref_line:
152             self.fail('missing call %' % ref_line)
153
154     def run(self):
155         self.standalone()
156         self.trace()
157         self.dump()
158
159         self.pass_()
160         return
161
162         ref_prefix = os.path.abspath(os.path.join(self.results, self.name + '.ref.'))
163         src_prefix = os.path.join(self.results, self.name + '.src.')
164         diff_prefix = os.path.join(self.results, self.name + '.diff.')
165
166
167         if not os.path.isfile(trace):
168             sys.stdout.write('SKIP (no trace)\n')
169             return
170         args = [_get_build_path('glretrace')]
171         if swapbuffers:
172             args += ['-db']
173             frames = swapbuffers
174         else:
175             args += ['-sb']
176             frames = flushes
177         args += ['-s', src_prefix]
178         args += [trace]
179         p = popen(args, stdout=subprocess.PIPE)
180         image_re = re.compile(r'^Wrote (.*\.png)$')
181         images = []
182         for line in p.stdout:
183             line = line.rstrip()
184             mo = image_re.match(line)
185             if mo:
186                 image = mo.group(1)
187                 if image.startswith(src_prefix):
188                     image = image[len(src_prefix):]
189                     images.append(image)
190         p.wait()
191         if p.returncode != 0:
192             sys.stdout.write('FAIL (glretrace)\n')
193             return
194
195         for image in images:
196             ref_image = ref_prefix + image
197             src_image = src_prefix + image
198             diff_image = diff_prefix + image
199             
200             if not os.path.isfile(ref_image):
201                 continue
202             assert os.path.isfile(src_image)
203
204             comparer = Comparer(ref_image, src_image)
205             match = comparer.ae()
206             sys.stdout.write('%s: %s bits\n' % (image, comparer.precision()))
207             if not match:
208                 comparer.write_diff(diff_image)
209                 #report.add_snapshot(ref_image, src_image, diff_image)
210                 sys.stdout.write('FAIL (snapshot)\n')
211                 return
212
213     def fail(self, reason=None):
214         self._exit('FAIL', 1, reason)
215
216     def skip(self, reason=None):
217         self._exit('SKIP', 0, reason)
218
219     def pass_(self, reason=None):
220         self._exit('PASS', 0, reason)
221
222     def _exit(self, status, code, reason=None):
223         if reason is None:
224             reason = ''
225         else:
226             reason = ' (%s)' % reason
227         sys.stdout.write('%s%s\n' % (status, reason))
228         sys.exit(code)
229
230
231
232 def main():
233     global options
234
235     # Parse command line options
236     optparser = optparse.OptionParser(
237         usage='\n\t%prog [options] -- program [args] ...',
238         version='%%prog')
239     optparser.add_option(
240         '-B', '--build', metavar='PATH',
241         type='string', dest='build', default='..',
242         help='path to apitrace build')
243     optparser.add_option(
244         '-C', '--directory', metavar='PATH',
245         type='string', dest='cwd', default=None,
246         help='change to directory')
247     optparser.add_option(
248         '-R', '--results', metavar='PATH',
249         type='string', dest='results', default='.',
250         help='results directory [default=%default]')
251     optparser.add_option(
252         '--ref-dump', metavar='PATH',
253         type='string', dest='ref_dump', default=None,
254         help='reference dump')
255
256     (options, args) = optparser.parse_args(sys.argv[1:])
257     if not args:
258         optparser.error('program must be specified')
259
260     test = TestCase(
261         name = os.path.basename(args[0]), 
262         args = args,
263         cwd = options.cwd,
264         build = options.build,
265         results = options.results,
266     )
267     test.ref_dump = options.ref_dump
268
269     test.run()
270
271
272 if __name__ == '__main__':
273     main()