Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(8)

Side by Side Diff: tools/jsondiff.py

Issue 18103005: Create jsondiff tool to capture diffs between two JSON expectations files. (Closed) Base URL: http://skia.googlecode.com/svn/trunk/
Patch Set: fix_self_test Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « gm/gm_json.py ('k') | tools/tests/jsondiff/input/new.json » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Property Changes:
Added: svn:executable
+ *
OLDNEW
(Empty)
1 #!/usr/bin/python
2
3 '''
4 Copyright 2013 Google Inc.
5
6 Use of this source code is governed by a BSD-style license that can be
7 found in the LICENSE file.
8 '''
9
10 '''
11 Gathers diffs between 2 JSON expectations files, or between actual and
12 expected results within a single JSON actual-results file,
13 and generates an old-vs-new diff dictionary.
epoger 2013/07/02 18:24:32 For now, this tool just generates a dictionary sho
14 '''
15
16 # System-level imports
17 import argparse
18 import json
19 import os
20 import sys
21 import urllib2
22
23 # Imports from within Skia
24 #
25 # We need to add the 'gm' directory, so that we can import gm_json.py within
26 # that directory. That script allows us to parse the actual-results.json file
27 # written out by the GM tool.
28 # Make sure that the 'gm' dir is in the PYTHONPATH, but add it at the *end*
29 # so any dirs that are already in the PYTHONPATH will be preferred.
30 #
31 # This assumes that the 'gm' directory has been checked out as a sibling of
32 # the 'tools' directory containing this script, which will be the case if
33 # 'trunk' was checked out as a single unit.
34 GM_DIRECTORY = os.path.realpath(
35 os.path.join(os.path.dirname(os.path.dirname(__file__)), 'gm'))
36 if GM_DIRECTORY not in sys.path:
37 sys.path.append(GM_DIRECTORY)
38 import gm_json
39
40
41 # Object that generates diffs between two JSON gm result files.
42 class GMDiffer(object):
43
44 def __init__(self):
45 pass
46
47 def _GetFileContentsAsString(self, filepath):
48 """Returns the full contents of a file, as a single string.
49 If the filename looks like a URL, download its contents..."""
50 if filepath.startswith('http:') or filepath.startswith('https:'):
51 return urllib2.urlopen(filepath).read()
52 else:
53 return open(filepath, 'r').read()
54
55 def _GetExpectedResults(self, filepath):
56 """Returns the dictionary of expected results from a JSON file,
57 in this form:
58
59 {
60 'test1' : 14760033689012826769,
61 'test2' : 9151974350149210736,
62 ...
63 }
64
65 We make these simplifying assumptions:
66 1. Each test has either 0 or 1 allowed results.
67 2. All expectations are of type JSONKEY_HASHTYPE_BITMAP_64BITMD5.
68
69 Any tests which violate those assumptions will cause an exception to
70 be raised.
71
72 Any tests for which we have no expectations will be left out of the
73 returned dictionary.
74 """
75 result_dict = {}
76 contents = self._GetFileContentsAsString(filepath)
77 json_dict = gm_json.LoadFromString(contents)
78 all_expectations = json_dict[gm_json.JSONKEY_EXPECTEDRESULTS]
79 for test_name in all_expectations.keys():
80 test_expectations = all_expectations[test_name]
81 allowed_digests = test_expectations[
82 gm_json.JSONKEY_EXPECTEDRESULTS_ALLOWEDDIGESTS]
83 if allowed_digests:
84 num_allowed_digests = len(allowed_digests)
85 if num_allowed_digests > 1:
86 raise ValueError(
87 'test %s in file %s has %d allowed digests' % (
88 test_name, filepath, num_allowed_digests))
89 digest_pair = allowed_digests[0]
90 if digest_pair[0] != gm_json.JSONKEY_HASHTYPE_BITMAP_64BITMD5:
91 raise ValueError(
92 'test %s in file %s has unsupported hashtype %s' % (
93 test_name, filepath, digest_pair[0]))
94 result_dict[test_name] = digest_pair[1]
95 return result_dict
96
97 def _GetActualResults(self, filepath):
98 """Returns the dictionary of actual results from a JSON file,
99 in this form:
100
101 {
102 'test1' : 14760033689012826769,
103 'test2' : 9151974350149210736,
104 ...
105 }
106
107 We make these simplifying assumptions:
108 1. All results are of type JSONKEY_HASHTYPE_BITMAP_64BITMD5.
109
110 Any tests which violate those assumptions will cause an exception to
111 be raised.
112
113 Any tests for which we have no actual results will be left out of the
114 returned dictionary.
115 """
116 result_dict = {}
117 contents = self._GetFileContentsAsString(filepath)
118 json_dict = gm_json.LoadFromString(contents)
119 all_result_types = json_dict[gm_json.JSONKEY_ACTUALRESULTS]
120 for result_type in all_result_types.keys():
121 results_of_this_type = all_result_types[result_type]
122 if results_of_this_type:
123 for test_name in results_of_this_type.keys():
124 digest_pair = results_of_this_type[test_name]
125 if digest_pair[0] != gm_json.JSONKEY_HASHTYPE_BITMAP_64BITMD 5:
126 raise ValueError(
127 'test %s in file %s has unsupported hashtype %s' % (
128 test_name, filepath, digest_pair[0]))
129 result_dict[test_name] = digest_pair[1]
130 return result_dict
131
132 def _DictionaryDiff(self, old_dict, new_dict):
133 """Generate a dictionary showing the diffs between old_dict and new_dict .
134 Any entries which are identical across them will be left out."""
135 diff_dict = {}
136 all_keys = set(old_dict.keys() + new_dict.keys())
137 for key in all_keys:
138 if old_dict.get(key) != new_dict.get(key):
139 new_entry = {}
140 new_entry['old'] = old_dict.get(key)
141 new_entry['new'] = new_dict.get(key)
142 diff_dict[key] = new_entry
143 return diff_dict
144
145 def GenerateDiffDict(self, oldfile, newfile=None):
146 """Generate a dictionary showing the diffs:
147 old = expectations within oldfile
148 new = expectations within newfile
149
150 If newfile is not specified, then 'new' is the actual results within
151 oldfile.
152 """
153 old_results = self._GetExpectedResults(oldfile)
154 if newfile:
155 new_results = self._GetExpectedResults(newfile)
156 else:
157 new_results = self._GetActualResults(oldfile)
158 return self._DictionaryDiff(old_results, new_results)
159
160
161 # main...
162 parser = argparse.ArgumentParser()
163 parser.add_argument('old',
164 help='Path to JSON file whose expectations to display on ' +
165 'the "old" side of the diff. This can be a filepath on ' +
166 'local storage, or a URL.')
167 parser.add_argument('new', nargs='?',
168 help='Path to JSON file whose expectations to display on ' +
169 'the "new" side of the diff; if not specified, uses the ' +
170 'ACTUAL results from the "old" JSON file. This can be a ' +
171 'filepath on local storage, or a URL.')
172 args = parser.parse_args()
173 differ = GMDiffer()
174 diffs = differ.GenerateDiffDict(oldfile=args.old, newfile=args.new)
175 json.dump(diffs, sys.stdout, sort_keys=True, indent=2)
OLDNEW
« no previous file with comments | « gm/gm_json.py ('k') | tools/tests/jsondiff/input/new.json » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698