/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/tests/test_source.py

  • Committer: Breezy landing bot
  • Author(s): Colin Watson
  • Date: 2020-11-16 21:47:08 UTC
  • mfrom: (7521.1.1 remove-lp-workaround)
  • Revision ID: breezy.the.bot@gmail.com-20201116214708-jos209mgxi41oy15
Remove breezy.git workaround for bazaar.launchpad.net.

Merged from https://code.launchpad.net/~cjwatson/brz/remove-lp-workaround/+merge/393710

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
 
"""These tests are tests about the source code of bzrlib itself.
 
17
"""These tests are tests about the source code of breezy itself.
18
18
 
19
19
They are useful for testing code quality, checking coverage metric etc.
20
20
"""
26
26
import sys
27
27
import token
28
28
 
29
 
from bzrlib import (
 
29
from breezy import (
30
30
    osutils,
31
31
    )
32
 
import bzrlib.branch
33
 
from bzrlib.tests import (
 
32
import breezy.branch
 
33
from breezy.tests import (
 
34
    features,
34
35
    TestCase,
35
36
    TestSkipped,
36
37
    )
39
40
# Files which are listed here will be skipped when testing for Copyright (or
40
41
# GPL) statements.
41
42
COPYRIGHT_EXCEPTIONS = [
42
 
    'bzrlib/_bencode_py.py',
43
 
    'bzrlib/doc_generate/conf.py',
44
 
    'bzrlib/lsprof.py',
 
43
    'breezy/_bencode_py.py',
 
44
    'breezy/doc_generate/conf.py',
 
45
    'breezy/lsprof.py',
45
46
    ]
46
47
 
47
48
LICENSE_EXCEPTIONS = [
48
 
    'bzrlib/_bencode_py.py',
49
 
    'bzrlib/doc_generate/conf.py',
50
 
    'bzrlib/lsprof.py',
 
49
    'breezy/_bencode_py.py',
 
50
    'breezy/doc_generate/conf.py',
 
51
    'breezy/lsprof.py',
51
52
    ]
52
 
# Technically, 'bzrlib/lsprof.py' should be 'bzrlib/util/lsprof.py',
53
 
# (we do not check bzrlib/util/, since that is code bundled from elsewhere)
 
53
# Technically, 'breezy/lsprof.py' should be 'breezy/util/lsprof.py',
 
54
# (we do not check breezy/util/, since that is code bundled from elsewhere)
54
55
# but for compatibility with previous releases, we don't want to move it.
55
56
#
56
57
# sphinx_conf is semi-autogenerated.
74
75
    def find_occurences(self, rule, filename):
75
76
        """Find the number of occurences of rule in a file."""
76
77
        occurences = 0
77
 
        source = file(filename, 'r')
 
78
        source = open(filename, 'r')
78
79
        for line in source:
79
80
            if line.find(rule) > -1:
80
81
                occurences += 1
83
84
    def test_branch_working_tree(self):
84
85
        """Test that the number of uses of working_tree in branch is stable."""
85
86
        occurences = self.find_occurences('self.working_tree()',
86
 
                                          self.source_file_name(bzrlib.branch))
 
87
                                          self.source_file_name(breezy.branch))
87
88
        # do not even think of increasing this number. If you think you need to
88
89
        # increase it, then you almost certainly are doing something wrong as
89
90
        # the relationship from working_tree to branch is one way.
90
91
        # Note that this is an exact equality so that when the number drops,
91
 
        #it is not given a buffer but rather has this test updated immediately.
 
92
        # it is not given a buffer but rather has this test updated immediately.
92
93
        self.assertEqual(0, occurences)
93
94
 
94
95
    def test_branch_WorkingTree(self):
95
96
        """Test that the number of uses of working_tree in branch is stable."""
96
97
        occurences = self.find_occurences('WorkingTree',
97
 
                                          self.source_file_name(bzrlib.branch))
 
98
                                          self.source_file_name(breezy.branch))
98
99
        # Do not even think of increasing this number. If you think you need to
99
100
        # increase it, then you almost certainly are doing something wrong as
100
101
        # the relationship from working_tree to branch is one way.
104
105
 
105
106
class TestSource(TestSourceHelper):
106
107
 
107
 
    def get_bzrlib_dir(self):
108
 
        """Get the path to the root of bzrlib"""
109
 
        source = self.source_file_name(bzrlib)
 
108
    def get_breezy_dir(self):
 
109
        """Get the path to the root of breezy"""
 
110
        source = self.source_file_name(breezy)
110
111
        source_dir = os.path.dirname(source)
111
112
 
112
 
        # Avoid the case when bzrlib is packaged in a zip file
 
113
        # Avoid the case when breezy is packaged in a zip file
113
114
        if not os.path.isdir(source_dir):
114
115
            raise TestSkipped(
115
 
                'Cannot find bzrlib source directory. Expected %s'
 
116
                'Cannot find breezy source directory. Expected %s'
116
117
                % source_dir)
117
118
        return source_dir
118
119
 
119
120
    def get_source_files(self, extensions=None):
120
 
        """Yield all source files for bzr and bzrlib
 
