~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/__init__.py

  • Committer: Patch Queue Manager
  • Date: 2011-10-09 13:52:06 UTC
  • mfrom: (6202.1.3 revno-revision)
  • Revision ID: pqm@pqm.ubuntu.com-20111009135206-t3utsln6mtzv9eut
(jelmer) Add a --revision argument to 'bzr revno'. (Jelmer Vernooij)

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
"""Testing framework extensions"""
18
18
 
19
 
# TODO: Perhaps there should be an API to find out if bzr running under the
20
 
# test suite -- some plugins might want to avoid making intrusive changes if
21
 
# this is the case.  However, we want behaviour under to test to diverge as
22
 
# little as possible, so this should be used rarely if it's added at all.
23
 
# (Suggestion from j-a-meinel, 2005-11-24)
24
 
 
25
19
# NOTE: Some classes in here use camelCaseNaming() rather than
26
20
# underscore_naming().  That's for consistency with unittest; it's not the
27
21
# general style of bzrlib.  Please continue that consistency when adding e.g.
67
61
    chk_map,
68
62
    commands as _mod_commands,
69
63
    config,
 
64
    i18n,
70
65
    debug,
71
66
    errors,
72
67
    hooks,
94
89
    memory,
95
90
    pathfilter,
96
91
    )
 
92
from bzrlib.symbol_versioning import (
 
93
    deprecated_function,
 
94
    deprecated_in,
 
95
    )
