Blame src/gallium/tools/trace/diff_state.py

Packit Service 5cb53b
#! /usr/libexec/platform-python
Packit 8f2243
##########################################################################
Packit 8f2243
#
Packit 8f2243
# Copyright 2011 Jose Fonseca
Packit 8f2243
# All Rights Reserved.
Packit 8f2243
#
Packit 8f2243
# Permission is hereby granted, free of charge, to any person obtaining a copy
Packit 8f2243
# of this software and associated documentation files (the "Software"), to deal
Packit 8f2243
# in the Software without restriction, including without limitation the rights
Packit 8f2243
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
Packit 8f2243
# copies of the Software, and to permit persons to whom the Software is
Packit 8f2243
# furnished to do so, subject to the following conditions:
Packit 8f2243
#
Packit 8f2243
# The above copyright notice and this permission notice shall be included in
Packit 8f2243
# all copies or substantial portions of the Software.
Packit 8f2243
#
Packit 8f2243
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
Packit 8f2243
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
Packit 8f2243
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
Packit 8f2243
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
Packit 8f2243
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
Packit 8f2243
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
Packit 8f2243
# THE SOFTWARE.
Packit 8f2243
#
Packit 8f2243
##########################################################################/
Packit 8f2243
Packit 8f2243
Packit 8f2243
import json
Packit 8f2243
import optparse
Packit 8f2243
import re
Packit 8f2243
import difflib
Packit 8f2243
import sys
Packit 8f2243
Packit 8f2243
Packit 8f2243
def strip_object_hook(obj):
Packit 8f2243
    if '__class__' in obj:
Packit 8f2243
        return None
Packit 8f2243
    for name in obj.keys():
Packit 8f2243
        if name.startswith('__') and name.endswith('__'):
Packit 8f2243
            del obj[name]
Packit 8f2243
    return obj
Packit 8f2243
Packit 8f2243
Packit 8f2243
class Visitor:
Packit 8f2243
Packit 8f2243
    def visit(self, node, *args, **kwargs):
Packit 8f2243
        if isinstance(node, dict):
Packit 8f2243
            return self.visitObject(node, *args, **kwargs)
Packit 8f2243
        elif isinstance(node, list):
Packit 8f2243
            return self.visitArray(node, *args, **kwargs)
Packit 8f2243
        else:
Packit 8f2243
            return self.visitValue(node, *args, **kwargs)
Packit 8f2243
Packit 8f2243
    def visitObject(self, node, *args, **kwargs):
Packit 8f2243
        pass
Packit 8f2243
Packit 8f2243
    def visitArray(self, node, *args, **kwargs):
Packit 8f2243
        pass
Packit 8f2243
Packit 8f2243
    def visitValue(self, node, *args, **kwargs):
Packit 8f2243
        pass
Packit 8f2243
Packit 8f2243
Packit 8f2243
class Dumper(Visitor):
Packit 8f2243
Packit 8f2243
    def __init__(self, stream = sys.stdout):
Packit 8f2243
        self.stream = stream
Packit 8f2243
        self.level = 0
Packit 8f2243
Packit 8f2243
    def _write(self, s):
Packit 8f2243
        self.stream.write(s)
Packit 8f2243
Packit 8f2243
    def _indent(self):
Packit 8f2243
        self._write('  '*self.level)
Packit 8f2243
Packit 8f2243
    def _newline(self):
Packit 8f2243
        self._write('\n')
Packit 8f2243
Packit 8f2243
    def visitObject(self, node):
Packit 8f2243
        self.enter_object()
Packit 8f2243
Packit 8f2243
        members = node.keys()
Packit 8f2243
        members.sort()
Packit 8f2243
        for i in range(len(members)):
Packit 8f2243
            name = members[i]
Packit 8f2243
            value = node[name]
Packit 8f2243
            self.enter_member(name)
Packit 8f2243
            self.visit(value)
Packit 8f2243
            self.leave_member(i == len(members) - 1)
Packit 8f2243
        self.leave_object()
Packit 8f2243
Packit 8f2243
    def enter_object(self):
Packit 8f2243
        self._write('{')
