213
218
osutils.set_or_unset_env(var, value)
221
def _clear__type_equality_funcs(test):
222
"""Cleanup bound methods stored on TestCase instances
224
Clear the dict breaking a few (mostly) harmless cycles in the affected
225
unittests released with Python 2.6 and initial Python 2.7 versions.
227
For a few revisions between Python 2.7.1 and Python 2.7.2 that annoyingly
228
shipped in Oneiric, an object with no clear method was used, hence the
229
extra complications, see bug 809048 for details.
231
type_equality_funcs = getattr(test, "_type_equality_funcs", None)
232
if type_equality_funcs is not None:
233
tef_clear = getattr(type_equality_funcs, "clear", None)
234
if tef_clear is None:
235
tef_instance_dict = getattr(type_equality_funcs, "__dict__", None)
236
if tef_instance_dict is not None:
237
tef_clear = tef_instance_dict.clear
238
if tef_clear is not None:
216
242
class ExtendedTestResult(testtools.TextTestResult):
217
243
"""Accepts, reports and accumulates the results of running tests.
973
1001
def setUp(self):
974
1002
super(TestCase, self).setUp()
1004
# At this point we're still accessing the config files in $BZR_HOME (as
1005
# set by the user running selftest).
1006
timeout = config.GlobalStack().get('selftest.timeout')
1008
timeout_fixture = fixtures.TimeoutFixture(timeout)
1009
timeout_fixture.setUp()
1010
self.addCleanup(timeout_fixture.cleanUp)
975
1012
for feature in getattr(self, '_test_needs_features', []):
976
1013
self.requireFeature(feature)
977
1014
self._cleanEnvironment()
1016
if bzrlib.global_state is not None:
1017
self.overrideAttr(bzrlib.global_state, 'cmdline_overrides',
1018
config.CommandLineStore())
978
1020
self._silenceUI()
979
1021
self._startLogFile()
980
1022
self._benchcalls = []
1740
1781
:returns: The actual attr value.
1742
value = getattr(obj, attr_name)
1743
1783
# The actual value is captured by the call below
1744
self.addCleanup(setattr, obj, attr_name, value)
1784
value = getattr(obj, attr_name, _unitialized_attr)
1785
if value is _unitialized_attr:
1786
# When the test completes, the attribute should not exist, but if
1787
# we aren't setting a value, we don't need to do anything.
1788
if new is not _unitialized_attr:
1789
self.addCleanup(delattr, obj, attr_name)
1791
self.addCleanup(setattr, obj, attr_name, value)
1745
1792
if new is not _unitialized_attr:
1746
1793
setattr(obj, attr_name, new)
1760
1807
self.addCleanup(osutils.set_or_unset_env, name, value)
1810
def recordCalls(self, obj, attr_name):
1811
"""Monkeypatch in a wrapper that will record calls.
1813
The monkeypatch is automatically removed when the test concludes.
1815
:param obj: The namespace holding the reference to be replaced;
1816
typically a module, class, or object.
1817
:param attr_name: A string for the name of the attribute to
1819
:returns: A list that will be extended with one item every time the
1820
function is called, with a tuple of (args, kwargs).
1824
def decorator(*args, **kwargs):
1825
calls.append((args, kwargs))
1826
return orig(*args, **kwargs)
1827
orig = self.overrideAttr(obj, attr_name, decorator)
1763
1830
def _cleanEnvironment(self):
1764
1831
for name, value in isolated_environ.iteritems():
1765
1832
self.overrideEnv(name, value)
1772
1839
self._preserved_lazy_hooks.clear()
1774
1841
def knownFailure(self, reason):
1775
"""This test has failed for some known reason."""
1776
raise KnownFailure(reason)
1842
"""Declare that this test fails for a known reason
1844
Tests that are known to fail should generally be using expectedFailure
1845
with an appropriate reverse assertion if a change could cause the test
1846
to start passing. Conversely if the test has no immediate prospect of
1847
succeeding then using skip is more suitable.
1849
When this method is called while an exception is being handled, that
1850
traceback will be used, otherwise a new exception will be thrown to
1851
provide one but won't be reported.
1853
self._add_reason(reason)
1855
exc_info = sys.exc_info()
1856
if exc_info != (None, None, None):
1857
self._report_traceback(exc_info)
1860
raise self.failureException(reason)
1861
except self.failureException:
1862
exc_info = sys.exc_info()
1863
# GZ 02-08-2011: Maybe cleanup this err.exc_info attribute too?
1864
raise testtools.testcase._ExpectedFailure(exc_info)
1778
1868
def _suppress_log(self):
1779
1869
"""Remove the log info from details."""
2358
2455
self.transport_readonly_server = None
2359
2456
self.__vfs_server = None
2459
super(TestCaseWithMemoryTransport, self).setUp()
2461
def _add_disconnect_cleanup(transport):
2462
"""Schedule disconnection of given transport at test cleanup
2464
This needs to happen for all connected transports or leaks occur.
2466
Note reconnections may mean we call disconnect multiple times per
2467
transport which is suboptimal but seems harmless.
2469
self.addCleanup(transport.disconnect)
2471
_mod_transport.Transport.hooks.install_named_hook('post_connect',
2472
_add_disconnect_cleanup, None)
2474
self._make_test_root()
2475
self.addCleanup(os.chdir, os.getcwdu())
2476
self.makeAndChdirToTestDir()
2477
self.overrideEnvironmentForTesting()
2478
self.__readonly_server = None
2479
self.__server = None
2480
self.reduceLockdirTimeout()
2481
# Each test may use its own config files even if the local config files
2482
# don't actually exist. They'll rightly fail if they try to create them
2484
self.overrideAttr(config, '_shared_stores', {})
2361
2486
def get_transport(self, relpath=None):
2362
2487
"""Return a writeable transport.
2507
2633
root = TestCaseWithMemoryTransport.TEST_ROOT
2508
bzrdir.BzrDir.create_standalone_workingtree(root)
2635
# Make sure we get a readable and accessible home for .bzr.log
2636
# and/or config files, and not fallback to weird defaults (see
2637
# http://pad.lv/825027).
2638
self.assertIs(None, os.environ.get('BZR_HOME', None))
2639
os.environ['BZR_HOME'] = root
2640
wt = controldir.ControlDir.create_standalone_workingtree(root)
2641
del os.environ['BZR_HOME']
2642
except Exception, e:
2643
self.fail("Fail to initialize the safety net: %r\n" % (e,))
2644
# Hack for speed: remember the raw bytes of the dirstate file so that
2645
# we don't need to re-open the wt to check it hasn't changed.
2646
TestCaseWithMemoryTransport._SAFETY_NET_PRISTINE_DIRSTATE = (
2647
wt.control_transport.get_bytes('dirstate'))
2510
2649
def _check_safety_net(self):
2511
2650
"""Check that the safety .bzr directory have not been touched.
2555
2694
self.test_home_dir = self.test_dir + "/MemoryTransportMissingHomeDir"
2556
2695
self.permit_dir(self.test_dir)
2558
def make_branch(self, relpath, format=None):
2697
def make_branch(self, relpath, format=None, name=None):
2559
2698
"""Create a branch on the transport at relpath."""
2560
2699
repo = self.make_repository(relpath, format=format)
2561
return repo.bzrdir.create_branch()
2700
return repo.bzrdir.create_branch(append_revisions_only=False,
2703
def get_default_format(self):
2706
def resolve_format(self, format):
2707
"""Resolve an object to a ControlDir format object.
2709
The initial format object can either already be
2710
a ControlDirFormat, None (for the default format),
2711
or a string with the name of the control dir format.
2713
:param format: Object to resolve
2714
:return A ControlDirFormat instance
2717
format = self.get_default_format()
2718
if isinstance(format, basestring):
2719
format = controldir.format_registry.make_bzrdir(format)
2563
2722
def make_bzrdir(self, relpath, format=None):
2568
2727
t = _mod_transport.get_transport(maybe_a_url)
2569
2728
if len(segments) > 1 and segments[-1] not in ('', '.'):
2570
2729
t.ensure_base()
2573
if isinstance(format, basestring):
2574
format = bzrdir.format_registry.make_bzrdir(format)
2730
format = self.resolve_format(format)
2575
2731
return format.initialize_on_transport(t)
2576
2732
except errors.UninitializableFormat:
2577
2733
raise TestSkipped("Format %s is not initializable." % format)
2579
def make_repository(self, relpath, shared=False, format=None):
2735
def make_repository(self, relpath, shared=None, format=None):
2580
2736
"""Create a repository on our default transport at relpath.
2582
2738
Note that relpath must be a relative path, not a full url.
2613
2769
self.overrideEnv('HOME', test_home_dir)
2614
2770
self.overrideEnv('BZR_HOME', test_home_dir)
2617
super(TestCaseWithMemoryTransport, self).setUp()
2618
# Ensure that ConnectedTransport doesn't leak sockets
2619
def get_transport_with_cleanup(*args, **kwargs):
2620
t = orig_get_transport(*args, **kwargs)
2621
if isinstance(t, _mod_transport.ConnectedTransport):
2622
self.addCleanup(t.disconnect)
2625
orig_get_transport = self.overrideAttr(_mod_transport, 'get_transport',
2626
get_transport_with_cleanup)
2627
self._make_test_root()
2628
self.addCleanup(os.chdir, os.getcwdu())
2629
self.makeAndChdirToTestDir()
2630
self.overrideEnvironmentForTesting()
2631
self.__readonly_server = None
2632
self.__server = None
2633
self.reduceLockdirTimeout()
2635
2772
def setup_smart_server_with_call_log(self):
2636
2773
"""Sets up a smart server as the transport server with a call log."""
2637
2774
self.transport_server = test_server.SmartTCPServer_for_testing
2775
self.hpss_connections = []
2638
2776
self.hpss_calls = []
2639
2777
import traceback
2640
2778
# Skip the current stack down to the caller of
3263
3414
class TestDecorator(TestUtil.TestSuite):
3264
3415
"""A decorator for TestCase/TestSuite objects.
3266
Usually, subclasses should override __iter__(used when flattening test
3267
suites), which we do to filter, reorder, parallelise and so on, run() and
3417
Contains rather than flattening suite passed on construction
3271
def __init__(self, suite):
3272
TestUtil.TestSuite.__init__(self)
3275
def countTestCases(self):
3278
cases += test.countTestCases()
3285
def run(self, result):
3286
# Use iteration on self, not self._tests, to allow subclasses to hook
3289
if result.shouldStop:
3420
def __init__(self, suite=None):
3421
super(TestDecorator, self).__init__()
3422
if suite is not None:
3425
# Don't need subclass run method with suite emptying
3426
run = unittest.TestSuite.run
3295
3429
class CountingDecorator(TestDecorator):
3306
3440
"""A decorator which excludes test matching an exclude pattern."""
3308
3442
def __init__(self, suite, exclude_pattern):
3309
TestDecorator.__init__(self, suite)
3310
self.exclude_pattern = exclude_pattern
3311
self.excluded = False
3315
return iter(self._tests)
3316
self.excluded = True
3317
suite = exclude_tests_by_re(self, self.exclude_pattern)
3319
self.addTests(suite)
3320
return iter(self._tests)
3443
super(ExcludeDecorator, self).__init__(
3444
exclude_tests_by_re(suite, exclude_pattern))
3323
3447
class FilterTestsDecorator(TestDecorator):
3324
3448
"""A decorator which filters tests to those matching a pattern."""
3326
3450
def __init__(self, suite, pattern):
3327
TestDecorator.__init__(self, suite)
3328
self.pattern = pattern
3329
self.filtered = False
3333
return iter(self._tests)
3334
self.filtered = True
3335
suite = filter_suite_by_re(self, self.pattern)
3337
self.addTests(suite)
3338
return iter(self._tests)
3451
super(FilterTestsDecorator, self).__init__(
3452
filter_suite_by_re(suite, pattern))
3341
3455
class RandomDecorator(TestDecorator):
3342
3456
"""A decorator which randomises the order of its tests."""
3344
3458
def __init__(self, suite, random_seed, stream):
3345
TestDecorator.__init__(self, suite)
3346
self.random_seed = random_seed
3347
self.randomised = False
3348
self.stream = stream
3352
return iter(self._tests)
3353
self.randomised = True
3354
self.stream.write("Randomizing test order using seed %s\n\n" %
3355
(self.actual_seed()))
3459
random_seed = self.actual_seed(random_seed)
3460
stream.write("Randomizing test order using seed %s\n\n" %
3356
3462
# Initialise the random number generator.
3357
random.seed(self.actual_seed())
3358
suite = randomize_suite(self)
3360
self.addTests(suite)
3361
return iter(self._tests)
3463
random.seed(random_seed)
3464
super(RandomDecorator, self).__init__(randomize_suite(suite))
3363
def actual_seed(self):
3364
if self.random_seed == "now":
3467
def actual_seed(seed):
3365
3469
# We convert the seed to a long to make it reuseable across
3366
3470
# invocations (because the user can reenter it).
3367
self.random_seed = long(time.time())
3471
return long(time.time())
3369
3473
# Convert the seed to a long if we can
3371
self.random_seed = long(self.random_seed)
3476
except (TypeError, ValueError):
3374
return self.random_seed
3377
3481
class TestFirstDecorator(TestDecorator):
3378
3482
"""A decorator which moves named tests to the front."""
3380
3484
def __init__(self, suite, pattern):
3381
TestDecorator.__init__(self, suite)
3382
self.pattern = pattern
3383
self.filtered = False
3387
return iter(self._tests)
3388
self.filtered = True
3389
suites = split_suite_by_re(self, self.pattern)
3391
self.addTests(suites)
3392
return iter(self._tests)
3485
super(TestFirstDecorator, self).__init__()
3486
self.addTests(split_suite_by_re(suite, pattern))
3395
3489
def partition_tests(suite, count):
3440
3534
ProtocolTestCase.run(self, result)
3442
os.waitpid(self.pid, 0)
3536
pid, status = os.waitpid(self.pid, 0)
3537
# GZ 2011-10-18: If status is nonzero, should report to the result
3538
# that something went wrong.
3444
3540
test_blocks = partition_tests(suite, concurrency)
3541
# Clear the tests from the original suite so it doesn't keep them alive
3542
suite._tests[:] = []
3445
3543
for process_tests in test_blocks:
3446
process_suite = TestUtil.TestSuite()
3447
process_suite.addTests(process_tests)
3544
process_suite = TestUtil.TestSuite(process_tests)
3545
# Also clear each split list so new suite has only reference
3546
process_tests[:] = []
3448
3547
c2pread, c2pwrite = os.pipe()
3449
3548
pid = os.fork()
3451
workaround_zealous_crypto_random()
3551
stream = os.fdopen(c2pwrite, 'wb', 1)
3552
workaround_zealous_crypto_random()
3453
3553
os.close(c2pread)
3454
3554
# Leave stderr and stdout open so we can see test noise
3455
3555
# Close stdin so that the child goes away if it decides to
3456
3556
# read from stdin (otherwise its a roulette to see what
3457
3557
# child actually gets keystrokes for pdb etc).
3458
3558
sys.stdin.close()
3460
stream = os.fdopen(c2pwrite, 'wb', 1)
3461
3559
subunit_result = AutoTimingTestResultDecorator(
3462
TestProtocolClient(stream))
3560
SubUnitBzrProtocolClient(stream))
3463
3561
process_suite.run(subunit_result)
3563
# Try and report traceback on stream, but exit with error even
3564
# if stream couldn't be created or something else goes wrong.
3565
# The traceback is formatted to a string and written in one go
3566
# to avoid interleaving lines from multiple failing children.
3568
stream.write(traceback.format_exc())
3467
3573
os.close(c2pwrite)
3468
3574
stream = os.fdopen(c2pread, 'rb', 1)
3885
3991
'bzrlib.tests.test_email_message',
3886
3992
'bzrlib.tests.test_eol_filters',
3887
3993
'bzrlib.tests.test_errors',
3994
'bzrlib.tests.test_estimate_compressed_size',
3888
3995
'bzrlib.tests.test_export',
3889
3996
'bzrlib.tests.test_export_pot',
3890
3997
'bzrlib.tests.test_extract',
3998
'bzrlib.tests.test_features',
3891
3999
'bzrlib.tests.test_fetch',
3892
4000
'bzrlib.tests.test_fixtures',
3893
4001
'bzrlib.tests.test_fifo_cache',
3894
4002
'bzrlib.tests.test_filters',
4003
'bzrlib.tests.test_filter_tree',
3895
4004
'bzrlib.tests.test_ftp_transport',
3896
4005
'bzrlib.tests.test_foreign',
3897
4006
'bzrlib.tests.test_generate_docs',
4334
4448
% (os.path.basename(dirname), printable_e))
4337
class Feature(object):
4338
"""An operating system Feature."""
4341
self._available = None
4343
def available(self):
4344
"""Is the feature available?
4346
:return: True if the feature is available.
4348
if self._available is None:
4349
self._available = self._probe()
4350
return self._available
4353
"""Implement this method in concrete features.
4355
:return: True if the feature is available.
4357
raise NotImplementedError
4360
if getattr(self, 'feature_name', None):
4361
return self.feature_name()
4362
return self.__class__.__name__
4365
class _SymlinkFeature(Feature):
4368
return osutils.has_symlinks()
4370
def feature_name(self):
4373
SymlinkFeature = _SymlinkFeature()
4376
class _HardlinkFeature(Feature):
4379
return osutils.has_hardlinks()
4381
def feature_name(self):
4384
HardlinkFeature = _HardlinkFeature()
4387
class _OsFifoFeature(Feature):
4390
return getattr(os, 'mkfifo', None)
4392
def feature_name(self):
4393
return 'filesystem fifos'
4395
OsFifoFeature = _OsFifoFeature()
4398
class _UnicodeFilenameFeature(Feature):
4399
"""Does the filesystem support Unicode filenames?"""
4403
# Check for character combinations unlikely to be covered by any
4404
# single non-unicode encoding. We use the characters
4405
# - greek small letter alpha (U+03B1) and
4406
# - braille pattern dots-123456 (U+283F).
4407
os.stat(u'\u03b1\u283f')
4408
except UnicodeEncodeError:
4410
except (IOError, OSError):
4411
# The filesystem allows the Unicode filename but the file doesn't
4415
# The filesystem allows the Unicode filename and the file exists,
4419
UnicodeFilenameFeature = _UnicodeFilenameFeature()
4422
class _CompatabilityThunkFeature(Feature):
4423
"""This feature is just a thunk to another feature.
4425
It issues a deprecation warning if it is accessed, to let you know that you
4426
should really use a different feature.
4429
def __init__(self, dep_version, module, name,
4430
replacement_name, replacement_module=None):
4431
super(_CompatabilityThunkFeature, self).__init__()
4432
self._module = module
4433
if replacement_module is None:
4434
replacement_module = module
4435
self._replacement_module = replacement_module
4437
self._replacement_name = replacement_name
4438
self._dep_version = dep_version
4439
self._feature = None
4442
if self._feature is None:
4443
depr_msg = self._dep_version % ('%s.%s'
4444
% (self._module, self._name))
4445
use_msg = ' Use %s.%s instead.' % (self._replacement_module,
4446
self._replacement_name)
4447
symbol_versioning.warn(depr_msg + use_msg, DeprecationWarning)
4448
# Import the new feature and use it as a replacement for the
4450
self._feature = pyutils.get_named_object(
4451
self._replacement_module, self._replacement_name)
4455
return self._feature._probe()
4458
class ModuleAvailableFeature(Feature):
4459
"""This is a feature than describes a module we want to be available.
4461
Declare the name of the module in __init__(), and then after probing, the
4462
module will be available as 'self.module'.
4464
:ivar module: The module if it is available, else None.
4467
def __init__(self, module_name):
4468
super(ModuleAvailableFeature, self).__init__()
4469
self.module_name = module_name
4473
self._module = __import__(self.module_name, {}, {}, [''])
4480
if self.available(): # Make sure the probe has been done
4484
def feature_name(self):
4485
return self.module_name
4488
4451
def probe_unicode_in_user_encoding():
4489
4452
"""Try to encode several unicode strings to use in unicode-aware tests.
4490
4453
Return first successfull match.
4521
class _HTTPSServerFeature(Feature):
4522
"""Some tests want an https Server, check if one is available.
4524
Right now, the only way this is available is under python2.6 which provides
4535
def feature_name(self):
4536
return 'HTTPSServer'
4539
HTTPSServerFeature = _HTTPSServerFeature()
4542
class _UnicodeFilename(Feature):
4543
"""Does the filesystem support Unicode filenames?"""
4548
except UnicodeEncodeError:
4550
except (IOError, OSError):
4551
# The filesystem allows the Unicode filename but the file doesn't
4555
# The filesystem allows the Unicode filename and the file exists,
4559
UnicodeFilename = _UnicodeFilename()
4562
class _ByteStringNamedFilesystem(Feature):
4563
"""Is the filesystem based on bytes?"""
4566
if os.name == "posix":
4570
ByteStringNamedFilesystem = _ByteStringNamedFilesystem()
4573
class _UTF8Filesystem(Feature):
4574
"""Is the filesystem UTF-8?"""
4577
if osutils._fs_enc.upper() in ('UTF-8', 'UTF8'):
4581
UTF8Filesystem = _UTF8Filesystem()
4584
class _BreakinFeature(Feature):
4585
"""Does this platform support the breakin feature?"""
4588
from bzrlib import breakin
4589
if breakin.determine_signal() is None:
4591
if sys.platform == 'win32':
4592
# Windows doesn't have os.kill, and we catch the SIGBREAK signal.
4593
# We trigger SIGBREAK via a Console api so we need ctypes to
4594
# access the function
4601
def feature_name(self):
4602
return "SIGQUIT or SIGBREAK w/ctypes on win32"
4605
BreakinFeature = _BreakinFeature()
4608
class _CaseInsCasePresFilenameFeature(Feature):
4609
"""Is the file-system case insensitive, but case-preserving?"""
4612
fileno, name = tempfile.mkstemp(prefix='MixedCase')
4614
# first check truly case-preserving for created files, then check
4615
# case insensitive when opening existing files.
4616
name = osutils.normpath(name)
4617
base, rel = osutils.split(name)
4618
found_rel = osutils.canonical_relpath(base, name)
4619
return (found_rel == rel
4620
and os.path.isfile(name.upper())
4621
and os.path.isfile(name.lower()))
4626
def feature_name(self):
4627
return "case-insensitive case-preserving filesystem"
4629
CaseInsCasePresFilenameFeature = _CaseInsCasePresFilenameFeature()
4632
class _CaseInsensitiveFilesystemFeature(Feature):
4633
"""Check if underlying filesystem is case-insensitive but *not* case
4636
# Note that on Windows, Cygwin, MacOS etc, the file-systems are far
4637
# more likely to be case preserving, so this case is rare.
4640
if CaseInsCasePresFilenameFeature.available():
4643
if TestCaseWithMemoryTransport.TEST_ROOT is None:
4644
root = osutils.mkdtemp(prefix='testbzr-', suffix='.tmp')
4645
TestCaseWithMemoryTransport.TEST_ROOT = root
4647
root = TestCaseWithMemoryTransport.TEST_ROOT
4648
tdir = osutils.mkdtemp(prefix='case-sensitive-probe-', suffix='',
4650
name_a = osutils.pathjoin(tdir, 'a')
4651
name_A = osutils.pathjoin(tdir, 'A')
4653
result = osutils.isdir(name_A)
4654
_rmtree_temp_dir(tdir)
4657
def feature_name(self):
4658
return 'case-insensitive filesystem'
4660
CaseInsensitiveFilesystemFeature = _CaseInsensitiveFilesystemFeature()
4663
class _CaseSensitiveFilesystemFeature(Feature):
4666
if CaseInsCasePresFilenameFeature.available():
4668
elif CaseInsensitiveFilesystemFeature.available():
4673
def feature_name(self):
4674
return 'case-sensitive filesystem'
4676
# new coding style is for feature instances to be lowercase
4677
case_sensitive_filesystem_feature = _CaseSensitiveFilesystemFeature()
4680
4484
# Only define SubUnitBzrRunner if subunit is available.
4682
4486
from subunit import TestProtocolClient
4683
4487
from subunit.test_results import AutoTimingTestResultDecorator
4684
4488
class SubUnitBzrProtocolClient(TestProtocolClient):
4490
def stopTest(self, test):
4491
super(SubUnitBzrProtocolClient, self).stopTest(test)
4492
_clear__type_equality_funcs(test)
4686
4494
def addSuccess(self, test, details=None):
4687
4495
# The subunit client always includes the details in the subunit
4688
4496
# stream, but we don't want to include it in ours.
4700
4508
except ImportError:
4703
class _PosixPermissionsFeature(Feature):
4707
# create temporary file and check if specified perms are maintained.
4710
write_perms = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
4711
f = tempfile.mkstemp(prefix='bzr_perms_chk_')
4714
os.chmod(name, write_perms)
4716
read_perms = os.stat(name).st_mode & 0777
4718
return (write_perms == read_perms)
4720
return (os.name == 'posix') and has_perms()
4722
def feature_name(self):
4723
return 'POSIX permissions support'
4725
posix_permissions_feature = _PosixPermissionsFeature()
4512
# API compatibility for old plugins; see bug 892622.
4515
'HTTPServerFeature',
4516
'ModuleAvailableFeature',
4517
'HTTPSServerFeature', 'SymlinkFeature', 'HardlinkFeature',
4518
'OsFifoFeature', 'UnicodeFilenameFeature',
4519
'ByteStringNamedFilesystem', 'UTF8Filesystem',
4520
'BreakinFeature', 'CaseInsCasePresFilenameFeature',
4521
'CaseInsensitiveFilesystemFeature', 'case_sensitive_filesystem_feature',
4522
'posix_permissions_feature',
4524
globals()[name] = _CompatabilityThunkFeature(
4525
symbol_versioning.deprecated_in((2, 5, 0)),
4526
'bzrlib.tests', name,
4527
name, 'bzrlib.tests.features')
4530
for (old_name, new_name) in [
4531
('UnicodeFilename', 'UnicodeFilenameFeature'),
4533
globals()[name] = _CompatabilityThunkFeature(
4534
symbol_versioning.deprecated_in((2, 5, 0)),
4535
'bzrlib.tests', old_name,
4536
new_name, 'bzrlib.tests.features')