/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/tests/__init__.py

  • Committer: Robert Collins
  • Date: 2007-04-04 05:19:38 UTC
  • mfrom: (2395 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2401.
  • Revision ID: robertc@robertcollins.net-20070404051938-2lnvpsm2tbo5a6g2
Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
180
180
        self.num_tests = num_tests
181
181
        self.error_count = 0
182
182
        self.failure_count = 0
 
183
        self.known_failure_count = 0
183
184
        self.skip_count = 0
 
185
        self.unsupported = {}
184
186
        self.count = 0
185
187
        self._overall_start_time = time.time()
186
188
    
221
223
        """Record that a test has started."""
222
224
        self._start_time = time.time()
223
225
 
 
226
    def _cleanupLogFile(self, test):
 
227
        # We can only do this if we have one of our TestCases, not if
 
228
        # we have a doctest.
 
229
        setKeepLogfile = getattr(test, 'setKeepLogfile', None)
 
230
        if setKeepLogfile is not None:
 
231
            setKeepLogfile()
 
232
 
224
233
    def addError(self, test, err):
 
234
        self.extractBenchmarkTime(test)
 
235
        self._cleanupLogFile(test)
225
236
        if isinstance(err[1], TestSkipped):
226
 
            return self.addSkipped(test, err)    
 
237
            return self.addSkipped(test, err)
 
238
        elif isinstance(err[1], UnavailableFeature):
 
239
            return self.addNotSupported(test, err[1].args[0])
227
240
        unittest.TestResult.addError(self, test, err)
228
 
        # We can only do this if we have one of our TestCases, not if
229
 
        # we have a doctest.
230
 
        setKeepLogfile = getattr(test, 'setKeepLogfile', None)
231
 
        if setKeepLogfile is not None:
232
 
            setKeepLogfile()
233
 
        self.extractBenchmarkTime(test)
 
241
        self.error_count += 1
234
242
        self.report_error(test, err)
235
243
        if self.stop_early:
236
244
            self.stop()
237
245
 
238
246
    def addFailure(self, test, err):
 
247
        self._cleanupLogFile(test)
 
248
        self.extractBenchmarkTime(test)
 
249
        if isinstance(err[1], KnownFailure):
 
250
            return self.addKnownFailure(test, err)
239
251
        unittest.TestResult.addFailure(self, test, err)
240
 
        # We can only do this if we have one of our TestCases, not if
241
 
        # we have a doctest.
242
 
        setKeepLogfile = getattr(test, 'setKeepLogfile', None)
243
 
        if setKeepLogfile is not None:
244
 
            setKeepLogfile()
245
 
        self.extractBenchmarkTime(test)
 
252
        self.failure_count += 1
246
253
        self.report_failure(test, err)
247
254
        if self.stop_early:
248
255
            self.stop()
249
256
 
 
257
    def addKnownFailure(self, test, err):
 
258
        self.known_failure_count += 1
 
259
        self.report_known_failure(test, err)
 
260
 
 
261
    def addNotSupported(self, test, feature):
 
262
        self.unsupported.setdefault(str(feature), 0)
 
263
        self.unsupported[str(feature)] += 1
 
264
        self.report_unsupported(test, feature)
 
265
 
250
266
    def addSuccess(self, test):
251
267
        self.extractBenchmarkTime(test)
252
268
        if self._bench_history is not None:
258
274
        unittest.TestResult.addSuccess(self, test)
259
275
 
260
276
    def addSkipped(self, test, skip_excinfo):
261
 
        self.extractBenchmarkTime(test)
262
277
        self.report_skip(test, skip_excinfo)
263
278
        # seems best to treat this as success from point-of-view of unittest
264
279
        # -- it actually does nothing so it barely matters :)
301
316
class TextTestResult(ExtendedTestResult):
302
317
    """Displays progress and results of tests in text form"""
303
318
 
304
 
    def __init__(self, *args, **kw):
305
 
        ExtendedTestResult.__init__(self, *args, **kw)
306
 
        self.pb = self.ui.nested_progress_bar()
 
319
    def __init__(self, stream, descriptions, verbosity,
 
320
                 bench_history=None,
 
321
                 num_tests=None,
 
322
                 pb=None,
 
323
                 ):
 
324
        ExtendedTestResult.__init__(self, stream, descriptions, verbosity,
 
325
            bench_history, num_tests)
 
326
        if pb is None:
 
327
            self.pb = self.ui.nested_progress_bar()
 
328
            self._supplied_pb = False
 
329
        else:
 
330
            self.pb = pb
 
331
            self._supplied_pb = True
307
332
        self.pb.show_pct = False
308
333
        self.pb.show_spinner = False
309
 
        self.pb.show_eta = False, 
 
334
        self.pb.show_eta = False,
310
335
        self.pb.show_count = False
311
336
        self.pb.show_bar = False
312
337
 
322
347
            a += ', %d errors' % self.error_count
323
348
        if self.failure_count:
324
349
            a += ', %d failed' % self.failure_count
 
350
        if self.known_failure_count:
 
351
            a += ', %d known failures' % self.known_failure_count
325
352
        if self.skip_count:
326
353
            a += ', %d skipped' % self.skip_count
 
354
        if self.unsupported:
 
355
            a += ', %d missing features' % len(self.unsupported)
327
356
        a += ']'
328
357
        return a
329
358
 
342
371
            return self._shortened_test_description(test)
343
372
 
344
373
    def report_error(self, test, err):
345
 
        self.error_count += 1
346
374
        self.pb.note('ERROR: %s\n    %s\n', 
347
375
            self._test_description(test),
348
376
            err[1],
349
377
            )
350
378
 
351
379
    def report_failure(self, test, err):
352
 
        self.failure_count += 1
353
380
        self.pb.note('FAIL: %s\n    %s\n', 
354
381
            self._test_description(test),
355
382
            err[1],
356
383
            )
357
384
 
 
385
    def report_known_failure(self, test, err):
 
386
        self.pb.note('XFAIL: %s\n%s\n',
 
387
            self._test_description(test), err[1])
 
388
 
358
389
    def report_skip(self, test, skip_excinfo):
359
390
        self.skip_count += 1
360
391
        if False:
371
402
                # progress bar...
372
403
                self.pb.note('SKIP: %s', skip_excinfo[1])
373
404
 
 
405
    def report_unsupported(self, test, feature):
 
406
        """test cannot be run because feature is missing."""
 
407
                  
374
408
    def report_cleaning_up(self):
375
409
        self.pb.update('cleaning up...')
376
410
 
377
411
    def finished(self):
378
 
        self.pb.finished()
 
412
        if not self._supplied_pb:
 
413
            self.pb.finished()
379
414
 
380
415
 
381
416
class VerboseTestResult(ExtendedTestResult):
414
449
        return '%s%s' % (indent, err[1])
415
450
 
416
451
    def report_error(self, test, err):
417
 
        self.error_count += 1
418
452
        self.stream.writeln('ERROR %s\n%s'
419
453
                % (self._testTimeString(),
420
454
                   self._error_summary(err)))
421
455
 
422
456
    def report_failure(self, test, err):
423
 
        self.failure_count += 1
424
457
        self.stream.writeln(' FAIL %s\n%s'
425
458
                % (self._testTimeString(),
426
459
                   self._error_summary(err)))
427
460
 
 
461
    def report_known_failure(self, test, err):
 
462
        self.stream.writeln('XFAIL %s\n%s'
 
463
                % (self._testTimeString(),
 
464
                   self._error_summary(err)))
 
465
 
428
466
    def report_success(self, test):
429
467
        self.stream.writeln('   OK %s' % self._testTimeString())
430
468
        for bench_called, stats in getattr(test, '_benchcalls', []):
431
469
            self.stream.writeln('LSProf output for %s(%s, %s)' % bench_called)
432
470
            stats.pprint(file=self.stream)
 
471
        # flush the stream so that we get smooth output. This verbose mode is
 
472
        # used to show the output in PQM.
433
473
        self.stream.flush()
434
474
 
435
475
    def report_skip(self, test, skip_excinfo):
438
478
                % (self._testTimeString(),
439
479
                   self._error_summary(skip_excinfo)))
