/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 breezy/tests/features.py

  • Committer: Jelmer Vernooij
  • Date: 2017-09-01 07:15:43 UTC
  • mfrom: (6770.3.2 py3_test_cleanup)
  • Revision ID: jelmer@jelmer.uk-20170901071543-1t83321xkog9qrxh
Merge lp:~gz/brz/py3_test_cleanup

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
 
 
20
from __future__ import absolute_import
 
21
 
17
22
import os
 
23
import subprocess
18
24
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):
 
25
import sys
 
26
import tempfile
 
27
import warnings
 
28
 
 
29
from .. import (
 
30
    osutils,
 
31
    symbol_versioning,
 
32
    )
 
33
 
 
34
 
 
35
class Feature(object):
 
36
    """An operating system Feature."""
 
37
 
 
38
    def __init__(self):
 
39
        self._available = None
 
40
 
 
41
    def available(self):
 
42
        """Is the feature available?
 
43
 
 
44
        :return: True if the feature is available.
 
45
        """
 
46
        if self._available is None:
 
47
            self._available = self._probe()
 
48
        return self._available
 
49
 
 
50
    def _probe(self):
 
51
        """Implement this method in concrete features.
 
52
 
 
53
        :return: True if the feature is available.
 
54
        """
 
55
        raise NotImplementedError
 
56
 
 
57
    def __str__(self):
 
58
        if getattr(self, 'feature_name', None):
 
59
            return self.feature_name()
 
60
        return self.__class__.__name__
 
61
 
 
62
 
 
63
class _SymlinkFeature(Feature):
 
64
 
 
65
    def _probe(self):
 
66
        return osutils.has_symlinks()
 
67
 
 
68
    def feature_name(self):
 
69
        return 'symlinks'
 
70
 
 
71
SymlinkFeature = _SymlinkFeature()
 
72
 
 
73
 
 
74
class _HardlinkFeature(Feature):
 
75
 
 
76
    def _probe(self):
 
77
        return osutils.has_hardlinks()
 
78
 
 
79
    def feature_name(self):
 
80
        return 'hardlinks'
 
81
 
 
82
HardlinkFeature = _HardlinkFeature()
 
83
 
 
84
 
 
85
class _OsFifoFeature(Feature):
 
86
 
 
87
    def _probe(self):
 
88
        return getattr(os, 'mkfifo', None)
 
89
 
 
90
    def feature_name(self):
 
91
        return 'filesystem fifos'
 
92
 
 
93
OsFifoFeature = _OsFifoFeature()
 
94
 
 
95
 
 
96
class _UnicodeFilenameFeature(Feature):
 
97
    """Does the filesystem support Unicode filenames?"""
 
98
 
 
99
    def _probe(self):
 
100
        try:
 
101
            # Check for character combinations unlikely to be covered by any
 
102
            # single non-unicode encoding. We use the characters
 
103
            # - greek small letter alpha (U+03B1) and
 
104
            # - braille pattern dots-123456 (U+283F).
 
105
            os.stat(u'\u03b1\u283f')
 
106
        except UnicodeEncodeError:
 
107
            return False
 
108
        except (IOError, OSError):
 
109
            # The filesystem allows the Unicode filename but the file doesn't
 
110
            # exist.
 
111
            return True
 
112
        else:
 
113
            # The filesystem allows the Unicode filename and the file exists,
 
114
            # for some reason.
 
115
            return True
 
116
 
 
117
UnicodeFilenameFeature = _UnicodeFilenameFeature()
 
118
 
 
119
 
 
120
class _CompatabilityThunkFeature(Feature):
 
121
    """This feature is just a thunk to another feature.
 
122
 
 
123
    It issues a deprecation warning if it is accessed, to let you know that you
 
124
    should really use a different feature.
 
125
    """
 
126
 
 
127
    def __init__(self, dep_version, module, name,
 
128
                 replacement_name, replacement_module=None):
 
