~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/__init__.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2010-10-01 22:33:10 UTC
  • mfrom: (5452.3.1 doc-fix)
  • Revision ID: pqm@pqm.ubuntu.com-20101001223310-t8adqw9m9ogrvnlc
(jameinel) fixed link to main smart server doc from http smart server doc
 (dmuir)

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
"""Testing framework extensions"""
17
18
 
18
19
# TODO: Perhaps there should be an API to find out if bzr running under the
19
20
# test suite -- some plugins might want to avoid making intrusive changes if
28
29
 
29
30
import atexit
30
31
import codecs
31
 
from copy import copy
 
32
import copy
32
33
from cStringIO import StringIO
33
34
import difflib
34
35
import doctest
35
36
import errno
 
37
import itertools
36
38
import logging
37
 
import math
38
39
import os
39
 
from pprint import pformat
 
40
import platform
 
41
import pprint
40
42
import random
41
43
import re
42
44
import shlex
43
45
import stat
44
 
from subprocess import Popen, PIPE, STDOUT
 
46
import subprocess
45
47
import sys
46
48
import tempfile
47
49
import threading
69
71
    lock as _mod_lock,
70
72
    memorytree,
71
73
    osutils,
72
 
    progress,
73
74
    ui,
74
75
    urlutils,
75
76
    registry,
 
77
    transport as _mod_transport,
76
78
    workingtree,
77
79
    )
78
80
import bzrlib.branch
102
104
    )
103
105
import bzrlib.trace
104
106
from bzrlib.transport import (
105
 
    get_transport,
106
107
    memory,
107
108
    pathfilter,
108
109
    )
109
 
import bzrlib.transport
110
110
from bzrlib.trace import mutter, note
111
111
from bzrlib.tests import (
112
112
    test_server,
113
113
    TestUtil,
 
114
    treeshape,
114
115
    )
115
 
from bzrlib.tests.http_server import HttpServer
116
 
from bzrlib.tests.TestUtil import (
117
 
                          TestSuite,
118
 
                          TestLoader,
119
 
                          )
120
 
from bzrlib.tests.treeshape import build_tree_contents
121
116
from bzrlib.ui import NullProgressView
122
117
from bzrlib.ui.text import TextUIFactory
123
118
import bzrlib.version_info_formats.format_custom
139
134
SUBUNIT_SEEK_SET = 0
140
135
SUBUNIT_SEEK_CUR = 1
141
136
 
 
137
# These are intentionally brought into this namespace. That way plugins, etc
 
138
# can just "from bzrlib.tests import TestCase, TestLoader, etc"
 
139
TestSuite = TestUtil.TestSuite
 
140
TestLoader = TestUtil.TestLoader
142
141
 
143
 
class ExtendedTestResult(unittest._TextTestResult):
 
142
class ExtendedTestResult(testtools.TextTestResult):
144
143
    """Accepts, reports and accumulates the results of running tests.
145
144
 
146
145
    Compared to the unittest version this class adds support for
167
166
        :param bench_history: Optionally, a writable file object to accumulate
168
167
            benchmark results.
169
168
        """
170
 
        unittest._TextTestResult.__init__(self, stream, descriptions, verbosity)
 
169
        testtools.TextTestResult.__init__(self, stream)
171
170
        if bench_history is not None:
172
171
            from bzrlib.version import _get_bzr_source_tree
173
172
            src_tree = _get_bzr_source_tree()
194
193
        self.count = 0
195
194
        self._overall_start_time = time.time()
196
195
        self._strict = strict
 
196
        self._first_thread_leaker_id = None
 
197
        self._tests_leaking_threads_count = 0
197
198
 
198
199
    def stopTestRun(self):
199
200
        run = self.testsRun
200
201
        actionTaken = "Ran"
201
202
        stopTime = time.time()
202
203
        timeTaken = stopTime - self.startTime
203
 
        self.printErrors()
204
 
        self.stream.writeln(self.separator2)