440
480
 
 
481
    def report_unsupported(self, test, feature):
 
482
        """test cannot be run because feature is missing."""
 
483
        self.stream.writeln("NODEP %s\n    The feature '%s' is not available."
 
484
                %(self._testTimeString(), feature))
 
485
                  
 
486
 
441
487
 
442
488
class TextTestRunner(object):
443
489
    stop_on_failure = False
486
532
            if errored:
487
533
                if failed: self.stream.write(", ")
488
534
                self.stream.write("errors=%d" % errored)
 
535
            if result.known_failure_count:
 
536
                if failed or errored: self.stream.write(", ")
 
537
                self.stream.write("known_failure_count=%d" %
 
538
                    result.known_failure_count)
489
539
            self.stream.writeln(")")
490
540
        else:
491
 
            self.stream.writeln("OK")
 
541
            if result.known_failure_count:
 
542
                self.stream.writeln("OK (known_failures=%d)" %
 
543
                    result.known_failure_count)
 
544
            else:
 
545
                self.stream.writeln("OK")
492
546
        if result.skip_count > 0:
493
547
            skipped = result.skip_count
494
548
            self.stream.writeln('%d test%s skipped' %
495
549
                                (skipped, skipped != 1 and "s" or ""))
 
550
        if result.unsupported:
 
