/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/features.py

  • Committer: Martin Pool
  • Date: 2011-11-29 00:50:36 UTC
  • mto: This revision was merged to the branch mainline in revision 6320.
  • Revision ID: mbp@canonical.com-20111129005036-1vopao4wm0yo9ekn
Slight cleanup of TimeoutFixture

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2009, 2010 Canonical Ltd
 
1
# Copyright (C) 2009, 2010, 2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
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' to optionally run tests.
 
18
"""
 
19
 
17
20
import os
 
21
import subprocess
18
22
import stat
19
 
 
20
 
from bzrlib import tests
21
 
from bzrlib.symbol_versioning import deprecated_in
22
 
 
23
 
 
24
 
apport = tests.ModuleAvailableFeature('apport')
25
 
paramiko = tests.ModuleAvailableFeature('paramiko')
26
 
pycurl = tests.ModuleAvailableFeature('pycurl')
27
 
subunit = tests.ModuleAvailableFeature('subunit')
28
 
 
29
 
 
30
 
class _PosixPermissionsFeature(tests.Feature):
 
23
import sys
 
24
import tempfile
 
25
 
 
26
from bzrlib import (
 
27
    osutils,
 
28
    symbol_versioning,
 
29
    )
 
30
 
 
31
 
 
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
            # Import the new feature and use it as a replacement for the
 
145
            # deprecated one.
 
146
            self._feature = pyutils.get_named_object(
 
147
                self._replacement_module, self._replacement_name)
 
148
 
 
149
    def _probe(self):
 
150
        self._ensure()
 
151
        return self._feature._probe()
 
152
 
 
153
 
 
154
class ModuleAvailableFeature(Feature):
 
155
    """This is a feature than describes a module we want to be available.
 
156
 
 
157
    Declare the name of the module in __init__(), and then after probing, the
 
158
    module will be available as 'self.module'.
 
159
 
 
160
    :ivar module: The module if it is available, else None.
 
161
    """
 
162
 
 
163
    def __init__(self, module_name):
 
164
        super(ModuleAvailableFeature, self).__init__()
 
165
        self.module_name = module_name
 
166
 
 
167
    def _probe(self):
 
168
        sentinel = object()
 
169
        module = sys.modules.get(self.module_name, sentinel)
 
170
        if module is sentinel:
 
171
            try:
 
172
                self._module = __import__(self.module_name, {}, {}, [''])
 
173
                return True
 
174
            except ImportError:
 
175
                return False
 
176
        else:
 
177
            self._module = module
 
178
            return True
 
179
 
 
180
    @property
 
181
    def module(self):
 
182
        if self.available():
 
183
            return self._module
 
184
        return None
 
185
 
 
186
    def feature_name(self):
 
187
        return self.module_name
 
188
 
 
189
 
 
190
class _HTTPSServerFeature(Feature):
 
191
    """Some tests want an https Server, check if one is available.
 
192
 
 
193
    Right now, the only way this is available is under python2.6 which provides
 
194
    an ssl module.
 
