diff --git a/.gitignore b/.gitignore index 617c6ca5e..277fda9db 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *.pyc /git_cl_repo /tests/pymox +/tests/rietveld /tests/_trial /python /python.bat diff --git a/presubmit_canned_checks.py b/presubmit_canned_checks.py index 6fb954f1f..45f446c58 100644 --- a/presubmit_canned_checks.py +++ b/presubmit_canned_checks.py @@ -410,9 +410,76 @@ def CheckTreeIsOpen(input_api, output_api, return [] +def RunUnitTestsInDirectory( + input_api, output_api, directory, whitelist=None, blacklist=None, + verbose=False): + """Lists all files in a directory and runs them. Doesn't recurse. + + It's mainly a wrapper for RunUnitTests. USe whitelist and blacklist to filter + tests accordingly. + """ + unit_tests = [] + test_path = input_api.os_path.abspath( + input_api.os_path.join(input_api.PresubmitLocalPath(), directory)) + + def check(filename, filters): + return any(True for i in filters if input_api.re.match(i, filename)) + + for filename in input_api.os_listdir(test_path): + fullpath = input_api.os_path.join(test_path, filename) + if not input_api.os_path.isfile(fullpath): + continue + if whitelist and not check(filename, whitelist): + continue + if blacklist and check(filename, blacklist): + continue + unit_tests.append(input_api.os_path.join(directory, filename)) + return RunUnitTests(input_api, output_api, unit_tests, verbose) + + +def RunUnitTests(input_api, output_api, unit_tests, verbose=False): + """Runs all unit tests in a directory. + + On Windows, sys.executable is used for unit tests ending with ".py". + """ + # We don't want to hinder users from uploading incomplete patches. + if input_api.is_committing: + message_type = output_api.PresubmitError + else: + message_type = output_api.PresubmitPromptWarning + + if verbose: + pipe = None + else: + pipe = input_api.subprocess.PIPE + + results = [] + for unit_test in unit_tests: + cmd = [] + if input_api.platform == 'win32' and unit_test.endswith('.py'): + # Windows needs some help. + cmd = [input_api.python_executable] + cmd.append(unit_test) + if verbose: + print('Running %s' % unit_test) + try: + proc = input_api.subprocess.Popen( + cmd, cwd=input_api.PresubmitLocalPath(), stdout=pipe, stderr=pipe) + out = '\n'.join(filter(None, proc.communicate())) + if proc.returncode: + results.append(message_type( + '%s failed with return code %d\n%s' % ( + unit_test, proc.returncode, out))) + except (OSError, input_api.subprocess.CalledProcessError): + results.append(message_type('%s failed' % unit_test)) + return results + + def RunPythonUnitTests(input_api, output_api, unit_tests): """Run the unit tests out of process, capture the output and use the result code to determine success. + + DEPRECATED. """ # We don't want to hinder users from uploading incomplete patches. if input_api.is_committing: @@ -477,10 +544,10 @@ def _FetchAllFiles(input_api, white_list, black_list): return True return False - import os files = [] path_len = len(input_api.PresubmitLocalPath()) - for dirpath, dirnames, filenames in os.walk(input_api.PresubmitLocalPath()): + for dirpath, dirnames, filenames in input_api.os_walk( + input_api.PresubmitLocalPath()): # Passes dirnames in black list to speed up search. for item in dirnames[:]: filepath = input_api.os_path.join(dirpath, item)[path_len + 1:] diff --git a/presubmit_support.py b/presubmit_support.py index c8b4a460c..5a003eba0 100755 --- a/presubmit_support.py +++ b/presubmit_support.py @@ -6,7 +6,7 @@ """Enables directory-specific presubmit checks to run at upload and/or commit. """ -__version__ = '1.4' +__version__ = '1.5' # TODO(joi) Add caching where appropriate/needed. The API is designed to allow # caching (between all different invocations of presubmit scripts for a given @@ -251,6 +251,8 @@ class InputApi(object): self.cPickle = cPickle self.cStringIO = cStringIO self.json = json + self.os_listdir = os.listdir + self.os_walk = os.walk self.os_path = os.path self.pickle = pickle self.marshal = marshal diff --git a/tests/presubmit_unittest.py b/tests/presubmit_unittest.py index b420a59e9..76e5b2f40 100755 --- a/tests/presubmit_unittest.py +++ b/tests/presubmit_unittest.py @@ -1,5 +1,5 @@ -#!/usr/bin/python -# Copyright (c) 2010 The Chromium Authors. All rights reserved. +#!/usr/bin/env python2.5 +# Copyright (c) 2011 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -9,6 +9,7 @@ # pylint: disable=E1101,E1103,W0212,W0403 import StringIO +import sys # Fixes include path. from super_mox import mox, SuperMoxTestBase @@ -726,8 +727,8 @@ class InputApiUnittest(PresubmitTestsBase): 'LocalToDepotPath', 'PresubmitLocalPath', 'ReadFile', 'RightHandSideLines', 'ServerPaths', 'basename', 'cPickle', 'cStringIO', 'canned_checks', 'change', 'environ', - 'host_url', 'is_committing', 'json', 'marshal', 'os_path', - 'owners_db', 'pickle', 'platform', 'python_executable', 're', + 'host_url', 'is_committing', 'json', 'marshal', 'os_listdir', 'os_walk', + 'os_path', 'owners_db', 'pickle', 'platform', 'python_executable', 're', 'subprocess', 'tbr', 'tempfile', 'time', 'traceback', 'unittest', 'urllib2', 'version', ] @@ -1237,6 +1238,8 @@ class CannedChecksUnittest(PresubmitTestsBase): def MockInputApi(self, change, committing): input_api = self.mox.CreateMock(presubmit.InputApi) input_api.cStringIO = presubmit.cStringIO + input_api.os_listdir = self.mox.CreateMockAnything() + input_api.os_walk = self.mox.CreateMockAnything() input_api.os_path = presubmit.os.path input_api.re = presubmit.re input_api.traceback = presubmit.traceback @@ -1249,6 +1252,7 @@ class CannedChecksUnittest(PresubmitTestsBase): input_api.is_committing = committing input_api.tbr = False input_api.python_executable = 'pyyyyython' + input_api.platform = sys.platform return input_api def testMembersChanged(self): @@ -1273,6 +1277,7 @@ class CannedChecksUnittest(PresubmitTestsBase): 'CheckSvnModifiedDirectories', 'CheckSvnForCommonMimeTypes', 'CheckSvnProperty', 'RunPythonUnitTests', 'RunPylint', + 'RunUnitTests', 'RunUnitTestsInDirectory', ] # If this test fails, you should add the relevant test. self.compareMembers(presubmit_canned_checks, members) @@ -2010,6 +2015,60 @@ mac|success|blew self.AssertOwnersWorks(approvers=set(['ben@example.com']), uncovered_files=set()) + def testCannedRunUnitTests(self): + change = presubmit.Change( + 'foo1', 'description1', self.fake_root_dir, None, 0, 0) + input_api = self.MockInputApi(change, False) + unit_tests = ['allo', 'bar.py'] + input_api.PresubmitLocalPath().AndReturn(self.fake_root_dir) + input_api.PresubmitLocalPath().AndReturn(self.fake_root_dir) + proc1 = self.mox.CreateMockAnything() + input_api.subprocess.Popen(['allo'], cwd=self.fake_root_dir, + stderr=None, stdout=None).AndReturn(proc1) + proc1.returncode = 0 + proc1.communicate().AndReturn(['baz', None]) + proc2 = self.mox.CreateMockAnything() + input_api.subprocess.Popen(['bar.py'], cwd=self.fake_root_dir, + stderr=None, stdout=None).AndReturn(proc2) + proc2.returncode = 1 + proc2.communicate().AndReturn(['bouz', None]) + + self.mox.ReplayAll() + results = presubmit_canned_checks.RunUnitTests( + input_api, + presubmit.OutputApi, + unit_tests, + verbose=True) + self.assertEqual(1, len(results)) + self.assertEqual( + presubmit.OutputApi.PresubmitPromptWarning, results[0].__class__) + self.checkstdout('Running allo\nRunning bar.py\n') + + def testCannedRunUnitTestsInDirectory(self): + change = presubmit.Change( + 'foo1', 'description1', self.fake_root_dir, None, 0, 0) + input_api = self.MockInputApi(change, False) + input_api.PresubmitLocalPath().AndReturn(self.fake_root_dir) + input_api.PresubmitLocalPath().AndReturn(self.fake_root_dir) + path = presubmit.os.path.join(self.fake_root_dir, 'random_directory') + input_api.os_listdir(path).AndReturn(['.', '..', 'a', 'b', 'c']) + input_api.os_path.isfile = lambda x: not x.endswith('.') + proc1 = self.mox.CreateMockAnything() + input_api.subprocess.Popen(['random_directory/b'], cwd=self.fake_root_dir, + stderr=None, stdout=None).AndReturn(proc1) + proc1.returncode = 0 + proc1.communicate().AndReturn(['baz', None]) + + self.mox.ReplayAll() + results = presubmit_canned_checks.RunUnitTestsInDirectory( + input_api, + presubmit.OutputApi, + 'random_directory', + whitelist=['^a$', '^b$'], + blacklist=['a'], + verbose=True) + self.assertEqual(results, []) + self.checkstdout('Running random_directory/b\n') if __name__ == '__main__':