551
            for feature, count in sorted(result.unsupported.items()):
 
552
                self.stream.writeln("Missing feature '%s' skipped %d tests." %
 
553
                    (feature, count))
496
554
        result.report_cleaning_up()
497
555
        # This is still a little bogus, 
498
556
        # but only a little. Folk not using our testrunner will
545
603
    """Indicates that a test was intentionally skipped, rather than failing."""
546
604
 
547
605
 
 
606
class KnownFailure(AssertionError):
 
607
    """Indicates that a test failed in a precisely expected manner.
 
608
 
 
609
    Such failures dont block the whole test suite from passing because they are
 
610
    indicators of partially completed code or of future work. We have an
 
611
    explicit error for them so that we can ensure that they are always visible:
 
612
    KnownFailures are always shown in the output of bzr selftest.
 
613
    """
 
614
 
 
615
 
 
616
class UnavailableFeature(Exception):
 
617
    """A feature required for this test was not available.
 
618
 
 
619
    The feature should be used to construct the exception.
 
620
    """
 
621
 
 
622
 
548
623
class CommandFailed(Exception):
549
624
    pass
550
625
 
796
871
                excName = str(excClass)
797
872
            raise self.failureException, "%s not raised" % excName
798
873
 
 
874
    def assertRaises(self, excClass, func, *args, **kwargs):
 
875
        """Assert that a callable raises a particular exception.
 
876
 
 
877
        :param excClass: As for the except statement, this may be either an
 
878
        exception class, or a tuple of classes.
 
879
 
 
880
        Returns the exception so that you can examine it.
 
881
        """
 
882
        try:
 
883
            func(*args, **kwargs)
 
884
        except excClass, e:
 
885
            return e
 
886
        else:
 
887
            if getattr(excClass,'__name__', None) is not None:
 
888
                excName = excClass.__name__
 
889
            else:
 
890
                # probably a tuple
 
891
                excName = str(excClass)
 
892
            raise self.failureException, "%s not raised" % excName
 
893
 
799
894
    def assertIs(self, left, right, message=None):
800
895
        if not (left is right):
801
896
            if message is not None:
828
923
            self.fail("%r is an instance of %s rather than %s" % (
829
924
                obj, obj.__class__, kls))