205
 
        self.stream.writeln("%s %d test%s in %.3fs" % (actionTaken,
 
204
        # GZ 2010-07-19: Seems testtools has no printErrors method, and though
 
205
        #                the parent class method is similar have to duplicate
 
206
        self._show_list('ERROR', self.errors)
 
207
        self._show_list('FAIL', self.failures)
 
208
        self.stream.write(self.sep2)
 
209
        self.stream.write("%s %d test%s in %.3fs\n\n" % (actionTaken,
206
210
                            run, run != 1 and "s" or "", timeTaken))
207
 
        self.stream.writeln()
208
211
        if not self.wasSuccessful():
209
212
            self.stream.write("FAILED (")
210
213
            failed, errored = map(len, (self.failures, self.errors))
217
220
                if failed or errored: self.stream.write(", ")
218
221
                self.stream.write("known_failure_count=%d" %
219
222
                    self.known_failure_count)
220
 
            self.stream.writeln(")")
 
223
            self.stream.write(")\n")
221
224
        else:
222
225
            if self.known_failure_count:
223
 
                self.stream.writeln("OK (known_failures=%d)" %
 
226
                self.stream.write("OK (known_failures=%d)\n" %
224
227
                    self.known_failure_count)
225
228
            else:
226
 
                self.stream.writeln("OK")
 
229
                self.stream.write("OK\n")
227
230
        if self.skip_count > 0:
228
231
            skipped = self.skip_count
229
 
            self.stream.writeln('%d test%s skipped' %
 
232
            self.stream.write('%d test%s skipped\n' %
230
233
                                (skipped, skipped != 1 and "s" or ""))
231
234
        if self.unsupported:
232
235
            for feature, count in sorted(self.unsupported.items()):
233
 
                self.stream.writeln("Missing feature '%s' skipped %d tests." %
 
236
                self.stream.write("Missing feature '%s' skipped %d tests.\n" %
234
237
                    (feature, count))
235
238
        if self._strict:
236
239
            ok = self.wasStrictlySuccessful()
237
240
        else:
238
241
            ok = self.wasSuccessful()
239
 
        if TestCase._first_thread_leaker_id:
 
242
        if self._first_thread_leaker_id:
240
243
            self.stream.write(
241
244
                '%s is leaking threads among %d leaking tests.\n' % (
242
 
                TestCase._first_thread_leaker_id,
243
 
                TestCase._leaking_threads_tests))
 
245
                self._first_thread_leaker_id,
 
246
                self._tests_leaking_threads_count))
244
247
            # We don't report the main thread as an active one.
245
248
            self.stream.write(
246
249
                '%d non-main threads were left active in the end.\n'
247
 
                % (TestCase._active_threads - 1))
 
250
                % (len(self._active_threads) - 1))
248
251
 
249
252
    def getDescription(self, test):
250
253
        return test.id()
274
277
 
275
278
    def _shortened_test_description(self, test):
276
279
        what = test.id()
277
 
        what = re.sub(r'^bzrlib\.(tests|benchmarks)\.', '', what)
 
280
        what = re.sub(r'^bzrlib\.tests\.', '', what)
278
281
        return what
279
282
 
280
283
    def startTest(self, test):
281
 
        unittest.TestResult.startTest(self, test)
 
284
        super(ExtendedTestResult, self).startTest(test)
282
285
        if self.count == 0:
283
286
            self.startTests()
 
287
        self.count += 1
284
288
        self.report_test_start(test)
285
289
        test.number = self.count
286
290
        self._recordTestStartTime()
 
291
        # Only check for thread leaks if the test case supports cleanups
 
292
        addCleanup = getattr(test, "addCleanup", None)
 
293
        if addCleanup is not None:
 
294
            addCleanup(self._check_leaked_threads, test)
287
295
 
288
296
    def startTests(self):
289
 
        import platform
290
 
        if getattr(sys, 'frozen', None) is None:
291
 
            bzr_path = osutils.realpath(sys.argv[0])
292
 
        else:
293
 
            bzr_path = sys.executable
294
 
        self.stream.write(
295
 
            'bzr selftest: %s\n' % (bzr_path,))
296
 
        self.stream.write(
297
 
            '   %s\n' % (
298
 
                    bzrlib.__path__[0],))
299
 
        self.stream.write(
300
 
            '   bzr-%s python-%s %s\n' % (
301
 
                    bzrlib.version_string,
302
 
                    bzrlib._format_version_tuple(sys.version_info),
303
 
                    platform.platform(aliased=1),
304
 
                    ))
305
 
        self.stream.write('\n')
 
297
        self.report_tests_starting()
 
298
        self._active_threads = threading.enumerate()
 
299
 
 
300
    def _check_leaked_threads(self, test):
 
301
        """See if any threads have leaked since last call
 
302
 
 
303
        A sample of live threads is stored in the _active_threads attribute,
 
304
        when this method runs it compares the current live threads and any not
 
305
        in the previous sample are treated as having leaked.
 
306
        """
 
307
        now_active_threads = set(threading.enumerate())
 
308
        threads_leaked = now_active_threads.difference(self._active_threads)
 
309
        if threads_leaked:
 
310
            self._report_thread_leak(test, threads_leaked, now_active_threads)
 
311
            self._tests_leaking_threads_count += 1
 
312
            if self._first_thread_leaker_id is None:
 
313
                self._first_thread_leaker_id = test.id()
 
314
            self._active_threads = now_active_threads
306
315
 
307
316
    def _recordTestStartTime(self):
308
317
        """Record that a test has started."""
309
318
        self._start_time = time.time()
310
319
 
311
 
    def _cleanupLogFile(self, test):
312
 
        # We can only do this if we have one of our TestCases, not if
313
 
        # we have a doctest.
314
 
        setKeepLogfile = getattr(test, 'setKeepLogfile', None)
315
 
        if setKeepLogfile is not None:
316
 
            setKeepLogfile()
317
 
 
318
320
    def addError(self, test, err):
319
321
        """Tell result that test finished with an error.
320
322
 
322
324
        fails with an unexpected error.
323
325
        """
324
326
        self._post_mortem()
325
 
        unittest.TestResult.addError(self, test, err)
 
327
        super(ExtendedTestResult, self).addError(test, err)
326
328
        self.error_count += 1
327
329
        self.report_error(test, err)
328
330
        if self.stop_early:
329
331
            self.stop()
330
 
        self._cleanupLogFile(test)
331
332
 
332
333
    def addFailure(self, test, err):
333
334
        """Tell result that test failed.
336
337
        fails because e.g. an assert() method failed.
337
338
        """
338
339
        self._post_mortem()
339
 
        unittest.TestResult.addFailure(self, test, err)
 
340
        super(ExtendedTestResult, self).addFailure(test, err)
340
341
        self.failure_count += 1
341
342
        self.report_failure(test, err)
342
343
        if self.stop_early:
343
344
            self.stop()
344
 
        self._cleanupLogFile(test)
345
345
 
346
346
    def addSuccess(self, test, details=None):
347
347
        """Tell result that test completed successfully.
355
355
                    self._formatTime(benchmark_time),
356
356
                    test.id()))
357
357
        self.report_success(test)
358
 
        self._cleanupLogFile(test)
359
 
        unittest.TestResult.addSuccess(self, test)
 
358
        super(ExtendedTestResult, self).addSuccess(test)
360
359
        test._log_contents = ''
361
360
 
362
361
    def addExpectedFailure(self, test, err):
399
398
        else:
400
399
            raise errors.BzrError("Unknown whence %r" % whence)
401
400
 
402
 
    def report_cleaning_up(self):
403
 
        pass
 
401
    def report_tests_starting(self):
 
402
        """Display information before the test run begins"""
 
403
        if getattr(sys, 'frozen', None) is None:
 
404
            bzr_path = osutils.realpath(sys.argv[0])
 
405
        else:
 
406
            bzr_path = sys.executable
 
407
        self.stream.write(
 
408
            'bzr selftest: %s\n' % (bzr_path,))
 
409
        self.stream.write(
 
410
            '   %s\n' % (
 
411
                    bzrlib.__path__[0],))
 
412
        self.stream.write(
 
413
            '   bzr-%s python-%s %s\n' % (
 
414
                    bzrlib.version_string,
 
415
                    bzrlib._format_version_tuple(sys.version_info),
 
416
                    platform.platform(aliased=1),
 
417
                    ))
 
418
        self.stream.write('\n')
 
419
 
 
420
    def report_test_start(self, test):
 
421
        """Display information on the test just about to be run"""
 
422
 
 
423
    def _report_thread_leak(self, test, leaked_threads, active_threads):
 
424
        """Display information on a test that leaked one or more threads"""
 
425
        # GZ 2010-09-09: A leak summary reported separately from the general
 
426
        #                thread debugging would be nice. Tests under subunit
 
427
        #                need something not using stream, perhaps adding a
 
428
        #                testtools details object would be fitting.
 
429
        if 'threads' in selftest_debug_flags:
 
430
            self.stream.write('%s is leaking, active is now %d\n' %
 
431
                (test.id(), len(active_threads)))
404
432
 
405
433
    def startTestRun(self):
406
434
        self.startTime = time.time()
443
471
        self.pb.finished()
444
472
        super(TextTestResult, self).stopTestRun()
445
473
 
446
 
    def startTestRun(self):
447
 
        super(TextTestResult, self).startTestRun()
 
474
    def report_tests_starting(self):
 
475
        super(TextTestResult, self).report_tests_starting()
448
476
        self.pb.update('[test 0/%d] Starting' % (self.num_tests))
449
477
 
450
 
    def printErrors(self):
451
 
        # clear the pb to make room for the error listing
452
 
        self.pb.clear()
453
 
        super(TextTestResult, self).printErrors()
454
 
 
455
478
    def _progress_prefix_text(self):
456
479
        # the longer this text, the less space we have to show the test
457
480
        # name...
479
502
        return a
480
503
 
481
504
    def report_test_start(self, test):
482
 
        self.count += 1
483
505
        self.pb.update(
484
506
                self._progress_prefix_text()
485
507
                + ' '
489
511
        return self._shortened_test_description(test)
490
512
 
491
513
    def report_error(self, test, err):
492
 
        self.ui.note('ERROR: %s\n    %s\n' % (
 
514
        self.stream.write('ERROR: %s\n    %s\n' % (
493
515
            self._test_description(test),
494
516
            err[1],
495
517
            ))
496
518
 
497
519
    def report_failure(self, test, err):
498
 
        self.ui.note('FAIL: %s\n    %s\n' % (
 
520
        self.stream.write('FAIL: %s\n    %s\n' % (
499
521
            self._test_description(test),
500
522
            err[1],
501
523
            ))
512
534
    def report_unsupported(self, test, feature):
513
535
        """test cannot be run because feature is missing."""
514
536
 
515
 
    def report_cleaning_up(self):
516
 
        self.pb.update('Cleaning up')
517
 
 
518
537
 
519
538
class VerboseTestResult(ExtendedTestResult):
520
539
    """Produce long output, with one line per test run plus times"""
527
546
            result = a_string
528
547
        return result.ljust(final_width)
529
548
 
530
 
    def startTestRun(self):
531
 
        super(VerboseTestResult, self).startTestRun()
 
549
    def report_tests_starting(self):
532
550
        self.stream.write('running %d tests...\n' % self.num_tests)
 
551
        super(VerboseTestResult, self).report_tests_starting()
533
552
 
534
553
    def report_test_start(self, test):
535
 
        self.count += 1
536
554
        name = self._shortened_test_description(test)
537
555
        width = osutils.terminal_width()
538
556
        if width is not None:
550
568
        return '%s%s' % (indent, err[1])
551
569
 
552
570
    def report_error(self, test, err):
553
 
        self.stream.writeln('ERROR %s\n%s'
 
571
        self.stream.write('ERROR %s\n%s\n'
554
572
                % (self._testTimeString(test),
555
573
                   self._error_summary(err)))
556
574
 
557
575
    def report_failure(self, test, err):
558
 
        self.stream.writeln(' FAIL %s\n%s'
 
576
        self.stream.write(' FAIL %s\n%s\n'
559
577
                % (self._testTimeString(test),
560
578
                   self._error_summary(err)))
561
579
 
562
580
    def report_known_failure(self, test, err):
563
 
        self.stream.writeln('XFAIL %s\n%s'
 
581
        self.stream.write('XFAIL %s\n%s\n'
564
582
                % (self._testTimeString(test),
565
583
                   self._error_summary(err)))
566
584
 
567
585
    def report_success(self, test):
568
 
        self.stream.writeln('   OK %s' % self._testTimeString(test))
 
586
        self.stream.write('   OK %s\n' % self._testTimeString(test))
569
587
        for bench_called, stats in getattr(test, '_benchcalls', []):
570
 
            self.stream.writeln('LSProf output for %s(%s, %s)' % bench_called)
 
588
            self.stream.write('LSProf output for %s(%s, %s)\n' % bench_called)
571
589
            stats.pprint(file=self.stream)
572
590
        # flush the stream so that we get smooth output. This verbose mode is
573
591
        # used to show the output in PQM.
574
592
        self.stream.flush()
575
593
 
576
594
    def report_skip(self, test, reason):
577
 
        self.stream.writeln(' SKIP %s\n%s'
 
595
        self.stream.write(' SKIP %s\n%s\n'
578
596
                % (self._testTimeString(test), reason))
579
597
 
580
598
    def report_not_applicable(self, test, reason):
581
 
        self.stream.writeln('  N/A %s\n    %s'
 
599
        self.stream.write('  N/A %s\n    %s\n'
582
600
                % (self._testTimeString(test), reason))
583
601
 
584
602
    def report_unsupported(self, test, feature):
585
603
        """test cannot be run because feature is missing."""
586
 
        self.stream.writeln("NODEP %s\n    The feature '%s' is not available."
 
604
        self.stream.write("NODEP %s\n    The feature '%s' is not available.\n"
587
605
                %(self._testTimeString(test), feature))
588
606
 
589
607
 
618
636
            encode = codec.encode
619
637
        stream = osutils.UnicodeOrBytesToBytesWriter(encode, stream)
620
638
        stream.encoding = new_encoding
621
 
        self.stream = unittest._WritelnDecorator(stream)
 
639
        self.stream = stream
622
640
        self.descriptions = descriptions
623
641
        self.verbosity = verbosity
624
642
        self._bench_history = bench_history
748
766
    # XXX: Should probably unify more with CannedInputUIFactory or a
749
767
    # particular configuration of TextUIFactory, or otherwise have a clearer
750
768
    # idea of how they're supposed to be different.
751
 
    # See https://bugs.edge.launchpad.net/bzr/+bug/408213
 
769
    # See https://bugs.launchpad.net/bzr/+bug/408213
752
770
 
753
771
    def __init__(self, stdout=None, stderr=None, stdin=None):
754
772
        if stdin is not None:
788
806
    routine, and to build and check bzr trees.
789
807
 
790
808
    In addition to the usual method of overriding tearDown(), this class also
791
 
    allows subclasses to register functions into the _cleanups list, which is
 
809
    allows subclasses to register cleanup functions via addCleanup, which are
792
810
    run in order as the object is torn down.  It's less likely this will be
793
811
    accidentally overlooked.
794
812
    """
795
813
 
796
 
    _active_threads = None
797
 
    _leaking_threads_tests = 0
798
 
    _first_thread_leaker_id = None
799
 
    _log_file_name = None
 
814
    _log_file = None
800
815
    # record lsprof data when performing benchmark calls.
801
816
    _gather_lsprof_in_benchmarks = False
802
817
 
803
818
    def __init__(self, methodName='testMethod'):
804
819
        super(TestCase, self).__init__(methodName)
805
 
        self._cleanups = []
806
820
        self._directory_isolation = True
807
821
        self.exception_handlers.insert(0,
808
822
            (UnavailableFeature, self._do_unsupported_or_skip))
826
840
        self._track_transports()
827
841
        self._track_locks()
828
842
        self._clear_debug_flags()
829
 
        TestCase._active_threads = threading.activeCount()
830
 
        self.addCleanup(self._check_leaked_threads)
831
843
 
832
844
    def debug(self):
833
845
        # debug a frame up.
834
846
        import pdb
835
847
        pdb.Pdb().set_trace(sys._getframe().f_back)
836
848
 
837
 
    def _check_leaked_threads(self):
838
 
        active = threading.activeCount()
839
 
        leaked_threads = active - TestCase._active_threads
840
 
        TestCase._active_threads = active
841
 
        # If some tests make the number of threads *decrease*, we'll consider
842
 
        # that they are just observing old threads dieing, not agressively kill
843
 
        # random threads. So we don't report these tests as leaking. The risk
844
 
        # is that we have false positives that way (the test see 2 threads
845
 
        # going away but leak one) but it seems less likely than the actual
846
 
        # false positives (the test see threads going away and does not leak).
847
 
        if leaked_threads > 0:
848
 
            TestCase._leaking_threads_tests += 1
849
 
            if TestCase._first_thread_leaker_id is None:
850
 
                TestCase._first_thread_leaker_id = self.id()
 
849
    def discardDetail(self, name):
 
850
        """Extend the addDetail, getDetails api so we can remove a detail.
 
851
 
 
852
        eg. bzr always adds the 'log' detail at startup, but we don't want to
 
853
        include it for skipped, xfail, etc tests.
 
854
 
 
855
        It is safe to call this for a detail that doesn't exist, in case this
 
856
        gets called multiple times.
 
857
        """
 
858
        # We cheat. details is stored in __details which means we shouldn't
 
859
        # touch it. but getDetails() returns the dict directly, so we can
 
860
        # mutate it.
 
861
        details = self.getDetails()
 
862
        if name in details:
 
863
            del details[name]
851
864
 
852
865
    def _clear_debug_flags(self):
853
866
        """Prevent externally set debug flags affecting tests.
942
955
 
943
956
    def permit_dir(self, name):
944
957
        """Permit a directory to be used by this test. See permit_url."""
945
 
        name_transport = get_transport(name)
 
958
        name_transport = _mod_transport.get_transport(name)
946
959
        self.permit_url(name)
947
960
        self.permit_url(name_transport.base)
948
961
 
971
984
            try:
972
985
                workingtree.WorkingTree.open(path)
973
986
            except (errors.NotBranchError, errors.NoWorkingTree):
974
 
                return
 
987
                raise TestSkipped('Needs a working tree of bzr sources')
975
988
        finally:
976
989
            self.enable_directory_isolation()
977
990
 
1027
1040
        self.addCleanup(transport_server.stop_server)
1028
1041
        # Obtain a real transport because if the server supplies a password, it
1029
1042
        # will be hidden from the base on the client side.
1030
 
        t = get_transport(transport_server.get_url())
 
1043
        t = _mod_transport.get_transport(transport_server.get_url())
1031
1044
        # Some transport servers effectively chroot the backing transport;
1032
1045
        # others like SFTPServer don't - users of the transport can walk up the
1033
1046
        # transport to read the entire backing transport. This wouldn't matter
1094
1107
            message += '\n'
1095
1108
        raise AssertionError("%snot equal:\na = %s\nb = %s\n"
1096
1109
            % (message,
1097
 
               pformat(a), pformat(b)))
 
1110
               pprint.pformat(a), pprint.pformat(b)))
1098
1111
 
1099
1112
    assertEquals = assertEqual
1100
1113
 
1312
1325
            f.close()
1313
1326
        self.assertEqualDiff(content, s)
1314
1327
 
 
1328
    def assertDocstring(self, expected_docstring, obj):
 
1329
        """Fail if obj does not have expected_docstring"""
 
1330
        if __doc__ is None:
 
1331
            # With -OO the docstring should be None instead
 
1332
            self.assertIs(obj.__doc__, None)
 
1333
        else:
 
1334
            self.assertEqual(expected_docstring, obj.__doc__)
 
1335
 
1315
1336
    def failUnlessExists(self, path):
1316
1337
        """Fail unless path or paths, which may be abs or relative, exist."""
1317
1338
        if not isinstance(path, basestring):
1446
1467
 
1447
1468
        The file is removed as the test is torn down.
1448
1469
        """
1449
 
        fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
1450
 
        self._log_file = os.fdopen(fileno, 'w+')
 
1470
        self._log_file = StringIO()
1451
1471
        self._log_memento = bzrlib.trace.push_log_file(self._log_file)
1452
 
        self._log_file_name = name
1453
1472
        self.addCleanup(self._finishLogFile)
1454
1473
 
1455
1474
    def _finishLogFile(self):
1477
1496
        """
1478
1497
        debug.debug_flags.discard('strict_locks')
1479
1498
 
1480
 
    def addCleanup(self, callable, *args, **kwargs):
1481
 
        """Arrange to run a callable when this case is torn down.
1482
 
 
1483
 
        Callables are run in the reverse of the order they are registered,
1484
 
        ie last-in first-out.
1485
 
        """
1486
 
        self._cleanups.append((callable, args, kwargs))
1487
 
 
1488
1499
    def overrideAttr(self, obj, attr_name, new=_unitialized_attr):
1489
1500
        """Overrides an object attribute restoring it after the test.
1490
1501
 
1515
1526
            'EDITOR': None,
1516
1527
            'BZR_EMAIL': None,
1517
1528
            'BZREMAIL': None, # may still be present in the environment
1518
 
            'EMAIL': None,
 
1529
            'EMAIL': 'jrandom@example.com', # set EMAIL as bzr does not guess
1519
1530
            'BZR_PROGRESS_BAR': None,
1520
1531
            'BZR_LOG': None,
1521
1532
            'BZR_PLUGIN_PATH': None,
1574
1585
        """This test has failed for some known reason."""
1575
1586
        raise KnownFailure(reason)
1576
1587
 
 
1588
    def _suppress_log(self):
 
1589
        """Remove the log info from details."""
 
1590
        self.discardDetail('log')
 
1591
 
1577
1592
    def _do_skip(self, result, reason):
 
1593
        self._suppress_log()
1578
1594
        addSkip = getattr(result, 'addSkip', None)
1579
1595
        if not callable(addSkip):
1580
1596
            result.addSuccess(result)
1583
1599
 
1584
1600
    @staticmethod
1585
1601
    def _do_known_failure(self, result, e):
 
1602
        self._suppress_log()
1586
1603
        err = sys.exc_info()
1587
1604
        addExpectedFailure = getattr(result, 'addExpectedFailure', None)
1588
1605
        if addExpectedFailure is not None:
1596
1613
            reason = 'No reason given'
1597
1614
        else:
1598
1615
            reason = e.args[0]
 
1616
        self._suppress_log ()
1599
1617
        addNotApplicable = getattr(result, 'addNotApplicable', None)
1600
1618
        if addNotApplicable is not None:
1601
1619
            result.addNotApplicable(self, reason)
1603
1621
            self._do_skip(result, reason)
1604
1622
 
1605
1623
    @staticmethod
 
1624
    def _report_skip(self, result, err):
 
1625
        """Override the default _report_skip.
 
1626
 
 
1627
        We want to strip the 'log' detail. If we waint until _do_skip, it has
 
1628
        already been formatted into the 'reason' string, and we can't pull it
 
1629
        out again.
 
1630
        """
 
1631
        self._suppress_log()
 
1632
        super(TestCase, self)._report_skip(self, result, err)
 
1633
 
 
1634
    @staticmethod
 
1635
    def _report_expected_failure(self, result, err):
 
1636
        """Strip the log.
 
1637
 
 
1638
        See _report_skip for motivation.
 
1639
        """
 
1640
        self._suppress_log()
 
1641
        super(TestCase, self)._report_expected_failure(self, result, err)
 
1642
 
 
1643
    @staticmethod
1606
1644
    def _do_unsupported_or_skip(self, result, e):
1607
1645
        reason = e.args[0]
 
1646
        self._suppress_log()
1608
1647
        addNotSupported = getattr(result, 'addNotSupported', None)
1609
1648
        if addNotSupported is not None:
1610
1649
            result.addNotSupported(self, reason)
1657
1696
                unicodestr = self._log_contents.decode('utf8', 'replace')
1658
1697
                self._log_contents = unicodestr.encode('utf8')
1659
1698
            return self._log_contents
1660
 
        import bzrlib.trace
1661
 
        if bzrlib.trace._trace_file:
1662
 
            # flush the log file, to get all content
1663
 
            bzrlib.trace._trace_file.flush()
1664
 
        if self._log_file_name is not None:
1665
 
            logfile = open(self._log_file_name)
1666
 
            try:
1667
 
                log_contents = logfile.read()
1668
 
            finally:
1669
 
                logfile.close()
 
1699
        if self._log_file is not None:
 
1700
            log_contents = self._log_file.getvalue()
1670
1701
            try:
1671
1702
                log_contents.decode('utf8')
1672
1703
            except UnicodeDecodeError:
1673
1704
                unicodestr = log_contents.decode('utf8', 'replace')
1674
1705
                log_contents = unicodestr.encode('utf8')
1675
1706
            if not keep_log_file:
1676
 
                close_attempts = 0
1677
 
                max_close_attempts = 100
1678
 
                first_close_error = None
1679
 
                while close_attempts < max_close_attempts:
1680
 
                    close_attempts += 1
1681
 
                    try:
1682
 
                        self._log_file.close()
1683
 
                    except IOError, ioe:
1684
 
                        if ioe.errno is None:
1685
 
                            # No errno implies 'close() called during
1686
 
                            # concurrent operation on the same file object', so
1687
 
                            # retry.  Probably a thread is trying to write to
1688
 
                            # the log file.
1689
 
                            if first_close_error is None:
1690
 
                                first_close_error = ioe
1691
 
                            continue
1692
 
                        raise
1693
 
                    else:
1694
 
                        break
1695
 
                if close_attempts > 1:
1696
 
                    sys.stderr.write(
1697
 
                        'Unable to close log file on first attempt, '
1698
 
                        'will retry: %s\n' % (first_close_error,))
1699
 
                    if close_attempts == max_close_attempts:
1700
 
                        sys.stderr.write(
1701
 
                            'Unable to close log file after %d attempts.\n'
1702
 
                            % (max_close_attempts,))
1703
1707
                self._log_file = None
1704
1708
                # Permit multiple calls to get_log until we clean it up in
1705
1709
                # finishLogFile
1706
1710
                self._log_contents = log_contents
1707
 
                try:
1708
 
                    os.remove(self._log_file_name)
1709
 
                except OSError, e:
1710
 
                    if sys.platform == 'win32' and e.errno == errno.EACCES:
1711
 
                        sys.stderr.write(('Unable to delete log file '
1712
 
                                             ' %r\n' % self._log_file_name))
1713
 
                    else:
1714
 
                        raise
1715
 
                self._log_file_name = None
1716
1711
            return log_contents
1717
1712
        else:
1718
 
            return "No log file content and no log file name."
 
1713
            return "No log file content."
1719
1714
 
1720
1715
    def get_log(self):
1721
1716
        """Get a unicode string containing the log from bzrlib.trace.
1936
1931
            variables. A value of None will unset the env variable.
1937
1932
            The values must be strings. The change will only occur in the
1938
1933
            child, so you don't need to fix the environment after running.
1939
 
        :param skip_if_plan_to_signal: raise TestSkipped when true and os.kill
1940
 
            is not available.
 
1934
        :param skip_if_plan_to_signal: raise TestSkipped when true and system
 
1935
            doesn't support signalling subprocesses.
1941
1936
        :param allow_plugins: If False (default) pass --no-plugins to bzr.
1942
1937
 
1943
1938
        :returns: Popen object for the started process.
1944
1939
        """
1945
1940
        if skip_if_plan_to_signal:
1946
 
            if not getattr(os, 'kill', None):
1947
 
                raise TestSkipped("os.kill not available.")
 
1941
            if os.name != "posix":
 
1942
                raise TestSkipped("Sending signals not supported")
1948
1943
 
1949
1944
        if env_changes is None:
1950
1945
            env_changes = {}
1977
1972
            if not allow_plugins:
1978
1973
                command.append('--no-plugins')
1979
1974
            command.extend(process_args)
1980
 
            process = self._popen(command, stdin=PIPE, stdout=PIPE, stderr=PIPE)
 
1975
            process = self._popen(command, stdin=subprocess.PIPE,
 
1976
                                  stdout=subprocess.PIPE,
 
1977
                                  stderr=subprocess.PIPE)
1981
1978
        finally:
1982
1979
            restore_environment()
1983
1980
            if cwd is not None:
1991
1988
        Allows tests to override this method to intercept the calls made to
1992
1989
        Popen for introspection.
1993
1990
        """
1994
 
        return Popen(*args, **kwargs)
 
1991
        return subprocess.Popen(*args, **kwargs)
1995
1992
 
1996
1993
    def get_source_path(self):
1997
1994
        """Return the path of the directory containing bzrlib."""
1999
1996
 
2000
1997
    def get_bzr_path(self):
2001
1998
        """Return the path of the 'bzr' executable for this test suite."""
2002
 
        bzr_path = self.get_source_path()+'/bzr'
 
1999
        bzr_path = os.path.join(self.get_source_path(), "bzr")
2003
2000
        if not os.path.isfile(bzr_path):
2004
2001
            # We are probably installed. Assume sys.argv is the right file
2005
2002
            bzr_path = sys.argv[0]
2177
2174
 
2178
2175
        :param relpath: a path relative to the base url.
2179
2176
        """
2180
 
        t = get_transport(self.get_url(relpath))
 
2177
        t = _mod_transport.get_transport(self.get_url(relpath))
2181
2178
        self.assertFalse(t.is_readonly())
2182
2179
        return t
2183
2180
 
2189
2186
 
2190
2187
        :param relpath: a path relative to the base url.
2191
2188
        """
2192
 
        t = get_transport(self.get_readonly_url(relpath))
 
2189
        t = _mod_transport.get_transport(self.get_readonly_url(relpath))
2193
2190
        self.assertTrue(t.is_readonly())
2194
2191
        return t
2195
2192
 
2325
2322
        propagating. This method ensures than a test did not leaked.
2326
2323
        """
2327
2324
        root = TestCaseWithMemoryTransport.TEST_ROOT
2328
 
        self.permit_url(get_transport(root).base)
 
2325
        self.permit_url(_mod_transport.get_transport(root).base)
2329
2326
        wt = workingtree.WorkingTree.open(root)
2330
2327
        last_rev = wt.last_revision()
2331
2328
        if last_rev != 'null:':
2376
2373
            # might be a relative or absolute path
2377
2374
            maybe_a_url = self.get_url(relpath)
2378
2375
            segments = maybe_a_url.rsplit('/', 1)
2379
 
            t = get_transport(maybe_a_url)
 
2376
            t = _mod_transport.get_transport(maybe_a_url)
2380
2377
            if len(segments) > 1 and segments[-1] not in ('', '.'):
2381
2378
                t.ensure_base()
2382
2379
            if format is None:
2399
2396
        made_control = self.make_bzrdir(relpath, format=format)
2400
2397
        return made_control.create_repository(shared=shared)
2401
2398
 
2402
 
    def make_smart_server(self, path):
 
2399
    def make_smart_server(self, path, backing_server=None):
 
2400
        if backing_server is None:
 
2401
            backing_server = self.get_server()
2403
2402
        smart_server = test_server.SmartTCPServer_for_testing()
2404
 
        self.start_server(smart_server, self.get_server())
2405
 
        remote_transport = get_transport(smart_server.get_url()).clone(path)
 
2403
        self.start_server(smart_server, backing_server)
 
2404
        remote_transport = _mod_transport.get_transport(smart_server.get_url()
 
2405
                                                   ).clone(path)
2406
2406
        return remote_transport
2407
2407
 
2408
2408
    def make_branch_and_memory_tree(self, relpath, format=None):
2423
2423
 
2424
2424
    def setUp(self):
2425
2425
        super(TestCaseWithMemoryTransport, self).setUp()
 
2426
        # Ensure that ConnectedTransport doesn't leak sockets
 
2427
        def get_transport_with_cleanup(*args, **kwargs):
 
2428
            t = orig_get_transport(*args, **kwargs)
 
2429
            if isinstance(t, _mod_transport.ConnectedTransport):
 
2430
                self.addCleanup(t.disconnect)
 
2431
            return t
 
2432
 
 
2433
        orig_get_transport = self.overrideAttr(_mod_transport, 'get_transport',
 
2434
                                               get_transport_with_cleanup)
2426
2435
        self._make_test_root()
2427
2436
        self.addCleanup(os.chdir, os.getcwdu())
2428
2437
        self.makeAndChdirToTestDir()
2473
2482
 
2474
2483
    def check_file_contents(self, filename, expect):
2475
2484
        self.log("check contents of file %s" % filename)
2476
 
        contents = file(filename, 'r').read()
 
2485
        f = file(filename)
 
2486
        try:
 
2487
            contents = f.read()
 
2488
        finally:
 
2489
            f.close()
2477
2490
        if contents != expect:
2478
2491
            self.log("expected: %r" % expect)
2479
2492
            self.log("actually: %r" % contents)
2553
2566
                "a list or a tuple. Got %r instead" % (shape,))
2554
2567
        # It's OK to just create them using forward slashes on windows.
2555
2568
        if transport is None or transport.is_readonly():
2556
 
            transport = get_transport(".")
 
2569
            transport = _mod_transport.get_transport(".")
2557
2570
        for name in shape:
2558
2571
            self.assertIsInstance(name, basestring)
2559
2572
            if name[-1] == '/':
2569
2582
                content = "contents of %s%s" % (name.encode('utf-8'), end)
2570
2583
                transport.put_bytes_non_atomic(urlutils.escape(name), content)
2571
2584
 
2572
 
    def build_tree_contents(self, shape):
2573
 
        build_tree_contents(shape)
 
2585
    build_tree_contents = staticmethod(treeshape.build_tree_contents)
2574
2586
 
2575
2587
    def assertInWorkingTree(self, path, root_path='.', tree=None):
2576
2588
        """Assert whether path or paths are in the WorkingTree"""
2717
2729
    """
2718
2730
 
2719
2731
    def setUp(self):
 
2732
        from bzrlib.tests import http_server
2720
2733
        super(ChrootedTestCase, self).setUp()
2721
2734
        if not self.vfs_transport_factory == memory.MemoryServer:
2722
 
            self.transport_readonly_server = HttpServer
 
2735
            self.transport_readonly_server = http_server.HttpServer
2723
2736
 
2724
2737
 
2725
2738
def condition_id_re(pattern):
2728
2741
    :param pattern: A regular expression string.
2729
2742
    :return: A callable that returns True if the re matches.
2730
2743
    """
2731
 
    filter_re = osutils.re_compile_checked(pattern, 0,
2732
 
        'test filter')
 
2744
    filter_re = re.compile(pattern, 0)
2733
2745
    def condition(test):
2734
2746
        test_id = test.id()
2735
2747
        return filter_re.search(test_id)
2987
2999
 
2988
3000
 
2989
3001
def fork_decorator(suite):
 
3002
    if getattr(os, "fork", None) is None:
 
3003
        raise errors.BzrCommandError("platform does not support fork,"
 
3004
            " try --parallel=subprocess instead.")
2990
3005
    concurrency = osutils.local_concurrency()
2991
3006
    if concurrency == 1:
2992
3007
        return suite
3047
3062
    return suite
3048
3063
 
3049
3064
 
3050
 
class TestDecorator(TestSuite):
 
3065
class TestDecorator(TestUtil.TestSuite):
3051
3066
    """A decorator for TestCase/TestSuite objects.
3052
3067
    
3053
3068
    Usually, subclasses should override __iter__(used when flattening test
3056
3071
    """
3057
3072
 
3058
3073
    def __init__(self, suite):
3059
 
        TestSuite.__init__(self)
 
3074
        TestUtil.TestSuite.__init__(self)
3060
3075
        self.addTest(suite)
3061
3076
 
3062
3077
    def countTestCases(self):
3181
3196
 
3182
3197
def partition_tests(suite, count):
3183
3198
    """Partition suite into count lists of tests."""
3184
 
    result = []
3185
 
    tests = list(iter_suite_tests(suite))
3186
 
    tests_per_process = int(math.ceil(float(len(tests)) / count))
3187
 
    for block in range(count):
3188
 
        low_test = block * tests_per_process
3189
 
        high_test = low_test + tests_per_process
3190
 
        process_tests = tests[low_test:high_test]
3191
 
        result.append(process_tests)
3192
 
    return result
 
3199
    # This just assigns tests in a round-robin fashion.  On one hand this
 
3200
    # splits up blocks of related tests that might run faster if they shared
 
3201
    # resources, but on the other it avoids assigning blocks of slow tests to
 
3202
    # just one partition.  So the slowest partition shouldn't be much slower
 
3203
    # than the fastest.
 
3204
    partitions = [list() for i in range(count)]
 
3205
    tests = iter_suite_tests(suite)
 
3206
    for partition, test in itertools.izip(itertools.cycle(partitions), tests):
 
3207
        partition.append(test)
 
3208
    return partitions
 
3209
 
 
3210
 
 
3211
def workaround_zealous_crypto_random():
 
3212
    """Crypto.Random want to help us being secure, but we don't care here.
 
3213
 
 
3214
    This workaround some test failure related to the sftp server. Once paramiko
 
3215
    stop using the controversial API in Crypto.Random, we may get rid of it.
 
3216
    """
 
3217
    try:
 
3218
        from Crypto.Random import atfork
 
3219
        atfork()
 
3220
    except ImportError:
 
3221
        pass
3193
3222
 
3194
3223
 
3195
3224
def fork_for_tests(suite):
3212
3241
            try:
3213
3242
                ProtocolTestCase.run(self, result)
3214
3243
            finally:
3215
 
                os.waitpid(self.pid, os.WNOHANG)
 
3244
                os.waitpid(self.pid, 0)
3216
3245
 
3217
3246
    test_blocks = partition_tests(suite, concurrency)
3218
3247
    for process_tests in test_blocks:
3219
 
        process_suite = TestSuite()
 
3248
        process_suite = TestUtil.TestSuite()
3220
3249
        process_suite.addTests(process_tests)
3221
3250
        c2pread, c2pwrite = os.pipe()
3222
3251
        pid = os.fork()
3223
3252
        if pid == 0:
 
3253
            workaround_zealous_crypto_random()
3224
3254
            try:
3225
3255
                os.close(c2pread)
3226
3256
                # Leave stderr and stdout open so we can see test noise
3287
3317
                '--subunit']
