1
# Copyright (C) 2005 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 not f.endswith('.py'):
120
yield osutils.pathjoin(root, f)
122
def get_source_file_contents(self):
123
for fname in self.get_source_files():
124
f = open(fname, 'rb')
131
def is_copyright_exception(self, fname):
132
"""Certain files are allowed to be different"""
133
if '/util/' in fname or '/plugins/' in fname:
134
# We don't ask that external utilities or plugins be
138
for exc in COPYRIGHT_EXCEPTIONS:
139
if fname.endswith(exc):
144
def is_license_exception(self, fname):
145
"""Certain files are allowed to be different"""
146
if '/util/' in fname or '/plugins/' in fname:
147
# We don't ask that external utilities or plugins be
151
for exc in LICENSE_EXCEPTIONS:
152
if fname.endswith(exc):
157
def test_copyright(self):
158
"""Test that all .py files have a valid copyright statement"""
159
# These are files which contain a different copyright statement
163
copyright_re = re.compile('#\\s*copyright.*(?=\n)', re.I)
164
copyright_canonical_re = re.compile(
165
r'# Copyright \(C\) ' # Opening "# Copyright (C)"
166
r'(\d+)(, \d+)*' # Followed by a series of dates
167
r'.*Canonical Ltd' # And containing 'Canonical Ltd'
170
for fname, text in self.get_source_file_contents():
171
if self.is_copyright_exception(fname):
173
match = copyright_canonical_re.search(text)
175
match = copyright_re.search(text)
177
incorrect.append((fname, 'found: %s' % (match.group(),)))
179
incorrect.append((fname, 'no copyright line found\n'))
181
if 'by Canonical' in match.group():
182
incorrect.append((fname,
183
'should not have: "by Canonical": %s'
187
help_text = ["Some files have missing or incorrect copyright"
190
"Please either add them to the list of"
191
" COPYRIGHT_EXCEPTIONS in"
192
" bzrlib/tests/test_source.py",
193
# this is broken to prevent a false match
194
"or add '# Copyright (C)"
195
" 2006 Canonical Ltd' to these files:",
198
for fname, comment in incorrect:
199
help_text.append(fname)
200
help_text.append((' '*4) + comment)
202
self.fail('\n'.join(help_text))
205
"""Test that all .py files have a GPL disclaimer"""
209
# This program is free software; you can redistribute it and/or modify
210
# it under the terms of the GNU General Public License as published by
211
# the Free Software Foundation; either version 2 of the License, or
212
# (at your option) any later version.
214
# This program is distributed in the hope that it will be useful,
215
# but WITHOUT ANY WARRANTY; without even the implied warranty of
216
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
217
# GNU General Public License for more details.
219
# You should have received a copy of the GNU General Public License
220
# along with this program; if not, write to the Free Software
221
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
223
gpl_re = re.compile(re.escape(gpl_txt), re.MULTILINE)
225
for fname, text in self.get_source_file_contents():
226
if self.is_license_exception(fname):
228
if not gpl_re.search(text):
229
incorrect.append(fname)
232
help_text = ['Some files have missing or incomplete GPL statement',
234
"Please either add them to the list of"
235
" LICENSE_EXCEPTIONS in"
236
" bzrlib/tests/test_source.py",
237
"Or add the following text to the beginning:",
240
for fname in incorrect:
241
help_text.append((' '*4) + fname)
243
self.fail('\n'.join(help_text))