~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/features.py

(gz) Remove bzrlib/util/elementtree/ package (Martin Packman)

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
16
16
 
17
 
"""A collection of commonly used 'Features' which bzrlib uses to skip tests."""
 
17
"""A collection of commonly used 'Features' to optionally run tests.
 
18
"""
18
19
 
19
20
import os
 
21
import subprocess
20
22
import stat
 
23
import sys
 
24
import tempfile
21
25
 
22
26
from bzrlib import (
23
27
    osutils,
24
 
    tests,
 
28
    symbol_versioning,
25
29
    )
26
30
 
27
31
 
28
 
class _NotRunningAsRoot(tests.Feature):
 
32
class Feature(object):
 
33
    """An operating system Feature."""
 
34
 
 
35
    def __init__(self):
 
36
        self._available = None
 
37
 
 
38
    def available(self):
 
39
        """Is the feature available?
 
40
 
 
41
        :return: True if the feature is available.
 
42
        """
 
43
        if self._available is None:
 
44
            self._available = self._probe()
 
45
        return self._available
 
46
 
 
47
    def _probe(self):
 
48
        """Implement this method in concrete features.
 
49
 
 
50
        :return: True if the feature is available.
 
51
        """
 
52
        raise NotImplementedError
 
53
 
 
54
    def __str__(self):
 
55
        if getattr(self, 'feature_name', None):
 
56
            return self.feature_name()
 
57
        return self.__class__.__name__
 
58
 
 
59
 
 
60
class _SymlinkFeature(Feature):
 
61
 
 
62
    def _probe(self):
 
63
        return osutils.has_symlinks()
 
64
 
 
65
    def feature_name(self):
 
66
        return 'symlinks'
 
67
 
 
68
SymlinkFeature = _SymlinkFeature()
 
69
 
 
70
 
 
71
class _HardlinkFeature(Feature):
 
72
 
 
73
    def _probe(self):
 
74
        return osutils.has_hardlinks()
 
75
 
 
76
    def feature_name(self):
 
77
        return 'hardlinks'
 
78
 
 
79
HardlinkFeature = _HardlinkFeature()
 
80
 
 
81
 
 
82
class _OsFifoFeature(Feature):
 
83
 
 
84
    def _probe(self):
 
85
        return getattr(os, 'mkfifo', None)
 
86
 
 
87
    def feature_name(self):
 
88
        return 'filesystem fifos'
 
89
 
 
90
OsFifoFeature = _OsFifoFeature()
 
91
 
 
92
 
 
93
class _UnicodeFilenameFeature(Feature):
 
94
    """Does the filesystem support Unicode filenames?"""
 
95
 
 
96
    def _probe(self):
 
97
        try:
 
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:
 
104
            return False
 
105
        except (IOError, OSError):
 
106
            # The filesystem allows the Unicode filename but the file doesn't
 
107
            # exist.
 
108
            return True
 
109
        else:
 
110
            # The filesystem allows the Unicode filename and the file exists,
 
111
            # for some reason.
 
112
            return True
 
113
 
 
114
UnicodeFilenameFeature = _UnicodeFilenameFeature()
 
115
 
 
116
 
 
117
class _CompatabilityThunkFeature(Feature):
 
118
    """This feature is just a thunk to another feature.
 
119
 
 
120
    It issues a deprecation warning if it is accessed, to let you know that you
 
121
    should really use a different feature.
 
122
    """
 
123
 
 
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
 
131
        self._name = name
 
132
        self._replacement_name = replacement_name
 
133
        self._dep_version = dep_version
 
134
        self._feature = None
 
135
 
 
136
    def _ensure(self):
 
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
                                   stacklevel=5)
 
145
            # Import the new feature and use it as a replacement for the
 
146
            # deprecated one.
 
147
            self._feature = pyutils.get_named_object(
 
148
                self._replacement_module, self._replacement_name)
 
149
 
 
150
    def _probe(self):
 
151
        self._ensure()
 