3288
3318
            if '--no-plugins' in sys.argv:
3289
3319
                argv.append('--no-plugins')
3290
 
            # stderr=STDOUT would be ideal, but until we prevent noise on
3291
 
            # stderr it can interrupt the subunit protocol.
3292
 
            process = Popen(argv, stdin=PIPE, stdout=PIPE, stderr=PIPE,
3293
 
                bufsize=1)
 
3320
            # stderr=subprocess.STDOUT would be ideal, but until we prevent
 
3321
            # noise on stderr it can interrupt the subunit protocol.
 
3322
            process = subprocess.Popen(argv, stdin=subprocess.PIPE,
 
3323
                                      stdout=subprocess.PIPE,
 
3324
                                      stderr=subprocess.PIPE,
 
3325
                                      bufsize=1)
3294
3326
            test = TestInSubprocess(process, test_list_file_name)
3295
3327
            result.append(test)
3296
3328
        except:
3345
3377
 
3346
3378
    def startTest(self, test):
3347
3379
        self.profiler = bzrlib.lsprof.BzrProfiler()
 
3380
        # Prevent deadlocks in tests that use lsprof: those tests will
 
3381
        # unavoidably fail.
 
3382
        bzrlib.lsprof.BzrProfiler.profiler_block = 0
3348
3383
        self.profiler.start()
