~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/__init__.py

  • Committer: John Arbash Meinel
  • Date: 2008-09-26 22:14:42 UTC
  • mto: This revision was merged to the branch mainline in revision 3747.
  • Revision ID: john@arbash-meinel.com-20080926221442-3r67j99sr9rwe9w0
Make message optional, don't check the memory flag directly.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007, 2008 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
42
42
from subprocess import Popen, PIPE
43
43
import sys
44
44
import tempfile
 
45
import threading
 
46
import time
45
47
import unittest
46
 
import time
47
48
import warnings
48
49
 
49
50
 
50
51
from bzrlib import (
 
52
    branchbuilder,
51
53
    bzrdir,
52
54
    debug,
53
55
    errors,
56
58
    progress,
57
59
    ui,
58
60
    urlutils,
 
61
    registry,
59
62
    workingtree,
60
63
    )
61
64
import bzrlib.branch
72
75
    pass
73
76
from bzrlib.merge import merge_inner
74
77
import bzrlib.merge3
75
 
import bzrlib.osutils
76
78
import bzrlib.plugin
77
 
from bzrlib.revision import common_ancestor
78
79
import bzrlib.store
79
80
from bzrlib import symbol_versioning
80
81
from bzrlib.symbol_versioning import (
 
82
    DEPRECATED_PARAMETER,
 
83
    deprecated_function,
81
84
    deprecated_method,
82
 
    zero_eighteen,
 
85
    deprecated_passed,
83
86
    )
84
87
import bzrlib.trace
85
88
from bzrlib.transport import get_transport
89
92
from bzrlib.transport.readonly import ReadonlyServer
90
93
from bzrlib.trace import mutter, note
91
94
from bzrlib.tests import TestUtil
92
 
from bzrlib.tests.HttpServer import HttpServer
 
95
from bzrlib.tests.http_server import HttpServer
93
96
from bzrlib.tests.TestUtil import (
94
97
                          TestSuite,
95
98
                          TestLoader,
96
99
                          )
97
100
from bzrlib.tests.treeshape import build_tree_contents
 
101
import bzrlib.version_info_formats.format_custom
98
102
from bzrlib.workingtree import WorkingTree, WorkingTreeFormat2
99
103
 
100
104
# Mark this python module as being part of the implementation
104
108
 
105
109
default_transport = LocalURLServer
106
110
 
107
 
MODULES_TO_TEST = []
108
 
MODULES_TO_DOCTEST = [
109
 
                      bzrlib.timestamp,
110
 
                      bzrlib.errors,
111
 
                      bzrlib.export,
112
 
                      bzrlib.inventory,
113
 
                      bzrlib.iterablefile,
114
 
                      bzrlib.lockdir,
115
 
                      bzrlib.merge3,
116
 
                      bzrlib.option,
117
 
                      bzrlib.store,
118
 
                      ]
119
 
 
120
 
 
121
 
def packages_to_test():
122
 
    """Return a list of packages to test.
123
 
 
124
 
    The packages are not globally imported so that import failures are
125
 
    triggered when running selftest, not when importing the command.
126
 
    """
127
 
    import bzrlib.doc
128
 
    import bzrlib.tests.blackbox
129
 
    import bzrlib.tests.branch_implementations
130
 
    import bzrlib.tests.bzrdir_implementations
131
 
    import bzrlib.tests.interrepository_implementations
132
 
    import bzrlib.tests.interversionedfile_implementations
133
 
    import bzrlib.tests.intertree_implementations
134
 
    import bzrlib.tests.per_lock
135
 
    import bzrlib.tests.repository_implementations
136
 
    import bzrlib.tests.revisionstore_implementations
137
 
    import bzrlib.tests.tree_implementations
138
 
    import bzrlib.tests.workingtree_implementations
139
 
    return [
140
 
            bzrlib.doc,
141
 
            bzrlib.tests.blackbox,
142
 
            bzrlib.tests.branch_implementations,
143
 
            bzrlib.tests.bzrdir_implementations,
144
 
            bzrlib.tests.interrepository_implementations,
145
 
            bzrlib.tests.interversionedfile_implementations,
146
 
            bzrlib.tests.intertree_implementations,
147
 
            bzrlib.tests.per_lock,
148
 
            bzrlib.tests.repository_implementations,
149
 
            bzrlib.tests.revisionstore_implementations,
150
 
            bzrlib.tests.tree_implementations,
151
 
            bzrlib.tests.workingtree_implementations,
152
 
            ]
153
 
 
154
111
 
155
112
class ExtendedTestResult(unittest._TextTestResult):
156
113
    """Accepts, reports and accumulates the results of running tests.
157
114
 
158
 
    Compared to this unittest version this class adds support for profiling,
159
 
    benchmarking, stopping as soon as a test fails,  and skipping tests.
160
 
    There are further-specialized subclasses for different types of display.
 
115
    Compared to the unittest version this class adds support for
 
116
    profiling, benchmarking, stopping as soon as a test fails,  and
 
117
    skipping tests.  There are further-specialized subclasses for
 
118
    different types of display.
 
119
 
 
120
    When a test finishes, in whatever way, it calls one of the addSuccess,
 
121
    addFailure or addError classes.  These in turn may redirect to a more
 
122
    specific case for the special test results supported by our extended
 
123
    tests.
 
124
 
 
125
    Note that just one of these objects is fed the results from many tests.
161
126
    """
162
127
 
163
128
    stop_early = False
193
158
        self.failure_count = 0
194
159
        self.known_failure_count = 0
195
160
        self.skip_count = 0
 
161
        self.not_applicable_count = 0
196
162
        self.unsupported = {}
197
163
        self.count = 0
198
164
        self._overall_start_time = time.time()
199
165
    
200
 
    def extractBenchmarkTime(self, testCase):
 
166
    def _extractBenchmarkTime(self, testCase):
201
167
        """Add a benchmark time for the current test case."""
202
 
        self._benchmarkTime = getattr(testCase, "_benchtime", None)
 
168
        return getattr(testCase, "_benchtime", None)
203
169
    
204
170
    def _elapsedTestTimeString(self):
205
171
        """Return a time string for the overall time the current test has taken."""
206
172
        return self._formatTime(time.time() - self._start_time)
207
173
 
208
 
    def _testTimeString(self):
209
 
        if self._benchmarkTime is not None:
 
174
    def _testTimeString(self, testCase):
 
175
        benchmark_time = self._extractBenchmarkTime(testCase)
 
176
        if benchmark_time is not None:
210
177
            return "%s/%s" % (
211
 
                self._formatTime(self._benchmarkTime),
 
178
                self._formatTime(benchmark_time),
212
179
                self._elapsedTestTimeString())
213
180
        else:
214
181
            return "           %s" % self._elapsedTestTimeString()
242
209
            setKeepLogfile()
243
210
 
244
211
    def addError(self, test, err):
245
 
        self.extractBenchmarkTime(test)
246
 
        self._cleanupLogFile(test)
 
212
        """Tell result that test finished with an error.
 
213
 
 
214
        Called from the TestCase run() method when the test
 
215
        fails with an unexpected error.
 
216
        """
 
217
        self._testConcluded(test)
247
218
        if isinstance(err[1], TestSkipped):
248
 
            return self.addSkipped(test, err)
 
219
            return self._addSkipped(test, err)
249
220
        elif isinstance(err[1], UnavailableFeature):
250
221
            return self.addNotSupported(test, err[1].args[0])
251
 
        unittest.TestResult.addError(self, test, err)
252
 
        self.error_count += 1
253
 
        self.report_error(test, err)
254
 
        if self.stop_early:
255
 
            self.stop()
 
222
        else:
 
223
            unittest.TestResult.addError(self, test, err)
 
224
            self.error_count += 1
 
225
            self.report_error(test, err)
 
226
            if self.stop_early:
 
227
                self.stop()
 
228
            self._cleanupLogFile(test)
256
229
 
257
230
    def addFailure(self, test, err):
258
 
        self._cleanupLogFile(test)
259
 
        self.extractBenchmarkTime(test)
 
231
        """Tell result that test failed.
 
232
 
 
233
        Called from the TestCase run() method when the test
 
234
        fails because e.g. an assert() method failed.
 
235
        """
 
236
        self._testConcluded(test)
260
237
        if isinstance(err[1], KnownFailure):
261
 
            return self.addKnownFailure(test, err)
262
 
        unittest.TestResult.addFailure(self, test, err)
263
 
        self.failure_count += 1
264
 
        self.report_failure(test, err)
265
 
        if self.stop_early:
266
 
            self.stop()
267
 
 
268
 
    def addKnownFailure(self, test, err):
 
238
            return self._addKnownFailure(test, err)
 
239
        else:
 
240
            unittest.TestResult.addFailure(self, test, err)
 
241
            self.failure_count += 1
 
242
            self.report_failure(test, err)
 
243
            if self.stop_early:
 
244
                self.stop()
 
245
            self._cleanupLogFile(test)
 
246
 
 
247
    def addSuccess(self, test):
 
248
        """Tell result that test completed successfully.
 
249
 
 
250
        Called from the TestCase run()
 
251
        """
 
252
        self._testConcluded(test)
 
253
        if self._bench_history is not None:
 
254
            benchmark_time = self._extractBenchmarkTime(test)
 
255
            if benchmark_time is not None:
 
256
                self._bench_history.write("%s %s\n" % (
 
257
                    self._formatTime(benchmark_time),
 
258
                    test.id()))
 
259
        self.report_success(test)
 
260
        self._cleanupLogFile(test)
 
261
        unittest.TestResult.addSuccess(self, test)
 
262
        test._log_contents = ''
 
263
 
 
264
    def _testConcluded(self, test):
 
265
        """Common code when a test has finished.
 
266
 
 
267
        Called regardless of whether it succeded, failed, etc.
 
268
        """
 
269
        pass
 
270
 
 
271
    def _addKnownFailure(self, test, err):
269
272
        self.known_failure_count += 1
270
273
        self.report_known_failure(test, err)
271
274
 
272
275
    def addNotSupported(self, test, feature):
 
276
        """The test will not be run because of a missing feature.
 
277
        """
 
278
        # this can be called in two different ways: it may be that the
 
279
        # test started running, and then raised (through addError) 
 
280
        # UnavailableFeature.  Alternatively this method can be called
 
281
        # while probing for features before running the tests; in that
 
282
        # case we will see startTest and stopTest, but the test will never
 
283
        # actually run.
273
284
        self.unsupported.setdefault(str(feature), 0)
274
285
        self.unsupported[str(feature)] += 1
275
286
        self.report_unsupported(test, feature)
276
287
 
277
 
    def addSuccess(self, test):
278
 
        self.extractBenchmarkTime(test)
279
 
        if self._bench_history is not None:
280
 
            if self._benchmarkTime is not None:
281
 
                self._bench_history.write("%s %s\n" % (
282
 
                    self._formatTime(self._benchmarkTime),
283
 
                    test.id()))
284
 
        self.report_success(test)
285
 
        unittest.TestResult.addSuccess(self, test)
286
 
 
287
 
    def addSkipped(self, test, skip_excinfo):
288
 
        self.report_skip(test, skip_excinfo)
289
 
        # seems best to treat this as success from point-of-view of unittest
290
 
        # -- it actually does nothing so it barely matters :)
 
288
    def _addSkipped(self, test, skip_excinfo):
 
289
        if isinstance(skip_excinfo[1], TestNotApplicable):
 
290
            self.not_applicable_count += 1
 
291
            self.report_not_applicable(test, skip_excinfo)
 
292
        else:
 
293
            self.skip_count += 1
 
294
            self.report_skip(test, skip_excinfo)
291
295
        try:
292
296
            test.tearDown()
293
297
        except KeyboardInterrupt:
294
298
            raise
295
299
        except:
296
 
            self.addError(test, test.__exc_info())
 
300
            self.addError(test, test._exc_info())
297
301
        else:
 
302
            # seems best to treat this as success from point-of-view of unittest
 
303
            # -- it actually does nothing so it barely matters :)
298
304
            unittest.TestResult.addSuccess(self, test)
 
305
            test._log_contents = ''
299
306
 
300
307
    def printErrorList(self, flavour, errors):
301
308
        for test, err in errors:
303
310
            self.stream.write("%s: " % flavour)
304
311
            self.stream.writeln(self.getDescription(test))
305
312
            if getattr(test, '_get_log', None) is not None:
306
 
                print >>self.stream
307
 
                print >>self.stream, \
308
 
                        ('vvvv[log from %s]' % test.id()).ljust(78,'-')
309
 
                print >>self.stream, test._get_log()
310
 
                print >>self.stream, \
311
 
                        ('^^^^[log from %s]' % test.id()).ljust(78,'-')
 
