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' to optionally run tests.
17
"""A collection of commonly used 'Features' which bzrlib uses to skip tests."""
26
23
from bzrlib import (
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
module = sys.modules.get(self.module_name, sentinel)
171
if module is sentinel:
173
self._module = __import__(self.module_name, {}, {}, [''])
178
self._module = module
187
def feature_name(self):
188
return self.module_name
191
class _HTTPSServerFeature(Feature):
192
"""Some tests want an https Server, check if one is available.
194
Right now, the only way this is available is under python2.6 which provides
205
def feature_name(self):
209
HTTPSServerFeature = _HTTPSServerFeature()
212
class _ByteStringNamedFilesystem(Feature):
213
"""Is the filesystem based on bytes?"""
216
if os.name == "posix":
220
ByteStringNamedFilesystem = _ByteStringNamedFilesystem()
223
class _UTF8Filesystem(Feature):
224
"""Is the filesystem UTF-8?"""
227
if osutils._fs_enc.upper() in ('UTF-8', 'UTF8'):
231
UTF8Filesystem = _UTF8Filesystem()
234
class _BreakinFeature(Feature):
235
"""Does this platform support the breakin feature?"""
238
from bzrlib import breakin
239
if breakin.determine_signal() is None:
241
if sys.platform == 'win32':
242
# Windows doesn't have os.kill, and we catch the SIGBREAK signal.
243
# We trigger SIGBREAK via a Console api so we need ctypes to
244
# access the function
251
def feature_name(self):
252
return "SIGQUIT or SIGBREAK w/ctypes on win32"
255
BreakinFeature = _BreakinFeature()
258
class _CaseInsCasePresFilenameFeature(Feature):
259
"""Is the file-system case insensitive, but case-preserving?"""
262
fileno, name = tempfile.mkstemp(prefix='MixedCase')
264
# first check truly case-preserving for created files, then check
265
# case insensitive when opening existing files.
266
name = osutils.normpath(name)
267
base, rel = osutils.split(name)
268
found_rel = osutils.canonical_relpath(base, name)
269
return (found_rel == rel
270
and os.path.isfile(name.upper())
271
and os.path.isfile(name.lower()))
276
def feature_name(self):
277
return "case-insensitive case-preserving filesystem"
279
CaseInsCasePresFilenameFeature = _CaseInsCasePresFilenameFeature()
282
class _CaseInsensitiveFilesystemFeature(Feature):
283
"""Check if underlying filesystem is case-insensitive but *not* case
286
# Note that on Windows, Cygwin, MacOS etc, the file-systems are far
287
# more likely to be case preserving, so this case is rare.
290
if CaseInsCasePresFilenameFeature.available():
293
if tests.TestCaseWithMemoryTransport.TEST_ROOT is None:
294
root = osutils.mkdtemp(prefix='testbzr-', suffix='.tmp')
295
tests.TestCaseWithMemoryTransport.TEST_ROOT = root
297
root = tests.TestCaseWithMemoryTransport.TEST_ROOT
298
tdir = osutils.mkdtemp(prefix='case-sensitive-probe-', suffix='',
300
name_a = osutils.pathjoin(tdir, 'a')
301
name_A = osutils.pathjoin(tdir, 'A')
303
result = osutils.isdir(name_A)
304
tests._rmtree_temp_dir(tdir)
307
def feature_name(self):
308
return 'case-insensitive filesystem'
310
CaseInsensitiveFilesystemFeature = _CaseInsensitiveFilesystemFeature()
313
class _CaseSensitiveFilesystemFeature(Feature):
316
if CaseInsCasePresFilenameFeature.available():
318
elif CaseInsensitiveFilesystemFeature.available():
323
def feature_name(self):
324
return 'case-sensitive filesystem'
326
# new coding style is for feature instances to be lowercase
327
case_sensitive_filesystem_feature = _CaseSensitiveFilesystemFeature()
330
class _NotRunningAsRoot(Feature):
29
class _NotRunningAsRoot(tests.Feature):
344
43
not_running_as_root = _NotRunningAsRoot()
346
apport = ModuleAvailableFeature('apport')
347
gpgme = ModuleAvailableFeature('gpgme')
348
lzma = ModuleAvailableFeature('lzma')
349
meliae = ModuleAvailableFeature('meliae')
350
paramiko = ModuleAvailableFeature('paramiko')
351
pycurl = ModuleAvailableFeature('pycurl')
352
pywintypes = ModuleAvailableFeature('pywintypes')
353
sphinx = ModuleAvailableFeature('sphinx')
354
subunit = ModuleAvailableFeature('subunit')
355
testtools = ModuleAvailableFeature('testtools')
357
compiled_patiencediff_feature = ModuleAvailableFeature(
358
'bzrlib._patiencediff_c')
359
meliae_feature = ModuleAvailableFeature('meliae.scanner')
360
lsprof_feature = ModuleAvailableFeature('bzrlib.lsprof')
363
class _BackslashDirSeparatorFeature(Feature):
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):
412
132
diff_feature = ExecutableFeature('diff')
415
class _PosixPermissionsFeature(Feature):
419
# Create temporary file and check if specified perms are
421
write_perms = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
422
f = tempfile.mkstemp(prefix='bzr_perms_chk_')
425
os.chmod(name, write_perms)
427
read_perms = os.stat(name).st_mode & 0777
429
return (write_perms == read_perms)
431
return (os.name == 'posix') and has_perms()
433
def feature_name(self):
434
return 'POSIX permissions support'
437
posix_permissions_feature = _PosixPermissionsFeature()
440
class _StraceFeature(Feature):
444
proc = subprocess.Popen(['strace'],
445
stderr=subprocess.PIPE,
446
stdout=subprocess.PIPE)
450
if e.errno == errno.ENOENT:
451
# strace is not installed
456
def feature_name(self):
460
strace_feature = _StraceFeature()
463
class _AttribFeature(Feature):
466
if (sys.platform not in ('cygwin', 'win32')):
469
proc = subprocess.Popen(['attrib', '.'], stdout=subprocess.PIPE)
472
return (0 == proc.wait())
474
def feature_name(self):
475
return 'attrib Windows command-line tool'
478
AttribFeature = _AttribFeature()
481
class Win32Feature(Feature):
135
class Win32Feature(tests.Feature):
482
136
"""Feature testing whether we're running selftest on Windows
483
137
or Windows-like platform.
489
143
def feature_name(self):
490
144
return "win32 platform"
493
146
win32_feature = Win32Feature()
496
for name in ['HTTPServerFeature',
497
'HTTPSServerFeature', 'SymlinkFeature', 'HardlinkFeature',
498
'OsFifoFeature', 'UnicodeFilenameFeature',
499
'ByteStringNamedFilesystem', 'UTF8Filesystem',
500
'BreakinFeature', 'CaseInsCasePresFilenameFeature',
501
'CaseInsensitiveFilesystemFeature', 'case_sensitive_filesystem_feature',
502
'posix_permissions_feature',
504
setattr(tests, name, _CompatabilityThunkFeature(
505
symbol_versioning.deprecated_in((2, 5, 0)),
506
'bzrlib.tests', name,
507
name, 'bzrlib.tests.features'))
510
for (old_name, new_name) in [
511
('UnicodeFilename', 'UnicodeFilenameFeature'),
513
setattr(tests, name, _CompatabilityThunkFeature(
514
symbol_versioning.deprecated_in((2, 5, 0)),
515
'bzrlib.tests', old_name,
516
new_name, 'bzrlib.tests.features'))