830
925
 
 
926
    def expectFailure(self, reason, assertion, *args, **kwargs):
 
927
        """Invoke a test, expecting it to fail for the given reason.
 
928
 
 
929
        This is for assertions that ought to succeed, but currently fail.
 
930
        (The failure is *expected* but not *wanted*.)  Please be very precise
 
931
        about the failure you're expecting.  If a new bug is introduced,
 
932
        AssertionError should be raised, not KnownFailure.
 
933
 
 
934
        Frequently, expectFailure should be followed by an opposite assertion.
 
935
        See example below.
 
936
 
 
937
        Intended to be used with a callable that raises AssertionError as the
 
938
        'assertion' parameter.  args and kwargs are passed to the 'assertion'.
 
939
 
 
940
        Raises KnownFailure if the test fails.  Raises AssertionError if the
 
941
        test succeeds.
 
942
 
 
943
        example usage::
 
944
 
 
945
          self.expectFailure('Math is broken', self.assertNotEqual, 54,
 
946
                             dynamic_val)
 
947
          self.assertEqual(42, dynamic_val)
 
948
 
 
949
          This means that a dynamic_val of 54 will cause the test to raise
 
950
          a KnownFailure.  Once math is fixed and the expectFailure is removed,
 
951
          only a dynamic_val of 42 will allow the test to pass.  Anything other
 
952
          than 54 or 42 will cause an AssertionError.
 
953
        """
 
954
        try:
 
955
            assertion(*args, **kwargs)
 
956
        except AssertionError:
 
957
            raise KnownFailure(reason)
 
958
        else:
 
959
            self.fail('Unexpected success.  Should have failed: %s' % reason)
 
960
 
831
961
    def _capture_warnings(self, a_callable, *args, **kwargs):
832
962
        """A helper for callDeprecated and applyDeprecated.
833
963
 
974
1104
        for klass, hooks in self._preserved_hooks.items():
975
1105
            setattr(klass, 'hooks', hooks)
976
1106
 
 
1107
    def knownFailure(self, reason):
 
1108
        """This test has failed for some known reason."""
 
1109
        raise KnownFailure(reason)
 
1110
 
 
1111
    def run(self, result=None):
 
1112
        if result is None: result = self.defaultTestResult()
 
1113
        for feature in getattr(self, '_test_needs_features', []):
 
1114
            if not feature.available():
 
1115
                result.startTest(self)
 
1116
                if getattr(result, 'addNotSupported', None):
 
1117
                    result.addNotSupported(self, feature)
 
1118
                else:
 
1119
                    result.addSuccess(self)
 
1120
                result.stopTest(self)
 
1121
                return
 
1122
        return unittest.TestCase.run(self, result)
 
1123
 
977
1124
    def tearDown(self):
978
1125
        self._runCleanups()
979
1126
        unittest.TestCase.tearDown(self)
1049
1196
        """Shortcut that splits cmd into words, runs, and returns stdout"""
1050
1197
        return self.run_bzr_captured(cmd.split(), retcode=retcode)[0]
1051
1198
 
 
1199
    def requireFeature(self, feature):
 
1200
        """This test requires a specific feature is available.
 
1201
 
 
1202
        :raises UnavailableFeature: When feature is not available.
 
1203
        """
 
1204
        if not feature.available():
 
1205
            raise UnavailableFeature(feature)
 
1206
 
1052
1207
    def run_bzr_captured(self, argv, retcode=0, encoding=None, stdin=None,
1053
1208
                         working_dir=None):
1054
1209
        """Invoke bzr and return (stdout, stderr).
1387
1542
                    this_tree=wt_to)
1388
1543
        wt_to.add_parent_tree_id(branch_from.last_revision())
1389
1544
 
 
1545
    def reduceLockdirTimeout(self):
 
1546
        """Reduce the default lock timeout for the duration of the test, so that
 
1547
        if LockContention occurs during a test, it does so quickly.
 
1548
 
 
1549
        Tests that expect to provoke LockContention errors should call this.
 
1550
        """
 
