56
51
# but for compatibility with previous releases, we don't want to move it.
59
def check_coding_style(old_filename, oldlines, new_filename, newlines, to_file,
60
allow_binary=False, sequence_matcher=None,
61
path_encoding='utf8'):
62
"""text_differ to be passed to diff.DiffText, which checks code style """
63
if allow_binary is False:
64
textfile.check_text_lines(oldlines)
65
textfile.check_text_lines(newlines)
67
if sequence_matcher is None:
68
sequence_matcher = patiencediff.PatienceSequenceMatcher
70
started = [False] #trick to access parent scoped variable
71
def start_if_needed():
73
to_file.write('+++ %s\n' % new_filename)
76
def check_newlines(j1, j2):
77
for i, line in enumerate(newlines[j1:j2]):
78
bad_ws_match = re.match(r'^(([\t]*)(.*?)([\t ]*))(\r?\n)?$', line)
80
line_content = bad_ws_match.group(1)
81
has_leading_tabs = bool(bad_ws_match.group(2))
82
has_trailing_whitespace = bool(bad_ws_match.group(4))
85
to_file.write('line %i has leading tabs: "%s"\n'% (
86
i+1+j1, line_content))
87
if has_trailing_whitespace:
89
to_file.write('line %i has trailing whitespace: "%s"\n'% (
90
i+1+j1, line_content))
91
if len(line_content) > 79:
93
'\nFile %s\nline %i is longer than 79 characters:'
94
'\n"%s"'% (new_filename, i+1+j1, line_content))
96
for group in sequence_matcher(None, oldlines, newlines
97
).get_grouped_opcodes(0):
98
for tag, i1, i2, j1, j2 in group:
99
if tag == 'replace' or tag == 'insert':
100
check_newlines(j1, j2)
102
if len(newlines) == j2 and not newlines[j2-1].endswith('\n'):
104
to_file.write("\\ No newline at end of file\n")
107
54
class TestSourceHelper(TestCase):
109
56
def source_file_name(self, package):
293
245
# You should have received a copy of the GNU General Public License
294
246
# along with this program; if not, write to the Free Software
295
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
247
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
297
249
gpl_re = re.compile(re.escape(gpl_txt), re.MULTILINE)
299
for fname, text in self.get_source_file_contents():
251
for fname, text in self.get_source_file_contents(
252
extensions=('.py', '.pyx')):
300
253
if self.is_license_exception(fname):
302
255
if not gpl_re.search(text):
317
270
self.fail('\n'.join(help_text))
319
def test_no_tabs(self):
320
"""bzrlib source files should not contain any tab characters."""
323
for fname, text in self.get_source_file_contents():
324
if not self.is_our_code(fname):
327
incorrect.append(fname)
330
self.fail('Tab characters were found in the following source files.'
331
'\nThey should either be replaced by "\\t" or by spaces:'
333
% ('\n '.join(incorrect)))
272
def _push_file(self, dict_, fname, line_no):
273
if fname not in dict_:
274
dict_[fname] = [line_no]
276
dict_[fname].append(line_no)
278
def _format_message(self, dict_, message):
279
files = ["%s: %s" % (f, ', '.join([str(i+1) for i in lines]))
280
for f, lines in dict_.items()]
282
return message + '\n\n %s' % ('\n '.join(files))
335
284
def test_coding_style(self):
336
""" Check if bazaar code conforms to some coding style conventions.
285
"""Check if bazaar code conforms to some coding style conventions.
338
Currently we check all .py files for:
339
* new trailing white space
341
* new long lines (give warning only)
287
Currently we check for:
289
* trailing white space
342
291
* no newline at end of files
292
* lines longer than 79 chars
293
(only print how many files and lines are in violation)
344
bzr_dir = osutils.dirname(self.get_bzrlib_dir())
346
wt = WorkingTree.open(bzr_dir)
349
'Could not open bazaar working tree %s'
351
diff_output = StringIO()
355
old_tree = new_tree.basis_tree()
360
iterator = new_tree.iter_changes(old_tree)
361
for (file_id, paths, changed_content, versioned, parent,
362
name, kind, executable) in iterator:
363
if (changed_content and paths[1].endswith('.py')):
364
if kind == ('file', 'file'):
365
diff_text = diff.DiffText(old_tree, new_tree,
367
text_differ=check_coding_style)
368
diff_text.diff(file_id, paths[0], paths[1],
371
check_coding_style(name[0], (), name[1],
372
new_tree.get_file(file_id).readlines(),
379
if len(diff_output.getvalue()) > 0:
380
self.fail("Unacceptable coding style:\n" + diff_output.getvalue())
297
illegal_newlines = {}
299
no_newline_at_eof = []
300
for fname, text in self.get_source_file_contents(
301
extensions=('.py', '.pyx')):
302
if not self.is_our_code(fname):
304
lines = text.splitlines(True)
305
last_line_no = len(lines) - 1
306
for line_no, line in enumerate(lines):
308
self._push_file(tabs, fname, line_no)
309
if not line.endswith('\n') or line.endswith('\r\n'):
310
if line_no != last_line_no: # not no_newline_at_eof
311
self._push_file(illegal_newlines, fname, line_no)
312
if line.endswith(' \n'):
313
self._push_file(trailing_ws, fname, line_no)
315
self._push_file(long_lines, fname, line_no)
316
if not lines[-1].endswith('\n'):
317
no_newline_at_eof.append(fname)
320
problems.append(self._format_message(tabs,
321
'Tab characters were found in the following source files.'
322
'\nThey should either be replaced by "\\t" or by spaces:'))
324
problems.append(self._format_message(trailing_ws,
325
'Trailing white space was found in the following source files:'
328
problems.append(self._format_message(illegal_newlines,
329
'Non-unix newlines were found in the following source files:'))
331
print ("There are %i lines longer than 79 characters in %i files."
332
% (sum([len(lines) for f, lines in long_lines.items()]),
334
if no_newline_at_eof:
335
no_newline_at_eof.sort()
336
problems.append("The following source files doesn't have a "
337
"newline at the end:"
339
% ('\n '.join(no_newline_at_eof)))
341
raise KnownFailure("test_coding_style has failed")
342
self.fail('\n\n'.join(problems))
382
344
def test_no_asserts(self):
383
345
"""bzr shouldn't use the 'assert' statement."""