from PIL import Image
from snapdiff import Comparer
-from highlight import Highlighter
+from highlight import AutoHighlighter
import jsondiff
self.args = args
self.env = env
- def retrace(self):
+ def _retrace(self, args):
cmd = [
options.retrace,
+ ] + args + self.args
+ if self.env:
+ for name, value in self.env.iteritems():
+ sys.stderr.write('%s=%s ' % (name, value))
+ sys.stderr.write(' '.join(cmd) + '\n')
+ try:
+ return subprocess.Popen(cmd, env=self.env, stdout=subprocess.PIPE, stderr=NULL)
+ except OSError, ex:
+ sys.stderr.write('error: failed to execute %s: %s\n' % (cmd[0], ex.strerror))
+ sys.exit(1)
+
+ def retrace(self):
+ return self._retrace([
'-s', '-',
'-S', options.snapshot_frequency,
- ] + self.args
- p = subprocess.Popen(cmd, env=self.env, stdout=subprocess.PIPE, stderr=NULL)
- return p
+ ])
def dump_state(self, call_no):
'''Get the state dump at the specified call no.'''
- cmd = [
- options.retrace,
+ p = self._retrace([
'-D', str(call_no),
- ] + self.args
- p = subprocess.Popen(cmd, env=self.env, stdout=subprocess.PIPE, stderr=NULL)
+ ])
state = jsondiff.load(p.stdout)
p.wait()
return state.get('parameters', {})
- def diff_state(self, ref_call_no, src_call_no):
+ def diff_state(self, ref_call_no, src_call_no, stream):
'''Compare the state between two calls.'''
ref_state = self.dump_state(ref_call_no)
src_state = self.dump_state(src_call_no)
- sys.stdout.flush()
- differ = jsondiff.Differ(sys.stdout)
+ stream.flush()
+ differ = jsondiff.Differ(stream)
differ.visit(ref_state, src_state)
- sys.stdout.write('\n')
+ stream.write('\n')
def read_pnm(stream):
magic = stream.readline()
if not magic:
return None, None
- assert magic.rstrip() == 'P6'
+ magic = magic.rstrip()
+ if magic == 'P5':
+ channels = 1
+ mode = 'L'
+ elif magic == 'P6':
+ channels = 3
+ mode = 'RGB'
+ else:
+ raise Exception('Unsupported magic `%s`' % magic)
comment = ''
line = stream.readline()
while line.startswith('#'):
width, height = map(int, line.strip().split())
maximum = int(stream.readline().strip())
assert maximum == 255
- data = stream.read(height * width * 3)
- image = Image.frombuffer('RGB', (width, height), data, 'raw', 'RGB', 0, 1)
+ data = stream.read(height * width * channels)
+ image = Image.frombuffer(mode, (width, height), data, 'raw', mode, 0, 1)
return image, comment
def parse_env(optparser, entries):
'''Translate a list of NAME=VALUE entries into an environment dictionary.'''
+ if not entries:
+ return None
+
env = os.environ.copy()
for entry in entries:
try:
'-r', '--retrace', metavar='PROGRAM',
type='string', dest='retrace', default='glretrace',
help='retrace command [default: %default]')
+ optparser.add_option(
+ '--ref-driver', metavar='DRIVER',
+ type='string', dest='ref_driver', default=None,
+ help='force reference driver')
+ optparser.add_option(
+ '--src-driver', metavar='DRIVER',
+ type='string', dest='src_driver', default=None,
+ help='force source driver')
+ optparser.add_option(
+ '--ref-arg', metavar='OPTION',
+ type='string', action='append', dest='ref_args', default=[],
+ help='pass argument to reference retrace')
+ optparser.add_option(
+ '--src-arg', metavar='OPTION',
+ type='string', action='append', dest='src_args', default=[],
+ help='pass argument to source retrace')
optparser.add_option(
'--ref-env', metavar='NAME=VALUE',
type='string', action='append', dest='ref_env', default=[],
type="float", dest="threshold", default=12.0,
help="threshold precision [default: %default]")
optparser.add_option(
- '-S', '--snapshot-frequency', metavar='FREQUENCY',
+ '-S', '--snapshot-frequency', metavar='CALLSET',
type="string", dest="snapshot_frequency", default='draw',
- help="snapshot frequency: frame, framebuffer, or draw [default: %default]")
+ help="calls to compare [default: %default]")
+ optparser.add_option(
+ '-o', '--output', metavar='FILE',
+ type="string", dest="output",
+ help="output file [default: stdout]")
(options, args) = optparser.parse_args(sys.argv[1:])
ref_env = parse_env(optparser, options.ref_env)
src_env = parse_env(optparser, options.src_env)
if not args:
optparser.error("incorrect number of arguments")
+
+ if options.ref_driver:
+ options.ref_args.insert(0, '--driver=' + options.ref_driver)
+ if options.src_driver:
+ options.src_args.insert(0, '--driver=' + options.src_driver)
+
+ ref_setup = Setup(options.ref_args + args, ref_env)
+ src_setup = Setup(options.src_args + args, src_env)
- ref_setup = Setup(args, ref_env)
- src_setup = Setup(args, src_env)
+ if options.output:
+ output = open(options.output, 'wt')
+ else:
+ output = sys.stdout
- highligher = Highlighter(sys.stdout)
+ highligher = AutoHighlighter(output)
highligher.write('call\tprecision\n')
src_image.save(prefix + '.src.png')
comparer.write_diff(prefix + '.diff.png')
if last_bad < last_good:
- src_setup.diff_state(last_good, call_no)
+ src_setup.diff_state(last_good, call_no, output)
last_bad = call_no
else:
last_good = call_no