~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/features.py

  • Committer: Martin
  • Date: 2011-05-21 16:29:38 UTC
  • mto: This revision was merged to the branch mainline in revision 5907.
  • Revision ID: gzlist@googlemail.com-20110521162938-1vrw3hp0197l3vrl
Add tests for non-ascii conflict serialisation

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' to optionally run tests.
18
 
"""
 
17
"""A collection of commonly used 'Features' which bzrlib uses to skip tests."""
19
18
 
20
19
import os
21
20
import stat
22
 
import sys
23
 
import tempfile
24
21
 
25
22
from bzrlib import (
26
23
    osutils,
27
 
    symbol_versioning,
28
24
    tests,
29
25
    )
30
26
 
31
27
 
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
 
        try:
169
 
            self._module = __import__(self.module_name, {}, {}, [''])
170
 
            return True
171
 
        except ImportError:
172
 
            return False
173
 
 
174
 
    @property
175
 
    def module(self):
176
 
        if self.available():
177
 
            return self._module
178
 
        return None
179
 
 
180
 
    def feature_name(self):
181
 
        return self.module_name
182
 
 
183
 
 
184
 
class _HTTPSServerFeature(Feature):
185
 
    """Some tests want an https Server, check if one is available.
186
 
 
187
 
    Right now, the only way this is available is under python2.6 which provides
188
 
    an ssl module.
189
 
    """
190
 
 
191
 
    def _probe(self):
192
 
        try:
193
 
            import ssl
194
 
            return True
195
 
        except ImportError:
196
 
            return False
197
 
 
198
 
    def feature_name(self):
199
 
        return 'HTTPSServer'
200
 
 
201
 
 
202
 
HTTPSServerFeature = _HTTPSServerFeature()
203
 
 
204
 
 
205
 
class _ByteStringNamedFilesystem(Feature):
206
 
    """Is the filesystem based on bytes?"""
207
 
 
208
 
    def _probe(self):
209
 
        if os.name == "posix":
210
 
            return True
211
 
        return False
212
 
 
213
 
ByteStringNamedFilesystem = _ByteStringNamedFilesystem()
214
 
 
215
 
 
216
 
class _UTF8Filesystem(Feature):
217
 
    """Is the filesystem UTF-8?"""
218
 
 
219
 
    def _probe(self):
220
 
        if osutils._fs_enc.upper() in ('UTF-8', 'UTF8'):
221
 
            return True
222
 
        return False
223
 
 
224
 
UTF8Filesystem = _UTF8Filesystem()
225
 
 
226
 
 
227
 
class _BreakinFeature(Feature):
228
 
    """Does this platform support the breakin feature?"""
229
 
 
230
 
    def _probe(self):
231
 
        from bzrlib import breakin
232
 
        if breakin.determine_signal() is None:
233
 
            return False
234
 
        if sys.platform == 'win32':
235
 
            # Windows doesn't have os.kill, and we catch the SIGBREAK signal.
236
 
            # We trigger SIGBREAK via a Console api so we need ctypes to
237
 
            # access the function
238
 
            try:
239
 
                import ctypes
240
 
            except OSError:
241
 
                return False
242
 
        return True
243
 
 
244
 
    def feature_name(self):
245
 
        return "SIGQUIT or SIGBREAK w/ctypes on win32"
246
 
 
247
 
 
248
 
BreakinFeature = _BreakinFeature()
249
 
 
250
 
 
251
 
class _CaseInsCasePresFilenameFeature(Feature):
252
 
    """Is the file-system case insensitive, but case-preserving?"""
253
 
 
254
 
    def _probe(self):
255
 
        fileno, name = tempfile.mkstemp(prefix='MixedCase')
256
 
        try:
257
 
            # first check truly case-preserving for created files, then check
258
 
            # case insensitive when opening existing files.
259
 
            name = osutils.normpath(name)
260
 
            base, rel = osutils.split(name)
261
 
            found_rel = osutils.canonical_relpath(base, name)
262
 
            return (found_rel == rel
263
 
                    and os.path.isfile(name.upper())
264
 
                    and os.path.isfile(name.lower()))
265
 
        finally:
266
 
            os.close(fileno)
267
 
            os.remove(name)
268
 
 
269
 
    def feature_name(self):
270
 
        return "case-insensitive case-preserving filesystem"
271
 
 
272
 
CaseInsCasePresFilenameFeature = _CaseInsCasePresFilenameFeature()
273
 
 
274
 
 
275
 
class _CaseInsensitiveFilesystemFeature(Feature):
276
 
    """Check if underlying filesystem is case-insensitive but *not* case
