~bzr-pqm/bzr/bzr.dev

4763.2.4 by John Arbash Meinel
merge bzr.2.1 in preparation for NEWS entry.
1
# Copyright (C) 2005-2010 Canonical Ltd
1523 by Robert Collins
Test for the number of uses of self.working_tree() in branch.py
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1523 by Robert Collins
Test for the number of uses of self.working_tree() in branch.py
16
17
"""These tests are tests about the source code of bzrlib itself.
18
19
They are useful for testing code quality, checking coverage metric etc.
20
"""
21
22
# import system imports here
23
import os
3376.2.1 by Martin Pool
Add test_no_asserts and cleanup test_source
24
import parser
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
25
import re
3376.2.1 by Martin Pool
Add test_no_asserts and cleanup test_source
26
import symbol
1523 by Robert Collins
Test for the number of uses of self.working_tree() in branch.py
27
import sys
3376.2.1 by Martin Pool
Add test_no_asserts and cleanup test_source
28
import token
1523 by Robert Collins
Test for the number of uses of self.working_tree() in branch.py
29
30
#import bzrlib specific imports here
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
31
from bzrlib import (
32
    osutils,
33
    )
1523 by Robert Collins
Test for the number of uses of self.working_tree() in branch.py
34
import bzrlib.branch
3376.2.1 by Martin Pool
Add test_no_asserts and cleanup test_source
35
from bzrlib.tests import (
36
    TestCase,
37
    TestSkipped,
38
    )
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
39
40
2052.3.8 by John Arbash Meinel
Better documentation about the exception variables
41
# Files which are listed here will be skipped when testing for Copyright (or
42
# GPL) statements.
4634.37.3 by Martin Pool
sphinx autogenerate configuration is exempt from test_source
43
COPYRIGHT_EXCEPTIONS = ['bzrlib/lsprof.py', 'bzrlib/_bencode_py.py',
44
    'bzrlib/doc_generate/sphinx_conf.py']