3349
3384
        ForwardingResult.startTest(self, test)
3350
3385
 
3371
3406
#                           rather than failing tests. And no longer raise
3372
3407
#                           LockContention when fctnl locks are not being used
3373
3408
#                           with proper exclusion rules.
 
3409
#   -Ethreads               Will display thread ident at creation/join time to
 
3410
#                           help track thread leaks
3374
3411
selftest_debug_flags = set()
3375
3412
 
3376
3413
 
3609
3646
        'bzrlib.doc',
3610
3647
        'bzrlib.tests.blackbox',
3611
3648
        'bzrlib.tests.commands',
 
3649
        'bzrlib.tests.doc_generate',
3612
3650
        'bzrlib.tests.per_branch',
3613
3651
        'bzrlib.tests.per_bzrdir',
 
3652
        'bzrlib.tests.per_controldir',
 
3653
        'bzrlib.tests.per_controldir_colo',
3614
3654
        'bzrlib.tests.per_foreign_vcs',
3615
3655
        'bzrlib.tests.per_interrepository',
3616
3656
        'bzrlib.tests.per_intertree',
3629
3669
        'bzrlib.tests.per_workingtree',
3630
3670
        'bzrlib.tests.test__annotator',
3631
3671
        'bzrlib.tests.test__bencode',
 