277
 
    preserving.
278
 
    """
279
 
    # Note that on Windows, Cygwin, MacOS etc, the file-systems are far
280
 
    # more likely to be case preserving, so this case is rare.
281
 
 
282
 
    def _probe(self):
283
 
        if CaseInsCasePresFilenameFeature.available():
284
 
            return False
285
 
 
286
 
        if tests.TestCaseWithMemoryTransport.TEST_ROOT is None:
287
 
            root = osutils.mkdtemp(prefix='testbzr-', suffix='.tmp')
288
 
            tests.TestCaseWithMemoryTransport.TEST_ROOT = root
289
 
        else:
290
 
            root = tests.TestCaseWithMemoryTransport.TEST_ROOT
291
 
        tdir = osutils.mkdtemp(prefix='case-sensitive-probe-', suffix='',
292
 
            dir=root)
293
 
        name_a = osutils.pathjoin(tdir, 'a')
294
 
        name_A = osutils.pathjoin(tdir, 'A')
295
 
        os.mkdir(name_a)
296
 
        result = osutils.isdir(name_A)
297
 
        tests._rmtree_temp_dir(tdir)
298
 
        return result
299
 
 
300
 
    def feature_name(self):
301
 
        return 'case-insensitive filesystem'
302
 
 
303
 
CaseInsensitiveFilesystemFeature = _CaseInsensitiveFilesystemFeature()
304
 
 
305
 
 
306
 
class _CaseSensitiveFilesystemFeature(Feature):
307
 
 
308
 
    def _probe(self):
309
 
        if CaseInsCasePresFilenameFeature.available():
310
 
            return False
311
 
        elif CaseInsensitiveFilesystemFeature.available():
312
 
            return False
313
 
        else:
314
 
            return True
315
 
 
316
 
    def feature_name(self):
317
 
        return 'case-sensitive filesystem'
318
 
 
319
 
# new coding style is for feature instances to be lowercase
320
 
case_sensitive_filesystem_feature = _CaseSensitiveFilesystemFeature()
321
 
 
322
 
 
323
 
class _NotRunningAsRoot(Feature):
 
28
class _NotRunningAsRoot(tests.Feature):
324
29
 
325
30
    def _probe(self):
326
31
        try:
336
41
 
337
42
not_running_as_root = _NotRunningAsRoot()
338
43
 
339
 
apport = ModuleAvailableFeature('apport')
340
 
gpgme = ModuleAvailableFeature('gpgme')
341
 
lzma = ModuleAvailableFeature('lzma')
342
 
meliae = ModuleAvailableFeature('meliae')
343
 
paramiko = ModuleAvailableFeature('paramiko')
344
 
pycurl = ModuleAvailableFeature('pycurl')
345
 
pywintypes = ModuleAvailableFeature('pywintypes')
346
 
sphinx = ModuleAvailableFeature('sphinx')
347
 
subunit = ModuleAvailableFeature('subunit')
348
 
testtools = ModuleAvailableFeature('testtools')
349
 
 
350
 
compiled_patiencediff_feature = ModuleAvailableFeature(
351
 
    'bzrlib._patiencediff_c')
352
 
meliae_feature = ModuleAvailableFeature('meliae.scanner')
353
 
lsprof_feature = ModuleAvailableFeature('bzrlib.lsprof')
354
 
 
355
 
 
356
 
class _BackslashDirSeparatorFeature(Feature):
 
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):
357
55
 
358
56
    def _probe(self):
359
57
        try:
369
67
backslashdir_feature = _BackslashDirSeparatorFeature()
370
68
 
371
69
 
372
 
class _ChownFeature(Feature):
 
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):
373
97
    """os.chown is supported"""
374
98
 
375
99
    def _probe(self):
378
102
chown_feature = _ChownFeature()
379
103
 
380
104
 
381
 
class ExecutableFeature(Feature):
 
105
class ExecutableFeature(tests.Feature):
382
106
    """Feature testing whether an executable of a given name is on the PATH."""
383
107
 
384
108
    def __init__(self, name):
403
127
bash_feature = ExecutableFeature('bash')
404
128
sed_feature = ExecutableFeature('sed')
405
129
diff_feature = ExecutableFeature('diff')
406
 
 
407
 
 
408
 
class _PosixPermissionsFeature(Feature):
409
 
 
410
 
    def _probe(self):
411
 
        def has_perms():
412
 
            # Create temporary file and check if specified perms are
413
 
            # maintained.
414
 
            write_perms = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
415
 
            f = tempfile.mkstemp(prefix='bzr_perms_chk_')
416
 
            fd, name = f
417
 
            os.close(fd)
418
 
            os.chmod(name, write_perms)
419
 
 
420
 
            read_perms = os.stat(name).st_mode & 0777
421
 
            os.unlink(name)
422
 
            return (write_perms == read_perms)
423
 
 
424
 
        return (os.name == 'posix') and has_perms()
425
 
 
426
 
    def feature_name(self):
427
 
        return 'POSIX permissions support'
428
 
 
429
 
 
430
 
posix_permissions_feature = _PosixPermissionsFeature()
431
 
 
432
 
 
433
 
class _StraceFeature(Feature):
434
 
 
435
 
    def _probe(self):
436
 
        try:
437
 
            proc = subprocess.Popen(['strace'],
438
 
                stderr=subprocess.PIPE,
439
 
                stdout=subprocess.PIPE)
440
 
            proc.communicate()
441
 
            return True
442
 
        except OSError, e:
443
 
            if e.errno == errno.ENOENT:
444
 
                # strace is not installed
445
 
                return False
446
 
            else:
447
 
                raise
448
 
 
449
 
    def feature_name(self):
450
 
        return 'strace'
451
 
 
452
 
 
453
 
strace_feature = _StraceFeature()
454
 
 
455
 
 
456
 
class _AttribFeature(Feature):
457
 
 
458
 
    def _probe(self):
459
 
        if (sys.platform not in ('cygwin', 'win32')):
460
 
            return False
461
 
        try:
462
 
            proc = subprocess.Popen(['attrib', '.'], stdout=subprocess.PIPE)
463
 
        except OSError, e:
464
 
            return False
465
 
        return (0 == proc.wait())
466
 
 
467
 
    def feature_name(self):
468
 
        return 'attrib Windows command-line tool'
469
 
 
470
 
 
471
 
AttribFeature = _AttribFeature()
472
 
 
473
 
 
474
 
class Win32Feature(Feature):
475
 
    """Feature testing whether we're running selftest on Windows
