[go: nahoru, domu]

blob: e15afcc9101a450580ed2077fa283f6d3cc730c3 [file] [log] [blame]
gayane3dff8c22014-12-04 17:09:511# Copyright 2014 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Daniel Cheng13ca61a882017-08-25 15:11:255import fnmatch
gayane3dff8c22014-12-04 17:09:516import json
7import os
8import re
9import subprocess
10import sys
11
Daniel Cheng264a447d2017-09-28 22:17:5912# TODO(dcheng): It's kind of horrible that this is copy and pasted from
13# presubmit_canned_checks.py, but it's far easier than any of the alternatives.
14def _ReportErrorFileAndLine(filename, line_num, dummy_line):
15 """Default error formatter for _FindNewViolationsOfRule."""
16 return '%s:%s' % (filename, line_num)
17
18
19class MockCannedChecks(object):
20 def _FindNewViolationsOfRule(self, callable_rule, input_api,
21 source_file_filter=None,
22 error_formatter=_ReportErrorFileAndLine):
23 """Find all newly introduced violations of a per-line rule (a callable).
24
25 Arguments:
26 callable_rule: a callable taking a file extension and line of input and
27 returning True if the rule is satisfied and False if there was a
28 problem.
29 input_api: object to enumerate the affected files.
30 source_file_filter: a filter to be passed to the input api.
31 error_formatter: a callable taking (filename, line_number, line) and
32 returning a formatted error string.
33
34 Returns:
35 A list of the newly-introduced violations reported by the rule.
36 """
37 errors = []
38 for f in input_api.AffectedFiles(include_deletes=False,
39 file_filter=source_file_filter):
40 # For speed, we do two passes, checking first the full file. Shelling out
41 # to the SCM to determine the changed region can be quite expensive on
42 # Win32. Assuming that most files will be kept problem-free, we can
43 # skip the SCM operations most of the time.
44 extension = str(f.LocalPath()).rsplit('.', 1)[-1]
45 if all(callable_rule(extension, line) for line in f.NewContents()):
46 continue # No violation found in full text: can skip considering diff.
47
48 for line_num, line in f.ChangedContents():
49 if not callable_rule(extension, line):
50 errors.append(error_formatter(f.LocalPath(), line_num, line))
51
52 return errors
gayane3dff8c22014-12-04 17:09:5153
54class MockInputApi(object):
55 """Mock class for the InputApi class.
56
57 This class can be used for unittests for presubmit by initializing the files
58 attribute as the list of changed files.
59 """
60
61 def __init__(self):
Daniel Cheng264a447d2017-09-28 22:17:5962 self.canned_checks = MockCannedChecks()
Daniel Cheng13ca61a882017-08-25 15:11:2563 self.fnmatch = fnmatch
gayane3dff8c22014-12-04 17:09:5164 self.json = json
65 self.re = re
66 self.os_path = os.path
agrievebb9c5b472016-04-22 15:13:0067 self.platform = sys.platform
gayane3dff8c22014-12-04 17:09:5168 self.python_executable = sys.executable
pastarmovj89f7ee12016-09-20 14:58:1369 self.platform = sys.platform
gayane3dff8c22014-12-04 17:09:5170 self.subprocess = subprocess
71 self.files = []
72 self.is_committing = False
gayanee1702662014-12-13 03:48:0973 self.change = MockChange([])
dpapad5c9c24e2017-05-31 20:51:3474 self.presubmit_local_path = os.path.dirname(__file__)
gayane3dff8c22014-12-04 17:09:5175
agrievef32bcc72016-04-04 14:57:4076 def AffectedFiles(self, file_filter=None, include_deletes=False):
gayane3dff8c22014-12-04 17:09:5177 return self.files
78
glidere61efad2015-02-18 17:39:4379 def AffectedSourceFiles(self, file_filter=None):
80 return self.files
81
davileene0426252015-03-02 21:10:4182 def LocalPaths(self):
83 return self.files
84
gayane3dff8c22014-12-04 17:09:5185 def PresubmitLocalPath(self):
dpapad5c9c24e2017-05-31 20:51:3486 return self.presubmit_local_path
gayane3dff8c22014-12-04 17:09:5187
88 def ReadFile(self, filename, mode='rU'):
glidere61efad2015-02-18 17:39:4389 if hasattr(filename, 'AbsoluteLocalPath'):
90 filename = filename.AbsoluteLocalPath()
gayane3dff8c22014-12-04 17:09:5191 for file_ in self.files:
92 if file_.LocalPath() == filename:
93 return '\n'.join(file_.NewContents())
94 # Otherwise, file is not in our mock API.
95 raise IOError, "No such file or directory: '%s'" % filename
96
97
98class MockOutputApi(object):
gayane860db5c2014-12-05 16:16:4699 """Mock class for the OutputApi class.
gayane3dff8c22014-12-04 17:09:51100
101 An instance of this class can be passed to presubmit unittests for outputing
102 various types of results.
103 """
104
105 class PresubmitResult(object):
106 def __init__(self, message, items=None, long_text=''):
107 self.message = message
108 self.items = items
109 self.long_text = long_text
110
gayane940df072015-02-24 14:28:30111 def __repr__(self):
112 return self.message
113
gayane3dff8c22014-12-04 17:09:51114 class PresubmitError(PresubmitResult):
davileene0426252015-03-02 21:10:41115 def __init__(self, message, items=None, long_text=''):
gayane3dff8c22014-12-04 17:09:51116 MockOutputApi.PresubmitResult.__init__(self, message, items, long_text)
117 self.type = 'error'
118
119 class PresubmitPromptWarning(PresubmitResult):
davileene0426252015-03-02 21:10:41120 def __init__(self, message, items=None, long_text=''):
gayane3dff8c22014-12-04 17:09:51121 MockOutputApi.PresubmitResult.__init__(self, message, items, long_text)
122 self.type = 'warning'
123
124 class PresubmitNotifyResult(PresubmitResult):
davileene0426252015-03-02 21:10:41125 def __init__(self, message, items=None, long_text=''):
gayane3dff8c22014-12-04 17:09:51126 MockOutputApi.PresubmitResult.__init__(self, message, items, long_text)
127 self.type = 'notify'
128
129 class PresubmitPromptOrNotify(PresubmitResult):
davileene0426252015-03-02 21:10:41130 def __init__(self, message, items=None, long_text=''):
gayane3dff8c22014-12-04 17:09:51131 MockOutputApi.PresubmitResult.__init__(self, message, items, long_text)
132 self.type = 'promptOrNotify'
133
Daniel Cheng7052cdf2017-11-21 19:23:29134 def __init__(self):
135 self.more_cc = []
136
137 def AppendCC(self, more_cc):
138 self.more_cc.extend(more_cc)
139
gayane3dff8c22014-12-04 17:09:51140
141class MockFile(object):
142 """Mock class for the File class.
143
144 This class can be used to form the mock list of changed files in
145 MockInputApi for presubmit unittests.
146 """
147
Yoland Yanb92fa522017-08-28 17:37:06148 def __init__(self, local_path, new_contents, old_contents=None, action='A'):
gayane3dff8c22014-12-04 17:09:51149 self._local_path = local_path
150 self._new_contents = new_contents
151 self._changed_contents = [(i + 1, l) for i, l in enumerate(new_contents)]
agrievef32bcc72016-04-04 14:57:40152 self._action = action
jbriance9e12f162016-11-25 07:57:50153 self._scm_diff = "--- /dev/null\n+++ %s\n@@ -0,0 +1,%d @@\n" % (local_path,
154 len(new_contents))
Yoland Yanb92fa522017-08-28 17:37:06155 self._old_contents = old_contents
jbriance9e12f162016-11-25 07:57:50156 for l in new_contents:
157 self._scm_diff += "+%s\n" % l
gayane3dff8c22014-12-04 17:09:51158
dbeam37e8e7402016-02-10 22:58:20159 def Action(self):
agrievef32bcc72016-04-04 14:57:40160 return self._action
dbeam37e8e7402016-02-10 22:58:20161
gayane3dff8c22014-12-04 17:09:51162 def ChangedContents(self):
163 return self._changed_contents
164
165 def NewContents(self):
166 return self._new_contents
167
168 def LocalPath(self):
169 return self._local_path
170
rdevlin.cronin9ab806c2016-02-26 23:17:13171 def AbsoluteLocalPath(self):
172 return self._local_path
173
jbriance9e12f162016-11-25 07:57:50174 def GenerateScmDiff(self):
jbriance2c51e821a2016-12-12 08:24:31175 return self._scm_diff
jbriance9e12f162016-11-25 07:57:50176
Yoland Yanb92fa522017-08-28 17:37:06177 def OldContents(self):
178 return self._old_contents
179
davileene0426252015-03-02 21:10:41180 def rfind(self, p):
181 """os.path.basename is called on MockFile so we need an rfind method."""
182 return self._local_path.rfind(p)
183
184 def __getitem__(self, i):
185 """os.path.basename is called on MockFile so we need a get method."""
186 return self._local_path[i]
187
pastarmovj89f7ee12016-09-20 14:58:13188 def __len__(self):
189 """os.path.basename is called on MockFile so we need a len method."""
190 return len(self._local_path)
191
gayane3dff8c22014-12-04 17:09:51192
glidere61efad2015-02-18 17:39:43193class MockAffectedFile(MockFile):
194 def AbsoluteLocalPath(self):
195 return self._local_path
196
197
gayane3dff8c22014-12-04 17:09:51198class MockChange(object):
199 """Mock class for Change class.
200
201 This class can be used in presubmit unittests to mock the query of the
202 current change.
203 """
204
205 def __init__(self, changed_files):
206 self._changed_files = changed_files
207
208 def LocalPaths(self):
209 return self._changed_files
rdevlin.cronin11366822016-05-02 17:05:54210
211 def AffectedFiles(self, include_dirs=False, include_deletes=True,
212 file_filter=None):
213 return self._changed_files