Packit 8f2243
        self._newline()
Packit 8f2243
        self.level += 1
Packit 8f2243
Packit 8f2243
    def enter_member(self, name):
Packit 8f2243
        self._indent()
Packit 8f2243
        self._write('%s: ' % name)
Packit 8f2243
Packit 8f2243
    def leave_member(self, last):
Packit 8f2243
        if not last:
Packit 8f2243
            self._write(',')
Packit 8f2243
        self._newline()
Packit 8f2243
Packit 8f2243
    def leave_object(self):
Packit 8f2243
        self.level -= 1
Packit 8f2243
        self._indent()
Packit 8f2243
        self._write('}')
Packit 8f2243
        if self.level <= 0:
Packit 8f2243
            self._newline()
Packit 8f2243
Packit 8f2243
    def visitArray(self, node):
Packit 8f2243
        self.enter_array()
Packit 8f2243
        for i in range(len(node)):
Packit 8f2243
            value = node[i]
Packit 8f2243
            self._indent()
Packit 8f2243
            self.visit(value)
Packit 8f2243
            if i != len(node) - 1:
Packit 8f2243
                self._write(',')
Packit 8f2243
            self._newline()
Packit 8f2243
        self.leave_array()
Packit 8f2243
Packit 8f2243
    def enter_array(self):
Packit 8f2243
        self._write('[')
Packit 8f2243
        self._newline()
Packit 8f2243
        self.level += 1
Packit 8f2243
Packit 8f2243
    def leave_array(self):
Packit 8f2243
        self.level -= 1
Packit 8f2243
        self._indent()
Packit 8f2243
        self._write(']')
Packit 8f2243
Packit 8f2243
    def visitValue(self, node):
Packit 8f2243
        self._write(json.dumps(node, allow_nan=True))
Packit 8f2243
Packit 8f2243
Packit 8f2243
Packit 8f2243
class Comparer(Visitor):
Packit 8f2243
Packit 8f2243
    def __init__(self, ignore_added = False, tolerance = 2.0 ** -24):
Packit 8f2243
        self.ignore_added = ignore_added
Packit 8f2243
        self.tolerance = tolerance
Packit 8f2243
Packit 8f2243
    def visitObject(self, a, b):
Packit 8f2243
        if not isinstance(b, dict):
Packit 8f2243
            return False
Packit 8f2243
        if len(a) != len(b) and not self.ignore_added:
Packit 8f2243
            return False
Packit 8f2243
        ak = a.keys()
Packit 8f2243
        bk = b.keys()
Packit 8f2243
        ak.sort()
Packit 8f2243
        bk.sort()
Packit 8f2243
        if ak != bk and not self.ignore_added:
Packit 8f2243
            return False
Packit 8f2243
        for k in ak:
Packit 8f2243
            ae = a[k]
Packit 8f2243
            try:
Packit 8f2243
                be = b[k]
Packit 8f2243
            except KeyError:
Packit 8f2243
                return False
Packit 8f2243
            if not self.visit(ae, be):
Packit 8f2243
                return False
Packit 8f2243
        return True
Packit 8f2243
Packit 8f2243
    def visitArray(self, a, b):
Packit 8f2243
        if not isinstance(b, list):
Packit 8f2243
            return False
Packit 8f2243
        if len(a) != len(b):
Packit 8f2243
            return False
Packit 8f2243
        for ae, be in zip(a, b):
Packit 8f2243
            if not self.visit(ae, be):
Packit 8f2243
                return False
Packit 8f2243
        return True
Packit 8f2243
Packit 8f2243
    def visitValue(self, a, b):
Packit 8f2243
        if isinstance(a, float) or isinstance(b, float):
Packit 8f2243
            if a == 0:
Packit 8f2243
                return abs(b) < self.tolerance
Packit 8f2243
            else:
Packit 8f2243
                return abs((b - a)/a) < self.tolerance
Packit 8f2243
        else:
Packit 8f2243
            return a == b
Packit 8f2243
Packit 8f2243
Packit 8f2243
class Differ(Visitor):
Packit 8f2243
Packit 8f2243
    def __init__(self, stream = sys.stdout, ignore_added = False):
