~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/__init__.py

  • Committer: Martin Pool
  • Date: 2006-11-02 10:20:19 UTC
  • mfrom: (2114 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2119.
  • Revision ID: mbp@sourcefrog.net-20061102102019-9a5a02f485dff6f6
merge bzr.dev and reconcile several changes, also some test fixes

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 by Canonical Ltd
 
1
# Copyright (C) 2005, 2006 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
70
70
import bzrlib.trace
71
71
from bzrlib.transport import get_transport
72
72
import bzrlib.transport
73
 
from bzrlib.transport.local import LocalRelpathServer
 
73
from bzrlib.transport.local import LocalURLServer
74
74
from bzrlib.transport.memory import MemoryServer
75
75
from bzrlib.transport.readonly import ReadonlyServer
76
 
from bzrlib.trace import mutter
 
76
from bzrlib.trace import mutter, note
77
77
from bzrlib.tests import TestUtil
78
78
from bzrlib.tests.TestUtil import (
79
79
                          TestSuite,
83
83
import bzrlib.urlutils as urlutils
84
84
from bzrlib.workingtree import WorkingTree, WorkingTreeFormat2
85
85
 
86
 
default_transport = LocalRelpathServer
 
86
default_transport = LocalURLServer
87
87
 
88
88
MODULES_TO_TEST = []
89
89
MODULES_TO_DOCTEST = [
90
 
                      bzrlib.branch,
91
90
                      bzrlib.bundle.serializer,
92
 
                      bzrlib.commands,
93
91
                      bzrlib.errors,
94
92
                      bzrlib.export,
95
93
                      bzrlib.inventory,
97
95
                      bzrlib.lockdir,
98
96
                      bzrlib.merge3,
99
97
                      bzrlib.option,
100
 
                      bzrlib.osutils,
101
98
                      bzrlib.store,
102
 
                      bzrlib.transport,
103
99
                      ]
104
100
 
105
101
 
135
131
            ]
136
132
 
137
133
 
138
 
class _MyResult(unittest._TextTestResult):
139
 
    """Custom TestResult.
 
134
class ExtendedTestResult(unittest._TextTestResult):
 
135
    """Accepts, reports and accumulates the results of running tests.
140
136
 
141
 
    Shows output in a different format, including displaying runtime for tests.
 
137
    Compared to this unittest version this class adds support for profiling,
 
138
    benchmarking, stopping as soon as a test fails,  and skipping tests.
 
139
    There are further-specialized subclasses for different types of display.
142
140
    """
 
141
 
143
142
    stop_early = False
144
143
    
145
 
    def __init__(self, stream, descriptions, verbosity, pb=None,
146
 
                 bench_history=None):
 
144
    def __init__(self, stream, descriptions, verbosity,
 
145
                 bench_history=None,
 
146
                 num_tests=None,
 
147
                 ):
147
148
        """Construct new TestResult.
148
149
 
149
150
        :param bench_history: Optionally, a writable file object to accumulate
150
151
            benchmark results.
151
152
        """
152
153
        unittest._TextTestResult.__init__(self, stream, descriptions, verbosity)
153
 
        self.pb = pb
154
154
        if bench_history is not None:
155
155
            from bzrlib.version import _get_bzr_source_tree
156
156
            src_tree = _get_bzr_source_tree()
166
166
                revision_id = ''
167
167
            bench_history.write("--date %s %s\n" % (time.time(), revision_id))
168
168
        self._bench_history = bench_history
 
169
        self.ui = bzrlib.ui.ui_factory
 
170
        self.num_tests = num_tests
 
171
        self.error_count = 0
 
172
        self.failure_count = 0
 
173
        self.skip_count = 0
 
174
        self.count = 0
 
175
        self._overall_start_time = time.time()
169
176
    
170
177
    def extractBenchmarkTime(self, testCase):
171
178
        """Add a benchmark time for the current test case."""
187
194
        """Format seconds as milliseconds with leading spaces."""
188
195
        return "%5dms" % (1000 * seconds)
189
196
 
190
 
    def _ellipsise_unimportant_words(self, a_string, final_width,
191
 
                                   keep_start=False):
192
 
        """Add ellipses (sp?) for overly long strings.
193
 
        
194
 
        :param keep_start: If true preserve the start of a_string rather
195
 
                           than the end of it.
196
 
        """
197
 
        if keep_start:
198
 
            if len(a_string) > final_width:
199
 
                result = a_string[:final_width-3] + '...'
200
 
            else:
201
 
                result = a_string
202
 
        else:
203
 
            if len(a_string) > final_width:
204
 
                result = '...' + a_string[3-final_width:]
205
 
            else:
206
 
                result = a_string
207
 
        return result.ljust(final_width)
 
197
    def _shortened_test_description(self, test):
 
198
        what = test.id()
 
199
        what = re.sub(r'^bzrlib\.(tests|benchmark)\.', '', what)
 
200
        return what
208
201
 
209
202
    def startTest(self, test):
210
203
        unittest.TestResult.startTest(self, test)
211
 
        # In a short description, the important words are in
212
 
        # the beginning, but in an id, the important words are
213
 
        # at the end
214
 
        SHOW_DESCRIPTIONS = False
215
 
 
216
 
        if not self.showAll and self.dots and self.pb is not None:
217
 
            final_width = 13
218
 
        else:
219
 
            final_width = osutils.terminal_width()
220
 
            final_width = final_width - 15 - 8
221
 
        what = None
222
 
        if SHOW_DESCRIPTIONS:
223
 
            what = test.shortDescription()
224
 
            if what:
225
 
                what = self._ellipsise_unimportant_words(what, final_width, keep_start=True)
226
 
        if what is None:
227
 
            what = test.id()
228
 
            if what.startswith('bzrlib.tests.'):
229
 
                what = what[13:]
230
 
            what = self._ellipsise_unimportant_words(what, final_width)
231
 
        if self.showAll:
232
 
            self.stream.write(what)
233
 
        elif self.dots and self.pb is not None:
234
 
            self.pb.update(what, self.testsRun - 1, None)
235
 
        self.stream.flush()
 
204
        self.report_test_start(test)
236
205
        self._recordTestStartTime()
237
206
 
238
207
    def _recordTestStartTime(self):
249
218
        if setKeepLogfile is not None:
250
219
            setKeepLogfile()
251
220
        self.extractBenchmarkTime(test)
252
 
        if self.showAll:
253
 
            self.stream.writeln("ERROR %s" % self._testTimeString())
254
 
        elif self.dots and self.pb is None:
255
 
            self.stream.write('E')
256
 
        elif self.dots:
257
 
            self.pb.update(self._ellipsise_unimportant_words('ERROR', 13), self.testsRun, None)
258
 
            self.pb.note(self._ellipsise_unimportant_words(
259
 
                            test.id() + ': ERROR',
260
 
                            osutils.terminal_width()))
261
 
        self.stream.flush()
 
221
        self.report_error(test, err)
262
222
        if self.stop_early:
263
223
            self.stop()
264
224
 
270
230
        if setKeepLogfile is not None:
271
231
            setKeepLogfile()
272
232
        self.extractBenchmarkTime(test)
273
 
        if self.showAll:
274
 
            self.stream.writeln(" FAIL %s" % self._testTimeString())
275
 
        elif self.dots and self.pb is None:
276
 
            self.stream.write('F')
277
 
        elif self.dots:
278
 
            self.pb.update(self._ellipsise_unimportant_words('FAIL', 13), self.testsRun, None)
279
 
            self.pb.note(self._ellipsise_unimportant_words(
280
 
                            test.id() + ': FAIL',
281
 
                            osutils.terminal_width()))
282
 
        self.stream.flush()
 
233
        self.report_failure(test, err)
283
234
        if self.stop_early:
284
235
            self.stop()
285
236
 
290
241
                self._bench_history.write("%s %s\n" % (
291
242
                    self._formatTime(self._benchmarkTime),
292
243
                    test.id()))
293
 
        if self.showAll:
294
 
            self.stream.writeln('   OK %s' % self._testTimeString())
295
 
            for bench_called, stats in getattr(test, '_benchcalls', []):
296
 
                self.stream.writeln('LSProf output for %s(%s, %s)' % bench_called)
297
 
                stats.pprint(file=self.stream)
298
 
        elif self.dots and self.pb is None:
299
 
            self.stream.write('~')
300
 
        elif self.dots:
301
 
            self.pb.update(self._ellipsise_unimportant_words('OK', 13), self.testsRun, None)
302
 
        self.stream.flush()
 
244
        self.report_success(test)
303
245
        unittest.TestResult.addSuccess(self, test)
304
246
 
305
247
    def addSkipped(self, test, skip_excinfo):
306
248
        self.extractBenchmarkTime(test)
307
 
        if self.showAll:
308
 
            print >>self.stream, ' SKIP %s' % self._testTimeString()
309
 
            print >>self.stream, '     %s' % skip_excinfo[1]
310
 
        elif self.dots and self.pb is None:
311
 
            self.stream.write('S')
312
 
        elif self.dots:
313
 
            self.pb.update(self._ellipsise_unimportant_words('SKIP', 13), self.testsRun, None)
314
 
        self.stream.flush()
 
249
        self.report_skip(test, skip_excinfo)
315
250
        # seems best to treat this as success from point-of-view of unittest
316
251
        # -- it actually does nothing so it barely matters :)
317
252
        try:
337
272
            self.stream.writeln(self.separator2)
338
273
            self.stream.writeln("%s" % err)
339
274
 
 
275
    def finished(self):
 
276
        pass
 
277
 
 
278
    def report_cleaning_up(self):
 
279
        pass
 
280
 
 
281
    def report_success(self, test):
 
282
        pass
 
283
 
 
284
 
 
285
class TextTestResult(ExtendedTestResult):
 
286
    """Displays progress and results of tests in text form"""
 
287
 
 
288
    def __init__(self, *args, **kw):
 
289
        ExtendedTestResult.__init__(self, *args, **kw)
 
290
        self.pb = self.ui.nested_progress_bar()
 
291
        self.pb.show_pct = False
 
292
        self.pb.show_spinner = False
 
293
        self.pb.show_eta = False, 
 
294
        self.pb.show_count = False
 
295
        self.pb.show_bar = False
 
296
 
 
297
    def report_starting(self):
 
298
        self.pb.update('[test 0/%d] starting...' % (self.num_tests))
 
299
 
 
300
    def _progress_prefix_text(self):
 
301
        a = '[%d' % self.count
 
302
        if self.num_tests is not None:
 
303
            a +='/%d' % self.num_tests
 
304
        a += ' in %ds' % (time.time() - self._overall_start_time)
 
305
        if self.error_count:
 
306
            a += ', %d errors' % self.error_count
 
307
        if self.failure_count:
 
308
            a += ', %d failed' % self.failure_count
 
309
        if self.skip_count:
 
310
            a += ', %d skipped' % self.skip_count
 
311
        a += ']'
 
312
        return a
 
313
 
 
314
    def report_test_start(self, test):
 
315
        self.count += 1
 
316
        self.pb.update(
 
317
                self._progress_prefix_text()
 
318
                + ' ' 
 
319
                + self._shortened_test_description(test))
 
320
 
 
321
    def report_error(self, test, err):
 
322
        self.error_count += 1
 
323
        self.pb.note('ERROR: %s\n    %s\n' % (
 
324
            self._shortened_test_description(test),
 
325
            err[1],
 
326
            ))
 
327
 
 
328
    def report_failure(self, test, err):
 
329
        self.failure_count += 1
 
330
        self.pb.note('FAIL: %s\n    %s\n' % (
 
331
            self._shortened_test_description(test),
 
332
            err[1],
 
333
            ))
 
334
 
 
335
    def report_skip(self, test, skip_excinfo):
 
336
        self.skip_count += 1
 
337
        if False:
 
338
            # at the moment these are mostly not things we can fix
 
339
            # and so they just produce stipple; use the verbose reporter
 
340
            # to see them.
 
341
            if False:
 
342
                # show test and reason for skip
 
343
                self.pb.note('SKIP: %s\n    %s\n' % (
 
344
                    self._shortened_test_description(test),
 
345
                    skip_excinfo[1]))
 
346
            else:
 
347
                # since the class name was left behind in the still-visible
 
348
                # progress bar...
 
349
                self.pb.note('SKIP: %s' % (skip_excinfo[1]))
 
350
 
 
351
    def report_cleaning_up(self):
 
352
        self.pb.update('cleaning up...')
 
353
 
 
354
    def finished(self):
 
355
        self.pb.finished()
 
356
 
 
357
 
 
358
class VerboseTestResult(ExtendedTestResult):
 
359
    """Produce long output, with one line per test run plus times"""
 
360
 
 
361
    def _ellipsize_to_right(self, a_string, final_width):
 
362
        """Truncate and pad a string, keeping the right hand side"""
 
363
        if len(a_string) > final_width:
 
364
            result = '...' + a_string[3-final_width:]
 
365
        else:
 
366
            result = a_string
 
367
        return result.ljust(final_width)
 
368
 
 
369
    def report_starting(self):
 
370
        self.stream.write('running %d tests...\n' % self.num_tests)
 
371
 
 
372
    def report_test_start(self, test):
 
373
        self.count += 1
 
374
        name = self._shortened_test_description(test)
 
375
        self.stream.write(self._ellipsize_to_right(name, 60))
 
376
        self.stream.flush()
 
377
 
 
378
    def report_error(self, test, err):
 
379
        self.error_count += 1
 
380
        self.stream.writeln('ERROR %s\n    %s' 
 
381
                % (self._testTimeString(), err[1]))
 
382
 
 
383
    def report_failure(self, test, err):
 
384
        self.failure_count += 1
 
385
        self.stream.writeln('FAIL %s\n    %s'
 
386
                % (self._testTimeString(), err[1]))
 
387
 
 
388
    def report_success(self, test):
 
389
        self.stream.writeln('   OK %s' % self._testTimeString())
 
390
        for bench_called, stats in getattr(test, '_benchcalls', []):
 
391
            self.stream.writeln('LSProf output for %s(%s, %s)' % bench_called)
 
392
            stats.pprint(file=self.stream)
 
393
        self.stream.flush()
 
394
 
 
395
    def report_skip(self, test, skip_excinfo):
 
396
        print >>self.stream, ' SKIP %s' % self._testTimeString()
 
397
        print >>self.stream, '     %s' % skip_excinfo[1]
 
398
 
340
399
 
341
400
class TextTestRunner(object):
342
401
    stop_on_failure = False
346
405
                 descriptions=0,
347
406
                 verbosity=1,
348
407
                 keep_output=False,
349
 
                 pb=None,
350
408
                 bench_history=None):
351
409
        self.stream = unittest._WritelnDecorator(stream)
352
410
        self.descriptions = descriptions
353
411
        self.verbosity = verbosity
354
412
        self.keep_output = keep_output
355
 
        self.pb = pb
356
413
        self._bench_history = bench_history
357
414
 
358
 
    def _makeResult(self):
359
 
        result = _MyResult(self.stream,
360
 
                           self.descriptions,
361
 
                           self.verbosity,
362
 
                           pb=self.pb,
363
 
                           bench_history=self._bench_history)
364
 
        result.stop_early = self.stop_on_failure
365
 
        return result
366
 
 
367
415
    def run(self, test):
368
416
        "Run the given test case or test suite."
369
 
        result = self._makeResult()
370
417
        startTime = time.time()
371
 
        if self.pb is not None:
372
 
            self.pb.update('Running tests', 0, test.countTestCases())
 
418
        if self.verbosity == 1:
 
419
            result_class = TextTestResult
 
420
        elif self.verbosity >= 2:
 
421
            result_class = VerboseTestResult
 
422
        result = result_class(self.stream,
 
423
                              self.descriptions,
 
424
                              self.verbosity,
 
425
                              bench_history=self._bench_history,
 
426
                              num_tests=test.countTestCases(),
 
427
                              )
 
428
        result.stop_early = self.stop_on_failure
 
429
        result.report_starting()
373
430
        test.run(result)
374
431
        stopTime = time.time()
375
432
        timeTaken = stopTime - startTime
390
447
            self.stream.writeln(")")
391
448
        else:
392
449
            self.stream.writeln("OK")
393
 
        if self.pb is not None:
394
 
            self.pb.update('Cleaning up', 0, 1)
 
450
        result.report_cleaning_up()
395
451
        # This is still a little bogus, 
396
452
        # but only a little. Folk not using our testrunner will
397
453
        # have to delete their temp directories themselves.
412
468
                        sys.getfilesystemencoding())
413
469
                osutils.rmtree(test_root)
414
470
        else:
415
 
            if self.pb is not None:
416
 
                self.pb.note("Failed tests working directories are in '%s'\n",
417
 
                             test_root)
418
 
            else:
419
 
                self.stream.writeln(
420
 
                    "Failed tests working directories are in '%s'\n" %
421
 
                    test_root)
 
471
            note("Failed tests working directories are in '%s'\n", test_root)
422
472
        TestCaseWithMemoryTransport.TEST_ROOT = None
423
 
        if self.pb is not None:
424
 
            self.pb.clear()
 
473
        result.finished()
425
474
        return result
426
475
 
427
476
 
507
556
        unittest.TestCase.setUp(self)
508
557
        self._cleanEnvironment()
509
558
        bzrlib.trace.disable_default_logging()
 
559
        self._silenceUI()
510
560
        self._startLogFile()
511
561
        self._benchcalls = []
512
562
        self._benchtime = None
513
563
 
 
564
    def _silenceUI(self):
 
565
        """Turn off UI for duration of test"""
 
566
        # by default the UI is off; tests can turn it on if they want it.
 
567
        saved = bzrlib.ui.ui_factory
 
568
        def _restore():
 
569
            bzrlib.ui.ui_factory = saved
 
570
        bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
 
571
        self.addCleanup(_restore)
 
572
 
514
573
    def _ndiff_strings(self, a, b):
515
574
        """Return ndiff between two strings containing lines.
516
575
        
607
666
            a_callable(*args, **kwargs).
608
667
        """
609
668
        local_warnings = []
610
 
        def capture_warnings(msg, cls, stacklevel=None):
 
669
        def capture_warnings(msg, cls=None, stacklevel=None):
611
670
            # we've hooked into a deprecation specific callpath,
612
671
            # only deprecations should getting sent via it.
613
672
            self.assertEqual(cls, DeprecationWarning)
931
990
            The values must be strings. The change will only occur in the
932
991
            child, so you don't need to fix the environment after running.
933
992
        :param universal_newlines: Convert CRLF => LF
 
993
        :param allow_plugins: By default the subprocess is run with
 
994
            --no-plugins to ensure test reproducibility. Also, it is possible
 
995
            for system-wide plugins to create unexpected output on stderr,
 
996
            which can cause unnecessary test failures.
934
997
        """
935
998
        env_changes = kwargs.get('env_changes', {})
936
999
        working_dir = kwargs.get('working_dir', None)
 
1000
        allow_plugins = kwargs.get('allow_plugins', False)
937
1001
        process = self.start_bzr_subprocess(args, env_changes=env_changes,
938
 
                                            working_dir=working_dir)
 
1002
                                            working_dir=working_dir,
 
1003
                                            allow_plugins=allow_plugins)
