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=None, results = '.'):
119 self.results = results
121 if not os.path.exists(results):
125 def _get_build_path(self, path):
126 if self.build is not None:
127 path = os.path.abspath(os.path.join(self.build, path))
128 if not os.path.exists(path):
129 sys.stderr.write('error: %s does not exist\n' % path)
133 def run(self, report):
135 trace = os.path.abspath(os.path.join(self.results, self.name + '.trace'))
137 ld_preload = self._get_build_path('glxtrace.so')
139 env = os.environ.copy()
140 env['LD_PRELOAD'] = ld_preload
141 env['TRACE_FILE'] = trace
143 window_name = self.args[0]
145 p = popen(self.args, cwd=self.cwd)
148 if p.poll() is not None:
150 if subprocess.call(['xwininfo', '-name', window_name], stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0:
152 if p.returncode is None:
155 sys.stdout.write('SKIP (app)\n')
158 ref_image = os.path.join(self.results, self.name + '.ref.png')
159 p = popen(self.args, env=env, cwd=self.cwd)
163 if p.poll() is not None:
165 if subprocess.call(['xwininfo', '-name', window_name], stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0:
168 if p.returncode is None:
169 subprocess.call("xwd -name '%s' | xwdtopnm | pnmtopng > '%s'" % (window_name, ref_image), shell=True)
171 if p.returncode is None:
175 sys.stdout.write('FAIL (trace)\n')
178 p = popen([self._get_build_path('tracedump'), trace], stdout=subprocess.PIPE)
179 call_re = re.compile('^([0-9]+) (\w+)\(')
182 for orig_line in p.stdout:
183 orig_line = orig_line.rstrip()
184 line = ansi_strip(orig_line)
185 mo = call_re.match(line)
187 call_no = int(mo.group(1))
188 function_name = mo.group(2)
189 if function_name in ignored_function_names:
191 if function_name == 'glXSwapBuffers':
193 if function_name in ('glFlush', 'glFinish'):
196 if p.returncode != 0:
197 sys.stdout.write('FAIL (tracedump)\n')
200 args = [self._get_build_path('glretrace')]
207 if os.path.exists(ref_image) and frames < 10:
208 snapshot_prefix = os.path.join(self.results, self.name + '.')
209 args += ['-s', snapshot_prefix]
211 snapshot_prefix = None
213 p = popen(args, stdout=subprocess.PIPE)
214 image_re = re.compile('^Wrote (.*\.png)$')
216 for line in p.stdout:
218 mo = image_re.match(line)
222 if p.returncode != 0:
223 sys.stdout.write('FAIL (glretrace)\n')
227 delta_image = os.path.join(self.results, self.name + '.diff.png')
233 '-dissimilarity-threshold', '1',
234 ref_image, image, delta_image
235 ], stderr = subprocess.PIPE)
236 _, stderr = p.communicate()
244 report.add_snapshot(ref_image, image, delta_image)
245 sys.stdout.write('FAIL (snapshot)\n')
248 sys.stdout.write('PASS\n')
252 # Parse command line options
253 optparser = optparse.OptionParser(
254 usage='\n\t%prog [options] -- program [args] ...',
256 optparser.add_option(
257 '-B', '--build', metavar='PATH',
258 type='string', dest='build', default=None,
259 help='path to apitrace build')
260 optparser.add_option(
261 '-C', '--directory', metavar='PATH',
262 type='string', dest='cwd', default=None,
263 help='change to directory')
264 optparser.add_option(
265 '-R', '--results', metavar='PATH',
266 type='string', dest='results', default='results',
267 help='results directory [default=%default]')
269 (options, args) = optparser.parse_args(sys.argv[1:])
271 optparser.error('program must be specified')
274 name = os.path.basename(args[0]),
277 build = options.build,
278 results = options.results,
281 report = Report(options.results)
285 if __name__ == '__main__':