Packit 8f2243
        self.dumper = Dumper(stream)
Packit 8f2243
        self.comparer = Comparer(ignore_added = ignore_added)
Packit 8f2243
Packit 8f2243
    def visit(self, a, b):
Packit 8f2243
        if self.comparer.visit(a, b):
Packit 8f2243
            return
Packit 8f2243
        Visitor.visit(self, a, b)
Packit 8f2243
Packit 8f2243
    def visitObject(self, a, b):
Packit 8f2243
        if not isinstance(b, dict):
Packit 8f2243
            self.replace(a, b)
Packit 8f2243
        else:
Packit 8f2243
            self.dumper.enter_object()
Packit 8f2243
            names = set(a.keys())
Packit 8f2243
            if not self.comparer.ignore_added:
Packit 8f2243
                names.update(b.keys())
Packit 8f2243
            names = list(names)
Packit 8f2243
            names.sort()
Packit 8f2243
Packit 8f2243
            for i in range(len(names)):
Packit 8f2243
                name = names[i]
Packit 8f2243
                ae = a.get(name, None)
Packit 8f2243
                be = b.get(name, None)
Packit 8f2243
                if not self.comparer.visit(ae, be):
Packit 8f2243
                    self.dumper.enter_member(name)
Packit 8f2243
                    self.visit(ae, be)
Packit 8f2243
                    self.dumper.leave_member(i == len(names) - 1)
Packit 8f2243
Packit 8f2243
            self.dumper.leave_object()
Packit 8f2243
Packit 8f2243
    def visitArray(self, a, b):
Packit 8f2243
        if not isinstance(b, list):
Packit 8f2243
            self.replace(a, b)
Packit 8f2243
        else:
Packit 8f2243
            self.dumper.enter_array()
Packit 8f2243
            max_len = max(len(a), len(b))
Packit 8f2243
            for i in range(max_len):
Packit 8f2243
                try:
Packit 8f2243
                    ae = a[i]
Packit 8f2243
                except IndexError:
Packit 8f2243
                    ae = None
Packit 8f2243
                try:
Packit 8f2243
                    be = b[i]
Packit 8f2243
                except IndexError:
Packit 8f2243
                    be = None
Packit 8f2243
                self.dumper._indent()
Packit 8f2243
                if self.comparer.visit(ae, be):
Packit 8f2243
                    self.dumper.visit(ae)
Packit 8f2243
                else:
Packit 8f2243
                    self.visit(ae, be)
Packit 8f2243
                if i != max_len - 1:
Packit 8f2243
                    self.dumper._write(',')
Packit 8f2243
                self.dumper._newline()
Packit 8f2243
Packit 8f2243
            self.dumper.leave_array()
Packit 8f2243
Packit 8f2243
    def visitValue(self, a, b):
Packit 8f2243
        if a != b:
Packit 8f2243
            self.replace(a, b)
Packit 8f2243
Packit 8f2243
    def replace(self, a, b):
Packit 8f2243
        if isinstance(a, basestring) and isinstance(b, basestring):
Packit 8f2243
            if '\n' in a or '\n' in b:
Packit 8f2243
                a = a.splitlines()
Packit 8f2243
                b = b.splitlines()
Packit 8f2243
                differ = difflib.Differ()
Packit 8f2243
                result = differ.compare(a, b)
Packit 8f2243
                self.dumper.level += 1
Packit 8f2243
                for entry in result:
Packit 8f2243
                    self.dumper._newline()
Packit 8f2243
                    self.dumper._indent()
Packit 8f2243
                    tag = entry[:2]
Packit 8f2243
                    text = entry[2:]
Packit 8f2243
                    if tag == '? ':
Packit 8f2243
                        tag = '  '
Packit 8f2243
                        prefix = ' '
Packit 8f2243
                        text = text.rstrip()
Packit 8f2243
                        suffix = ''
Packit 8f2243
                    else:
Packit 8f2243
                        prefix = '"'
Packit 8f2243
                        suffix = '\\n"'
Packit 8f2243
                    line = tag + prefix + text + suffix
Packit 8f2243
                    self.dumper._write(line)
