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:
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:
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:
verbose = False
+ threshold_precision = 12.0
+
def __init__(self):
self.stateCache = {}
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)
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)
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))
def checkState(self, callNo, refStateFileName):
srcState = self.getState(callNo)
- refState = json.load(open(refStateFileName, 'rt'), strict=False)
+ refState = self.getRefState(refStateFileName)
from jsondiff import Comparer, Differ
comparer = Comparer(ignore_added = True)
differ.visit(refState, srcState)
fail('state from call %u does not match %s' % (callNo, refStateFileName))
+ def getRefState(self, refStateFileName):
+ 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)
try:
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']
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:
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] ...',
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,
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