/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: Martin
  • Date: 2017-08-26 16:16:11 UTC
  • mto: This revision was merged to the branch mainline in revision 6763.
  • Revision ID: gzlist@googlemail.com-20170826161611-gzjgbcvq03u9vr38
One last dict.iteritems() in hooks tests

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