195
    """
 
196
 
 
197
    def _probe(self):
 
198
        try:
 
199
            import ssl
 
200
            return True
 
201
        except ImportError:
 
202
            return False
 
203
 
 
204
    def feature_name(self):
 
205
        return 'HTTPSServer'
 
206
 
 
207
 
 
208
HTTPSServerFeature = _HTTPSServerFeature()
 
209
 
 
210
 
 
211
class _ByteStringNamedFilesystem(Feature):
 
212
    """Is the filesystem based on bytes?"""
 
213
 
 
214
    def _probe(self):
 
215
        if os.name == "posix":
 
216
            return True
 
217
        return False
 
218
 
 
219
ByteStringNamedFilesystem = _ByteStringNamedFilesystem()
 
220
 
 
221
 
 
222
class _UTF8Filesystem(Feature):
 
223
    """Is the filesystem UTF-8?"""
 
224
 
 
225
    def _probe(self):
 
226
        if osutils._fs_enc.upper() in ('UTF-8', 'UTF8'):
 
227
            return True
 
228
        return False
 
229
 
 
230
UTF8Filesystem = _UTF8Filesystem()
 
231
 
 
232
 
 
233
class _BreakinFeature(Feature):
 
234
    """Does this platform support the breakin feature?"""
 
235
 
 
236
    def _probe(self):
 
237
        from bzrlib import breakin
 
238
        if breakin.determine_signal() is None:
 
239
            return False
 
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
 
244
            try:
 
245
                import ctypes
 
246
            except OSError:
 
247
                return False
 
248
        return True
 
249
 
 
250
    def feature_name(self):
 
251
        return "SIGQUIT or SIGBREAK w/ctypes on win32"
 
252
 
 
253
 
 
254
BreakinFeature = _BreakinFeature()
 
255
 
 
256
 
 
257
class _CaseInsCasePresFilenameFeature(Feature):
 
258
    """Is the file-system case insensitive, but case-preserving?"""
 
259
 
 
260
    def _probe(self):
 
261
        fileno, name = tempfile.mkstemp(prefix='MixedCase')
 
262
        try:
 
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()))
 
271
        finally:
 
272
            os.close(fileno)
 
273
            os.remove(name)
 
274
 
 
275
    def feature_name(self):
 
276
        return "case-insensitive case-preserving filesystem"
 
277
 
 
278
CaseInsCasePresFilenameFeature = _CaseInsCasePresFilenameFeature()
 
279
 
 
280
 
 
281
class _CaseInsensitiveFilesystemFeature(Feature):
 
282
    """Check if underlying filesystem is case-insensitive but *not* case
 
283
    preserving.
 