3672
        'bzrlib.tests.test__btree_serializer',
3632
3673
        'bzrlib.tests.test__chk_map',
3633
3674
        'bzrlib.tests.test__dirstate_helpers',
3634
3675
        'bzrlib.tests.test__groupcompress',
3677
3718
        'bzrlib.tests.test_export',
3678
3719
        'bzrlib.tests.test_extract',
3679
3720
        'bzrlib.tests.test_fetch',
 
3721
        'bzrlib.tests.test_fixtures',
3680
3722
        'bzrlib.tests.test_fifo_cache',
3681
3723
        'bzrlib.tests.test_filters',
3682
3724
        'bzrlib.tests.test_ftp_transport',
3703
3745
        'bzrlib.tests.test_knit',
3704
3746
        'bzrlib.tests.test_lazy_import',
3705
3747
        'bzrlib.tests.test_lazy_regex',
 
3748
        'bzrlib.tests.test_library_state',
3706
3749
        'bzrlib.tests.test_lock',
3707
3750
        'bzrlib.tests.test_lockable_files',
3708
3751
        'bzrlib.tests.test_lockdir',
3710
3753
        'bzrlib.tests.test_lru_cache',
3711
3754
        'bzrlib.tests.test_lsprof',
3712
3755
        'bzrlib.tests.test_mail_client',
 
