| 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 |