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

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

Issue 2396433004: Reland of "update SCM and expectations all at once after all commands". (Closed)
Patch Set: Add file to list of files to rm from SCM even if it doesn't exist in SCM. 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 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
80 The reason for keeping track of these changes together is that after rebasel ining 80 The reason for keeping track of these changes together is that after rebasel ining
81 many tests in parallel by subprocesses, we can update the git staged files l ist 81 many tests in parallel by subprocesses, we can update the git staged files l ist
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 lines to remove are only tracked in o ne format. 87 TODO(qyearsley): Refactor this so that lines to remove are only tracked in o ne format.
88 """ 88 """
89 def __init__(self, files_to_add=None, files_to_delete=None, lines_to_remove= None): 89 def __init__(self, files_to_add=None, files_to_delete=None, lines_to_remove= None):
90 self.files_to_add = files_to_add or [] 90 self._files_to_add = set(files_to_add or [])
91 self.files_to_delete = files_to_delete or [] 91 self._files_to_delete = set(files_to_delete or [])
92 self.lines_to_remove = lines_to_remove or {} 92 lines_to_remove = lines_to_remove or {}
93 self._lines_to_remove = {t: set(builders) for t, builders in lines_to_re move.iteritems()}
93 94
94 def add_file(self, path): 95 def add_file(self, path):
95 self.files_to_add.append(path) 96 self._files_to_add.add(path)
96 97
97 def delete_file(self, path): 98 def delete_file(self, path):
98 self.files_to_delete.append(path) 99 self._files_to_delete.add(path)
99 100
100 def remove_line(self, test, builder): 101 def remove_line(self, test, builder):
101 if test not in self.lines_to_remove: 102 if test not in self._lines_to_remove:
102 self.lines_to_remove[test] = [] 103 self._lines_to_remove[test] = set()
103 self.lines_to_remove[test].append(builder) 104 self._lines_to_remove[test].add(builder)
105
106 @property
107 def files_to_add(self):
108 return sorted(self._files_to_add - self._files_to_delete)
109
110 @property
111 def files_to_delete(self):
112 return sorted(self._files_to_delete - self._files_to_add)
113
114 @property
115 def lines_to_remove(self):
116 return {t: sorted(set(builders)) for t, builders in sorted(self._lines_t o_remove.iteritems())}
104 117
105 def to_dict(self): 118 def to_dict(self):
106 remove_lines = [] 119 remove_lines = []
107 for test in self.lines_to_remove: 120 for test in sorted(self.lines_to_remove):
108 for builder in self.lines_to_remove[test]: 121 for builder in sorted(set(self.lines_to_remove[test])):
109 remove_lines.append({'test': test, 'builder': builder}) 122 remove_lines.append({'test': test, 'builder': builder})
110 return { 123 return {
111 'add': list(self.files_to_add), 124 'add': self.files_to_add,
112 'delete': list(self.files_to_delete), 125 'delete': self.files_to_delete,
113 'remove-lines': remove_lines, 126 'remove-lines': remove_lines,
114 } 127 }
115 128
116 @staticmethod 129 @staticmethod
117 def from_dict(change_dict): 130 def from_dict(change_dict):
118 files_to_add = set() 131 files_to_add = set()
119 files_to_delete = set() 132 files_to_delete = set()
120 lines_to_remove = {} 133 lines_to_remove = {}
121 if 'add' in change_dict: 134 if 'add' in change_dict:
122 files_to_add.update(change_dict['add']) 135 files_to_add.update(change_dict['add'])
123 if 'delete' in change_dict: 136 if 'delete' in change_dict:
124 files_to_delete.update(change_dict['delete']) 137 files_to_delete.update(change_dict['delete'])
125 if 'remove-lines' in change_dict: 138 if 'remove-lines' in change_dict:
126 for line_to_remove in change_dict['remove-lines']: 139 for line_to_remove in change_dict['remove-lines']:
127 test = line_to_remove['test'] 140 test = line_to_remove['test']
128 builder = line_to_remove['builder'] 141 builder = line_to_remove['builder']
129 if test not in lines_to_remove: 142 if test not in lines_to_remove:
130 lines_to_remove[test] = [] 143 lines_to_remove[test] = []
131 lines_to_remove[test].append(builder) 144 lines_to_remove[test].append(builder)
132 return ChangeSet( 145 return ChangeSet(
133 files_to_add=list(files_to_add), 146 files_to_add=files_to_add,
134 files_to_delete=list(files_to_delete), 147 files_to_delete=files_to_delete,
135 lines_to_remove=lines_to_remove) 148 lines_to_remove=lines_to_remove)
136 149
137 def update(self, other): 150 def update(self, other):
138 assert isinstance(other, ChangeSet) 151 assert isinstance(other, ChangeSet)
139 assert type(other.lines_to_remove) is dict 152 self._files_to_add.update(other.files_to_add)
140 self.files_to_add.extend(other.files_to_add) 153 self._files_to_delete.update(other.files_to_delete)
141 self.files_to_delete.extend(other.files_to_delete)
142 for test in other.lines_to_remove: 154 for test in other.lines_to_remove:
143 if test not in self.lines_to_remove: 155 if test not in self._lines_to_remove:
144 self.lines_to_remove[test] = [] 156 self._lines_to_remove[test] = set()
145 self.lines_to_remove[test].extend(other.lines_to_remove[test]) 157 self._lines_to_remove[test].update(other.lines_to_remove[test])
146 158
147 159
148 class BaseInternalRebaselineCommand(AbstractRebaseliningCommand): 160 class BaseInternalRebaselineCommand(AbstractRebaseliningCommand):
149 """Base class for rebaseline-related commands that are intended to be used b y other commands.""" 161 """Base class for rebaseline-related commands that are intended to be used b y other commands."""
150 # Not overriding execute() - pylint: disable=abstract-method 162 # Not overriding execute() - pylint: disable=abstract-method
151 163
152 def __init__(self): 164 def __init__(self):
153 super(BaseInternalRebaselineCommand, self).__init__(options=[ 165 super(BaseInternalRebaselineCommand, self).__init__(options=[
154 self.results_directory_option, 166 self.results_directory_option,
155 self.suffixes_option, 167 self.suffixes_option,
(...skipping 324 matching lines...) Expand 10 before | Expand all | Expand 10 after
480 def _port_skips_test(port, test, generic_expectations, full_expectations): 492 def _port_skips_test(port, test, generic_expectations, full_expectations):
481 fs = port.host.filesystem 493 fs = port.host.filesystem
482 if port.default_smoke_test_only(): 494 if port.default_smoke_test_only():
483 smoke_test_filename = fs.join(port.layout_tests_dir(), 'SmokeTests') 495 smoke_test_filename = fs.join(port.layout_tests_dir(), 'SmokeTests')
484 if fs.exists(smoke_test_filename) and test not in fs.read_text_file( smoke_test_filename): 496 if fs.exists(smoke_test_filename) and test not in fs.read_text_file( smoke_test_filename):
485 return True 497 return True
486 498
487 return (SKIP in full_expectations.get_expectations(test) and 499 return (SKIP in full_expectations.get_expectations(test) and
488 SKIP not in generic_expectations.get_expectations(test)) 500 SKIP not in generic_expectations.get_expectations(test))
489 501
490 def _run_in_parallel(self, commands, update_scm=True): 502 def _run_in_parallel(self, commands):
491 if not commands: 503 if not commands:
492 return {} 504 return ChangeSet()
493 505
494 command_results = self._tool.executive.run_in_parallel(commands) 506 command_results = self._tool.executive.run_in_parallel(commands)
495 for _, _, stderr in command_results: 507 for _, _, stderr in command_results:
496 if stderr: 508 if stderr:
497 _log.error(stderr) 509 _log.error(stderr)
498 510
499 change_set = self._extract_scm_changes(command_results) 511 return self._extract_scm_changes(command_results)
500 512
501 # TODO(qyearsley): Instead of updating the SCM state here, aggregate cha nges 513 def _rebaseline(self, options, test_prefix_list):
502 # and update once in _rebaseline. See http://crbug.com/639410.
503 if update_scm:
504 if change_set.files_to_delete:
505 self._tool.scm().delete_list(change_set.files_to_delete)
506 if change_set.files_to_add:
507 self._tool.scm().add_list(change_set.files_to_add)
508
509 return change_set.lines_to_remove
510
511 def _rebaseline(self, options, test_prefix_list, update_scm=True):
512 """Downloads new baselines in parallel, then updates expectations files 514 """Downloads new baselines in parallel, then updates expectations files
513 and optimizes baselines. 515 and optimizes baselines.
514 516
515 Args: 517 Args:
516 options: An object with the options passed to the current command. 518 options: An object with the options passed to the current command.
517 test_prefix_list: A map of test names to Build objects to file suffi xes 519 test_prefix_list: A map of test names to Build objects to file suffi xes
518 for new baselines. For example: 520 for new baselines. For example:
519 { 521 {
520 "some/test.html": {Build("builder-1", 412): ["txt"], Build(" builder-2", 100): ["txt"]}, 522 "some/test.html": {Build("builder-1", 412): ["txt"], Build(" builder-2", 100): ["txt"]},
521 "some/other.html": {Build("builder-1", 412): ["txt"]} 523 "some/other.html": {Build("builder-1", 412): ["txt"]}
522 } 524 }
523 This would mean that new text baselines should be downloaded for 525 This would mean that new text baselines should be downloaded for
524 "some/test.html" on both builder-1 (build 412) and builder-2 526 "some/test.html" on both builder-1 (build 412) and builder-2
525 (build 100), and new text baselines should be downloaded for 527 (build 100), and new text baselines should be downloaded for
526 "some/other.html" but only from builder-1. 528 "some/other.html" but only from builder-1.
527 TODO(qyearsley): Replace test_prefix_list everywhere with some 529 TODO(qyearsley): Replace test_prefix_list everywhere with some
528 sort of class that contains the same data. 530 sort of class that contains the same data.
529 update_scm: If True, commands like `git add` and `git rm` will be ru n.
530 """ 531 """
531 for test, builds_to_check in sorted(test_prefix_list.items()): 532 for test, builds_to_check in sorted(test_prefix_list.items()):
532 _log.info("Rebaselining %s", test) 533 _log.info("Rebaselining %s", test)
533 for build, suffixes in sorted(builds_to_check.items()): 534 for build, suffixes in sorted(builds_to_check.items()):
534 _log.debug(" %s: %s", build, ",".join(suffixes)) 535 _log.debug(" %s: %s", build, ",".join(suffixes))
535 536
536 copy_baseline_commands, rebaseline_commands, extra_lines_to_remove = sel f._rebaseline_commands( 537 copy_baseline_commands, rebaseline_commands, extra_lines_to_remove = sel f._rebaseline_commands(
537 test_prefix_list, options) 538 test_prefix_list, options)
538 lines_to_remove = {}
539 539
540 self._run_in_parallel(copy_baseline_commands, update_scm=update_scm) 540 change_set = ChangeSet(lines_to_remove=extra_lines_to_remove)
541 lines_to_remove = self._run_in_parallel(rebaseline_commands, update_scm= update_scm)
542 541
543 for test in extra_lines_to_remove: 542 change_set.update(self._run_in_parallel(copy_baseline_commands))
544 if test in lines_to_remove: 543 change_set.update(self._run_in_parallel(rebaseline_commands))
545 lines_to_remove[test] = lines_to_remove[test] + extra_lines_to_r emove[test]
546 else:
547 lines_to_remove[test] = extra_lines_to_remove[test]
548 544
549 if lines_to_remove: 545 if change_set.lines_to_remove:
550 self._update_expectations_files(lines_to_remove) 546 self._update_expectations_files(change_set.lines_to_remove)
551 547
552 if options.optimize: 548 if options.optimize:
553 # TODO(wkorman): Consider changing temporary branch to base off of H EAD rather than 549 # TODO(wkorman): Consider changing temporary branch to base off of H EAD rather than
554 # origin/master to ensure we run baseline optimization processes wit h the same code as 550 # origin/master to ensure we run baseline optimization processes wit h the same code as
555 # auto-rebaseline itself. 551 # auto-rebaseline itself.
556 self._run_in_parallel(self._optimize_baselines(test_prefix_list, opt ions.verbose), update_scm=update_scm) 552 change_set.update(self._run_in_parallel(self._optimize_baselines(tes t_prefix_list, options.verbose)))
553
554 if change_set.files_to_delete:
555 self._tool.scm().delete_list(change_set.files_to_delete)
556 if change_set.files_to_add:
557 self._tool.scm().add_list(change_set.files_to_add)
557 558
558 def _suffixes_for_actual_failures(self, test, build, existing_suffixes): 559 def _suffixes_for_actual_failures(self, test, build, existing_suffixes):
559 """Gets the baseline suffixes for actual mismatch failures in some resul ts. 560 """Gets the baseline suffixes for actual mismatch failures in some resul ts.
560 561
561 Args: 562 Args:
562 test: A full test path string. 563 test: A full test path string.
563 build: A Build object. 564 build: A Build object.
564 existing_suffixes: A collection of all suffixes to consider. 565 existing_suffixes: A collection of all suffixes to consider.
565 566
566 Returns: 567 Returns:
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after
683 for test in args: 684 for test in args:
684 if test not in test_prefix_list: 685 if test not in test_prefix_list:
685 test_prefix_list[test] = {} 686 test_prefix_list[test] = {}
686 build = Build(builder) 687 build = Build(builder)
687 test_prefix_list[test][build] = suffixes_to_update 688 test_prefix_list[test][build] = suffixes_to_update
688 689
689 if options.verbose: 690 if options.verbose:
690 _log.debug("rebaseline-json: " + str(test_prefix_list)) 691 _log.debug("rebaseline-json: " + str(test_prefix_list))
691 692
692 self._rebaseline(options, test_prefix_list) 693 self._rebaseline(options, test_prefix_list)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698