3756
        'bzrlib.tests.test_matchers',
3713
3757
        'bzrlib.tests.test_memorytree',
3714
3758
        'bzrlib.tests.test_merge',
3715
3759
        'bzrlib.tests.test_merge3',
3764
3808
        'bzrlib.tests.test_switch',
3765
3809
        'bzrlib.tests.test_symbol_versioning',
3766
3810
        'bzrlib.tests.test_tag',
 
3811
        'bzrlib.tests.test_test_server',
3767
3812
        'bzrlib.tests.test_testament',
3768
3813
        'bzrlib.tests.test_textfile',
3769
3814
        'bzrlib.tests.test_textmerge',
3775
3820
        'bzrlib.tests.test_transport_log',
3776
3821
        'bzrlib.tests.test_tree',
3777
3822
        'bzrlib.tests.test_treebuilder',
 
3823
        'bzrlib.tests.test_treeshape',
3778
3824
        'bzrlib.tests.test_tsort',
3779
3825
        'bzrlib.tests.test_tuned_gzip',
3780
3826
        'bzrlib.tests.test_ui',
3784
3830
        'bzrlib.tests.test_urlutils',
3785
3831
        'bzrlib.tests.test_version',
3786
3832
        'bzrlib.tests.test_version_info',
 
3833
        'bzrlib.tests.test_versionedfile',
