/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
3376.2.1 by Martin Pool
Add test_no_asserts and cleanup test_source
1
# Copyright (C) 2005, 2006, 2008 Canonical Ltd
1523 by Robert Collins
Test for the number of uses of self.working_tree() in branch.py
2
#   Authors: Robert Collins <robert.collins@canonical.com>
3376.2.1 by Martin Pool
Add test_no_asserts and cleanup test_source
3
#            and others
1523 by Robert Collins
Test for the number of uses of self.working_tree() in branch.py
4
#
5
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 2 of the License, or
8
# (at your option) any later version.
9
#
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, write to the Free Software
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
19
"""These tests are tests about the source code of bzrlib itself.
20
21
They are useful for testing code quality, checking coverage metric etc.
22
"""
23
24
# import system imports here
25
import os
3376.2.1 by Martin Pool
Add test_no_asserts and cleanup test_source
26
import parser
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
27
import re
3376.2.1 by Martin Pool
Add test_no_asserts and cleanup test_source
28
import symbol
1523 by Robert Collins
Test for the number of uses of self.working_tree() in branch.py
29
import sys
3376.2.1 by Martin Pool
Add test_no_asserts and cleanup test_source
30
import token
1523 by Robert Collins
Test for the number of uses of self.working_tree() in branch.py
31
32
#import bzrlib specific imports here
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
33
from bzrlib import (
34
    osutils,
35
    )
1523 by Robert Collins
Test for the number of uses of self.working_tree() in branch.py
36
import bzrlib.branch
3376.2.1 by Martin Pool
Add test_no_asserts and cleanup test_source
37
from bzrlib.tests import (
38
    TestCase,
39
    TestSkipped,
40
    )
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
41
42
2052.3.8 by John Arbash Meinel
Better documentation about the exception variables
43
# Files which are listed here will be skipped when testing for Copyright (or
44
# GPL) statements.
2052.3.5 by John Arbash Meinel
Guide people to how to add files to the list of exceptions
45
COPYRIGHT_EXCEPTIONS = ['bzrlib/lsprof.py']
46
47
LICENSE_EXCEPTIONS = ['bzrlib/lsprof.py']
2052.3.8 by John Arbash Meinel
Better documentation about the exception variables
48
# Technically, 'bzrlib/lsprof.py' should be 'bzrlib/util/lsprof.py',
49
# (we do not check bzrlib/util/, since that is code bundled from elsewhere)
50
# but for compatibility with previous releases, we don't want to move it.
2052.3.5 by John Arbash Meinel
Guide people to how to add files to the list of exceptions
51
3572.1.7 by Marius Kruger
Code style and minor changes as per review.
52
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
53
class TestSourceHelper(TestCase):
54
55
    def source_file_name(self, package):
56
        """Return the path of the .py file for package."""
3616.2.7 by Mark Hammond
prefer getattr() over hasattr()
57
        if getattr(sys, "frozen", None) is not None:
3616.2.5 by Mark Hammond
don't try and test source code when we are frozen.
58
            raise TestSkipped("can't test sources in frozen distributions.")
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
59
        path = package.__file__
60
        if path[-1] in 'co':
61
            return path[:-1]
62
        else:
63
            return path
64
65
66
class TestApiUsage(TestSourceHelper):
1523 by Robert Collins
Test for the number of uses of self.working_tree() in branch.py
67
1524 by Robert Collins
Test the uses of WorkingTree from branch.py
68
    def find_occurences(self, rule, filename):
69
        """Find the number of occurences of rule in a file."""
1523 by Robert Collins
Test for the number of uses of self.working_tree() in branch.py
70
        occurences = 0
1524 by Robert Collins
Test the uses of WorkingTree from branch.py
71
        source = file(filename, 'r')
1523 by Robert Collins
Test for the number of uses of self.working_tree() in branch.py
72
        for line in source:
1524 by Robert Collins
Test the uses of WorkingTree from branch.py
73
            if line.find(rule) > -1:
1523 by Robert Collins
Test for the number of uses of self.working_tree() in branch.py
74
                occurences += 1
1524 by Robert Collins
Test the uses of WorkingTree from branch.py
75
        return occurences
76
77
    def test_branch_working_tree(self):
78
        """Test that the number of uses of working_tree in branch is stable."""