129
        super(_CompatabilityThunkFeature, self).__init__()
 
130
        self._module = module
 
131
        if replacement_module is None:
 
132
            replacement_module = module
 
133
        self._replacement_module = replacement_module
 
134
        self._name = name
 
135
        self._replacement_name = replacement_name
 
136
        self._dep_version = dep_version
 
137
        self._feature = None
 
138
 
 
139
    def _ensure(self):
 
140
        if self._feature is None:
 
141
            from breezy import pyutils
 
142
            depr_msg = self._dep_version % ('%s.%s'
 
143
                                            % (self._module, self._name))
 
144
            use_msg = ' Use %s.%s instead.' % (self._replacement_module,
 
145
                                               self._replacement_name)
 
146
            symbol_versioning.warn(depr_msg + use_msg, DeprecationWarning,
 
147
                                   stacklevel=5)
 
148
            # Import the new feature and use it as a replacement for the
 
149
            # deprecated one.
 
150
            self._feature = pyutils.get_named_object(
 
151
                self._replacement_module, self._replacement_name)
 
152
 
 
153
    def _probe(self):
 
154
        self._ensure()
 
155
        return self._feature._probe()
 
156
 
 
157
 
 
158
class ModuleAvailableFeature(Feature):
 
159
    """This is a feature than describes a module we want to be available.
 
160
 
 
161
    Declare the name of the module in __init__(), and then after probing, the
 
162
    module will be available as 'self.module'.
 
163
 
 
164
    :ivar module: The module if it is available, else None.
 
165
    """
 
166
 
 
167
    def __init__(self, module_name, ignore_warnings=None):
 
168
        super(ModuleAvailableFeature, self).__init__()
 
169
        self.module_name = module_name
 
170
        if ignore_warnings is None:
 
171
            ignore_warnings = ()
 
172
        self.ignore_warnings = ignore_warnings
 
173
 
 
174
    def _probe(self):
 
175
        sentinel = object()
 
176
        module = sys.modules.get(self.module_name, sentinel)
 
177
        if module is sentinel:
 
178
            with warnings.catch_warnings():
 
179
                for warning_category in self.ignore_warnings:
 
180
                    warnings.simplefilter('ignore', warning_category)
 
181
                try:
 
182
                    self._module = __import__(self.module_name)
 
183
                except ImportError:
 
184
                    return False
 
185
                return True
 
186
        else:
 
187
            self._module = module
 
188
            return True
 
189
 
 
190
    @property
 
191
    def module(self):
 
192
        if self.available():
 
193
            return self._module
 
194
        return None
 
195
 
 
196
    def feature_name(self):
 
197
        return self.module_name
 
198
 
 
199
 
 
200
class PluginLoadedFeature(Feature):
 
201
    """Check whether a plugin with specific name is loaded.
 
202
 
 
203
    This is different from ModuleAvailableFeature, because
 
204
    plugins can be available but explicitly disabled
 
205
    (e.g. through BRZ_DISABLE_PLUGINS=blah).
 
206
 
 
207
    :ivar plugin_name: The name of the plugin
 
208
    """
 
209
 
 
210
    def __init__(self, plugin_name):
 
211
        super(PluginLoadedFeature, self).__init__()
 
212
        self.plugin_name = plugin_name
 
213
 
 
214
    def _probe(self):
 
215
        from breezy.plugin import get_loaded_plugin
 
216
        return (get_loaded_plugin(self.plugin_name) is not None)
 
217
 
 
218
    @property
 
219
    def plugin(self):
 
220
        from breezy.plugin import get_loaded_plugin
 
221
        return get_loaded_plugin(self.plugin_name)
 
222
 
 
223
    def feature_name(self):
 
224
        return '%s plugin' % self.plugin_name
 
225
 
 
226
 
 
227
class _HTTPSServerFeature(Feature):
 
