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.
22
from bzrlib import tests
23
from bzrlib.symbol_versioning import deprecated_in
26
apport = tests.ModuleAvailableFeature('apport')
27
paramiko = tests.ModuleAvailableFeature('paramiko')
28
pycurl = tests.ModuleAvailableFeature('pycurl')
29
pywintypes = tests.ModuleAvailableFeature('pywintypes')
30
subunit = tests.ModuleAvailableFeature('subunit')
31
sphinx = tests.ModuleAvailableFeature('sphinx')
34
class _BackslashDirSeparatorFeature(tests.Feature):
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,
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
from bzrlib import tests
295
if tests.TestCaseWithMemoryTransport.TEST_ROOT is None:
296
root = osutils.mkdtemp(prefix='testbzr-', suffix='.tmp')
297
tests.TestCaseWithMemoryTransport.TEST_ROOT = root
299
root = tests.TestCaseWithMemoryTransport.TEST_ROOT
300
tdir = osutils.mkdtemp(prefix='case-sensitive-probe-', suffix='',
302
name_a = osutils.pathjoin(tdir, 'a')
303
name_A = osutils.pathjoin(tdir, 'A')
305
result = osutils.isdir(name_A)
306
tests._rmtree_temp_dir(tdir)
309
def feature_name(self):
310
return 'case-insensitive filesystem'
312
CaseInsensitiveFilesystemFeature = _CaseInsensitiveFilesystemFeature()
315
class _CaseSensitiveFilesystemFeature(Feature):
318
if CaseInsCasePresFilenameFeature.available():
320
elif CaseInsensitiveFilesystemFeature.available():
325
def feature_name(self):
326
return 'case-sensitive filesystem'
328
# new coding style is for feature instances to be lowercase
329
case_sensitive_filesystem_feature = _CaseSensitiveFilesystemFeature()
332
class _NotRunningAsRoot(Feature):
337
except AttributeError:
338
# If there is no uid, chances are there is no root either
342
def feature_name(self):
343
return 'Not running as root'
346
not_running_as_root = _NotRunningAsRoot()
348
apport = ModuleAvailableFeature('apport')
349
gpgme = ModuleAvailableFeature('gpgme')
350
lzma = ModuleAvailableFeature('lzma')
351
meliae = ModuleAvailableFeature('meliae.scanner')
352
paramiko = ModuleAvailableFeature('paramiko')
353
pycurl = ModuleAvailableFeature('pycurl')
354
pywintypes = ModuleAvailableFeature('pywintypes')
355
sphinx = ModuleAvailableFeature('sphinx')
356
subunit = ModuleAvailableFeature('subunit')
357
testtools = ModuleAvailableFeature('testtools')
359
compiled_patiencediff_feature = ModuleAvailableFeature(
360
'bzrlib._patiencediff_c')
361
lsprof_feature = ModuleAvailableFeature('bzrlib.lsprof')
364
class _BackslashDirSeparatorFeature(Feature):
100
path = os.environ.get('PATH')
103
for d in path.split(os.pathsep):
105
f = os.path.join(d, self.name)
106
if os.access(f, os.X_OK):
404
self._path = osutils.find_executable_on_path(self.name)
405
return self._path is not None
111
407
def feature_name(self):
112
408
return '%s executable' % self.name
115
411
bash_feature = ExecutableFeature('bash')
412
diff_feature = ExecutableFeature('diff')
116
413
sed_feature = ExecutableFeature('sed')
117
diff_feature = ExecutableFeature('diff')
414
msgmerge_feature = ExecutableFeature('msgmerge')
417
class _PosixPermissionsFeature(Feature):
421
# Create temporary file and check if specified perms are
423
write_perms = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
424
f = tempfile.mkstemp(prefix='bzr_perms_chk_')
427
osutils.chmod_if_possible(name, write_perms)
429
read_perms = os.stat(name).st_mode & 0777
431
return (write_perms == read_perms)
433
return (os.name == 'posix') and has_perms()
435
def feature_name(self):
436
return 'POSIX permissions support'
439
posix_permissions_feature = _PosixPermissionsFeature()
442
class _StraceFeature(Feature):
446
proc = subprocess.Popen(['strace'],
447
stderr=subprocess.PIPE,
448
stdout=subprocess.PIPE)
452
if e.errno == errno.ENOENT:
453
# strace is not installed
458
def feature_name(self):
462
strace_feature = _StraceFeature()
465
class _AttribFeature(Feature):
468
if (sys.platform not in ('cygwin', 'win32')):
471
proc = subprocess.Popen(['attrib', '.'], stdout=subprocess.PIPE)
474
return (0 == proc.wait())
476
def feature_name(self):
477
return 'attrib Windows command-line tool'
480
AttribFeature = _AttribFeature()
483
class Win32Feature(Feature):
484
"""Feature testing whether we're running selftest on Windows
485
or Windows-like platform.
489
return sys.platform == 'win32'
491
def feature_name(self):
492
return "win32 platform"
495
win32_feature = Win32Feature()