152
        return self._feature._probe()
 
153
 
 
154
 
 
155
class ModuleAvailableFeature(Feature):
 
156
    """This is a feature than describes a module we want to be available.
 
157
 
 
158
    Declare the name of the module in __init__(), and then after probing, the
 
159
    module will be available as 'self.module'.
 
160
 
 
161
    :ivar module: The module if it is available, else None.
 
162
    """
 
163
 
 
164
    def __init__(self, module_name):
 
165
        super(ModuleAvailableFeature, self).__init__()
 
166
        self.module_name = module_name
 
167
 
 
168
    def _probe(self):
 
169
        sentinel = object()
 
170
        module = sys.modules.get(self.module_name, sentinel)
 
171
        if module is sentinel:
 
172
            try:
 
173
                self._module = __import__(self.module_name, {}, {}, [''])
 
174
                return True
 
175
            except ImportError:
 
176
                return False
 
177
        else:
 
178
            self._module = module
 
179
            return True
 
180
 
 
181
    @property
 
182
    def module(self):
 
183
        if self.available():
 
184
            return self._module
 
185
        return None
 
186
 
 
187
    def feature_name(self):
 
188
        return self.module_name
 
189
 
 
190
 
 
191
class _HTTPSServerFeature(Feature):
 
192
    """Some tests want an https Server, check if one is available.
 
193
 
 
194
    Right now, the only way this is available is under python2.6 which provides
 
195
    an ssl module.
 
196
    """
 
197
 
 
198
    def _probe(self):
 
199
        try:
 
200
            import ssl
 
201
            return True
 
202
        except ImportError:
 
203
            return False
 
204
 
 
205
    def feature_name(self):
 
206
        return 'HTTPSServer'
 
207
 
 
208
 
 
209
HTTPSServerFeature = _HTTPSServerFeature()
 
210
 
 
211
 
 
212
class _ByteStringNamedFilesystem(Feature):
 
213
    """Is the filesystem based on bytes?"""
 
214
 
 
215
    def _probe(self):
 
216
        if os.name == "posix":
 
217
            return True
 
218
        return False
 
219
 
 
220
ByteStringNamedFilesystem = _ByteStringNamedFilesystem()
 
221
 
 
222
 
 
223
class _UTF8Filesystem(Feature):
 
224
    """Is the filesystem UTF-8?"""
 
225
 
 
226
    def _probe(self):
 
227
        if osutils._fs_enc.upper() in ('UTF-8', 'UTF8'):
 
228
            return True
 
229
        return False
 
230
 
 
231
UTF8Filesystem = _UTF8Filesystem()
 
232
 
 
233
 
 
234
class _BreakinFeature(Feature):
 
235
    """Does this platform support the breakin feature?"""
 
236
 
 
237
    def _probe(self):
 
238
        from bzrlib import breakin
 
239
        if breakin.determine_signal() is None:
 
240
            return False
 
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
 
245
            try:
 
246
                import ctypes
 
247
            except OSError:
 
248
                return False
 
249
        return True
 
250
 
 
251
    def feature_name(self):
 
252
        return "SIGQUIT or SIGBREAK w/ctypes on win32"
 
253
 
 
254
 
 
255
BreakinFeature = _BreakinFeature()
 
256
 
 
257
 
 
258
class _CaseInsCasePresFilenameFeature(Feature):
 
259
    """Is the file-system case insensitive, but case-preserving?"""
 
260
 
 
261
    def _probe(self):
 
262
        fileno, name = tempfile.mkstemp(prefix='MixedCase')
 
263
        try:
 
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()))
 
272
        finally:
 
273
            os.close(fileno)
 
274
            os.remove(name)
 
275
 
 
276
    def feature_name(self):
 
277
        return "case-insensitive case-preserving filesystem"
 
278
 
 
279
CaseInsCasePresFilenameFeature = _CaseInsCasePresFilenameFeature()
 
280
 
 
281
 
 
282
class _CaseInsensitiveFilesystemFeature(Feature):
 
