OLD | NEW |
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 re | 19 import re |
20 import subprocess | 20 import subprocess |
21 import sys | 21 import sys |
22 import urllib2 | 22 import urllib2 |
23 | 23 |
| 24 # Imports from local directory |
| 25 import rebaseline_imagefiles |
| 26 |
24 # Imports from within Skia | 27 # Imports from within Skia |
25 # | 28 # |
26 # We need to add the 'gm' directory, so that we can import gm_json.py within | 29 # We need to add the 'gm' directory, so that we can import gm_json.py within |
27 # that directory. That script allows us to parse the actual-results.json file | 30 # that directory. That script allows us to parse the actual-results.json file |
28 # written out by the GM tool. | 31 # written out by the GM tool. |
29 # Make sure that the 'gm' dir is in the PYTHONPATH, but add it at the *end* | 32 # Make sure that the 'gm' dir is in the PYTHONPATH, but add it at the *end* |
30 # so any dirs that are already in the PYTHONPATH will be preferred. | 33 # so any dirs that are already in the PYTHONPATH will be preferred. |
31 # | 34 # |
32 # This assumes that the 'gm' directory has been checked out as a sibling of | 35 # This assumes that the 'gm' directory has been checked out as a sibling of |
33 # the 'tools' directory containing this script, which will be the case if | 36 # the 'tools' directory containing this script, which will be the case if |
34 # 'trunk' was checked out as a single unit. | 37 # 'trunk' was checked out as a single unit. |
35 GM_DIRECTORY = os.path.realpath( | 38 GM_DIRECTORY = os.path.realpath( |
36 os.path.join(os.path.dirname(os.path.dirname(__file__)), 'gm')) | 39 os.path.join(os.path.dirname(os.path.dirname(__file__)), 'gm')) |
37 if GM_DIRECTORY not in sys.path: | 40 if GM_DIRECTORY not in sys.path: |
38 sys.path.append(GM_DIRECTORY) | 41 sys.path.append(GM_DIRECTORY) |
39 import gm_json | 42 import gm_json |
40 | 43 |
| 44 JSON_EXPECTATIONS_FILENAME='expected-results.json' |
41 | 45 |
42 # Mapping of gm-expectations subdir (under | 46 # Mapping of gm-expectations subdir (under |
43 # https://skia.googlecode.com/svn/gm-expected/ ) | 47 # https://skia.googlecode.com/svn/gm-expected/ ) |
44 # to builder name (see list at http://108.170.217.252:10117/builders ) | 48 # to builder name (see list at http://108.170.217.252:10117/builders ) |
45 SUBDIR_MAPPING = { | 49 SUBDIR_MAPPING = { |
46 'base-shuttle-win7-intel-float': | 50 'base-shuttle-win7-intel-float': |
47 'Test-Win7-ShuttleA-HD2000-x86-Release', | 51 'Test-Win7-ShuttleA-HD2000-x86-Release', |
48 'base-shuttle-win7-intel-angle': | 52 'base-shuttle-win7-intel-angle': |
49 'Test-Win7-ShuttleA-HD2000-x86-Release-ANGLE', | 53 'Test-Win7-ShuttleA-HD2000-x86-Release-ANGLE', |
50 'base-shuttle-win7-intel-directwrite': | 54 'base-shuttle-win7-intel-directwrite': |
(...skipping 13 matching lines...) Expand all Loading... |
64 'base-android-xoom': | 68 'base-android-xoom': |
65 'Test-Android-Xoom-Tegra2-Arm7-Release', | 69 'Test-Android-Xoom-Tegra2-Arm7-Release', |
66 'base-android-nexus-10': | 70 'base-android-nexus-10': |
67 'Test-Android-Nexus10-MaliT604-Arm7-Release', | 71 'Test-Android-Nexus10-MaliT604-Arm7-Release', |
68 } | 72 } |
69 | 73 |
70 | 74 |
71 class CommandFailedException(Exception): | 75 class CommandFailedException(Exception): |
72 pass | 76 pass |
73 | 77 |
74 class Rebaseliner(object): | 78 # Object that rebaselines a JSON expectations file (not individual image files). |
| 79 # |
| 80 # TODO(epoger): Most of this is just the code from the old ImageRebaseliner... |
| 81 # some of it will need to be updated in order to properly rebaseline JSON files. |
| 82 # There is a lot of code duplicated between here and ImageRebaseliner, but |
| 83 # that's fine because we will delete ImageRebaseliner soon. |
| 84 class JsonRebaseliner(object): |
75 | 85 |
76 # params: | 86 # params: |
| 87 # expectations_root: root directory of all expectations |
77 # json_base_url: base URL from which to read json_filename | 88 # json_base_url: base URL from which to read json_filename |
78 # json_filename: filename (under json_base_url) from which to read a | 89 # json_filename: filename (under json_base_url) from which to read a |
79 # summary of results; typically "actual-results.json" | 90 # summary of results; typically "actual-results.json" |
80 # subdirs: which platform subdirectories to rebaseline; if not specified, | |
81 # rebaseline all platform subdirectories | |
82 # tests: list of tests to rebaseline, or None if we should rebaseline | 91 # tests: list of tests to rebaseline, or None if we should rebaseline |
83 # whatever files the JSON results summary file tells us to | 92 # whatever files the JSON results summary file tells us to |
84 # configs: which configs to run for each test; this should only be | 93 # configs: which configs to run for each test; this should only be |
85 # specified if the list of tests was also specified (otherwise, | 94 # specified if the list of tests was also specified (otherwise, |
86 # the JSON file will give us test names and configs) | 95 # the JSON file will give us test names and configs) |
87 # dry_run: if True, instead of actually downloading files or adding | 96 # dry_run: if True, instead of actually downloading files or adding |
88 # files to checkout, display a list of operations that | 97 # files to checkout, display a list of operations that |
89 # we would normally perform | 98 # we would normally perform |
90 # add_new: if True, add expectations for tests which don't have any yet | 99 # add_new: if True, add expectations for tests which don't have any yet |
91 def __init__(self, json_base_url, json_filename, | 100 # missing_json_is_fatal: whether to halt execution if we cannot read a |
92 subdirs=None, tests=None, configs=None, dry_run=False, | 101 # JSON actual result summary file |
93 add_new=False): | 102 def __init__(self, expectations_root, json_base_url, json_filename, |
| 103 tests=None, configs=None, dry_run=False, |
| 104 add_new=False, missing_json_is_fatal=False): |
| 105 raise ValueError('JsonRebaseliner not yet implemented') # TODO(epoger) |
94 if configs and not tests: | 106 if configs and not tests: |
95 raise ValueError('configs should only be specified if tests ' + | 107 raise ValueError('configs should only be specified if tests ' + |
96 'were specified also') | 108 'were specified also') |
| 109 self._expectations_root = expectations_root |
97 self._tests = tests | 110 self._tests = tests |
98 self._configs = configs | 111 self._configs = configs |
99 if not subdirs: | |
100 self._subdirs = sorted(SUBDIR_MAPPING.keys()) | |
101 self._missing_json_is_fatal = False | |
102 else: | |
103 self._subdirs = subdirs | |
104 self._missing_json_is_fatal = True | |
105 self._json_base_url = json_base_url | 112 self._json_base_url = json_base_url |
106 self._json_filename = json_filename | 113 self._json_filename = json_filename |
107 self._dry_run = dry_run | 114 self._dry_run = dry_run |
108 self._add_new = add_new | 115 self._add_new = add_new |
| 116 self._missing_json_is_fatal = missing_json_is_fatal |
109 self._googlestorage_gm_actuals_root = ( | 117 self._googlestorage_gm_actuals_root = ( |
110 'http://chromium-skia-gm.commondatastorage.googleapis.com/gm') | 118 'http://chromium-skia-gm.commondatastorage.googleapis.com/gm') |
111 self._testname_pattern = re.compile('(\S+)_(\S+).png') | 119 self._testname_pattern = re.compile('(\S+)_(\S+).png') |
112 self._is_svn_checkout = ( | 120 self._is_svn_checkout = ( |
113 os.path.exists('.svn') or | 121 os.path.exists('.svn') or |
114 os.path.exists(os.path.join(os.pardir, '.svn'))) | 122 os.path.exists(os.path.join(os.pardir, '.svn'))) |
115 self._is_git_checkout = ( | 123 self._is_git_checkout = ( |
116 os.path.exists('.git') or | 124 os.path.exists('.git') or |
117 os.path.exists(os.path.join(os.pardir, '.git'))) | 125 os.path.exists(os.path.join(os.pardir, '.git'))) |
118 | 126 |
(...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
332 print '# ' + expectations_subdir + ':' | 340 print '# ' + expectations_subdir + ':' |
333 for config in configs: | 341 for config in configs: |
334 infilename = test + '_' + config + '.png' | 342 infilename = test + '_' + config + '.png' |
335 outfilename = os.path.join(expectations_subdir, infilename); | 343 outfilename = os.path.join(expectations_subdir, infilename); |
336 self._RebaselineOneFile(expectations_subdir=expectations_subdir, | 344 self._RebaselineOneFile(expectations_subdir=expectations_subdir, |
337 builder_name=builder_name, | 345 builder_name=builder_name, |
338 infilename=infilename, | 346 infilename=infilename, |
339 outfilename=outfilename, | 347 outfilename=outfilename, |
340 all_results=all_results) | 348 all_results=all_results) |
341 | 349 |
342 # Rebaseline all platforms/tests/types we specified in the constructor. | 350 # Rebaseline all tests/types we specified in the constructor, |
343 def RebaselineAll(self): | 351 # within this gm-expectations subdir. |
344 for subdir in self._subdirs: | 352 # |
345 if not subdir in SUBDIR_MAPPING.keys(): | 353 # params: |
346 raise Exception(('unrecognized platform subdir "%s"; ' + | 354 # subdir : e.g. 'base-shuttle-win7-intel-float' |
347 'should be one of %s') % ( | 355 # builder : e.g. 'Test-Win7-ShuttleA-HD2000-x86-Release' |
348 subdir, SUBDIR_MAPPING.keys())) | 356 def RebaselineSubdir(self, subdir, builder): |
349 builder_name = SUBDIR_MAPPING[subdir] | 357 json_url = '/'.join([self._json_base_url, |
350 json_url = '/'.join([self._json_base_url, | 358 subdir, builder, subdir, |
351 subdir, builder_name, subdir, | 359 self._json_filename]) |
352 self._json_filename]) | 360 all_results = self._GetActualResults(json_url=json_url) |
353 all_results = self._GetActualResults(json_url=json_url) | |
354 | 361 |
355 if self._tests: | 362 if self._tests: |
356 for test in self._tests: | 363 for test in self._tests: |
357 self._RebaselineOneTest(expectations_subdir=subdir, | 364 self._RebaselineOneTest(expectations_subdir=subdir, |
358 builder_name=builder_name, | 365 builder_name=builder, |
359 test=test, all_results=all_results) | 366 test=test, all_results=all_results) |
360 else: # get the raw list of files that need rebaselining from JSON | 367 else: # get the raw list of files that need rebaselining from JSON |
361 filenames = self._GetFilesToRebaseline(json_url=json_url, | 368 filenames = self._GetFilesToRebaseline(json_url=json_url, |
362 add_new=self._add_new) | 369 add_new=self._add_new) |
363 for filename in filenames: | 370 for filename in filenames: |
364 outfilename = os.path.join(subdir, filename); | 371 outfilename = os.path.join(subdir, filename); |
365 self._RebaselineOneFile(expectations_subdir=subdir, | 372 self._RebaselineOneFile(expectations_subdir=subdir, |
366 builder_name=builder_name, | 373 builder_name=builder, |
367 infilename=filename, | 374 infilename=filename, |
368 outfilename=outfilename, | 375 outfilename=outfilename, |
369 all_results=all_results) | 376 all_results=all_results) |
370 | 377 |
371 # main... | 378 # main... |
372 | 379 |
373 parser = argparse.ArgumentParser() | 380 parser = argparse.ArgumentParser() |
374 parser.add_argument('--add-new', action='store_true', | 381 parser.add_argument('--add-new', action='store_true', |
375 help='in addition to the standard behavior of ' + | 382 help='in addition to the standard behavior of ' + |
376 'updating expectations for failing tests, add ' + | 383 'updating expectations for failing tests, add ' + |
377 'expectations for tests which don\'t have expectations ' + | 384 'expectations for tests which don\'t have expectations ' + |
378 'yet.') | 385 'yet.') |
379 parser.add_argument('--configs', metavar='CONFIG', nargs='+', | 386 parser.add_argument('--configs', metavar='CONFIG', nargs='+', |
380 help='which configurations to rebaseline, e.g. ' + | 387 help='which configurations to rebaseline, e.g. ' + |
381 '"--configs 565 8888"; if unspecified, run a default ' + | 388 '"--configs 565 8888"; if unspecified, run a default ' + |
382 'set of configs. This should ONLY be specified if ' + | 389 'set of configs. This should ONLY be specified if ' + |
383 '--tests has also been specified.') | 390 '--tests has also been specified.') |
384 parser.add_argument('--dry-run', action='store_true', | 391 parser.add_argument('--dry-run', action='store_true', |
385 help='instead of actually downloading files or adding ' + | 392 help='instead of actually downloading files or adding ' + |
386 'files to checkout, display a list of operations that ' + | 393 'files to checkout, display a list of operations that ' + |
387 'we would normally perform') | 394 'we would normally perform') |
| 395 parser.add_argument('--expectations-root', |
| 396 help='root of expectations directory to update-- should ' + |
| 397 'contain one or more base-* subdirectories. Defaults to ' + |
| 398 '%(default)s', |
| 399 default='.') |
388 parser.add_argument('--json-base-url', | 400 parser.add_argument('--json-base-url', |
389 help='base URL from which to read JSON_FILENAME ' + | 401 help='base URL from which to read JSON_FILENAME ' + |
390 'files; defaults to %(default)s', | 402 'files; defaults to %(default)s', |
391 default='http://skia-autogen.googlecode.com/svn/gm-actual') | 403 default='http://skia-autogen.googlecode.com/svn/gm-actual') |
392 parser.add_argument('--json-filename', | 404 parser.add_argument('--json-filename', |
393 help='filename (under JSON_BASE_URL) to read a summary ' + | 405 help='filename (under JSON_BASE_URL) to read a summary ' + |
394 'of results from; defaults to %(default)s', | 406 'of results from; defaults to %(default)s', |
395 default='actual-results.json') | 407 default='actual-results.json') |
396 parser.add_argument('--subdirs', metavar='SUBDIR', nargs='+', | 408 parser.add_argument('--subdirs', metavar='SUBDIR', nargs='+', |
397 help='which platform subdirectories to rebaseline; ' + | 409 help='which platform subdirectories to rebaseline; ' + |
398 'if unspecified, rebaseline all subdirs, same as ' + | 410 'if unspecified, rebaseline all subdirs, same as ' + |
399 '"--subdirs %s"' % ' '.join(sorted(SUBDIR_MAPPING.keys()))) | 411 '"--subdirs %s"' % ' '.join(sorted(SUBDIR_MAPPING.keys()))) |
400 parser.add_argument('--tests', metavar='TEST', nargs='+', | 412 parser.add_argument('--tests', metavar='TEST', nargs='+', |
401 help='which tests to rebaseline, e.g. ' + | 413 help='which tests to rebaseline, e.g. ' + |
402 '"--tests aaclip bigmatrix"; if unspecified, then all ' + | 414 '"--tests aaclip bigmatrix"; if unspecified, then all ' + |
403 'failing tests (according to the actual-results.json ' + | 415 'failing tests (according to the actual-results.json ' + |
404 'file) will be rebaselined.') | 416 'file) will be rebaselined.') |
405 args = parser.parse_args() | 417 args = parser.parse_args() |
406 rebaseliner = Rebaseliner(tests=args.tests, configs=args.configs, | 418 if args.subdirs: |
407 subdirs=args.subdirs, dry_run=args.dry_run, | 419 subdirs = args.subdirs |
408 json_base_url=args.json_base_url, | 420 missing_json_is_fatal = True |
409 json_filename=args.json_filename, | 421 else: |
410 add_new=args.add_new) | 422 subdirs = sorted(SUBDIR_MAPPING.keys()) |
411 rebaseliner.RebaselineAll() | 423 missing_json_is_fatal = False |
| 424 for subdir in subdirs: |
| 425 if not subdir in SUBDIR_MAPPING.keys(): |
| 426 raise Exception(('unrecognized platform subdir "%s"; ' + |
| 427 'should be one of %s') % ( |
| 428 subdir, SUBDIR_MAPPING.keys())) |
| 429 builder = SUBDIR_MAPPING[subdir] |
| 430 |
| 431 # We instantiate different Rebaseliner objects depending |
| 432 # on whether we are rebaselining an expected-results.json file, or |
| 433 # individual image files. Different gm-expected subdirectories may move |
| 434 # from individual image files to JSON-format expectations at different |
| 435 # times, so we need to make this determination per subdirectory. |
| 436 # |
| 437 # See https://goto.google.com/ChecksumTransitionDetail |
| 438 expectations_json_file = os.path.join(args.expectations_root, subdir, |
| 439 JSON_EXPECTATIONS_FILENAME) |
| 440 if os.path.isfile(expectations_json_file): |
| 441 sys.stderr.write('ERROR: JsonRebaseliner is not implemented yet.\n') |
| 442 sys.exit(1) |
| 443 rebaseliner = JsonRebaseliner( |
| 444 expectations_root=args.expectations_root, |
| 445 tests=args.tests, configs=args.configs, |
| 446 dry_run=args.dry_run, |
| 447 json_base_url=args.json_base_url, |
| 448 json_filename=args.json_filename, |
| 449 add_new=args.add_new, |
| 450 missing_json_is_fatal=missing_json_is_fatal) |
| 451 else: |
| 452 rebaseliner = rebaseline_imagefiles.ImageRebaseliner( |
| 453 expectations_root=args.expectations_root, |
| 454 tests=args.tests, configs=args.configs, |
| 455 dry_run=args.dry_run, |
| 456 json_base_url=args.json_base_url, |
| 457 json_filename=args.json_filename, |
| 458 add_new=args.add_new, |
| 459 missing_json_is_fatal=missing_json_is_fatal) |
| 460 rebaseliner.RebaselineSubdir(subdir=subdir, builder=builder) |
OLD | NEW |