284
    """
 
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.
 
287
 
 
288
    def _probe(self):
 
289
        if CaseInsCasePresFilenameFeature.available():
 
290
            return False
 
291
 
 
292
        from bzrlib import tests
 
293
 
 
294
        if tests.TestCaseWithMemoryTransport.TEST_ROOT is None:
 
295
            root = osutils.mkdtemp(prefix='testbzr-', suffix='.tmp')
 
296
            tests.TestCaseWithMemoryTransport.TEST_ROOT = root
 
297
        else:
 
298
            root = tests.TestCaseWithMemoryTransport.TEST_ROOT
 
299
        tdir = osutils.mkdtemp(prefix='case-sensitive-probe-', suffix='',
 
300
            dir=root)
 
301
        name_a = osutils.pathjoin(tdir, 'a')
 
302
        name_A = osutils.pathjoin(tdir, 'A')
 
303
        os.mkdir(name_a)
 
304
        result = osutils.isdir(name_A)
 
305
        tests._rmtree_temp_dir(tdir)
 
306
        return result
 
307
 
 
308
    def feature_name(self):
 
309
        return 'case-insensitive filesystem'
 
310
 
 
311
CaseInsensitiveFilesystemFeature = _CaseInsensitiveFilesystemFeature()
 
312
 
 
313
 
 
314
class _CaseSensitiveFilesystemFeature(Feature):
 
315
 
 
316
    def _probe(self):
 
317
        if CaseInsCasePresFilenameFeature.available():
 
318
            return False
 
319
        elif CaseInsensitiveFilesystemFeature.available():
 
320
            return False
 
321
        else:
 
322
            return True
 
323
 
 
324
    def feature_name(self):
 
325
        return 'case-sensitive filesystem'
 
326
 
 
327
# new coding style is for feature instances to be lowercase
 
328
case_sensitive_filesystem_feature = _CaseSensitiveFilesystemFeature()
 
329
 
 
330
 
 
331
class _NotRunningAsRoot(Feature):
 
332
 
 
333
    def _probe(self):
 
334
        try:
 
335
            uid = os.getuid()
 
336
        except AttributeError:
 
337
            # If there is no uid, chances are there is no root either
 
338
            return True
 
339
        return uid != 0
 
340
 
 
341
    def feature_name(self):
 
342
        return 'Not running as root'
 
343
 
 
344
 
 
345
not_running_as_root = _NotRunningAsRoot()
 
346
 
 
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')
 
357
 
 
358
compiled_patiencediff_feature = ModuleAvailableFeature(
 
359
    'bzrlib._patiencediff_c')
 
360
lsprof_feature = ModuleAvailableFeature('bzrlib.lsprof')
 
361
 
 
362
 
 
363
class _BackslashDirSeparatorFeature(Feature):
 
364
 
 
365
    def _probe(self):
 
366
        try:
 
367
            os.lstat(os.getcwd() + '\\')
 
368
        except OSError:
 
369
            return False
 
370
        else:
 
371
            return True
 
372
 
 
373
    def feature_name(self):
 
374
        return "Filesystem treats '\\' as a directory separator."
 
375
 
 
376
backslashdir_feature = _BackslashDirSeparatorFeature()
 
377
 
 
378
 
 
379
class _ChownFeature(Feature):
 
380
    """os.chown is supported"""
 
381
 
 
382
    def _probe(self):
 
383
        return os.name == 'posix' and hasattr(os, 'chown')
 
384
 
 
385
chown_feature = _ChownFeature()
 
386
 
 
387
 
 
388
class ExecutableFeature(Feature):
 
389
    """Feature testing whether an executable of a given name is on the PATH."""
 
390
 
 
391
    def __init__(self, name):
 
392
        super(ExecutableFeature, self).__init__()
 
393
        self.name = name
 
394
        self._path = None
 
395
 
 
396
    @property
 
397
    def path(self):
 
398
        # This is a property, so accessing path ensures _probe was called
 
399
        self.available()
 
400
        return self._path
 
401
 
 
402
    def _probe(self):
 
403
        self._path = osutils.find_executable_on_path(self.name)
 
404
        return self._path is not None
 
405
 
 
406
    def feature_name(self):
 
407
        return '%s executable' % self.name
 
408
 
 
409
 
 
410
bash_feature = ExecutableFeature('bash')
 
411
sed_feature = ExecutableFeature('sed')
 
412
diff_feature = ExecutableFeature('diff')
 
413
 
 
414
 
 
415
class _PosixPermissionsFeature(Feature):
31
416
 
32
417
    def _probe(self):
33
418
        def has_perms():
34
 
            # create temporary file and check if specified perms are maintained.
35
 
            import tempfile
36
 
 
 
419
            # Create temporary file and check if specified perms are
 
420
            # maintained.
37
421
            write_perms = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
38
422
            f = tempfile.mkstemp(prefix='bzr_perms_chk_')
39
423
            fd, name = f
53
437
posix_permissions_feature = _PosixPermissionsFeature()
54
438
 
55
439
 
56
 
class _ChownFeature(tests.Feature):
57
 
    """os.chown is supported"""
58
 
 
59
 
    def _probe(self):
60
 
        return os.name == 'posix' and hasattr(os, 'chown')
61
 
 
62
 
chown_feature = _ChownFeature()
63
 
 
 
440
class _StraceFeature(Feature):
 
441
 
 
442
    def _probe(self):
 
443
        try:
 
444
            proc = subprocess.Popen(['strace'],
 
445
                stderr=subprocess.PIPE,
 
446
                stdout=subprocess.PIPE)
 
447
            proc.communicate()
 
448
            return True
 
449
        except OSError, e:
 
450
            if e.errno == errno.ENOENT:
 
451
                # strace is not installed
 
452
                return False
 
453
            else:
 
454
                raise
 
455
 
 
456
    def feature_name(self):
 
457
        return 'strace'
 
458
 
 
459
 
 
460
strace_feature = _StraceFeature()
 
461
 
 
462
 
 
463
class _AttribFeature(Feature):
 
464
 
 
465
    def _probe(self):
 
466
        if (sys.platform not in ('cygwin', 'win32')):
 
467
            return False
 
468
        try:
 
469
            proc = subprocess.Popen(['attrib', '.'], stdout=subprocess.PIPE)
 
470
        except OSError, e:
 
471
            return False
 
472
        return (0 == proc.wait())
 
473
 
 
474
    def feature_name(self):
 
475
        return 'attrib Windows command-line tool'
 
476
 
 
477
 
 
478
AttribFeature = _AttribFeature()
 
479
 
 
480
 
 
481
class Win32Feature(Feature):
 
482
    """Feature testing whether we're running selftest on Windows
 
483
    or Windows-like platform.
 
484
    """
 
485
 
 
486
    def _probe(self):
 
487
        return sys.platform == 'win32'
 
488
 
 
489
    def feature_name(self):
 
490
        return "win32 platform"
 
491
 
 
492
 
 
493
win32_feature = Win32Feature()