228
    """Some tests want an https Server, check if one is available.
 
229
 
 
230
    Right now, the only way this is available is under python2.6 which provides
 
231
    an ssl module.
 
232
    """
 
233
 
 
234
    def _probe(self):
 
235
        try:
 
236
            import ssl
 
237
            return True
 
238
        except ImportError:
 
239
            return False
 
240
 
 
241
    def feature_name(self):
 
242
        return 'HTTPSServer'
 
243
 
 
244
 
 
245
HTTPSServerFeature = _HTTPSServerFeature()
 
246
 
 
247
 
 
248
class _ByteStringNamedFilesystem(Feature):
 
249
    """Is the filesystem based on bytes?"""
 
250
 
 
251
    def _probe(self):
 
252
        if os.name == "posix":
 
253
            return True
 
254
        return False
 
255
 
 
256
ByteStringNamedFilesystem = _ByteStringNamedFilesystem()
 
257
 
 
258
 
 
259
class _UTF8Filesystem(Feature):
 
260
    """Is the filesystem UTF-8?"""
 
261
 
 
262
    def _probe(self):
 
263
        if osutils._fs_enc.upper() in ('UTF-8', 'UTF8'):
 
264
            return True
 
265
        return False
 
266
 
 
267
UTF8Filesystem = _UTF8Filesystem()
 
268
 
 
269
 
 
270
class _BreakinFeature(Feature):
 
271
    """Does this platform support the breakin feature?"""
 
272
 
 
273
    def _probe(self):
 
274
        from breezy import breakin
 
275
        if breakin.determine_signal() is None:
 
276
            return False
 
277
        if sys.platform == 'win32':
 
278
            # Windows doesn't have os.kill, and we catch the SIGBREAK signal.
 
279
            # We trigger SIGBREAK via a Console api so we need ctypes to
 
280
            # access the function
 
281
            try:
 
282
                import ctypes
 
283
            except OSError:
 
284
                return False
 
285
        return True
 
286
 
 
287
    def feature_name(self):
 
288
        return "SIGQUIT or SIGBREAK w/ctypes on win32"
 
289
 
 
290
 
 
291
BreakinFeature = _BreakinFeature()
 
292
 
 
293
 
 
294
class _CaseInsCasePresFilenameFeature(Feature):
 
295
    """Is the file-system case insensitive, but case-preserving?"""
 
296
 
 
297
    def _probe(self):
 
298
        fileno, name = tempfile.mkstemp(prefix='MixedCase')
 
299
        try:
 
300
            # first check truly case-preserving for created files, then check
 
301
            # case insensitive when opening existing files.
 
302
            name = osutils.normpath(name)
 
303
            base, rel = osutils.split(name)
 
304
            found_rel = osutils.canonical_relpath(base, name)
 
305
            return (found_rel == rel
 
306
                    and os.path.isfile(name.upper())
 
307
                    and os.path.isfile(name.lower()))
 
308
        finally:
 
309
            os.close(fileno)
 
310
            os.remove(name)
 
311
 
 
312
    def feature_name(self):
 
313
        return "case-insensitive case-preserving filesystem"
 
314
 
 
315
CaseInsCasePresFilenameFeature = _CaseInsCasePresFilenameFeature()
 
316
 
 
317
 
 
318
class _CaseInsensitiveFilesystemFeature(Feature):
 
319
    """Check if underlying filesystem is case-insensitive but *not* case
 
320
    preserving.
 
321
    """
 
322
    # Note that on Windows, Cygwin, MacOS etc, the file-systems are far
 
323
    # more likely to be case preserving, so this case is rare.
 
324
 
 
325
    def _probe(self):
 
326
        if CaseInsCasePresFilenameFeature.available():
 
327
            return False
 
328
 
 
329
        from breezy import tests
 
330
 
 
331
        if tests.TestCaseWithMemoryTransport.TEST_ROOT is None:
 
332
            root = osutils.mkdtemp(prefix='testbzr-', suffix='.tmp')
 
