/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-06-18 10:15:11 UTC
  • mto: This revision was merged to the branch mainline in revision 6715.
  • Revision ID: gzlist@googlemail.com-20170618101511-fri1mouxt1hc09r8
Make _simple_set tests pass on py3 and with random hash

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
 
17
20
import os
 
21
import subprocess
18
22
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):
 
23
import sys
 
24
import tempfile
 
25
 
 
26
from .. import (
 
27
    osutils,
 
28
    symbol_versioning,
 
29
    )
 
30
 
 
31
 
 
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 breezy 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
                                   stacklevel=5)
 
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):
 
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
 
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 PluginLoadedFeature(Feature):
 
192
    """Check whether a plugin with specific name is loaded.
 
193
 
 
194
    This is different from ModuleAvailableFeature, because
 
195
    plugins can be available but explicitly disabled
 
196
    (e.g. through BRZ_DISABLE_PLUGINS=blah).
 
197
 
 
198
    :ivar plugin_name: The name of the plugin
 
199
    """
 
200
 
 
201
    def __init__(self, plugin_name):
 
202
        super(PluginLoadedFeature, self).__init__()
 
203
        self.plugin_name = plugin_name
 
204
 
 
205
    def _probe(self):
 
206
        import breezy
 
207
        return self.plugin_name in breezy.global_state.plugins
 
208
 
 
209
    @property
 
210
    def plugin(self):
 
211
        import breezy
 
212
        return breezy.global_state.plugins.get(self.plugin_name)
 
213
 
 
214
    def feature_name(self):
 
215
        return '%s plugin' % self.plugin_name
 
216
 
 
217
 
 
218
class _HTTPSServerFeature(Feature):
 
219
    """Some tests want an https Server, check if one is available.
 
220
 
 
221
    Right now, the only way this is available is under python2.6 which provides
 
222
    an ssl module.
 
223
    """
 
224
 
 
225
    def _probe(self):
 
226
        try:
 
227
            import ssl
 
228
            return True
 
229
        except ImportError:
 
230
            return False
 
231
 
 
232
    def feature_name(self):
 
233
        return 'HTTPSServer'
 
234
 
 
235
 
 
236
HTTPSServerFeature = _HTTPSServerFeature()
 
237
 
 
238
 
 
239
class _ByteStringNamedFilesystem(Feature):
 
240
    """Is the filesystem based on bytes?"""
 
241
 
 
242
    def _probe(self):
 
243
        if os.name == "posix":
 
244
            return True
 
245
        return False
 
246
 
 
247
ByteStringNamedFilesystem = _ByteStringNamedFilesystem()
 
248
 
 
249
 
 
250
class _UTF8Filesystem(Feature):
 
251
    """Is the filesystem UTF-8?"""
 
252
 
 
253
    def _probe(self):
 
254
        if osutils._fs_enc.upper() in ('UTF-8', 'UTF8'):
 
255
            return True
 
256
        return False
 
257
 
 
258
UTF8Filesystem = _UTF8Filesystem()
 
259
 
 
260
 
 
261
class _BreakinFeature(Feature):
 
262
    """Does this platform support the breakin feature?"""
 
263
 
 
264
    def _probe(self):
 
265
        from breezy import breakin
 
266
        if breakin.determine_signal() is None:
 
267
            return False
 
268
        if sys.platform == 'win32':
 
269
            # Windows doesn't have os.kill, and we catch the SIGBREAK signal.
 
270
            # We trigger SIGBREAK via a Console api so we need ctypes to
 
271
            # access the function
 
272
            try:
 
273
                import ctypes
 
274
            except OSError:
 
275
                return False
 
276
        return True
 
277
 
 
278
    def feature_name(self):
 
279
        return "SIGQUIT or SIGBREAK w/ctypes on win32"
 
280
 
 
281
 
 
282
BreakinFeature = _BreakinFeature()
 
283
 
 
284
 
 
285
class _CaseInsCasePresFilenameFeature(Feature):
 
286
    """Is the file-system case insensitive, but case-preserving?"""
 
287
 
 
288
    def _probe(self):
 
289
        fileno, name = tempfile.mkstemp(prefix='MixedCase')
 
290
        try:
 
291
            # first check truly case-preserving for created files, then check
 
292
            # case insensitive when opening existing files.
 
293
            name = osutils.normpath(name)
 
294
            base, rel = osutils.split(name)
 
295
            found_rel = osutils.canonical_relpath(base, name)
 