939
1004
        # We distinguish between retcode=None and retcode not passed.
940
1005
        supplied_retcode = kwargs.get('retcode', 0)
941
1006
        return self.finish_bzr_subprocess(process, retcode=supplied_retcode,
944
1009
 
945
1010
    def start_bzr_subprocess(self, process_args, env_changes=None,
946
1011
                             skip_if_plan_to_signal=False,
947
 
                             working_dir=None):
 
1012
                             working_dir=None,
 
1013
                             allow_plugins=False):
948
1014
        """Start bzr in a subprocess for testing.
949
1015
 
950
1016
        This starts a new Python interpreter and runs bzr in there.
961
1027
            child, so you don't need to fix the environment after running.
962
1028
        :param skip_if_plan_to_signal: raise TestSkipped when true and os.kill
963
1029
            is not available.
 
1030
        :param allow_plugins: If False (default) pass --no-plugins to bzr.
964
1031
 
965
1032
        :returns: Popen object for the started process.
966
1033
        """
992
1059
            # so we will avoid using it on all platforms, just to
993
1060
            # make sure the code path is used, and we don't break on win32
994
1061
            cleanup_environment()
995
 
            process = Popen([sys.executable, bzr_path] + list(process_args),
996
 
                             stdin=PIPE, stdout=PIPE, stderr=PIPE)
 
