1
# Copyright (C) 2005, 2006 Canonical Ltd
2
# Authors: Robert Collins <robert.collins@canonical.com>
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
# GNU General Public License for more details.
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
"""These tests are tests about the source code of bzrlib itself.
20
They are useful for testing code quality, checking coverage metric etc.
23
# import system imports here
28
#import bzrlib specific imports here
33
from bzrlib.tests import TestCase, TestSkipped
36
# Files which are listed here will be skipped when testing for Copyright (or
38
COPYRIGHT_EXCEPTIONS = ['bzrlib/lsprof.py']
40
LICENSE_EXCEPTIONS = ['bzrlib/lsprof.py']
41
# Technically, 'bzrlib/lsprof.py' should be 'bzrlib/util/lsprof.py',
42
# (we do not check bzrlib/util/, since that is code bundled from elsewhere)
43
# but for compatibility with previous releases, we don't want to move it.
46
class TestSourceHelper(TestCase):
48
def source_file_name(self, package):
49
"""Return the path of the .py file for package."""
50
path = package.__file__
57
class TestApiUsage(TestSourceHelper):
59
def find_occurences(self, rule, filename):
60
"""Find the number of occurences of rule in a file."""
62
source = file(filename, 'r')
64
if line.find(rule) > -1:
68
def test_branch_working_tree(self):
69
"""Test that the number of uses of working_tree in branch is stable."""
70
occurences = self.find_occurences('self.working_tree()',
71
self.source_file_name(bzrlib.branch))
72
# do not even think of increasing this number. If you think you need to
73
# increase it, then you almost certainly are doing something wrong as
74
# the relationship from working_tree to branch is one way.
75
# Note that this is an exact equality so that when the number drops,
76
#it is not given a buffer but rather has this test updated immediately.
77
self.assertEqual(0, occurences)
79
def test_branch_WorkingTree(self):
80
"""Test that the number of uses of working_tree in branch is stable."""
81
occurences = self.find_occurences('WorkingTree',
82
self.source_file_name(bzrlib.branch))
83
# do not even think of increasing this number. If you think you need to
84
# increase it, then you almost certainly are doing something wrong as
85
# the relationship from working_tree to branch is one way.
86
# This number should be 4 (import NoWorkingTree and WorkingTree,
87
# raise NoWorkingTree from working_tree(), and construct a working tree
88
# there) but a merge that regressed this was done before this test was
89
# written. Note that this is an exact equality so that when the number
90
# drops, it is not given a buffer but rather this test updated
92
self.assertEqual(2, occurences)
95
class TestSource(TestSourceHelper):
97
def get_bzrlib_dir(self):
98
"""Get the path to the root of bzrlib"""
99
source = self.source_file_name(bzrlib)
100
source_dir = os.path.dirname(source)
102
# Avoid the case when bzrlib is packaged in a zip file
103
if not os.path.isdir(source_dir):
104
raise TestSkipped('Cannot find bzrlib source directory. Expected %s'
108
def get_source_files(self):
109
"""yield all source files for bzr and bzrlib"""
110
bzrlib_dir = self.get_bzrlib_dir()
112
# This is the front-end 'bzr' script
113
bzr_path = self.get_bzr_path()
116
for root, dirs, files in os.walk(bzrlib_dir):
118
if d.endswith('.tmp'):
121
if not f.endswith('.py'):
123
yield osutils.pathjoin(root, f)
125
def get_source_file_contents(self):
126
for fname in self.get_source_files():
127
f = open(fname, 'rb')
134
def is_copyright_exception(self, fname):
135
"""Certain files are allowed to be different"""
136
if '/util/' in fname or '/plugins/' in fname:
137
# We don't ask that external utilities or plugins be
141
for exc in COPYRIGHT_EXCEPTIONS:
142
if fname.endswith(exc):
147
def is_license_exception(self, fname):
148
"""Certain files are allowed to be different"""
149
if '/util/' in fname or '/plugins/' in fname:
150
# We don't ask that external utilities or plugins be
154
for exc in LICENSE_EXCEPTIONS:
155
if fname.endswith(exc):
160
def test_tmpdir_not_in_source_files(self):
161
"""When scanning for source files, we don't descend test tempdirs"""
162
for filename in self.get_source_files():
163
if re.search(r'test....\.tmp', filename):
164
self.fail("get_source_file() returned filename %r "
165
"from within a temporary directory"
168
def test_copyright(self):
169
"""Test that all .py files have a valid copyright statement"""
170
# These are files which contain a different copyright statement
174
copyright_re = re.compile('#\\s*copyright.*(?=\n)', re.I)
175
copyright_canonical_re = re.compile(
176
r'# Copyright \(C\) ' # Opening "# Copyright (C)"
177
r'(\d+)(, \d+)*' # Followed by a series of dates
178
r'.*Canonical Ltd' # And containing 'Canonical Ltd'
181
for fname, text in self.get_source_file_contents():
182
if self.is_copyright_exception(fname):
184
match = copyright_canonical_re.search(text)
186
match = copyright_re.search(text)
188
incorrect.append((fname, 'found: %s' % (match.group(),)))
190
incorrect.append((fname, 'no copyright line found\n'))
192
if 'by Canonical' in match.group():
193
incorrect.append((fname,
194
'should not have: "by Canonical": %s'
198
help_text = ["Some files have missing or incorrect copyright"
201
"Please either add them to the list of"
202
" COPYRIGHT_EXCEPTIONS in"
203
" bzrlib/tests/test_source.py",
204
# this is broken to prevent a false match
205
"or add '# Copyright (C)"
206
" 2007 Canonical Ltd' to these files:",
209
for fname, comment in incorrect:
210
help_text.append(fname)
211
help_text.append((' '*4) + comment)
213
self.fail('\n'.join(help_text))
216
"""Test that all .py files have a GPL disclaimer"""
220
# This program is free software; you can redistribute it and/or modify
221
# it under the terms of the GNU General Public License as published by
222
# the Free Software Foundation; either version 2 of the License, or
223
# (at your option) any later version.
225
# This program is distributed in the hope that it will be useful,
226
# but WITHOUT ANY WARRANTY; without even the implied warranty of
227
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
228
# GNU General Public License for more details.
230
# You should have received a copy of the GNU General Public License
231
# along with this program; if not, write to the Free Software
232
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
234
gpl_re = re.compile(re.escape(gpl_txt), re.MULTILINE)
236
for fname, text in self.get_source_file_contents():
237
if self.is_license_exception(fname):
239
if not gpl_re.search(text):
240
incorrect.append(fname)
243
help_text = ['Some files have missing or incomplete GPL statement',
245
"Please either add them to the list of"
246
" LICENSE_EXCEPTIONS in"
247
" bzrlib/tests/test_source.py",
248
"Or add the following text to the beginning:",
251
for fname in incorrect:
252
help_text.append((' '*4) + fname)
254
self.fail('\n'.join(help_text))
256
def test_no_tabs(self):
257
"""bzrlib source files should not contain any tab characters."""
260
for fname, text in self.get_source_file_contents():
261
if '/util/' in fname or '/plugins/' in fname:
264
incorrect.append(fname)
267
self.fail('Tab characters were found in the following source files.'
268
'\nThey should either be replaced by "\\t" or by spaces:'
270
% ('\n '.join(incorrect)))