296
            return (found_rel == rel
 
297
                    and os.path.isfile(name.upper())
 
298
                    and os.path.isfile(name.lower()))
 
299
        finally:
 
300
            os.close(fileno)
 
301
            os.remove(name)
 
302
 
 
303
    def feature_name(self):
 
304
        return "case-insensitive case-preserving filesystem"
 
305
 
 
306
CaseInsCasePresFilenameFeature = _CaseInsCasePresFilenameFeature()
 
307
 
 
308
 
 
309
class _CaseInsensitiveFilesystemFeature(Feature):
 
310
    """Check if underlying filesystem is case-insensitive but *not* case
 
311
    preserving.
 
312
    """
 
313
    # Note that on Windows, Cygwin, MacOS etc, the file-systems are far
 
314
    # more likely to be case preserving, so this case is rare.
 
315
 
 
316
    def _probe(self):
 
317
        if CaseInsCasePresFilenameFeature.available():
 
318
            return False
 
319
 
 
320
        from breezy import tests
 
321
 
 
322
        if tests.TestCaseWithMemoryTransport.TEST_ROOT is None:
 
323
            root = osutils.mkdtemp(prefix='testbzr-', suffix='.tmp')
 
324
            tests.TestCaseWithMemoryTransport.TEST_ROOT = root
 
325
        else:
 
326
            root = tests.TestCaseWithMemoryTransport.TEST_ROOT
 
327
        tdir = osutils.mkdtemp(prefix='case-sensitive-probe-', suffix='',
 
328
            dir=root)
 
329
        name_a = osutils.pathjoin(tdir, 'a')
 
330
        name_A = osutils.pathjoin(tdir, 'A')
 
331
        os.mkdir(name_a)
 
332
        result = osutils.isdir(name_A)
 
333
        tests._rmtree_temp_dir(tdir)
 
334
        return result
 
335
 
 
336
    def feature_name(self):
 
337
        return 'case-insensitive filesystem'
 
338
 
 
339
CaseInsensitiveFilesystemFeature = _CaseInsensitiveFilesystemFeature()
 
340
 
 
341
 
 
342
class _CaseSensitiveFilesystemFeature(Feature):
 
343
 
 
344
    def _probe(self):
 
345
        if CaseInsCasePresFilenameFeature.available():
 
346
            return False
 
347
        elif CaseInsensitiveFilesystemFeature.available():
 
348
            return False
 
349
        else:
 
350
            return True
 
351
 
 
352
    def feature_name(self):
 
353
        return 'case-sensitive filesystem'
 
354
 
 
355
# new coding style is for feature instances to be lowercase
 
356
case_sensitive_filesystem_feature = _CaseSensitiveFilesystemFeature()
 
357
 
 
358
 
 
359
class _NotRunningAsRoot(Feature):
 
360
 
 
361
    def _probe(self):
 
362
        try:
 
363
            uid = os.getuid()
 
364
        except AttributeError:
 
365
            # If there is no uid, chances are there is no root either
 
366
            return True
 
367
        return uid != 0
 
368
 
 
369
    def feature_name(self):
 
370
        return 'Not running as root'
 
371
 
 
372
 
 
373
not_running_as_root = _NotRunningAsRoot()
 
374
 
 
375
apport = ModuleAvailableFeature('apport')
 
376
gpgme = ModuleAvailableFeature('gpgme')
 
377
lzma = ModuleAvailableFeature('lzma')
 
378
meliae = ModuleAvailableFeature('meliae.scanner')
 
379
paramiko = ModuleAvailableFeature('paramiko')
 
380
pywintypes = ModuleAvailableFeature('pywintypes')
 
381
subunit = ModuleAvailableFeature('subunit')
 
382
testtools = ModuleAvailableFeature('testtools')
 
383
 
 
384
compiled_patiencediff_feature = ModuleAvailableFeature(
 
385
    'breezy._patiencediff_c')
 
386
lsprof_feature = ModuleAvailableFeature('breezy.lsprof')
 
387
 
 
388
 
 
389
class _BackslashDirSeparatorFeature(Feature):
 
390
 
 
391
    def _probe(self):
 
392
        try:
 
393
            os.lstat(os.getcwd() + '\\')
 
394
        except OSError:
 
395
            return False
 
396
        else:
 
397
            return True
 
398
 
 
399
    def feature_name(self):
 
400
        return "Filesystem treats '\\' as a directory separator."
 