1062
            command = [sys.executable, bzr_path]
 
1063
            if not allow_plugins:
 
1064
                command.append('--no-plugins')
 
1065
            command.extend(process_args)
 
1066
            process = self._popen(command, stdin=PIPE, stdout=PIPE, stderr=PIPE)
997
1067
        finally:
998
1068
            restore_environment()
999
1069
            if cwd is not None:
1001
1071
 
1002
1072
        return process
1003
1073
 
 
1074
    def _popen(self, *args, **kwargs):
 
1075
        """Place a call to Popen.
 
1076
 
 
1077
        Allows tests to override this method to intercept the calls made to
 
1078
        Popen for introspection.
 
1079
        """
 
1080
        return Popen(*args, **kwargs)
 
1081
 
1004
1082
    def get_bzr_path(self):
1005
1083
        """Return the path of the 'bzr' executable for this test suite."""
1006
1084
        bzr_path = os.path.dirname(os.path.dirname(bzrlib.__file__))+'/bzr'
1293
1371
        made_control = self.make_bzrdir(relpath, format=format)
1294
1372
        return made_control.create_repository(shared=shared)
1295
1373
 
1296
 
    def make_branch_and_memory_tree(self, relpath):
 
1374
    def make_branch_and_memory_tree(self, relpath, format=None):
