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.
20
from __future__ import absolute_import
20
from bzrlib import tests
21
from bzrlib.symbol_versioning import deprecated_in
24
apport = tests.ModuleAvailableFeature('apport')
25
paramiko = tests.ModuleAvailableFeature('paramiko')
26
pycurl = tests.ModuleAvailableFeature('pycurl')
27
subunit = tests.ModuleAvailableFeature('subunit')
30
class _PosixPermissionsFeature(tests.Feature):
35
class Feature(object):
36
"""An operating system Feature."""
39
self._available = None
42
"""Is the feature available?
44
:return: True if the feature is available.
46
if self._available is None:
47
self._available = self._probe()
48
return self._available
51
"""Implement this method in concrete features.
53
:return: True if the feature is available.
55
raise NotImplementedError
58
if getattr(self, 'feature_name', None):
59
return self.feature_name()
60
return self.__class__.__name__
63
class _SymlinkFeature(Feature):
66
return osutils.has_symlinks()
68
def feature_name(self):
71
SymlinkFeature = _SymlinkFeature()
74
class _HardlinkFeature(Feature):
77
return osutils.has_hardlinks()
79
def feature_name(self):
82
HardlinkFeature = _HardlinkFeature()
85
class _OsFifoFeature(Feature):
88
return getattr(os, 'mkfifo', None)
90
def feature_name(self):
91
return 'filesystem fifos'
93
OsFifoFeature = _OsFifoFeature()
96
class _UnicodeFilenameFeature(Feature):
97
"""Does the filesystem support Unicode filenames?"""
101
# Check for character combinations unlikely to be covered by any
102
# single non-unicode encoding. We use the characters
103
# - greek small letter alpha (U+03B1) and
104
# - braille pattern dots-123456 (U+283F).
105
os.stat(u'\u03b1\u283f')
106
except UnicodeEncodeError:
108
except (IOError, OSError):
109
# The filesystem allows the Unicode filename but the file doesn't
113
# The filesystem allows the Unicode filename and the file exists,
117
UnicodeFilenameFeature = _UnicodeFilenameFeature()
120
class _CompatabilityThunkFeature(Feature):
121
"""This feature is just a thunk to another feature.
123
It issues a deprecation warning if it is accessed, to let you know that you
124
should really use a different feature.
127
def __init__(self, dep_version, module, name,
128
replacement_name, replacement_module=None):
129
super(_CompatabilityThunkFeature, self).__init__()
130
self._module = module
131
if replacement_module is None:
132
replacement_module = module
133
self._replacement_module = replacement_module
135
self._replacement_name = replacement_name
136
self._dep_version = dep_version
140
if self._feature is None:
141
from breezy import pyutils
142
depr_msg = self._dep_version % ('%s.%s'
143
% (self._module, self._name))
144
use_msg = ' Use %s.%s instead.' % (self._replacement_module,
145
self._replacement_name)
146
symbol_versioning.warn(depr_msg + use_msg, DeprecationWarning,
148
# Import the new feature and use it as a replacement for the
150
self._feature = pyutils.get_named_object(
151
self._replacement_module, self._replacement_name)
155
return self._feature._probe()
158
class ModuleAvailableFeature(Feature):
159
"""This is a feature than describes a module we want to be available.
161
Declare the name of the module in __init__(), and then after probing, the
162
module will be available as 'self.module'.
164
:ivar module: The module if it is available, else None.
167
def __init__(self, module_name, ignore_warnings=None):
168
super(ModuleAvailableFeature, self).__init__()
169
self.module_name = module_name
170
if ignore_warnings is None:
172
self.ignore_warnings = ignore_warnings
176
module = sys.modules.get(self.module_name, sentinel)
177
if module is sentinel:
178
with warnings.catch_warnings():
179
for warning_category in self.ignore_warnings:
180
warnings.simplefilter('ignore', warning_category)
182
self._module = __import__(self.module_name)
187
self._module = module
196
def feature_name(self):
197
return self.module_name
200
class PluginLoadedFeature(Feature):
201
"""Check whether a plugin with specific name is loaded.
203
This is different from ModuleAvailableFeature, because
204
plugins can be available but explicitly disabled
205
(e.g. through BRZ_DISABLE_PLUGINS=blah).
207
:ivar plugin_name: The name of the plugin
210
def __init__(self, plugin_name):
211
super(PluginLoadedFeature, self).__init__()
212
self.plugin_name = plugin_name
215
from breezy.plugin import get_loaded_plugin
216
return (get_loaded_plugin(self.plugin_name) is not None)
220
from breezy.plugin import get_loaded_plugin
221
return get_loaded_plugin(self.plugin_name)
223
def feature_name(self):
224
return '%s plugin' % self.plugin_name
227
class _HTTPSServerFeature(Feature):
228
"""Some tests want an https Server, check if one is available.
230
Right now, the only way this is available is under python2.6 which provides
241
def feature_name(self):
245
HTTPSServerFeature = _HTTPSServerFeature()
248
class _ByteStringNamedFilesystem(Feature):
249
"""Is the filesystem based on bytes?"""
252
if os.name == "posix":
256
ByteStringNamedFilesystem = _ByteStringNamedFilesystem()
259
class _UTF8Filesystem(Feature):
260
"""Is the filesystem UTF-8?"""
263
if osutils._fs_enc.upper() in ('UTF-8', 'UTF8'):
267
UTF8Filesystem = _UTF8Filesystem()
270
class _BreakinFeature(Feature):
271
"""Does this platform support the breakin feature?"""
274
from breezy import breakin
275
if breakin.determine_signal() is None:
277
if sys.platform == 'win32':
278
# Windows doesn't have os.kill, and we catch the SIGBREAK signal.
279
# We trigger SIGBREAK via a Console api so we need ctypes to
280
# access the function
287
def feature_name(self):
288
return "SIGQUIT or SIGBREAK w/ctypes on win32"
291
BreakinFeature = _BreakinFeature()
294
class _CaseInsCasePresFilenameFeature(Feature):
295
"""Is the file-system case insensitive, but case-preserving?"""
298
fileno, name = tempfile.mkstemp(prefix='MixedCase')
300
# first check truly case-preserving for created files, then check
301
# case insensitive when opening existing files.
302
name = osutils.normpath(name)
303
base, rel = osutils.split(name)
304
found_rel = osutils.canonical_relpath(base, name)
305
return (found_rel == rel
306
and os.path.isfile(name.upper())
307
and os.path.isfile(name.lower()))
312
def feature_name(self):
313
return "case-insensitive case-preserving filesystem"
315
CaseInsCasePresFilenameFeature = _CaseInsCasePresFilenameFeature()
318
class _CaseInsensitiveFilesystemFeature(Feature):
319
"""Check if underlying filesystem is case-insensitive but *not* case
322
# Note that on Windows, Cygwin, MacOS etc, the file-systems are far
323
# more likely to be case preserving, so this case is rare.
326
if CaseInsCasePresFilenameFeature.available():
329
from breezy import tests
331
if tests.TestCaseWithMemoryTransport.TEST_ROOT is None:
332
root = osutils.mkdtemp(prefix='testbzr-', suffix='.tmp')
333
tests.TestCaseWithMemoryTransport.TEST_ROOT = root
335
root = tests.TestCaseWithMemoryTransport.TEST_ROOT
336
tdir = osutils.mkdtemp(prefix='case-sensitive-probe-', suffix='',
338
name_a = osutils.pathjoin(tdir, 'a')
339
name_A = osutils.pathjoin(tdir, 'A')
341
result = osutils.isdir(name_A)
342
tests._rmtree_temp_dir(tdir)
345
def feature_name(self):
346
return 'case-insensitive filesystem'
348
CaseInsensitiveFilesystemFeature = _CaseInsensitiveFilesystemFeature()
351
class _CaseSensitiveFilesystemFeature(Feature):
354
if CaseInsCasePresFilenameFeature.available():
356
elif CaseInsensitiveFilesystemFeature.available():
361
def feature_name(self):
362
return 'case-sensitive filesystem'
364
# new coding style is for feature instances to be lowercase
365
case_sensitive_filesystem_feature = _CaseSensitiveFilesystemFeature()
368
class _NotRunningAsRoot(Feature):
373
except AttributeError:
374
# If there is no uid, chances are there is no root either
378
def feature_name(self):
379
return 'Not running as root'
382
not_running_as_root = _NotRunningAsRoot()
384
# Apport uses deprecated imp module on python3.
385
apport = ModuleAvailableFeature(
387
ignore_warnings=[DeprecationWarning, PendingDeprecationWarning])
388
gpg = ModuleAvailableFeature('gpg')
389
lzma = ModuleAvailableFeature('lzma')
390
meliae = ModuleAvailableFeature('meliae.scanner')
391
paramiko = ModuleAvailableFeature('paramiko')
392
pywintypes = ModuleAvailableFeature('pywintypes')
393
subunit = ModuleAvailableFeature('subunit')
394
testtools = ModuleAvailableFeature('testtools')
396
compiled_patiencediff_feature = ModuleAvailableFeature(
397
'breezy._patiencediff_c')
398
lsprof_feature = ModuleAvailableFeature('breezy.lsprof')
401
class _BackslashDirSeparatorFeature(Feature):
405
os.lstat(os.getcwd() + '\\')
411
def feature_name(self):
412
return "Filesystem treats '\\' as a directory separator."
414
backslashdir_feature = _BackslashDirSeparatorFeature()
417
class _ChownFeature(Feature):
418
"""os.chown is supported"""
421
return os.name == 'posix' and hasattr(os, 'chown')
423
chown_feature = _ChownFeature()
426
class ExecutableFeature(Feature):
427
"""Feature testing whether an executable of a given name is on the PATH."""
429
def __init__(self, name):
430
super(ExecutableFeature, self).__init__()
436
# This is a property, so accessing path ensures _probe was called
441
self._path = osutils.find_executable_on_path(self.name)
442
return self._path is not None
444
def feature_name(self):
445
return '%s executable' % self.name
448
bash_feature = ExecutableFeature('bash')
449
diff_feature = ExecutableFeature('diff')
450
sed_feature = ExecutableFeature('sed')
451
msgmerge_feature = ExecutableFeature('msgmerge')
454
class _PosixPermissionsFeature(Feature):
34
# create temporary file and check if specified perms are maintained.
458
# Create temporary file and check if specified perms are
37
460
write_perms = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
38
461
f = tempfile.mkstemp(prefix='bzr_perms_chk_')
41
os.chmod(name, write_perms)
464
osutils.chmod_if_possible(name, write_perms)
43
read_perms = os.stat(name).st_mode & 0777
466
read_perms = os.stat(name).st_mode & 0o777
45
468
return (write_perms == read_perms)