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

Side by Side Diff: tools/rebaseline.py

Issue 16306010: rebaseline.py: --tests and --configs are now FILTERS within json results (Closed) Base URL: http://skia.googlecode.com/svn/trunk/
Patch Set: add_blank_lines Created 7 years, 6 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
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 2
3 ''' 3 '''
4 Copyright 2012 Google Inc. 4 Copyright 2012 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 '''
11 Rebaselines the given GM tests, on all bots and all configurations. 11 Rebaselines the given GM tests, on all bots and all configurations.
12 Must be run from the gm-expected directory. If run from a git or SVN 12 Must be run from the gm-expected directory. If run from a git or SVN
13 checkout, the files will be added to the staging area for commit. 13 checkout, the files will be added to the staging area for commit.
14 ''' 14 '''
15 15
16 # System-level imports 16 # System-level imports
17 import argparse 17 import argparse
18 import os 18 import os
19 import subprocess 19 import subprocess
20 import sys 20 import sys
21 import urllib2 21 import urllib2
22 22
23 # Imports from within Skia 23 # Imports from within Skia
24 # 24 #
25 # Make sure that they are in the PYTHONPATH, but add them at the *end* 25 # We need to add the 'gm' directory, so that we can import gm_json.py within
26 # so any that are already in the PYTHONPATH will be preferred. 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 ir 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.
27 GM_DIRECTORY = os.path.realpath( 34 GM_DIRECTORY = os.path.realpath(
28 os.path.join(os.path.dirname(os.path.dirname(__file__)), 'gm')) 35 os.path.join(os.path.dirname(os.path.dirname(__file__)), 'gm'))
29 if GM_DIRECTORY not in sys.path: 36 if GM_DIRECTORY not in sys.path:
30 sys.path.append(GM_DIRECTORY) 37 sys.path.append(GM_DIRECTORY)
31 import gm_json 38 import gm_json
32 39
33 40
34 # Mapping of gm-expectations subdir (under 41 # Mapping of gm-expectations subdir (under
35 # https://skia.googlecode.com/svn/gm-expected/ ) 42 # https://skia.googlecode.com/svn/gm-expected/ )
36 # to builder name (see list at http://108.170.217.252:10117/builders ) 43 # to builder name (see list at http://108.170.217.252:10117/builders )
(...skipping 27 matching lines...) Expand all
64 pass 71 pass
65 72
66 class Rebaseliner(object): 73 class Rebaseliner(object):
67 74
68 # params: 75 # params:
69 # json_base_url: base URL from which to read json_filename 76 # json_base_url: base URL from which to read json_filename
70 # json_filename: filename (under json_base_url) from which to read a 77 # json_filename: filename (under json_base_url) from which to read a
71 # summary of results; typically "actual-results.json" 78 # summary of results; typically "actual-results.json"
72 # subdirs: which platform subdirectories to rebaseline; if not specified, 79 # subdirs: which platform subdirectories to rebaseline; if not specified,
73 # rebaseline all platform subdirectories 80 # rebaseline all platform subdirectories
74 # tests: list of tests to rebaseline, or None if we should rebaseline 81 # tests: list of tests to rebaseline, as a filter applied to
75 # whatever files the JSON results summary file tells us to 82 # the list from the JSON file
76 # configs: which configs to run for each test; this should only be 83 # configs: which configs to run for each test, as a filter applied to
77 # specified if the list of tests was also specified (otherwise, 84 # the list from the JSON file
78 # the JSON file will give us test names and configs)
79 # dry_run: if True, instead of actually downloading files or adding 85 # dry_run: if True, instead of actually downloading files or adding
80 # files to checkout, display a list of operations that 86 # files to checkout, display a list of operations that
81 # we would normally perform 87 # we would normally perform
82 def __init__(self, json_base_url, json_filename, 88 def __init__(self, json_base_url, json_filename,
83 subdirs=None, tests=None, configs=None, dry_run=False): 89 subdirs=None, tests=None, configs=None, dry_run=False):
84 if configs and not tests:
85 raise ValueError('configs should only be specified if tests ' +
86 'were specified also')
87 self._tests = tests 90 self._tests = tests
88 self._configs = configs 91 self._configs = configs
89 if not subdirs: 92 if not subdirs:
90 self._subdirs = sorted(SUBDIR_MAPPING.keys()) 93 self._subdirs = sorted(SUBDIR_MAPPING.keys())
91 else: 94 else:
92 self._subdirs = subdirs 95 self._subdirs = subdirs
93 self._json_base_url = json_base_url 96 self._json_base_url = json_base_url
94 self._json_filename = json_filename 97 self._json_filename = json_filename
95 self._dry_run = dry_run 98 self._dry_run = dry_run
96 self._is_svn_checkout = ( 99 self._is_svn_checkout = (
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
144 # rather than a list of TESTS, like this: 147 # rather than a list of TESTS, like this:
145 # ['imageblur', 'xfermodes'] 148 # ['imageblur', 'xfermodes']
146 # 149 #
147 # params: 150 # params:
148 # json_url: URL pointing to a JSON actual result summary file 151 # json_url: URL pointing to a JSON actual result summary file
149 # 152 #
150 # TODO(epoger): add a parameter indicating whether "no-comparison" 153 # TODO(epoger): add a parameter indicating whether "no-comparison"
151 # results (those for which we don't have any expectations yet) 154 # results (those for which we don't have any expectations yet)
152 # should be rebaselined. For now, we only return failed expectations. 155 # should be rebaselined. For now, we only return failed expectations.
153 def _GetFilesToRebaseline(self, json_url): 156 def _GetFilesToRebaseline(self, json_url):
157 if self._dry_run:
158 print ''
159 print '#'
154 print ('# Getting files to rebaseline from JSON summary URL %s ...' 160 print ('# Getting files to rebaseline from JSON summary URL %s ...'
155 % json_url) 161 % json_url)
156 json_contents = self._GetContentsOfUrl(json_url) 162 json_contents = self._GetContentsOfUrl(json_url)
157 json_dict = gm_json.LoadFromString(json_contents) 163 json_dict = gm_json.LoadFromString(json_contents)
158 actual_results = json_dict[gm_json.JSONKEY_ACTUALRESULTS] 164 actual_results = json_dict[gm_json.JSONKEY_ACTUALRESULTS]
159 165
160 files_to_rebaseline = [] 166 files_to_rebaseline = []
161 failed_results = actual_results[gm_json.JSONKEY_ACTUALRESULTS_FAILED] 167 failed_results = actual_results[gm_json.JSONKEY_ACTUALRESULTS_FAILED]
162 if failed_results: 168 if failed_results:
163 files_to_rebaseline.extend(failed_results.keys()) 169 files_to_rebaseline.extend(failed_results.keys())
164 170
165 print '# ... found files_to_rebaseline %s' % files_to_rebaseline 171 print '# ... found files_to_rebaseline %s' % files_to_rebaseline
172 if self._dry_run:
173 print '#'
166 return files_to_rebaseline 174 return files_to_rebaseline
167 175
168 # Rebaseline a single file. 176 # Rebaseline a single file.
169 def _RebaselineOneFile(self, expectations_subdir, builder_name, 177 def _RebaselineOneFile(self, expectations_subdir, builder_name,
170 infilename, outfilename): 178 infilename, outfilename):
179 if self._dry_run:
180 print ''
171 print '# ' + infilename 181 print '# ' + infilename
172 url = ('http://skia-autogen.googlecode.com/svn/gm-actual/' + 182 url = ('http://skia-autogen.googlecode.com/svn/gm-actual/' +
173 expectations_subdir + '/' + builder_name + '/' + 183 expectations_subdir + '/' + builder_name + '/' +
174 expectations_subdir + '/' + infilename) 184 expectations_subdir + '/' + infilename)
175 185
176 # Try to download this file, but if that fails, keep going... 186 # Try to download this file.
177 # 187 #
178 # This not treated as a fatal failure because not all 188 # If the download fails, this will raise an exception and halt the
179 # platforms generate all configs (e.g., Android does not 189 # rebaseline process. Since the JSON results summary told us that
180 # generate PDF). 190 # this file needed rebaselining, we ought to be able to download it...
181 # 191 self._DownloadFile(source_url=url, dest_filename=outfilename)
182 # We could tweak the list of configs within this tool to
183 # reflect which combinations the bots actually generate, and
184 # then fail if any of those expected combinations are
185 # missing... but then this tool would become useless every
186 # time someone tweaked the configs on the bots without
187 # updating this script.
188 try:
189 self._DownloadFile(source_url=url, dest_filename=outfilename)
190 except CommandFailedException:
191 print '# Couldn\'t fetch ' + url
192 return
193 192
194 # Add this file to version control (if it isn't already). 193 # Add this file to version control (if it isn't already).
195 if self._is_svn_checkout: 194 if self._is_svn_checkout:
196 cmd = [ 'svn', 'add', '--quiet', outfilename ] 195 cmd = [ 'svn', 'add', '--quiet', outfilename ]
197 self._Call(cmd) 196 self._Call(cmd)
198 cmd = [ 'svn', 'propset', '--quiet', 'svn:mime-type', 'image/png', 197 cmd = [ 'svn', 'propset', '--quiet', 'svn:mime-type', 'image/png',
199 outfilename ]; 198 outfilename ];
200 self._Call(cmd) 199 self._Call(cmd)
201 elif self._is_git_checkout: 200 elif self._is_git_checkout:
202 cmd = [ 'git', 'add', outfilename ] 201 cmd = [ 'git', 'add', outfilename ]
203 self._Call(cmd) 202 self._Call(cmd)
204 203
205 # Rebaseline the given configs for a single test.
206 #
207 # params:
208 # expectations_subdir
209 # builder_name
210 # test: a single test to rebaseline
211 def _RebaselineOneTest(self, expectations_subdir, builder_name, test):
212 if self._configs:
213 configs = self._configs
214 else:
215 if (expectations_subdir == 'base-shuttle-win7-intel-angle'):
216 configs = [ 'angle', 'anglemsaa16' ]
217 else:
218 configs = [ '565', '8888', 'gpu', 'pdf', 'mesa', 'msaa16',
219 'msaa4' ]
220 print '# ' + expectations_subdir + ':'
221 for config in configs:
222 infilename = test + '_' + config + '.png'
223 outfilename = os.path.join(expectations_subdir, infilename);
224 self._RebaselineOneFile(expectations_subdir=expectations_subdir,
225 builder_name=builder_name,
226 infilename=infilename,
227 outfilename=outfilename)
228
229 # Rebaseline all platforms/tests/types we specified in the constructor. 204 # Rebaseline all platforms/tests/types we specified in the constructor.
230 def RebaselineAll(self): 205 def RebaselineAll(self):
231 for subdir in self._subdirs: 206 for subdir in self._subdirs:
232 if not subdir in SUBDIR_MAPPING.keys(): 207 if not subdir in SUBDIR_MAPPING.keys():
233 raise Exception(('unrecognized platform subdir "%s"; ' + 208 raise Exception(('unrecognized platform subdir "%s"; ' +
234 'should be one of %s') % ( 209 'should be one of %s') % (
235 subdir, SUBDIR_MAPPING.keys())) 210 subdir, SUBDIR_MAPPING.keys()))
236 builder_name = SUBDIR_MAPPING[subdir] 211 builder_name = SUBDIR_MAPPING[subdir]
237 if self._tests: 212 json_url = '/'.join([self._json_base_url,
238 for test in self._tests: 213 subdir, builder_name, subdir,
239 self._RebaselineOneTest(expectations_subdir=subdir, 214 self._json_filename])
240 builder_name=builder_name, 215 filenames = self._GetFilesToRebaseline(json_url=json_url)
241 test=test) 216 for filename in filenames:
242 else: # get the raw list of files that need rebaselining from JSON 217 outfilename = os.path.join(subdir, filename);
243 json_url = '/'.join([self._json_base_url, 218 self._RebaselineOneFile(expectations_subdir=subdir,
244 subdir, builder_name, subdir, 219 builder_name=builder_name,
245 self._json_filename]) 220 infilename=filename,
246 filenames = self._GetFilesToRebaseline(json_url=json_url) 221 outfilename=outfilename)
247 for filename in filenames:
248 outfilename = os.path.join(subdir, filename);
249 self._RebaselineOneFile(expectations_subdir=subdir,
250 builder_name=builder_name,
251 infilename=filename,
252 outfilename=outfilename)
253 222
254 # main... 223 # main...
255 224
256 parser = argparse.ArgumentParser() 225 parser = argparse.ArgumentParser()
257 parser.add_argument('--configs', metavar='CONFIG', nargs='+', 226 parser.add_argument('--configs', metavar='CONFIG', nargs='+',
258 help='which configurations to rebaseline, e.g. ' + 227 help='which configurations to rebaseline, e.g. ' +
259 '"--configs 565 8888"; if unspecified, run a default ' + 228 '"--configs 565 8888", as a filter over the configs ' +
260 'set of configs. This should ONLY be specified if ' + 229 'which JSON_FILENAME tells us need rebaselining; ' +
261 '--tests has also been specified.') 230 'if unspecified, then rebaseline all the configs that ' +
262 parser.add_argument('--dry_run', action='store_true', 231 'JSON_FILENAME tells us need rebaselining.')
232 parser.add_argument('--dry-run', action='store_true',
263 help='instead of actually downloading files or adding ' + 233 help='instead of actually downloading files or adding ' +
264 'files to checkout, display a list of operations that ' + 234 'files to checkout, display a list of operations that ' +
265 'we would normally perform') 235 'we would normally perform')
266 parser.add_argument('--json_base_url', 236 parser.add_argument('--json-base-url',
267 help='base URL from which to read JSON_FILENAME ' + 237 help='base URL from which to read JSON_FILENAME ' +
268 'files; defaults to %(default)s', 238 'files; defaults to %(default)s',
269 default='http://skia-autogen.googlecode.com/svn/gm-actual') 239 default='http://skia-autogen.googlecode.com/svn/gm-actual')
270 parser.add_argument('--json_filename', 240 parser.add_argument('--json-filename',
271 help='filename (under JSON_BASE_URL) to read a summary ' + 241 help='filename (under JSON_BASE_URL) to read a summary ' +
272 'of results from; defaults to %(default)s', 242 'of results from; defaults to %(default)s',
273 default='actual-results.json') 243 default='actual-results.json')
274 parser.add_argument('--subdirs', metavar='SUBDIR', nargs='+', 244 parser.add_argument('--subdirs', metavar='SUBDIR', nargs='+',
275 help='which platform subdirectories to rebaseline; ' + 245 help='which platform subdirectories to rebaseline; ' +
276 'if unspecified, rebaseline all subdirs, same as ' + 246 'if unspecified, rebaseline all subdirs, same as ' +
277 '"--subdirs %s"' % ' '.join(sorted(SUBDIR_MAPPING.keys()))) 247 '"--subdirs %s"' % ' '.join(sorted(SUBDIR_MAPPING.keys())))
278 parser.add_argument('--tests', metavar='TEST', nargs='+', 248 parser.add_argument('--tests', metavar='TEST', nargs='+',
279 help='which tests to rebaseline, e.g. ' + 249 help='which tests to rebaseline, e.g. ' +
280 '"--tests aaclip bigmatrix"; if unspecified, then all ' + 250 '"--tests aaclip bigmatrix", as a filter over the tests ' +
281 'failing tests (according to the actual-results.json ' + 251 'which JSON_FILENAME tells us need rebaselining; ' +
282 'file) will be rebaselined.') 252 'if unspecified, then rebaseline all the tests that ' +
253 'JSON_FILENAME tells us need rebaselining.')
283 args = parser.parse_args() 254 args = parser.parse_args()
284 rebaseliner = Rebaseliner(tests=args.tests, configs=args.configs, 255 rebaseliner = Rebaseliner(tests=args.tests, configs=args.configs,
285 subdirs=args.subdirs, dry_run=args.dry_run, 256 subdirs=args.subdirs, dry_run=args.dry_run,
286 json_base_url=args.json_base_url, 257 json_base_url=args.json_base_url,
287 json_filename=args.json_filename) 258 json_filename=args.json_filename)
288 rebaseliner.RebaselineAll() 259 rebaseliner.RebaselineAll()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698