2 ##########################################################################
4 # Copyright 2011 Jose Fonseca
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:
14 # The above copyright notice and this permission notice shall be included in
15 # all copies or substantial portions of the Software.
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
25 ##########################################################################/
27 '''Common test suite code.'''
39 ansi_re = re.compile('\x1b\[[0-9]{1,2}(;[0-9]{1,2}){0,2}m')
43 # http://www.theeggeadventure.com/wikimedia/index.php/Linux_Tips#Use_sed_to_remove_ANSI_colors
44 return ansi_re.sub('', s)
47 def popen(command, *args, **kwargs):
49 sys.stdout.write('cd %s && ' % kwargs['cwd'])
51 for name, value in kwargs['env'].iteritems():
52 if value != os.environ.get(name, None):
53 sys.stdout.write('%s=%s ' % (name, value))
54 sys.stdout.write(' '.join(command) + '\n')
56 return subprocess.Popen(command, *args, **kwargs)
59 ignored_function_names = set([
61 'glXGetCurrentDisplay',
64 'glXGetProcAddressARB',
66 'glXGetVisualFromFBConfig',
68 'glXCreateNewContext',
69 'glXMakeContextCurrent',
77 def __init__(self, basedir):
78 self.basedir = basedir
79 if not os.path.exists(basedir):
81 self.html = open(os.path.join(basedir, 'index.html'), 'wt')
85 self.html.write('<html>\n')
86 self.html.write(' <body>\n')
87 self.html.write(' <table border="1">\n')
88 self.html.write(' <tr><th>Ref</th><th>Src</th><th>Δ</th></tr>\n')
90 def _image_tag(self, image):
91 url = os.path.relpath(image, self.basedir)
92 self.html.write(' <td><a href="%s"><img src="%s"/></a></td>\n' % (url, url))
94 def add_snapshot(self, ref_image, src_image, diff_image):
95 self.html.write(' <tr>\n')
96 self._image_tag(ref_image)
97 self._image_tag(src_image)
98 self._image_tag(diff_image)
99 self.html.write(' </tr>\n')
103 self.html.write(' </table>\n')
104 self.html.write(' </body>\n')
105 self.html.write('</html>\n')
114 def __init__(self, name, args, cwd=None, build = '.', results = '.'):
119 self.results = results
121 if not os.path.exists(results):
124 def run(self, report):
126 trace = os.path.abspath(os.path.join(self.results, self.name + '.trace'))
128 ld_preload = os.path.abspath(os.path.join(self.build, 'glxtrace.so'))
129 if not os.path.exists(ld_preload):
130 sys.stderr.write('error: could not find %s\n' % ld_preload)
133 env = os.environ.copy()
134 env['LD_PRELOAD'] = ld_preload
135 env['TRACE_FILE'] = trace
137 window_name = self.args[0]
139 p = popen(self.args, cwd=self.cwd)
142 if p.poll() is not None:
144 if subprocess.call(['xwininfo', '-name', window_name], stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0:
146 if p.returncode is None:
149 sys.stdout.write('SKIP (app)\n')
152 ref_image = os.path.join(self.results, self.name + '.ref.png')
153 p = popen(self.args, env=env, cwd=self.cwd)
157 if p.poll() is not None:
159 if subprocess.call(['xwininfo', '-name', window_name], stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0:
162 if p.returncode is None:
163 subprocess.call("xwd -name '%s' | xwdtopnm | pnmtopng > '%s'" % (window_name, ref_image), shell=True)
165 if p.returncode is None:
170 sys.stdout.write('FAIL (trace)\n')
173 p = popen([os.path.join(self.build, 'tracedump'), trace], stdout=subprocess.PIPE)
174 call_re = re.compile('^([0-9]+) (\w+)\(')
177 for orig_line in p.stdout:
178 orig_line = orig_line.rstrip()
179 line = ansi_strip(orig_line)
180 mo = call_re.match(line)
182 call_no = int(mo.group(1))
183 function_name = mo.group(2)
184 if function_name in ignored_function_names:
186 if function_name == 'glXSwapBuffers':
188 if function_name in ('glFlush', 'glFinish'):
192 if p.returncode != 0:
193 sys.stdout.write('FAIL (tracedump)\n')
196 args = [os.path.join(self.build, 'glretrace')]
203 if os.path.exists(ref_image) and frames < 10:
204 snapshot_prefix = os.path.join(self.results, self.name + '.')
205 args += ['-s', snapshot_prefix]
207 snapshot_prefix = None
209 p = popen(args, stdout=subprocess.PIPE)
210 image_re = re.compile('^Wrote (.*\.png)$')
212 for line in p.stdout:
214 mo = image_re.match(line)
218 if p.returncode != 0:
219 sys.stdout.write('FAIL (glretrace)\n')
223 delta_image = os.path.join(self.results, self.name + '.diff.png')
229 '-dissimilarity-threshold', '1',
230 ref_image, image, delta_image
231 ], stderr = subprocess.PIPE)
232 _, stderr = p.communicate()
240 report.add_snapshot(ref_image, image, delta_image)
241 sys.stdout.write('FAIL (snapshot)\n')
244 sys.stdout.write('PASS\n')
248 # Parse command line options
249 optparser = optparse.OptionParser(
250 usage='\n\t%prog [options] -- program [args] ...',
252 optparser.add_option(
253 '--build', metavar='PATH',
254 type='string', dest='build', default='.',
255 help='path to apitrace build [default=%default]')
256 optparser.add_option(
257 '--cwd', metavar='PATH',
258 type='string', dest='cwd', default='.',
259 help='change directory [default=%default]')
260 optparser.add_option(
261 '--results', metavar='PATH',
262 type='string', dest='results', default='results',
263 help='results directory [default=%default]')
265 (options, args) = optparser.parse_args(sys.argv[1:])
267 optparser.error('program must be specified')
270 name = os.path.basename(args[0]),
273 build = options.build,
274 results = options.results,
277 report = Report(options.results)
281 if __name__ == '__main__':