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

Side by Side Diff: gm/rebaseline_server/results.py

Issue 25045003: HTTP GM results viewer: server now returns category summaries along with testData (Closed) Base URL: http://skia.googlecode.com/svn/trunk/
Patch Set: final_tweaks Created 7 years, 2 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 | « no previous file | gm/rebaseline_server/server.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 2
3 ''' 3 '''
4 Copyright 2013 Google Inc. 4 Copyright 2013 Google Inc.
5 5
6 Use of this source code is governed by a BSD-style license that can be 6 Use of this source code is governed by a BSD-style license that can be
7 found in the LICENSE file. 7 found in the LICENSE file.
8 ''' 8 '''
9 9
10 ''' 10 '''
(...skipping 13 matching lines...) Expand all
24 # that directory. That script allows us to parse the actual-results.json file 24 # that directory. That script allows us to parse the actual-results.json file
25 # written out by the GM tool. 25 # written out by the GM tool.
26 # Make sure that the 'gm' dir is in the PYTHONPATH, but add it at the *end* 26 # Make sure that the 'gm' dir is in the PYTHONPATH, but add it at the *end*
27 # so any dirs that are already in the PYTHONPATH will be preferred. 27 # so any dirs that are already in the PYTHONPATH will be preferred.
28 GM_DIRECTORY = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) 28 GM_DIRECTORY = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
29 if GM_DIRECTORY not in sys.path: 29 if GM_DIRECTORY not in sys.path:
30 sys.path.append(GM_DIRECTORY) 30 sys.path.append(GM_DIRECTORY)
31 import gm_json 31 import gm_json
32 32
33 IMAGE_FILENAME_RE = re.compile(gm_json.IMAGE_FILENAME_PATTERN) 33 IMAGE_FILENAME_RE = re.compile(gm_json.IMAGE_FILENAME_PATTERN)
34 CATEGORIES_TO_SUMMARIZE = [
35 'builder', 'test', 'config', 'resultType',
36 ]
34 37
35 class Results(object): 38 class Results(object):
36 """ Loads actual and expected results from all builders, supplying combined 39 """ Loads actual and expected results from all builders, supplying combined
37 reports as requested. """ 40 reports as requested. """
38 41
39 def __init__(self, actuals_root, expected_root): 42 def __init__(self, actuals_root, expected_root):
40 """ 43 """
41 params: 44 params:
42 actuals_root: root directory containing all actual-results.json files 45 actuals_root: root directory containing all actual-results.json files
43 expected_root: root directory containing all expected-results.json files 46 expected_root: root directory containing all expected-results.json files
44 """ 47 """
45 self._actual_builder_dicts = Results._GetDictsFromRoot(actuals_root) 48 self._actual_builder_dicts = Results._GetDictsFromRoot(actuals_root)
46 self._expected_builder_dicts = Results._GetDictsFromRoot(expected_root) 49 self._expected_builder_dicts = Results._GetDictsFromRoot(expected_root)
47 self._all_results = self._Combine() 50 self._all_results = Results._Combine(
51 actual_builder_dicts=self._actual_builder_dicts,
52 expected_builder_dicts=self._expected_builder_dicts)
48 53
49 def GetAll(self): 54 def GetAll(self):
50 """Return results of all tests, as a list in this form: 55 """Return results of all tests, as a dictionary in this form:
51 56
52 [ 57 {
58 "categories": # dictionary of categories listed in
59 # CATEGORIES_TO_SUMMARIZE, with the number of times
60 # each value appears within its category
53 { 61 {
54 "builder": "Test-Mac10.6-MacMini4.1-GeForce320M-x86-Debug", 62 "resultType": # category name
55 "test": "bigmatrix", 63 {
56 "config": "8888", 64 "failed": 29, # category value and total number found of that value
57 "resultType": "failed", 65 "failure-ignored": 948,
58 "expectedHashType": "bitmap-64bitMD5", 66 "no-comparison": 4502,
59 "expectedHashDigest": "10894408024079689926", 67 "succeeded": 38609,
60 "actualHashType": "bitmap-64bitMD5", 68 },
61 "actualHashDigest": "2409857384569", 69 "builder":
62 }, 70 {
63 ... 71 "Test-Mac10.6-MacMini4.1-GeForce320M-x86-Debug": 1286,
64 ] 72 "Test-Mac10.6-MacMini4.1-GeForce320M-x86-Release": 1134,
73 ...
74 },
75 ... # other categories from CATEGORIES_TO_SUMMARIZE
76 }, # end of "categories" dictionary
77
78 "testData": # list of test results, with a dictionary for each
79 [
80 {
81 "builder": "Test-Mac10.6-MacMini4.1-GeForce320M-x86-Debug",
82 "test": "bigmatrix",
83 "config": "8888",
84 "resultType": "failed",
85 "expectedHashType": "bitmap-64bitMD5",
86 "expectedHashDigest": "10894408024079689926",
87 "actualHashType": "bitmap-64bitMD5",
88 "actualHashDigest": "2409857384569",
89 },
90 ...
91 ], # end of "testData" list
92 }
65 """ 93 """
66 return self._all_results 94 return self._all_results
67 95
68 @staticmethod 96 @staticmethod
69 def _GetDictsFromRoot(root, pattern='*.json'): 97 def _GetDictsFromRoot(root, pattern='*.json'):
70 """Read all JSON dictionaries within a directory tree, returning them within 98 """Read all JSON dictionaries within a directory tree, returning them within
71 a meta-dictionary (keyed by the builder name for each dictionary). 99 a meta-dictionary (keyed by the builder name for each dictionary).
72 100
73 params: 101 params:
74 root: path to root of directory tree 102 root: path to root of directory tree
75 pattern: which files to read within root (fnmatch-style pattern) 103 pattern: which files to read within root (fnmatch-style pattern)
76 """ 104 """
77 meta_dict = {} 105 meta_dict = {}
78 for dirpath, dirnames, filenames in os.walk(root): 106 for dirpath, dirnames, filenames in os.walk(root):
79 for matching_filename in fnmatch.filter(filenames, pattern): 107 for matching_filename in fnmatch.filter(filenames, pattern):
80 builder = os.path.basename(dirpath) 108 builder = os.path.basename(dirpath)
81 if builder.endswith('-Trybot'): 109 if builder.endswith('-Trybot'):
82 continue 110 continue
83 fullpath = os.path.join(dirpath, matching_filename) 111 fullpath = os.path.join(dirpath, matching_filename)
84 meta_dict[builder] = gm_json.LoadFromFile(fullpath) 112 meta_dict[builder] = gm_json.LoadFromFile(fullpath)
85 return meta_dict 113 return meta_dict
86 114
87 def _Combine(self): 115 @staticmethod
88 """Returns a list of all tests, across all builders, based on the 116 def _Combine(actual_builder_dicts, expected_builder_dicts):
89 contents of self._actual_builder_dicts and self._expected_builder_dicts . 117 """Gathers the results of all tests, across all builders (based on the
90 Returns the list in the same form needed for GetAllResults(). 118 contents of actual_builder_dicts and expected_builder_dicts)
119 and returns it in a list in the same form needed for self.GetAll().
120
121 This is a static method, because once we start refreshing results
122 asynchronously, we need to make sure we are not corrupting the object's
123 member variables.
91 """ 124 """
92 all_tests = [] 125 test_data = []
93 for builder in sorted(self._actual_builder_dicts.keys()): 126 category_dict = {}
127 for builder in sorted(actual_builder_dicts.keys()):
94 actual_results_for_this_builder = ( 128 actual_results_for_this_builder = (
95 self._actual_builder_dicts[builder][gm_json.JSONKEY_ACTUALRESULTS]) 129 actual_builder_dicts[builder][gm_json.JSONKEY_ACTUALRESULTS])
96 for result_type in sorted(actual_results_for_this_builder.keys()): 130 for result_type in sorted(actual_results_for_this_builder.keys()):
97 results_of_this_type = actual_results_for_this_builder[result_type] 131 results_of_this_type = actual_results_for_this_builder[result_type]
98 if not results_of_this_type: 132 if not results_of_this_type:
99 continue 133 continue
100 for image_name in sorted(results_of_this_type.keys()): 134 for image_name in sorted(results_of_this_type.keys()):
101 actual_image = results_of_this_type[image_name] 135 actual_image = results_of_this_type[image_name]
102 try: 136 try:
103 # TODO(epoger): assumes a single allowed digest per test 137 # TODO(epoger): assumes a single allowed digest per test
104 expected_image = ( 138 expected_image = (
105 self._expected_builder_dicts 139 expected_builder_dicts
106 [builder][gm_json.JSONKEY_EXPECTEDRESULTS] 140 [builder][gm_json.JSONKEY_EXPECTEDRESULTS]
107 [image_name][gm_json.JSONKEY_EXPECTEDRESULTS_ALLOWEDDIGESTS] 141 [image_name][gm_json.JSONKEY_EXPECTEDRESULTS_ALLOWEDDIGESTS]
108 [0]) 142 [0])
109 except (KeyError, TypeError): 143 except (KeyError, TypeError):
110 # There are several cases in which we would expect to find 144 # There are several cases in which we would expect to find
111 # no expectations for a given test: 145 # no expectations for a given test:
112 # 146 #
113 # 1. result_type == NOCOMPARISON 147 # 1. result_type == NOCOMPARISON
114 # There are no expectations for this test yet! 148 # There are no expectations for this test yet!
115 # 149 #
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
152 # as ignoreFailure but it still shows up in the "failed" 186 # as ignoreFailure but it still shows up in the "failed"
153 # category? Maybe we should not rely on the result_type 187 # category? Maybe we should not rely on the result_type
154 # categories recorded within the gm_actuals AT ALL, and 188 # categories recorded within the gm_actuals AT ALL, and
155 # instead evaluate the result_type ourselves based on what 189 # instead evaluate the result_type ourselves based on what
156 # we see in expectations vs actual checksum? 190 # we see in expectations vs actual checksum?
157 if expected_image == actual_image: 191 if expected_image == actual_image:
158 updated_result_type = gm_json.JSONKEY_ACTUALRESULTS_SUCCEEDED 192 updated_result_type = gm_json.JSONKEY_ACTUALRESULTS_SUCCEEDED
159 else: 193 else:
160 updated_result_type = result_type 194 updated_result_type = result_type
161 195
162 # TODO(epoger): For now, don't include succeeded results.
163 # There are so many of them that they make the client too slow.
164 if updated_result_type == gm_json.JSONKEY_ACTUALRESULTS_SUCCEEDED:
165 continue
166
167 (test, config) = IMAGE_FILENAME_RE.match(image_name).groups() 196 (test, config) = IMAGE_FILENAME_RE.match(image_name).groups()
168 all_tests.append({ 197 results_for_this_test = {
169 "builder": builder, 198 "builder": builder,
170 "test": test, 199 "test": test,
171 "config": config, 200 "config": config,
172 "resultType": updated_result_type, 201 "resultType": updated_result_type,
173 "actualHashType": actual_image[0], 202 "actualHashType": actual_image[0],
174 "actualHashDigest": str(actual_image[1]), 203 "actualHashDigest": str(actual_image[1]),
175 "expectedHashType": expected_image[0], 204 "expectedHashType": expected_image[0],
176 "expectedHashDigest": str(expected_image[1]), 205 "expectedHashDigest": str(expected_image[1]),
177 }) 206 }
178 return all_tests 207 Results._AddToCategoryDict(category_dict, results_for_this_test)
208
209 # TODO(epoger): For now, don't include succeeded results in the raw
210 # data. There are so many of them that they make the client too slow.
211 if updated_result_type != gm_json.JSONKEY_ACTUALRESULTS_SUCCEEDED:
212 test_data.append(results_for_this_test)
213 return {"categories": category_dict, "testData": test_data}
214
215 @staticmethod
216 def _AddToCategoryDict(category_dict, test_results):
217 """Add test_results to the category dictionary we are building
218 (see documentation of self.GetAll() for the format of this dictionary).
219
220 params:
221 category_dict: category dict-of-dicts to add to; modify this in-place
222 test_results: test data with which to update category_list, in a dict:
223 {
224 "category_name": "category_value",
225 "category_name": "category_value",
226 ...
227 }
228 """
229 for category in CATEGORIES_TO_SUMMARIZE:
230 category_value = test_results.get(category)
231 if not category_value:
232 continue # test_results did not include this category, keep going
233 if not category_dict.get(category):
234 category_dict[category] = {}
235 if not category_dict[category].get(category_value):
236 category_dict[category][category_value] = 0
237 category_dict[category][category_value] += 1
OLDNEW
« no previous file with comments | « no previous file | gm/rebaseline_server/server.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698