3787
3834
        'bzrlib.tests.test_weave',
3788
3835
        'bzrlib.tests.test_whitebox',
3789
3836
        'bzrlib.tests.test_win32utils',
3795
3842
 
3796
3843
 
3797
3844
def _test_suite_modules_to_doctest():
3798
 
    """Return the list of modules to doctest."""   
 
3845
    """Return the list of modules to doctest."""
 
3846
    if __doc__ is None:
 
3847
        # GZ 2009-03-31: No docstrings with -OO so there's nothing to doctest
 
3848
        return []
3799
3849
    return [
3800
3850
        'bzrlib',
3801
3851
        'bzrlib.branchbuilder',
3808
3858
        'bzrlib.option',
3809
3859
        'bzrlib.symbol_versioning',
3810
3860
        'bzrlib.tests',
 
3861
        'bzrlib.tests.fixtures',
3811
3862
        'bzrlib.timestamp',
3812
3863
        'bzrlib.version_info_formats.format_custom',
3813
3864
        ]
3954
4005
    ...     bzrlib.tests.test_sampler.DemoTest('test_nothing'),
3955
4006
    ...     [('one', dict(param=1)),
3956
4007
    ...      ('two', dict(param=2))],
3957
 
    ...     TestSuite())
 
4008
    ...     TestUtil.TestSuite())
