ed6a750de3238a5db5a49d3571fafd022414391b
[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 import fnmatch
6 import json
7 import os
8 import re
9 import subprocess
10 import sys
11
12 # 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.
14 def _ReportErrorFileAndLine(filename, line_num, dummy_line):
15   """Default error formatter for _FindNewViolationsOfRule."""
16   return '%s:%s' % (filename, line_num)
17
18
19 class 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
53
54
55 class MockInputApi(object):
56   """Mock class for the InputApi class.
57
58   This class can be used for unittests for presubmit by initializing the files
59   attribute as the list of changed files.
60   """
61
62   DEFAULT_BLACK_LIST = ()
63
64   def __init__(self):
65     self.canned_checks = MockCannedChecks()
66     self.fnmatch = fnmatch
67     self.json = json
68     self.re = re
69     self.os_path = os.path
70     self.platform = sys.platform
71     self.python_executable = sys.executable
72     self.platform = sys.platform
73     self.subprocess = subprocess
74     self.files = []
75     self.is_committing = False
76     self.change = MockChange([])
77     self.presubmit_local_path = os.path.dirname(__file__)
78
79   def CreateMockFileInPath(self, f_list):
80     self.os_path.exists = lambda x: x in f_list
81
82   def AffectedFiles(self, file_filter=None, include_deletes=False):
83     for file in self.files:
84       if file_filter and not file_filter(file):
85         continue
86       if not include_deletes and file.Action() == 'D':
87         continue
88       yield file
89
90   def AffectedSourceFiles(self, file_filter=None):
91     return self.AffectedFiles(file_filter=file_filter)
92
93   def FilterSourceFile(self, file, white_list=(), black_list=()):
94     local_path = file.LocalPath()
95     found_in_white_list = not white_list
96     if white_list:
97       if type(white_list) is str:
98         raise TypeError('white_list should be an iterable of strings')
99       for pattern in white_list:
100         compiled_pattern = re.compile(pattern)
101         if compiled_pattern.search(local_path):
102           found_in_white_list = True
103           break
104     if black_list:
105       if type(black_list) is str:
106         raise TypeError('black_list should be an iterable of strings')
107       for pattern in black_list:
108         compiled_pattern = re.compile(pattern)
109         if compiled_pattern.search(local_path):
110           return False
111     return found_in_white_list
112
113   def LocalPaths(self):
114     return self.files
115
116   def PresubmitLocalPath(self):
117     return self.presubmit_local_path
118
119   def ReadFile(self, filename, mode='rU'):
120     if hasattr(filename, 'AbsoluteLocalPath'):
121        filename = filename.AbsoluteLocalPath()
122     for file_ in self.files:
123       if file_.LocalPath() == filename:
124         return '\n'.join(file_.NewContents())
125     # Otherwise, file is not in our mock API.
126     raise IOError, "No such file or directory: '%s'" % filename
127
128
129 class MockOutputApi(object):
130   """Mock class for the OutputApi class.
131
132   An instance of this class can be passed to presubmit unittests for outputing
133   various types of results.
134   """
135
136   class PresubmitResult(object):
137     def __init__(self, message, items=None, long_text=''):
138       self.message = message
139       self.items = items
140       self.long_text = long_text
141
142     def __repr__(self):
143       return self.message
144
145   class PresubmitError(PresubmitResult):
146     def __init__(self, message, items=None, long_text=''):
147       MockOutputApi.PresubmitResult.__init__(self, message, items, long_text)
148       self.type = 'error'
149
150   class PresubmitPromptWarning(PresubmitResult):
151     def __init__(self, message, items=None, long_text=''):
152       MockOutputApi.PresubmitResult.__init__(self, message, items, long_text)
153       self.type = 'warning'
154
155   class PresubmitNotifyResult(PresubmitResult):
156     def __init__(self, message, items=None, long_text=''):
157       MockOutputApi.PresubmitResult.__init__(self, message, items, long_text)
158       self.type = 'notify'
159
160   class PresubmitPromptOrNotify(PresubmitResult):
161     def __init__(self, message, items=None, long_text=''):
162       MockOutputApi.PresubmitResult.__init__(self, message, items, long_text)
163       self.type = 'promptOrNotify'
164
165   def __init__(self):
166     self.more_cc = []
167
168   def AppendCC(self, more_cc):
169     self.more_cc.extend(more_cc)
170
171
172 class MockFile(object):
173   """Mock class for the File class.
174
175   This class can be used to form the mock list of changed files in
176   MockInputApi for presubmit unittests.
177   """
178
179   def __init__(self, local_path, new_contents, old_contents=None, action='A'):
180     self._local_path = local_path
181     self._new_contents = new_contents
182     self._changed_contents = [(i + 1, l) for i, l in enumerate(new_contents)]
183     self._action = action
184     self._scm_diff = "--- /dev/null\n+++ %s\n@@ -0,0 +1,%d @@\n" % (local_path,
185       len(new_contents))
186     self._old_contents = old_contents
187     for l in new_contents:
188       self._scm_diff += "+%s\n" % l
189
190   def Action(self):
191     return self._action
192
193   def ChangedContents(self):
194     return self._changed_contents
195
196   def NewContents(self):
197     return self._new_contents
198
199   def LocalPath(self):
200     return self._local_path
201
202   def AbsoluteLocalPath(self):
203     return self._local_path
204
205   def GenerateScmDiff(self):
206     return self._scm_diff
207
208   def OldContents(self):
209     return self._old_contents
210
211   def rfind(self, p):
212     """os.path.basename is called on MockFile so we need an rfind method."""
213     return self._local_path.rfind(p)
214
215   def __getitem__(self, i):
216     """os.path.basename is called on MockFile so we need a get method."""
217     return self._local_path[i]
218
219   def __len__(self):
220     """os.path.basename is called on MockFile so we need a len method."""
221     return len(self._local_path)
222
223
224 class MockAffectedFile(MockFile):
225   def AbsoluteLocalPath(self):
226     return self._local_path
227
228
229 class MockChange(object):
230   """Mock class for Change class.
231
232   This class can be used in presubmit unittests to mock the query of the
233   current change.
234   """
235
236   def __init__(self, changed_files):
237     self._changed_files = changed_files
238
239   def LocalPaths(self):
240     return self._changed_files
241
242   def AffectedFiles(self, include_dirs=False, include_deletes=True,
243                     file_filter=None):
244     return self._changed_files