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
# As of 20070809, there are no longer any mentions at all.
87
self.assertEqual(0, occurences)
90
class TestSource(TestSourceHelper):
92
def get_bzrlib_dir(self):
93
"""Get the path to the root of bzrlib"""
94
source = self.source_file_name(bzrlib)
95
source_dir = os.path.dirname(source)
97
# Avoid the case when bzrlib is packaged in a zip file
98
if not os.path.isdir(source_dir):
99
raise TestSkipped('Cannot find bzrlib source directory. Expected %s'
103
def get_source_files(self):
104
"""yield all source files for bzr and bzrlib"""
105
bzrlib_dir = self.get_bzrlib_dir()
107
# This is the front-end 'bzr' script
108
bzr_path = self.get_bzr_path()
111
for root, dirs, files in os.walk(bzrlib_dir):
113
if d.endswith('.tmp'):
116
if not f.endswith('.py'):
118
yield osutils.pathjoin(root, f)
120
def get_source_file_contents(self):
121
for fname in self.get_source_files():
122
f = open(fname, 'rb')
129
def is_copyright_exception(self, fname):
130
"""Certain files are allowed to be different"""
131
if '/util/' in fname or '/plugins/' in fname:
132
# We don't ask that external utilities or plugins be
136
for exc in COPYRIGHT_EXCEPTIONS:
137
if fname.endswith(exc):
142
def is_license_exception(self, fname):
143
"""Certain files are allowed to be different"""
144
if '/util/' in fname or '/plugins/' in fname:
145
# We don't ask that external utilities or plugins be
149
for exc in LICENSE_EXCEPTIONS:
150
if fname.endswith(exc):
155
def test_tmpdir_not_in_source_files(self):
156
"""When scanning for source files, we don't descend test tempdirs"""
157
for filename in self.get_source_files():
158
if re.search(r'test....\.tmp', filename):
159
self.fail("get_source_file() returned filename %r "
160
"from within a temporary directory"
163
def test_copyright(self):
164
"""Test that all .py files have a valid copyright statement"""
165
# These are files which contain a different copyright statement
169
copyright_re = re.compile('#\\s*copyright.*(?=\n)', re.I)
170
copyright_canonical_re = re.compile(
171
r'# Copyright \(C\) ' # Opening "# Copyright (C)"
172
r'(\d+)(, \d+)*' # Followed by a series of dates
173
r'.*Canonical Ltd' # And containing 'Canonical Ltd'
176
for fname, text in self.get_source_file_contents():
177
if self.is_copyright_exception(fname):
179
match = copyright_canonical_re.search(text)
181
match = copyright_re.search(text)
183
incorrect.append((fname, 'found: %s' % (match.group(),)))
185
incorrect.append((fname, 'no copyright line found\n'))
187
if 'by Canonical' in match.group():
188
incorrect.append((fname,
189
'should not have: "by Canonical": %s'
193
help_text = ["Some files have missing or incorrect copyright"
196
"Please either add them to the list of"
197
" COPYRIGHT_EXCEPTIONS in"
198
" bzrlib/tests/test_source.py",
199
# this is broken to prevent a false match
200
"or add '# Copyright (C)"
201
" 2007 Canonical Ltd' to these files:",
204
for fname, comment in incorrect:
205
help_text.append(fname)
206
help_text.append((' '*4) + comment)
208
self.fail('\n'.join(help_text))
211
"""Test that all .py files have a GPL disclaimer"""
215
# This program is free software; you can redistribute it and/or modify
216
# it under the terms of the GNU General Public License as published by
217
# the Free Software Foundation; either version 2 of the License, or
218
# (at your option) any later version.
220
# This program is distributed in the hope that it will be useful,
221
# but WITHOUT ANY WARRANTY; without even the implied warranty of
222
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
223
# GNU General Public License for more details.
225
# You should have received a copy of the GNU General Public License
226
# along with this program; if not, write to the Free Software
227
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
229
gpl_re = re.compile(re.escape(gpl_txt), re.MULTILINE)
231
for fname, text in self.get_source_file_contents():
232
if self.is_license_exception(fname):
234
if not gpl_re.search(text):
235
incorrect.append(fname)
238
help_text = ['Some files have missing or incomplete GPL statement',
240
"Please either add them to the list of"
241
" LICENSE_EXCEPTIONS in"
242
" bzrlib/tests/test_source.py",
243
"Or add the following text to the beginning:",
246
for fname in incorrect:
247
help_text.append((' '*4) + fname)
249
self.fail('\n'.join(help_text))
251
def test_no_tabs(self):
252
"""bzrlib source files should not contain any tab characters."""
255
for fname, text in self.get_source_file_contents():
256
if '/util/' in fname or '/plugins/' in fname:
259
incorrect.append(fname)
262
self.fail('Tab characters were found in the following source files.'
263
'\nThey should either be replaced by "\\t" or by spaces:'
265
% ('\n '.join(incorrect)))