97
96
from bzrlib.tests import (
98
97
    test_server,
99
98
    TestUtil,
984
983
        for feature in getattr(self, '_test_needs_features', []):
985
984
            self.requireFeature(feature)
986
985
        self._cleanEnvironment()
 
986
        self.overrideAttr(bzrlib.global_state, 'cmdline_overrides',
 
987
                          config.CommandLineSection())
987
988
        self._silenceUI()
988
989
        self._startLogFile()
989
990
        self._benchcalls = []
1007
1008
        self._counters = {}
1008
1009
        if 'config_stats' in selftest_debug_flags:
1009
1010
            self._install_config_stats_hooks()
 
1011
        # Do not use i18n for tests (unless the test reverses this)
 
1012
        i18n.disable_i18n()
1010
1013
 
1011
1014
    def debug(self):
1012
1015
        # debug a frame up.
1013
1016
        import pdb
1014
 
        pdb.Pdb().set_trace(sys._getframe().f_back)
 
1017
        # The sys preserved stdin/stdout should allow blackbox tests debugging
 
1018
        pdb.Pdb(stdin=sys.__stdin__, stdout=sys.__stdout__
 
1019
                ).set_trace(sys._getframe().f_back)
1015
1020
 
1016
1021
    def discardDetail(self, name):
1017
1022
        """Extend the addDetail, getDetails api so we can remove a detail.
1172
1177
 
1173
1178
    def permit_dir(self, name):
1174
1179
        """Permit a directory to be used by this test. See permit_url."""
1175
 
        name_transport = _mod_transport.get_transport(name)
 
1180
        name_transport = _mod_transport.get_transport_from_path(name)
1176
1181
        self.permit_url(name)
1177
1182
        self.permit_url(name_transport.base)
1178
1183
 
1257
1262
        self.addCleanup(transport_server.stop_server)
1258
1263
        # Obtain a real transport because if the server supplies a password, it
1259
1264
        # will be hidden from the base on the client side.
1260
 
        t = _mod_transport.get_transport(transport_server.get_url())
 
1265
        t = _mod_transport.get_transport_from_url(transport_server.get_url())
1261
1266
        # Some transport servers effectively chroot the backing transport;
1262
1267
        # others like SFTPServer don't - users of the transport can walk up the
1263
1268
        # transport to read the entire backing transport. This wouldn't matter
1804
1809
        self._preserved_lazy_hooks.clear()
1805
1810
 
1806
1811
    def knownFailure(self, reason):
1807
 
        """This test has failed for some known reason."""
1808
 
        raise KnownFailure(reason)
 
1812
        """Declare that this test fails for a known reason
 
1813
 
 
1814
        Tests that are known to fail should generally be using expectedFailure
 
1815
        with an appropriate reverse assertion if a change could cause the test
 
1816
        to start passing. Conversely if the test has no immediate prospect of
 
1817
        succeeding then using skip is more suitable.
 
1818
 
 
1819
        When this method is called while an exception is being handled, that
 
1820
        traceback will be used, otherwise a new exception will be thrown to
 
1821
        provide one but won't be reported.
 
1822
        """
 
1823
        self._add_reason(reason)
 
1824
        try:
 
1825
            exc_info = sys.exc_info()
 
1826
            if exc_info != (None, None, None):
 
1827
                self._report_traceback(exc_info)
 
1828
            else:
 
1829
                try:
 
1830
                    raise self.failureException(reason)
 
1831
                except self.failureException:
 
1832
                    exc_info = sys.exc_info()
 
1833
            # GZ 02-08-2011: Maybe cleanup this err.exc_info attribute too?
 
1834
            raise testtools.testcase._ExpectedFailure(exc_info)
 
1835
        finally:
 
1836
            del exc_info
1809
1837
 
1810
1838
    def _suppress_log(self):
1811
1839
        """Remove the log info from details."""
2398
2426
 
2399
2427
        :param relpath: a path relative to the base url.
2400
2428
        """
2401
 
        t = _mod_transport.get_transport(self.get_url(relpath))
 
2429
        t = _mod_transport.get_transport_from_url(self.get_url(relpath))
2402
2430
        self.assertFalse(t.is_readonly())
2403
2431
        return t
2404
2432
 
2410
2438
 
2411
2439
        :param relpath: a path relative to the base url.
2412
2440
        """
2413
 
        t = _mod_transport.get_transport(self.get_readonly_url(relpath))
 
2441
        t = _mod_transport.get_transport_from_url(
 
2442
            self.get_readonly_url(relpath))
2414
2443
        self.assertTrue(t.is_readonly())
2415
2444
        return t
2416
2445
 
2559
2588
        propagating. This method ensures than a test did not leaked.
2560
2589
        """
2561
2590
        root = TestCaseWithMemoryTransport.TEST_ROOT
2562
 
        t = _mod_transport.get_transport(root)
 
2591
        t = _mod_transport.get_transport_from_path(root)
2563
2592
        self.permit_url(t.base)
2564
2593
        if (t.get_bytes('.bzr/checkout/dirstate') != 
2565
2594
                TestCaseWithMemoryTransport._SAFETY_NET_PRISTINE_DIRSTATE):
2603
2632
    def make_branch(self, relpath, format=None):
2604
2633
        """Create a branch on the transport at relpath."""
2605
2634
        repo = self.make_repository(relpath, format=format)
2606
 
        return repo.bzrdir.create_branch()
 
2635
        return repo.bzrdir.create_branch(append_revisions_only=False)
 
2636
 
 
2637
    def get_default_format(self):
 
2638
        return 'default'
 
2639
 
 
2640
    def resolve_format(self, format):
 
2641
        """Resolve an object to a ControlDir format object.
 
2642
 
 
2643
        The initial format object can either already be
 
2644
        a ControlDirFormat, None (for the default format),
 
2645
        or a string with the name of the control dir format.
 
2646
 
 
2647
        :param format: Object to resolve
 
2648
        :return A ControlDirFormat instance
 
2649
        """
 
2650
        if format is None:
 
2651
            format = self.get_default_format()
 
2652
        if isinstance(format, basestring):
 
2653
            format = bzrdir.format_registry.make_bzrdir(format)
 
2654
        return format
2607
2655
 
2608
2656
    def make_bzrdir(self, relpath, format=None):
2609
2657
        try:
2613
2661
            t = _mod_transport.get_transport(maybe_a_url)
2614
2662
            if len(segments) > 1 and segments[-1] not in ('', '.'):
2615
2663
                t.ensure_base()
2616
 
            if format is None:
2617
 
                format = 'default'
2618
 
            if isinstance(format, basestring):
2619
 
                format = bzrdir.format_registry.make_bzrdir(format)
 
2664
            format = self.resolve_format(format)
2620
2665
            return format.initialize_on_transport(t)
2621
2666
        except errors.UninitializableFormat:
2622
2667
            raise TestSkipped("Format %s is not initializable." % format)
2623
2668
 
2624
 
    def make_repository(self, relpath, shared=False, format=None):
 
2669
    def make_repository(self, relpath, shared=None, format=None):
2625
2670
        """Create a repository on our default transport at relpath.
2626
2671
 
2627
2672
        Note that relpath must be a relative path, not a full url.
2638
2683
            backing_server = self.get_server()
2639
2684
        smart_server = test_server.SmartTCPServer_for_testing()
2640
2685
        self.start_server(smart_server, backing_server)
2641
 
        remote_transport = _mod_transport.get_transport(smart_server.get_url()
 
2686
        remote_transport = _mod_transport.get_transport_from_url(smart_server.get_url()
2642
2687
                                                   ).clone(path)
2643
2688
        return remote_transport
2644
2689
 
2661
2706
    def setUp(self):
2662
2707
        super(TestCaseWithMemoryTransport, self).setUp()
2663
2708
        # Ensure that ConnectedTransport doesn't leak sockets
2664
 
        def get_transport_with_cleanup(*args, **kwargs):
2665
 
            t = orig_get_transport(*args, **kwargs)
 
2709
        def get_transport_from_url_with_cleanup(*args, **kwargs):
 
2710
            t = orig_get_transport_from_url(*args, **kwargs)
2666
2711
            if isinstance(t, _mod_transport.ConnectedTransport):
2667
2712
                self.addCleanup(t.disconnect)
2668
2713
            return t
2669
2714
 
2670
 
        orig_get_transport = self.overrideAttr(_mod_transport, 'get_transport',
2671
 
                                               get_transport_with_cleanup)
 
2715
        orig_get_transport_from_url = self.overrideAttr(
 
2716
            _mod_transport, 'get_transport_from_url',
 
2717
            get_transport_from_url_with_cleanup)
2672
2718
        self._make_test_root()
2673
2719
        self.addCleanup(os.chdir, os.getcwdu())
2674
2720
        self.makeAndChdirToTestDir()
2809
2855
                "a list or a tuple. Got %r instead" % (shape,))
2810
2856
        # It's OK to just create them using forward slashes on windows.
2811
2857
        if transport is None or transport.is_readonly():
2812
 
            transport = _mod_transport.get_transport(".")
 
2858
            transport = _mod_transport.get_transport_from_path(".")
2813
2859
        for name in shape:
2814
2860
            self.assertIsInstance(name, basestring)
2815
2861
            if name[-1] == '/':
2900
2946
        # this obviously requires a format that supports branch references
2901
2947
        # so check for that by checking bzrdir.BzrDirFormat.get_default_format()
2902
2948
        # RBC 20060208
 
2949
        format = self.resolve_format(format=format)
2903
2950
        b = self.make_branch(relpath, format=format)
2904
2951
        try:
2905
2952
            return b.bzrdir.create_workingtree()
3930
3977
        'bzrlib.tests.test_email_message',
3931
3978
        'bzrlib.tests.test_eol_filters',
3932
3979
        'bzrlib.tests.test_errors',
 
3980
        'bzrlib.tests.test_estimate_compressed_size',
3933
3981
        'bzrlib.tests.test_export',
3934
3982
        'bzrlib.tests.test_export_pot',
3935
3983
        'bzrlib.tests.test_extract',
 
3984
        'bzrlib.tests.test_features',
3936
3985
        'bzrlib.tests.test_fetch',
3937
3986
        'bzrlib.tests.test_fixtures',
3938
3987
        'bzrlib.tests.test_fifo_cache',
3939
3988
        'bzrlib.tests.test_filters',
 
3989
        'bzrlib.tests.test_filter_tree',
3940
3990
        'bzrlib.tests.test_ftp_transport',
3941
3991
        'bzrlib.tests.test_foreign',
3942
3992
        'bzrlib.tests.test_generate_docs',
4017
4067
        'bzrlib.tests.test_smart',
4018
4068
        'bzrlib.tests.test_smart_add',
4019
4069
        'bzrlib.tests.test_smart_request',
 
4070
        'bzrlib.tests.test_smart_signals',
4020
4071
        'bzrlib.tests.test_smart_transport',
4021
4072
        'bzrlib.tests.test_smtp_connection',
4022
4073
        'bzrlib.tests.test_source',
4333
4384
        the module is available.
4334
4385
    """
4335
4386
 
 
4387
    from bzrlib.tests.features import ModuleAvailableFeature
4336
4388
    py_module = pyutils.get_named_object(py_module_name)
4337
4389
    scenarios = [
4338
4390
        ('python', {'module': py_module}),
4379
4431
                         % (os.path.basename(dirname), printable_e))
4380
4432
 
4381
4433
 
4382
 
class Feature(object):
4383
 
    """An operating system Feature."""
4384
 
 
4385
 
    def __init__(self):
4386
 
        self._available = None
4387
 
 
4388
 
    def available(self):
4389
 
        """Is the feature available?
4390
 
 
4391
 
        :return: True if the feature is available.
4392
 
        """
4393
 
        if self._available is None:
4394
 
            self._available = self._probe()
4395
 
        return self._available
4396
 
 
4397
 
    def _probe(self):
4398
 
        """Implement this method in concrete features.
4399
 
 
4400
 
        :return: True if the feature is available.
4401
 
        """
4402
 
        raise NotImplementedError
4403
 
 
4404
 
    def __str__(self):
4405
 
        if getattr(self, 'feature_name', None):
4406
 
            return self.feature_name()
4407
 
        return self.__class__.__name__
4408
 
 
4409
 
 
4410
 
class _SymlinkFeature(Feature):
4411
 
 
4412
 
    def _probe(self):
4413
 
        return osutils.has_symlinks()
4414
 
 
4415
 
    def feature_name(self):
4416
 
        return 'symlinks'
4417
 
 
4418
 
SymlinkFeature = _SymlinkFeature()
4419
 
 
4420
 
 
4421
 
class _HardlinkFeature(Feature):
4422
 
 
4423
 
    def _probe(self):
4424
 
        return osutils.has_hardlinks()
4425
 
 
4426
 
    def feature_name(self):
4427
 
        return 'hardlinks'
4428
 
 
4429
 
HardlinkFeature = _HardlinkFeature()
4430
 
 
4431
 
 
4432
 
class _OsFifoFeature(Feature):
4433
 
 
4434
 
    def _probe(self):
4435
 
        return getattr(os, 'mkfifo', None)
4436
 
 
4437
 
    def feature_name(self):
4438
 
        return 'filesystem fifos'
4439
 
 
4440
 
OsFifoFeature = _OsFifoFeature()
4441
 
 
4442
 
 
4443
 
class _UnicodeFilenameFeature(Feature):
4444
 
    """Does the filesystem support Unicode filenames?"""
4445
 
 
4446
 
    def _probe(self):
4447
 
        try:
4448
 
            # Check for character combinations unlikely to be covered by any
4449
 
            # single non-unicode encoding. We use the characters
4450
 
            # - greek small letter alpha (U+03B1) and
4451
 
            # - braille pattern dots-123456 (U+283F).
4452
 
            os.stat(u'\u03b1\u283f')
4453
 
        except UnicodeEncodeError:
4454
 
            return False
4455
 
        except (IOError, OSError):
4456
 
            # The filesystem allows the Unicode filename but the file doesn't
4457
 
            # exist.
4458
 
            return True
4459
 
        else:
4460
 
            # The filesystem allows the Unicode filename and the file exists,
4461
 
            # for some reason.
4462
 
            return True
4463
 
 
4464
 
UnicodeFilenameFeature = _UnicodeFilenameFeature()
4465
 
 
4466
 
 
4467
 
class _CompatabilityThunkFeature(Feature):
4468
 
    """This feature is just a thunk to another feature.
4469
 
 
4470
 
    It issues a deprecation warning if it is accessed, to let you know that you
4471
 
    should really use a different feature.
4472
 
    """
4473
 
 
4474
 
    def __init__(self, dep_version, module, name,
4475
 
                 replacement_name, replacement_module=None):
4476
 
        super(_CompatabilityThunkFeature, self).__init__()
4477
 
        self._module = module
4478
 
        if replacement_module is None:
4479
 
            replacement_module = module
4480
 
        self._replacement_module = replacement_module
4481
 
        self._name = name
4482
 
        self._replacement_name = replacement_name
4483
 
        self._dep_version = dep_version
4484
 
        self._feature = None
4485
 
 
4486
 
    def _ensure(self):
4487
 
        if self._feature is None:
4488
 
            depr_msg = self._dep_version % ('%s.%s'
4489
 
                                            % (self._module, self._name))
4490
 
            use_msg = ' Use %s.%s instead.' % (self._replacement_module,
4491
 
                                               self._replacement_name)
4492
 
            symbol_versioning.warn(depr_msg + use_msg, DeprecationWarning)
4493
 
            # Import the new feature and use it as a replacement for the
4494
 
            # deprecated one.
4495
 
            self._feature = pyutils.get_named_object(
4496
 
                self._replacement_module, self._replacement_name)
4497
 
 
4498
 
    def _probe(self):
4499
 
        self._ensure()
4500
 
        return self._feature._probe()
4501
 
 
4502
 
 
4503
 
class ModuleAvailableFeature(Feature):
4504
 
    """This is a feature than describes a module we want to be available.
4505
 
 
4506
 
    Declare the name of the module in __init__(), and then after probing, the
4507
 
    module will be available as 'self.module'.
4508
 
 
4509
 
    :ivar module: The module if it is available, else None.
4510
 
    """
4511
 
 
4512
 
    def __init__(self, module_name):
4513
 
        super(ModuleAvailableFeature, self).__init__()
4514
 
        self.module_name = module_name
4515
 
 
4516
 
    def _probe(self):
4517
 
        try:
4518
 
            self._module = __import__(self.module_name, {}, {}, [''])
4519
 
            return True
4520
 
        except ImportError:
4521
 
            return False
4522
 
 
4523
 
    @property
4524
 
    def module(self):
4525
 
        if self.available(): # Make sure the probe has been done
4526
 
            return self._module
4527
 
        return None
4528
 
 
4529
 
    def feature_name(self):
4530
 
        return self.module_name
4531
 
 
4532
 
 
4533
4434
def probe_unicode_in_user_encoding():
4534
4435
    """Try to encode several unicode strings to use in unicode-aware tests.
4535
4436
    Return first successfull match.
4563
4464
    return None
4564
4465
 
4565
4466
 
4566
 
class _HTTPSServerFeature(Feature):
4567
 
    """Some tests want an https Server, check if one is available.
4568
 
 
4569
 
    Right now, the only way this is available is under python2.6 which provides
4570
 
    an ssl module.
4571
 
    """
4572
 
 
4573
 
    def _probe(self):
4574
 
        try:
4575
 
            import ssl
4576
 
            return True
4577
 
        except ImportError:
4578
 
            return False
4579
 
 
4580
 
    def feature_name(self):
4581
 
        return 'HTTPSServer'
4582
 
 
4583
 
 
4584
 
HTTPSServerFeature = _HTTPSServerFeature()
4585
 
 
4586
 
 
4587
 
class _UnicodeFilename(Feature):
4588
 
    """Does the filesystem support Unicode filenames?"""
4589
 
 
4590
 
    def _probe(self):
4591
 
        try:
4592
 
            os.stat(u'\u03b1')
4593
 
        except UnicodeEncodeError:
4594
 
            return False
4595
 
        except (IOError, OSError):
4596
 
            # The filesystem allows the Unicode filename but the file doesn't
4597
 
            # exist.
4598
 
            return True
4599
 
        else:
4600
 
            # The filesystem allows the Unicode filename and the file exists,
4601
 
            # for some reason.
4602
 
            return True
4603
 
 
4604
 
UnicodeFilename = _UnicodeFilename()
4605
 
 
4606
 
 
4607
 
class _ByteStringNamedFilesystem(Feature):
4608
 
    """Is the filesystem based on bytes?"""
4609
 
 
4610
 
    def _probe(self):
4611
 
        if os.name == "posix":
4612
 
            return True
4613
 
        return False
4614
 
 
4615
 
ByteStringNamedFilesystem = _ByteStringNamedFilesystem()
4616
 
 
4617
 
 
4618
 
class _UTF8Filesystem(Feature):
4619
 
    """Is the filesystem UTF-8?"""
4620
 
 
4621
 
    def _probe(self):
4622
 
        if osutils._fs_enc.upper() in ('UTF-8', 'UTF8'):
4623
 
            return True
4624
 
        return False
4625
 
 
4626
 
UTF8Filesystem = _UTF8Filesystem()
4627
 
 
4628
 
 
4629
 
class _BreakinFeature(Feature):
4630
 
    """Does this platform support the breakin feature?"""
4631
 
 
4632
 
    def _probe(self):
4633
 
        from bzrlib import breakin
4634
 
        if breakin.determine_signal() is None:
4635
 
            return False
4636
 
        if sys.platform == 'win32':
4637
 
            # Windows doesn't have os.kill, and we catch the SIGBREAK signal.
4638
 
            # We trigger SIGBREAK via a Console api so we need ctypes to
4639
 
            # access the function
4640
 
            try:
4641
 
                import ctypes
4642
 
            except OSError:
4643
 
                return False
4644
 
        return True
4645
 
 
4646
 
    def feature_name(self):
4647
 
        return "SIGQUIT or SIGBREAK w/ctypes on win32"
4648
 
 
4649
 
 
4650
 
BreakinFeature = _BreakinFeature()
4651
 
 
4652
 
 
4653
 
class _CaseInsCasePresFilenameFeature(Feature):
4654
 
    """Is the file-system case insensitive, but case-preserving?"""
4655
 
 
4656
 
    def _probe(self):
4657
 
        fileno, name = tempfile.mkstemp(prefix='MixedCase')
4658
 
        try:
4659
 
            # first check truly case-preserving for created files, then check
4660
 
            # case insensitive when opening existing files.
4661
 
            name = osutils.normpath(name)
4662
 
            base, rel = osutils.split(name)
4663
 
            found_rel = osutils.canonical_relpath(base, name)
4664
 
            return (found_rel == rel
4665
 
                    and os.path.isfile(name.upper())
4666
 
                    and os.path.isfile(name.lower()))
4667
 
        finally:
4668
 
            os.close(fileno)
4669
 
            os.remove(name)
4670
 
 
4671
 
    def feature_name(self):
4672
 
        return "case-insensitive case-preserving filesystem"
4673
 
 
4674
 
CaseInsCasePresFilenameFeature = _CaseInsCasePresFilenameFeature()
4675
 
 
4676
 
 
4677
 
class _CaseInsensitiveFilesystemFeature(Feature):
4678
 
    """Check if underlying filesystem is case-insensitive but *not* case
4679
 
    preserving.
4680
 
    """
4681
 
    # Note that on Windows, Cygwin, MacOS etc, the file-systems are far
4682
 
    # more likely to be case preserving, so this case is rare.
4683
 
 
4684
 
    def _probe(self):
4685
 
        if CaseInsCasePresFilenameFeature.available():
4686
 
            return False
4687
 
 
4688
 
        if TestCaseWithMemoryTransport.TEST_ROOT is None:
4689
 
            root = osutils.mkdtemp(prefix='testbzr-', suffix='.tmp')
4690
 
            TestCaseWithMemoryTransport.TEST_ROOT = root
4691
 
        else:
4692
 
            root = TestCaseWithMemoryTransport.TEST_ROOT
4693
 
        tdir = osutils.mkdtemp(prefix='case-sensitive-probe-', suffix='',
4694
 
            dir=root)
4695
 
        name_a = osutils.pathjoin(tdir, 'a')
4696
 
        name_A = osutils.pathjoin(tdir, 'A')
4697
 
        os.mkdir(name_a)
4698
 
        result = osutils.isdir(name_A)
4699
 
        _rmtree_temp_dir(tdir)
4700
 
        return result
4701
 
 
4702
 
    def feature_name(self):
4703
 
        return 'case-insensitive filesystem'
4704
 
 
4705
 
CaseInsensitiveFilesystemFeature = _CaseInsensitiveFilesystemFeature()
4706
 
 
4707
 
 
4708
 
class _CaseSensitiveFilesystemFeature(Feature):
4709
 
 
4710
 
    def _probe(self):
4711
 
        if CaseInsCasePresFilenameFeature.available():
4712
 
            return False
4713
 
        elif CaseInsensitiveFilesystemFeature.available():
4714
 
            return False
4715
 
        else:
4716
 
            return True
4717
 
 
4718
 
    def feature_name(self):
4719
 
        return 'case-sensitive filesystem'
4720
 
 
4721
 
# new coding style is for feature instances to be lowercase
4722
 
case_sensitive_filesystem_feature = _CaseSensitiveFilesystemFeature()
4723
 
 
4724
 
 
4725
4467
# Only define SubUnitBzrRunner if subunit is available.
4726
4468
try:
4727
4469
    from subunit import TestProtocolClient
4745
4487
except ImportError:
4746
4488
    pass
4747
4489
 
4748
 
class _PosixPermissionsFeature(Feature):
4749
 
 
4750
 
    def _probe(self):
4751
 
        def has_perms():
4752
 
            # create temporary file and check if specified perms are maintained.
4753
 
            import tempfile
4754
 
 
4755
 
            write_perms = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
4756
 
            f = tempfile.mkstemp(prefix='bzr_perms_chk_')
4757
 
            fd, name = f
4758
 
            os.close(fd)
4759
 
            os.chmod(name, write_perms)
4760
 
 
4761
 
            read_perms = os.stat(name).st_mode & 0777
4762
 
            os.unlink(name)
4763
 
            return (write_perms == read_perms)
4764
 
 
4765
 
        return (os.name == 'posix') and has_perms()
4766
 
 
4767
 
    def feature_name(self):
4768
 
        return 'POSIX permissions support'
4769
 
 
4770
 
posix_permissions_feature = _PosixPermissionsFeature()
 
4490
 
 
4491
@deprecated_function(deprecated_in((2, 5, 0)))
 
4492
def ModuleAvailableFeature(name):
 
4493
    from bzrlib.tests import features
 
4494
    return features.ModuleAvailableFeature(name)
 
4495