X-Git-Url: https://git.cworth.org/git?p=apitrace-tests;a=blobdiff_plain;f=driver.py;h=d5789e3dfaa2132e43e35c20270d3311b0e5e186;hp=39d7420c4e9e4853d47f33f592d94580f51e1200;hb=8c500069e366f273fd806349e3c0fefbdb639d8d;hpb=195b2e54eda22ea844af868ac6990c45e8772d37 diff --git a/driver.py b/driver.py index 39d7420..d5789e3 100755 --- a/driver.py +++ b/driver.py @@ -31,15 +31,12 @@ import optparse import os.path import platform import re -import shutil import subprocess import sys import time import json import base64 -from PIL import Image - try: from cStringIO import StringIO except ImportError: @@ -67,35 +64,72 @@ def pass_(reason=None): def popen(command, *args, **kwargs): if kwargs.get('cwd', None) is not None: sys.stdout.write('cd %s && ' % kwargs['cwd']) - if 'env' in kwargs: - for name, value in kwargs['env'].iteritems(): + + try: + env = kwargs.pop('env') + except KeyError: + env = None + else: + names = env.keys() + names.sort() + for name in names: + value = env[name] if value != os.environ.get(name, None): sys.stdout.write('%s=%s ' % (name, value)) + env[name] = str(value) + sys.stdout.write(' '.join(command) + '\n') sys.stdout.flush() - return subprocess.Popen(command, *args, **kwargs) + + return subprocess.Popen(command, *args, env=env, **kwargs) + + +def which(executable): + dirs = os.environ['PATH'].split(os.path.pathsep) + for dir in dirs: + path = os.path.join(dir, executable) + if os.path.exists(path): + return path + return None -def _get_build_path(path): - if options.build is not None: - path = os.path.abspath(os.path.join(options.build, path)) +def _get_bin_path(): + if os.path.exists(options.apitrace): + apitrace_abspath = os.path.abspath(options.apitrace) + else: + apitrace_abspath = which(options.apitrace) + if apitrace_abspath is None: + sys.stderr.write('error: could not determine the absolute path of\n' % options.apitrace) + sys.exit(1) + return os.path.dirname(apitrace_abspath) + + +def _get_build_program(program): + bin_path = _get_bin_path() + if platform.system() == 'Windows': + program += '.exe' + path = os.path.join(bin_path, program) if not os.path.exists(path): sys.stderr.write('error: %s does not exist\n' % path) sys.exit(1) return path -def _get_build_program(program): - if platform.system() == 'Windows': - program += '.exe' - return _get_build_path(program) - -def _get_source_path(path): - cache = _get_build_path('CMakeCache.txt') - for line in open(cache, 'rt'): - if line.startswith('CMAKE_HOME_DIRECTORY:INTERNAL='): - _, source_root = line.strip().split('=', 1) - return os.path.join(source_root, path) - return None +def _get_scripts_path(): + bin_path = _get_bin_path() + + try_paths = [ + 'scripts', + '../lib/scripts', + '../lib/apitrace/scripts', + ] + + for try_path in try_paths: + path = os.path.join(bin_path, try_path) + if os.path.exists(path): + return os.path.abspath(path) + + sys.stderr.write('error: could not find scripts directory\n') + sys.exit(1) class TraceChecker: @@ -129,7 +163,7 @@ class TraceChecker: sys.stdout.write(line + '\n') mo = self.call_re.match(line) if mo: - self.call_no = int(mo.group(1)) + self.callNo = int(mo.group(1)) function_name = mo.group(2) if function_name.find('SwapBuffers') != -1 or \ line.find('kCGLPFADoubleBuffer') != -1: @@ -205,6 +239,8 @@ class TestCase: verbose = False + threshold_precision = 12.0 + def __init__(self): self.stateCache = {} @@ -232,7 +268,13 @@ class TestCase: return if self.trace_file is None: - name = os.path.basename(self.cmd[0]) + if self.ref_dump is not None: + name = self.ref_dump + else: + name = self.cmd[0] + name, ext = os.path.splitext(os.path.basename(name)) + while ext: + name, ext = os.path.splitext(os.path.basename(name)) self.trace_file = os.path.abspath(os.path.join(self.results, name + '.trace')) if os.path.exists(self.trace_file): os.remove(self.trace_file) @@ -244,36 +286,23 @@ class TestCase: cmd = self.cmd env = os.environ.copy() - system = platform.system() - local_wrapper = None - if system == 'Windows': - wrapper = _get_build_path('wrappers/opengl32.dll') - local_wrapper = os.path.join(os.path.dirname(self.cmd[0]), os.path.basename(wrapper)) - shutil.copy(wrapper, local_wrapper) - env['TRACE_FILE'] = self.trace_file - else: - apitrace = _get_build_program('apitrace') - cmd = [ - apitrace, 'trace', - '--api', self.api_map[self.api], - '--output', self.trace_file, - '--' - ] + cmd + cmd = [ + options.apitrace, 'trace', + '--api', self.api_map[self.api], + '--output', self.trace_file, + '--' + ] + cmd if self.max_frames is not None: env['TRACE_FRAMES'] = str(self.max_frames) - try: - p = popen(cmd, env=env, cwd=self.cwd) - p.wait() - finally: - if local_wrapper is not None: - os.remove(local_wrapper) + p = popen(cmd, env=env, cwd=self.cwd) + p.wait() if not os.path.exists(self.trace_file): fail('no trace file generated\n') def checkTrace(self): - cmd = [_get_build_program('apitrace'), 'dump', '--color=never', self.trace_file] + cmd = [options.apitrace, 'dump', '--color=never', self.trace_file] p = popen(cmd, stdout=subprocess.PIPE) checker = TraceChecker(p.stdout, self.ref_dump, self.verbose) @@ -290,15 +319,22 @@ class TestCase: self.checkState(callNo, refStateFileName) def checkImage(self, callNo, refImageFileName): + try: + from PIL import Image + except ImportError: + return + srcImage = self.getImage(callNo) refImage = Image.open(refImageFileName) from snapdiff import Comparer comparer = Comparer(refImage, srcImage) - match = comparer.ae() - if not match: + precision = comparer.precision(filter=True) + sys.stdout.write('precision of %f bits against %s\n' % (precision, refImageFileName)) + if precision < self.threshold_precision: prefix = '%s.%u' % (self.getNamePrefix(), callNo) srcImageFileName = prefix + '.src.png' + srcImage.save(srcImageFileName) diffImageFileName = prefix + '.diff.png' comparer.write_diff(diffImageFileName) fail('snapshot from call %u does not match %s' % (callNo, refImageFileName)) @@ -321,13 +357,12 @@ class TestCase: differ.visit(refState, srcState) fail('state from call %u does not match %s' % (callNo, refStateFileName)) - # Allo non-standard JS comments in JSON - json_comment_re = re.compile(r'//.*$', re.MULTILINE) - def getRefState(self, refStateFileName): - data = open(refStateFileName, 'rt').read() - data = self.json_comment_re.sub('', data) - return json.loads(data, strict=False) + stream = open(refStateFileName, 'rt') + from jsondiff import load + state = load(stream) + self.adjustRefState(state) + return state def getNamePrefix(self): name = os.path.basename(self.ref_dump) @@ -350,6 +385,7 @@ class TestCase: fail('retrace failed with code %i' % (p.returncode)) def getImage(self, callNo): + from PIL import Image state = self.getState(callNo) if self.doubleBuffer: attachments = ['GL_BACK', 'GL_BACK_LEFT', 'GL_BACK_RIGHT', 'GL_COLOR_ATTACHMENT0'] @@ -387,13 +423,66 @@ class TestCase: if p.returncode != 0: fail('retrace returned code %i' % (p.returncode)) + self.adjustSrcState(state) + self.stateCache[callNo] = state return state + def adjustSrcState(self, state): + # Do some adjustments on the obtained state to eliminate failures from + # bugs/issues outside of apitrace + + try: + parameters = state['parameters'] + except KeyError: + return + + # On NVIDIA drivers glGetIntegerv(GL_INDEX_WRITEMASK) returns -1 + self.replaceState(parameters, 'GL_INDEX_WRITEMASK', 255, -1) + + # On Gallium + if 'Gallium' in parameters['GL_RENDERER'].split(): + # Gallium drivers have wrong defaults for draw/read buffer state + self.replaceState(parameters, 'GL_DRAW_BUFFER', 'GL_BACK_LEFT', 'GL_BACK') + self.replaceState(parameters, 'GL_DRAW_BUFFER0', 'GL_BACK_LEFT', 'GL_BACK') + self.replaceState(parameters, 'GL_READ_BUFFER', 'GL_BACK_LEFT', 'GL_BACK') + self.replaceState(parameters, 'GL_DRAW_BUFFER', 'GL_FRONT_LEFT', 'GL_FRONT') + self.replaceState(parameters, 'GL_DRAW_BUFFER0', 'GL_FRONT_LEFT', 'GL_FRONT') + self.replaceState(parameters, 'GL_READ_BUFFER', 'GL_FRONT_LEFT', 'GL_FRONT') + + def adjustRefState(self, state): + # Do some adjustments on reference state to eliminate failures from + # bugs/issues outside of apitrace + + try: + parameters = state['parameters'] + except KeyError: + return + + if platform.system() == 'Darwin': + # Mac OS X drivers fail on GL_COLOR_SUM + # XXX: investigate this + self.removeState(parameters, 'GL_COLOR_SUM') + + def replaceState(self, obj, key, srcValue, dstValue): + try: + value = obj[key] + except KeyError: + pass + else: + if value == srcValue: + obj[key] = dstValue + + def removeState(self, obj, key): + try: + del obj[key] + except KeyError: + pass + def _retrace(self, args = None, stdout=subprocess.PIPE): retrace = self.api_map[self.api] + 'retrace' - cmd = [_get_build_path(retrace)] + cmd = [_get_build_program(retrace)] if self.doubleBuffer: cmd += ['-db'] else: @@ -415,6 +504,10 @@ class TestCase: def main(): global options + default_apitrace = 'apitrace' + if platform.system() == 'Windows': + default_apitrace += '.exe' + # Parse command line options optparser = optparse.OptionParser( usage='\n\t%prog [options] -- [TRACE|PROGRAM] ...', @@ -429,9 +522,9 @@ def main(): type='string', dest='api', default='gl', help='api to trace') optparser.add_option( - '-B', '--build', metavar='PATH', - type='string', dest='build', default='..', - help='path to apitrace build') + '--apitrace', metavar='PROGRAM', + type='string', dest='apitrace', default=default_apitrace, + help='path to apitrace executable') optparser.add_option( '-C', '--directory', metavar='PATH', type='string', dest='cwd', default=None, @@ -452,7 +545,9 @@ def main(): if not os.path.exists(options.results): os.makedirs(options.results) - sys.path.insert(0, _get_source_path('scripts')) + print _get_scripts_path() + + sys.path.insert(0, _get_scripts_path()) test = TestCase() test.verbose = options.verbose