79
        occurences = self.find_occurences('self.working_tree()',
1526 by Robert Collins
Bugfix to source testing logic to get the right path when .py is returned by __file__
80
                                          self.source_file_name(bzrlib.branch))
1523 by Robert Collins
Test for the number of uses of self.working_tree() in branch.py
81
        # do not even think of increasing this number. If you think you need to
82
        # increase it, then you almost certainly are doing something wrong as
83
        # the relationship from working_tree to branch is one way.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
84
        # Note that this is an exact equality so that when the number drops,
1534.4.35 by Robert Collins
Give branch its own basis tree and last_revision methods; deprecated branch.working_tree()
85
        #it is not given a buffer but rather has this test updated immediately.
86
        self.assertEqual(0, occurences)
1524 by Robert Collins
Test the uses of WorkingTree from branch.py
87
88
    def test_branch_WorkingTree(self):
89
        """Test that the number of uses of working_tree in branch is stable."""
90
        occurences = self.find_occurences('WorkingTree',
1526 by Robert Collins
Bugfix to source testing logic to get the right path when .py is returned by __file__
91
                                          self.source_file_name(bzrlib.branch))
2696.1.1 by Martin Pool
Remove things deprecated in 0.11 and earlier
92
        # Do not even think of increasing this number. If you think you need to
1524 by Robert Collins
Test the uses of WorkingTree from branch.py
93
        # increase it, then you almost certainly are doing something wrong as
94
        # the relationship from working_tree to branch is one way.
2696.1.1 by Martin Pool
Remove things deprecated in 0.11 and earlier
95
        # As of 20070809, there are no longer any mentions at all.
96
        self.assertEqual(0, occurences)
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
97
98
99
class TestSource(TestSourceHelper):
100
101
    def get_bzrlib_dir(self):
102
        """Get the path to the root of bzrlib"""
103
        source = self.source_file_name(bzrlib)
104
        source_dir = os.path.dirname(source)
105
106
        # Avoid the case when bzrlib is packaged in a zip file
107
        if not os.path.isdir(source_dir):
108
            raise TestSkipped('Cannot find bzrlib source directory. Expected %s'
109
                              % source_dir)
110
        return source_dir
111
112
    def get_source_files(self):
3376.2.1 by Martin Pool
Add test_no_asserts and cleanup test_source
113
        """Yield all source files for bzr and bzrlib
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
114
3376.2.1 by Martin Pool
Add test_no_asserts and cleanup test_source
115
        :param our_files_only: If true, exclude files from included libraries
116
            or plugins.
117
        """
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
118
        bzrlib_dir = self.get_bzrlib_dir()
119
120
        # This is the front-end 'bzr' script
121
        bzr_path = self.get_bzr_path()
122
        yield bzr_path
123
124
        for root, dirs, files in os.walk(bzrlib_dir):
2102.3.1 by mbp at sourcefrog
test_source should avoid walking into tempdirs
125
            for d in dirs:
126
                if d.endswith('.tmp'):
127
                    dirs.remove(d)
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
128
            for f in files:
129
                if not f.endswith('.py'):
130
                    continue
131
                yield osutils.pathjoin(root, f)
132
133
    def get_source_file_contents(self):
134
        for fname in self.get_source_files():
135
            f = open(fname, 'rb')
136
            try:
137
                text = f.read()
138
            finally:
139
                f.close()
140
            yield fname, text
141
3376.2.1 by Martin Pool
Add test_no_asserts and cleanup test_source
142
    def is_our_code(self, fname):
143
        """Return true if it's a "real" part of bzrlib rather than external code"""
144
        if '/util/' in fname or '/plugins/' in fname:
145
            return False
146
        else:
147
            return True
148
2052.3.5 by John Arbash Meinel
Guide people to how to add files to the list of exceptions
149
    def is_copyright_exception(self, fname):
150
        """Certain files are allowed to be different"""
3376.2.1 by Martin Pool
Add test_no_asserts and cleanup test_source
151
        if not self.is_our_code(fname):
2084.1.5 by John Arbash Meinel
Don't check plugins for copyright or license
152
            # We don't ask that external utilities or plugins be
153
            # (C) Canonical Ltd
2052.3.5 by John Arbash Meinel
Guide people to how to add files to the list of exceptions
154
            return True