3958
4009
    >>> tests = list(iter_suite_tests(r))
3959
4010
    >>> len(tests)
3960
4011
    2
4007
4058
    :param new_id: The id to assign to it.
4008
4059
    :return: The new test.
4009
4060
    """
4010
 
    new_test = copy(test)
 
4061
    new_test = copy.copy(test)
4011
4062
    new_test.id = lambda: new_id
 
4063
    # XXX: Workaround <https://bugs.launchpad.net/testtools/+bug/637725>, which
 
4064
    # causes cloned tests to share the 'details' dict.  This makes it hard to
 
4065
    # read the test output for parameterized tests, because tracebacks will be
 
4066
    # associated with irrelevant tests.
 
4067
    try:
 
4068
        details = new_test._TestCase__details
 
4069
    except AttributeError:
 
4070
        # must be a different version of testtools than expected.  Do nothing.
 
4071
        pass
 
4072
    else:
 
4073
        # Reset the '__details' dict.
 
4074
        new_test._TestCase__details = {}
4012
4075
    return new_test
4013
4076
 
4014
4077
 
4074
4137
        if test_id != None:
4075
4138
            ui.ui_factory.clear_term()
4076
4139
            sys.stderr.write('\nWhile running: %s\n' % (test_id,))
 
4140
        # Ugly, but the last thing we want here is fail, so bear with it.
 
4141
        printable_e = str(e).decode(osutils.get_user_encoding(), 'replace'
 
4142
                                    ).encode('ascii', 'replace')
4077
4143
        sys.stderr.write('Unable to remove testing dir %s\n%s'
4078
 
                         % (os.path.basename(dirname), e))
 
4144
                         % (os.path.basename(dirname), printable_e))
4079
4145
 
4080
4146
 
4081
4147
class Feature(object):
4311
4377
UnicodeFilename = _UnicodeFilename()
4312
4378
 
4313
4379
 
 
4380
class _ByteStringNamedFilesystem(Feature):
 
4381
    """Is the filesystem based on bytes?"""
 
4382
 
 
4383
    def _probe(self):
 
4384
        if os.name == "posix":
 
4385
            return True
 
4386
        return False
 
4387
 
 
4388
ByteStringNamedFilesystem = _ByteStringNamedFilesystem()
 
4389
 
 
4390
 
4314
4391
class _UTF8Filesystem(Feature):
4315
4392
    """Is the filesystem UTF-8?"""
4316
4393
 
4426
4503
try:
4427
4504
    from subunit import TestProtocolClient
4428
4505
    from subunit.test_results import AutoTimingTestResultDecorator
 
4506
    class SubUnitBzrProtocolClient(TestProtocolClient):
 
4507
 
 
4508
        def addSuccess(self, test, details=None):
 
4509
            # The subunit client always includes the details in the subunit
 
4510
            # stream, but we don't want to include it in ours.
 
4511
            if details is not None and 'log' in details:
 
4512
                del details['log']
 
4513
            return super(SubUnitBzrProtocolClient, self).addSuccess(
 
4514
                test, details)
 
4515
 
4429
4516
    class SubUnitBzrRunner(TextTestRunner):
4430
4517
        def run(self, test):
4431
4518
            result = AutoTimingTestResultDecorator(
4432
 
                TestProtocolClient(self.stream))
 
4519
                SubUnitBzrProtocolClient(self.stream))
4433
4520
            test.run(result)
4434
4521
            return result
4435
4522
except ImportError:
4436
4523
    pass
 
4524
 
 
4525
class _PosixPermissionsFeature(Feature):
 
4526
 
 
4527
    def _probe(self):
 
4528
        def has_perms():
 
4529
            # create temporary file and check if specified perms are maintained.
 
4530
            import tempfile
 
4531
 
 
4532
            write_perms = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
 
4533
            f = tempfile.mkstemp(prefix='bzr_perms_chk_')
 
4534
            fd, name = f
 
4535
            os.close(fd)
 
4536
            os.chmod(name, write_perms)
 
4537
 
 
4538
            read_perms = os.stat(name).st_mode & 0777
 
4539
            os.unlink(name)
 
4540
            return (write_perms == read_perms)
 
4541
 
 
4542
        return (os.name == 'posix') and has_perms()
 
4543
 
 
4544
    def feature_name(self):
 
4545
        return 'POSIX permissions support'
 
4546
 
 
4547
posix_permissions_feature = _PosixPermissionsFeature()