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

Side by Side Diff: third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/merge_results.py

Issue 2825253003: webkitpy: Improve performance of merge-results script. (Closed)
Patch Set: Created 3 years, 8 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
« no previous file with comments | « no previous file | third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/merge_results_unittest.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright 2017 The Chromium Authors. All rights reserved. 1 # Copyright 2017 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """Classes for merging layout tests results directories together. 5 """Classes for merging layout tests results directories together.
6 6
7 This is split into three parts: 7 This is split into three parts:
8 8
9 * Generic code to merge JSON data together. 9 * Generic code to merge JSON data together.
10 * Generic code to merge directories together. 10 * Generic code to merge directories together.
(...skipping 10 matching lines...) Expand all
21 * Helper functions can be provided to deal with merging specific file objects. 21 * Helper functions can be provided to deal with merging specific file objects.
22 * Helper functions are called when a given Match object returns true for the 22 * Helper functions are called when a given Match object returns true for the
23 filenames. 23 filenames.
24 * The default helper functions only merge if file contents match or the file 24 * The default helper functions only merge if file contents match or the file
25 only exists in one input directory. 25 only exists in one input directory.
26 26
27 The quickest way to understand how the mergers, helper functions and match 27 The quickest way to understand how the mergers, helper functions and match
28 objects work together is to look at the unit tests. 28 objects work together is to look at the unit tests.
29 """ 29 """
30 30
31 import collections
32 import itertools
31 import json 33 import json
32 import logging 34 import logging
33 import pprint 35 import pprint
34 import re 36 import re
35 import types 37 import types
36 38
37 from webkitpy.common.system.filesystem import FileSystem 39 from webkitpy.common.system.filesystem import FileSystem
38 40
39 41
40 _log = logging.getLogger(__name__) 42 _log = logging.getLogger(__name__)
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
87 def __init__(self, value): 89 def __init__(self, value):
88 self.value = value 90 self.value = value
89 91
90 def __call__(self, obj, name=None): 92 def __call__(self, obj, name=None):
91 return obj == self.value 93 return obj == self.value
92 94
93 95
94 class MergeFailure(Exception): 96 class MergeFailure(Exception):
95 """Base exception for merge failing.""" 97 """Base exception for merge failing."""
96 98
97 def __init__(self, msg, name, obj_a, obj_b): 99 def __init__(self, msg, name, objs):
98 emsg = ( 100 emsg = (
99 "Failure merging {name}: " 101 "Failure merging {name}: "
100 " {msg}\nTrying to merge {a} and {b}." 102 " {msg}\nTrying to merge {objs}."
101 ).format( 103 ).format(
102 name=name, 104 name=name,
103 msg=msg, 105 msg=msg,
104 a=obj_a, 106 objs=objs,
105 b=obj_b,
106 ) 107 )
107 Exception.__init__(self, emsg) 108 Exception.__init__(self, emsg)
108 109
109 @classmethod 110 @classmethod
110 def assert_type_eq(cls, name, obj_a, obj_b): 111 def assert_type_eq(cls, name, objs):
111 if type(obj_a) != type(obj_b): 112 obj_0 = objs[0]
112 raise cls("Types don't match", name, obj_a, obj_b) 113 for obj_n in objs[1:]:
114 if type(obj_0) != type(obj_n):
115 raise cls("Types don't match", name, (obj_0, obj_n))
113 116
114 117
115 class Merger(object): 118 class Merger(object):
116 """Base class for merger objects.""" 119 """Base class for merger objects."""
117 120
118 def __init__(self): 121 def __init__(self):
119 self.helpers = [] 122 self.helpers = []
120 123
121 def add_helper(self, match_func, merge_func): 124 def add_helper(self, match_func, merge_func):
122 """Add function which merges values. 125 """Add function which merges values.
(...skipping 10 matching lines...) Expand all
133 class JSONMerger(Merger): 136 class JSONMerger(Merger):
134 """Merge two JSON-like objects. 137 """Merge two JSON-like objects.
135 138
136 For adding helpers; 139 For adding helpers;
137 140
138 match_func is a function of form 141 match_func is a function of form
139 def f(obj, name=None) -> bool 142 def f(obj, name=None) -> bool
140 When the function returns true, the merge_func will be called. 143 When the function returns true, the merge_func will be called.
141 144
142 merge_func is a function of the form 145 merge_func is a function of the form
143 def f(obj_a, obj_b, name=None) -> obj_merged 146 def f(list_of_objs, name=None) -> obj_merged
144 Merge functions should *never* modify the input arguments. 147 Merge functions should *never* modify the input arguments.
145 """ 148 """
146 149
147 def __init__(self): 150 def __init__(self):
148 Merger.__init__(self) 151 Merger.__init__(self)
149 152
150 self.add_helper( 153 self.add_helper(
151 TypeMatch(types.ListType, types.TupleType), self.merge_listlike) 154 TypeMatch(types.ListType, types.TupleType), self.merge_listlike)
152 self.add_helper( 155 self.add_helper(
153 TypeMatch(types.DictType), self.merge_dictlike) 156 TypeMatch(types.DictType), self.merge_dictlike)
154 157
155 def fallback_matcher(self, obj_a, obj_b, name=None): 158 def fallback_matcher(self, objs, name=None):
156 raise MergeFailure( 159 raise MergeFailure(
157 "No merge helper found!", name, obj_a, obj_b) 160 "No merge helper found!", name, objs)
158 161
159 def merge_equal(self, obj_a, obj_b, name=None): 162 def merge_equal(self, objs, name=None):
160 """Merge two equal objects together.""" 163 """Merge two equal objects together."""
alancutter (OOO until 2018) 2017/04/19 07:43:43 Remove "two".
mithro 2017/04/19 08:38:39 Done.
161 if obj_a != obj_b: 164 obj_0 = objs[0]
162 raise MergeFailure( 165 for obj_n in objs[1:]:
163 "Unable to merge!", name, obj_a, obj_b) 166 if obj_0 != obj_n:
164 return obj_a 167 raise MergeFailure(
168 "Unable to merge!", name, (obj_0, obj_n))
169 return obj_0
165 170
166 def merge_listlike(self, list_a, list_b, name=None): # pylint: disable=unus ed-argument 171 def merge_listlike(self, lists, name=None): # pylint: disable=unused-argume nt
167 """Merge two things which are "list like" (tuples, lists, sets).""" 172 """Merge two things which are "list like" (tuples, lists, sets)."""
alancutter (OOO until 2018) 2017/04/19 07:43:43 Remove "two".
mithro 2017/04/19 08:38:39 Done.
168 assert type(list_a) == type(list_b), ( 173 list_0 = lists[0]
169 "Types of %r and %r don't match, refusing to merge." % ( 174 for list_n in lists[1:]:
170 list_a, list_b)) 175 assert type(list_0) == type(list_n), (
171 output = list(list_a) 176 "Types of %r and %r don't match, refusing to merge." % (
172 output.extend(list_b) 177 list_0, list_n))
173 return list_a.__class__(output) 178 output = list(list_0)
179 for list_n in lists[1:]:
180 output.extend(list_n)
181 return list_0.__class__(output)
174 182
175 def merge_dictlike(self, dict_a, dict_b, name=None): 183 def merge_dictlike(self, dicts, name=None):
176 """Merge two things which are dictionaries.""" 184 """Merge two things which are dictionaries."""
alancutter (OOO until 2018) 2017/04/19 07:43:43 Remove "two".
mithro 2017/04/19 08:38:39 Done.
177 assert type(dict_a) == type(dict_b), ( 185 dict_0 = dicts[0]
178 "Types of %r and %r don't match, refusing to merge." % ( 186 for dict_n in dicts[1:]:
179 dict_a, dict_b)) 187 assert type(dict_0) == type(dict_n), (
180 dict_out = dict_a.__class__({}) 188 "Types of %r and %r don't match, refusing to merge." % (
181 for key in dict_a.keys() + dict_b.keys(): 189 dict_0, dict_n))
alancutter (OOO until 2018) 2017/04/19 07:43:43 I think this is worth pulling out as a helper asse
mithro 2017/04/19 08:38:39 Done.
182 if key in dict_a and key in dict_b: 190
191 ordered_keys = collections.OrderedDict.fromkeys(
mcgreevy 2017/04/19 06:57:20 Why do you need an OrderedDict here? Can't you ju
mithro 2017/04/19 08:38:39 Set doesn't preserve the order and OrderedSet does
mcgreevy 2017/04/20 00:08:25 Right, but why do you need to preserve the order?
mithro 2017/04/20 02:02:44 I've added documentation on this.
192 itertools.chain(*(d.iterkeys() for d in dicts)))
193
194 dict_out = dict_0.__class__({})
195 for key in ordered_keys:
196 values_to_merge = []
197 for dobj in dicts:
mcgreevy 2017/04/19 06:57:20 This nested for loop looks up every key in every d
mithro 2017/04/19 08:38:39 I tried implementing this and it made no differenc
198 if key in dobj:
199 values_to_merge.append(dobj[key])
200
201 if len(values_to_merge) == 1:
202 dict_out[key] = values_to_merge[0]
203 elif len(values_to_merge) > 1:
183 dict_out[key] = self.merge( 204 dict_out[key] = self.merge(
184 dict_a[key], dict_b[key], 205 values_to_merge,
185 name=join_name(name, key)) 206 name=join_name(name, key))
186 elif key in dict_a:
187 dict_out[key] = dict_a[key]
188 elif key in dict_b:
189 dict_out[key] = dict_b[key]
190 else: 207 else:
191 assert False 208 assert False, "Key %s not found in any inputs!" % (key,)
192 return dict_out 209 return dict_out
193 210
194 def merge(self, obj_a, obj_b, name=""): 211 def merge(self, objs, name=""):
195 """Generic merge function. 212 """Generic merge function.
196 213
197 name is a string representing the current key value separated by 214 name is a string representing the current key value separated by
198 semicolons. For example, if file.json had the following; 215 semicolons. For example, if file.json had the following;
199 216
200 {'key1': {'key2': 3}} 217 {'key1': {'key2': 3}}
201 218
202 Then the name of the value 3 is 'file.json:key1:key2' 219 Then the name of the value 3 is 'file.json:key1:key2'
203 """ 220 """
204 if obj_a is None and obj_b is None: 221 objs = [o for o in objs if o is not None]
222
223 if not objs:
205 return None 224 return None
206 elif obj_b is None:
207 return obj_a
208 elif obj_a is None:
209 return obj_b
210 225
211 MergeFailure.assert_type_eq(name, obj_a, obj_b) 226 obj_0 = objs[0]
227 for obj_n in objs[1:]:
228 MergeFailure.assert_type_eq(name, (obj_0, obj_n))
212 229
213 # Try the merge helpers. 230 # Try the merge helpers.
214 for match_func, merge_func in reversed(self.helpers): 231 for match_func, merge_func in reversed(self.helpers):
215 if match_func(obj_a, name): 232 for obj in objs:
alancutter (OOO until 2018) 2017/04/19 07:43:43 Seems strange that it needs to test against every
216 return merge_func(obj_a, obj_b, name=name) 233 if match_func(obj, name):
217 if match_func(obj_b, name): 234 return merge_func(objs, name=name)
218 return merge_func(obj_a, obj_b, name=name)
219 235
220 return self.fallback_matcher(obj_a, obj_b, name=name) 236 return self.fallback_matcher(objs, name=name)
221 237
222 238
223 # Classes for recursively merging a directory together. 239 # Classes for recursively merging a directory together.
224 # ------------------------------------------------------------------------ 240 # ------------------------------------------------------------------------
225 241
226 242
227 class FilenameMatch(object): 243 class FilenameMatch(object):
228 """Match based on name matching a regex.""" 244 """Match based on name matching a regex."""
229 245
230 def __init__(self, regex): 246 def __init__(self, regex):
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
268 for filename in to_merge[1:]: 284 for filename in to_merge[1:]:
269 other_data = self.filesystem.read_binary_file(filename) 285 other_data = self.filesystem.read_binary_file(filename)
270 if data != other_data: 286 if data != other_data:
271 nonmatching.append(filename) 287 nonmatching.append(filename)
272 288
273 if nonmatching: 289 if nonmatching:
274 raise MergeFailure( 290 raise MergeFailure(
275 '\n'.join( 291 '\n'.join(
276 ['File contents don\'t match:'] + nonmatching), 292 ['File contents don\'t match:'] + nonmatching),
277 out_filename, 293 out_filename,
278 to_merge[0], to_merge[1:]) 294 to_merge)
279 295
280 self.filesystem.write_binary_file(out_filename, data) 296 self.filesystem.write_binary_file(out_filename, data)
281 297
282 298
283 class MergeFilesLinesSorted(MergeFiles): 299 class MergeFilesLinesSorted(MergeFiles):
284 """Merge and sort the files of the given files.""" 300 """Merge and sort the files of the given files."""
285 301
286 def __call__(self, out_filename, to_merge): 302 def __call__(self, out_filename, to_merge):
287 lines = [] 303 lines = []
288 for filename in to_merge: 304 for filename in to_merge:
(...skipping 26 matching lines...) Expand all
315 output. 331 output.
316 """ 332 """
317 333
318 def __init__(self, filesystem, json_data_merger=None, json_data_value_overri des=None): 334 def __init__(self, filesystem, json_data_merger=None, json_data_value_overri des=None):
319 MergeFiles.__init__(self, filesystem) 335 MergeFiles.__init__(self, filesystem)
320 self._json_data_merger = json_data_merger or JSONMerger() 336 self._json_data_merger = json_data_merger or JSONMerger()
321 self._json_data_value_overrides = json_data_value_overrides or {} 337 self._json_data_value_overrides = json_data_value_overrides or {}
322 338
323 def __call__(self, out_filename, to_merge): 339 def __call__(self, out_filename, to_merge):
324 try: 340 try:
325 before_a, output_data, after_a = self.load_jsonp( 341 before_0, new_json_data_0, after_0 = self.load_jsonp(
326 self.filesystem.open_binary_file_for_reading(to_merge[0])) 342 self.filesystem.open_binary_file_for_reading(to_merge[0]))
327 except ValueError as e: 343 except ValueError as e:
328 raise MergeFailure(e.message, to_merge[0], None, None) 344 raise MergeFailure(e.message, to_merge[0], None)
329 345
330 for filename in to_merge[1:]: 346 input_data = [new_json_data_0]
347 for filename_n in to_merge[1:]:
331 try: 348 try:
332 before_b, new_json_data, after_b = self.load_jsonp( 349 beforen, new_json_data_n, aftern = self.load_jsonp(
333 self.filesystem.open_binary_file_for_reading(filename)) 350 self.filesystem.open_binary_file_for_reading(filename_n))
334 except ValueError as e: 351 except ValueError as e:
335 raise MergeFailure(e.message, filename, None, None) 352 raise MergeFailure(e.message, filename_n, None)
336 353
337 if before_a != before_b: 354 if before_0 != beforen:
338 raise MergeFailure( 355 raise MergeFailure(
339 "jsonp starting data from %s doesn't match." % filename, 356 "jsonp starting data from %s doesn't match." % filename_n,
340 out_filename, 357 out_filename,
341 before_a, before_b) 358 [before_0, beforen])
342 359
343 if after_a != after_b: 360 if after_0 != aftern:
344 raise MergeFailure( 361 raise MergeFailure(
345 "jsonp ending data from %s doesn't match." % filename, 362 "jsonp ending data from %s doesn't match." % filename_n,
346 out_filename, 363 out_filename,
347 after_a, after_b) 364 [after_0, aftern])
348 365
349 output_data = self._json_data_merger.merge(output_data, new_json_dat a, filename) 366 input_data.append(new_json_data_n)
350 367
368 output_data = self._json_data_merger.merge(input_data, name=out_filename )
351 output_data.update(self._json_data_value_overrides) 369 output_data.update(self._json_data_value_overrides)
352 370
353 self.dump_jsonp( 371 self.dump_jsonp(
354 self.filesystem.open_binary_file_for_writing(out_filename), 372 self.filesystem.open_binary_file_for_writing(out_filename),
355 before_a, output_data, after_a) 373 before_0, output_data, after_0)
356 374
357 @staticmethod 375 @staticmethod
358 def load_jsonp(fd): 376 def load_jsonp(fd):
359 """Load a JSONP file and return the JSON data parsed. 377 """Load a JSONP file and return the JSON data parsed.
360 378
361 JSONP files have a JSON data structure wrapped in a function call or 379 JSONP files have a JSON data structure wrapped in a function call or
362 other non-JSON data. 380 other non-JSON data.
363 """ 381 """
364 in_data = fd.read() 382 in_data = fd.read()
365 383
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
458 # rel_file is the path of f relative to the base directory 476 # rel_file is the path of f relative to the base directory
459 rel_file = self.filesystem.join(dir_path, f)[len(base_dir) + 1:] 477 rel_file = self.filesystem.join(dir_path, f)[len(base_dir) + 1:]
460 files.setdefault(rel_file, []).append(base_dir) 478 files.setdefault(rel_file, []).append(base_dir)
461 479
462 # Go through each file and try to merge it. 480 # Go through each file and try to merge it.
463 # partial_file_path is the file relative to the directories. 481 # partial_file_path is the file relative to the directories.
464 for partial_file_path, in_dirs in sorted(files.iteritems()): 482 for partial_file_path, in_dirs in sorted(files.iteritems()):
465 out_path = self.filesystem.join(output_dir, partial_file_path) 483 out_path = self.filesystem.join(output_dir, partial_file_path)
466 if self.filesystem.exists(out_path): 484 if self.filesystem.exists(out_path):
467 raise MergeFailure( 485 raise MergeFailure(
468 'File %s already exist in output.', out_path, None, None) 486 'File %s already exist in output.', out_path, None)
469 487
470 dirname = self.filesystem.dirname(out_path) 488 dirname = self.filesystem.dirname(out_path)
471 if not self.filesystem.exists(dirname): 489 if not self.filesystem.exists(dirname):
472 self.filesystem.maybe_make_directory(dirname) 490 self.filesystem.maybe_make_directory(dirname)
473 491
474 to_merge = [self.filesystem.join(d, partial_file_path) for d in in_d irs] 492 to_merge = [self.filesystem.join(d, partial_file_path) for d in in_d irs]
475 493
476 _log.debug("Creating merged %s from %s", out_path, to_merge) 494 _log.debug("Creating merged %s from %s", out_path, to_merge)
477 495
478 for match_func, merge_func in reversed(self.helpers): 496 for match_func, merge_func in reversed(self.helpers):
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
529 ':num_passes$', 547 ':num_passes$',
530 ':num_regressions$', 548 ':num_regressions$',
531 ':skipped$', 549 ':skipped$',
532 ':skips$', 550 ':skips$',
533 # All keys inside the num_failures_by_type entry. 551 # All keys inside the num_failures_by_type entry.
534 ':num_failures_by_type:', 552 ':num_failures_by_type:',
535 ] 553 ]
536 for match_name in addable: 554 for match_name in addable:
537 self.add_helper( 555 self.add_helper(
538 NameMatch(match_name), 556 NameMatch(match_name),
539 lambda a, b, name=None: a + b) 557 lambda o, name=None: sum(o))
540 558
541 # If any shard is interrupted, mark the whole thing as interrupted. 559 # If any shard is interrupted, mark the whole thing as interrupted.
542 self.add_helper( 560 self.add_helper(
543 NameMatch(':interrupted$'), 561 NameMatch(':interrupted$'),
544 lambda a, b, name=None: a or b) 562 lambda o, name=None: bool(sum(o)))
545 563
546 # Layout test directory value is randomly created on each shard, so 564 # Layout test directory value is randomly created on each shard, so
547 # clear it. 565 # clear it.
548 self.add_helper( 566 self.add_helper(
549 NameMatch(':layout_tests_dir$'), 567 NameMatch(':layout_tests_dir$'),
550 lambda a, b, name=None: None) 568 lambda o, name=None: None)
551 569
552 # seconds_since_epoch is the start time, so we just take the earliest. 570 # seconds_since_epoch is the start time, so we just take the earliest.
553 self.add_helper( 571 self.add_helper(
554 NameMatch(':seconds_since_epoch$'), 572 NameMatch(':seconds_since_epoch$'),
555 lambda a, b, name=None: min(a, b)) 573 lambda o, name=None: min(*o))
556 574
557 def fallback_matcher(self, obj_a, obj_b, name=None): 575 def fallback_matcher(self, objs, name=None):
558 if self.allow_unknown_if_matching: 576 if self.allow_unknown_if_matching:
559 result = self.merge_equal(obj_a, obj_b, name) 577 result = self.merge_equal(objs, name)
560 _log.warning('Unknown value %s, accepting anyway as it matches.', na me) 578 _log.warning('Unknown value %s, accepting anyway as it matches.', na me)
561 return result 579 return result
562 return JSONMerger.fallback_matcher(self, obj_a, obj_b, name) 580 return JSONMerger.fallback_matcher(self, objs, name)
563 581
564 582
565 class LayoutTestDirMerger(DirMerger): 583 class LayoutTestDirMerger(DirMerger):
566 """Merge layout test result directory.""" 584 """Merge layout test result directory."""
567 585
568 def __init__(self, filesystem=None, 586 def __init__(self, filesystem=None,
569 results_json_value_overrides=None, 587 results_json_value_overrides=None,
570 results_json_allow_unknown_if_matching=False): 588 results_json_allow_unknown_if_matching=False):
571 DirMerger.__init__(self, filesystem) 589 DirMerger.__init__(self, filesystem)
572 590
573 # JSON merger for non-"result style" JSON files. 591 # JSON merger for non-"result style" JSON files.
574 basic_json_data_merger = JSONMerger() 592 basic_json_data_merger = JSONMerger()
575 basic_json_data_merger.fallback_matcher = basic_json_data_merger.merge_e qual 593 basic_json_data_merger.fallback_matcher = basic_json_data_merger.merge_e qual
576 self.add_helper( 594 self.add_helper(
577 FilenameMatch('\\.json'), 595 FilenameMatch('\\.json$'),
578 MergeFilesJSONP(self.filesystem, basic_json_data_merger)) 596 MergeFilesJSONP(self.filesystem, basic_json_data_merger))
579 597
580 # access_log and error_log are httpd log files which are sortable. 598 # access_log and error_log are httpd log files which are sortable.
581 self.add_helper( 599 self.add_helper(
582 FilenameMatch('access_log\\.txt'), 600 FilenameMatch('access_log\\.txt$'),
583 MergeFilesLinesSorted(self.filesystem)) 601 MergeFilesLinesSorted(self.filesystem))
584 self.add_helper( 602 self.add_helper(
585 FilenameMatch('error_log\\.txt'), 603 FilenameMatch('error_log\\.txt$'),
586 MergeFilesLinesSorted(self.filesystem)) 604 MergeFilesLinesSorted(self.filesystem))
587 605
588 # pywebsocket files aren't particularly useful, so just save them. 606 # pywebsocket files aren't particularly useful, so just save them.
589 self.add_helper( 607 self.add_helper(
590 FilenameMatch('pywebsocket\\.ws\\.log-.*-err.txt'), 608 FilenameMatch('pywebsocket\\.ws\\.log-.*-err\\.txt$'),
591 MergeFilesKeepFiles(self.filesystem)) 609 MergeFilesKeepFiles(self.filesystem))
592 610
593 # These JSON files have "result style" JSON in them. 611 # These JSON files have "result style" JSON in them.
594 results_json_file_merger = MergeFilesJSONP( 612 results_json_file_merger = MergeFilesJSONP(
595 self.filesystem, 613 self.filesystem,
596 JSONTestResultsMerger( 614 JSONTestResultsMerger(
597 allow_unknown_if_matching=results_json_allow_unknown_if_matching ), 615 allow_unknown_if_matching=results_json_allow_unknown_if_matching ),
598 json_data_value_overrides=results_json_value_overrides or {}) 616 json_data_value_overrides=results_json_value_overrides or {})
599 617
600 self.add_helper( 618 self.add_helper(
601 FilenameMatch('failing_results.json'), 619 FilenameMatch('failing_results\\.json$'),
602 results_json_file_merger) 620 results_json_file_merger)
603 self.add_helper( 621 self.add_helper(
604 FilenameMatch('full_results.json'), 622 FilenameMatch('full_results\\.json$'),
605 results_json_file_merger) 623 results_json_file_merger)
606 self.add_helper( 624 self.add_helper(
607 FilenameMatch('output.json'), 625 FilenameMatch('output\\.json$'),
608 results_json_file_merger) 626 results_json_file_merger)
OLDNEW
« no previous file with comments | « no previous file | third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/merge_results_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698