1551
        orig_timeout = bzrlib.lockdir._DEFAULT_TIMEOUT_SECONDS
 
1552
        def resetTimeout():
 
1553
            bzrlib.lockdir._DEFAULT_TIMEOUT_SECONDS = orig_timeout
 
1554
        self.addCleanup(resetTimeout)
 
1555
        bzrlib.lockdir._DEFAULT_TIMEOUT_SECONDS = 0
1390
1556
 
1391
1557
BzrTestBase = TestCase
1392
1558
 
1416
1582
        # execution. Variables that the parameteriser sets need to be 
1417
1583
        # ones that are not set by setUp, or setUp will trash them.
1418
1584
        super(TestCaseWithMemoryTransport, self).__init__(methodName)
1419
 
        self.transport_server = default_transport
 
1585
        self.vfs_transport_factory = default_transport
 
1586
        self.transport_server = None
1420
1587
        self.transport_readonly_server = None
 
1588
        self.__vfs_server = None
1421
1589
 
1422
1590
    def get_transport(self):
1423
1591
        """Return a writeable transport for the test scratch space"""
1451
1619
            if self.transport_readonly_server is None:
1452
1620
                # readonly decorator requested
1453
1621
                # bring up the server
1454
 
                self.get_url()
1455
1622
                self.__readonly_server = ReadonlyServer()
1456
 
                self.__readonly_server.setUp(self.__server)
 
1623
                self.__readonly_server.setUp(self.get_vfs_only_server())
1457
1624
            else:
1458
1625
                self.__readonly_server = self.create_transport_readonly_server()
1459
 
                self.__readonly_server.setUp()
 
1626
                self.__readonly_server.setUp(self.get_vfs_only_server())
1460
1627
            self.addCleanup(self.__readonly_server.tearDown)
1461
1628
        return self.__readonly_server
1462
1629
 
1475
1642
            base = base + relpath
1476
1643
        return base
1477
1644
 
1478
 
    def get_server(self):
1479
 
        """Get the read/write server instance.
 
1645
    def get_vfs_only_server(self):
 
1646
        """Get the vfs only read/write server instance.
1480
1647
 
1481
1648
        This is useful for some tests with specific servers that need
1482
1649
        diagnostics.
1484
1651
        For TestCaseWithMemoryTransport this is always a MemoryServer, and there
1485
1652
        is no means to override it.
1486
1653
        """
 
1654
        if self.__vfs_server is None:
 
1655
            self.__vfs_server = MemoryServer()
 
1656
            self.__vfs_server.setUp()
 
1657
            self.addCleanup(self.__vfs_server.tearDown)
 
1658
        return self.__vfs_server
 
1659
 
 
1660
    def get_server(self):
 
1661
        """Get the read/write server instance.
 
1662
 
 
1663
        This is useful for some tests with specific servers that need
 
1664
        diagnostics.
 
1665
 
 
1666
        This is built from the self.transport_server factory. If that is None,
 
1667
        then the self.get_vfs_server is returned.
 
1668
        """
1487
1669
        if self.__server is None:
1488
 
            self.__server = MemoryServer()
1489
 
            self.__server.setUp()
 
1670
            if self.transport_server is None or self.transport_server is self.vfs_transport_factory:
 
1671
                return self.get_vfs_only_server()
 
1672
            else:
 
1673
                # bring up a decorated means of access to the vfs only server.
 
1674
                self.__server = self.transport_server()
 
1675
                try:
 
1676
                    self.__server.setUp(self.get_vfs_only_server())
 
1677
                except TypeError, e:
 
1678
                    # This should never happen; the try:Except here is to assist
 
1679
                    # developers having to update code rather than seeing an
 
1680
                    # uninformative TypeError.
 
1681
                    raise Exception, "Old server API in use: %s, %s" % (self.__server, e)
1490
1682
            self.addCleanup(self.__server.tearDown)
1491
1683
        return self.__server
1492
1684
 
1493
 
    def get_url(self, relpath=None):
 
1685
    def _adjust_url(self, base, relpath):
1494
1686
        """Get a URL (or maybe a path) for the readwrite transport.
