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