155
        for exc in COPYRIGHT_EXCEPTIONS:
156
            if fname.endswith(exc):
157
                return True
158
        return False
159
160
    def is_license_exception(self, fname):
161
        """Certain files are allowed to be different"""
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
162
        if not self.is_our_code(fname):
2052.3.5 by John Arbash Meinel
Guide people to how to add files to the list of exceptions
163
            return True
164
        for exc in LICENSE_EXCEPTIONS:
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
165
            if fname.endswith(exc):
166
                return True
167
        return False
168
2102.3.1 by mbp at sourcefrog
test_source should avoid walking into tempdirs
169
    def test_tmpdir_not_in_source_files(self):
170
        """When scanning for source files, we don't descend test tempdirs"""
171
        for filename in self.get_source_files():
172
            if re.search(r'test....\.tmp', filename):
173
                self.fail("get_source_file() returned filename %r "
174
                          "from within a temporary directory"
175
                          % filename)
176
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
177
    def test_copyright(self):
178
        """Test that all .py files have a valid copyright statement"""
179
        # These are files which contain a different copyright statement
180
        # and that is okay.
181
        incorrect = []
182
2052.3.7 by John Arbash Meinel
Use positive lookahead to avoid extra newlines
183
        copyright_re = re.compile('#\\s*copyright.*(?=\n)', re.I)
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
184
        copyright_canonical_re = re.compile(
185
            r'# Copyright \(C\) ' # Opening "# Copyright (C)"
186
            r'(\d+)(, \d+)*' # Followed by a series of dates
187
            r'.*Canonical Ltd' # And containing 'Canonical Ltd'
188
            )
189
190
        for fname, text in self.get_source_file_contents():
2052.3.5 by John Arbash Meinel
Guide people to how to add files to the list of exceptions
191
            if self.is_copyright_exception(fname):
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
192
                continue
2052.3.2 by John Arbash Meinel
Change Copyright .. by Canonical to Copyright ... Canonical
193
            match = copyright_canonical_re.search(text)
194
            if not match:
195
                match = copyright_re.search(text)
196
                if match:
197
                    incorrect.append((fname, 'found: %s' % (match.group(),)))
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
198
                else:
199
                    incorrect.append((fname, 'no copyright line found\n'))
2052.3.2 by John Arbash Meinel
Change Copyright .. by Canonical to Copyright ... Canonical
200
            else:
201
                if 'by Canonical' in match.group():
202
                    incorrect.append((fname,
203
                        'should not have: "by Canonical": %s'
204
                        % (match.group(),)))
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
205
206
        if incorrect:
207
            help_text = ["Some files have missing or incorrect copyright"
208
                         " statements.",
2052.3.5 by John Arbash Meinel
Guide people to how to add files to the list of exceptions
209
                         "",
210
                         "Please either add them to the list of"
211
                         " COPYRIGHT_EXCEPTIONS in"
212
                         " bzrlib/tests/test_source.py",
213
                         # this is broken to prevent a false match
214
                         "or add '# Copyright (C)"
2613.1.2 by Martin Pool
Move bencode tests into util.tests
215
                         " 2007 Canonical Ltd' to these files:",
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
216
                         "",
217
                        ]
218
            for fname, comment in incorrect:
219
                help_text.append(fname)
220
                help_text.append((' '*4) + comment)
221
222
            self.fail('\n'.join(help_text))
223
224
    def test_gpl(self):
225
        """Test that all .py files have a GPL disclaimer"""
226
        incorrect = []
227
228
        gpl_txt = """
229
# This program is free software; you can redistribute it and/or modify
230
# it under the terms of the GNU General Public License as published by
231
# the Free Software Foundation; either version 2 of the License, or
232
# (at your option) any later version.
233
#
234
# This program is distributed in the hope that it will be useful,
235
# but WITHOUT ANY WARRANTY; without even the implied warranty of
236
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
237
# GNU General Public License for more details.
238
#
239
# You should have received a copy of the GNU General Public License
240
# along with this program; if not, write to the Free Software
241
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
242
"""
243
        gpl_re = re.compile(re.escape(gpl_txt), re.MULTILINE)
244
245
        for fname, text in self.get_source_file_contents():