1495
1687
 
1496
1688
        This will either be backed by '.' or to an equivalent non-file based
1498
1690
        relpath provides for clients to get a path relative to the base url.
1499
1691
        These should only be downwards relative, not upwards.
1500
1692
        """
1501
 
        base = self.get_server().get_url()
1502
1693
        if relpath is not None and relpath != '.':
1503
1694
            if not base.endswith('/'):
1504
1695
                base = base + '/'
1512
1703
                base += urlutils.escape(relpath)
1513
1704
        return base
1514
1705
 
 
1706
    def get_url(self, relpath=None):
 
1707
        """Get a URL (or maybe a path) for the readwrite transport.
 
1708
 
 
1709
        This will either be backed by '.' or to an equivalent non-file based
 
1710
        facility.
 
1711
        relpath provides for clients to get a path relative to the base url.
 
1712
        These should only be downwards relative, not upwards.
 
1713
        """
 
1714
        base = self.get_server().get_url()
 
1715
        return self._adjust_url(base, relpath)
 
1716
 
 
1717
    def get_vfs_only_url(self, relpath=None):
 
1718
        """Get a URL (or maybe a path for the plain old vfs transport.
 
1719
 
 
1720
        This will never be a smart protocol.
 
1721
        :param relpath: provides for clients to get a path relative to the base
 
1722
            url.  These should only be downwards relative, not upwards.
 
1723
        """
 
1724
        base = self.get_vfs_only_server().get_url()
 
1725
        return self._adjust_url(base, relpath)
 
1726
 
1515
1727
    def _make_test_root(self):
1516
1728
        if TestCaseWithMemoryTransport.TEST_ROOT is not None:
1517
1729
            return
1595
1807
        self.overrideEnvironmentForTesting()
1596
1808
        self.__readonly_server = None
1597
1809
        self.__server = None
 
1810
        self.reduceLockdirTimeout()
1598
1811
 
1599
1812
     
1600
1813
class TestCaseInTempDir(TestCaseWithMemoryTransport):
1739
1952
    readwrite one must both define get_url() as resolving to os.getcwd().
1740
1953
    """
1741
1954
 
1742
 
    def create_transport_server(self):
1743
 
        """Create a transport server from class defined at init.
1744
 
 
1745
 
        This is mostly a hook for daughter classes.
1746
 
        """
1747
 
        return self.transport_server()
1748
 
 
1749
 
    def get_server(self):
 
1955
    def get_vfs_only_server(self):
1750
1956
        """See TestCaseWithMemoryTransport.
1751
1957
 
1752
1958
        This is useful for some tests with specific servers that need
1753
1959
        diagnostics.
1754
1960
        """
1755
 
        if self.__server is None:
1756
 
            self.__server = self.create_transport_server()
1757
 
            self.__server.setUp()
1758
 
            self.addCleanup(self.__server.tearDown)
1759
 
        return self.__server
 
1961
        if self.__vfs_server is None:
 
1962
            self.__vfs_server = self.vfs_transport_factory()
 
1963
            self.__vfs_server.setUp()
 
1964
            self.addCleanup(self.__vfs_server.tearDown)
 
1965
        return self.__vfs_server
1760
1966
 
1761
1967
    def make_branch_and_tree(self, relpath, format=None):
1762
1968
        """Create a branch on the transport and a tree locally.
1763
1969
 
1764
1970
        If the transport is not a LocalTransport, the Tree can't be created on
1765
 
        the transport.  In that case the working tree is created in the local
1766
 
        directory, and the returned tree's branch and repository will also be
1767
 
        accessed locally.
1768
 
 
1769
 
        This will fail if the original default transport for this test
1770
 
        case wasn't backed by the working directory, as the branch won't
1771
 
        be on disk for us to open it.  
 
1971
        the transport.  In that case if the vfs_transport_factory is
 
1972
        LocalURLServer the working tree is created in the local
 
1973
        directory backing the transport, and the returned tree's branch and
 
1974
        repository will also be accessed locally. Otherwise a lightweight
 
1975
        checkout is created and returned.
1772
1976
 
1773
1977
        :param format: The BzrDirFormat.
1774
1978
        :returns: the WorkingTree.
1782
1986
            return b.bzrdir.create_workingtree()
1783
1987
        except errors.NotLocalUrl:
1784
1988
            # We can only make working trees locally at the moment.  If the
1785
 
            # transport can't support them, then reopen the branch on a local
1786
 
            # transport, and create the working tree there.  
1787
 
            #
1788
 
            # Possibly we should instead keep
1789
 
            # the non-disk-backed branch and create a local checkout?
1790
 
            bd = bzrdir.BzrDir.open(relpath)
1791
 
            return bd.create_workingtree()
 
1989
            # transport can't support them, then we keep the non-disk-backed
 
1990
            # branch and create a local checkout.
 
1991
            if self.vfs_transport_factory is LocalURLServer:
 
1992
                # the branch is colocated on disk, we cannot create a checkout.
 
1993
                # hopefully callers will expect this.
 
1994
                local_controldir= bzrdir.BzrDir.open(self.get_vfs_only_url(relpath))
 
1995
                return local_controldir.create_workingtree()
 
1996
            else:
 
1997
                return b.create_checkout(relpath, lightweight=True)
1792
1998
 
1793
1999
    def assertIsDirectory(self, relpath, transport):
1794
2000
        """Assert that relpath within transport is a directory.
1816
2022
 
1817
2023
    def setUp(self):
1818
2024
        super(TestCaseWithTransport, self).setUp()
1819
 
        self.__server = None
 
2025
        self.__vfs_server = None
1820
2026
 
1821
2027
 
1822
2028
class ChrootedTestCase(TestCaseWithTransport):
1833
2039
 
1834
2040
    def setUp(self):
1835
2041
        super(ChrootedTestCase, self).setUp()
1836
 
        if not self.transport_server == MemoryServer:
 
2042
        if not self.vfs_transport_factory == MemoryServer:
1837
2043
            self.transport_readonly_server = HttpServer
1838
2044
 
1839
2045
 
2006
2212
                   'bzrlib.tests.test_ssh_transport',
2007
2213
                   'bzrlib.tests.test_status',
2008
2214
                   'bzrlib.tests.test_store',
 
2215
                   'bzrlib.tests.test_strace',
2009
2216
                   'bzrlib.tests.test_subsume',
2010
2217
                   'bzrlib.tests.test_symbol_versioning',
2011
2218
                   'bzrlib.tests.test_tag',
2097
2304
            if not quiet:
2098
2305
                print 'delete directory:', i
2099
2306
            shutil.rmtree(i)
 
2307
 
 
2308
 
 
2309
class Feature(object):
 
2310
    """An operating system Feature."""
 
2311
 
 
2312
    def __init__(self):
 
2313
        self._available = None
 
2314
 
 
2315
    def available(self):
 
2316
        """Is the feature available?
 
2317
 
 
2318
        :return: True if the feature is available.
 
2319
        """
 
2320
        if self._available is None:
 
2321
            self._available = self._probe()
 
2322
        return self._available
 
2323
 
 
2324
    def _probe(self):
 
2325
        """Implement this method in concrete features.
 
2326
 
 
2327
        :return: True if the feature is available.
 
2328
        """
 
2329
        raise NotImplementedError
 
2330
 
 
2331
    def __str__(self):
 
2332
        if getattr(self, 'feature_name', None):
 
2333
            return self.feature_name()
 
2334
        return self.__class__.__name__