476
 
    or Windows-like platform.
477
 
    """
478
 
 
479
 
    def _probe(self):
480
 
        return sys.platform == 'win32'
481
 
 
482
 
    def feature_name(self):
483
 
        return "win32 platform"
484
 
 
485
 
 
486
 
win32_feature = Win32Feature()
487
 
 
488
 
 
489
 
for name in ['HTTPServerFeature', 
490
 
    'HTTPSServerFeature', 'SymlinkFeature', 'HardlinkFeature',
491
 
    'OsFifoFeature', 'UnicodeFilenameFeature',
492
 
    'ByteStringNamedFilesystem', 'UTF8Filesystem',
493
 
    'BreakinFeature', 'CaseInsCasePresFilenameFeature',
494
 
    'CaseInsensitiveFilesystemFeature', 'case_sensitive_filesystem_feature',
495
 
    'posix_permissions_feature',
496
 
    ]:
497
 
    setattr(tests, name, _CompatabilityThunkFeature(
498
 
        symbol_versioning.deprecated_in((2, 5, 0)),
499
 
        'bzrlib.tests', name,
500
 
        name, 'bzrlib.tests.features'))
501
 
 
502
 
 
503
 
for (old_name, new_name) in [
504
 
    ('UnicodeFilename', 'UnicodeFilenameFeature'),
505
 
    ]:
506
 
    setattr(tests, name, _CompatabilityThunkFeature(
507
 
        symbol_versioning.deprecated_in((2, 5, 0)),
508
 
        'bzrlib.tests', old_name,
509
 
        new_name, 'bzrlib.tests.features'))