/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
6060.1.1 by John Arbash Meinel
import subprocess in 'bzrlib.tests.features' so that certain features can properly probe.
21
import subprocess
5036.3.8 by Parth Malwankar
closed review comments from vila
22
import stat
5609.47.1 by Alexander Belchenko
Win32Feature
23
import sys
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
24
import tempfile
4584.3.21 by Martin Pool
Start adding tests for apport
25
5321.2.1 by Vincent Ladeuil
Fix style issues, including vertical spaces, lines too long and multi lines imports.
26
from bzrlib import (
27
    osutils,
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
28
    symbol_versioning,
5321.2.1 by Vincent Ladeuil
Fix style issues, including vertical spaces, lines too long and multi lines imports.
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):
5642.4.3 by Vincent Ladeuil
Rework the fix taking mgz remarks into account and rebasing against trunk.
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
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
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
6280.1.1 by Martin Pool
Restore old name for bzrlib.tests.feature to unbreak bzr fastimport etc
292
        from bzrlib import tests
293
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
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)
6280.1.2 by Martin Pool
Fix NameError
305
        tests._rmtree_temp_dir(tdir)
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
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):
4797.70.1 by Vincent Ladeuil
Skip chmodbits dependent tests when running as root
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()
5448.5.9 by Vincent Ladeuil
Make the test depends on a feature so it's skipped if meliae is not installed
346
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
347
apport = ModuleAvailableFeature('apport')
5967.12.2 by Martin Pool
Move all features to bzrlib.tests.features in 2.5
348
gpgme = ModuleAvailableFeature('gpgme')
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
349
lzma = ModuleAvailableFeature('lzma')
6091.2.2 by Max Bowsher
Per jam's review comments, get rid of features.meliae_feature, which is new in
350
meliae = ModuleAvailableFeature('meliae.scanner')
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
351
paramiko = ModuleAvailableFeature('paramiko')
352
pycurl = ModuleAvailableFeature('pycurl')
353
pywintypes = ModuleAvailableFeature('pywintypes')
354
sphinx = ModuleAvailableFeature('sphinx')
355
subunit = ModuleAvailableFeature('subunit')
5967.12.3 by Martin Pool
Unify duplicated UnicodeFilename and _PosixPermissionsFeature
356
testtools = ModuleAvailableFeature('testtools')
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
357
358
compiled_patiencediff_feature = ModuleAvailableFeature(
359
    'bzrlib._patiencediff_c')
360
lsprof_feature = ModuleAvailableFeature('bzrlib.lsprof')
361
362
363
class _BackslashDirSeparatorFeature(Feature):
5241.2.1 by Robert Collins
Merge up from 2.0/2.1:
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
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
379
class _ChownFeature(Feature):
5051.4.10 by Parth Malwankar
moved ChownFeature to tests/features.py
380
    """os.chown is supported"""
381
382
    def _probe(self):
383
        return os.name == 'posix' and hasattr(os, 'chown')
384
5051.4.11 by Parth Malwankar
closed Martins review comments.
385
chown_feature = _ChownFeature()
5051.4.10 by Parth Malwankar
moved ChownFeature to tests/features.py
386
5147.5.20 by Martin von Gagern
Move ExecutableFeature class from bash_completion plugin to bzrlib tests.
387
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
388
class ExecutableFeature(Feature):
5147.5.20 by Martin von Gagern
Move ExecutableFeature class from bash_completion plugin to bzrlib tests.
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
5147.5.23 by Martin von Gagern
Have ExecutableFeature implement and use _probe.
394
        self._path = None
5147.5.20 by Martin von Gagern
Move ExecutableFeature class from bash_completion plugin to bzrlib tests.
395
396
    @property
397
    def path(self):
5147.5.23 by Martin von Gagern
Have ExecutableFeature implement and use _probe.
398
        # This is a property, so accessing path ensures _probe was called
399
        self.available()
400
        return self._path
5147.5.20 by Martin von Gagern
Move ExecutableFeature class from bash_completion plugin to bzrlib tests.
401
5147.5.23 by Martin von Gagern
Have ExecutableFeature implement and use _probe.
402
    def _probe(self):
5321.1.82 by Gordon Tyler
Changed ExecutableFeature to use osutils.find_executable_on_path.
403
        self._path = osutils.find_executable_on_path(self.name)
404
        return self._path is not None
5147.5.20 by Martin von Gagern
Move ExecutableFeature class from bash_completion plugin to bzrlib tests.
405
406
    def feature_name(self):
407
        return '%s executable' % self.name
5147.5.24 by Martin von Gagern
Move ExecutableFeature instances to tests.features module.
408
409
410
bash_feature = ExecutableFeature('bash')
411
sed_feature = ExecutableFeature('sed')
5349.1.5 by Matthäus G. Chajdas
Fix issues raised by Vincent Ladeuil.
412
diff_feature = ExecutableFeature('diff')
5609.47.1 by Alexander Belchenko
Win32Feature
413
414
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
415
class _PosixPermissionsFeature(Feature):
416
417
    def _probe(self):
418
        def has_perms():
419
            # Create temporary file and check if specified perms are
420
            # maintained.
421
            write_perms = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
422
            f = tempfile.mkstemp(prefix='bzr_perms_chk_')
423
            fd, name = f
424
            os.close(fd)
425
            os.chmod(name, write_perms)
426
427
            read_perms = os.stat(name).st_mode & 0777
428
            os.unlink(name)
429
            return (write_perms == read_perms)
430
431
        return (os.name == 'posix') and has_perms()
432
433
    def feature_name(self):
434
        return 'POSIX permissions support'
435
436
437
posix_permissions_feature = _PosixPermissionsFeature()
438
439
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()
5967.12.2 by Martin Pool
Move all features to bzrlib.tests.features in 2.5
479
480
5967.12.3 by Martin Pool
Unify duplicated UnicodeFilename and _PosixPermissionsFeature
481
class Win32Feature(Feature):
5609.47.1 by Alexander Belchenko
Win32Feature
482
    """Feature testing whether we're running selftest on Windows
5609.47.4 by Alexander Belchenko
fixed typo
483
    or Windows-like platform.
5609.47.1 by Alexander Belchenko
Win32Feature
484
    """
485
486
    def _probe(self):
487
        return sys.platform == 'win32'
488
489
    def feature_name(self):
490
        return "win32 platform"
491
5967.12.4 by Martin Pool
Support (but deprecated) old feature names
492
5609.47.1 by Alexander Belchenko
Win32Feature
493
win32_feature = Win32Feature()