1297
1375
        """Create a branch on the default transport and a MemoryTree for it."""
1298
 
        b = self.make_branch(relpath)
 
1376
        b = self.make_branch(relpath, format=format)
1299
1377
        return memorytree.MemoryTree.create_on_branch(b)
1300
1378
 
1301
1379
    def overrideEnvironmentForTesting(self):
1496
1574
    def setUp(self):
1497
1575
        super(TestCaseWithTransport, self).setUp()
1498
1576
        self.__server = None
1499
 
        self.transport_server = default_transport
1500
1577
 
1501
1578
 
1502
1579
class ChrootedTestCase(TestCaseWithTransport):
1532
1609
    TestCase._gather_lsprof_in_benchmarks = lsprof_timed
1533
1610
    if verbose:
1534
1611
        verbosity = 2
1535
 
        pb = None
1536
1612
    else:
1537
1613
        verbosity = 1
1538
 
        pb = progress.ProgressBar()
1539
1614
    runner = TextTestRunner(stream=sys.stdout,
1540
1615
                            descriptions=0,
1541
1616
                            verbosity=verbosity,
1542
1617
                            keep_output=keep_output,
1543
 
                            pb=pb,
1544
1618
                            bench_history=bench_history)
1545
1619
    runner.stop_on_failure=stop_on_failure
