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."""
25
22
from bzrlib import (
32
class Feature(object):
33
"""An operating system Feature."""
36
self._available = None
39
"""Is the feature available?
41
:return: True if the feature is available.
43
if self._available is None:
44
self._available = self._probe()
45
return self._available
48
"""Implement this method in concrete features.
50
:return: True if the feature is available.
52
raise NotImplementedError
55
if getattr(self, 'feature_name', None):
56
return self.feature_name()
57
return self.__class__.__name__
60
class _SymlinkFeature(Feature):
63
return osutils.has_symlinks()
65
def feature_name(self):
68
SymlinkFeature = _SymlinkFeature()
71
class _HardlinkFeature(Feature):
74
return osutils.has_hardlinks()
76
def feature_name(self):
79
HardlinkFeature = _HardlinkFeature()
82
class _OsFifoFeature(Feature):
85
return getattr(os, 'mkfifo', None)
87
def feature_name(self):
88
return 'filesystem fifos'
90
OsFifoFeature = _OsFifoFeature()
93
class _UnicodeFilenameFeature(Feature):
94
"""Does the filesystem support Unicode filenames?"""
98
# Check for character combinations unlikely to be covered by any
99
# single non-unicode encoding. We use the characters
100
# - greek small letter alpha (U+03B1) and
101
# - braille pattern dots-123456 (U+283F).
102
os.stat(u'\u03b1\u283f')
103
except UnicodeEncodeError:
105
except (IOError, OSError):
106
# The filesystem allows the Unicode filename but the file doesn't
110
# The filesystem allows the Unicode filename and the file exists,
114
UnicodeFilenameFeature = _UnicodeFilenameFeature()
117
class _CompatabilityThunkFeature(Feature):
118
"""This feature is just a thunk to another feature.
120
It issues a deprecation warning if it is accessed, to let you know that you
121
should really use a different feature.
124
def __init__(self, dep_version, module, name,
125
replacement_name, replacement_module=None):
126
super(_CompatabilityThunkFeature, self).__init__()
127
self._module = module
128
if replacement_module is None:
129
replacement_module = module
130
self._replacement_module = replacement_module
132
self._replacement_name = replacement_name
133
self._dep_version = dep_version
137
if self._feature is None:
138
from bzrlib import pyutils
139
depr_msg = self._dep_version % ('%s.%s'
140
% (self._module, self._name))
141
use_msg = ' Use %s.%s instead.' % (self._replacement_module,
142
self._replacement_name)
143
symbol_versioning.warn(depr_msg + use_msg, DeprecationWarning)
144
# Import the new feature and use it as a replacement for the
146
self._feature = pyutils.get_named_object(
147
self._replacement_module, self._replacement_name)
151
return self._feature._probe()
154
class ModuleAvailableFeature(Feature):
155
"""This is a feature than describes a module we want to be available.
157
Declare the name of the module in __init__(), and then after probing, the
158
module will be available as 'self.module'.
160
:ivar module: The module if it is available, else None.
163
def __init__(self, module_name):
164
super(ModuleAvailableFeature, self).__init__()
165
self.module_name = module_name
169
self._module = __import__(self.module_name, {}, {}, [''])
180
def feature_name(self):
181
return self.module_name
184
class _HTTPSServerFeature(Feature):
185
"""Some tests want an https Server, check if one is available.
187
Right now, the only way this is available is under python2.6 which provides
198
def feature_name(self):
202
HTTPSServerFeature = _HTTPSServerFeature()
205
class _ByteStringNamedFilesystem(Feature):
206
"""Is the filesystem based on bytes?"""
209
if os.name == "posix":
213
ByteStringNamedFilesystem = _ByteStringNamedFilesystem()
216
class _UTF8Filesystem(Feature):
217
"""Is the filesystem UTF-8?"""
220
if osutils._fs_enc.upper() in ('UTF-8', 'UTF8'):
224
UTF8Filesystem = _UTF8Filesystem()
227
class _BreakinFeature(Feature):
228
"""Does this platform support the breakin feature?"""
231
from bzrlib import breakin
232
if breakin.determine_signal() is None:
234
if sys.platform == 'win32':
235
# Windows doesn't have os.kill, and we catch the SIGBREAK signal.
236
# We trigger SIGBREAK via a Console api so we need ctypes to
237
# access the function
244
def feature_name(self):
245
return "SIGQUIT or SIGBREAK w/ctypes on win32"
248
BreakinFeature = _BreakinFeature()
251
class _CaseInsCasePresFilenameFeature(Feature):
252
"""Is the file-system case insensitive, but case-preserving?"""
255
fileno, name = tempfile.mkstemp(prefix='MixedCase')
257
# first check truly case-preserving for created files, then check
258
# case insensitive when opening existing files.
259
name = osutils.normpath(name)
260
base, rel = osutils.split(name)
261
found_rel = osutils.canonical_relpath(base, name)
262
return (found_rel == rel
263
and os.path.isfile(name.upper())
264
and os.path.isfile(name.lower()))
269
def feature_name(self):
270
return "case-insensitive case-preserving filesystem"
272
CaseInsCasePresFilenameFeature = _CaseInsCasePresFilenameFeature()
275
class _CaseInsensitiveFilesystemFeature(Feature):
276
"""Check if underlying filesystem is case-insensitive but *not* case
279
# Note that on Windows, Cygwin, MacOS etc, the file-systems are far
280
# more likely to be case preserving, so this case is rare.
283
if CaseInsCasePresFilenameFeature.available():
286
if tests.TestCaseWithMemoryTransport.TEST_ROOT is None:
287
root = osutils.mkdtemp(prefix='testbzr-', suffix='.tmp')
288
tests.TestCaseWithMemoryTransport.TEST_ROOT = root
290
root = tests.TestCaseWithMemoryTransport.TEST_ROOT
291
tdir = osutils.mkdtemp(prefix='case-sensitive-probe-', suffix='',
293
name_a = osutils.pathjoin(tdir, 'a')
294
name_A = osutils.pathjoin(tdir, 'A')
296
result = osutils.isdir(name_A)
297
tests._rmtree_temp_dir(tdir)
300
def feature_name(self):
301
return 'case-insensitive filesystem'
303
CaseInsensitiveFilesystemFeature = _CaseInsensitiveFilesystemFeature()
306
class _CaseSensitiveFilesystemFeature(Feature):
309
if CaseInsCasePresFilenameFeature.available():
311
elif CaseInsensitiveFilesystemFeature.available():
316
def feature_name(self):
317
return 'case-sensitive filesystem'
319
# new coding style is for feature instances to be lowercase
320
case_sensitive_filesystem_feature = _CaseSensitiveFilesystemFeature()
323
class _NotRunningAsRoot(Feature):
28
class _NotRunningAsRoot(tests.Feature):
403
127
bash_feature = ExecutableFeature('bash')
404
128
sed_feature = ExecutableFeature('sed')
405
129
diff_feature = ExecutableFeature('diff')
408
class _PosixPermissionsFeature(Feature):
412
# Create temporary file and check if specified perms are
414
write_perms = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
415
f = tempfile.mkstemp(prefix='bzr_perms_chk_')
418
os.chmod(name, write_perms)
420
read_perms = os.stat(name).st_mode & 0777
422
return (write_perms == read_perms)
424
return (os.name == 'posix') and has_perms()
426
def feature_name(self):
427
return 'POSIX permissions support'
430
posix_permissions_feature = _PosixPermissionsFeature()
433
class _StraceFeature(Feature):
437
proc = subprocess.Popen(['strace'],
438
stderr=subprocess.PIPE,
439
stdout=subprocess.PIPE)
443
if e.errno == errno.ENOENT:
444
# strace is not installed
449
def feature_name(self):
453
strace_feature = _StraceFeature()
456
class _AttribFeature(Feature):
459
if (sys.platform not in ('cygwin', 'win32')):
462
proc = subprocess.Popen(['attrib', '.'], stdout=subprocess.PIPE)
465
return (0 == proc.wait())
467
def feature_name(self):
468
return 'attrib Windows command-line tool'
471
AttribFeature = _AttribFeature()
474
class Win32Feature(Feature):
475
"""Feature testing whether we're running selftest on Windows
476
or Windows-like platform.
480
return sys.platform == 'win32'
482
def feature_name(self):
483
return "win32 platform"
486
win32_feature = Win32Feature()
489
for name in ['HTTPServerFeature',
490
'HTTPSServerFeature', 'SymlinkFeature', 'HardlinkFeature',
491
'OsFifoFeature', 'UnicodeFilenameFeature',
492
'ByteStringNamedFilesystem', 'UTF8Filesystem',
493
'BreakinFeature', 'CaseInsCasePresFilenameFeature',
494
'CaseInsensitiveFilesystemFeature', 'case_sensitive_filesystem_feature',
495
'posix_permissions_feature',
497
setattr(tests, name, _CompatabilityThunkFeature(
498
symbol_versioning.deprecated_in((2, 5, 0)),
499
'bzrlib.tests', name,
500
name, 'bzrlib.tests.features'))
503
for (old_name, new_name) in [
504
('UnicodeFilename', 'UnicodeFilenameFeature'),
506
setattr(tests, name, _CompatabilityThunkFeature(
507
symbol_versioning.deprecated_in((2, 5, 0)),
508
'bzrlib.tests', old_name,
509
new_name, 'bzrlib.tests.features'))