121
        """Yield all source files for bzr and breezy
121
122
 
122
123
        :param our_files_only: If true, exclude files from included libraries
123
124
            or plugins.
124
125
        """
125
 
        bzrlib_dir = self.get_bzrlib_dir()
 
126
        breezy_dir = self.get_breezy_dir()
126
127
        if extensions is None:
127
128
            extensions = ('.py',)
128
129
 
129
130
        # This is the front-end 'bzr' script
130
 
        bzr_path = self.get_bzr_path()
 
131
        bzr_path = self.get_brz_path()
131
132
        yield bzr_path
132
133
 
133
 
        for root, dirs, files in os.walk(bzrlib_dir):
 
134
        for root, dirs, files in os.walk(breezy_dir):
134
135
            for d in dirs:
135
136
                if d.endswith('.tmp'):
136
137
                    dirs.remove(d)
145
146
 
146
147
    def get_source_file_contents(self, extensions=None):
147
148
        for fname in self.get_source_files(extensions=extensions):
148
 
            f = open(fname, 'rb')
149
 
            try:
150
 
                text = f.read()
151
 
            finally:
152
 
                f.close()
153
 
            yield fname, text
 
149
            with open(fname, 'r') as f:
 
150
                yield fname, f.read()
154
151
 
155
152
    def is_our_code(self, fname):
156
 
        """True if it's a "real" part of bzrlib rather than external code"""
 
153
        """True if it's a "real" part of breezy rather than external code"""
157
154
        if '/util/' in fname or '/plugins/' in fname:
158
155
            return False
159
156
        else:
162
159
    def is_copyright_exception(self, fname):
163
160
        """Certain files are allowed to be different"""
164
161
        if not self.is_our_code(fname):
165
 
            # We don't ask that external utilities or plugins be
166
 
            # (C) Canonical Ltd
167
162
            return True
168
163
        for exc in COPYRIGHT_EXCEPTIONS:
169
164
            if fname.endswith(exc):
192
187
        incorrect = []
193
188
 
194
189
        copyright_re = re.compile('#\\s*copyright.*(?=\n)', re.I)
195
 
        copyright_canonical_re = re.compile(
 
190
        copyright_statement_re = re.compile(
196
191
            r'# Copyright \(C\) '  # Opening "# Copyright (C)"
197
 
            r'(\d+)(, \d+)*'       # followed by a series of dates
198
 
            r'.*Canonical Ltd')    # and containing 'Canonical Ltd'.
 
192
            r'(\d+?)((, |-)\d+)*'  # followed by a series of dates
 
193
            r' [^ ]*')             # and then whoever.
199
194
 
