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