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):
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
module = sys.modules.get(self.module_name, sentinel)
170
if module is sentinel:
172
self._module = __import__(self.module_name, {}, {}, [''])
177
self._module = module
186
def feature_name(self):
187
return self.module_name
190
class _HTTPSServerFeature(Feature):
191
"""Some tests want an https Server, check if one is available.
193
Right now, the only way this is available is under python2.6 which provides
204
def feature_name(self):
208
HTTPSServerFeature = _HTTPSServerFeature()
211
class _ByteStringNamedFilesystem(Feature):
212
"""Is the filesystem based on bytes?"""
215
if os.name == "posix":
219
ByteStringNamedFilesystem = _ByteStringNamedFilesystem()
222
class _UTF8Filesystem(Feature):
223
"""Is the filesystem UTF-8?"""
226
if osutils._fs_enc.upper() in ('UTF-8', 'UTF8'):
230
UTF8Filesystem = _UTF8Filesystem()
233
class _BreakinFeature(Feature):
234
"""Does this platform support the breakin feature?"""
237
from bzrlib import breakin
238
if breakin.determine_signal() is None:
240
if sys.platform == 'win32':
241
# Windows doesn't have os.kill, and we catch the SIGBREAK signal.
242
# We trigger SIGBREAK via a Console api so we need ctypes to
243
# access the function
250
def feature_name(self):
251
return "SIGQUIT or SIGBREAK w/ctypes on win32"
254
BreakinFeature = _BreakinFeature()
257
class _CaseInsCasePresFilenameFeature(Feature):
258
"""Is the file-system case insensitive, but case-preserving?"""
261
fileno, name = tempfile.mkstemp(prefix='MixedCase')
263
# first check truly case-preserving for created files, then check
264
# case insensitive when opening existing files.
265
name = osutils.normpath(name)
266
base, rel = osutils.split(name)
267
found_rel = osutils.canonical_relpath(base, name)
268
return (found_rel == rel
269
and os.path.isfile(name.upper())
270
and os.path.isfile(name.lower()))
275
def feature_name(self):
276
return "case-insensitive case-preserving filesystem"
278
CaseInsCasePresFilenameFeature = _CaseInsCasePresFilenameFeature()
281
class _CaseInsensitiveFilesystemFeature(Feature):
282
"""Check if underlying filesystem is case-insensitive but *not* case
285
# Note that on Windows, Cygwin, MacOS etc, the file-systems are far
286
# more likely to be case preserving, so this case is rare.
289
if CaseInsCasePresFilenameFeature.available():
292
from bzrlib import tests
294
if tests.TestCaseWithMemoryTransport.TEST_ROOT is None:
295
root = osutils.mkdtemp(prefix='testbzr-', suffix='.tmp')
296
tests.TestCaseWithMemoryTransport.TEST_ROOT = root
298
root = tests.TestCaseWithMemoryTransport.TEST_ROOT
299
tdir = osutils.mkdtemp(prefix='case-sensitive-probe-', suffix='',
301
name_a = osutils.pathjoin(tdir, 'a')
302
name_A = osutils.pathjoin(tdir, 'A')
304
result = osutils.isdir(name_A)
305
tests._rmtree_temp_dir(tdir)
308
def feature_name(self):
309
return 'case-insensitive filesystem'
311
CaseInsensitiveFilesystemFeature = _CaseInsensitiveFilesystemFeature()
314
class _CaseSensitiveFilesystemFeature(Feature):
317
if CaseInsCasePresFilenameFeature.available():
319
elif CaseInsensitiveFilesystemFeature.available():
324
def feature_name(self):
325
return 'case-sensitive filesystem'
327
# new coding style is for feature instances to be lowercase
328
case_sensitive_filesystem_feature = _CaseSensitiveFilesystemFeature()
331
class _NotRunningAsRoot(Feature):
43
345
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):
347
apport = ModuleAvailableFeature('apport')
348
gpgme = ModuleAvailableFeature('gpgme')
349
lzma = ModuleAvailableFeature('lzma')
350
meliae = ModuleAvailableFeature('meliae.scanner')
351
paramiko = ModuleAvailableFeature('paramiko')
352
pycurl = ModuleAvailableFeature('pycurl')
353
pywintypes = ModuleAvailableFeature('pywintypes')
354
sphinx = ModuleAvailableFeature('sphinx')
355
subunit = ModuleAvailableFeature('subunit')
356
testtools = ModuleAvailableFeature('testtools')
358
compiled_patiencediff_feature = ModuleAvailableFeature(
359
'bzrlib._patiencediff_c')
360
lsprof_feature = ModuleAvailableFeature('bzrlib.lsprof')
363
class _BackslashDirSeparatorFeature(Feature):
132
412
diff_feature = ExecutableFeature('diff')
135
class Win32Feature(tests.Feature):
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):
136
482
"""Feature testing whether we're running selftest on Windows
137
483
or Windows-like platform.