14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
"""A collection of commonly used 'Features' which bzrlib uses to skip tests."""
17
"""A collection of commonly used 'Features' to optionally run tests.
23
26
from bzrlib import (
29
class _NotRunningAsRoot(tests.Feature):
33
class Feature(object):
34
"""An operating system Feature."""
37
self._available = None
40
"""Is the feature available?
42
:return: True if the feature is available.
44
if self._available is None:
45
self._available = self._probe()
46
return self._available
49
"""Implement this method in concrete features.
51
:return: True if the feature is available.
53
raise NotImplementedError
56
if getattr(self, 'feature_name', None):
57
return self.feature_name()
58
return self.__class__.__name__
61
class _SymlinkFeature(Feature):
64
return osutils.has_symlinks()
66
def feature_name(self):
69
SymlinkFeature = _SymlinkFeature()
72
class _HardlinkFeature(Feature):
75
return osutils.has_hardlinks()
77
def feature_name(self):
80
HardlinkFeature = _HardlinkFeature()
83
class _OsFifoFeature(Feature):
86
return getattr(os, 'mkfifo', None)
88
def feature_name(self):
89
return 'filesystem fifos'
91
OsFifoFeature = _OsFifoFeature()
94
class _UnicodeFilenameFeature(Feature):
95
"""Does the filesystem support Unicode filenames?"""
99
# Check for character combinations unlikely to be covered by any
100
# single non-unicode encoding. We use the characters
101
# - greek small letter alpha (U+03B1) and
102
# - braille pattern dots-123456 (U+283F).
103
os.stat(u'\u03b1\u283f')
104
except UnicodeEncodeError:
106
except (IOError, OSError):
107
# The filesystem allows the Unicode filename but the file doesn't
111
# The filesystem allows the Unicode filename and the file exists,
115
UnicodeFilenameFeature = _UnicodeFilenameFeature()
118
class _CompatabilityThunkFeature(Feature):
119
"""This feature is just a thunk to another feature.
121
It issues a deprecation warning if it is accessed, to let you know that you
122
should really use a different feature.
125
def __init__(self, dep_version, module, name,
126
replacement_name, replacement_module=None):
127
super(_CompatabilityThunkFeature, self).__init__()
128
self._module = module
129
if replacement_module is None:
130
replacement_module = module
131
self._replacement_module = replacement_module
133
self._replacement_name = replacement_name
134
self._dep_version = dep_version
138
if self._feature is None:
139
from bzrlib import pyutils
140
depr_msg = self._dep_version % ('%s.%s'
141
% (self._module, self._name))
142
use_msg = ' Use %s.%s instead.' % (self._replacement_module,
143
self._replacement_name)
144
symbol_versioning.warn(depr_msg + use_msg, DeprecationWarning)
145
# Import the new feature and use it as a replacement for the
147
self._feature = pyutils.get_named_object(
148
self._replacement_module, self._replacement_name)
152
return self._feature._probe()
155
class ModuleAvailableFeature(Feature):
156
"""This is a feature than describes a module we want to be available.
158
Declare the name of the module in __init__(), and then after probing, the
159
module will be available as 'self.module'.
161
:ivar module: The module if it is available, else None.
164
def __init__(self, module_name):
165
super(ModuleAvailableFeature, self).__init__()
166
self.module_name = module_name
170
self._module = __import__(self.module_name, {}, {}, [''])
181
def feature_name(self):
182
return self.module_name
185
class _HTTPSServerFeature(Feature):
186
"""Some tests want an https Server, check if one is available.
188
Right now, the only way this is available is under python2.6 which provides
199
def feature_name(self):
203
HTTPSServerFeature = _HTTPSServerFeature()
206
class _ByteStringNamedFilesystem(Feature):
207
"""Is the filesystem based on bytes?"""
210
if os.name == "posix":
214
ByteStringNamedFilesystem = _ByteStringNamedFilesystem()
217
class _UTF8Filesystem(Feature):
218
"""Is the filesystem UTF-8?"""
221
if osutils._fs_enc.upper() in ('UTF-8', 'UTF8'):
225
UTF8Filesystem = _UTF8Filesystem()
228
class _BreakinFeature(Feature):
229
"""Does this platform support the breakin feature?"""
232
from bzrlib import breakin
233
if breakin.determine_signal() is None:
235
if sys.platform == 'win32':
236
# Windows doesn't have os.kill, and we catch the SIGBREAK signal.
237
# We trigger SIGBREAK via a Console api so we need ctypes to
238
# access the function
245
def feature_name(self):
246
return "SIGQUIT or SIGBREAK w/ctypes on win32"
249
BreakinFeature = _BreakinFeature()
252
class _CaseInsCasePresFilenameFeature(Feature):
253
"""Is the file-system case insensitive, but case-preserving?"""
256
fileno, name = tempfile.mkstemp(prefix='MixedCase')
258
# first check truly case-preserving for created files, then check
259
# case insensitive when opening existing files.
260
name = osutils.normpath(name)
261
base, rel = osutils.split(name)
262
found_rel = osutils.canonical_relpath(base, name)
263
return (found_rel == rel
264
and os.path.isfile(name.upper())
265
and os.path.isfile(name.lower()))
270
def feature_name(self):
271
return "case-insensitive case-preserving filesystem"
273
CaseInsCasePresFilenameFeature = _CaseInsCasePresFilenameFeature()
276
class _CaseInsensitiveFilesystemFeature(Feature):
277
"""Check if underlying filesystem is case-insensitive but *not* case
280
# Note that on Windows, Cygwin, MacOS etc, the file-systems are far
281
# more likely to be case preserving, so this case is rare.
284
if CaseInsCasePresFilenameFeature.available():
287
if tests.TestCaseWithMemoryTransport.TEST_ROOT is None:
288
root = osutils.mkdtemp(prefix='testbzr-', suffix='.tmp')
289
tests.TestCaseWithMemoryTransport.TEST_ROOT = root
291
root = tests.TestCaseWithMemoryTransport.TEST_ROOT
292
tdir = osutils.mkdtemp(prefix='case-sensitive-probe-', suffix='',
294
name_a = osutils.pathjoin(tdir, 'a')
295
name_A = osutils.pathjoin(tdir, 'A')
297
result = osutils.isdir(name_A)
298
tests._rmtree_temp_dir(tdir)
301
def feature_name(self):
302
return 'case-insensitive filesystem'
304
CaseInsensitiveFilesystemFeature = _CaseInsensitiveFilesystemFeature()
307
class _CaseSensitiveFilesystemFeature(Feature):
310
if CaseInsCasePresFilenameFeature.available():
312
elif CaseInsensitiveFilesystemFeature.available():
317
def feature_name(self):
318
return 'case-sensitive filesystem'
320
# new coding style is for feature instances to be lowercase
321
case_sensitive_filesystem_feature = _CaseSensitiveFilesystemFeature()
324
class _NotRunningAsRoot(Feature):
43
338
not_running_as_root = _NotRunningAsRoot()
45
apport = tests.ModuleAvailableFeature('apport')
46
gpgme = tests.ModuleAvailableFeature('gpgme')
47
lzma = tests.ModuleAvailableFeature('lzma')
48
meliae = tests.ModuleAvailableFeature('meliae')
49
paramiko = tests.ModuleAvailableFeature('paramiko')
50
pycurl = tests.ModuleAvailableFeature('pycurl')
51
pywintypes = tests.ModuleAvailableFeature('pywintypes')
52
sphinx = tests.ModuleAvailableFeature('sphinx')
53
subunit = tests.ModuleAvailableFeature('subunit')
54
testtools = tests.ModuleAvailableFeature('testtools')
57
class _BackslashDirSeparatorFeature(tests.Feature):
340
apport = ModuleAvailableFeature('apport')
341
gpgme = ModuleAvailableFeature('gpgme')
342
lzma = ModuleAvailableFeature('lzma')
343
meliae = ModuleAvailableFeature('meliae')
344
paramiko = ModuleAvailableFeature('paramiko')
345
pycurl = ModuleAvailableFeature('pycurl')
346
pywintypes = ModuleAvailableFeature('pywintypes')
347
sphinx = ModuleAvailableFeature('sphinx')
348
subunit = ModuleAvailableFeature('subunit')
349
testtools = ModuleAvailableFeature('testtools')
351
compiled_patiencediff_feature = ModuleAvailableFeature(
352
'bzrlib._patiencediff_c')
353
meliae_feature = ModuleAvailableFeature('meliae.scanner')
354
lsprof_feature = ModuleAvailableFeature('bzrlib.lsprof')
357
class _BackslashDirSeparatorFeature(Feature):
132
406
diff_feature = ExecutableFeature('diff')
135
class Win32Feature(tests.Feature):
409
class _PosixPermissionsFeature(Feature):
413
# Create temporary file and check if specified perms are
415
write_perms = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
416
f = tempfile.mkstemp(prefix='bzr_perms_chk_')
419
os.chmod(name, write_perms)
421
read_perms = os.stat(name).st_mode & 0777
423
return (write_perms == read_perms)
425
return (os.name == 'posix') and has_perms()
427
def feature_name(self):
428
return 'POSIX permissions support'
431
posix_permissions_feature = _PosixPermissionsFeature()
434
class _StraceFeature(Feature):
438
proc = subprocess.Popen(['strace'],
439
stderr=subprocess.PIPE,
440
stdout=subprocess.PIPE)
444
if e.errno == errno.ENOENT:
445
# strace is not installed
450
def feature_name(self):
454
strace_feature = _StraceFeature()
457
class _AttribFeature(Feature):
460
if (sys.platform not in ('cygwin', 'win32')):
463
proc = subprocess.Popen(['attrib', '.'], stdout=subprocess.PIPE)
466
return (0 == proc.wait())
468
def feature_name(self):
469
return 'attrib Windows command-line tool'
472
AttribFeature = _AttribFeature()
475
class Win32Feature(Feature):
136
476
"""Feature testing whether we're running selftest on Windows
137
477
or Windows-like platform.
143
483
def feature_name(self):
144
484
return "win32 platform"
146
487
win32_feature = Win32Feature()
490
for name in ['HTTPServerFeature',
491
'HTTPSServerFeature', 'SymlinkFeature', 'HardlinkFeature',
492
'OsFifoFeature', 'UnicodeFilenameFeature',
493
'ByteStringNamedFilesystem', 'UTF8Filesystem',
494
'BreakinFeature', 'CaseInsCasePresFilenameFeature',
495
'CaseInsensitiveFilesystemFeature', 'case_sensitive_filesystem_feature',
496
'posix_permissions_feature',
498
setattr(tests, name, _CompatabilityThunkFeature(
499
symbol_versioning.deprecated_in((2, 5, 0)),
500
'bzrlib.tests', name,
501
name, 'bzrlib.tests.features'))
504
for (old_name, new_name) in [
505
('UnicodeFilename', 'UnicodeFilenameFeature'),
507
setattr(tests, name, _CompatabilityThunkFeature(
508
symbol_versioning.deprecated_in((2, 5, 0)),
509
'bzrlib.tests', old_name,
510
new_name, 'bzrlib.tests.features'))