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

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

Issue 26659002: rebaseline_server: extend returned JSON dict to allow for result-editing in coming CL (Closed) Base URL: http://skia.googlecode.com/svn/trunk/
Patch Set: oppa_python_style 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 '''
9 8
10 '''
11 Repackage expected/actual GM results as needed by our HTML rebaseline viewer. 9 Repackage expected/actual GM results as needed by our HTML rebaseline viewer.
12 ''' 10 """
13 11
14 # System-level imports 12 # System-level imports
15 import fnmatch 13 import fnmatch
16 import json 14 import json
17 import os 15 import os
18 import re 16 import re
19 import sys 17 import sys
20 18
21 # Imports from within Skia 19 # Imports from within Skia
22 # 20 #
(...skipping 11 matching lines...) Expand all
34 CATEGORIES_TO_SUMMARIZE = [ 32 CATEGORIES_TO_SUMMARIZE = [
35 'builder', 'test', 'config', 'resultType', 33 'builder', 'test', 'config', 'resultType',
36 ] 34 ]
37 35
38 class Results(object): 36 class Results(object):
39 """ Loads actual and expected results from all builders, supplying combined 37 """ Loads actual and expected results from all builders, supplying combined
40 reports as requested. """ 38 reports as requested. """
41 39
42 def __init__(self, actuals_root, expected_root): 40 def __init__(self, actuals_root, expected_root):
43 """ 41 """
44 params: 42 Args:
45 actuals_root: root directory containing all actual-results.json files 43 actuals_root: root directory containing all actual-results.json files
46 expected_root: root directory containing all expected-results.json files 44 expected_root: root directory containing all expected-results.json files
47 """ 45 """
48 self._actual_builder_dicts = Results._GetDictsFromRoot(actuals_root) 46 self._actual_builder_dicts = Results._GetDictsFromRoot(actuals_root)
49 self._expected_builder_dicts = Results._GetDictsFromRoot(expected_root) 47 self._expected_builder_dicts = Results._GetDictsFromRoot(expected_root)
50 self._all_results = Results._Combine( 48 self._all_results = Results._Combine(
51 actual_builder_dicts=self._actual_builder_dicts, 49 actual_builder_dicts=self._actual_builder_dicts,
52 expected_builder_dicts=self._expected_builder_dicts) 50 expected_builder_dicts=self._expected_builder_dicts)
53 51
54 def GetAll(self): 52 def GetAll(self):
55 """Return results of all tests, as a dictionary in this form: 53 """Return results of all tests, as a dictionary in this form:
56 54
57 { 55 {
58 "categories": # dictionary of categories listed in 56 'categories': # dictionary of categories listed in
59 # CATEGORIES_TO_SUMMARIZE, with the number of times 57 # CATEGORIES_TO_SUMMARIZE, with the number of times
60 # each value appears within its category 58 # each value appears within its category
61 { 59 {
62 "resultType": # category name 60 'resultType': # category name
63 { 61 {
64 "failed": 29, # category value and total number found of that value 62 'failed': 29, # category value and total number found of that value
65 "failure-ignored": 948, 63 'failure-ignored': 948,
66 "no-comparison": 4502, 64 'no-comparison': 4502,
67 "succeeded": 38609, 65 'succeeded': 38609,
68 }, 66 },
69 "builder": 67 'builder':
70 { 68 {
71 "Test-Mac10.6-MacMini4.1-GeForce320M-x86-Debug": 1286, 69 'Test-Mac10.6-MacMini4.1-GeForce320M-x86-Debug': 1286,
72 "Test-Mac10.6-MacMini4.1-GeForce320M-x86-Release": 1134, 70 'Test-Mac10.6-MacMini4.1-GeForce320M-x86-Release': 1134,
73 ... 71 ...
74 }, 72 },
75 ... # other categories from CATEGORIES_TO_SUMMARIZE 73 ... # other categories from CATEGORIES_TO_SUMMARIZE
76 }, # end of "categories" dictionary 74 }, # end of 'categories' dictionary
77 75
78 "testData": # list of test results, with a dictionary for each 76 'testData': # list of test results, with a dictionary for each
79 [ 77 [
80 { 78 {
81 "builder": "Test-Mac10.6-MacMini4.1-GeForce320M-x86-Debug", 79 'index': 0, # index of this result within testData list
82 "test": "bigmatrix", 80 'builder': 'Test-Mac10.6-MacMini4.1-GeForce320M-x86-Debug',
83 "config": "8888", 81 'test': 'bigmatrix',
84 "resultType": "failed", 82 'config': '8888',
85 "expectedHashType": "bitmap-64bitMD5", 83 'resultType': 'failed',
86 "expectedHashDigest": "10894408024079689926", 84 'expectedHashType': 'bitmap-64bitMD5',
87 "actualHashType": "bitmap-64bitMD5", 85 'expectedHashDigest': '10894408024079689926',
88 "actualHashDigest": "2409857384569", 86 'actualHashType': 'bitmap-64bitMD5',
87 'actualHashDigest': '2409857384569',
89 }, 88 },
90 ... 89 ...
91 ], # end of "testData" list 90 ], # end of 'testData' list
92 } 91 }
93 """ 92 """
94 return self._all_results 93 return self._all_results
95 94
96 @staticmethod 95 @staticmethod
97 def _GetDictsFromRoot(root, pattern='*.json'): 96 def _GetDictsFromRoot(root, pattern='*.json'):
98 """Read all JSON dictionaries within a directory tree, returning them within 97 """Read all JSON dictionaries within a directory tree.
99 a meta-dictionary (keyed by the builder name for each dictionary).
100 98
101 params: 99 Args:
102 root: path to root of directory tree 100 root: path to root of directory tree
103 pattern: which files to read within root (fnmatch-style pattern) 101 pattern: which files to read within root (fnmatch-style pattern)
102
103 Returns:
104 A meta-dictionary containing all the JSON dictionaries found within
105 the directory tree, keyed by the builder name of each dictionary.
104 """ 106 """
105 meta_dict = {} 107 meta_dict = {}
106 for dirpath, dirnames, filenames in os.walk(root): 108 for dirpath, dirnames, filenames in os.walk(root):
107 for matching_filename in fnmatch.filter(filenames, pattern): 109 for matching_filename in fnmatch.filter(filenames, pattern):
108 builder = os.path.basename(dirpath) 110 builder = os.path.basename(dirpath)
109 if builder.endswith('-Trybot'): 111 if builder.endswith('-Trybot'):
110 continue 112 continue
111 fullpath = os.path.join(dirpath, matching_filename) 113 fullpath = os.path.join(dirpath, matching_filename)
112 meta_dict[builder] = gm_json.LoadFromFile(fullpath) 114 meta_dict[builder] = gm_json.LoadFromFile(fullpath)
113 return meta_dict 115 return meta_dict
114 116
115 @staticmethod 117 @staticmethod
116 def _Combine(actual_builder_dicts, expected_builder_dicts): 118 def _Combine(actual_builder_dicts, expected_builder_dicts):
117 """Gathers the results of all tests, across all builders (based on the 119 """Gathers the results of all tests, across all builders (based on the
118 contents of actual_builder_dicts and expected_builder_dicts) 120 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
121 This is a static method, because once we start refreshing results 122 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 asynchronously, we need to make sure we are not corrupting the object's
123 member variables. 124 member variables.
125
126 Args:
127 actual_builder_dicts: a meta-dictionary of all actual JSON results,
128 as returned by _GetDictsFromRoot().
129 actual_builder_dicts: a meta-dictionary of all expected JSON results,
130 as returned by _GetDictsFromRoot().
131
132 Returns:
133 A list of all the results of all tests, in the same form returned by
134 self.GetAll().
124 """ 135 """
125 test_data = [] 136 test_data = []
126 category_dict = {} 137 category_dict = {}
127 Results._EnsureIncludedInCategoryDict(category_dict, 'resultType', [ 138 Results._EnsureIncludedInCategoryDict(category_dict, 'resultType', [
128 gm_json.JSONKEY_ACTUALRESULTS_FAILED, 139 gm_json.JSONKEY_ACTUALRESULTS_FAILED,
129 gm_json.JSONKEY_ACTUALRESULTS_FAILUREIGNORED, 140 gm_json.JSONKEY_ACTUALRESULTS_FAILUREIGNORED,
130 gm_json.JSONKEY_ACTUALRESULTS_NOCOMPARISON, 141 gm_json.JSONKEY_ACTUALRESULTS_NOCOMPARISON,
131 gm_json.JSONKEY_ACTUALRESULTS_SUCCEEDED, 142 gm_json.JSONKEY_ACTUALRESULTS_SUCCEEDED,
132 ]) 143 ])
133 144
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
176 gm_json.JSONKEY_ACTUALRESULTS_NOCOMPARISON, 187 gm_json.JSONKEY_ACTUALRESULTS_NOCOMPARISON,
177 gm_json.JSONKEY_ACTUALRESULTS_FAILUREIGNORED] : 188 gm_json.JSONKEY_ACTUALRESULTS_FAILUREIGNORED] :
178 print 'WARNING: No expectations found for test: %s' % { 189 print 'WARNING: No expectations found for test: %s' % {
179 'builder': builder, 190 'builder': builder,
180 'image_name': image_name, 191 'image_name': image_name,
181 'result_type': result_type, 192 'result_type': result_type,
182 } 193 }
183 expected_image = [None, None] 194 expected_image = [None, None]
184 195
185 # If this test was recently rebaselined, it will remain in 196 # If this test was recently rebaselined, it will remain in
186 # the "failed" set of actuals until all the bots have 197 # the 'failed' set of actuals until all the bots have
187 # cycled (although the expectations have indeed been set 198 # cycled (although the expectations have indeed been set
188 # from the most recent actuals). Treat these as successes 199 # from the most recent actuals). Treat these as successes
189 # instead of failures. 200 # instead of failures.
190 # 201 #
191 # TODO(epoger): Do we need to do something similar in 202 # TODO(epoger): Do we need to do something similar in
192 # other cases, such as when we have recently marked a test 203 # other cases, such as when we have recently marked a test
193 # as ignoreFailure but it still shows up in the "failed" 204 # as ignoreFailure but it still shows up in the 'failed'
194 # category? Maybe we should not rely on the result_type 205 # category? Maybe we should not rely on the result_type
195 # categories recorded within the gm_actuals AT ALL, and 206 # categories recorded within the gm_actuals AT ALL, and
196 # instead evaluate the result_type ourselves based on what 207 # instead evaluate the result_type ourselves based on what
197 # we see in expectations vs actual checksum? 208 # we see in expectations vs actual checksum?
198 if expected_image == actual_image: 209 if expected_image == actual_image:
199 updated_result_type = gm_json.JSONKEY_ACTUALRESULTS_SUCCEEDED 210 updated_result_type = gm_json.JSONKEY_ACTUALRESULTS_SUCCEEDED
200 else: 211 else:
201 updated_result_type = result_type 212 updated_result_type = result_type
202 213
203 (test, config) = IMAGE_FILENAME_RE.match(image_name).groups() 214 (test, config) = IMAGE_FILENAME_RE.match(image_name).groups()
204 results_for_this_test = { 215 results_for_this_test = {
205 "builder": builder, 216 'index': len(test_data),
206 "test": test, 217 'builder': builder,
207 "config": config, 218 'test': test,
208 "resultType": updated_result_type, 219 'config': config,
209 "actualHashType": actual_image[0], 220 'resultType': updated_result_type,
210 "actualHashDigest": str(actual_image[1]), 221 'actualHashType': actual_image[0],
211 "expectedHashType": expected_image[0], 222 'actualHashDigest': str(actual_image[1]),
212 "expectedHashDigest": str(expected_image[1]), 223 'expectedHashType': expected_image[0],
224 'expectedHashDigest': str(expected_image[1]),
213 } 225 }
214 Results._AddToCategoryDict(category_dict, results_for_this_test) 226 Results._AddToCategoryDict(category_dict, results_for_this_test)
215 test_data.append(results_for_this_test) 227 test_data.append(results_for_this_test)
216 return {"categories": category_dict, "testData": test_data} 228 return {'categories': category_dict, 'testData': test_data}
217 229
218 @staticmethod 230 @staticmethod
219 def _AddToCategoryDict(category_dict, test_results): 231 def _AddToCategoryDict(category_dict, test_results):
220 """Add test_results to the category dictionary we are building. 232 """Add test_results to the category dictionary we are building.
221 (See documentation of self.GetAll() for the format of this dictionary.) 233 (See documentation of self.GetAll() for the format of this dictionary.)
222 234
223 params: 235 Args:
224 category_dict: category dict-of-dicts to add to; modify this in-place 236 category_dict: category dict-of-dicts to add to; modify this in-place
225 test_results: test data with which to update category_list, in a dict: 237 test_results: test data with which to update category_list, in a dict:
226 { 238 {
227 "category_name": "category_value", 239 'category_name': 'category_value',
228 "category_name": "category_value", 240 'category_name': 'category_value',
229 ... 241 ...
230 } 242 }
231 """ 243 """
232 for category in CATEGORIES_TO_SUMMARIZE: 244 for category in CATEGORIES_TO_SUMMARIZE:
233 category_value = test_results.get(category) 245 category_value = test_results.get(category)
234 if not category_value: 246 if not category_value:
235 continue # test_results did not include this category, keep going 247 continue # test_results did not include this category, keep going
236 if not category_dict.get(category): 248 if not category_dict.get(category):
237 category_dict[category] = {} 249 category_dict[category] = {}
238 if not category_dict[category].get(category_value): 250 if not category_dict[category].get(category_value):
239 category_dict[category][category_value] = 0 251 category_dict[category][category_value] = 0
240 category_dict[category][category_value] += 1 252 category_dict[category][category_value] += 1
241 253
242 @staticmethod 254 @staticmethod
243 def _EnsureIncludedInCategoryDict(category_dict, 255 def _EnsureIncludedInCategoryDict(category_dict,
244 category_name, category_values): 256 category_name, category_values):
245 """Ensure that the category name/value pairs are included in category_dict, 257 """Ensure that the category name/value pairs are included in category_dict,
246 even if there aren't any results with that name/value pair. 258 even if there aren't any results with that name/value pair.
247 (See documentation of self.GetAll() for the format of this dictionary.) 259 (See documentation of self.GetAll() for the format of this dictionary.)
248 260
249 params: 261 Args:
250 category_dict: category dict-of-dicts to modify 262 category_dict: category dict-of-dicts to modify
251 category_name: category name, as a string 263 category_name: category name, as a string
252 category_values: list of values we want to make sure are represented 264 category_values: list of values we want to make sure are represented
253 for this category 265 for this category
254 """ 266 """
255 if not category_dict.get(category_name): 267 if not category_dict.get(category_name):
256 category_dict[category_name] = {} 268 category_dict[category_name] = {}
257 for category_value in category_values: 269 for category_value in category_values:
258 if not category_dict[category_name].get(category_value): 270 if not category_dict[category_name].get(category_value):
259 category_dict[category_name][category_value] = 0 271 category_dict[category_name][category_value] = 0
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