2052.3.5 by John Arbash Meinel
Guide people to how to add files to the list of exceptions
45
4634.37.3 by Martin Pool
sphinx autogenerate configuration is exempt from test_source
46
LICENSE_EXCEPTIONS = ['bzrlib/lsprof.py', 'bzrlib/_bencode_py.py',
47
    'bzrlib/doc_generate/sphinx_conf.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.
4634.37.3 by Martin Pool
sphinx autogenerate configuration is exempt from test_source
51
#
52
# sphinx_conf is semi-autogenerated.
2052.3.5 by John Arbash Meinel
Guide people to how to add files to the list of exceptions
53
3572.1.7 by Marius Kruger
Code style and minor changes as per review.
54
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
55
class TestSourceHelper(TestCase):
56
57
    def source_file_name(self, package):
58
        """Return the path of the .py file for package."""
3616.2.7 by Mark Hammond
prefer getattr() over hasattr()
59
        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.
60
            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
61
        path = package.__file__
62
        if path[-1] in 'co':
63
            return path[:-1]
64
        else:
65
            return path
66
67
68
class TestApiUsage(TestSourceHelper):
1523 by Robert Collins
Test for the number of uses of self.working_tree() in branch.py
69
1524 by Robert Collins
Test the uses of WorkingTree from branch.py
70
    def find_occurences(self, rule, filename):
71
        """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
72
        occurences = 0
1524 by Robert Collins
Test the uses of WorkingTree from branch.py
73
        source = file(filename, 'r')
1523 by Robert Collins
Test for the number of uses of self.working_tree() in branch.py
74
        for line in source:
1524 by Robert Collins
Test the uses of WorkingTree from branch.py
75
            if line.find(rule) > -1:
1523 by Robert Collins
Test for the number of uses of self.working_tree() in branch.py
76
                occurences += 1
1524 by Robert Collins
Test the uses of WorkingTree from branch.py
77
        return occurences
78
79
    def test_branch_working_tree(self):
80
        """Test that the number of uses of working_tree in branch is stable."""
81
        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__
82
                                          self.source_file_name(bzrlib.branch))
1523 by Robert Collins
Test for the number of uses of self.working_tree() in branch.py
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.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
86
        # 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()
87
        #it is not given a buffer but rather has this test updated immediately.
88
        self.assertEqual(0, occurences)
1524 by Robert Collins
Test the uses of WorkingTree from branch.py
89
90
    def test_branch_WorkingTree(self):
91
        """Test that the number of uses of working_tree in branch is stable."""
92
        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__
93
                                          self.source_file_name(bzrlib.branch))
2696.1.1 by Martin Pool
Remove things deprecated in 0.11 and earlier
94
        # 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
95
        # increase it, then you almost certainly are doing something wrong as
96
        # the relationship from working_tree to branch is one way.
2696.1.1 by Martin Pool
Remove things deprecated in 0.11 and earlier
97
        # As of 20070809, there are no longer any mentions at all.
98
        self.assertEqual(0, occurences)
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
99
100
101
class TestSource(TestSourceHelper):
102
103
    def get_bzrlib_dir(self):
104
        """Get the path to the root of bzrlib"""
105
        source = self.source_file_name(bzrlib)
106
        source_dir = os.path.dirname(source)
107
108
        # Avoid the case when bzrlib is packaged in a zip file
109
        if not os.path.isdir(source_dir):
110
            raise TestSkipped('Cannot find bzrlib source directory. Expected %s'
111
                              % source_dir)
112
        return source_dir
113
4198.1.1 by John Arbash Meinel
Include pyrex files in our source testing for GPL and Copyright checks.
114
    def get_source_files(self, extensions=None):
3376.2.1 by Martin Pool
Add test_no_asserts and cleanup test_source
115
        """Yield all source files for bzr and bzrlib
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
116
3376.2.1 by Martin Pool
Add test_no_asserts and cleanup test_source
117
        :param our_files_only: If true, exclude files from included libraries
118
            or plugins.
119
        """
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
120
        bzrlib_dir = self.get_bzrlib_dir()
4198.1.1 by John Arbash Meinel
Include pyrex files in our source testing for GPL and Copyright checks.
121
        if extensions is None:
122
            extensions = ('.py',)
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
123
124
        # This is the front-end 'bzr' script
125
        bzr_path = self.get_bzr_path()
126
        yield bzr_path
127
128
        for root, dirs, files in os.walk(bzrlib_dir):
2102.3.1 by mbp at sourcefrog
test_source should avoid walking into tempdirs
129
            for d in dirs:
130
                if d.endswith('.tmp'):
131
                    dirs.remove(d)
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
132
            for f in files:
4198.1.1 by John Arbash Meinel
Include pyrex files in our source testing for GPL and Copyright checks.
133
                for extension in extensions:
134
                    if f.endswith(extension):
135
                        break
136
                else:
137
                    # Did not match the accepted extensions
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
138
                    continue
139
                yield osutils.pathjoin(root, f)
140
4198.1.1 by John Arbash Meinel
Include pyrex files in our source testing for GPL and Copyright checks.
141
    def get_source_file_contents(self, extensions=None):
142
        for fname in self.get_source_files(extensions=extensions):
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
143
            f = open(fname, 'rb')
144
            try:
145
                text = f.read()
146
            finally:
147
                f.close()
148
            yield fname, text
149
3376.2.1 by Martin Pool
Add test_no_asserts and cleanup test_source
150
    def is_our_code(self, fname):
151
        """Return true if it's a "real" part of bzrlib rather than external code"""
152
        if '/util/' in fname or '/plugins/' in fname:
153
            return False
154
        else:
155
            return True
156
2052.3.5 by John Arbash Meinel
Guide people to how to add files to the list of exceptions
157
    def is_copyright_exception(self, fname):
158
        """Certain files are allowed to be different"""
3376.2.1 by Martin Pool
Add test_no_asserts and cleanup test_source
159
        if not self.is_our_code(fname):
2084.1.5 by John Arbash Meinel
Don't check plugins for copyright or license
160
            # We don't ask that external utilities or plugins be
161
            # (C) Canonical Ltd
2052.3.5 by John Arbash Meinel
Guide people to how to add files to the list of exceptions
162
            return True
163
        for exc in COPYRIGHT_EXCEPTIONS:
164
            if fname.endswith(exc):
165
                return True
166
        return False
167
168
    def is_license_exception(self, fname):
169
        """Certain files are allowed to be different"""
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
170
        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
171
            return True
172
        for exc in LICENSE_EXCEPTIONS:
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
173
            if fname.endswith(exc):
174
                return True
175
        return False
176
2102.3.1 by mbp at sourcefrog
test_source should avoid walking into tempdirs
177
    def test_tmpdir_not_in_source_files(self):
178
        """When scanning for source files, we don't descend test tempdirs"""
179
        for filename in self.get_source_files():
180
            if re.search(r'test....\.tmp', filename):
181
                self.fail("get_source_file() returned filename %r "
182
                          "from within a temporary directory"
183
                          % filename)
184
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
185
    def test_copyright(self):
4198.1.1 by John Arbash Meinel
Include pyrex files in our source testing for GPL and Copyright checks.
186
        """Test that all .py and .pyx files have a valid copyright statement"""
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
187
        incorrect = []
188
2052.3.7 by John Arbash Meinel
Use positive lookahead to avoid extra newlines
189
        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
190
        copyright_canonical_re = re.compile(
191
            r'# Copyright \(C\) ' # Opening "# Copyright (C)"
192
            r'(\d+)(, \d+)*' # Followed by a series of dates
193
            r'.*Canonical Ltd' # And containing 'Canonical Ltd'
194
            )
195
4198.1.1 by John Arbash Meinel
Include pyrex files in our source testing for GPL and Copyright checks.
196
        for fname, text in self.get_source_file_contents(
197
                extensions=('.py', '.pyx')):
2052.3.5 by John Arbash Meinel
Guide people to how to add files to the list of exceptions
198
            if self.is_copyright_exception(fname):
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
199
                continue
2052.3.2 by John Arbash Meinel
Change Copyright .. by Canonical to Copyright ... Canonical
200
            match = copyright_canonical_re.search(text)
201
            if not match:
202
                match = copyright_re.search(text)
203
                if match:
204
                    incorrect.append((fname, 'found: %s' % (match.group(),)))
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
205
                else:
206
                    incorrect.append((fname, 'no copyright line found\n'))
2052.3.2 by John Arbash Meinel
Change Copyright .. by Canonical to Copyright ... Canonical
207
            else:
208
                if 'by Canonical' in match.group():
209
                    incorrect.append((fname,
210
                        'should not have: "by Canonical": %s'
211
                        % (match.group(),)))
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
212
213
        if incorrect:
214
            help_text = ["Some files have missing or incorrect copyright"
215
                         " statements.",
2052.3.5 by John Arbash Meinel
Guide people to how to add files to the list of exceptions
216
                         "",
217
                         "Please either add them to the list of"
218
                         " COPYRIGHT_EXCEPTIONS in"
219
                         " bzrlib/tests/test_source.py",
220
                         # this is broken to prevent a false match
221
                         "or add '# Copyright (C)"
2613.1.2 by Martin Pool
Move bencode tests into util.tests
222
                         " 2007 Canonical Ltd' to these files:",
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
223
                         "",
224
                        ]
225
            for fname, comment in incorrect:
226
                help_text.append(fname)
227
                help_text.append((' '*4) + comment)
228
229
            self.fail('\n'.join(help_text))
230
231
    def test_gpl(self):
4198.1.1 by John Arbash Meinel
Include pyrex files in our source testing for GPL and Copyright checks.
232
        """Test that all .py and .pyx files have a GPL disclaimer."""
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
233
        incorrect = []
234
235
        gpl_txt = """
236
# This program is free software; you can redistribute it and/or modify
237
# it under the terms of the GNU General Public License as published by
238
# the Free Software Foundation; either version 2 of the License, or
239
# (at your option) any later version.
240
#
241
# This program is distributed in the hope that it will be useful,
242
# but WITHOUT ANY WARRANTY; without even the implied warranty of
243
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
244
# GNU General Public License for more details.
245
#
246
# You should have received a copy of the GNU General Public License
247
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
248
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
249
"""
250
        gpl_re = re.compile(re.escape(gpl_txt), re.MULTILINE)
251
4198.1.1 by John Arbash Meinel
Include pyrex files in our source testing for GPL and Copyright checks.
252
        for fname, text in self.get_source_file_contents(
253
                extensions=('.py', '.pyx')):
2052.3.5 by John Arbash Meinel
Guide people to how to add files to the list of exceptions
254
            if self.is_license_exception(fname):
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
255
                continue
256
            if not gpl_re.search(text):
257
                incorrect.append(fname)
258
259
        if incorrect:
260
            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
261
                         "",
262
                         "Please either add them to the list of"
263
                         " LICENSE_EXCEPTIONS in"
264
                         " bzrlib/tests/test_source.py",
265
                         "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
266
                         gpl_txt
267
                        ]