313
                self.stream.write('\n')
 
314
                self.stream.write(
 
315
                        ('vvvv[log from %s]' % test.id()).ljust(78,'-'))
 
316
                self.stream.write('\n')
 
317
                self.stream.write(test._get_log())
 
318
                self.stream.write('\n')
 
319
                self.stream.write(
 
320
                        ('^^^^[log from %s]' % test.id()).ljust(78,'-'))
 
321
                self.stream.write('\n')
312
322
            self.stream.writeln(self.separator2)
313
323
            self.stream.writeln("%s" % err)
314
324
 
321
331
    def report_success(self, test):
322
332
        pass
323
333
 
 
334
    def wasStrictlySuccessful(self):
 
335
        if self.unsupported or self.known_failure_count:
 
336
            return False
 
337
        return self.wasSuccessful()
 
338
 
324
339
 
325
340
class TextTestResult(ExtendedTestResult):
326
341
    """Displays progress and results of tests in text form"""
348
363
        self.pb.update('[test 0/%d] starting...' % (self.num_tests))
349
364
 
350
365
    def _progress_prefix_text(self):
351
 
        a = '[%d' % self.count
 
366
        # the longer this text, the less space we have to show the test
 
367
        # name...
 
368
        a = '[%d' % self.count              # total that have been run
 
369
        # tests skipped as known not to be relevant are not important enough
 
370
        # to show here
 
371
        ## if self.skip_count:
 
372
        ##     a += ', %d skip' % self.skip_count
 
373
        ## if self.known_failure_count:
 
374
        ##     a += '+%dX' % self.known_failure_count
352
375
        if self.num_tests is not None:
353
376
            a +='/%d' % self.num_tests
354
 
        a += ' in %ds' % (time.time() - self._overall_start_time)
 
377
        a += ' in '
 
378
        runtime = time.time() - self._overall_start_time
 
379
        if runtime >= 60:
 
380
            a += '%dm%ds' % (runtime / 60, runtime % 60)
 
381
        else:
 
382
            a += '%ds' % runtime
355
383
        if self.error_count:
356
 
            a += ', %d errors' % self.error_count
 
384
            a += ', %d err' % self.error_count
357
385
        if self.failure_count:
358
 
            a += ', %d failed' % self.failure_count
359
 
        if self.known_failure_count:
360
 
            a += ', %d known failures' % self.known_failure_count
361
 
        if self.skip_count:
362
 
            a += ', %d skipped' % self.skip_count
 
386
            a += ', %d fail' % self.failure_count
363
387
        if self.unsupported:
364
 
            a += ', %d missing features' % len(self.unsupported)
 
388
            a += ', %d missing' % len(self.unsupported)
365
389
        a += ']'
366
390
        return a
367
391
 
392
416
            self._test_description(test), err[1])
393
417
 
394
418
    def report_skip(self, test, skip_excinfo):
395
 
        self.skip_count += 1
396
 
        if False:
397
 
            # at the moment these are mostly not things we can fix
398
 
            # and so they just produce stipple; use the verbose reporter
399
 
            # to see them.
400
 
            if False:
401
 
                # show test and reason for skip
402
 
                self.pb.note('SKIP: %s\n    %s\n', 
403
 
                    self._shortened_test_description(test),
404
 
                    skip_excinfo[1])
405
 
            else:
406
 
                # since the class name was left behind in the still-visible
407
 
                # progress bar...
408
 
                self.pb.note('SKIP: %s', skip_excinfo[1])
 
419
        pass
 
420
 
 
421
    def report_not_applicable(self, test, skip_excinfo):
 
422
        pass
409
423
 
410
424
    def report_unsupported(self, test, feature):
411
425
        """test cannot be run because feature is missing."""
448
462
 
449
463
    def report_error(self, test, err):