Packit 8f2243
                self.dumper.level -= 1
Packit 8f2243
                return
Packit 8f2243
        self.dumper.visit(a)
Packit 8f2243
        self.dumper._write(' -> ')
Packit 8f2243
        self.dumper.visit(b)
Packit 8f2243
Packit 8f2243
    def isMultilineString(self, value):
Packit 8f2243
        return isinstance(value, basestring) and '\n' in value
Packit 8f2243
    
Packit 8f2243
    def replaceMultilineString(self, a, b):
Packit 8f2243
        self.dumper.visit(a)
Packit 8f2243
        self.dumper._write(' -> ')
Packit 8f2243
        self.dumper.visit(b)
Packit 8f2243
Packit 8f2243
Packit 8f2243
#
Packit 8f2243
# Unfortunately JSON standard does not include comments, but this is a quite
Packit 8f2243
# useful feature to have on regressions tests
Packit 8f2243
#
Packit 8f2243
Packit 8f2243
_token_res = [
Packit 8f2243
    r'//[^\r\n]*', # comment
Packit 8f2243
    r'"[^"\\]*(\\.[^"\\]*)*"', # string
Packit 8f2243
]
Packit 8f2243
Packit 8f2243
_tokens_re = re.compile(r'|'.join(['(' + token_re + ')' for token_re in _token_res]), re.DOTALL)
Packit 8f2243
Packit 8f2243
Packit 8f2243
def _strip_comment(mo):
Packit 8f2243
    if mo.group(1):
Packit 8f2243
        return ''
Packit 8f2243
    else:
Packit 8f2243
        return mo.group(0)
Packit 8f2243
Packit 8f2243
Packit 8f2243
def _strip_comments(data):
Packit 8f2243
    '''Strip (non-standard) JSON comments.'''
Packit 8f2243
    return _tokens_re.sub(_strip_comment, data)
Packit 8f2243
Packit 8f2243
Packit 8f2243
assert _strip_comments('''// a comment
Packit 8f2243
"// a comment in a string
Packit 8f2243
"''') == '''
Packit 8f2243
"// a comment in a string
Packit 8f2243
"'''
Packit 8f2243
Packit 8f2243
Packit 8f2243
def load(stream, strip_images = True, strip_comments = True):
Packit 8f2243
    if strip_images:
Packit 8f2243
        object_hook = strip_object_hook
Packit 8f2243
    else:
Packit 8f2243
        object_hook = None
Packit 8f2243
    if strip_comments:
Packit 8f2243
        data = stream.read()
Packit 8f2243
        data = _strip_comments(data)
Packit 8f2243
        return json.loads(data, strict=False, object_hook = object_hook)
Packit 8f2243
    else:
Packit 8f2243
        return json.load(stream, strict=False, object_hook = object_hook)
Packit 8f2243
Packit 8f2243
Packit 8f2243
def main():
Packit 8f2243
    optparser = optparse.OptionParser(
Packit 8f2243
        usage="\n\t%prog [options] <ref_json> <src_json>")
Packit 8f2243
    optparser.add_option(
Packit 8f2243
        '--keep-images',
Packit 8f2243
        action="store_false", dest="strip_images", default=True,
Packit 8f2243
        help="compare images")
Packit 8f2243
Packit 8f2243
    (options, args) = optparser.parse_args(sys.argv[1:])
Packit 8f2243
Packit 8f2243
    if len(args) != 2:
Packit 8f2243
        optparser.error('incorrect number of arguments')
Packit 8f2243
Packit 8f2243
    a = load(open(sys.argv[1], 'rt'), options.strip_images)
Packit 8f2243
    b = load(open(sys.argv[2], 'rt'), options.strip_images)
Packit 8f2243
Packit 8f2243
    if False:
Packit 8f2243
        dumper = Dumper()
Packit 8f2243
        dumper.visit(a)
Packit 8f2243
Packit 8f2243
    differ = Differ()
Packit 8f2243
    differ.visit(a, b)
Packit 8f2243
Packit 8f2243
Packit 8f2243
if __name__ == '__main__':
Packit 8f2243
    main()