2052.3.5 by John Arbash Meinel
Guide people to how to add files to the list of exceptions
246
            if self.is_license_exception(fname):
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
247
                continue
248
            if not gpl_re.search(text):
249
                incorrect.append(fname)
250
251
        if incorrect:
252
            help_text = ['Some files have missing or incomplete GPL statement',
2052.3.5 by John Arbash Meinel
Guide people to how to add files to the list of exceptions
253
                         "",
254
                         "Please either add them to the list of"
255
                         " LICENSE_EXCEPTIONS in"
256
                         " bzrlib/tests/test_source.py",
257
                         "Or add the following text to the beginning:",
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
258
                         gpl_txt
259
                        ]
260
            for fname in incorrect:
261
                help_text.append((' '*4) + fname)
262
263
            self.fail('\n'.join(help_text))
2120.2.1 by John Arbash Meinel
Remove tabs from source files, and add a test to keep it that way.
264
3943.7.2 by Marius Kruger
* also check for unix style newlines and note in HACKING that this is what we use.
265
    def _push_file(self, dict_, fname, line_no):
266
        if fname not in dict_:
267
            dict_[fname] = [line_no]
268
        else:
269
            dict_[fname].append(line_no)
270
271
    def _format_message(self, dict_, message):
272
        files = ["%s: %s" % (f, ', '.join([str(i+1) for i in lines]))
273
                for f, lines in dict_.items()]
3943.7.3 by Marius Kruger
sort error output
274
        files.sort()
3943.7.2 by Marius Kruger
* also check for unix style newlines and note in HACKING that this is what we use.
275
        return message + '\n\n    %s' % ('\n    '.join(files))
276
3943.7.1 by Marius Kruger
* Change test_no_tabs to test_coding_style and let it check for trailing newlines too.
277
    def test_coding_style(self):
278
        """Check if bazaar code conforms to some coding style conventions.
279
280
        Currently we check for:
281
         * any tab characters
282
         * trailing white space
3943.7.2 by Marius Kruger
* also check for unix style newlines and note in HACKING that this is what we use.
283
         * non-unix newlines
3943.7.1 by Marius Kruger
* Change test_no_tabs to test_coding_style and let it check for trailing newlines too.
284
         * no newline at end of files
3943.7.5 by Marius Kruger
* test_source also notes how many longlines exist
285
         * lines longer than 79 chars
286
           (only print how many files and lines are in violation)
3943.7.1 by Marius Kruger
* Change test_no_tabs to test_coding_style and let it check for trailing newlines too.
287
        """
3943.7.2 by Marius Kruger
* also check for unix style newlines and note in HACKING that this is what we use.
288
        tabs = {}
3943.7.1 by Marius Kruger
* Change test_no_tabs to test_coding_style and let it check for trailing newlines too.
289
        trailing_ws = {}
3943.7.2 by Marius Kruger
* also check for unix style newlines and note in HACKING that this is what we use.
290
        illegal_newlines = {}
3943.7.5 by Marius Kruger
* test_source also notes how many longlines exist
291
        long_lines = {}
3943.7.4 by Marius Kruger
now also check for 'no newline at end of files'
292
        no_newline_at_eof = []
2120.2.1 by John Arbash Meinel
Remove tabs from source files, and add a test to keep it that way.
293
        for fname, text in self.get_source_file_contents():
3376.2.1 by Martin Pool
Add test_no_asserts and cleanup test_source
294
            if not self.is_our_code(fname):
