~bzr-pqm/bzr/bzr.dev

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)
6325.3.1 by Vincent Ladeuil
Give meaningful deprecation warnings for deprecated test features
143
            symbol_versioning.warn(depr_msg + use_msg, DeprecationWarning,
144
                                   stacklevel=5)
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
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):
5642.4.3 by Vincent Ladeuil
Rework the fix taking mgz remarks into account and rebasing against trunk.
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
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
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
6280.1.1 by Martin Pool
Restore old name for bzrlib.tests.feature to unbreak bzr fastimport etc
293
        from bzrlib import tests
294
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
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)
6280.1.2 by Martin Pool
Fix NameError
306
        tests._rmtree_temp_dir(tdir)
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
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):
4797.70.1 by Vincent Ladeuil
Skip chmodbits dependent tests when running as root
333
334
    def _probe(self):
335
        try:
336
            uid = os.getuid()
337
        except AttributeError:
338
            # If there is no uid, chances are there is no root either
339
            return True
340
        return uid != 0
341
342
    def feature_name(self):
343
        return 'Not running as root'
344
345
346
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
347
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
348
apport = ModuleAvailableFeature('apport')
5967.12.2 by Martin Pool
Move all features to bzrlib.tests.features in 2.5
349
gpgme = ModuleAvailableFeature('gpgme')
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
350
lzma = ModuleAvailableFeature('lzma')
6091.2.2 by Max Bowsher
Per jam's review comments, get rid of features.meliae_feature, which is new in
351
meliae = ModuleAvailableFeature('meliae.scanner')
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
352
paramiko = ModuleAvailableFeature('paramiko')
353
pycurl = ModuleAvailableFeature('pycurl')
354
pywintypes = ModuleAvailableFeature('pywintypes')
355
sphinx = ModuleAvailableFeature('sphinx')
356
subunit = ModuleAvailableFeature('subunit')
5967.12.3 by Martin Pool
Unify duplicated UnicodeFilename and _PosixPermissionsFeature
357
testtools = ModuleAvailableFeature('testtools')
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
358
359
compiled_patiencediff_feature = ModuleAvailableFeature(
360
    'bzrlib._patiencediff_c')
361
lsprof_feature = ModuleAvailableFeature('bzrlib.lsprof')
362
363
364
class _BackslashDirSeparatorFeature(Feature):
5241.2.1 by Robert Collins
Merge up from 2.0/2.1:
365
366
    def _probe(self):
367
        try:
368
            os.lstat(os.getcwd() + '\\')
369
        except OSError:
370
            return False
371
        else:
372
            return True
373
374
    def feature_name(self):
375
        return "Filesystem treats '\\' as a directory separator."
376
377
backslashdir_feature = _BackslashDirSeparatorFeature()
378
379
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
380
class _ChownFeature(Feature):
5051.4.10 by Parth Malwankar
moved ChownFeature to tests/features.py
381
    """os.chown is supported"""
382
383
    def _probe(self):
384
        return os.name == 'posix' and hasattr(os, 'chown')
385
5051.4.11 by Parth Malwankar
closed Martins review comments.
386
chown_feature = _ChownFeature()
5051.4.10 by Parth Malwankar
moved ChownFeature to tests/features.py
387
5147.5.20 by Martin von Gagern
Move ExecutableFeature class from bash_completion plugin to bzrlib tests.
388
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
389
class ExecutableFeature(Feature):
5147.5.20 by Martin von Gagern
Move ExecutableFeature class from bash_completion plugin to bzrlib tests.
390
    """Feature testing whether an executable of a given name is on the PATH."""
391
392
    def __init__(self, name):
393
        super(ExecutableFeature, self).__init__()
394
        self.name = name
5147.5.23 by Martin von Gagern
Have ExecutableFeature implement and use _probe.
395
        self._path = None
5147.5.20 by Martin von Gagern
Move ExecutableFeature class from bash_completion plugin to bzrlib tests.
396
397
    @property
398
    def path(self):
5147.5.23 by Martin von Gagern
Have ExecutableFeature implement and use _probe.
399
        # This is a property, so accessing path ensures _probe was called
400
        self.available()
401
        return self._path
5147.5.20 by Martin von Gagern
Move ExecutableFeature class from bash_completion plugin to bzrlib tests.
402
5147.5.23 by Martin von Gagern
Have ExecutableFeature implement and use _probe.
403
    def _probe(self):
5321.1.82 by Gordon Tyler
Changed ExecutableFeature to use osutils.find_executable_on_path.
404
        self._path = osutils.find_executable_on_path(self.name)
405
        return self._path is not None
5147.5.20 by Martin von Gagern
Move ExecutableFeature class from bash_completion plugin to bzrlib tests.
406
407
    def feature_name(self):
408
        return '%s executable' % self.name
5147.5.24 by Martin von Gagern
Move ExecutableFeature instances to tests.features module.
409
410
411
bash_feature = ExecutableFeature('bash')
6282.3.1 by Vincent Ladeuil
First cut at a working plugin to avoid conflicts in .po files by shelling out to msgmerge.
412
diff_feature = ExecutableFeature('diff')
5147.5.24 by Martin von Gagern
Move ExecutableFeature instances to tests.features module.
413
sed_feature = ExecutableFeature('sed')
6282.3.1 by Vincent Ladeuil
First cut at a working plugin to avoid conflicts in .po files by shelling out to msgmerge.
414
msgmerge_feature = ExecutableFeature('msgmerge')
5609.47.1 by Alexander Belchenko
Win32Feature
415
416
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
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)
6344.1.1 by Martin Packman
Merge 2.4 into bzr.dev
427
            osutils.chmod_if_possible(name, write_perms)
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
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()
5967.12.2 by Martin Pool
Move all features to bzrlib.tests.features in 2.5
481
482
5967.12.3 by Martin Pool
Unify duplicated UnicodeFilename and _PosixPermissionsFeature
483
class Win32Feature(Feature):
5609.47.1 by Alexander Belchenko
Win32Feature
484
    """Feature testing whether we're running selftest on Windows
5609.47.4 by Alexander Belchenko
fixed typo
485
    or Windows-like platform.
5609.47.1 by Alexander Belchenko
Win32Feature
486
    """
487
488
    def _probe(self):
489
        return sys.platform == 'win32'
490
491
    def feature_name(self):
492
        return "win32 platform"
493
5967.12.4 by Martin Pool
Support (but deprecated) old feature names
494
5609.47.1 by Alexander Belchenko
Win32Feature
495
win32_feature = Win32Feature()