401
 
 
402
backslashdir_feature = _BackslashDirSeparatorFeature()
 
403
 
 
404
 
 
405
class _ChownFeature(Feature):
 
406
    """os.chown is supported"""
 
407
 
 
408
    def _probe(self):
 
409
        return os.name == 'posix' and hasattr(os, 'chown')
 
410
 
 
411
chown_feature = _ChownFeature()
 
412
 
 
413
 
 
414
class ExecutableFeature(Feature):
 
415
    """Feature testing whether an executable of a given name is on the PATH."""
 
416
 
 
417
    def __init__(self, name):
 
418
        super(ExecutableFeature, self).__init__()
 
419
        self.name = name
 
420
        self._path = None
 
421
 
 
422
    @property
 
423
    def path(self):
 
424
        # This is a property, so accessing path ensures _probe was called
 
425
        self.available()
 
426
        return self._path
 
427
 
 
428
    def _probe(self):
 
429
        self._path = osutils.find_executable_on_path(self.name)
 
430
        return self._path is not None
 
431
 
 
432
    def feature_name(self):
 
433
        return '%s executable' % self.name
 
434
 
 
435
 
 
436
bash_feature = ExecutableFeature('bash')
 
437
diff_feature = ExecutableFeature('diff')
 
438
sed_feature = ExecutableFeature('sed')
 
439
msgmerge_feature = ExecutableFeature('msgmerge')
 
440
 
 
441
 
 
442
class _PosixPermissionsFeature(Feature):
31
443
 
32
444
    def _probe(self):
33
445
        def has_perms():
34
 
            # create temporary file and check if specified perms are maintained.
35
 
            import tempfile
36
 
 
 
446
            # Create temporary file and check if specified perms are
 
447
            # maintained.
37
448
            write_perms = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
38
449
            f = tempfile.mkstemp(prefix='bzr_perms_chk_')
39
450
            fd, name = f
40
451
            os.close(fd)
41
 
            os.chmod(name, write_perms)
 
452
            osutils.chmod_if_possible(name, write_perms)
42
453
 
43
 
            read_perms = os.stat(name).st_mode & 0777
 
454
            read_perms = os.stat(name).st_mode & 0o777
44
455
            os.unlink(name)
45
456
            return (write_perms == read_perms)
46
457
 
53
464
posix_permissions_feature = _PosixPermissionsFeature()
54
465
 
55
466
 
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
 
 
 
467
class _StraceFeature(Feature):
 
468
 
 
469
    def _probe(self):
 
470
        try:
 
471
            proc = subprocess.Popen(['strace'],
 
472
                stderr=subprocess.PIPE,
 
473
                stdout=subprocess.PIPE)
 
474
            proc.communicate()
 
475
            return True
 
476
        except OSError as e:
 
477
            if e.errno == errno.ENOENT:
 
478
                # strace is not installed
 
479
                return False
 
480
            else:
 
481
                raise
 
482
 
 
483
    def feature_name(self):
 
484
        return 'strace'
 
485
 
 
486
 
 
487
strace_feature = _StraceFeature()
 
488
 
 
489
 
 
490
class _AttribFeature(Feature):
 
491
 
 
492
    def _probe(self):
 
493
        if (sys.platform not in ('cygwin', 'win32')):
 
494
            return False
 
495
        try:
 
496
            proc = subprocess.Popen(['attrib', '.'], stdout=subprocess.PIPE)
 
497
        except OSError as e:
 
498
            return False
 
499
        return (0 == proc.wait())
 
500
 
 
501
    def feature_name(self):
 
502
        return 'attrib Windows command-line tool'
 
503
 
 
504
 
 
505
AttribFeature = _AttribFeature()
 
506
 
 
507
 
 
508
class Win32Feature(Feature):
 
509
    """Feature testing whether we're running selftest on Windows
 
510
    or Windows-like platform.
 
511
    """
 
512
 
 
513
    def _probe(self):
 
514
        return sys.platform == 'win32'
 
515
 
 
516
    def feature_name(self):
 
517
        return "win32 platform"
 
518
 
 
519
 
 
520
win32_feature = Win32Feature()
 
521
 
 
522
 
 
523
class _ColorFeature(Feature):
 
524
 
 
525
    def _probe(self):
 
526
        from breezy._termcolor import allow_color
 
527
        return allow_color()
 
528
 
 
529
    def feature_name(self):
 
530
        return "Terminal supports color."
 
531
 
 
532
ColorFeature = _ColorFeature()