2234.4.1 by Wouter van Heyst
(John Arbash Meinel) Fix selftest for installed bzr (#80330)
295
                continue
3943.7.2 by Marius Kruger
* also check for unix style newlines and note in HACKING that this is what we use.
296
            lines = text.splitlines(True)
3943.7.4 by Marius Kruger
now also check for 'no newline at end of files'
297
            last_line_no = len(lines) - 1
3943.7.2 by Marius Kruger
* also check for unix style newlines and note in HACKING that this is what we use.
298
            for line_no, line in enumerate(lines):
299
                if '\t' in line:
300
                    self._push_file(tabs, fname, line_no)
301
                if not line.endswith('\n') or line.endswith('\r\n'):
3943.7.4 by Marius Kruger
now also check for 'no newline at end of files'
302
                    if line_no != last_line_no: # not no_newline_at_eof
303
                        self._push_file(illegal_newlines, fname, line_no)
3943.7.2 by Marius Kruger
* also check for unix style newlines and note in HACKING that this is what we use.
304
                if line.endswith(' \n'):
305
                    self._push_file(trailing_ws, fname, line_no)
3943.7.5 by Marius Kruger
* test_source also notes how many longlines exist
306
                if len(line) > 80:
307
                    self._push_file(long_lines, fname, line_no)
3943.7.4 by Marius Kruger
now also check for 'no newline at end of files'
308
            if not lines[-1].endswith('\n'):
309
                no_newline_at_eof.append(fname)
3943.7.1 by Marius Kruger
* Change test_no_tabs to test_coding_style and let it check for trailing newlines too.
310
        problems = []
311
        if tabs:
3943.7.2 by Marius Kruger
* also check for unix style newlines and note in HACKING that this is what we use.
312
            problems.append(self._format_message(tabs,
3943.7.1 by Marius Kruger
* Change test_no_tabs to test_coding_style and let it check for trailing newlines too.
313
                'Tab characters were found in the following source files.'
3943.7.2 by Marius Kruger
* also check for unix style newlines and note in HACKING that this is what we use.
314
                '\nThey should either be replaced by "\\t" or by spaces:'))
3943.7.1 by Marius Kruger
* Change test_no_tabs to test_coding_style and let it check for trailing newlines too.
315
        if trailing_ws:
3943.7.2 by Marius Kruger
* also check for unix style newlines and note in HACKING that this is what we use.
316
            problems.append(self._format_message(trailing_ws,
3943.7.4 by Marius Kruger
now also check for 'no newline at end of files'
317
                'Trailing white space was found in the following source files:'
318
                ))
3943.7.2 by Marius Kruger
* also check for unix style newlines and note in HACKING that this is what we use.
319
        if illegal_newlines:
320
            problems.append(self._format_message(illegal_newlines,
3943.7.4 by Marius Kruger
now also check for 'no newline at end of files'
321
                'Non-unix newlines were found in the following source files:'))
3943.7.5 by Marius Kruger
* test_source also notes how many longlines exist
322
        if long_lines:
323
            print ("There are %i lines longer than 79 characters in %i files."
324
                % (sum([len(lines) for f, lines in long_lines.items()]),
325
                    len(long_lines)))
3943.7.4 by Marius Kruger
now also check for 'no newline at end of files'
326
        if no_newline_at_eof:
327
            no_newline_at_eof.sort()
328
            problems.append("The following source files doesn't have a "
329
                "newline at the end:"
330
               '\n\n    %s'
331
               % ('\n    '.join(no_newline_at_eof)))
3943.7.1 by Marius Kruger
* Change test_no_tabs to test_coding_style and let it check for trailing newlines too.
332
        if problems:
3943.7.4 by Marius Kruger
now also check for 'no newline at end of files'
333
            self.fail('\n\n'.join(problems))
3572.1.3 by Marius Kruger
* move test_coding_style into test_source
334
3376.2.1 by Martin Pool
Add test_no_asserts and cleanup test_source
335
    def test_no_asserts(self):
336
        """bzr shouldn't use the 'assert' statement."""
337
        # assert causes too much variation between -O and not, and tends to
338
        # give bad errors to the user
339
        def search(x):
340
            # scan down through x for assert statements, report any problems
341
            # this is a bit cheesy; it may get some false positives?
342
            if x[0] == symbol.assert_stmt:
343
                return True
344
            elif x[0] == token.NAME:
345
                # can't search further down
346
                return False
347
            for sub in x[1:]:
348
                if sub and search(sub):
349
                    return True
350
            return False
351
        badfiles = []
352
        for fname, text in self.get_source_file_contents():
353
            if not self.is_our_code(fname):
354
                continue
355
            ast = parser.ast2tuple(parser.suite(''.join(text)))
356
            if search(ast):
357
                badfiles.append(fname)
358
        if badfiles:
3376.2.7 by Martin Pool
Treat assert statements in our code as a hard error
359
            self.fail(
3376.2.1 by Martin Pool
Add test_no_asserts and cleanup test_source
360
                "these files contain an assert statement and should not:\n%s"
361
                % '\n'.join(badfiles))