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

Side by Side Diff: third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline.py

Issue 2341643005: In rebaseline.py, update SCM and expectations all at once after all commands. (Closed)
Patch Set: Add unit test for ChangeSet Created 4 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
OLDNEW
1 # Copyright (c) 2010 Google Inc. All rights reserved. 1 # Copyright (c) 2010 Google Inc. All rights reserved.
2 # 2 #
3 # Redistribution and use in source and binary forms, with or without 3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions are 4 # modification, are permitted provided that the following conditions are
5 # met: 5 # met:
6 # 6 #
7 # * Redistributions of source code must retain the above copyright 7 # * Redistributions of source code must retain the above copyright
8 # notice, this list of conditions and the following disclaimer. 8 # notice, this list of conditions and the following disclaimer.
9 # * Redistributions in binary form must reproduce the above 9 # * Redistributions in binary form must reproduce the above
10 # copyright notice, this list of conditions and the following disclaimer 10 # copyright notice, this list of conditions and the following disclaimer
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
82 and the TestExpectations all at once at the end. 82 and the TestExpectations all at once at the end.
83 83
84 Each subprocess can communicate some set of changes by outputting JSON lines , 84 Each subprocess can communicate some set of changes by outputting JSON lines ,
85 and another process can aggregate the changes. 85 and another process can aggregate the changes.
86 86
87 TODO(qyearsley): Refactor this so that: 87 TODO(qyearsley): Refactor this so that:
88 - lines to remove are only tracked in one format. 88 - lines to remove are only tracked in one format.
89 - files to add and delete are always disjoint sets. 89 - files to add and delete are always disjoint sets.
90 """ 90 """
91 def __init__(self, files_to_add=None, files_to_delete=None, lines_to_remove= None): 91 def __init__(self, files_to_add=None, files_to_delete=None, lines_to_remove= None):
92 self.files_to_add = files_to_add or [] 92 self._files_to_add = set(files_to_add or [])
93 self.files_to_delete = files_to_delete or [] 93 self._files_to_delete = set(files_to_delete or [])
94 self.lines_to_remove = lines_to_remove or {} 94 lines_to_remove = lines_to_remove or {}
95 self._lines_to_remove = {t: set(builders) for t, builders in lines_to_re move.iteritems()}
95 96
96 def add_file(self, path): 97 def add_file(self, path):
97 self.files_to_add.append(path) 98 self._files_to_add.add(path)
98 99
99 def delete_file(self, path): 100 def delete_file(self, path):
100 self.files_to_delete.append(path) 101 self._files_to_delete.add(path)
101 102
102 def remove_line(self, test, builder): 103 def remove_line(self, test, builder):
103 if test not in self.lines_to_remove: 104 if test not in self._lines_to_remove:
104 self.lines_to_remove[test] = [] 105 self._lines_to_remove[test] = set()
105 self.lines_to_remove[test].append(builder) 106 self._lines_to_remove[test].add(builder)
107
108 @property
109 def files_to_add(self):
110 return sorted(self._files_to_add - self._files_to_delete)
111
112 @property
113 def files_to_delete(self):
114 return sorted(self._files_to_delete - self._files_to_add)
115
116 @property
117 def lines_to_remove(self):
118 return {t: sorted(set(builders)) for t, builders in sorted(self._lines_t o_remove.iteritems())}
106 119
107 def to_dict(self): 120 def to_dict(self):
108 remove_lines = [] 121 remove_lines = []
109 for test in self.lines_to_remove: 122 for test in sorted(self.lines_to_remove):
110 for builder in self.lines_to_remove[test]: 123 for builder in sorted(set(self.lines_to_remove[test])):
111 remove_lines.append({'test': test, 'builder': builder}) 124 remove_lines.append({'test': test, 'builder': builder})
112 return { 125 return {
113 'add': list(self.files_to_add), 126 'add': self.files_to_add,
114 'delete': list(self.files_to_delete), 127 'delete': self.files_to_delete,
115 'remove-lines': remove_lines, 128 'remove-lines': remove_lines,
116 } 129 }
117 130
118 @staticmethod 131 @staticmethod
119 def from_dict(change_dict): 132 def from_dict(change_dict):
120 files_to_add = set() 133 files_to_add = set()
121 files_to_delete = set() 134 files_to_delete = set()
122 lines_to_remove = {} 135 lines_to_remove = {}
123 if 'add' in change_dict: 136 if 'add' in change_dict:
124 files_to_add.update(change_dict['add']) 137 files_to_add.update(change_dict['add'])
125 if 'delete' in change_dict: 138 if 'delete' in change_dict:
126 files_to_delete.update(change_dict['delete']) 139 files_to_delete.update(change_dict['delete'])
127 if 'remove-lines' in change_dict: 140 if 'remove-lines' in change_dict:
128 for line_to_remove in change_dict['remove-lines']: 141 for line_to_remove in change_dict['remove-lines']:
129 test = line_to_remove['test'] 142 test = line_to_remove['test']
130 builder = line_to_remove['builder'] 143 builder = line_to_remove['builder']
131 if test not in lines_to_remove: 144 if test not in lines_to_remove:
132 lines_to_remove[test] = [] 145 lines_to_remove[test] = []
133 lines_to_remove[test].append(builder) 146 lines_to_remove[test].append(builder)
134 return ChangeSet( 147 return ChangeSet(
135 files_to_add=list(files_to_add), 148 files_to_add=files_to_add,
136 files_to_delete=list(files_to_delete), 149 files_to_delete=files_to_delete,
137 lines_to_remove=lines_to_remove) 150 lines_to_remove=lines_to_remove)
138 151
139 def update(self, other): 152 def update(self, other):
140 assert isinstance(other, ChangeSet) 153 assert isinstance(other, ChangeSet)
141 assert type(other.lines_to_remove) is dict 154 self._files_to_add.update(other.files_to_add)
142 self.files_to_add.extend(other.files_to_add) 155 self._files_to_delete.update(other.files_to_delete)
143 self.files_to_delete.extend(other.files_to_delete)
144 for test in other.lines_to_remove: 156 for test in other.lines_to_remove:
145 if test not in self.lines_to_remove: 157 if test not in self._lines_to_remove:
146 self.lines_to_remove[test] = [] 158 self._lines_to_remove[test] = set()
147 self.lines_to_remove[test].extend(other.lines_to_remove[test]) 159 self._lines_to_remove[test].update(other.lines_to_remove[test])
148 160
149 161
150 class BaseInternalRebaselineCommand(AbstractRebaseliningCommand): 162 class BaseInternalRebaselineCommand(AbstractRebaseliningCommand):
151 """Base class for rebaseline-related commands that are intended to be used b y other commands.""" 163 """Base class for rebaseline-related commands that are intended to be used b y other commands."""
152 # Not overriding execute() - pylint: disable=abstract-method 164 # Not overriding execute() - pylint: disable=abstract-method
153 165
154 def __init__(self): 166 def __init__(self):
155 super(BaseInternalRebaselineCommand, self).__init__(options=[ 167 super(BaseInternalRebaselineCommand, self).__init__(options=[
156 self.results_directory_option, 168 self.results_directory_option,
157 self.suffixes_option, 169 self.suffixes_option,
(...skipping 329 matching lines...) Expand 10 before | Expand all | Expand 10 after
487 def _port_skips_test(port, test, generic_expectations, full_expectations): 499 def _port_skips_test(port, test, generic_expectations, full_expectations):
488 fs = port.host.filesystem 500 fs = port.host.filesystem
489 if port.default_smoke_test_only(): 501 if port.default_smoke_test_only():
490 smoke_test_filename = fs.join(port.layout_tests_dir(), 'SmokeTests') 502 smoke_test_filename = fs.join(port.layout_tests_dir(), 'SmokeTests')
491 if fs.exists(smoke_test_filename) and test not in fs.read_text_file( smoke_test_filename): 503 if fs.exists(smoke_test_filename) and test not in fs.read_text_file( smoke_test_filename):
492 return True 504 return True
493 505
494 return (SKIP in full_expectations.get_expectations(test) and 506 return (SKIP in full_expectations.get_expectations(test) and
495 SKIP not in generic_expectations.get_expectations(test)) 507 SKIP not in generic_expectations.get_expectations(test))
496 508
497 def _run_in_parallel(self, commands, update_scm=True): 509 def _run_in_parallel(self, commands):
498 if not commands: 510 if not commands:
499 return {} 511 return ChangeSet()
500 512
501 # TODO(qyearsley): Refactor this block. 513 # TODO(qyearsley): Refactor this block.
502 command_results = self._tool.executive.run_in_parallel(commands) 514 command_results = self._tool.executive.run_in_parallel(commands)
503 log_output = '\n'.join(result[2] for result in command_results).replace( '\n\n', '\n') 515 log_output = '\n'.join(result[2] for result in command_results).replace( '\n\n', '\n')
504 for line in log_output.split('\n'): 516 for line in log_output.split('\n'):
505 if line: 517 if line:
506 _log.error(line) 518 _log.error(line)
507 519
508 change_set = self._serial_commands(command_results) 520 return self._serial_commands(command_results)
509 521
510 # TODO(qyearsley): Instead of updating the SCM state here, aggregate cha nges 522 def _rebaseline(self, options, test_prefix_list):
511 # and update once in _rebaseline. See http://crbug.com/639410.
512 if update_scm:
513 if change_set.files_to_delete:
514 self._tool.scm().delete_list(change_set.files_to_delete)
515 if change_set.files_to_add:
516 self._tool.scm().add_list(change_set.files_to_add)
517
518 return change_set.lines_to_remove
519
520 def _rebaseline(self, options, test_prefix_list, update_scm=True):
521 """Downloads new baselines in parallel, then updates expectations files 523 """Downloads new baselines in parallel, then updates expectations files
522 and optimizes baselines. 524 and optimizes baselines.
523 525
524 Args: 526 Args:
525 options: An object with the options passed to the current command. 527 options: An object with the options passed to the current command.
526 test_prefix_list: A map of test names to Build objects to file suffi xes 528 test_prefix_list: A map of test names to Build objects to file suffi xes
527 for new baselines. For example: 529 for new baselines. For example:
528 { 530 {
529 "some/test.html": {Build("builder-1", 412): ["txt"], Build(" builder-2", 100): ["txt"]}, 531 "some/test.html": {Build("builder-1", 412): ["txt"], Build(" builder-2", 100): ["txt"]},
530 "some/other.html": {Build("builder-1", 412): ["txt"]} 532 "some/other.html": {Build("builder-1", 412): ["txt"]}
531 } 533 }
532 This would mean that new text baselines should be downloaded for 534 This would mean that new text baselines should be downloaded for
533 "some/test.html" on both builder-1 (build 412) and builder-2 535 "some/test.html" on both builder-1 (build 412) and builder-2
534 (build 100), and new text baselines should be downloaded for 536 (build 100), and new text baselines should be downloaded for
535 "some/other.html" but only from builder-1. 537 "some/other.html" but only from builder-1.
536 TODO(qyearsley): Replace test_prefix_list everywhere with some 538 TODO(qyearsley): Replace test_prefix_list everywhere with some
537 sort of class that contains the same data. 539 sort of class that contains the same data.
538 update_scm: If True, commands like `git add` and `git rm` will be ru n.
539 """ 540 """
540 for test, builds_to_check in sorted(test_prefix_list.items()): 541 for test, builds_to_check in sorted(test_prefix_list.items()):
541 _log.info("Rebaselining %s", test) 542 _log.info("Rebaselining %s", test)
542 for build, suffixes in sorted(builds_to_check.items()): 543 for build, suffixes in sorted(builds_to_check.items()):
543 _log.debug(" %s: %s", build, ",".join(suffixes)) 544 _log.debug(" %s: %s", build, ",".join(suffixes))
544 545
545 copy_baseline_commands, rebaseline_commands, extra_lines_to_remove = sel f._rebaseline_commands( 546 copy_baseline_commands, rebaseline_commands, extra_lines_to_remove = sel f._rebaseline_commands(
546 test_prefix_list, options) 547 test_prefix_list, options)
547 lines_to_remove = {}
548 548
549 self._run_in_parallel(copy_baseline_commands, update_scm=update_scm) 549 change_set = ChangeSet(lines_to_remove=extra_lines_to_remove)
550 lines_to_remove = self._run_in_parallel(rebaseline_commands, update_scm= update_scm)
551 550
552 for test in extra_lines_to_remove: 551 change_set.update(self._run_in_parallel(copy_baseline_commands))
553 if test in lines_to_remove: 552 change_set.update(self._run_in_parallel(rebaseline_commands))
554 lines_to_remove[test] = lines_to_remove[test] + extra_lines_to_r emove[test]
555 else:
556 lines_to_remove[test] = extra_lines_to_remove[test]
557 553
558 if lines_to_remove: 554 if change_set.lines_to_remove:
559 self._update_expectations_files(lines_to_remove) 555 self._update_expectations_files(change_set.lines_to_remove)
560 556
561 if options.optimize: 557 if options.optimize:
562 # TODO(wkorman): Consider changing temporary branch to base off of H EAD rather than 558 # TODO(wkorman): Consider changing temporary branch to base off of H EAD rather than
563 # origin/master to ensure we run baseline optimization processes wit h the same code as 559 # origin/master to ensure we run baseline optimization processes wit h the same code as
564 # auto-rebaseline itself. 560 # auto-rebaseline itself.
565 self._run_in_parallel(self._optimize_baselines(test_prefix_list, opt ions.verbose), update_scm=update_scm) 561 change_set.update(self._run_in_parallel(self._optimize_baselines(tes t_prefix_list, options.verbose)))
562
563 if change_set.files_to_delete:
564 self._tool.scm().delete_list(change_set.files_to_delete)
565 if change_set.files_to_add:
566 self._tool.scm().add_list(change_set.files_to_add)
566 567
567 def _suffixes_for_actual_failures(self, test, build, existing_suffixes): 568 def _suffixes_for_actual_failures(self, test, build, existing_suffixes):
568 """Gets the baseline suffixes for actual mismatch failures in some resul ts. 569 """Gets the baseline suffixes for actual mismatch failures in some resul ts.
569 570
570 Args: 571 Args:
571 test: A full test path string. 572 test: A full test path string.
572 build: A Build object. 573 build: A Build object.
573 existing_suffixes: A collection of all suffixes to consider. 574 existing_suffixes: A collection of all suffixes to consider.
574 575
575 Returns: 576 Returns:
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after
692 for test in args: 693 for test in args:
693 if test not in test_prefix_list: 694 if test not in test_prefix_list:
694 test_prefix_list[test] = {} 695 test_prefix_list[test] = {}
695 build = Build(builder) 696 build = Build(builder)
696 test_prefix_list[test][build] = suffixes_to_update 697 test_prefix_list[test][build] = suffixes_to_update
697 698
698 if options.verbose: 699 if options.verbose:
699 _log.debug("rebaseline-json: " + str(test_prefix_list)) 700 _log.debug("rebaseline-json: " + str(test_prefix_list))
700 701
701 self._rebaseline(options, test_prefix_list) 702 self._rebaseline(options, test_prefix_list)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698