268
            for fname in incorrect:
269
                help_text.append((' '*4) + fname)
270
271
            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.
272
3943.7.2 by Marius Kruger
* also check for unix style newlines and note in HACKING that this is what we use.
273
    def _push_file(self, dict_, fname, line_no):
274
        if fname not in dict_:
275
            dict_[fname] = [line_no]
276
        else:
277
            dict_[fname].append(line_no)
278
279
    def _format_message(self, dict_, message):
280
        files = ["%s: %s" % (f, ', '.join([str(i+1) for i in lines]))
281
                for f, lines in dict_.items()]
3943.7.3 by Marius Kruger
sort error output
282
        files.sort()
3943.7.2 by Marius Kruger
* also check for unix style newlines and note in HACKING that this is what we use.
283
        return message + '\n\n    %s' % ('\n    '.join(files))
284
3943.7.1 by Marius Kruger
* Change test_no_tabs to test_coding_style and let it check for trailing newlines too.
285
    def test_coding_style(self):
286
        """Check if bazaar code conforms to some coding style conventions.
287
4210.5.1 by Marius Kruger
don't raise KnownFailure for all coding_style regressions,
288
        Currently we assert that the following is not present:
3943.7.1 by Marius Kruger
* Change test_no_tabs to test_coding_style and let it check for trailing newlines too.
289
         * any tab characters
3943.7.2 by Marius Kruger
* also check for unix style newlines and note in HACKING that this is what we use.
290
         * 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.
291
         * no newline at end of files
4210.5.1 by Marius Kruger
don't raise KnownFailure for all coding_style regressions,
292
293
        Print how many files have
294
         * trailing white space
3943.7.5 by Marius Kruger
* test_source also notes how many longlines exist
295
         * lines longer than 79 chars
3943.7.1 by Marius Kruger
* Change test_no_tabs to test_coding_style and let it check for trailing newlines too.
296
        """
