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

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

Issue 302003009: Make rebaselining not use gigabytes of memory. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: cleanup style Created 6 years, 6 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 | Annotate | Revision Log
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 350 matching lines...) Expand 10 before | Expand all | Expand 10 after
361 fallback_path = port.baseline_search_path() 361 fallback_path = port.baseline_search_path()
362 if fallback_path not in builders_to_fallback_paths.values(): 362 if fallback_path not in builders_to_fallback_paths.values():
363 builders_to_fallback_paths[builder] = fallback_path 363 builders_to_fallback_paths[builder] = fallback_path
364 return builders_to_fallback_paths.keys() 364 return builders_to_fallback_paths.keys()
365 365
366 def _rebaseline_commands(self, test_prefix_list, options): 366 def _rebaseline_commands(self, test_prefix_list, options):
367 path_to_webkit_patch = self._tool.path() 367 path_to_webkit_patch = self._tool.path()
368 cwd = self._tool.scm().checkout_root 368 cwd = self._tool.scm().checkout_root
369 copy_baseline_commands = [] 369 copy_baseline_commands = []
370 rebaseline_commands = [] 370 rebaseline_commands = []
371 lines_to_remove = {}
371 port = self._tool.port_factory.get() 372 port = self._tool.port_factory.get()
372 373
373 for test_prefix in test_prefix_list: 374 for test_prefix in test_prefix_list:
374 for test in port.tests([test_prefix]): 375 for test in port.tests([test_prefix]):
375 for builder in self._builders_to_fetch_from(test_prefix_list[tes t_prefix]): 376 for builder in self._builders_to_fetch_from(test_prefix_list[tes t_prefix]):
376 actual_failures_suffixes = self._suffixes_for_actual_failure s(test, builder, test_prefix_list[test_prefix][builder]) 377 actual_failures_suffixes = self._suffixes_for_actual_failure s(test, builder, test_prefix_list[test_prefix][builder])
377 if not actual_failures_suffixes: 378 if not actual_failures_suffixes:
379 # If we're not going to rebaseline the test because it's passing on this
380 # builder, we still want to remove the line from TestExp ectations.
381 if test not in lines_to_remove:
382 lines_to_remove[test] = []
383 lines_to_remove[test].append(builder)
378 continue 384 continue
379 385
380 suffixes = ','.join(actual_failures_suffixes) 386 suffixes = ','.join(actual_failures_suffixes)
381 cmd_line = ['--suffixes', suffixes, '--builder', builder, '- -test', test] 387 cmd_line = ['--suffixes', suffixes, '--builder', builder, '- -test', test]
382 if options.results_directory: 388 if options.results_directory:
383 cmd_line.extend(['--results-directory', options.results_ directory]) 389 cmd_line.extend(['--results-directory', options.results_ directory])
384 if options.verbose: 390 if options.verbose:
385 cmd_line.append('--verbose') 391 cmd_line.append('--verbose')
386 copy_baseline_commands.append(tuple([[path_to_webkit_patch, 'copy-existing-baselines-internal'] + cmd_line, cwd])) 392 copy_baseline_commands.append(tuple([[path_to_webkit_patch, 'copy-existing-baselines-internal'] + cmd_line, cwd]))
387 rebaseline_commands.append(tuple([[path_to_webkit_patch, 're baseline-test-internal'] + cmd_line, cwd])) 393 rebaseline_commands.append(tuple([[path_to_webkit_patch, 're baseline-test-internal'] + cmd_line, cwd]))
388 return copy_baseline_commands, rebaseline_commands 394 return copy_baseline_commands, rebaseline_commands, lines_to_remove
389 395
390 def _serial_commands(self, command_results): 396 def _serial_commands(self, command_results):
391 files_to_add = set() 397 files_to_add = set()
392 files_to_delete = set() 398 files_to_delete = set()
393 lines_to_remove = {} 399 lines_to_remove = {}
394 for output in [result[1].split('\n') for result in command_results]: 400 for output in [result[1].split('\n') for result in command_results]:
395 file_added = False 401 file_added = False
396 for line in output: 402 for line in output:
397 try: 403 try:
398 if line: 404 if line:
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
430 cmd_line.append('--verbose') 436 cmd_line.append('--verbose')
431 437
432 path_to_webkit_patch = self._tool.path() 438 path_to_webkit_patch = self._tool.path()
433 cwd = self._tool.scm().checkout_root 439 cwd = self._tool.scm().checkout_root
434 optimize_commands.append(tuple([[path_to_webkit_patch, 'optimize-bas elines'] + cmd_line, cwd])) 440 optimize_commands.append(tuple([[path_to_webkit_patch, 'optimize-bas elines'] + cmd_line, cwd]))
435 return optimize_commands 441 return optimize_commands
436 442
437 def _update_expectations_files(self, lines_to_remove): 443 def _update_expectations_files(self, lines_to_remove):
438 # FIXME: This routine is way too expensive. We're creating N ports and N TestExpectations 444 # FIXME: This routine is way too expensive. We're creating N ports and N TestExpectations
439 # objects and (re-)writing the actual expectations file N times, for eac h test we update. 445 # objects and (re-)writing the actual expectations file N times, for eac h test we update.
440 # We should be able to update everything in memory, once, and then write the file out a single time. 446 # We should be able to update everything in memory, once, and then write the file out a single time.
Dirk Pranke 2014/06/01 21:25:33 I think this is you fixing this FIXME, yes?
ojan 2014/06/02 01:41:15 Mostly. I updated it.
447 tests = lines_to_remove.keys()
448 to_remove = []
449
450 for port_name in self._tool.port_factory.all_port_names():
451 port = self._tool.port_factory.get(port_name)
452 generic_expectations = TestExpectations(port, tests=tests, include_o verrides=False)
eseidel 2014/06/01 04:03:42 Why don't we just always load TestExpectations wit
ojan 2014/06/01 04:26:33 That might work. I think that should be a separate
Dirk Pranke 2014/06/01 21:25:33 I think Eric's idea is a reasonable suggestion, bu
ojan 2014/06/02 01:41:15 +1
453 full_expectations = TestExpectations(port, tests=tests, include_over rides=True)
454 for test in tests:
455 if self._port_skips_test(port, test, generic_expectations, full_ expectations):
456 for test_configuration in port.all_test_configurations():
457 if test_configuration.version == port.test_configuration ().version:
458 to_remove.append((test, test_configuration))
Dirk Pranke 2014/06/01 21:25:33 It seems like lines 454-458 could use a comment ex
ojan 2014/06/02 01:41:15 Yeah...I'm not 100% sure what this was for either.
459
441 for test in lines_to_remove: 460 for test in lines_to_remove:
442 for builder in lines_to_remove[test]: 461 for builder in lines_to_remove[test]:
443 port = self._tool.port_factory.get_from_builder_name(builder) 462 port = self._tool.port_factory.get_from_builder_name(builder)
444 path = port.path_to_generic_test_expectations_file()
445 expectations = TestExpectations(port, include_overrides=False)
446 for test_configuration in port.all_test_configurations(): 463 for test_configuration in port.all_test_configurations():
447 if test_configuration.version == port.test_configuration().v ersion: 464 if test_configuration.version == port.test_configuration().v ersion:
448 expectationsString = expectations.remove_configuration_f rom_test(test, test_configuration) 465 to_remove.append((test, test_configuration))
449 self._tool.filesystem.write_text_file(path, expectationsString)
450 466
451 for port_name in self._tool.port_factory.all_port_names(): 467 port = self._tool.port_factory.get()
452 port = self._tool.port_factory.get(port_name) 468 expectations = TestExpectations(port, include_overrides=False)
453 generic_expectations = TestExpectations(port, tests=[test], incl ude_overrides=False) 469 expectationsString = expectations.remove_configurations(to_remove)
454 if self._port_skips_test(port, test, generic_expectations): 470 path = port.path_to_generic_test_expectations_file()
455 for test_configuration in port.all_test_configurations(): 471 self._tool.filesystem.write_text_file(path, expectationsString)
456 if test_configuration.version == port.test_configuration ().version:
457 expectationsString = generic_expectations.remove_con figuration_from_test(test, test_configuration)
458 generic_path = port.path_to_generic_test_expectations_file()
459 self._tool.filesystem.write_text_file(generic_path, expectat ionsString)
460 472
461 def _port_skips_test(self, port, test, generic_expectations): 473 def _port_skips_test(self, port, test, generic_expectations, full_expectatio ns):
462 fs = port.host.filesystem 474 fs = port.host.filesystem
463 if port.default_smoke_test_only(): 475 if port.default_smoke_test_only():
464 smoke_test_filename = fs.join(port.layout_tests_dir(), 'SmokeTests') 476 smoke_test_filename = fs.join(port.layout_tests_dir(), 'SmokeTests')
465 if fs.exists(smoke_test_filename) and test not in fs.read_text_file( smoke_test_filename): 477 if fs.exists(smoke_test_filename) and test not in fs.read_text_file( smoke_test_filename):
466 return True 478 return True
467 479
468 full_expectations = TestExpectations(port, tests=[test], include_overrid es=True)
469 return (SKIP in full_expectations.get_expectations(test) and 480 return (SKIP in full_expectations.get_expectations(test) and
470 SKIP not in generic_expectations.get_expectations(test)) 481 SKIP not in generic_expectations.get_expectations(test))
471 482
472 def _run_in_parallel_and_update_scm(self, commands): 483 def _run_in_parallel_and_update_scm(self, commands, extra_lines_to_remove={} ):
Dirk Pranke 2014/06/01 21:25:33 Is extra_lines_to_remove used in this routine? I d
ojan 2014/06/02 01:41:15 Whoops. Sorry, this was a leftover from an earlier
473 command_results = self._tool.executive.run_in_parallel(commands) 484 command_results = self._tool.executive.run_in_parallel(commands)
474 log_output = '\n'.join(result[2] for result in command_results).replace( '\n\n', '\n') 485 log_output = '\n'.join(result[2] for result in command_results).replace( '\n\n', '\n')
475 for line in log_output.split('\n'): 486 for line in log_output.split('\n'):
476 if line: 487 if line:
477 print >> sys.stderr, line # FIXME: Figure out how to log proper ly. 488 print >> sys.stderr, line # FIXME: Figure out how to log proper ly.
478 489
479 files_to_add, files_to_delete, lines_to_remove = self._serial_commands(c ommand_results) 490 files_to_add, files_to_delete, lines_to_remove = self._serial_commands(c ommand_results)
491
480 if files_to_delete: 492 if files_to_delete:
481 self._tool.scm().delete_list(files_to_delete) 493 self._tool.scm().delete_list(files_to_delete)
482 if files_to_add: 494 if files_to_add:
483 self._tool.scm().add_list(files_to_add) 495 self._tool.scm().add_list(files_to_add)
484 if lines_to_remove: 496 return lines_to_remove
485 self._update_expectations_files(lines_to_remove)
486 497
487 def _rebaseline(self, options, test_prefix_list): 498 def _rebaseline(self, options, test_prefix_list):
488 for test, builders_to_check in sorted(test_prefix_list.items()): 499 for test, builders_to_check in sorted(test_prefix_list.items()):
489 _log.info("Rebaselining %s" % test) 500 _log.info("Rebaselining %s" % test)
490 for builder, suffixes in sorted(builders_to_check.items()): 501 for builder, suffixes in sorted(builders_to_check.items()):
491 _log.debug(" %s: %s" % (builder, ",".join(suffixes))) 502 _log.debug(" %s: %s" % (builder, ",".join(suffixes)))
492 503
493 copy_baseline_commands, rebaseline_commands = self._rebaseline_commands( test_prefix_list, options) 504 copy_baseline_commands, rebaseline_commands, extra_lines_to_remove = sel f._rebaseline_commands(test_prefix_list, options)
505 lines_to_remove = {}
506
494 if copy_baseline_commands: 507 if copy_baseline_commands:
495 self._run_in_parallel_and_update_scm(copy_baseline_commands) 508 self._run_in_parallel_and_update_scm(copy_baseline_commands)
496 if rebaseline_commands: 509 if rebaseline_commands:
497 self._run_in_parallel_and_update_scm(rebaseline_commands) 510 lines_to_remove = self._run_in_parallel_and_update_scm(rebaseline_co mmands)
511
512 for test in extra_lines_to_remove:
513 if test in lines_to_remove:
514 lines_to_remove[test] = lines_to_remove[test] + extra_lines_to_r emove[test]
515 else:
516 lines_to_remove[test] = extra_lines_to_remove[test]
517
518 if lines_to_remove:
519 self._update_expectations_files(lines_to_remove)
520
498 if options.optimize: 521 if options.optimize:
499 self._run_in_parallel_and_update_scm(self._optimize_baselines(test_p refix_list, options.verbose)) 522 self._run_in_parallel_and_update_scm(self._optimize_baselines(test_p refix_list, options.verbose))
500 523
501 def _suffixes_for_actual_failures(self, test, builder_name, existing_suffixe s): 524 def _suffixes_for_actual_failures(self, test, builder_name, existing_suffixe s):
502 actual_results = self.builder_data()[builder_name].actual_results(test) 525 actual_results = self.builder_data()[builder_name].actual_results(test)
503 if not actual_results: 526 if not actual_results:
504 return set() 527 return set()
505 return set(existing_suffixes) & TestExpectations.suffixes_for_actual_exp ectations_string(actual_results) 528 return set(existing_suffixes) & TestExpectations.suffixes_for_actual_exp ectations_string(actual_results)
506 529
507 530
(...skipping 350 matching lines...) Expand 10 before | Expand all | Expand 10 after
858 881
859 try: 882 try:
860 old_branch_name = tool.scm().current_branch() 883 old_branch_name = tool.scm().current_branch()
861 tool.scm().delete_branch(self.AUTO_REBASELINE_BRANCH_NAME) 884 tool.scm().delete_branch(self.AUTO_REBASELINE_BRANCH_NAME)
862 tool.scm().create_clean_branch(self.AUTO_REBASELINE_BRANCH_NAME) 885 tool.scm().create_clean_branch(self.AUTO_REBASELINE_BRANCH_NAME)
863 886
864 # If the tests are passing everywhere, then this list will be empty. We don't need 887 # If the tests are passing everywhere, then this list will be empty. We don't need
865 # to rebaseline, but we'll still need to update TestExpectations. 888 # to rebaseline, but we'll still need to update TestExpectations.
866 if test_prefix_list: 889 if test_prefix_list:
867 self._rebaseline(options, test_prefix_list) 890 self._rebaseline(options, test_prefix_list)
868 # If a test is not failing on the bot, we don't try to rebaseline it , but we still
869 # want to remove the NeedsRebaseline line.
870 self._update_expectations_files(lines_to_remove)
871 891
872 tool.scm().commit_locally_with_message(self.commit_message(author, r evision, bugs)) 892 tool.scm().commit_locally_with_message(self.commit_message(author, r evision, bugs))
873 893
874 # FIXME: It would be nice if we could dcommit the patch without uplo ading, but still 894 # FIXME: It would be nice if we could dcommit the patch without uplo ading, but still
875 # go through all the precommit hooks. For rebaselines with lots of f iles, uploading 895 # go through all the precommit hooks. For rebaselines with lots of f iles, uploading
876 # takes a long time and sometimes fails, but we don't want to commit if, e.g. the 896 # takes a long time and sometimes fails, but we don't want to commit if, e.g. the
877 # tree is closed. 897 # tree is closed.
878 did_finish = self._run_git_cl_command(options, ['upload', '-f']) 898 did_finish = self._run_git_cl_command(options, ['upload', '-f'])
879 899
880 if did_finish: 900 if did_finish:
(...skipping 26 matching lines...) Expand all
907 if options.verbose: 927 if options.verbose:
908 rebaseline_command.append('--verbose') 928 rebaseline_command.append('--verbose')
909 # Use call instead of run_command so that stdout doesn't get swa llowed. 929 # Use call instead of run_command so that stdout doesn't get swa llowed.
910 tool.executive.call(rebaseline_command) 930 tool.executive.call(rebaseline_command)
911 except: 931 except:
912 traceback.print_exc(file=sys.stderr) 932 traceback.print_exc(file=sys.stderr)
913 # Sometimes git crashes and leaves us on a detached head. 933 # Sometimes git crashes and leaves us on a detached head.
914 tool.scm().checkout_branch(old_branch_name) 934 tool.scm().checkout_branch(old_branch_name)
915 935
916 time.sleep(self.SLEEP_TIME_IN_SECONDS) 936 time.sleep(self.SLEEP_TIME_IN_SECONDS)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698