333
            tests.TestCaseWithMemoryTransport.TEST_ROOT = root
 
334
        else:
 
335
            root = tests.TestCaseWithMemoryTransport.TEST_ROOT
 
336
        tdir = osutils.mkdtemp(prefix='case-sensitive-probe-', suffix='',
 
337
            dir=root)
 
338
        name_a = osutils.pathjoin(tdir, 'a')
 
339
        name_A = osutils.pathjoin(tdir, 'A')
 
340
        os.mkdir(name_a)
 
341
        result = osutils.isdir(name_A)
 
342
        tests._rmtree_temp_dir(tdir)
 
343
        return result
 
344
 
 
345
    def feature_name(self):
 
346
        return 'case-insensitive filesystem'
 
347
 
 
348
CaseInsensitiveFilesystemFeature = _CaseInsensitiveFilesystemFeature()
 
349
 
 
350
 
 
351
class _CaseSensitiveFilesystemFeature(Feature):
 
352
 
 
353
    def _probe(self):
 
354
        if CaseInsCasePresFilenameFeature.available():
 
355
            return False
 
356
        elif CaseInsensitiveFilesystemFeature.available():
 
357
            return False
 
358
        else:
 
359
            return True
 
360
 
 
361
    def feature_name(self):
 
362
        return 'case-sensitive filesystem'
 
363
 
 
364
# new coding style is for feature instances to be lowercase
 
365
case_sensitive_filesystem_feature = _CaseSensitiveFilesystemFeature()
 
366
 
 
367
 
 
368
class _NotRunningAsRoot(Feature):
 
369
 
 
370
    def _probe(self):
 
371
        try:
 
372
            uid = os.getuid()
 
373
        except AttributeError:
 
374
            # If there is no uid, chances are there is no root either
 
375
            return True
 
376
        return uid != 0
 
377
 
 
378
    def feature_name(self):
 
379
        return 'Not running as root'
 
380
 
 
381
 
 
382
not_running_as_root = _NotRunningAsRoot()
 
383
 
 
384
# Apport uses deprecated imp module on python3.
 
385
apport = ModuleAvailableFeature(
 
386
    'apport.report',
 
387
    ignore_warnings=[DeprecationWarning, PendingDeprecationWarning])
 
388
gpg = ModuleAvailableFeature('gpg')
 
389
lzma = ModuleAvailableFeature('lzma')
 
390
meliae = ModuleAvailableFeature('meliae.scanner')
 
391
paramiko = ModuleAvailableFeature('paramiko')
 
392
pywintypes = ModuleAvailableFeature('pywintypes')
 
393
subunit = ModuleAvailableFeature('subunit')
 
394
testtools = ModuleAvailableFeature('testtools')
 
395
 
 
396
compiled_patiencediff_feature = ModuleAvailableFeature(
 
397
    'breezy._patiencediff_c')
 
398
lsprof_feature = ModuleAvailableFeature('breezy.lsprof')
 
399
 
 
400
 
 
401
class _BackslashDirSeparatorFeature(Feature):
 
402
 
 
403
    def _probe(self):
 
404
        try:
 
405
            os.lstat(os.getcwd() + '\\')
 
406
        except OSError:
 
407
            return False
 
408
        else:
 
409
            return True
 
410
 
 
411
    def feature_name(self):
 
412
        return "Filesystem treats '\\' as a directory separator."
 
413
 
 
414
backslashdir_feature = _BackslashDirSeparatorFeature()
 
415
 
 
416
 
 
417
class _ChownFeature(Feature):
 
418
    """os.chown is supported"""
 
419
 
 
420
    def _probe(self):
 
421
        return os.name == 'posix' and hasattr(os, 'chown')
 
422
 
 
423
chown_feature = _ChownFeature()
 
424
 
 
425
 
 
426
class ExecutableFeature(Feature):
 
427
    """Feature testing whether an executable of a given name is on the PATH."""
 
428
 
 
429
    def __init__(self, name):
 