3943.7.2 by Marius Kruger
* also check for unix style newlines and note in HACKING that this is what we use.
297
        tabs = {}
3943.7.1 by Marius Kruger
* Change test_no_tabs to test_coding_style and let it check for trailing newlines too.
298
        trailing_ws = {}
3943.7.2 by Marius Kruger
* also check for unix style newlines and note in HACKING that this is what we use.
299
        illegal_newlines = {}
3943.7.5 by Marius Kruger
* test_source also notes how many longlines exist
300
        long_lines = {}
3943.7.4 by Marius Kruger
now also check for 'no newline at end of files'
301
        no_newline_at_eof = []
4198.1.1 by John Arbash Meinel
Include pyrex files in our source testing for GPL and Copyright checks.
302
        for fname, text in self.get_source_file_contents(
303
                extensions=('.py', '.pyx')):
3376.2.1 by Martin Pool
Add test_no_asserts and cleanup test_source
304
            if not self.is_our_code(fname):
2234.4.1 by Wouter van Heyst
(John Arbash Meinel) Fix selftest for installed bzr (#80330)
305
                continue
3943.7.2 by Marius Kruger
* also check for unix style newlines and note in HACKING that this is what we use.
306
            lines = text.splitlines(True)
3943.7.4 by Marius Kruger
now also check for 'no newline at end of files'
307
            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.
308
            for line_no, line in enumerate(lines):
309
                if '\t' in line:
310
                    self._push_file(tabs, fname, line_no)
311
                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'
312
                    if line_no != last_line_no: # not no_newline_at_eof
313
                        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.
314
                if line.endswith(' \n'):
315
                    self._push_file(trailing_ws, fname, line_no)
3943.7.5 by Marius Kruger
* test_source also notes how many longlines exist
316
                if len(line) > 80:
317
                    self._push_file(long_lines, fname, line_no)
3943.7.4 by Marius Kruger
now also check for 'no newline at end of files'
318
            if not lines[-1].endswith('\n'):
319
                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.
320
        problems = []
321
        if tabs:
3943.7.2 by Marius Kruger
* also check for unix style newlines and note in HACKING that this is what we use.
322
            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.
323
                '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.
324
                '\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.
325
        if trailing_ws:
4210.5.1 by Marius Kruger
don't raise KnownFailure for all coding_style regressions,
326
            print ("There are %i lines with trailing white space in %i files."
327
                % (sum([len(lines) for f, lines in trailing_ws.items()]),
328
                    len(trailing_ws)))
3943.7.2 by Marius Kruger
* also check for unix style newlines and note in HACKING that this is what we use.
329
        if illegal_newlines:
330
            problems.append(self._format_message(illegal_newlines,
3943.7.4 by Marius Kruger
now also check for 'no newline at end of files'
331
                'Non-unix newlines were found in the following source files:'))
3943.7.5 by Marius Kruger
* test_source also notes how many longlines exist
332
        if long_lines:
333
            print ("There are %i lines longer than 79 characters in %i files."
334
                % (sum([len(lines) for f, lines in long_lines.items()]),
335
                    len(long_lines)))
3943.7.4 by Marius Kruger
now also check for 'no newline at end of files'
336
        if no_newline_at_eof:
337
            no_newline_at_eof.sort()
338
            problems.append("The following source files doesn't have a "
339
                "newline at the end:"
340
               '\n\n    %s'
341
               % ('\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.
342
        if problems:
3943.7.4 by Marius Kruger
now also check for 'no newline at end of files'
343
            self.fail('\n\n'.join(problems))
3572.1.3 by Marius Kruger
* move test_coding_style into test_source
344
3376.2.1 by Martin Pool
Add test_no_asserts and cleanup test_source
345
    def test_no_asserts(self):
346
        """bzr shouldn't use the 'assert' statement."""
347
        # assert causes too much variation between -O and not, and tends to
348
        # give bad errors to the user
349
        def search(x):
350
            # scan down through x for assert statements, report any problems
351
            # this is a bit cheesy; it may get some false positives?
352
            if x[0] == symbol.assert_stmt:
353
                return True
354
            elif x[0] == token.NAME:
355
                # can't search further down
356
                return False
357
            for sub in x[1:]:
358
                if sub and search(sub):
359
                    return True
360
            return False
361
        badfiles = []
4699.1.1 by Harald Meland
Make test_no_asserts quicker by doing a regexp check for the presence of any 'assert' word before doing any expensive AST stuff
362
        assert_re = re.compile(r'\bassert\b')
3376.2.1 by Martin Pool
Add test_no_asserts and cleanup test_source
363
        for fname, text in self.get_source_file_contents():
364
            if not self.is_our_code(fname):
365
                continue
4699.1.1 by Harald Meland
Make test_no_asserts quicker by doing a regexp check for the presence of any 'assert' word before doing any expensive AST stuff
366
            if not assert_re.search(text):
367
                continue
368
            ast = parser.ast2tuple(parser.suite(text))
3376.2.1 by Martin Pool
Add test_no_asserts and cleanup test_source
369
            if search(ast):
370
                badfiles.append(fname)
371
        if badfiles:
3376.2.7 by Martin Pool
Treat assert statements in our code as a hard error
372
            self.fail(
3376.2.1 by Martin Pool
Add test_no_asserts and cleanup test_source
373
                "these files contain an assert statement and should not:\n%s"
374
                % '\n'.join(badfiles))
4634.117.5 by John Arbash Meinel
Add a test_source test, to ensure that all funcs have either an except
375
376
    def test_extension_exceptions(self):
377
        """Extension functions should propagate exceptions.
378
379
        Either they should return an object, have an 'except' clause, or have a
4634.117.10 by John Arbash Meinel
Change 'no except' to 'cannot_raise'
380
        "# cannot_raise" to indicate that we've audited them and defined them as not
4634.117.5 by John Arbash Meinel
Add a test_source test, to ensure that all funcs have either an except
381
        raising exceptions.
382
        """
383
        both_exc_and_no_exc = []
384
        missing_except = []
4932.1.2 by John Arbash Meinel
Clean up _simple_set.pyx, and handle 'nogil'.
385
        class_re = re.compile(r'^(cdef\s+)?(public\s+)?(api\s+)?class (\w+).*:',
386
                              re.MULTILINE)
387
        except_re = re.compile(r'cdef\s+' # start with cdef
4634.117.5 by John Arbash Meinel
Add a test_source test, to ensure that all funcs have either an except
388
                               r'([\w *]*?)\s*' # this is the return signature
389
                               r'(\w+)\s*\(' # the function name
390
                               r'[^)]*\)\s*' # parameters
391
                               r'(.*)\s*:' # the except clause
4634.117.10 by John Arbash Meinel
Change 'no except' to 'cannot_raise'
392
                               r'\s*(#\s*cannot[- _]raise)?' # cannot raise comment
4634.117.5 by John Arbash Meinel
Add a test_source test, to ensure that all funcs have either an except
393
                              )
394
        for fname, text in self.get_source_file_contents(
395
                extensions=('.pyx',)):
4932.1.2 by John Arbash Meinel
Clean up _simple_set.pyx, and handle 'nogil'.
396
            known_classes = set([m[-1] for m in class_re.findall(text)])
4634.117.5 by John Arbash Meinel
Add a test_source test, to ensure that all funcs have either an except
397
            cdefs = except_re.findall(text)
398
            for sig, func, exc_clause, no_exc_comment in cdefs:
4932.1.2 by John Arbash Meinel
Clean up _simple_set.pyx, and handle 'nogil'.
399
                if sig.startswith('api '):
400
                    sig = sig[4:]
4634.117.8 by John Arbash Meinel
Use a class regex to find other signatures to exclude.
401
                if not sig or sig in known_classes:
4634.117.5 by John Arbash Meinel
Add a test_source test, to ensure that all funcs have either an except
402
                    sig = 'object'
4932.1.2 by John Arbash Meinel
Clean up _simple_set.pyx, and handle 'nogil'.
403
                if 'nogil' in exc_clause:
404
                    exc_clause = exc_clause.replace('nogil', '').strip()
4634.117.5 by John Arbash Meinel
Add a test_source test, to ensure that all funcs have either an except
405
                if exc_clause and no_exc_comment:
406
                    both_exc_and_no_exc.append((fname, func))
407
                if sig != 'object' and not (exc_clause or no_exc_comment):
408
                    missing_except.append((fname, func))
409
        error_msg = []
410
        if both_exc_and_no_exc:
4634.117.10 by John Arbash Meinel
Change 'no except' to 'cannot_raise'
411
            error_msg.append('The following functions had "cannot raise" comments'
4634.117.5 by John Arbash Meinel
Add a test_source test, to ensure that all funcs have either an except
412
                             ' but did have an except clause set:')
413
            for fname, func in both_exc_and_no_exc:
414
                error_msg.append('%s:%s' % (fname, func))
415
            error_msg.extend(('', ''))
416
        if missing_except:
417
            error_msg.append('The following functions have fixed return types,'
4634.117.6 by John Arbash Meinel
Start going through the test failures.
418
                             ' but no except clause.')
4634.117.10 by John Arbash Meinel
Change 'no except' to 'cannot_raise'
419
            error_msg.append('Either add an except or append "# cannot_raise".')
4634.117.5 by John Arbash Meinel
Add a test_source test, to ensure that all funcs have either an except
420
            for fname, func in missing_except:
421
                error_msg.append('%s:%s' % (fname, func))
422
            error_msg.extend(('', ''))
423
        if error_msg:
424
            self.fail('\n'.join(error_msg))