~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_source.py

  • Committer: Robert Collins
  • Date: 2007-07-15 15:40:37 UTC
  • mto: (2592.3.33 repository)
  • mto: This revision was merged to the branch mainline in revision 2624.
  • Revision ID: robertc@robertcollins.net-20070715154037-3ar8g89decddc9su
Make GraphIndex accept nodes as key, value, references, so that the method
signature is closer to what a simple key->value index delivers. Also
change the behaviour when the reference list count is zero to accept
key, value as nodes, and emit key, value to make it identical in that case
to a simple key->value index. This may not be a good idea, but for now it
seems ok.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2008 Canonical Ltd
 
1
# Copyright (C) 2005, 2006 Canonical Ltd
2
2
#   Authors: Robert Collins <robert.collins@canonical.com>
3
 
#            and others
4
3
#
5
4
# This program is free software; you can redistribute it and/or modify
6
5
# it under the terms of the GNU General Public License as published by
14
13
#
15
14
# You should have received a copy of the GNU General Public License
16
15
# along with this program; if not, write to the Free Software
17
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
17
 
19
18
"""These tests are tests about the source code of bzrlib itself.
20
19
 
23
22
 
24
23
# import system imports here
25
24
import os
26
 
import parser
27
25
import re
28
 
import symbol
29
26
import sys
30
 
import token
31
27
 
32
28
#import bzrlib specific imports here
33
29
from bzrlib import (
34
30
    osutils,
35
31
    )
36
32
import bzrlib.branch
37
 
from bzrlib.tests import (
38
 
    TestCase,
39
 
    TestSkipped,
40
 
    )
 
33
from bzrlib.tests import TestCase, TestSkipped
41
34
 
42
35
 
43
36
# Files which are listed here will be skipped when testing for Copyright (or
44
37
# GPL) statements.
45
 
COPYRIGHT_EXCEPTIONS = ['bzrlib/lsprof.py', 'bzrlib/_bencode_py.py']
 
38
COPYRIGHT_EXCEPTIONS = ['bzrlib/lsprof.py']
46
39
 
47
 
LICENSE_EXCEPTIONS = ['bzrlib/lsprof.py', 'bzrlib/_bencode_py.py']
 
40
LICENSE_EXCEPTIONS = ['bzrlib/lsprof.py']
48
41
# Technically, 'bzrlib/lsprof.py' should be 'bzrlib/util/lsprof.py',
49
42
# (we do not check bzrlib/util/, since that is code bundled from elsewhere)
50
43
# but for compatibility with previous releases, we don't want to move it.
54
47
 
55
48
    def source_file_name(self, package):
56
49
        """Return the path of the .py file for package."""
57
 
        if getattr(sys, "frozen", None) is not None:
58
 
            raise TestSkipped("can't test sources in frozen distributions.")
59
50
        path = package.__file__
60
51
        if path[-1] in 'co':
61
52
            return path[:-1]
81
72
        # do not even think of increasing this number. If you think you need to
82
73
        # increase it, then you almost certainly are doing something wrong as
83
74
        # the relationship from working_tree to branch is one way.
84
 
        # Note that this is an exact equality so that when the number drops,
 
75
        # Note that this is an exact equality so that when the number drops, 
85
76
        #it is not given a buffer but rather has this test updated immediately.
86
77
        self.assertEqual(0, occurences)
87
78
 
89
80
        """Test that the number of uses of working_tree in branch is stable."""
90
81
        occurences = self.find_occurences('WorkingTree',
91
82
                                          self.source_file_name(bzrlib.branch))
92
 
        # Do not even think of increasing this number. If you think you need to
 
83
        # do not even think of increasing this number. If you think you need to
93
84
        # increase it, then you almost certainly are doing something wrong as
94
85
        # the relationship from working_tree to branch is one way.
95
 
        # As of 20070809, there are no longer any mentions at all.
96
 
        self.assertEqual(0, occurences)
 
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
 
91
        # immediately.
 
92
        self.assertEqual(2, occurences)
97
93
 
98
94
 
99
95
class TestSource(TestSourceHelper):
109
105
                              % source_dir)
110
106
        return source_dir
111
107
 
112
 
    def get_source_files(self, extensions=None):
113
 
        """Yield all source files for bzr and bzrlib
114
 
 
115
 
        :param our_files_only: If true, exclude files from included libraries
116
 
            or plugins.