430
        super(ExecutableFeature, self).__init__()
 
431
        self.name = name
 
432
        self._path = None
 
433
 
 
434
    @property
 
435
    def path(self):
 
436
        # This is a property, so accessing path ensures _probe was called
 
437
        self.available()
 
438
        return self._path
 
439
 
 
440
    def _probe(self):
 
441
        self._path = osutils.find_executable_on_path(self.name)
 
442
        return self._path is not None
 
443
 
 
444
    def feature_name(self):
 
445
        return '%s executable' % self.name
 
446
 
 
447
 
 
448
bash_feature = ExecutableFeature('bash')
 
449
diff_feature = ExecutableFeature('diff')
 
450
sed_feature = ExecutableFeature('sed')
 
451
msgmerge_feature = ExecutableFeature('msgmerge')
 
452
 
 
453
 
 
454
class _PosixPermissionsFeature(Feature):
31
455
 
32
456
    def _probe(self):
33
457
        def has_perms():
34
 
            # create temporary file and check if specified perms are maintained.
35
 
            import tempfile
36
 
 
 
458
            # Create temporary file and check if specified perms are
 
459
            # maintained.
37
460
            write_perms = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
38
461
            f = tempfile.mkstemp(prefix='bzr_perms_chk_')
39
462
            fd, name = f
40
463
            os.close(fd)
41
 
            os.chmod(name, write_perms)
 
464
            osutils.chmod_if_possible(name, write_perms)
42
465
 
43
 
            read_perms = os.stat(name).st_mode & 0777
 
466
            read_perms = os.stat(name).st_mode & 0o777
44
467
            os.unlink(name)
45
468
            return (write_perms == read_perms)
46
469
 
53
476
posix_permissions_feature = _PosixPermissionsFeature()
54
477
 
55
478
 
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
 
 
 
479
class _StraceFeature(Feature):
 
480
 
 
481
    def _probe(self):
 
482
        try:
 
483
            proc = subprocess.Popen(['strace'],
 
484
                stderr=subprocess.PIPE,
 
485
                stdout=subprocess.PIPE)
 
486
            proc.communicate()
 
487
            return True
 
488
        except OSError as e:
 
489
            if e.errno == errno.ENOENT:
 
490
                # strace is not installed
 
491
                return False
 
492
            else:
 
493
                raise
 
494
 
 
495
    def feature_name(self):
 
496
        return 'strace'
 
497
 
 
498
 
 
499
strace_feature = _StraceFeature()
 
500
 
 
501
 
 
502
class _AttribFeature(Feature):
 
503
 
 
504
    def _probe(self):
 
505
        if (sys.platform not in ('cygwin', 'win32')):
 
506
            return False
 
507
        try:
 
508
            proc = subprocess.Popen(['attrib', '.'], stdout=subprocess.PIPE)
 
509
        except OSError as e:
 
510
            return False
 
511
        return (0 == proc.wait())
 
512
 
 
513
    def feature_name(self):
 
514
        return 'attrib Windows command-line tool'
 
515
 
 
516
 
 
517
AttribFeature = _AttribFeature()
 
518
 
 
519
 
 
520
class Win32Feature(Feature):
 
521
    """Feature testing whether we're running selftest on Windows
 
522
    or Windows-like platform.
 
523
    """
 
524
 
 
525
    def _probe(self):
 
526
        return sys.platform == 'win32'
 
527
 
 
528
    def feature_name(self):
 
529
        return "win32 platform"
 
530
 
 
531
 
 
532
win32_feature = Win32Feature()
 
533
 
 
534
 
 
535
class _ColorFeature(Feature):
 
536
 
 
537
    def _probe(self):
 
538
        from breezy._termcolor import allow_color
 
539
        return allow_color()
 
540
 
 
541
    def feature_name(self):
 
542
        return "Terminal supports color."
 
543
 
 
544
ColorFeature = _ColorFeature()