200
195
        for fname, text in self.get_source_file_contents(
201
196
                extensions=('.py', '.pyx')):
202
197
            if self.is_copyright_exception(fname):
203
198
                continue
204
 
            match = copyright_canonical_re.search(text)
 
199
            match = copyright_statement_re.search(text)
205
200
            if not match:
206
201
                match = copyright_re.search(text)
207
202
                if match:
211
206
            else:
212
207
                if 'by Canonical' in match.group():
213
208
                    incorrect.append((fname,
214
 
                        'should not have: "by Canonical": %s'
215
 
                        % (match.group(),)))
 
209
                                      'should not have: "by Canonical": %s'
 
210
                                      % (match.group(),)))
216
211
 
217
212
        if incorrect:
218
213
            help_text = ["Some files have missing or incorrect copyright"
220
215
                         "",
221
216
                         "Please either add them to the list of"
222
217
                         " COPYRIGHT_EXCEPTIONS in"
223
 
                         " bzrlib/tests/test_source.py",
 
218
                         " breezy/tests/test_source.py",
224
219
                         # this is broken to prevent a false match
225
220
                         "or add '# Copyright (C)"
226
 
                         " 2007 Canonical Ltd' to these files:",
 
221
                         " 2007 Bazaar hackers' to these files:",
227
222
                         "",
228
 
                        ]
 
223
                         ]
229
224
            for fname, comment in incorrect:
230
225
                help_text.append(fname)
231
226
                help_text.append((' ' * 4) + comment)
265
260
                         "",
266
261
                         "Please either add them to the list of"
267
262
                         " LICENSE_EXCEPTIONS in"
268
 
                         " bzrlib/tests/test_source.py",
 
263
                         " breezy/tests/test_source.py",
269
264
                         "Or add the following text to the beginning:",
270
265
                         gpl_txt]
271
266
            for fname in incorrect:
280
275
            dict_[fname].append(line_no)
281
276
 
282
277
    def _format_message(self, dict_, message):
283
 
        files = ["%s: %s" % (f, ', '.join([str(i + 1) for i in lines]))
284
 
                for f, lines in dict_.items()]
285
 
        files.sort()
 
278
        files = sorted(["%s: %s" % (f, ', '.join([str(i + 1) for i in lines]))
 
279
                        for f, lines in dict_.items()])
286
280
        return message + '\n\n    %s' % ('\n    '.join(files))
287
281
 
288
282
    def test_coding_style(self):
316
310
        problems = []
317
311
        if tabs:
318
312
            problems.append(self._format_message(tabs,
319
 
                'Tab characters were found in the following source files.'
320
 
                '\nThey should either be replaced by "\\t" or by spaces:'))
 
313
                                                 'Tab characters were found in the following source files.'
 
314
                                                 '\nThey should either be replaced by "\\t" or by spaces:'))
321
315
        if illegal_newlines:
322
316
            problems.append(self._format_message(illegal_newlines,
323
 
                'Non-unix newlines were found in the following source files:'))
 
317
                                                 'Non-unix newlines were found in the following source files:'))
324
318
        if no_newline_at_eof:
325
319
            no_newline_at_eof.sort()
326
320
            problems.append("The following source files doesn't have a "
327
 
                "newline at the end:"
328
 
               '\n\n    %s'
329
 
               % ('\n    '.join(no_newline_at_eof)))
 
321
                            "newline at the end:"
 
322
                            '\n\n    %s'
 
323
                            % ('\n    '.join(no_newline_at_eof)))
330
324
        if problems:
331
325
            self.fail('\n\n'.join(problems))
332
326
 
 
327
    def test_flake8(self):
 
328
        self.requireFeature(features.flake8)
 
329
        # Older versions of flake8 don't support the 'paths'
 
330
        # variable
 
331
        new_path = list(sys.path)
 
332
        new_path.insert(
 
333
            0, os.path.join(os.path.dirname(__file__), '..', '..', 'tools'))
 