450
464
        self.stream.writeln('ERROR %s\n%s'
451
 
                % (self._testTimeString(),
 
465
                % (self._testTimeString(test),
452
466
                   self._error_summary(err)))
453
467
 
454
468
    def report_failure(self, test, err):
455
469
        self.stream.writeln(' FAIL %s\n%s'
456
 
                % (self._testTimeString(),
 
470
                % (self._testTimeString(test),
457
471
                   self._error_summary(err)))
458
472
 
459
473
    def report_known_failure(self, test, err):
460
474
        self.stream.writeln('XFAIL %s\n%s'
461
 
                % (self._testTimeString(),
 
475
                % (self._testTimeString(test),
462
476
                   self._error_summary(err)))
463
477
 
464
478
    def report_success(self, test):
465
 
        self.stream.writeln('   OK %s' % self._testTimeString())
 
479
        self.stream.writeln('   OK %s' % self._testTimeString(test))
466
480
        for bench_called, stats in getattr(test, '_benchcalls', []):
467
481
            self.stream.writeln('LSProf output for %s(%s, %s)' % bench_called)
468
482
            stats.pprint(file=self.stream)
471
485
        self.stream.flush()
472
486
 
473
487
    def report_skip(self, test, skip_excinfo):
474
 
        self.skip_count += 1
475
488
        self.stream.writeln(' SKIP %s\n%s'
476
 
                % (self._testTimeString(),
 
489
                % (self._testTimeString(test),
 
490
                   self._error_summary(skip_excinfo)))
 
491
 
 
492
    def report_not_applicable(self, test, skip_excinfo):
 
493
        self.stream.writeln('  N/A %s\n%s'
 
494
                % (self._testTimeString(test),
477
495
                   self._error_summary(skip_excinfo)))
478
496
 
479
497
    def report_unsupported(self, test, feature):
480
498
        """test cannot be run because feature is missing."""
481
499
        self.stream.writeln("NODEP %s\n    The feature '%s' is not available."
482
 
                %(self._testTimeString(), feature))
483
 
                  
 
500
                %(self._testTimeString(test), feature))
484
501
 
485
502
 
486
503
class TextTestRunner(object):
581
598
    """Indicates that a test was intentionally skipped, rather than failing."""
582
599
 
583
600
 
 
601
class TestNotApplicable(TestSkipped):
 
602
    """A test is not applicable to the situation where it was run.
 
603
 
 
604
    This is only normally raised by parameterized tests, if they find that 
 
605
    the instance they're constructed upon does not support one aspect 
 
606
    of its interface.
 
607
    """
 
608
 
 
609
 
584
610
class KnownFailure(AssertionError):
585
611
    """Indicates that a test failed in a precisely expected manner.
586
612
 
693
719
        return password
694
720
 
695
721
 
 
722
def _report_leaked_threads():
 
723
    bzrlib.trace.warning('%s is leaking threads among %d leaking tests',
 
724
                         TestCase._first_thread_leaker_id,
 
725
                         TestCase._leaking_threads_tests)
 
726
 
 
727
 
696
728
class TestCase(unittest.TestCase):
697
729
    """Base class for bzr unit tests.
698
730
    
714
746
    accidentally overlooked.
715
747
    """
716
748
 
 
749
    _active_threads = None
 
750
    _leaking_threads_tests = 0
 
751
    _first_thread_leaker_id = None
717
752
    _log_file_name = None
718
753
    _log_contents = ''
719
754
    _keep_log_file = False
720
755
    # record lsprof data when performing benchmark calls.
721
756
    _gather_lsprof_in_benchmarks = False
 
757
    attrs_to_keep = ('id', '_testMethodName', '_testMethodDoc',
 
758
                     '_log_contents', '_log_file_name', '_benchtime',
 
759
                     '_TestCase__testMethodName')
722
760
 
723
761
    def __init__(self, methodName='testMethod'):
724
762
        super(TestCase, self).__init__(methodName)
727
765
    def setUp(self):
728
766
        unittest.TestCase.setUp(self)
729
767
        self._cleanEnvironment()
730
 
        bzrlib.trace.disable_default_logging()
731
768
        self._silenceUI()
732
769
        self._startLogFile()
733
770
        self._benchcalls = []
734
771
        self._benchtime = None
735
772
        self._clear_hooks()
736
773
        self._clear_debug_flags()
 
774
        TestCase._active_threads = threading.activeCount()
 
775
        self.addCleanup(self._check_leaked_threads)
 
776
 
 
777
    def _check_leaked_threads(self):
 
778
        active = threading.activeCount()
 
779
        leaked_threads = active - TestCase._active_threads
 
780
        TestCase._active_threads = active
 
781
        if leaked_threads:
 
782
            TestCase._leaking_threads_tests += 1
 
783
            if TestCase._first_thread_leaker_id is None:
 
784
                TestCase._first_thread_leaker_id = self.id()
 
785
                # we're not specifically told when all tests are finished.
 
786
                # This will do. We use a function to avoid keeping a reference
 
787
                # to a TestCase object.
 
788
                atexit.register(_report_leaked_threads)
737
789
 
738
790
    def _clear_debug_flags(self):
739
791
        """Prevent externally set debug flags affecting tests.
741
793
        Tests that want to use debug flags can just set them in the
742
794
        debug_flags set during setup/teardown.
743
795
        """
744
 
        self._preserved_debug_flags = set(debug.debug_flags)
745
 
        debug.debug_flags.clear()
746
 
        self.addCleanup(self._restore_debug_flags)
 
796
        if 'allow_debug' not in selftest_debug_flags:
 
797
            self._preserved_debug_flags = set(debug.debug_flags)
 
798
            debug.debug_flags.clear()
 
799
            self.addCleanup(self._restore_debug_flags)
747
800
 
748
801
    def _clear_hooks(self):
749
802
        # prevent hooks affecting tests
751
804
        import bzrlib.smart.server
752
805
        self._preserved_hooks = {
753
806
            bzrlib.branch.Branch: bzrlib.branch.Branch.hooks,
 
807
            bzrlib.mutabletree.MutableTree: bzrlib.mutabletree.MutableTree.hooks,
754
808
            bzrlib.smart.server.SmartTCPServer: bzrlib.smart.server.SmartTCPServer.hooks,
755
809
            }
756
810
        self.addCleanup(self._restoreHooks)
809
863
            return
810
864
        if message is None:
811
865
            message = "texts not equal:\n"
 
866
        if a == b + '\n':
 
867
            message = 'first string is missing a final newline.\n'
 
868
        if a + '\n' == b:
 
869
            message = 'second string is missing a final newline.\n'
812
870
        raise AssertionError(message +
813
871
                             self._ndiff_strings(a, b))
814
872
        
816
874
        self.assertEqual(mode, mode_test,
817
875
                         'mode mismatch %o != %o' % (mode, mode_test))
818
876
 
 
877
    def assertPositive(self, val):
 
878
        """Assert that val is greater than 0."""
 
879
        self.assertTrue(val > 0, 'expected a positive value, but got %s' % val)
 
880
 
 
881
    def assertNegative(self, val):
 
882
        """Assert that val is less than 0."""
 
883
        self.assertTrue(val < 0, 'expected a negative value, but got %s' % val)
 
884
 
819
885
    def assertStartsWith(self, s, prefix):
820
886
        if not s.startswith(prefix):
821
887
            raise AssertionError('string %r does not start with %r' % (s, prefix))
845
911
 
846
912
    def assertSubset(self, sublist, superlist):
847
913
        """Assert that every entry in sublist is present in superlist."""
848
 
        missing = []
849
 
        for entry in sublist:
850
 
            if entry not in superlist:
851
 
                missing.append(entry)
 
914
        missing = set(sublist) - set(superlist)
852
915
        if len(missing) > 0:
853
 
            raise AssertionError("value(s) %r not present in container %r" % 
 
916
            raise AssertionError("value(s) %r not present in container %r" %
854
917
                                 (missing, superlist))
855
918
 
856
919
    def assertListRaises(self, excClass, func, *args, **kwargs):
862
925
        """
863
926
        try:
864
927
            list(func(*args, **kwargs))
865
 
        except excClass:
866
 
            return
 
928
        except excClass, e:
 
929
            return e
867
930
        else:
868
931
            if getattr(excClass,'__name__', None) is not None:
869
932
                excName = excClass.__name__
919
982
        self.assertEqual(mode, actual_mode,
920
983
            'mode of %r incorrect (%o != %o)' % (path, mode, actual_mode))
921
984
 
 
985
    def assertIsSameRealPath(self, path1, path2):
 
986
        """Fail if path1 and path2 points to different files"""
 
987
        self.assertEqual(osutils.realpath(path1),
 
988
                         osutils.realpath(path2),
 
989
                         "apparent paths:\na = %s\nb = %s\n," % (path1, path2))
 
990
 
922
991
    def assertIsInstance(self, obj, kls):
923
992
        """Fail if obj is not an instance of kls"""
924
993
        if not isinstance(obj, kls):
960
1029
        else:
961
1030
            self.fail('Unexpected success.  Should have failed: %s' % reason)
962
1031
 
963
 
    def _capture_warnings(self, a_callable, *args, **kwargs):
 
1032
    def assertFileEqual(self, content, path):
 
1033
        """Fail if path does not contain 'content'."""
 
1034
        self.failUnlessExists(path)
 
1035
        f = file(path, 'rb')
 
1036
        try:
 
1037
            s = f.read()
 
1038
        finally:
 
1039
            f.close()
 
1040
        self.assertEqualDiff(content, s)
 
1041
 
 
1042
    def failUnlessExists(self, path):
 
1043
        """Fail unless path or paths, which may be abs or relative, exist."""
 
1044
        if not isinstance(path, basestring):
 
1045
            for p in path:
 
1046
                self.failUnlessExists(p)
 
1047
        else:
 
1048
            self.failUnless(osutils.lexists(path),path+" does not exist")
 
1049
 
 
1050
    def failIfExists(self, path):
 
1051
        """Fail if path or paths, which may be abs or relative, exist."""
 
1052
        if not isinstance(path, basestring):
 
1053
            for p in path:
 
1054
                self.failIfExists(p)
 
1055
        else:
 
1056
            self.failIf(osutils.lexists(path),path+" exists")
 
1057
 
 
1058
    def _capture_deprecation_warnings(self, a_callable, *args, **kwargs):
964
1059
        """A helper for callDeprecated and applyDeprecated.
965
1060
 
966
1061
        :param a_callable: A callable to call.
989
1084
        Note that this only captures warnings raised by symbol_versioning.warn,
990
1085
        not other callers that go direct to the warning module.
991
1086
 
 
1087
        To test that a deprecated method raises an error, do something like
 
1088
        this::
 
1089
 
 
1090
            self.assertRaises(errors.ReservedId,
 
1091
                self.applyDeprecated,
 
1092
                deprecated_in((1, 5, 0)),
 
1093
                br.append_revision,
 
1094
                'current:')
 
1095
 
992
1096
        :param deprecation_format: The deprecation format that the callable
993
1097
            should have been deprecated with. This is the same type as the
994
1098
            parameter to deprecated_method/deprecated_function. If the
1001
1105
        :param kwargs: The keyword arguments for the callable
1002
1106
        :return: The result of a_callable(``*args``, ``**kwargs``)
1003
1107
        """
1004
 
        call_warnings, result = self._capture_warnings(a_callable,
 
1108
        call_warnings, result = self._capture_deprecation_warnings(a_callable,
1005
1109
            *args, **kwargs)
1006
1110
        expected_first_warning = symbol_versioning.deprecation_string(
1007
1111
            a_callable, deprecation_format)
1011
1115
        self.assertEqual(expected_first_warning, call_warnings[0])
1012
1116
        return result
1013
1117
 
 
1118
    def callCatchWarnings(self, fn, *args, **kw):
 
1119
        """Call a callable that raises python warnings.
 
1120
 
 
1121
        The caller's responsible for examining the returned warnings.
 
1122
 
 
1123
        If the callable raises an exception, the exception is not
 
1124
        caught and propagates up to the caller.  In that case, the list
 
1125
        of warnings is not available.
 
1126
 
 
1127
        :returns: ([warning_object, ...], fn_result)
 
1128
        """
 
1129
        # XXX: This is not perfect, because it completely overrides the
 
1130
        # warnings filters, and some code may depend on suppressing particular
 
1131
        # warnings.  It's the easiest way to insulate ourselves from -Werror,
 
1132
        # though.  -- Andrew, 20071062
 
1133
        wlist = []
 
1134
        def _catcher(message, category, filename, lineno, file=None):
 
1135
            # despite the name, 'message' is normally(?) a Warning subclass
 
1136
            # instance
 
1137
            wlist.append(message)
 
1138
        saved_showwarning = warnings.showwarning
 
1139
        saved_filters = warnings.filters
 
1140
        try:
 
1141
            warnings.showwarning = _catcher
 
1142
            warnings.filters = []
 
1143
            result = fn(*args, **kw)
 
1144
        finally:
 
1145
            warnings.showwarning = saved_showwarning
 
1146
            warnings.filters = saved_filters
 
1147
        return wlist, result
 
1148
 
1014
1149
    def callDeprecated(self, expected, callable, *args, **kwargs):
1015
1150
        """Assert that a callable is deprecated in a particular way.
1016
1151
 
1020
1155
        and will ensure that that is issued for the function being called.
1021
1156
 
1022
1157
        Note that this only captures warnings raised by symbol_versioning.warn,
1023
 
        not other callers that go direct to the warning module.
 
1158
        not other callers that go direct to the warning module.  To catch
 
1159
        general warnings, use callCatchWarnings.
1024
1160
 
1025
1161
        :param expected: a list of the deprecation warnings expected, in order
1026
1162
        :param callable: The callable to call
1027
1163
        :param args: The positional arguments for the callable
1028
1164
        :param kwargs: The keyword arguments for the callable
1029
1165
        """
1030
 
        call_warnings, result = self._capture_warnings(callable,
 
1166
        call_warnings, result = self._capture_deprecation_warnings(callable,
1031
1167
            *args, **kwargs)
1032
1168
        self.assertEqual(expected, call_warnings)
1033
1169
        return result
1039
1175
        """
1040
1176
        fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
1041
1177
        self._log_file = os.fdopen(fileno, 'w+')
1042
 
        self._log_nonce = bzrlib.trace.enable_test_log(self._log_file)
 
1178
        self._log_memento = bzrlib.trace.push_log_file(self._log_file)
1043
1179
        self._log_file_name = name
1044
1180
        self.addCleanup(self._finishLogFile)
1045
1181
 
1050
1186
        """
1051
1187
        if self._log_file is None:
1052
1188
            return
1053
 
        bzrlib.trace.disable_test_log(self._log_nonce)
 
1189
        bzrlib.trace.pop_log_file(self._log_memento)
1054
1190
        self._log_file.close()
1055
1191
        self._log_file = None
1056
1192
        if not self._keep_log_file:
1061
1197
        """Make the logfile not be deleted when _finishLogFile is called."""
1062
1198
        self._keep_log_file = True
1063
1199
 
1064
 
    def addCleanup(self, callable):
 
1200
    def addCleanup(self, callable, *args, **kwargs):
1065
1201
        """Arrange to run a callable when this case is torn down.
1066
1202
 
1067
1203
        Callables are run in the reverse of the order they are registered, 
1068
1204
        ie last-in first-out.
1069
1205
        """
1070
 
        if callable in self._cleanups:
1071
 
            raise ValueError("cleanup function %r already registered on %s" 
1072
 
                    % (callable, self))
1073
 
        self._cleanups.append(callable)
 
1206
        self._cleanups.append((callable, args, kwargs))
1074
1207
 
1075
1208
    def _cleanEnvironment(self):
1076
1209
        new_env = {
1077
1210
            'BZR_HOME': None, # Don't inherit BZR_HOME to all the tests.
1078
1211
            'HOME': os.getcwd(),
1079
1212
            'APPDATA': None,  # bzr now use Win32 API and don't rely on APPDATA
 
1213
            'BZR_EDITOR': None, # test_msgeditor manipulates this variable
1080
1214
            'BZR_EMAIL': None,
1081
1215
            'BZREMAIL': None, # may still be present in the environment
1082
1216
            'EMAIL': None,
1083
1217
            'BZR_PROGRESS_BAR': None,
 
1218
            'BZR_LOG': None,
 
1219
            # SSH Agent
 
1220
            'SSH_AUTH_SOCK': None,
1084
1221
            # Proxies
1085
1222
            'http_proxy': None,
1086
1223
            'HTTP_PROXY': None,
1133
1270
                    result.addSuccess(self)
1134
1271
                result.stopTest(self)
1135
1272
                return
1136
 
        return unittest.TestCase.run(self, result)
 
1273
        try:
 
1274
            return unittest.TestCase.run(self, result)
 
1275
        finally:
 
1276
            saved_attrs = {}
 
1277
            absent_attr = object()
 
1278
            for attr_name in self.attrs_to_keep:
 
1279
                attr = getattr(self, attr_name, absent_attr)
 
1280
                if attr is not absent_attr:
 
1281
                    saved_attrs[attr_name] = attr
 
1282
            self.__dict__ = saved_attrs
1137
1283
 
1138
1284
    def tearDown(self):
1139
1285
        self._runCleanups()
1172
1318
        # Actually pop the cleanups from the list so tearDown running
1173
1319
        # twice is safe (this happens for skipped tests).
1174
1320
        while self._cleanups:
1175
 
            self._cleanups.pop()()
 
1321
            cleanup, args, kwargs = self._cleanups.pop()
 
1322
            cleanup(*args, **kwargs)
1176
1323
 
1177
1324
    def log(self, *args):
1178
1325
        mutter(*args)
1179
1326
 
1180
1327
    def _get_log(self, keep_log_file=False):
1181
 
        """Return as a string the log for this test. If the file is still
1182
 
        on disk and keep_log_file=False, delete the log file and store the
1183
 
        content in self._log_contents."""
 
1328
        """Get the log from bzrlib.trace calls from this test.
 
1329
 
 
1330
        :param keep_log_file: When True, if the log is still a file on disk
 
1331
            leave it as a file on disk. When False, if the log is still a file
 
1332
            on disk, the log file is deleted and the log preserved as
 
1333
            self._log_contents.
 
1334
        :return: A string containing the log.
 
1335
        """
1184
1336
        # flush the log file, to get all content
1185
1337
        import bzrlib.trace
1186
1338
        bzrlib.trace._trace_file.flush()
1187
1339
        if self._log_contents:
 
1340
            # XXX: this can hardly contain the content flushed above --vila
 
1341
            # 20080128
1188
1342
            return self._log_contents
1189
1343
        if self._log_file_name is not None:
1190
1344
            logfile = open(self._log_file_name)
1198
1352
                    os.remove(self._log_file_name)
1199
1353
                except OSError, e:
1200
1354
                    if sys.platform == 'win32' and e.errno == errno.EACCES:
1201
 
                        print >>sys.stderr, ('Unable to delete log file '
1202
 
                                             ' %r' % self._log_file_name)
 
1355
                        sys.stderr.write(('Unable to delete log file '
 
1356
                                             ' %r\n' % self._log_file_name))
1203
1357
                    else:
1204
1358
                        raise
1205
1359
            return log_contents
1206
1360
        else:
1207
1361
            return "DELETED log file to reduce memory footprint"
1208
1362
 
1209
 
    @deprecated_method(zero_eighteen)
1210
 
    def capture(self, cmd, retcode=0):
1211
 
        """Shortcut that splits cmd into words, runs, and returns stdout"""
1212
 
        return self.run_bzr_captured(cmd.split(), retcode=retcode)[0]
1213
 
 
1214
1363
    def requireFeature(self, feature):
1215
1364
        """This test requires a specific feature is available.
1216
1365
 
1219
1368
        if not feature.available():
1220
1369
            raise UnavailableFeature(feature)
1221
1370
 
1222
 
    @deprecated_method(zero_eighteen)
1223
 
    def run_bzr_captured(self, argv, retcode=0, encoding=None, stdin=None,
1224
 
                         working_dir=None):
1225
 
        """Invoke bzr and return (stdout, stderr).
1226
 
 
1227
 
        Don't call this method, just use run_bzr() which is equivalent.
1228
 
 
1229
 
        :param argv: Arguments to invoke bzr.  This may be either a 
1230
 
            single string, in which case it is split by shlex into words, 
1231
 
            or a list of arguments.
1232
 
        :param retcode: Expected return code, or None for don't-care.
1233
 
        :param encoding: Encoding for sys.stdout and sys.stderr
1234
 
        :param stdin: A string to be used as stdin for the command.
1235
 
        :param working_dir: Change to this directory before running
1236
 
        """
1237
 
        return self._run_bzr_autosplit(argv, retcode=retcode,
1238
 
                encoding=encoding, stdin=stdin, working_dir=working_dir,
1239
 
                )
1240
 
 
1241
1371
    def _run_bzr_autosplit(self, args, retcode, encoding, stdin,
1242
1372
            working_dir):
1243
1373
        """Run bazaar command line, splitting up a string command line."""
1244
1374
        if isinstance(args, basestring):
1245
 
            args = list(shlex.split(args))
 
1375
            # shlex don't understand unicode strings,
 
1376
            # so args should be plain string (bialix 20070906)
 
1377
            args = list(shlex.split(str(args)))
1246
1378
        return self._run_bzr_core(args, retcode=retcode,
1247
1379
                encoding=encoding, stdin=stdin, working_dir=working_dir,
1248
1380
                )
1273
1405
        try:
1274
1406
            result = self.apply_redirected(ui.ui_factory.stdin,
1275
1407
                stdout, stderr,
1276
 
                bzrlib.commands.run_bzr_catch_errors,
 
1408
                bzrlib.commands.run_bzr_catch_user_errors,
1277
1409
                args)
1278
1410
        finally:
1279
1411
            logger.removeHandler(handler)
1292
1424
                              message='Unexpected return code')
1293
1425
        return out, err
1294
1426
 
1295
 
    def run_bzr(self, *args, **kwargs):
 
1427
    def run_bzr(self, args, retcode=0, encoding=None, stdin=None,
 
1428
                working_dir=None, error_regexes=[], output_encoding=None):
1296
1429
        """Invoke bzr, as if it were run from the command line.
1297
1430
 
1298
1431
        The argument list should not include the bzr program name - the
1306
1439
        2- A single string, eg "add a".  This is the most convenient 
1307
1440
        for hardcoded commands.
1308
1441
 
1309
 
        3- Several varargs parameters, eg run_bzr("add", "a").  
1310
 
        This is not recommended for new code.
1311
 
 
1312
1442
        This runs bzr through the interface that catches and reports
1313
1443
        errors, and with logging set to something approximating the
1314
1444
        default, so that error reporting can be checked.
1327
1457
        :keyword error_regexes: A list of expected error messages.  If
1328
1458
            specified they must be seen in the error output of the command.
1329
1459
        """
1330
 
        retcode = kwargs.pop('retcode', 0)
1331
 
        encoding = kwargs.pop('encoding', None)
1332
 
        stdin = kwargs.pop('stdin', None)
1333
 
        working_dir = kwargs.pop('working_dir', None)
1334
 
        error_regexes = kwargs.pop('error_regexes', [])
1335
 
 
1336
 
        if len(args) == 1:
1337
 
            if isinstance(args[0], (list, basestring)):
1338
 
                args = args[0]
1339
 
        else:
1340
 
            symbol_versioning.warn(zero_eighteen % "passing varargs to run_bzr",
1341
 
                                   DeprecationWarning, stacklevel=3)
1342
 
 
1343
 
        out, err = self._run_bzr_autosplit(args=args,
 
1460
        out, err = self._run_bzr_autosplit(
 
1461
            args=args,
1344
1462
            retcode=retcode,
1345
 
            encoding=encoding, stdin=stdin, working_dir=working_dir,
 
1463
            encoding=encoding,
 
1464
            stdin=stdin,
 
1465
            working_dir=working_dir,
1346
1466
            )
1347
 
 
1348
1467
        for regex in error_regexes:
1349
1468
            self.assertContainsRe(err, regex)
1350
1469
        return out, err
1351
1470
 
1352
 
    def run_bzr_decode(self, *args, **kwargs):
1353
 
        if 'encoding' in kwargs:
1354
 
            encoding = kwargs['encoding']
1355
 
        else:
1356
 
            encoding = bzrlib.user_encoding
1357
 
        return self.run_bzr(*args, **kwargs)[0].decode(encoding)
1358
 
 
1359
1471
    def run_bzr_error(self, error_regexes, *args, **kwargs):
1360
1472
        """Run bzr, and check that stderr contains the supplied regexes
1361
1473
 
1374
1486
 
1375
1487
            # Make sure that commit is failing because there is nothing to do
1376
1488
            self.run_bzr_error(['no changes to commit'],
1377
 
                               'commit', '-m', 'my commit comment')
 
1489
                               ['commit', '-m', 'my commit comment'])
1378
1490
            # Make sure --strict is handling an unknown file, rather than
1379
1491
            # giving us the 'nothing to do' error
1380
1492
            self.build_tree(['unknown'])
1381
1493
            self.run_bzr_error(['Commit refused because there are unknown files'],
1382
 
                               'commit', '--strict', '-m', 'my commit comment')
 
1494
                               ['commit', --strict', '-m', 'my commit comment'])
1383
1495
        """
1384
1496
        kwargs.setdefault('retcode', 3)
1385
1497
        kwargs['error_regexes'] = error_regexes
1410
1522
        env_changes = kwargs.get('env_changes', {})
1411
1523
        working_dir = kwargs.get('working_dir', None)
1412
1524
        allow_plugins = kwargs.get('allow_plugins', False)
 
1525
        if len(args) == 1:
 
1526
            if isinstance(args[0], list):
 
1527
                args = args[0]
 
1528
            elif isinstance(args[0], basestring):
 
1529
                args = list(shlex.split(args[0]))
 
1530
        else:
 
1531
            raise ValueError("passing varargs to run_bzr_subprocess")
1413
1532
        process = self.start_bzr_subprocess(args, env_changes=env_changes,
1414
1533
                                            working_dir=working_dir,
1415
1534
                                            allow_plugins=allow_plugins)
1471
1590
            # so we will avoid using it on all platforms, just to
1472
1591
            # make sure the code path is used, and we don't break on win32
1473
1592
            cleanup_environment()
1474
 
            command = [sys.executable, bzr_path]
 
1593
            command = [sys.executable]
 
1594
            # frozen executables don't need the path to bzr
 
1595
            if getattr(sys, "frozen", None) is None:
 
1596
                command.append(bzr_path)
1475
1597
            if not allow_plugins:
1476
1598
                command.append('--no-plugins')
1477
1599
            command.extend(process_args)
1591
1713
        self.addCleanup(resetTimeout)
1592
1714
        bzrlib.lockdir._DEFAULT_TIMEOUT_SECONDS = 0
1593
1715
 
 
1716
    def make_utf8_encoded_stringio(self, encoding_type=None):
 
1717
        """Return a StringIOWrapper instance, that will encode Unicode
 
1718
        input to UTF-8.
 
1719
        """
 
1720
        if encoding_type is None:
 
1721
            encoding_type = 'strict'
 
1722
        sio = StringIO()
 
1723
        output_encoding = 'utf-8'
 
1724
        sio = codecs.getwriter(output_encoding)(sio, errors=encoding_type)
 
1725
        sio.encoding = output_encoding
 
1726
        return sio
 
1727
 
1594
1728
 
1595
1729
class TestCaseWithMemoryTransport(TestCase):
1596
1730
    """Common test class for tests that do not need disk resources.
1615
1749
    _TEST_NAME = 'test'
1616
1750
 
1617
1751
    def __init__(self, methodName='runTest'):
1618
 
        # allow test parameterisation after test construction and before test
1619
 
        # execution. Variables that the parameteriser sets need to be 
 
1752
        # allow test parameterization after test construction and before test
 
1753
        # execution. Variables that the parameterizer sets need to be 
1620
1754
        # ones that are not set by setUp, or setUp will trash them.
1621
1755
        super(TestCaseWithMemoryTransport, self).__init__(methodName)
1622
1756
        self.vfs_transport_factory = default_transport
1628
1762
        """Return a writeable transport.
1629
1763
 
1630
1764
        This transport is for the test scratch space relative to
1631
 
        "self._test_root""
 
1765
        "self._test_root"
1632
1766
        
1633
1767
        :param relpath: a path relative to the base url.
1634
1768
        """
1772
1906
        base = self.get_vfs_only_server().get_url()
1773
1907
        return self._adjust_url(base, relpath)
1774
1908
 
 
1909
    def _create_safety_net(self):
 
1910
        """Make a fake bzr directory.
 
1911
 
 
1912
        This prevents any tests propagating up onto the TEST_ROOT directory's
 
1913
        real branch.
 
1914
        """
 
1915
        root = TestCaseWithMemoryTransport.TEST_ROOT
 
1916
        bzrdir.BzrDir.create_standalone_workingtree(root)
 
1917
 
 
1918
    def _check_safety_net(self):
 
1919
        """Check that the safety .bzr directory have not been touched.
 
1920
 
 
1921
        _make_test_root have created a .bzr directory to prevent tests from
 
1922
        propagating. This method ensures than a test did not leaked.
 
1923
        """
 
1924
        root = TestCaseWithMemoryTransport.TEST_ROOT
 
1925
        wt = workingtree.WorkingTree.open(root)
 
1926
        last_rev = wt.last_revision()
 
1927
        if last_rev != 'null:':
 
1928
            # The current test have modified the /bzr directory, we need to
 
1929
            # recreate a new one or all the followng tests will fail.
 
1930
            # If you need to inspect its content uncomment the following line
 
1931
            # import pdb; pdb.set_trace()
 
1932
            _rmtree_temp_dir(root + '/.bzr')
 
1933
            self._create_safety_net()
 
1934
            raise AssertionError('%s/.bzr should not be modified' % root)
 
1935
 
1775
1936
    def _make_test_root(self):
1776
 
        if TestCaseWithMemoryTransport.TEST_ROOT is not None:
1777
 
            return
1778
 
        root = tempfile.mkdtemp(prefix='testbzr-', suffix='.tmp')
1779
 
        TestCaseWithMemoryTransport.TEST_ROOT = root
1780
 
        
1781
 
        # make a fake bzr directory there to prevent any tests propagating
1782
 
        # up onto the source directory's real branch
1783
 
        bzrdir.BzrDir.create_standalone_workingtree(root)
1784
 
 
1785
 
        # The same directory is used by all tests, and we're not specifically
1786
 
        # told when all tests are finished.  This will do.
1787
 
        atexit.register(_rmtree_temp_dir, root)
 
1937
        if TestCaseWithMemoryTransport.TEST_ROOT is None:
 
1938
            root = osutils.mkdtemp(prefix='testbzr-', suffix='.tmp')
 
1939
            TestCaseWithMemoryTransport.TEST_ROOT = root
 
1940
 
 
1941
            self._create_safety_net()
 
1942
 
 
1943
            # The same directory is used by all tests, and we're not
 
1944
            # specifically told when all tests are finished.  This will do.
 
1945
            atexit.register(_rmtree_temp_dir, root)
 
1946
 
 
1947
        self.addCleanup(self._check_safety_net)
1788
1948
 
1789
1949
    def makeAndChdirToTestDir(self):
1790
1950
        """Create a temporary directories for this one test.
1836
1996
        b = self.make_branch(relpath, format=format)
1837
1997
        return memorytree.MemoryTree.create_on_branch(b)
1838
1998
 
 
1999
    def make_branch_builder(self, relpath, format=None):
 
2000
        url = self.get_url(relpath)
 
2001
        tran = get_transport(url)
 
2002
        return branchbuilder.BranchBuilder(get_transport(url), format=format)
 
2003
 
1839
2004
    def overrideEnvironmentForTesting(self):
1840
2005
        os.environ['HOME'] = self.test_home_dir
1841
2006
        os.environ['BZR_HOME'] = self.test_home_dir
1884
2049
            self.log("actually: %r" % contents)
1885
2050
            self.fail("contents of %s not as expected" % filename)
1886
2051
 
 
2052
    def _getTestDirPrefix(self):
 
2053
        # create a directory within the top level test directory
 
2054
        if sys.platform == 'win32':
 
2055
            name_prefix = re.sub('[<>*=+",:;_/\\-]', '_', self.id())
 
2056
            # windows is likely to have path-length limits so use a short name
 
2057
            name_prefix = name_prefix[-30:]
 
2058
        else:
 
2059
            name_prefix = re.sub('[/]', '_', self.id())
 
2060
        return name_prefix
 
2061
 
1887
2062
    def makeAndChdirToTestDir(self):
1888
2063
        """See TestCaseWithMemoryTransport.makeAndChdirToTestDir().
1889
2064
        
1890
2065
        For TestCaseInTempDir we create a temporary directory based on the test
1891
2066
        name and then create two subdirs - test and home under it.
1892
2067
        """
1893
 
        # create a directory within the top level test directory
1894
 
        candidate_dir = tempfile.mkdtemp(dir=self.TEST_ROOT)
 
2068
        name_prefix = osutils.pathjoin(self.TEST_ROOT, self._getTestDirPrefix())
 
2069
        name = name_prefix
 
2070
        for i in range(100):
 
2071
            if os.path.exists(name):
 
2072
                name = name_prefix + '_' + str(i)
 
2073
            else:
 
2074
                os.mkdir(name)
 
2075
                break
1895
2076
        # now create test and home directories within this dir
1896
 
        self.test_base_dir = candidate_dir
 
2077
        self.test_base_dir = name
1897
2078
        self.test_home_dir = self.test_base_dir + '/home'
1898
2079
        os.mkdir(self.test_home_dir)
1899
2080
        self.test_dir = self.test_base_dir + '/work'
1921
2102
 
1922
2103
        This doesn't add anything to a branch.
1923
2104
 
 
2105
        :type shape:    list or tuple.
1924
2106
        :param line_endings: Either 'binary' or 'native'
1925
2107
            in binary mode, exact contents are written in native mode, the
1926
2108
            line endings match the default platform endings.
1928
2110
            If the transport is readonly or None, "." is opened automatically.
1929
2111
        :return: None
1930
2112
        """
 
2113
        if type(shape) not in (list, tuple):
 
2114
            raise AssertionError("Parameter 'shape' should be "
 
2115
                "a list or a tuple. Got %r instead" % (shape,))
1931
2116
        # It's OK to just create them using forward slashes on windows.
1932
2117
        if transport is None or transport.is_readonly():
1933
2118
            transport = get_transport(".")
1949
2134
    def build_tree_contents(self, shape):
1950
2135
        build_tree_contents(shape)
1951
2136
 
1952
 
    def assertFileEqual(self, content, path):
1953
 
        """Fail if path does not contain 'content'."""
1954
 
        self.failUnlessExists(path)
1955
 
        f = file(path, 'rb')
1956
 
        try:
1957
 
            s = f.read()
1958
 
        finally:
1959
 
            f.close()
1960
 
        self.assertEqualDiff(content, s)
1961
 
 
1962
 
    def failUnlessExists(self, path):
1963
 
        """Fail unless path or paths, which may be abs or relative, exist."""
1964
 
        if not isinstance(path, basestring):
1965
 
            for p in path:
1966
 
                self.failUnlessExists(p)
1967
 
        else:
1968
 
            self.failUnless(osutils.lexists(path),path+" does not exist")
1969
 
 
1970
 
    def failIfExists(self, path):
1971
 
        """Fail if path or paths, which may be abs or relative, exist."""
1972
 
        if not isinstance(path, basestring):
1973
 
            for p in path:
1974
 
                self.failIfExists(p)
1975
 
        else:
1976
 
            self.failIf(osutils.lexists(path),path+" exists")
1977
 
 
1978
 
    def assertInWorkingTree(self,path,root_path='.',tree=None):
 
2137
    def assertInWorkingTree(self, path, root_path='.', tree=None):
1979
2138
        """Assert whether path or paths are in the WorkingTree"""
1980
2139
        if tree is None:
1981
2140
            tree = workingtree.WorkingTree.open(root_path)
1982
2141
        if not isinstance(path, basestring):
1983
2142
            for p in path:
1984
 
                self.assertInWorkingTree(p,tree=tree)
 
2143
                self.assertInWorkingTree(p, tree=tree)
1985
2144
        else:
1986
2145
            self.assertIsNot(tree.path2id(path), None,
1987
2146
                path+' not in working tree.')
1988
2147
 
1989
 
    def assertNotInWorkingTree(self,path,root_path='.',tree=None):
 
2148
    def assertNotInWorkingTree(self, path, root_path='.', tree=None):
1990
2149
        """Assert whether path or paths are not in the WorkingTree"""
1991
2150
        if tree is None:
1992
2151
            tree = workingtree.WorkingTree.open(root_path)
2051
2210
                # the branch is colocated on disk, we cannot create a checkout.
2052
2211
                # hopefully callers will expect this.
2053
2212
                local_controldir= bzrdir.BzrDir.open(self.get_vfs_only_url(relpath))
2054
 
                return local_controldir.create_workingtree()
 
2213
                wt = local_controldir.create_workingtree()
 
2214
                if wt.branch._format != b._format:
 
2215
                    wt._branch = b
 
2216
                    # Make sure that assigning to wt._branch fixes wt.branch,
 
2217
                    # in case the implementation details of workingtree objects
 
2218
                    # change.
 
2219
                    self.assertIs(b, wt.branch)
 
2220
                return wt
2055
2221
            else:
2056
2222
                return b.create_checkout(relpath, lightweight=True)
2057
2223
 
2102
2268
            self.transport_readonly_server = HttpServer
2103
2269
 
2104
2270
 
2105
 
def filter_suite_by_re(suite, pattern, exclude_pattern=None,
2106
 
                       random_order=False):
2107
 
    """Create a test suite by filtering another one.
2108
 
    
2109
 
    :param suite:           the source suite
2110
 
    :param pattern:         pattern that names must match
2111
 
    :param exclude_pattern: pattern that names must not match, if any
2112
 
    :param random_order:    if True, tests in the new suite will be put in
2113
 
                            random order
2114
 
    :returns: the newly created suite
2115
 
    """ 
2116
 
    return sort_suite_by_re(suite, pattern, exclude_pattern,
2117
 
        random_order, False)
2118
 
 
2119
 
 
2120
 
def sort_suite_by_re(suite, pattern, exclude_pattern=None,
2121
 
                     random_order=False, append_rest=True):
2122
 
    """Create a test suite by sorting another one.
2123
 
    
2124
 
    :param suite:           the source suite
2125
 
    :param pattern:         pattern that names must match in order to go
2126
 
                            first in the new suite
2127
 
    :param exclude_pattern: pattern that names must not match, if any
2128
 
    :param random_order:    if True, tests in the new suite will be put in
2129
 
                            random order
2130
 
    :param append_rest:     if False, pattern is a strict filter and not
2131
 
                            just an ordering directive
2132
 
    :returns: the newly created suite
2133
 
    """ 
2134
 
    first = []
2135
 
    second = []
 
2271
def condition_id_re(pattern):
 
2272
    """Create a condition filter which performs a re check on a test's id.
 
2273
    
 
2274
    :param pattern: A regular expression string.
 
2275
    :return: A callable that returns True if the re matches.
 
2276
    """
2136
2277
    filter_re = re.compile(pattern)
2137
 
    if exclude_pattern is not None:
2138
 
        exclude_re = re.compile(exclude_pattern)
2139
 
    for test in iter_suite_tests(suite):
 
2278
    def condition(test):
2140
2279
        test_id = test.id()
2141
 
        if exclude_pattern is None or not exclude_re.search(test_id):
2142
 
            if filter_re.search(test_id):
2143
 
                first.append(test)
2144
 
            elif append_rest:
2145
 
                second.append(test)
2146
 
    if random_order:
2147
 
        random.shuffle(first)
2148
 
        random.shuffle(second)
2149
 
    return TestUtil.TestSuite(first + second)
 
2280
        return filter_re.search(test_id)
 
2281
    return condition
 
2282
 
 
2283
 
 
2284
def condition_isinstance(klass_or_klass_list):
 
2285
    """Create a condition filter which returns isinstance(param, klass).
 
2286
    
 
2287
    :return: A callable which when called with one parameter obj return the
 
2288
        result of isinstance(obj, klass_or_klass_list).
 
2289
    """
 
2290
    def condition(obj):
 
2291
        return isinstance(obj, klass_or_klass_list)
 
2292
    return condition
 
2293
 
 
2294
 
 
2295
def condition_id_in_list(id_list):
 
2296
    """Create a condition filter which verify that test's id in a list.
 
2297
    
 
2298
    :param id_list: A TestIdList object.
 
2299
    :return: A callable that returns True if the test's id appears in the list.
 
2300
    """
 
2301
    def condition(test):
 
2302
        return id_list.includes(test.id())
 
2303
    return condition
 
2304
 
 
2305
 
 
2306
def condition_id_startswith(starts):
 
2307
    """Create a condition filter verifying that test's id starts with a string.
 
2308
    
 
2309
    :param starts: A list of string.
 
2310
    :return: A callable that returns True if the test's id starts with one of 
 
2311
        the given strings.
 
2312
    """
 
2313
    def condition(test):
 
2314
        for start in starts:
 
2315
            if test.id().startswith(start):
 
2316
                return True
 
2317
        return False
 
2318
    return condition
 
2319
 
 
2320
 
 
2321
def exclude_tests_by_condition(suite, condition):
 
2322
    """Create a test suite which excludes some tests from suite.
 
2323
 
 
2324
    :param suite: The suite to get tests from.
 
2325
    :param condition: A callable whose result evaluates True when called with a
 
2326
        test case which should be excluded from the result.
 
2327
    :return: A suite which contains the tests found in suite that fail
 
2328
        condition.
 
2329
    """
 
2330
    result = []
 
2331
    for test in iter_suite_tests(suite):
 
2332
        if not condition(test):
 
2333
            result.append(test)
 
2334
    return TestUtil.TestSuite(result)
 
2335
 
 
2336
 
 
2337
def filter_suite_by_condition(suite, condition):
 
2338
    """Create a test suite by filtering another one.
 
2339
    
 
2340
    :param suite: The source suite.
 
2341
    :param condition: A callable whose result evaluates True when called with a
 
2342
        test case which should be included in the result.
 
2343
    :return: A suite which contains the tests found in suite that pass
 
2344
        condition.
 
2345
    """
 
2346
    result = []
 
2347
    for test in iter_suite_tests(suite):
 
2348
        if condition(test):
 
2349
            result.append(test)
 
2350
    return TestUtil.TestSuite(result)
 
2351
 
 
2352
 
 
2353
def filter_suite_by_re(suite, pattern):
 
2354
    """Create a test suite by filtering another one.
 
2355
    
 
2356
    :param suite:           the source suite
 
2357
    :param pattern:         pattern that names must match
 
2358
    :returns: the newly created suite
 
2359
    """
 
2360
    condition = condition_id_re(pattern)
 
2361
    result_suite = filter_suite_by_condition(suite, condition)
 
2362
    return result_suite
 
2363
 
 
2364
 
 
2365
def filter_suite_by_id_list(suite, test_id_list):
 
2366
    """Create a test suite by filtering another one.
 
2367
 
 
2368
    :param suite: The source suite.
 
2369
    :param test_id_list: A list of the test ids to keep as strings.
 
2370
    :returns: the newly created suite
 
2371
    """
 
2372
    condition = condition_id_in_list(test_id_list)
 
2373
    result_suite = filter_suite_by_condition(suite, condition)
 
2374
    return result_suite
 
2375
 
 
2376
 
 
2377
def filter_suite_by_id_startswith(suite, start):
 
2378
    """Create a test suite by filtering another one.
 
2379
 
 
2380
    :param suite: The source suite.
 
2381
    :param start: A list of string the test id must start with one of.
 
2382
    :returns: the newly created suite
 
2383
    """
 
2384
    condition = condition_id_startswith(start)
 
2385
    result_suite = filter_suite_by_condition(suite, condition)
 
2386
    return result_suite
 
2387
 
 
2388
 
 
2389
def exclude_tests_by_re(suite, pattern):
 
2390
    """Create a test suite which excludes some tests from suite.
 
2391
 
 
2392
    :param suite: The suite to get tests from.
 
2393
    :param pattern: A regular expression string. Test ids that match this
 
2394
        pattern will be excluded from the result.
 
2395
    :return: A TestSuite that contains all the tests from suite without the
 
2396
        tests that matched pattern. The order of tests is the same as it was in
 
2397
        suite.
 
2398
    """
 
2399
    return exclude_tests_by_condition(suite, condition_id_re(pattern))
 
2400
 
 
2401
 
 
2402
def preserve_input(something):
 
2403
    """A helper for performing test suite transformation chains.
 
2404
 
 
2405
    :param something: Anything you want to preserve.
 
2406
    :return: Something.
 
2407
    """
 
2408
    return something
 
2409
 
 
2410
 
 
2411
def randomize_suite(suite):
 
2412
    """Return a new TestSuite with suite's tests in random order.
 
2413
    
 
2414
    The tests in the input suite are flattened into a single suite in order to
 
2415
    accomplish this. Any nested TestSuites are removed to provide global
 
2416
    randomness.
 
2417
    """
 
2418
    tests = list(iter_suite_tests(suite))
 
2419
    random.shuffle(tests)
 
2420
    return TestUtil.TestSuite(tests)
 
2421
 
 
2422
 
 
2423
def split_suite_by_condition(suite, condition):
 
2424
    """Split a test suite into two by a condition.
 
2425
    
 
2426
    :param suite: The suite to split.
 
2427
    :param condition: The condition to match on. Tests that match this
 
2428
        condition are returned in the first test suite, ones that do not match
 
2429
        are in the second suite.
 
2430
    :return: A tuple of two test suites, where the first contains tests from
 
2431
        suite matching the condition, and the second contains the remainder
 
2432
        from suite. The order within each output suite is the same as it was in
 
2433
        suite.
 
2434
    """
 
2435
    matched = []
 
2436
    did_not_match = []
 
2437
    for test in iter_suite_tests(suite):
 
2438
        if condition(test):
 
2439
            matched.append(test)
 
2440
        else:
 
2441
            did_not_match.append(test)
 
2442
    return TestUtil.TestSuite(matched), TestUtil.TestSuite(did_not_match)
 
2443
 
 
2444
 
 
2445
def split_suite_by_re(suite, pattern):
 
2446
    """Split a test suite into two by a regular expression.
 
2447
    
 
2448
    :param suite: The suite to split.
 
2449
    :param pattern: A regular expression string. Test ids that match this
 
2450
        pattern will be in the first test suite returned, and the others in the
 
2451
        second test suite returned.
 
2452
    :return: A tuple of two test suites, where the first contains tests from
 
2453
        suite matching pattern, and the second contains the remainder from
 
2454
        suite. The order within each output suite is the same as it was in
 
2455
        suite.
 
2456
    """
 
2457
    return split_suite_by_condition(suite, condition_id_re(pattern))
2150
2458
 
2151
2459
 
2152
2460
def run_suite(suite, name='test', verbose=False, pattern=".*",
2156
2464
              list_only=False,
2157
2465
              random_seed=None,
2158
2466
              exclude_pattern=None,
2159
 
              ):
 
2467
              strict=False):
2160
2468
    TestCase._gather_lsprof_in_benchmarks = lsprof_timed
2161
2469
    if verbose:
2162
2470
        verbosity = 2
2186
2494
            (random_seed))
2187
2495
        random.seed(random_seed)
2188
2496
    # Customise the list of tests if requested
2189
 
    if pattern != '.*' or exclude_pattern is not None or random_order:
 
2497
    if exclude_pattern is not None:
 
2498
        suite = exclude_tests_by_re(suite, exclude_pattern)
 
2499
    if random_order:
 
2500
        order_changer = randomize_suite
 
2501
    else:
 
2502
        order_changer = preserve_input
 
2503
    if pattern != '.*' or random_order:
2190
2504
        if matching_tests_first:
2191
 
            suite = sort_suite_by_re(suite, pattern, exclude_pattern,
2192
 
                random_order)
 
2505
            suites = map(order_changer, split_suite_by_re(suite, pattern))
 
2506
            suite = TestUtil.TestSuite(suites)
2193
2507
        else:
2194
 
            suite = filter_suite_by_re(suite, pattern, exclude_pattern,
2195
 
                random_order)
 
2508
            suite = order_changer(filter_suite_by_re(suite, pattern))
 
2509
 
2196
2510
    result = runner.run(suite)
 
2511
 
 
2512
    if strict:
 
2513
        return result.wasStrictlySuccessful()
 
2514
 
2197
2515
    return result.wasSuccessful()
2198
2516
 
2199
2517
 
 
2518
# Controlled by "bzr selftest -E=..." option
 
2519
selftest_debug_flags = set()
 
2520
 
 
2521
 
2200
2522
def selftest(verbose=False, pattern=".*", stop_on_failure=True,
2201
2523
             transport=None,
2202
2524
             test_suite_factory=None,
2205
2527
             matching_tests_first=None,
2206
2528
             list_only=False,
2207
2529
             random_seed=None,
2208
 
             exclude_pattern=None):
 
2530
             exclude_pattern=None,
 
2531
             strict=False,
 
2532
             load_list=None,
 
2533
             debug_flags=None,
 
2534
             starting_with=None,
 
2535
             ):
2209
2536
    """Run the whole test suite under the enhanced runner"""
2210
2537
    # XXX: Very ugly way to do this...
2211
2538
    # Disable warning about old formats because we don't want it to disturb
2218
2545
        transport = default_transport
2219
2546
    old_transport = default_transport
2220
2547
    default_transport = transport
 
2548
    global selftest_debug_flags
 
2549
    old_debug_flags = selftest_debug_flags
 
2550
    if debug_flags is not None:
 
2551
        selftest_debug_flags = set(debug_flags)
2221
2552
    try:
 
2553
        if load_list is None:
 
2554
            keep_only = None
 
2555
        else:
 
2556
            keep_only = load_test_id_list(load_list)
2222
2557
        if test_suite_factory is None:
2223
 
            suite = test_suite()
 
2558
            suite = test_suite(keep_only, starting_with)
2224
2559
        else:
2225
2560
            suite = test_suite_factory()
2226
2561
        return run_suite(suite, 'testbzr', verbose=verbose, pattern=pattern,
2231
2566
                     matching_tests_first=matching_tests_first,
2232
2567
                     list_only=list_only,
2233
2568
                     random_seed=random_seed,
2234
 
                     exclude_pattern=exclude_pattern)
 
2569
                     exclude_pattern=exclude_pattern,
 
2570
                     strict=strict)
2235
2571
    finally:
2236
2572
        default_transport = old_transport
2237
 
 
2238
 
 
2239
 
def test_suite():
 
2573
        selftest_debug_flags = old_debug_flags
 
2574
 
 
2575
 
 
2576
def load_test_id_list(file_name):
 
2577
    """Load a test id list from a text file.
 
2578
 
 
2579
    The format is one test id by line.  No special care is taken to impose
 
2580
    strict rules, these test ids are used to filter the test suite so a test id
 
2581
    that do not match an existing test will do no harm. This allows user to add
 
2582
    comments, leave blank lines, etc.
 
2583
    """
 
2584
    test_list = []
 
2585
    try:
 
2586
        ftest = open(file_name, 'rt')
 
2587
    except IOError, e:
 
2588
        if e.errno != errno.ENOENT:
 
2589
            raise
 
2590
        else:
 
2591
            raise errors.NoSuchFile(file_name)
 
2592
 
 
2593
    for test_name in ftest.readlines():
 
2594
        test_list.append(test_name.strip())
 
2595
    ftest.close()
 
2596
    return test_list
 
2597
 
 
2598
 
 
2599
def suite_matches_id_list(test_suite, id_list):
 
2600
    """Warns about tests not appearing or appearing more than once.
 
2601
 
 
2602
    :param test_suite: A TestSuite object.
 
2603
    :param test_id_list: The list of test ids that should be found in 
 
2604
         test_suite.
 
2605
 
 
2606
    :return: (absents, duplicates) absents is a list containing the test found
 
2607
        in id_list but not in test_suite, duplicates is a list containing the
 
2608
        test found multiple times in test_suite.
 
2609
 
 
2610
    When using a prefined test id list, it may occurs that some tests do not
 
2611
    exist anymore or that some tests use the same id. This function warns the
 
2612
    tester about potential problems in his workflow (test lists are volatile)
 
2613
    or in the test suite itself (using the same id for several tests does not
 
2614
    help to localize defects).
 
2615
    """
 
2616
    # Build a dict counting id occurrences
 
2617
    tests = dict()
 
2618
    for test in iter_suite_tests(test_suite):
 
2619
        id = test.id()
 
2620
        tests[id] = tests.get(id, 0) + 1
 
2621
 
 
2622
    not_found = []
 
2623
    duplicates = []
 
2624
    for id in id_list:
 
2625
        occurs = tests.get(id, 0)
 
2626
        if not occurs:
 
2627
            not_found.append(id)
 
2628
        elif occurs > 1:
 
2629
            duplicates.append(id)
 
2630
 
 
2631
    return not_found, duplicates
 
2632
 
 
2633
 
 
2634
class TestIdList(object):
 
2635
    """Test id list to filter a test suite.
 
2636
 
 
2637
    Relying on the assumption that test ids are built as:
 
2638
    <module>[.<class>.<method>][(<param>+)], <module> being in python dotted
 
2639
    notation, this class offers methods to :
 
2640
    - avoid building a test suite for modules not refered to in the test list,
 
2641
    - keep only the tests listed from the module test suite.
 
2642
    """
 
2643
 
 
2644
    def __init__(self, test_id_list):
 
2645
        # When a test suite needs to be filtered against us we compare test ids
 
2646
        # for equality, so a simple dict offers a quick and simple solution.
 
2647
        self.tests = dict().fromkeys(test_id_list, True)
 
2648
 
 
2649
        # While unittest.TestCase have ids like:
 
2650
        # <module>.<class>.<method>[(<param+)],
 
2651
        # doctest.DocTestCase can have ids like:
 
2652
        # <module>
 
2653
        # <module>.<class>
 
2654
        # <module>.<function>
 
2655
        # <module>.<class>.<method>
 
2656
 
 
2657
        # Since we can't predict a test class from its name only, we settle on
 
2658
        # a simple constraint: a test id always begins with its module name.
 
2659
 
 
2660
        modules = {}
 
2661
        for test_id in test_id_list:
 
2662
            parts = test_id.split('.')
 
2663
            mod_name = parts.pop(0)
 
2664
            modules[mod_name] = True
 
2665
            for part in parts:
 
2666
                mod_name += '.' + part
 
2667
                modules[mod_name] = True
 
2668
        self.modules = modules
 
2669
 
 
2670
    def refers_to(self, module_name):
 
2671
        """Is there tests for the module or one of its sub modules."""
 
2672
        return self.modules.has_key(module_name)
 
2673
 
 
2674
    def includes(self, test_id):
 
2675
        return self.tests.has_key(test_id)
 
2676
 
 
2677
 
 
2678
class TestPrefixAliasRegistry(registry.Registry):
 
2679
    """A registry for test prefix aliases.
 
2680
 
 
2681
    This helps implement shorcuts for the --starting-with selftest
 
2682
    option. Overriding existing prefixes is not allowed but not fatal (a
 
2683
    warning will be emitted).
 
2684
    """
 
2685
 
 
2686
    def register(self, key, obj, help=None, info=None,
 
2687
                 override_existing=False):
 
2688
        """See Registry.register.
 
2689
 
 
2690
        Trying to override an existing alias causes a warning to be emitted,
 
2691
        not a fatal execption.
 
2692
        """
 
2693
        try:
 
2694
            super(TestPrefixAliasRegistry, self).register(
 
2695
                key, obj, help=help, info=info, override_existing=False)
 
2696
        except KeyError:
 
2697
            actual = self.get(key)
 
2698
            note('Test prefix alias %s is already used for %s, ignoring %s'
 
2699
                 % (key, actual, obj))
 
2700
 
 
2701
    def resolve_alias(self, id_start):
 
2702
        """Replace the alias by the prefix in the given string.
 
2703
 
 
2704
        Using an unknown prefix is an error to help catching typos.
 
2705
        """
 
2706
        parts = id_start.split('.')
 
2707
        try:
 
2708
            parts[0] = self.get(parts[0])
 
2709
        except KeyError:
 
2710
            raise errors.BzrCommandError(
 
2711
                '%s is not a known test prefix alias' % parts[0])
 
2712
        return '.'.join(parts)
 
2713
 
 
2714
 
 
2715
test_prefix_alias_registry = TestPrefixAliasRegistry()
 
2716
"""Registry of test prefix aliases."""
 
2717
 
 
2718
 
 
2719
# This alias allows to detect typos ('bzrlin.') by making all valid test ids
 
2720
# appear prefixed ('bzrlib.' is "replaced" by 'bzrlib.').
 
2721
test_prefix_alias_registry.register('bzrlib', 'bzrlib')
 
2722
 
 
2723
# Obvious higest levels prefixes, feel free to add your own via a plugin
 
2724
test_prefix_alias_registry.register('bd', 'bzrlib.doc')
 
2725
test_prefix_alias_registry.register('bu', 'bzrlib.utils')
 
2726
test_prefix_alias_registry.register('bt', 'bzrlib.tests')
 
2727
test_prefix_alias_registry.register('bb', 'bzrlib.tests.blackbox')
 
2728
test_prefix_alias_registry.register('bp', 'bzrlib.plugins')
 
2729
 
 
2730
 
 
2731
def test_suite(keep_only=None, starting_with=None):
2240
2732
    """Build and return TestSuite for the whole of bzrlib.
2241
 
    
 
2733
 
 
2734
    :param keep_only: A list of test ids limiting the suite returned.
 
2735
 
 
2736
    :param starting_with: An id limiting the suite returned to the tests
 
2737
         starting with it.
 
2738
 
2242
2739
    This function can be replaced if you need to change the default test
2243
2740
    suite on a global basis, but it is not encouraged.
2244
2741
    """
2245
2742
    testmod_names = [
 
2743
                   'bzrlib.doc',
2246
2744
                   'bzrlib.util.tests.test_bencode',
 
2745
                   'bzrlib.tests.blackbox',
 
2746
                   'bzrlib.tests.branch_implementations',
 
2747
                   'bzrlib.tests.bzrdir_implementations',
 
2748
                   'bzrlib.tests.commands',
 
2749
                   'bzrlib.tests.inventory_implementations',
 
2750
                   'bzrlib.tests.interrepository_implementations',
 
2751
                   'bzrlib.tests.intertree_implementations',
 
2752
                   'bzrlib.tests.per_lock',
 
2753
                   'bzrlib.tests.repository_implementations',
 
2754
                   'bzrlib.tests.test__dirstate_helpers',
2247
2755
                   'bzrlib.tests.test_ancestry',
2248
2756
                   'bzrlib.tests.test_annotate',
2249
2757
                   'bzrlib.tests.test_api',
2250
2758
                   'bzrlib.tests.test_atomicfile',
2251
2759
                   'bzrlib.tests.test_bad_files',
 
2760
                   'bzrlib.tests.test_bisect_multi',
2252
2761
                   'bzrlib.tests.test_branch',
2253
2762
                   'bzrlib.tests.test_branchbuilder',
 
2763
                   'bzrlib.tests.test_btree_index',
2254
2764
                   'bzrlib.tests.test_bugtracker',
2255
2765
                   'bzrlib.tests.test_bundle',
2256
2766
                   'bzrlib.tests.test_bzrdir',
2257
2767
                   'bzrlib.tests.test_cache_utf8',
 
2768
                   'bzrlib.tests.test_chunk_writer',
2258
2769
                   'bzrlib.tests.test_commands',
2259
2770
                   'bzrlib.tests.test_commit',
2260
2771
                   'bzrlib.tests.test_commit_merge',
2261
2772
                   'bzrlib.tests.test_config',
2262
2773
                   'bzrlib.tests.test_conflicts',
2263
 
                   'bzrlib.tests.test_pack',
2264
2774
                   'bzrlib.tests.test_counted_lock',
2265
2775
                   'bzrlib.tests.test_decorators',
2266
2776
                   'bzrlib.tests.test_delta',
2267
2777
                   'bzrlib.tests.test_deprecated_graph',
2268
2778
                   'bzrlib.tests.test_diff',
2269
2779
                   'bzrlib.tests.test_dirstate',
 
2780
                   'bzrlib.tests.test_directory_service',
 
2781
                   'bzrlib.tests.test_email_message',
2270
2782
                   'bzrlib.tests.test_errors',
2271
 
                   'bzrlib.tests.test_escaped_store',
2272
2783
                   'bzrlib.tests.test_extract',
2273
2784
                   'bzrlib.tests.test_fetch',
2274
2785
                   'bzrlib.tests.test_ftp_transport',
2281
2792
                   'bzrlib.tests.test_help',
2282
2793
                   'bzrlib.tests.test_hooks',
2283
2794
                   'bzrlib.tests.test_http',
 
2795
                   'bzrlib.tests.test_http_implementations',
2284
2796
                   'bzrlib.tests.test_http_response',
2285
2797
                   'bzrlib.tests.test_https_ca_bundle',
2286
2798
                   'bzrlib.tests.test_identitymap',
2287
2799
                   'bzrlib.tests.test_ignores',
 
2800
                   'bzrlib.tests.test_index',
2288
2801
                   'bzrlib.tests.test_info',
2289
2802
                   'bzrlib.tests.test_inv',
2290
2803
                   'bzrlib.tests.test_knit',
2294
2807
                   'bzrlib.tests.test_lockable_files',
2295
2808
                   'bzrlib.tests.test_log',
2296
2809
                   'bzrlib.tests.test_lsprof',
 
2810
                   'bzrlib.tests.test_lru_cache',
 
2811
                   'bzrlib.tests.test_mail_client',
2297
2812
                   'bzrlib.tests.test_memorytree',
2298
2813
                   'bzrlib.tests.test_merge',
2299
2814
                   'bzrlib.tests.test_merge3',
2301
2816
                   'bzrlib.tests.test_merge_directive',
2302
2817
                   'bzrlib.tests.test_missing',
2303
2818
                   'bzrlib.tests.test_msgeditor',
 
2819
                   'bzrlib.tests.test_multiparent',
 
2820
                   'bzrlib.tests.test_mutabletree',
2304
2821
                   'bzrlib.tests.test_nonascii',
2305
2822
                   'bzrlib.tests.test_options',
2306
2823
                   'bzrlib.tests.test_osutils',
2307
2824
                   'bzrlib.tests.test_osutils_encodings',
 
2825
                   'bzrlib.tests.test_pack',
 
2826
                   'bzrlib.tests.test_pack_repository',
2308
2827
                   'bzrlib.tests.test_patch',
2309
2828
                   'bzrlib.tests.test_patches',
2310
2829
                   'bzrlib.tests.test_permissions',
2311
2830
                   'bzrlib.tests.test_plugins',
2312
2831
                   'bzrlib.tests.test_progress',
 
2832
                   'bzrlib.tests.test_read_bundle',
 
2833
                   'bzrlib.tests.test_reconfigure',
2313
2834
                   'bzrlib.tests.test_reconcile',
2314
2835
                   'bzrlib.tests.test_registry',
2315
2836
                   'bzrlib.tests.test_remote',
2316
2837
                   'bzrlib.tests.test_repository',
 
2838
                   'bzrlib.tests.per_repository_reference',
2317
2839
                   'bzrlib.tests.test_revert',
2318
2840
                   'bzrlib.tests.test_revision',
2319
 
                   'bzrlib.tests.test_revisionnamespaces',
 
2841
                   'bzrlib.tests.test_revisionspec',
2320
2842
                   'bzrlib.tests.test_revisiontree',
2321
2843
                   'bzrlib.tests.test_rio',
 
2844
                   'bzrlib.tests.test_rules',
2322
2845
                   'bzrlib.tests.test_sampler',
2323
2846
                   'bzrlib.tests.test_selftest',
2324
2847
                   'bzrlib.tests.test_setup',
2333
2856
                   'bzrlib.tests.test_store',
2334
2857
                   'bzrlib.tests.test_strace',
2335
2858
                   'bzrlib.tests.test_subsume',
 
2859
                   'bzrlib.tests.test_switch',
2336
2860
                   'bzrlib.tests.test_symbol_versioning',
2337
2861
                   'bzrlib.tests.test_tag',
2338
2862
                   'bzrlib.tests.test_testament',
2343
2867
                   'bzrlib.tests.test_transactions',
2344
2868
                   'bzrlib.tests.test_transform',
2345
2869
                   'bzrlib.tests.test_transport',
 
2870
                   'bzrlib.tests.test_transport_implementations',
 
2871
                   'bzrlib.tests.test_transport_log',
2346
2872
                   'bzrlib.tests.test_tree',
2347
2873
                   'bzrlib.tests.test_treebuilder',
2348
2874
                   'bzrlib.tests.test_tsort',
2349
2875
                   'bzrlib.tests.test_tuned_gzip',
2350
2876
                   'bzrlib.tests.test_ui',
 
2877
                   'bzrlib.tests.test_uncommit',
2351
2878
                   'bzrlib.tests.test_upgrade',
 
2879
                   'bzrlib.tests.test_upgrade_stacked',
2352
2880
                   'bzrlib.tests.test_urlutils',
2353
2881
                   'bzrlib.tests.test_versionedfile',
2354
2882
                   'bzrlib.tests.test_version',
2355
2883
                   'bzrlib.tests.test_version_info',
 
2884
                   'bzrlib.tests.test__walkdirs_win32',
2356
2885
                   'bzrlib.tests.test_weave',
2357
2886
                   'bzrlib.tests.test_whitebox',
 
2887
                   'bzrlib.tests.test_win32utils',
2358
2888
                   'bzrlib.tests.test_workingtree',
2359
2889
                   'bzrlib.tests.test_workingtree_4',
2360
2890
                   'bzrlib.tests.test_wsgi',
2361
2891
                   'bzrlib.tests.test_xml',
 
2892
                   'bzrlib.tests.tree_implementations',
 
2893
                   'bzrlib.tests.workingtree_implementations',
2362
2894
                   ]
2363
 
    test_transport_implementations = [
2364
 
        'bzrlib.tests.test_transport_implementations',
2365
 
        'bzrlib.tests.test_read_bundle',
2366
 
        ]
2367
 
    suite = TestUtil.TestSuite()
 
2895
 
2368
2896
    loader = TestUtil.TestLoader()
 
2897
 
 
2898
    if starting_with:
 
2899
        starting_with = [test_prefix_alias_registry.resolve_alias(start)
 
2900
                         for start in starting_with]
 
2901
        # We take precedence over keep_only because *at loading time* using
 
2902
        # both options means we will load less tests for the same final result.
 
2903
        def interesting_module(name):
 
2904
            for start in starting_with:
 
2905
                if (
 
2906
                    # Either the module name starts with the specified string
 
2907
                    name.startswith(start)
 
2908
                    # or it may contain tests starting with the specified string
 
2909
                    or start.startswith(name)
 
2910
                    ):
 
2911
                    return True
 
2912
            return False
 
2913
        loader = TestUtil.FilteredByModuleTestLoader(interesting_module)
 
2914
 
 
2915
    elif keep_only is not None:
 
2916
        id_filter = TestIdList(keep_only)
 
2917
        loader = TestUtil.FilteredByModuleTestLoader(id_filter.refers_to)
 
2918
        def interesting_module(name):
 
2919
            return id_filter.refers_to(name)
 
2920
 
 
2921
    else:
 
2922
        loader = TestUtil.TestLoader()
 
2923
        def interesting_module(name):
 
2924
            # No filtering, all modules are interesting
 
2925
            return True
 
2926
 
 
2927
    suite = loader.suiteClass()
 
2928
 
 
2929
    # modules building their suite with loadTestsFromModuleNames
2369
2930
    suite.addTest(loader.loadTestsFromModuleNames(testmod_names))
2370
 
    from bzrlib.tests.test_transport_implementations import TransportTestProviderAdapter
2371
 
    adapter = TransportTestProviderAdapter()
2372
 
    adapt_modules(test_transport_implementations, adapter, loader, suite)
2373
 
    for package in packages_to_test():
2374
 
        suite.addTest(package.test_suite())
2375
 
    for m in MODULES_TO_TEST:
2376
 
        suite.addTest(loader.loadTestsFromModule(m))
2377
 
    for m in MODULES_TO_DOCTEST:
 
2931
 
 
2932
    modules_to_doctest = [
 
2933
        'bzrlib',
 
2934
        'bzrlib.errors',
 
2935
        'bzrlib.export',
 
2936
        'bzrlib.inventory',
 
2937
        'bzrlib.iterablefile',
 
2938
        'bzrlib.lockdir',
 
2939
        'bzrlib.merge3',
 
2940
        'bzrlib.option',
 
2941
        'bzrlib.store',
 
2942
        'bzrlib.symbol_versioning',
 
2943
        'bzrlib.tests',
 
2944
        'bzrlib.timestamp',
 
2945
        'bzrlib.version_info_formats.format_custom',
 
2946
        ]
 
2947
 
 
2948
    for mod in modules_to_doctest:
 
2949
        if not interesting_module(mod):
 
2950
            # No tests to keep here, move along
 
2951
            continue
2378
2952
        try:
2379
 
            suite.addTest(doctest.DocTestSuite(m))
 
2953
            doc_suite = doctest.DocTestSuite(mod)
2380
2954
        except ValueError, e:
2381
 
            print '**failed to get doctest for: %s\n%s' %(m,e)
 
2955
            print '**failed to get doctest for: %s\n%s' % (mod, e)
2382
2956
            raise
2383
 
    for name, plugin in bzrlib.plugin.all_plugins().items():
2384
 
        if getattr(plugin, 'test_suite', None) is not None:
2385
 
            default_encoding = sys.getdefaultencoding()
2386
 
            try:
2387
 
                plugin_suite = plugin.test_suite()
2388
 
            except ImportError, e:
2389
 
                bzrlib.trace.warning(
2390
 
                    'Unable to test plugin "%s": %s', name, e)
2391
 
            else:
2392
 
                suite.addTest(plugin_suite)
2393
 
            if default_encoding != sys.getdefaultencoding():
2394
 
                bzrlib.trace.warning(
2395
 
                    'Plugin "%s" tried to reset default encoding to: %s', name,
2396
 
                    sys.getdefaultencoding())
2397
 
                reload(sys)
2398
 
                sys.setdefaultencoding(default_encoding)
2399
 
    return suite
 
2957
        suite.addTest(doc_suite)
 
2958
 
 
2959
    default_encoding = sys.getdefaultencoding()
 
2960
    for name, plugin in bzrlib.plugin.plugins().items():
 
2961
        if not interesting_module(plugin.module.__name__):
 
2962
            continue
 
2963
        plugin_suite = plugin.test_suite()
 
2964
        # We used to catch ImportError here and turn it into just a warning,
 
2965
        # but really if you don't have --no-plugins this should be a failure.
 
2966
        # mbp 20080213 - see http://bugs.launchpad.net/bugs/189771
 
2967
        if plugin_suite is None:
 
2968
            plugin_suite = plugin.load_plugin_tests(loader)
 
2969
        if plugin_suite is not None:
 
2970
            suite.addTest(plugin_suite)
 
2971
        if default_encoding != sys.getdefaultencoding():
 
2972
            bzrlib.trace.warning(
 
2973
                'Plugin "%s" tried to reset default encoding to: %s', name,
 
2974
                sys.getdefaultencoding())
 
2975
            reload(sys)
 
2976
            sys.setdefaultencoding(default_encoding)
 
2977
 
 
2978
    if starting_with:
 
2979
        suite = filter_suite_by_id_startswith(suite, starting_with)
 
2980
 
 
2981
    if keep_only is not None:
 
2982
        # Now that the referred modules have loaded their tests, keep only the
 
2983
        # requested ones.
 
2984
        suite = filter_suite_by_id_list(suite, id_filter)
 
2985
        # Do some sanity checks on the id_list filtering
 
2986
        not_found, duplicates = suite_matches_id_list(suite, keep_only)
 
2987
        if starting_with:
 
2988
            # The tester has used both keep_only and starting_with, so he is
 
2989
            # already aware that some tests are excluded from the list, there
 
2990
            # is no need to tell him which.
 
2991
            pass
 
2992
        else:
 
2993
            # Some tests mentioned in the list are not in the test suite. The
 
2994
            # list may be out of date, report to the tester.
 
2995
            for id in not_found:
 
2996
                bzrlib.trace.warning('"%s" not found in the test suite', id)
 
2997
        for id in duplicates:
 
2998
            bzrlib.trace.warning('"%s" is used as an id by several tests', id)
 
2999
 
 
3000
    return suite
 
3001
 
 
3002
 
 
3003
def multiply_tests_from_modules(module_name_list, scenario_iter, loader=None):
 
3004
    """Adapt all tests in some given modules to given scenarios.
 
3005
 
 
3006
    This is the recommended public interface for test parameterization.
 
3007
    Typically the test_suite() method for a per-implementation test
 
3008
    suite will call multiply_tests_from_modules and return the 
 
3009
    result.
 
3010
 
 
3011
    :param module_name_list: List of fully-qualified names of test
 
3012
        modules.
 
3013
    :param scenario_iter: Iterable of pairs of (scenario_name, 
 
3014
        scenario_param_dict).
 
3015
    :param loader: If provided, will be used instead of a new 
 
3016
        bzrlib.tests.TestLoader() instance.
 
3017
 
 
3018
    This returns a new TestSuite containing the cross product of
 
3019
    all the tests in all the modules, each repeated for each scenario.
 
3020
    Each test is adapted by adding the scenario name at the end 
 
3021
    of its name, and updating the test object's __dict__ with the
 
3022
    scenario_param_dict.
 
3023
 
 
3024
    >>> r = multiply_tests_from_modules(
 
3025
    ...     ['bzrlib.tests.test_sampler'],
 
3026
    ...     [('one', dict(param=1)), 
 
3027
    ...      ('two', dict(param=2))])
 
3028
    >>> tests = list(iter_suite_tests(r))
 
3029
    >>> len(tests)
 
3030
    2
 
3031
    >>> tests[0].id()
 
3032
    'bzrlib.tests.test_sampler.DemoTest.test_nothing(one)'
 
3033
    >>> tests[0].param
 
3034
    1
 
3035
    >>> tests[1].param
 
3036
    2
 
3037
    """
 
3038
    # XXX: Isn't load_tests() a better way to provide the same functionality
 
3039
    # without forcing a predefined TestScenarioApplier ? --vila 080215
 
3040
    if loader is None:
 
3041
        loader = TestUtil.TestLoader()
 
3042
 
 
3043
    suite = loader.suiteClass()
 
3044
 
 
3045
    adapter = TestScenarioApplier()
 
3046
    adapter.scenarios = list(scenario_iter)
 
3047
    adapt_modules(module_name_list, adapter, loader, suite)
 
3048
    return suite
 
3049
 
 
3050
 
 
3051
def multiply_scenarios(scenarios_left, scenarios_right):
 
3052
    """Multiply two sets of scenarios.
 
3053
 
 
3054
    :returns: the cartesian product of the two sets of scenarios, that is
 
3055
        a scenario for every possible combination of a left scenario and a
 
3056
        right scenario.
 
3057
    """
 
3058
    return [
 
3059
        ('%s,%s' % (left_name, right_name),
 
3060
         dict(left_dict.items() + right_dict.items()))
 
3061
        for left_name, left_dict in scenarios_left
 
3062
        for right_name, right_dict in scenarios_right]
 
3063
 
2400
3064
 
2401
3065
 
2402
3066
def adapt_modules(mods_list, adapter, loader, suite):
2403
3067
    """Adapt the modules in mods_list using adapter and add to suite."""
2404
 
    for test in iter_suite_tests(loader.loadTestsFromModuleNames(mods_list)):
 
3068
    tests = loader.loadTestsFromModuleNames(mods_list)
 
3069
    adapt_tests(tests, adapter, suite)
 
3070
 
 
3071
 
 
3072
def adapt_tests(tests_list, adapter, suite):
 
3073
    """Adapt the tests in tests_list using adapter and add to suite."""
 
3074
    for test in iter_suite_tests(tests_list):
2405
3075
        suite.addTests(adapter.adapt(test))
2406
3076
 
2407
3077
 
2421
3091
        osutils.rmtree(dirname)
2422
3092
    except OSError, e:
2423
3093
        if sys.platform == 'win32' and e.errno == errno.EACCES:
2424
 
            print >>sys.stderr, ('Permission denied: '
 
3094
            sys.stderr.write(('Permission denied: '
2425
3095
                                 'unable to remove testing dir '
2426
 
                                 '%s' % os.path.basename(dirname))
 
3096
                                 '%s\n' % os.path.basename(dirname)))
2427
3097
        else:
2428
3098
            raise
2429
3099
 
2456
3126
        return self.__class__.__name__
2457
3127
 
2458
3128
 
 
3129
class _SymlinkFeature(Feature):
 
3130
 
 
3131
    def _probe(self):
 
3132
        return osutils.has_symlinks()
 
3133
 
 
3134
    def feature_name(self):
 
3135
        return 'symlinks'
 
3136
 
 
3137
SymlinkFeature = _SymlinkFeature()
 
3138
 
 
3139
 
 
3140
class _HardlinkFeature(Feature):
 
3141
 
 
3142
    def _probe(self):
 
3143
        return osutils.has_hardlinks()
 
3144
 
 
3145
    def feature_name(self):
 
3146
        return 'hardlinks'
 
3147
 
 
3148
HardlinkFeature = _HardlinkFeature()
 
3149
 
 
3150
 
 
3151
class _OsFifoFeature(Feature):
 
3152
 
 
3153
    def _probe(self):
 
3154
        return getattr(os, 'mkfifo', None)
 
3155
 
 
3156
    def feature_name(self):
 
3157
        return 'filesystem fifos'
 
3158
 
 
3159
OsFifoFeature = _OsFifoFeature()
 
3160
 
 
3161
 
 
3162
class _UnicodeFilenameFeature(Feature):
 
3163
    """Does the filesystem support Unicode filenames?"""
 
3164
 
 
3165
    def _probe(self):
 
3166
        try:
 
3167
            # Check for character combinations unlikely to be covered by any
 
3168
            # single non-unicode encoding. We use the characters
 
3169
            # - greek small letter alpha (U+03B1) and
 
3170
            # - braille pattern dots-123456 (U+283F).
 
3171
            os.stat(u'\u03b1\u283f')
 
3172
        except UnicodeEncodeError:
 
3173
            return False
 
3174
        except (IOError, OSError):
 
3175
            # The filesystem allows the Unicode filename but the file doesn't
 
3176
            # exist.
 
3177
            return True
 
3178
        else:
 
3179
            # The filesystem allows the Unicode filename and the file exists,
 
3180
            # for some reason.
 
3181
            return True
 
3182
 
 
3183
UnicodeFilenameFeature = _UnicodeFilenameFeature()
 
3184
 
 
3185
 
2459
3186
class TestScenarioApplier(object):
2460
3187
    """A tool to apply scenarios to tests."""
2461
3188
 
2483
3210
        new_id = "%s(%s)" % (new_test.id(), scenario[0])
2484
3211
        new_test.id = lambda: new_id
2485
3212
        return new_test
 
3213
 
 
3214
 
 
3215
def probe_unicode_in_user_encoding():
 
3216
    """Try to encode several unicode strings to use in unicode-aware tests.
 
3217
    Return first successfull match.
 
3218
 
 
3219
    :return:  (unicode value, encoded plain string value) or (None, None)
 
3220
    """
 
3221
    possible_vals = [u'm\xb5', u'\xe1', u'\u0410']
 
3222
    for uni_val in possible_vals:
 
3223
        try:
 
3224
            str_val = uni_val.encode(bzrlib.user_encoding)
 
3225
        except UnicodeEncodeError:
 
3226
            # Try a different character
 
3227
            pass
 
3228
        else:
 
3229
            return uni_val, str_val
 
3230
    return None, None
 
3231
 
 
3232
 
 
3233
def probe_bad_non_ascii(encoding):
 
3234
    """Try to find [bad] character with code [128..255]
 
3235
    that cannot be decoded to unicode in some encoding.
 
3236
    Return None if all non-ascii characters is valid
 
3237
    for given encoding.
 
3238
    """
 
3239
    for i in xrange(128, 256):
 
3240
        char = chr(i)
 
3241
        try:
 
3242
            char.decode(encoding)
 
3243
        except UnicodeDecodeError:
 
3244
            return char
 
3245
    return None
 
3246
 
 
3247
 
 
3248
class _FTPServerFeature(Feature):
 
3249
    """Some tests want an FTP Server, check if one is available.
 
3250
 
 
3251
    Right now, the only way this is available is if 'medusa' is installed.
 
3252
    http://www.amk.ca/python/code/medusa.html
 
3253
    """
 
3254
 
 
3255
    def _probe(self):
 
3256
        try:
 
3257
            import bzrlib.tests.ftp_server
 
3258
            return True
 
3259
        except ImportError:
 
3260
            return False
 
3261
 
 
3262
    def feature_name(self):
 
3263
        return 'FTPServer'
 
3264
 
 
3265
FTPServerFeature = _FTPServerFeature()
 
3266
 
 
3267
 
 
3268
class _UnicodeFilename(Feature):
 
3269
    """Does the filesystem support Unicode filenames?"""
 
3270
 
 
3271
    def _probe(self):
 
3272
        try:
 
3273
            os.stat(u'\u03b1')
 
3274
        except UnicodeEncodeError:
 
3275
            return False
 
3276
        except (IOError, OSError):
 
3277
            # The filesystem allows the Unicode filename but the file doesn't
 
3278
            # exist.
 
3279
            return True
 
3280
        else:
 
3281
            # The filesystem allows the Unicode filename and the file exists,
 
3282
            # for some reason.
 
3283
            return True
 
3284
 
 
3285
UnicodeFilename = _UnicodeFilename()
 
3286
 
 
3287
 
 
3288
class _UTF8Filesystem(Feature):
 
3289
    """Is the filesystem UTF-8?"""
 
3290
 
 
3291
    def _probe(self):
 
3292
        if osutils._fs_enc.upper() in ('UTF-8', 'UTF8'):
 
3293
            return True
 
3294
        return False
 
3295
 
 
3296
UTF8Filesystem = _UTF8Filesystem()
 
3297
 
 
3298
 
 
3299
class _CaseInsensitiveFilesystemFeature(Feature):
 
3300
    """Check if underlying filesystem is case-insensitive
 
3301
    (e.g. on Windows, Cygwin, MacOS)
 
3302
    """
 
3303
 
 
3304
    def _probe(self):
 
3305
        if TestCaseWithMemoryTransport.TEST_ROOT is None:
 
3306
            root = osutils.mkdtemp(prefix='testbzr-', suffix='.tmp')
 
3307
            TestCaseWithMemoryTransport.TEST_ROOT = root
 
3308
        else:
 
3309
            root = TestCaseWithMemoryTransport.TEST_ROOT
 
3310
        tdir = osutils.mkdtemp(prefix='case-sensitive-probe-', suffix='',
 
3311
            dir=root)
 
3312
        name_a = osutils.pathjoin(tdir, 'a')
 
3313
        name_A = osutils.pathjoin(tdir, 'A')
 
3314
        os.mkdir(name_a)
 
3315
        result = osutils.isdir(name_A)
 
3316
        _rmtree_temp_dir(tdir)
 
3317
        return result
 
3318
 
 
3319
    def feature_name(self):
 
3320
        return 'case-insensitive filesystem'
 
3321
 
 
3322
CaseInsensitiveFilesystemFeature = _CaseInsensitiveFilesystemFeature()