283
    """Check if underlying filesystem is case-insensitive but *not* case
 
284
    preserving.
 
285
    """
 
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.
 
288
 
 
289
    def _probe(self):
 
290
        if CaseInsCasePresFilenameFeature.available():
 
291
            return False
 
292
 
 
293
        from bzrlib import tests
 
294
 
 
295
        if tests.TestCaseWithMemoryTransport.TEST_ROOT is None:
 
296
            root = osutils.mkdtemp(prefix='testbzr-', suffix='.tmp')
 
297
            tests.TestCaseWithMemoryTransport.TEST_ROOT = root
 
298
        else:
 
299
            root = tests.TestCaseWithMemoryTransport.TEST_ROOT
 
300
        tdir = osutils.mkdtemp(prefix='case-sensitive-probe-', suffix='',
 
301
            dir=root)
 
302
        name_a = osutils.pathjoin(tdir, 'a')
 
303
        name_A = osutils.pathjoin(tdir, 'A')
 
304
        os.mkdir(name_a)
 
305
        result = osutils.isdir(name_A)
 
306
        tests._rmtree_temp_dir(tdir)
 
307
        return result
 
308
 
 
309
    def feature_name(self):
 
310
        return 'case-insensitive filesystem'
 
311
 
 
312
CaseInsensitiveFilesystemFeature = _CaseInsensitiveFilesystemFeature()
 
313
 
 
314
 
 
315
class _CaseSensitiveFilesystemFeature(Feature):
 
316
 
 
317
    def _probe(self):
 
318
        if CaseInsCasePresFilenameFeature.available():
 
319
            return False
 
320
        elif CaseInsensitiveFilesystemFeature.available():
 
321
            return False
 
322
        else:
 
323
            return True
 
324
 
 
325
    def feature_name(self):
 
326
        return 'case-sensitive filesystem'
 
327
 
 
328
# new coding style is for feature instances to be lowercase
 
329
case_sensitive_filesystem_feature = _CaseSensitiveFilesystemFeature()
 
330
 
 
331
 
 
332
class _NotRunningAsRoot(Feature):
29
333
 
30
334
    def _probe(self):
31
335
        try:
41
345
 
42
346
not_running_as_root = _NotRunningAsRoot()
43
347
 
44
 
apport = tests.ModuleAvailableFeature('apport')
45
 
lzma = tests.ModuleAvailableFeature('lzma')
46
 
meliae = tests.ModuleAvailableFeature('meliae')
47
 
paramiko = tests.ModuleAvailableFeature('paramiko')
48
 
pycurl = tests.ModuleAvailableFeature('pycurl')
49
 
pywintypes = tests.ModuleAvailableFeature('pywintypes')
50
 
sphinx = tests.ModuleAvailableFeature('sphinx')
51
 
subunit = tests.ModuleAvailableFeature('subunit')
52
 
 
53
 
 
54
 
class _BackslashDirSeparatorFeature(tests.Feature):
 
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')
 
358
 
 
359
compiled_patiencediff_feature = ModuleAvailableFeature(
 
360
    'bzrlib._patiencediff_c')
 
361
lsprof_feature = ModuleAvailableFeature('bzrlib.lsprof')
 
362
 
 
363
 
 
364
class _BackslashDirSeparatorFeature(Feature):
55
365
 
56
366
    def _probe(self):
57
367
        try:
67
377
backslashdir_feature = _BackslashDirSeparatorFeature()
68
378
 
69
379
 
70
 
class _PosixPermissionsFeature(tests.Feature):
71
 
 
72
 
    def _probe(self):
73
 
        def has_perms():
74
 
            # create temporary file and check if specified perms are maintained.
75
 
            import tempfile
76
 
 
77
 
            write_perms = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
78
 
            f = tempfile.mkstemp(prefix='bzr_perms_chk_')
79
 
            fd, name = f
80
 
            os.close(fd)
81
 
            os.chmod(name, write_perms)