117
 
        """
 
108
    def get_source_files(self):
 
109
        """yield all source files for bzr and bzrlib"""
118
110
        bzrlib_dir = self.get_bzrlib_dir()
119
 
        if extensions is None:
120
 
            extensions = ('.py',)
121
111
 
122
112
        # This is the front-end 'bzr' script
123
113
        bzr_path = self.get_bzr_path()
128
118
                if d.endswith('.tmp'):
129
119
                    dirs.remove(d)
130
120
            for f in files:
131
 
                for extension in extensions:
132
 
                    if f.endswith(extension):
133
 
                        break
134
 
                else:
135
 
                    # Did not match the accepted extensions
 
121
                if not f.endswith('.py'):
136
122
                    continue
137
123
                yield osutils.pathjoin(root, f)
138
124
 
139
 
    def get_source_file_contents(self, extensions=None):
140
 
        for fname in self.get_source_files(extensions=extensions):
 
125
    def get_source_file_contents(self):
 
126
        for fname in self.get_source_files():
141
127
            f = open(fname, 'rb')
142
128
            try:
143
129
                text = f.read()
145
131
                f.close()
146
132
            yield fname, text
147
133
 
148
 
    def is_our_code(self, fname):
149
 
        """Return true if it's a "real" part of bzrlib rather than external code"""
150
 
        if '/util/' in fname or '/plugins/' in fname:
151
 
            return False
152
 
        else:
153
 
            return True
154
 
 
155
134
    def is_copyright_exception(self, fname):
156
135
        """Certain files are allowed to be different"""
157
 
        if not self.is_our_code(fname):
 
136
        if '/util/' in fname or '/plugins/' in fname:
158
137
            # We don't ask that external utilities or plugins be
159
138
            # (C) Canonical Ltd
160
139
            return True
 
140
 
161
141
        for exc in COPYRIGHT_EXCEPTIONS:
162
142
            if fname.endswith(exc):
163
143
                return True
 
144
 
164
145
        return False
165
146
 
166
147
    def is_license_exception(self, fname):
167
148
        """Certain files are allowed to be different"""
168
 
        if not self.is_our_code(fname):
 
149
        if '/util/' in fname or '/plugins/' in fname:
 
150
            # We don't ask that external utilities or plugins be
 
151
            # (C) Canonical Ltd
169
152
            return True
 
153
 
170
154
        for exc in LICENSE_EXCEPTIONS:
171
155
            if fname.endswith(exc):
172
156
                return True
 
157
 
173
158
        return False
174
159
 
175
160
    def test_tmpdir_not_in_source_files(self):
181
166
                          % filename)
182
167
 
183
168
    def test_copyright(self):
184
 
        """Test that all .py and .pyx files have a valid copyright statement"""
 
169
        """Test that all .py files have a valid copyright statement"""
 
170
        # These are files which contain a different copyright statement
 
171
        # and that is okay.
185
172
        incorrect = []
186
173
 
187
174
        copyright_re = re.compile('#\\s*copyright.*(?=\n)', re.I)
191
178
            r'.*Canonical Ltd' # And containing 'Canonical Ltd'
192
179
            )
193
180
 
194
 
        for fname, text in self.get_source_file_contents(
195
 
                extensions=('.py', '.pyx')):
 
181
        for fname, text in self.get_source_file_contents():
196
182
            if self.is_copyright_exception(fname):
197
183
                continue
198
184
            match = copyright_canonical_re.search(text)
217
203
                         " bzrlib/tests/test_source.py",
218
204
                         # this is broken to prevent a false match
219
205
                         "or add '# Copyright (C)"
220
 
                         " 2007 Canonical Ltd' to these files:",
 
206
                         " 2006 Canonical Ltd' to these files:",
221
207
                         "",
222
208
                        ]
223
209
            for fname, comment in incorrect:
227
213
            self.fail('\n'.join(help_text))
228
214
 
229
215
    def test_gpl(self):
230
 
        """Test that all .py and .pyx files have a GPL disclaimer."""
 
216
        """Test that all .py files have a GPL disclaimer"""
231
217
        incorrect = []
232
218
 
233
219
        gpl_txt = """
243
229
#
244
230
# You should have received a copy of the GNU General Public License
245
231
# along with this program; if not, write to the Free Software
246
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
232
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
247
233
"""
248
234
        gpl_re = re.compile(re.escape(gpl_txt), re.MULTILINE)
249
235
 
