OLD | NEW |
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2012 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 """Generic presubmit checks that can be reused by other presubmit checks.""" | 5 """Generic presubmit checks that can be reused by other presubmit checks.""" |
6 | 6 |
7 import os as _os | 7 import os as _os |
8 _HERE = _os.path.dirname(_os.path.abspath(__file__)) | 8 _HERE = _os.path.dirname(_os.path.abspath(__file__)) |
9 | 9 |
10 | 10 |
(...skipping 465 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
476 connection.close() | 476 connection.close() |
477 if input_api.re.match(closed, status): | 477 if input_api.re.match(closed, status): |
478 long_text = status + '\n' + url | 478 long_text = status + '\n' + url |
479 return [output_api.PresubmitError('The tree is closed.', | 479 return [output_api.PresubmitError('The tree is closed.', |
480 long_text=long_text)] | 480 long_text=long_text)] |
481 except IOError as e: | 481 except IOError as e: |
482 return [output_api.PresubmitError('Error fetching tree status.', | 482 return [output_api.PresubmitError('Error fetching tree status.', |
483 long_text=str(e))] | 483 long_text=str(e))] |
484 return [] | 484 return [] |
485 | 485 |
486 | 486 def GetUnitTestsInDirectory( |
487 def RunUnitTestsInDirectory( | |
488 input_api, output_api, directory, whitelist=None, blacklist=None): | 487 input_api, output_api, directory, whitelist=None, blacklist=None): |
489 """Lists all files in a directory and runs them. Doesn't recurse. | 488 """Lists all files in a directory and runs them. Doesn't recurse. |
490 | 489 |
491 It's mainly a wrapper for RunUnitTests. USe whitelist and blacklist to filter | 490 It's mainly a wrapper for RunUnitTests. USe whitelist and blacklist to filter |
492 tests accordingly. | 491 tests accordingly. |
493 """ | 492 """ |
494 unit_tests = [] | 493 unit_tests = [] |
495 test_path = input_api.os_path.abspath( | 494 test_path = input_api.os_path.abspath( |
496 input_api.os_path.join(input_api.PresubmitLocalPath(), directory)) | 495 input_api.os_path.join(input_api.PresubmitLocalPath(), directory)) |
497 | 496 |
(...skipping 12 matching lines...) Expand all Loading... |
510 continue | 509 continue |
511 unit_tests.append(input_api.os_path.join(directory, filename)) | 510 unit_tests.append(input_api.os_path.join(directory, filename)) |
512 to_run += 1 | 511 to_run += 1 |
513 input_api.logging.debug('Found %d files, running %d' % (found, to_run)) | 512 input_api.logging.debug('Found %d files, running %d' % (found, to_run)) |
514 if not to_run: | 513 if not to_run: |
515 return [ | 514 return [ |
516 output_api.PresubmitPromptWarning( | 515 output_api.PresubmitPromptWarning( |
517 'Out of %d files, found none that matched w=%r, b=%r in directory %s' | 516 'Out of %d files, found none that matched w=%r, b=%r in directory %s' |
518 % (found, whitelist, blacklist, directory)) | 517 % (found, whitelist, blacklist, directory)) |
519 ] | 518 ] |
520 return RunUnitTests(input_api, output_api, unit_tests) | 519 return GetUnitTests(input_api, output_api, unit_tests) |
521 | 520 |
522 | 521 |
523 def RunUnitTests(input_api, output_api, unit_tests): | 522 def GetUnitTests(input_api, output_api, unit_tests): |
524 """Runs all unit tests in a directory. | 523 """Runs all unit tests in a directory. |
525 | 524 |
526 On Windows, sys.executable is used for unit tests ending with ".py". | 525 On Windows, sys.executable is used for unit tests ending with ".py". |
527 """ | 526 """ |
528 # We don't want to hinder users from uploading incomplete patches. | 527 # We don't want to hinder users from uploading incomplete patches. |
529 if input_api.is_committing: | 528 if input_api.is_committing: |
530 message_type = output_api.PresubmitError | 529 message_type = output_api.PresubmitError |
531 else: | 530 else: |
532 message_type = output_api.PresubmitPromptWarning | 531 message_type = output_api.PresubmitPromptWarning |
533 | 532 |
534 results = [] | 533 results = [] |
535 for unit_test in unit_tests: | 534 for unit_test in unit_tests: |
536 cmd = [] | 535 cmd = [] |
537 if input_api.platform == 'win32' and unit_test.endswith('.py'): | 536 if input_api.platform == 'win32' and unit_test.endswith('.py'): |
538 # Windows needs some help. | 537 # Windows needs some help. |
539 cmd = [input_api.python_executable] | 538 cmd = [input_api.python_executable] |
540 cmd.append(unit_test) | 539 cmd.append(unit_test) |
541 if input_api.verbose: | 540 if input_api.verbose: |
542 print('Running %s' % unit_test) | 541 print('Running %s' % unit_test) |
543 cmd.append('--verbose') | 542 cmd.append('--verbose') |
544 try: | 543 results.append(input_api.Command( |
545 if input_api.verbose: | 544 name=unit_test, |
546 input_api.subprocess.check_call(cmd, cwd=input_api.PresubmitLocalPath()) | 545 cmd=cmd, |
547 else: | 546 kwargs={'cwd': input_api.PresubmitLocalPath()}, |
548 input_api.subprocess.check_output( | 547 message=message_type)) |
549 cmd, | |
550 stderr=input_api.subprocess.STDOUT, | |
551 cwd=input_api.PresubmitLocalPath()) | |
552 except (OSError, input_api.subprocess.CalledProcessError), e: | |
553 results.append(message_type('%s failed!\n%s' % (unit_test, e))) | |
554 return results | 548 return results |
555 | 549 |
556 | 550 def GetPythonUnitTests(input_api, output_api, unit_tests): |
557 def RunPythonUnitTests(input_api, output_api, unit_tests): | |
558 """Run the unit tests out of process, capture the output and use the result | 551 """Run the unit tests out of process, capture the output and use the result |
559 code to determine success. | 552 code to determine success. |
560 | 553 |
561 DEPRECATED. | 554 DEPRECATED. |
562 """ | 555 """ |
563 # We don't want to hinder users from uploading incomplete patches. | 556 # We don't want to hinder users from uploading incomplete patches. |
564 if input_api.is_committing: | 557 if input_api.is_committing: |
565 message_type = output_api.PresubmitError | 558 message_type = output_api.PresubmitError |
566 else: | 559 else: |
567 message_type = output_api.PresubmitNotifyResult | 560 message_type = output_api.PresubmitNotifyResult |
(...skipping 15 matching lines...) Expand all Loading... |
583 unit_test = input_api.os_path.basename(unit_test) | 576 unit_test = input_api.os_path.basename(unit_test) |
584 env = input_api.environ.copy() | 577 env = input_api.environ.copy() |
585 # At least on Windows, it seems '.' must explicitly be in PYTHONPATH | 578 # At least on Windows, it seems '.' must explicitly be in PYTHONPATH |
586 backpath = [ | 579 backpath = [ |
587 '.', input_api.os_path.pathsep.join(['..'] * (cwd.count('/') + 1)) | 580 '.', input_api.os_path.pathsep.join(['..'] * (cwd.count('/') + 1)) |
588 ] | 581 ] |
589 if env.get('PYTHONPATH'): | 582 if env.get('PYTHONPATH'): |
590 backpath.append(env.get('PYTHONPATH')) | 583 backpath.append(env.get('PYTHONPATH')) |
591 env['PYTHONPATH'] = input_api.os_path.pathsep.join((backpath)) | 584 env['PYTHONPATH'] = input_api.os_path.pathsep.join((backpath)) |
592 cmd = [input_api.python_executable, '-m', '%s' % unit_test] | 585 cmd = [input_api.python_executable, '-m', '%s' % unit_test] |
593 try: | 586 results.append(input_api.Command( |
594 input_api.subprocess.check_output( | 587 name=unit_test_name, |
595 cmd, stderr=input_api.subprocess.STDOUT, cwd=cwd, env=env) | 588 cmd=cmd, |
596 except (OSError, input_api.subprocess.CalledProcessError), e: | 589 kwargs={'env': env, 'cwd': cwd}, |
597 results.append(message_type('%s failed!\n%s' % (unit_test_name, e))) | 590 message=message_type)) |
598 return results | 591 return results |
599 | 592 |
600 | 593 |
| 594 def RunUnitTestsInDirectory(input_api, *args, **kwargs): |
| 595 """Run tests in a directory serially. |
| 596 |
| 597 For better performance, use GetUnitTestsInDirectory and then |
| 598 pass to input_api.RunTests. |
| 599 """ |
| 600 return input_api.RunTests( |
| 601 GetUnitTestsInDirectory(input_api, *args, **kwargs), False) |
| 602 |
| 603 |
| 604 def RunUnitTests(input_api, *args, **kwargs): |
| 605 """Run tests serially. |
| 606 |
| 607 For better performance, use GetUnitTests and then pass to |
| 608 input_api.RunTests. |
| 609 """ |
| 610 return input_api.RunTests(GetUnitTests(input_api, *args, **kwargs), False) |
| 611 |
| 612 |
| 613 def RunPythonUnitTests(input_api, *args, **kwargs): |
| 614 """Run python tests in a directory serially. |
| 615 |
| 616 DEPRECATED |
| 617 """ |
| 618 return input_api.RunTests( |
| 619 GetPythonUnitTests(input_api, *args, **kwargs), False) |
| 620 |
| 621 |
601 def _FetchAllFiles(input_api, white_list, black_list): | 622 def _FetchAllFiles(input_api, white_list, black_list): |
602 """Hack to fetch all files.""" | 623 """Hack to fetch all files.""" |
603 # We cannot use AffectedFiles here because we want to test every python | 624 # We cannot use AffectedFiles here because we want to test every python |
604 # file on each single python change. It's because a change in a python file | 625 # file on each single python change. It's because a change in a python file |
605 # can break another unmodified file. | 626 # can break another unmodified file. |
606 # Use code similar to InputApi.FilterSourceFile() | 627 # Use code similar to InputApi.FilterSourceFile() |
607 def Find(filepath, filters): | 628 def Find(filepath, filters): |
608 for item in filters: | 629 for item in filters: |
609 if input_api.re.match(item, filepath): | 630 if input_api.re.match(item, filepath): |
610 return True | 631 return True |
611 return False | 632 return False |
612 | 633 |
613 files = [] | 634 files = [] |
614 path_len = len(input_api.PresubmitLocalPath()) | 635 path_len = len(input_api.PresubmitLocalPath()) |
615 for dirpath, dirnames, filenames in input_api.os_walk( | 636 for dirpath, dirnames, filenames in input_api.os_walk( |
616 input_api.PresubmitLocalPath()): | 637 input_api.PresubmitLocalPath()): |
617 # Passes dirnames in black list to speed up search. | 638 # Passes dirnames in black list to speed up search. |
618 for item in dirnames[:]: | 639 for item in dirnames[:]: |
619 filepath = input_api.os_path.join(dirpath, item)[path_len + 1:] | 640 filepath = input_api.os_path.join(dirpath, item)[path_len + 1:] |
620 if Find(filepath, black_list): | 641 if Find(filepath, black_list): |
621 dirnames.remove(item) | 642 dirnames.remove(item) |
622 for item in filenames: | 643 for item in filenames: |
623 filepath = input_api.os_path.join(dirpath, item)[path_len + 1:] | 644 filepath = input_api.os_path.join(dirpath, item)[path_len + 1:] |
624 if Find(filepath, white_list) and not Find(filepath, black_list): | 645 if Find(filepath, white_list) and not Find(filepath, black_list): |
625 files.append(filepath) | 646 files.append(filepath) |
626 return files | 647 return files |
627 | 648 |
628 | 649 |
629 def RunPylint(input_api, output_api, white_list=None, black_list=None, | 650 def GetPylint(input_api, output_api, white_list=None, black_list=None, |
630 disabled_warnings=None, extra_paths_list=None): | 651 disabled_warnings=None, extra_paths_list=None): |
631 """Run pylint on python files. | 652 """Run pylint on python files. |
632 | 653 |
633 The default white_list enforces looking only at *.py files. | 654 The default white_list enforces looking only at *.py files. |
634 """ | 655 """ |
635 white_list = tuple(white_list or ('.*\.py$',)) | 656 white_list = tuple(white_list or ('.*\.py$',)) |
636 black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST) | 657 black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST) |
637 extra_paths_list = extra_paths_list or [] | 658 extra_paths_list = extra_paths_list or [] |
638 | 659 |
639 if input_api.is_committing: | 660 if input_api.is_committing: |
(...skipping 22 matching lines...) Expand all Loading... |
662 input_api.logging.info('Skipping pylint: no matching changes.') | 683 input_api.logging.info('Skipping pylint: no matching changes.') |
663 return [] | 684 return [] |
664 | 685 |
665 extra_args = ['--rcfile=%s' % input_api.os_path.join(_HERE, 'pylintrc')] | 686 extra_args = ['--rcfile=%s' % input_api.os_path.join(_HERE, 'pylintrc')] |
666 if disabled_warnings: | 687 if disabled_warnings: |
667 extra_args.extend(['-d', ','.join(disabled_warnings)]) | 688 extra_args.extend(['-d', ','.join(disabled_warnings)]) |
668 | 689 |
669 files = _FetchAllFiles(input_api, white_list, black_list) | 690 files = _FetchAllFiles(input_api, white_list, black_list) |
670 if not files: | 691 if not files: |
671 return [] | 692 return [] |
| 693 files.sort() |
672 | 694 |
673 input_api.logging.info('Running pylint on %d files', len(files)) | 695 input_api.logging.info('Running pylint on %d files', len(files)) |
674 input_api.logging.debug('Running pylint on: %s', files) | 696 input_api.logging.debug('Running pylint on: %s', files) |
675 # Copy the system path to the environment so pylint can find the right | 697 # Copy the system path to the environment so pylint can find the right |
676 # imports. | 698 # imports. |
677 env = input_api.environ.copy() | 699 env = input_api.environ.copy() |
678 import sys | 700 import sys |
679 env['PYTHONPATH'] = input_api.os_path.pathsep.join( | 701 env['PYTHONPATH'] = input_api.os_path.pathsep.join( |
680 extra_paths_list + sys.path).encode('utf8') | 702 extra_paths_list + sys.path).encode('utf8') |
681 | 703 |
682 def run_lint(files): | 704 def GetPylintCmd(files): |
683 # We can't import pylint directly due to licensing issues, so we run | 705 # Windows needs help running python files so we explicitly specify |
684 # it in another process. Windows needs help running python files so we | 706 # the interpreter to use. It also has limitations on the size of |
685 # explicitly specify the interpreter to use. It also has limitations on | 707 # the command-line, so we pass arguments via a pipe. |
686 # the size of the command-line, so we pass arguments via a pipe. | 708 if len(files) == 1: |
687 command = [input_api.python_executable, | 709 description = files[0] |
688 input_api.os_path.join(_HERE, 'third_party', 'pylint.py'), | 710 else: |
689 '--args-on-stdin'] | 711 description = '%s files' % len(files) |
690 try: | |
691 child = input_api.subprocess.Popen(command, env=env, | |
692 stdin=input_api.subprocess.PIPE) | |
693 | 712 |
694 # Dump the arguments to the child process via a pipe. | 713 return input_api.Command( |
695 for filename in files: | 714 name='Pylint (%s)' % description, |
696 child.stdin.write(filename + '\n') | 715 cmd=[input_api.python_executable, |
697 for arg in extra_args: | 716 input_api.os_path.join(_HERE, 'third_party', 'pylint.py'), |
698 child.stdin.write(arg + '\n') | 717 '--args-on-stdin'], |
699 child.stdin.close() | 718 kwargs={'env': env, 'stdin': '\n'.join(files + extra_args)}, |
| 719 message=error_type) |
700 | 720 |
701 child.communicate() | |
702 return child.returncode | |
703 except OSError: | |
704 return 'Pylint failed!' | |
705 | |
706 result = None | |
707 # Always run pylint and pass it all the py files at once. | 721 # Always run pylint and pass it all the py files at once. |
708 # Passing py files one at time is slower and can produce | 722 # Passing py files one at time is slower and can produce |
709 # different results. input_api.verbose used to be used | 723 # different results. input_api.verbose used to be used |
710 # to enable this behaviour but differing behaviour in | 724 # to enable this behaviour but differing behaviour in |
711 # verbose mode is not desirable. | 725 # verbose mode is not desirable. |
712 # Leave this unreachable code in here so users can make | 726 # Leave this unreachable code in here so users can make |
713 # a quick local edit to diagnose pylint issues more | 727 # a quick local edit to diagnose pylint issues more |
714 # easily. | 728 # easily. |
715 if True: | 729 if True: |
716 print('Running pylint on %d files.' % len(files)) | 730 return [GetPylintCmd(files)] |
717 result = run_lint(sorted(files)) | |
718 else: | 731 else: |
719 for filename in sorted(files): | 732 return map(GetPylintCmd, files) |
720 print('Running pylint on %s' % filename) | 733 |
721 result = run_lint([filename]) or result | 734 |
722 if isinstance(result, basestring): | 735 def RunPylint(input_api, *args, **kwargs): |
723 return [error_type(result)] | 736 """Legacy presubmit function. |
724 elif result: | 737 |
725 return [error_type('Fix pylint errors first.')] | 738 For better performance, get all tests and then pass to |
726 return [] | 739 input_api.RunTests. |
| 740 """ |
| 741 return input_api.RunTests(GetPylint(input_api, *args, **kwargs), False) |
727 | 742 |
728 | 743 |
729 # TODO(dpranke): Get the host_url from the input_api instead | 744 # TODO(dpranke): Get the host_url from the input_api instead |
730 def CheckRietveldTryJobExecution(dummy_input_api, dummy_output_api, | 745 def CheckRietveldTryJobExecution(dummy_input_api, dummy_output_api, |
731 dummy_host_url, dummy_platforms, | 746 dummy_host_url, dummy_platforms, |
732 dummy_owner): | 747 dummy_owner): |
733 # Temporarily 'fix' the check while the Rietveld API is being upgraded to | 748 # Temporarily 'fix' the check while the Rietveld API is being upgraded to |
734 # something sensible. | 749 # something sensible. |
735 return [] | 750 return [] |
736 | 751 |
(...skipping 269 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1006 snapshot("checking description") | 1021 snapshot("checking description") |
1007 results.extend(input_api.canned_checks.CheckChangeHasDescription( | 1022 results.extend(input_api.canned_checks.CheckChangeHasDescription( |
1008 input_api, output_api)) | 1023 input_api, output_api)) |
1009 results.extend(input_api.canned_checks.CheckDoNotSubmitInDescription( | 1024 results.extend(input_api.canned_checks.CheckDoNotSubmitInDescription( |
1010 input_api, output_api)) | 1025 input_api, output_api)) |
1011 snapshot("checking do not submit in files") | 1026 snapshot("checking do not submit in files") |
1012 results.extend(input_api.canned_checks.CheckDoNotSubmitInFiles( | 1027 results.extend(input_api.canned_checks.CheckDoNotSubmitInFiles( |
1013 input_api, output_api)) | 1028 input_api, output_api)) |
1014 snapshot("done") | 1029 snapshot("done") |
1015 return results | 1030 return results |
OLD | NEW |