Upload upstream chromium 85.0.4183.84
[platform/framework/web/chromium-efl.git] / PRESUBMIT_test_mocks.py
1 # 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
5 from collections import defaultdict
6 import fnmatch
7 import json
8 import os
9 import re
10 import subprocess
11 import sys
12
13 # TODO(dcheng): It's kind of horrible that this is copy and pasted from
14 # presubmit_canned_checks.py, but it's far easier than any of the alternatives.
15 def _ReportErrorFileAndLine(filename, line_num, dummy_line):
16   """Default error formatter for _FindNewViolationsOfRule."""
17   return '%s:%s' % (filename, line_num)
18
19
20 class MockCannedChecks(object):
21   def _FindNewViolationsOfRule(self, callable_rule, input_api,
22                                source_file_filter=None,
23                                error_formatter=_ReportErrorFileAndLine):
24     """Find all newly introduced violations of a per-line rule (a callable).
25
26     Arguments:
27       callable_rule: a callable taking a file extension and line of input and
28         returning True if the rule is satisfied and False if there was a
29         problem.
30       input_api: object to enumerate the affected files.
31       source_file_filter: a filter to be passed to the input api.
32       error_formatter: a callable taking (filename, line_number, line) and
33         returning a formatted error string.
34
35     Returns:
36       A list of the newly-introduced violations reported by the rule.
37     """
38     errors = []
39     for f in input_api.AffectedFiles(include_deletes=False,
40                                      file_filter=source_file_filter):
41       # For speed, we do two passes, checking first the full file.  Shelling out
42       # to the SCM to determine the changed region can be quite expensive on
43       # Win32.  Assuming that most files will be kept problem-free, we can
44       # skip the SCM operations most of the time.
45       extension = str(f.LocalPath()).rsplit('.', 1)[-1]
46       if all(callable_rule(extension, line) for line in f.NewContents()):
47         continue  # No violation found in full text: can skip considering diff.
48
49       for line_num, line in f.ChangedContents():
50         if not callable_rule(extension, line):
51           errors.append(error_formatter(f.LocalPath(), line_num, line))
52
53     return errors
54
55
56 class MockInputApi(object):
57   """Mock class for the InputApi class.
58
59   This class can be used for unittests for presubmit by initializing the files
60   attribute as the list of changed files.
61   """
62
63   DEFAULT_BLACK_LIST = ()
64
65   def __init__(self):
66     self.canned_checks = MockCannedChecks()
67     self.fnmatch = fnmatch
68     self.json = json
69     self.re = re
70     self.os_path = os.path
71     self.platform = sys.platform
72     self.python_executable = sys.executable
73     self.platform = sys.platform
74     self.subprocess = subprocess
75     self.sys = sys
76     self.files = []
77     self.is_committing = False
78     self.change = MockChange([])
79     self.presubmit_local_path = os.path.dirname(__file__)
80
81   def CreateMockFileInPath(self, f_list):
82     self.os_path.exists = lambda x: x in f_list
83
84   def AffectedFiles(self, file_filter=None, include_deletes=False):
85     for file in self.files:
86       if file_filter and not file_filter(file):
87         continue
88       if not include_deletes and file.Action() == 'D':
89         continue
90       yield file
91
92   def AffectedSourceFiles(self, file_filter=None):
93     return self.AffectedFiles(file_filter=file_filter)
94
95   def FilterSourceFile(self, file, white_list=(), black_list=()):
96     local_path = file.LocalPath()
97     found_in_white_list = not white_list
98     if white_list:
99       if type(white_list) is str:
100         raise TypeError('white_list should be an iterable of strings')
101       for pattern in white_list:
102         compiled_pattern = re.compile(pattern)
103         if compiled_pattern.search(local_path):
104           found_in_white_list = True
105           break
106     if black_list:
107       if type(black_list) is str:
108         raise TypeError('black_list should be an iterable of strings')
109       for pattern in black_list:
110         compiled_pattern = re.compile(pattern)
111         if compiled_pattern.search(local_path):
112           return False
113     return found_in_white_list
114
115   def LocalPaths(self):
116     return [file.LocalPath() for file in self.files]
117
118   def PresubmitLocalPath(self):
119     return self.presubmit_local_path
120
121   def ReadFile(self, filename, mode='rU'):
122     if hasattr(filename, 'AbsoluteLocalPath'):
123        filename = filename.AbsoluteLocalPath()
124     for file_ in self.files:
125       if file_.LocalPath() == filename:
126         return '\n'.join(file_.NewContents())
127     # Otherwise, file is not in our mock API.
128     raise IOError, "No such file or directory: '%s'" % filename
129
130
131 class MockOutputApi(object):
132   """Mock class for the OutputApi class.
133
134   An instance of this class can be passed to presubmit unittests for outputing
135   various types of results.
136   """
137
138   class PresubmitResult(object):
139     def __init__(self, message, items=None, long_text=''):
140       self.message = message
141       self.items = items
142       self.long_text = long_text
143
144     def __repr__(self):
145       return self.message
146
147   class PresubmitError(PresubmitResult):
148     def __init__(self, message, items=None, long_text=''):
149       MockOutputApi.PresubmitResult.__init__(self, message, items, long_text)
150       self.type = 'error'
151
152   class PresubmitPromptWarning(PresubmitResult):
153     def __init__(self, message, items=None, long_text=''):
154       MockOutputApi.PresubmitResult.__init__(self, message, items, long_text)
155       self.type = 'warning'
156
157   class PresubmitNotifyResult(PresubmitResult):
158     def __init__(self, message, items=None, long_text=''):
159       MockOutputApi.PresubmitResult.__init__(self, message, items, long_text)
160       self.type = 'notify'
161
162   class PresubmitPromptOrNotify(PresubmitResult):
163     def __init__(self, message, items=None, long_text=''):
164       MockOutputApi.PresubmitResult.__init__(self, message, items, long_text)
165       self.type = 'promptOrNotify'
166
167   def __init__(self):
168     self.more_cc = []
169
170   def AppendCC(self, more_cc):
171     self.more_cc.extend(more_cc)
172
173
174 class MockFile(object):
175   """Mock class for the File class.
176
177   This class can be used to form the mock list of changed files in
178   MockInputApi for presubmit unittests.
179   """
180
181   def __init__(self, local_path, new_contents, old_contents=None, action='A'):
182     self._local_path = local_path
183     self._new_contents = new_contents
184     self._changed_contents = [(i + 1, l) for i, l in enumerate(new_contents)]
185     self._action = action
186     self._scm_diff = "--- /dev/null\n+++ %s\n@@ -0,0 +1,%d @@\n" % (local_path,
187       len(new_contents))
188     self._old_contents = old_contents
189     for l in new_contents:
190       self._scm_diff += "+%s\n" % l
191
192   def Action(self):
193     return self._action
194
195   def ChangedContents(self):
196     return self._changed_contents
197
198   def NewContents(self):
199     return self._new_contents
200
201   def LocalPath(self):
202     return self._local_path
203
204   def AbsoluteLocalPath(self):
205     return self._local_path
206
207   def GenerateScmDiff(self):
208     return self._scm_diff
209
210   def OldContents(self):
211     return self._old_contents
212
213   def rfind(self, p):
214     """os.path.basename is called on MockFile so we need an rfind method."""
215     return self._local_path.rfind(p)
216
217   def __getitem__(self, i):
218     """os.path.basename is called on MockFile so we need a get method."""
219     return self._local_path[i]
220
221   def __len__(self):
222     """os.path.basename is called on MockFile so we need a len method."""
223     return len(self._local_path)
224
225   def replace(self, altsep, sep):
226     """os.path.basename is called on MockFile so we need a replace method."""
227     return self._local_path.replace(altsep, sep)
228
229
230 class MockAffectedFile(MockFile):
231   def AbsoluteLocalPath(self):
232     return self._local_path
233
234
235 class MockChange(object):
236   """Mock class for Change class.
237
238   This class can be used in presubmit unittests to mock the query of the
239   current change.
240   """
241
242   def __init__(self, changed_files):
243     self._changed_files = changed_files
244     self.footers = defaultdict(list)
245
246   def LocalPaths(self):
247     return self._changed_files
248
249   def AffectedFiles(self, include_dirs=False, include_deletes=True,
250                     file_filter=None):
251     return self._changed_files
252
253   def GitFootersFromDescription(self):
254     return self.footers
255