1546
1620
    if pattern != '.*':
1618
1692
                   'bzrlib.tests.test_inv',
1619
1693
                   'bzrlib.tests.test_knit',
1620
1694
                   'bzrlib.tests.test_lazy_import',
 
1695
                   'bzrlib.tests.test_lazy_regex',
1621
1696
                   'bzrlib.tests.test_lockdir',
1622
1697
                   'bzrlib.tests.test_lockable_files',
1623
1698
                   'bzrlib.tests.test_log',
1636
1711
                   'bzrlib.tests.test_plugins',
1637
1712
                   'bzrlib.tests.test_progress',
1638
1713
                   'bzrlib.tests.test_reconcile',
 
1714
                   'bzrlib.tests.test_registry',
1639
1715
                   'bzrlib.tests.test_repository',
1640
1716
                   'bzrlib.tests.test_revert',
1641
1717
                   'bzrlib.tests.test_revision',
1689
1765
    for m in MODULES_TO_TEST:
1690
1766
        suite.addTest(loader.loadTestsFromModule(m))
1691
1767
    for m in MODULES_TO_DOCTEST:
1692
 
        suite.addTest(doctest.DocTestSuite(m))
 
1768
        try:
 
1769
            suite.addTest(doctest.DocTestSuite(m))
 
1770
        except ValueError, e:
 
1771
            print '**failed to get doctest for: %s\n%s' %(m,e)
 
1772
            raise
1693
1773
    for name, plugin in bzrlib.plugin.all_plugins().items():
1694
1774
        if getattr(plugin, 'test_suite', None) is not None:
1695
1775
            suite.addTest(plugin.test_suite())