250
 
        for fname, text in self.get_source_file_contents(
251
 
                extensions=('.py', '.pyx')):
 
236
        for fname, text in self.get_source_file_contents():
252
237
            if self.is_license_exception(fname):
253
238
                continue
254
239
            if not gpl_re.search(text):
268
253
 
269
254
            self.fail('\n'.join(help_text))
270
255
 
271
 
    def _push_file(self, dict_, fname, line_no):
272
 
        if fname not in dict_:
273
 
            dict_[fname] = [line_no]
274
 
        else:
275
 
            dict_[fname].append(line_no)
276
 
 
277
 
    def _format_message(self, dict_, message):
278
 
        files = ["%s: %s" % (f, ', '.join([str(i+1) for i in lines]))
279
 
                for f, lines in dict_.items()]
280
 
        files.sort()
281
 
        return message + '\n\n    %s' % ('\n    '.join(files))
282
 
 
283
 
    def test_coding_style(self):
284
 
        """Check if bazaar code conforms to some coding style conventions.
285
 
 
286
 
        Currently we assert that the following is not present:
287
 
         * any tab characters
288
 
         * non-unix newlines
289
 
         * no newline at end of files
290
 
 
291
 
        Print how many files have
292
 
         * trailing white space
293
 
         * lines longer than 79 chars
294
 
        """
295
 
        tabs = {}
296
 
        trailing_ws = {}
297
 
        illegal_newlines = {}
298
 
        long_lines = {}
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):
303
 
                continue
304
 
            lines = text.splitlines(True)
305
 
            last_line_no = len(lines) - 1
306
 
            for line_no, line in enumerate(lines):
307
 
                if '\t' in line:
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)
314
 
                if len(line) > 80:
315
 
                    self._push_file(long_lines, fname, line_no)
316
 
            if not lines[-1].endswith('\n'):
317
 
                no_newline_at_eof.append(fname)
318
 
        problems = []
319
 
        if tabs:
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:'))
323
 
        if trailing_ws:
324
 
            print ("There are %i lines with trailing white space in %i files."
325
 
                % (sum([len(lines) for f, lines in trailing_ws.items()]),
326
 
                    len(trailing_ws)))
327
 
        if illegal_newlines:
328
 
            problems.append(self._format_message(illegal_newlines,
329
 
                'Non-unix newlines were found in the following source files:'))
330
 
        if long_lines:
331
 
            print ("There are %i lines longer than 79 characters in %i files."
332
 
                % (sum([len(lines) for f, lines in long_lines.items()]),
333
 
                    len(long_lines)))
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:"
338
 
               '\n\n    %s'
339
 
               % ('\n    '.join(no_newline_at_eof)))
340
 
        if problems:
341
 
            self.fail('\n\n'.join(problems))
342
 
 
343
 
    def test_no_asserts(self):
344
 
        """bzr shouldn't use the 'assert' statement."""
345
 
        # assert causes too much variation between -O and not, and tends to
346
 
        # give bad errors to the user
347
 
        def search(x):
348
 
            # scan down through x for assert statements, report any problems
349
 
            # this is a bit cheesy; it may get some false positives?
350
 
            if x[0] == symbol.assert_stmt:
351
 
                return True
352
 
            elif x[0] == token.NAME:
353
 
                # can't search further down
354
 
                return False
355
 
            for sub in x[1:]:
356
 
                if sub and search(sub):
357
 
                    return True
358
 
            return False
359
 
        badfiles = []
 
256
    def test_no_tabs(self):
 
257
        """bzrlib source files should not contain any tab characters."""
 
258
        incorrect = []
 
259
 
360
260
        for fname, text in self.get_source_file_contents():
361
 
            if not self.is_our_code(fname):
 
261
            if '/util/' in fname or '/plugins/' in fname:
362
262
                continue
363
 
            ast = parser.ast2tuple(parser.suite(''.join(text)))
364
 
            if search(ast):
365
 
                badfiles.append(fname)
366
 
        if badfiles:
367
 
            self.fail(
368
 
                "these files contain an assert statement and should not:\n%s"
369
 
                % '\n'.join(badfiles))
 
263
            if '\t' in text:
 
264
                incorrect.append(fname)
 
265
 
 
266
        if incorrect:
 
267
            self.fail('Tab characters were found in the following source files.'
 
268
              '\nThey should either be replaced by "\\t" or by spaces:'
 
269
              '\n\n    %s'
 
270
              % ('\n    '.join(incorrect)))