334
        self.overrideAttr(sys, 'path', new_path)
 
335
        import argparse
 
336
        from flake8.main.application import Application
 
337
        from flake8.formatting.base import BaseFormatter
 
338
        app = Application()
 
339
        app.config = u'setup.cfg'
 
340
        app.jobs = 1
 
341
 
 
342
        class Formatter(BaseFormatter):
 
343
 
 
344
            def __init__(self):
 
345
                self.errors = []
 
346
 
 
347
            def start(self):
 
348
                pass
 
349
 
 
350
            def stop(self):
 
351
                app.file_checker_manager.report()
 
352
 
 
353
            def handle(self, error):
 
354
                self.errors.append(error)
 
355
 
 
356
        try:
 
357
            app.initialize([])
 
358
        except argparse.ArgumentError as e:
 
359
            self.skipTest('broken flake8: %r' % e)
 
360
        app.formatter = Formatter()
 
361
        app.run_checks()
 
362
        app.report()
 
363
        self.assertEqual(app.formatter.errors, [])
 
364
 
333
365
    def test_no_asserts(self):
334
366
        """bzr shouldn't use the 'assert' statement."""
335
367
        # assert causes too much variation between -O and not, and tends to
353
385
                continue
354
386
            if not assert_re.search(text):
355
387
                continue
356
 
            ast = parser.ast2tuple(parser.suite(text))
357
 
            if search(ast):
 
388
            st = parser.suite(text)
 
389
            code = parser.st2tuple(st)
 
390
            if search(code):
358
391
                badfiles.append(fname)
359
392
        if badfiles:
360
393
            self.fail(
370
403
        """
371
404
        both_exc_and_no_exc = []
372
405
        missing_except = []
 
406
        common_classes = ('StaticTuple',)
373
407
        class_re = re.compile(r'^(cdef\s+)?(public\s+)?'
374
408
                              r'(api\s+)?class (\w+).*:', re.MULTILINE)
375
 
        extern_class_re = re.compile(r'## extern cdef class (\w+)',
376
 
                                     re.MULTILINE)
377
409
        except_re = re.compile(
378
410
            r'cdef\s+'        # start with cdef
379
411
            r'([\w *]*?)\s*'  # this is the return signature
383
415
            r'\s*(#\s*cannot[- _]raise)?')  # cannot raise comment
384
416
        for fname, text in self.get_source_file_contents(
385
417
                extensions=('.pyx',)):
386
 
            known_classes = set([m[-1] for m in class_re.findall(text)])
387
 
            known_classes.update(extern_class_re.findall(text))
 
418
            known_classes = {m[-1] for m in class_re.findall(text)}
 
419
            known_classes.update(common_classes)
388
420
            cdefs = except_re.findall(text)
389
421
            for sig, func, exc_clause, no_exc_comment in cdefs:
390
422
                if sig.startswith('api '):
416
448
            error_msg.extend(('', ''))
417
449
        if error_msg:
418
450
            self.fail('\n'.join(error_msg))
419
 
 
420
 
    def test_feature_absolute_import(self):
421
 
        """Using absolute imports means avoiding unnecesary stat and
422
 
        open calls.
423
 
 
424
 
        Make sure that all non-test files have absolute imports enabled.
425
 
        """
426
 
        missing_absolute_import = []
427
 
        for fname, text in self.get_source_file_contents(
428
 
                extensions=('.py', )):
429
 
            if "/tests/" in fname or "test_" in fname:
430
 
                # We don't really care about tests
431
 
                continue
432
 
            if not "from __future__ import absolute_import" in text:
433
 
                missing_absolute_import.append(fname)
434
 
 
435
 
        if missing_absolute_import:
436
 
            self.fail(
437
 
                'The following files do not have absolute_import enabled:\n'
438
 
                '\n' + '\n'.join(missing_absolute_import))