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