OLD | NEW |
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 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
48 | 48 |
49 class ImageRebaseliner(object): | 49 class ImageRebaseliner(object): |
50 | 50 |
51 # params: | 51 # params: |
52 # expectations_root: root directory of all expectations | 52 # expectations_root: root directory of all expectations |
53 # json_base_url: base URL from which to read json_filename | 53 # json_base_url: base URL from which to read json_filename |
54 # json_filename: filename (under json_base_url) from which to read a | 54 # json_filename: filename (under json_base_url) from which to read a |
55 # summary of results; typically "actual-results.json" | 55 # summary of results; typically "actual-results.json" |
56 # tests: list of tests to rebaseline, or None if we should rebaseline | 56 # tests: list of tests to rebaseline, or None if we should rebaseline |
57 # whatever files the JSON results summary file tells us to | 57 # whatever files the JSON results summary file tells us to |
58 # configs: which configs to run for each test; this should only be | 58 # configs: which configs to run for each test, or None if we should |
59 # specified if the list of tests was also specified (otherwise, | 59 # rebaseline whatever configs the JSON results summary file tells |
60 # the JSON file will give us test names and configs) | 60 # us to |
61 # dry_run: if True, instead of actually downloading files or adding | 61 # dry_run: if True, instead of actually downloading files or adding |
62 # files to checkout, display a list of operations that | 62 # files to checkout, display a list of operations that |
63 # we would normally perform | 63 # we would normally perform |
64 # add_new: if True, add expectations for tests which don't have any yet | 64 # add_new: if True, add expectations for tests which don't have any yet |
65 # missing_json_is_fatal: whether to halt execution if we cannot read a | 65 # missing_json_is_fatal: whether to halt execution if we cannot read a |
66 # JSON actual result summary file | 66 # JSON actual result summary file |
67 def __init__(self, expectations_root, json_base_url, json_filename, | 67 def __init__(self, expectations_root, json_base_url, json_filename, |
68 tests=None, configs=None, dry_run=False, | 68 tests=None, configs=None, dry_run=False, |
69 add_new=False, missing_json_is_fatal=False): | 69 add_new=False, missing_json_is_fatal=False): |
70 if configs and not tests: | |
71 raise ValueError('configs should only be specified if tests ' + | |
72 'were specified also') | |
73 self._expectations_root = expectations_root | 70 self._expectations_root = expectations_root |
74 self._tests = tests | 71 self._tests = tests |
75 self._configs = configs | 72 self._configs = configs |
76 self._json_base_url = json_base_url | 73 self._json_base_url = json_base_url |
77 self._json_filename = json_filename | 74 self._json_filename = json_filename |
78 self._dry_run = dry_run | 75 self._dry_run = dry_run |
79 self._add_new = add_new | 76 self._add_new = add_new |
80 self._missing_json_is_fatal = missing_json_is_fatal | 77 self._missing_json_is_fatal = missing_json_is_fatal |
81 self._googlestorage_gm_actuals_root = ( | 78 self._googlestorage_gm_actuals_root = ( |
82 'http://chromium-skia-gm.commondatastorage.googleapis.com/gm') | 79 'http://chromium-skia-gm.commondatastorage.googleapis.com/gm') |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
118 return False | 115 return False |
119 url = '%s/%s/%s/%s.png' % (self._googlestorage_gm_actuals_root, | 116 url = '%s/%s/%s/%s.png' % (self._googlestorage_gm_actuals_root, |
120 hash_type, test_name, hash_value) | 117 hash_type, test_name, hash_value) |
121 try: | 118 try: |
122 self._DownloadFile(source_url=url, dest_filename=outfilename) | 119 self._DownloadFile(source_url=url, dest_filename=outfilename) |
123 return True | 120 return True |
124 except CommandFailedException: | 121 except CommandFailedException: |
125 print '# Couldn\'t fetch gs_url %s' % url | 122 print '# Couldn\'t fetch gs_url %s' % url |
126 return False | 123 return False |
127 | 124 |
128 # Download a single actual result from skia-autogen, returning True if it | |
129 # succeeded. | |
130 def _DownloadFromAutogen(self, infilename, outfilename, | |
131 expectations_subdir, builder_name): | |
132 url = ('http://skia-autogen.googlecode.com/svn/gm-actual/' + | |
133 expectations_subdir + '/' + builder_name + '/' + | |
134 expectations_subdir + '/' + infilename) | |
135 try: | |
136 self._DownloadFile(source_url=url, dest_filename=outfilename) | |
137 return True | |
138 except CommandFailedException: | |
139 print '# Couldn\'t fetch autogen_url %s' % url | |
140 return False | |
141 | |
142 # Download a single file, raising a CommandFailedException if it fails. | 125 # Download a single file, raising a CommandFailedException if it fails. |
143 def _DownloadFile(self, source_url, dest_filename): | 126 def _DownloadFile(self, source_url, dest_filename): |
144 # Download into a temporary file and then rename it afterwards, | 127 # Download into a temporary file and then rename it afterwards, |
145 # so that we don't corrupt the existing file if it fails midway thru. | 128 # so that we don't corrupt the existing file if it fails midway thru. |
146 temp_filename = os.path.join(os.path.dirname(dest_filename), | 129 temp_filename = os.path.join(os.path.dirname(dest_filename), |
147 '.temp-' + os.path.basename(dest_filename)) | 130 '.temp-' + os.path.basename(dest_filename)) |
148 | 131 |
149 # TODO(epoger): Replace calls to "curl"/"mv" (which will only work on | 132 # TODO(epoger): Replace calls to "curl"/"mv" (which will only work on |
150 # Unix) with a Python HTTP library (which should work cross-platform) | 133 # Unix) with a Python HTTP library (which should work cross-platform) |
151 self._Call([ 'curl', '--fail', '--silent', source_url, | 134 self._Call([ 'curl', '--fail', '--silent', source_url, |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
240 print '#' | 223 print '#' |
241 return files_to_rebaseline | 224 return files_to_rebaseline |
242 | 225 |
243 # Rebaseline a single file. | 226 # Rebaseline a single file. |
244 def _RebaselineOneFile(self, expectations_subdir, builder_name, | 227 def _RebaselineOneFile(self, expectations_subdir, builder_name, |
245 infilename, outfilename, all_results): | 228 infilename, outfilename, all_results): |
246 if self._dry_run: | 229 if self._dry_run: |
247 print '' | 230 print '' |
248 print '# ' + infilename | 231 print '# ' + infilename |
249 | 232 |
250 # First try to download this result image from Google Storage. | 233 # Download this result image from Google Storage; if that fails, |
251 # If that fails, try skia-autogen. | 234 # raise an exception (because if actual-results.json told us that |
252 # If that fails too, just go on to the next file. | 235 # a particular image version is available for download, we should |
253 # | 236 # always be able to get it!) |
254 # This not treated as a fatal failure because not all | |
255 # platforms generate all configs (e.g., Android does not | |
256 # generate PDF). | |
257 # | |
258 # TODO(epoger): Once we are downloading only files that the | |
259 # actual-results.json file told us to, this should become a | |
260 # fatal error. (If the actual-results.json file told us that | |
261 # the test failed with XXX results, we should be able to download | |
262 # those results every time.) | |
263 if not self._DownloadFromGoogleStorage(infilename=infilename, | 237 if not self._DownloadFromGoogleStorage(infilename=infilename, |
264 outfilename=outfilename, | 238 outfilename=outfilename, |
265 all_results=all_results): | 239 all_results=all_results): |
266 if not self._DownloadFromAutogen(infilename=infilename, | 240 raise Exception('# Couldn\'t fetch infilename ' + infilename) |
267 outfilename=outfilename, | |
268 expectations_subdir=expectations_su
bdir, | |
269 builder_name=builder_name): | |
270 print '# Couldn\'t fetch infilename ' + infilename | |
271 return | |
272 | 241 |
273 # Add this file to version control (if appropriate). | 242 # Add this file to version control (if appropriate). |
274 if self._add_new: | 243 if self._add_new: |
275 if self._is_svn_checkout: | 244 if self._is_svn_checkout: |
276 cmd = [ 'svn', 'add', '--quiet', outfilename ] | 245 cmd = [ 'svn', 'add', '--quiet', outfilename ] |
277 self._Call(cmd) | 246 self._Call(cmd) |
278 cmd = [ 'svn', 'propset', '--quiet', 'svn:mime-type', | 247 cmd = [ 'svn', 'propset', '--quiet', 'svn:mime-type', |
279 'image/png', outfilename ]; | 248 'image/png', outfilename ]; |
280 self._Call(cmd) | 249 self._Call(cmd) |
281 elif self._is_git_checkout: | 250 elif self._is_git_checkout: |
282 cmd = [ 'git', 'add', outfilename ] | 251 cmd = [ 'git', 'add', outfilename ] |
283 self._Call(cmd) | 252 self._Call(cmd) |
284 | 253 |
285 # Rebaseline the given configs for a single test. | |
286 # | |
287 # params: | |
288 # expectations_subdir | |
289 # builder_name | |
290 # test: a single test to rebaseline | |
291 # all_results: a dictionary of all actual results | |
292 def _RebaselineOneTest(self, expectations_subdir, builder_name, test, | |
293 all_results): | |
294 if self._configs: | |
295 configs = self._configs | |
296 else: | |
297 if (expectations_subdir == 'base-shuttle-win7-intel-angle'): | |
298 configs = [ 'angle', 'anglemsaa16' ] | |
299 else: | |
300 configs = [ '565', '8888', 'gpu', 'pdf', 'mesa', 'msaa16', | |
301 'msaa4' ] | |
302 if self._dry_run: | |
303 print '' | |
304 print '# ' + expectations_subdir + ':' | |
305 for config in configs: | |
306 infilename = test + '_' + config + '.png' | |
307 outfilename = os.path.join(self._expectations_root, | |
308 expectations_subdir, infilename); | |
309 self._RebaselineOneFile(expectations_subdir=expectations_subdir, | |
310 builder_name=builder_name, | |
311 infilename=infilename, | |
312 outfilename=outfilename, | |
313 all_results=all_results) | |
314 | |
315 # Rebaseline all tests/types we specified in the constructor, | 254 # Rebaseline all tests/types we specified in the constructor, |
316 # within this gm-expectations subdir. | 255 # within this gm-expectations subdir. |
317 # | 256 # |
318 # params: | 257 # params: |
319 # subdir : e.g. 'base-shuttle-win7-intel-float' | 258 # subdir : e.g. 'base-shuttle-win7-intel-float' |
320 # builder : e.g. 'Test-Win7-ShuttleA-HD2000-x86-Release' | 259 # builder : e.g. 'Test-Win7-ShuttleA-HD2000-x86-Release' |
321 def RebaselineSubdir(self, subdir, builder): | 260 def RebaselineSubdir(self, subdir, builder): |
322 json_url = '/'.join([self._json_base_url, | 261 json_url = '/'.join([self._json_base_url, |
323 subdir, builder, subdir, | 262 subdir, builder, subdir, |
324 self._json_filename]) | 263 self._json_filename]) |
325 all_results = self._GetActualResults(json_url=json_url) | 264 all_results = self._GetActualResults(json_url=json_url) |
| 265 filenames = self._GetFilesToRebaseline(json_url=json_url, |
| 266 add_new=self._add_new) |
| 267 skipped_files = [] |
| 268 for filename in filenames: |
| 269 (test, config) = self._testname_pattern.match(filename).groups() |
| 270 if self._tests: |
| 271 if test not in self._tests: |
| 272 skipped_files.append(filename) |
| 273 continue |
| 274 if self._configs: |
| 275 if config not in self._configs: |
| 276 skipped_files.append(filename) |
| 277 continue |
| 278 outfilename = os.path.join(subdir, filename); |
| 279 self._RebaselineOneFile(expectations_subdir=subdir, |
| 280 builder_name=builder, |
| 281 infilename=filename, |
| 282 outfilename=outfilename, |
| 283 all_results=all_results) |
326 | 284 |
327 if self._tests: | 285 if skipped_files: |
328 for test in self._tests: | 286 print ('Skipped these files due to test/config filters: %s' % |
329 self._RebaselineOneTest(expectations_subdir=subdir, | 287 skipped_files) |
330 builder_name=builder, | |
331 test=test, all_results=all_results) | |
332 else: # get the raw list of files that need rebaselining from JSON | |
333 filenames = self._GetFilesToRebaseline(json_url=json_url, | |
334 add_new=self._add_new) | |
335 for filename in filenames: | |
336 outfilename = os.path.join(subdir, filename); | |
337 self._RebaselineOneFile(expectations_subdir=subdir, | |
338 builder_name=builder, | |
339 infilename=filename, | |
340 outfilename=outfilename, | |
341 all_results=all_results) | |
OLD | NEW |