82
 
 
83
 
            read_perms = os.stat(name).st_mode & 0777
84
 
            os.unlink(name)
85
 
            return (write_perms == read_perms)
86
 
 
87
 
        return (os.name == 'posix') and has_perms()
88
 
 
89
 
    def feature_name(self):
90
 
        return 'POSIX permissions support'
91
 
 
92
 
 
93
 
posix_permissions_feature = _PosixPermissionsFeature()
94
 
 
95
 
 
96
 
class _ChownFeature(tests.Feature):
 
380
class _ChownFeature(Feature):
97
381
    """os.chown is supported"""
98
382
 
99
383
    def _probe(self):
102
386
chown_feature = _ChownFeature()
103
387
 
104
388
 
105
 
class ExecutableFeature(tests.Feature):
 
389
class ExecutableFeature(Feature):
106
390
    """Feature testing whether an executable of a given name is on the PATH."""
107
391
 
108
392
    def __init__(self, name):
125
409
 
126
410
 
127
411
bash_feature = ExecutableFeature('bash')
 
412
diff_feature = ExecutableFeature('diff')
128
413
sed_feature = ExecutableFeature('sed')
129
 
diff_feature = ExecutableFeature('diff')
 
414
msgmerge_feature = ExecutableFeature('msgmerge')
 
415
 
 
416
 
 
417
class _PosixPermissionsFeature(Feature):
 
418
 
 
419
    def _probe(self):
 
420
        def has_perms():
 
421
            # Create temporary file and check if specified perms are
 
422
            # maintained.
 
423
            write_perms = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
 
424
            f = tempfile.mkstemp(prefix='bzr_perms_chk_')
 
425
            fd, name = f
 
426
            os.close(fd)
 
427
            osutils.chmod_if_possible(name, write_perms)
 
428
 
 
429
            read_perms = os.stat(name).st_mode & 0777
 
430
            os.unlink(name)
 
431
            return (write_perms == read_perms)
 
432
 
 
433
        return (os.name == 'posix') and has_perms()
 
434
 
 
435
    def feature_name(self):
 
436
        return 'POSIX permissions support'
 
437
 
 
438
 
 
439
posix_permissions_feature = _PosixPermissionsFeature()
 
440
 
 
441
 
 
442
class _StraceFeature(Feature):
 
443
 
 
444
    def _probe(self):
 
445
        try:
 
446
            proc = subprocess.Popen(['strace'],
 
447
                stderr=subprocess.PIPE,
 
448
                stdout=subprocess.PIPE)
 
449
            proc.communicate()
 
450
            return True
 
451
        except OSError, e:
 
452
            if e.errno == errno.ENOENT:
 
453
                # strace is not installed
 
454
                return False
 
455
            else:
 
456
                raise
 
457
 
 
458
    def feature_name(self):
 
459
        return 'strace'
 
460
 
 
461
 
 
462
strace_feature = _StraceFeature()
 
463
 
 
464
 
 
465
class _AttribFeature(Feature):
 
466
 
 
467
    def _probe(self):
 
468
        if (sys.platform not in ('cygwin', 'win32')):
 
469
            return False
 
470
        try:
 
471
            proc = subprocess.Popen(['attrib', '.'], stdout=subprocess.PIPE)
 
472
        except OSError, e:
 
473
            return False
 
474
        return (0 == proc.wait())
 
475
 
 
476
    def feature_name(self):
 
477
        return 'attrib Windows command-line tool'
 
478
 
 
479
 
 
480
AttribFeature = _AttribFeature()
 
481
 
 
482
 
 
483
class Win32Feature(Feature):
 
484
    """Feature testing whether we're running selftest on Windows
 
485
    or Windows-like platform.
 
486
    """
 
487
 
 
488
    def _probe(self):
 
489
        return sys.platform == 'win32'
 
490
 
 
491
    def feature_name(self):
 
492
        return "win32 platform"
 
493
 
 
494
 
 
495
win32_feature = Win32Feature()