/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
5557.1.15 by John Arbash Meinel
Merge bzr.dev 5597 to resolve NEWS, aka bzr-2.3.txt
1
# Copyright (C) 2009, 2010, 2011 Canonical Ltd
4584.3.21 by Martin Pool
Start adding tests for apport
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
17
"""A collection of commonly used 'Features' to optionally run tests.
18
"""
5241.2.1 by Robert Collins
Merge up from 2.0/2.1:
19
5036.3.8 by Parth Malwankar
closed review comments from vila
20
import os
21
import stat
5609.47.1 by Alexander Belchenko
Win32Feature
22
import sys
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
23
import tempfile
4584.3.21 by Martin Pool
Start adding tests for apport
24
5321.2.1 by Vincent Ladeuil
Fix style issues, including vertical spaces, lines too long and multi lines imports.
25
from bzrlib import (
26
    osutils,
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
27
    symbol_versioning,
5321.2.1 by Vincent Ladeuil
Fix style issues, including vertical spaces, lines too long and multi lines imports.
28
    tests,
29
    )
4913.2.19 by John Arbash Meinel
Compatibly rename ApportFeature to features.apport.
30
31
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
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):
4797.70.1 by Vincent Ladeuil
Skip chmodbits dependent tests when running as root
324
325
    def _probe(self):
326
        try:
327
            uid = os.getuid()
328
        except AttributeError:
329
            # If there is no uid, chances are there is no root either
330
            return True
331
        return uid != 0
332
333
    def feature_name(self):
334
        return 'Not running as root'
335
336
337
not_running_as_root = _NotRunningAsRoot()
5448.5.9 by Vincent Ladeuil
Make the test depends on a feature so it's skipped if meliae is not installed
338
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
339
apport = ModuleAvailableFeature('apport')
5967.12.2 by Martin Pool
Move all features to bzrlib.tests.features in 2.5
340
gpgme = ModuleAvailableFeature('gpgme')
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
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')
5967.12.3 by Martin Pool
Unify duplicated UnicodeFilename and _PosixPermissionsFeature
348
testtools = ModuleAvailableFeature('testtools')
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
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):
5241.2.1 by Robert Collins
Merge up from 2.0/2.1:
357
358
    def _probe(self):
359
        try:
360
            os.lstat(os.getcwd() + '\\')
361
        except OSError:
362
            return False
363
        else:
364
            return True
365
366
    def feature_name(self):
367
        return "Filesystem treats '\\' as a directory separator."
368
369
backslashdir_feature = _BackslashDirSeparatorFeature()
370
371
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
372
class _ChownFeature(Feature):
5051.4.10 by Parth Malwankar
moved ChownFeature to tests/features.py
373
    """os.chown is supported"""
374
375
    def _probe(self):
376
        return os.name == 'posix' and hasattr(os, 'chown')
377
5051.4.11 by Parth Malwankar
closed Martins review comments.
378
chown_feature = _ChownFeature()
5051.4.10 by Parth Malwankar
moved ChownFeature to tests/features.py
379
5147.5.20 by Martin von Gagern
Move ExecutableFeature class from bash_completion plugin to bzrlib tests.
380
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
381
class ExecutableFeature(Feature):
5147.5.20 by Martin von Gagern
Move ExecutableFeature class from bash_completion plugin to bzrlib tests.
382
    """Feature testing whether an executable of a given name is on the PATH."""
383
384
    def __init__(self, name):
385
        super(ExecutableFeature, self).__init__()
386
        self.name = name
5147.5.23 by Martin von Gagern
Have ExecutableFeature implement and use _probe.
387
        self._path = None
5147.5.20 by Martin von Gagern
Move ExecutableFeature class from bash_completion plugin to bzrlib tests.
388
389
    @property
390
    def path(self):
5147.5.23 by Martin von Gagern
Have ExecutableFeature implement and use _probe.
391
        # This is a property, so accessing path ensures _probe was called
392
        self.available()
393
        return self._path
5147.5.20 by Martin von Gagern
Move ExecutableFeature class from bash_completion plugin to bzrlib tests.
394
5147.5.23 by Martin von Gagern
Have ExecutableFeature implement and use _probe.
395
    def _probe(self):
5321.1.82 by Gordon Tyler
Changed ExecutableFeature to use osutils.find_executable_on_path.
396
        self._path = osutils.find_executable_on_path(self.name)
397
        return self._path is not None
5147.5.20 by Martin von Gagern
Move ExecutableFeature class from bash_completion plugin to bzrlib tests.
398
399
    def feature_name(self):
400
        return '%s executable' % self.name
5147.5.24 by Martin von Gagern
Move ExecutableFeature instances to tests.features module.
401
402
403
bash_feature = ExecutableFeature('bash')
404
sed_feature = ExecutableFeature('sed')
5349.1.5 by Matthäus G. Chajdas
Fix issues raised by Vincent Ladeuil.
405
diff_feature = ExecutableFeature('diff')
5609.47.1 by Alexander Belchenko
Win32Feature
406
407
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
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()
5967.12.2 by Martin Pool
Move all features to bzrlib.tests.features in 2.5
472
473
5967.12.3 by Martin Pool
Unify duplicated UnicodeFilename and _PosixPermissionsFeature
474
class Win32Feature(Feature):
5609.47.1 by Alexander Belchenko
Win32Feature
475
    """Feature testing whether we're running selftest on Windows
5609.47.4 by Alexander Belchenko
fixed typo
476
    or Windows-like platform.
5609.47.1 by Alexander Belchenko
Win32Feature
477
    """
478
479
    def _probe(self):
480
        return sys.platform == 'win32'
481
482
    def feature_name(self):
483
        return "win32 platform"
484
5967.12.4 by Martin Pool
Support (but deprecated) old feature names
485
5609.47.1 by Alexander Belchenko
Win32Feature
486
win32_feature = Win32Feature()
5967.12.4 by Martin Pool
Support (but deprecated) old feature names
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, 4, 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, 4, 0)),
508
        'bzrlib.tests', old_name,
509
        new_name, 'bzrlib.tests.features'))