/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
6728.1.2 by Jelmer Vernooij
Sign using python-gpg rather than command-line gpg.
20
from __future__ import absolute_import
21
5036.3.8 by Parth Malwankar
closed review comments from vila
22
import os
6060.1.1 by John Arbash Meinel
import subprocess in 'bzrlib.tests.features' so that certain features can properly probe.
23
import subprocess
5036.3.8 by Parth Malwankar
closed review comments from vila
24
import stat
5609.47.1 by Alexander Belchenko
Win32Feature
25
import sys
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
26
import tempfile
6760.1.1 by Jelmer Vernooij
Ignore DeprecationWarning/PendingDeprecationWarning during module probes. We can't do anything about these anyway.
27
import warnings
4584.3.21 by Martin Pool
Start adding tests for apport
28
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
29
from .. import (
5321.2.1 by Vincent Ladeuil
Fix style issues, including vertical spaces, lines too long and multi lines imports.
30
    osutils,
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
31
    symbol_versioning,
5321.2.1 by Vincent Ladeuil
Fix style issues, including vertical spaces, lines too long and multi lines imports.
32
    )
4913.2.19 by John Arbash Meinel
Compatibly rename ApportFeature to features.apport.
33
34
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
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:
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
141
            from breezy import pyutils
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
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)
6325.3.1 by Vincent Ladeuil
Give meaningful deprecation warnings for deprecated test features
146
            symbol_versioning.warn(depr_msg + use_msg, DeprecationWarning,
147
                                   stacklevel=5)
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
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
6760.1.1 by Jelmer Vernooij
Ignore DeprecationWarning/PendingDeprecationWarning during module probes. We can't do anything about these anyway.
167
    def __init__(self, module_name, ignore_warnings=None):
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
168
        super(ModuleAvailableFeature, self).__init__()
169
        self.module_name = module_name
6760.1.3 by Jelmer Vernooij
Martin's review comments.
170
        if ignore_warnings is None:
171
            ignore_warnings = ()
6760.1.1 by Jelmer Vernooij
Ignore DeprecationWarning/PendingDeprecationWarning during module probes. We can't do anything about these anyway.
172
        self.ignore_warnings = ignore_warnings
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
173
174
    def _probe(self):
5642.4.3 by Vincent Ladeuil
Rework the fix taking mgz remarks into account and rebasing against trunk.
175
        sentinel = object()
176
        module = sys.modules.get(self.module_name, sentinel)
177
        if module is sentinel:
6760.1.1 by Jelmer Vernooij
Ignore DeprecationWarning/PendingDeprecationWarning during module probes. We can't do anything about these anyway.
178
            with warnings.catch_warnings():
6760.1.3 by Jelmer Vernooij
Martin's review comments.
179
                for warning_category in self.ignore_warnings:
6760.1.1 by Jelmer Vernooij
Ignore DeprecationWarning/PendingDeprecationWarning during module probes. We can't do anything about these anyway.
180
                    warnings.simplefilter('ignore', warning_category)
181
                try:
6759.3.3 by Jelmer Vernooij
merge trunk
182
                    self._module = __import__(self.module_name)
6760.1.1 by Jelmer Vernooij
Ignore DeprecationWarning/PendingDeprecationWarning during module probes. We can't do anything about these anyway.
183
                except ImportError:
184
                    return False
6760.1.3 by Jelmer Vernooij
Martin's review comments.
185
                return True
5642.4.3 by Vincent Ladeuil
Rework the fix taking mgz remarks into account and rebasing against trunk.
186
        else:
187
            self._module = module
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
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
6703.1.1 by Jelmer Vernooij
Add a ``PluginLoadedFeature``.
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):
6759.4.3 by Jelmer Vernooij
Avoid accessing global state.
215
        from breezy.plugin import get_loaded_plugin
216
        return (get_loaded_plugin(self.plugin_name) is not None)
6703.1.1 by Jelmer Vernooij
Add a ``PluginLoadedFeature``.
217
218
    @property
219
    def plugin(self):
6759.4.3 by Jelmer Vernooij
Avoid accessing global state.
220
        from breezy.plugin import get_loaded_plugin
221
        return get_loaded_plugin(self.plugin_name)
6703.1.1 by Jelmer Vernooij
Add a ``PluginLoadedFeature``.
222
223
    def feature_name(self):
6703.1.2 by Jelmer Vernooij
Change feature name to '... plugin'.
224
        return '%s plugin' % self.plugin_name
6703.1.1 by Jelmer Vernooij
Add a ``PluginLoadedFeature``.
225
226
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
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):
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
274
        from breezy import breakin
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
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
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
329
        from breezy import tests
6280.1.1 by Martin Pool
Restore old name for bzrlib.tests.feature to unbreak bzr fastimport etc
330
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
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)
6280.1.2 by Martin Pool
Fix NameError
342
        tests._rmtree_temp_dir(tdir)
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
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):
4797.70.1 by Vincent Ladeuil
Skip chmodbits dependent tests when running as root
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()
5448.5.9 by Vincent Ladeuil
Make the test depends on a feature so it's skipped if meliae is not installed
383
6760.1.2 by Jelmer Vernooij
Add note.
384
# Apport uses deprecated imp module on python3.
6760.1.1 by Jelmer Vernooij
Ignore DeprecationWarning/PendingDeprecationWarning during module probes. We can't do anything about these anyway.
385
apport = ModuleAvailableFeature(
386
    'apport.report',
387
    ignore_warnings=[DeprecationWarning, PendingDeprecationWarning])
6728.1.1 by Jelmer Vernooij
Use python-gpg rather than python-gpgme.
388
gpg = ModuleAvailableFeature('gpg')
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
389
lzma = ModuleAvailableFeature('lzma')
6091.2.2 by Max Bowsher
Per jam's review comments, get rid of features.meliae_feature, which is new in
390
meliae = ModuleAvailableFeature('meliae.scanner')
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
391
paramiko = ModuleAvailableFeature('paramiko')
392
pywintypes = ModuleAvailableFeature('pywintypes')
393
subunit = ModuleAvailableFeature('subunit')
5967.12.3 by Martin Pool
Unify duplicated UnicodeFilename and _PosixPermissionsFeature
394
testtools = ModuleAvailableFeature('testtools')
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
395
396
compiled_patiencediff_feature = ModuleAvailableFeature(
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
397
    'breezy._patiencediff_c')
398
lsprof_feature = ModuleAvailableFeature('breezy.lsprof')
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
399
400
401
class _BackslashDirSeparatorFeature(Feature):
5241.2.1 by Robert Collins
Merge up from 2.0/2.1:
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
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
417
class _ChownFeature(Feature):
5051.4.10 by Parth Malwankar
moved ChownFeature to tests/features.py
418
    """os.chown is supported"""
419
420
    def _probe(self):
421
        return os.name == 'posix' and hasattr(os, 'chown')
422
5051.4.11 by Parth Malwankar
closed Martins review comments.
423
chown_feature = _ChownFeature()
5051.4.10 by Parth Malwankar
moved ChownFeature to tests/features.py
424
5147.5.20 by Martin von Gagern
Move ExecutableFeature class from bash_completion plugin to bzrlib tests.
425
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
426
class ExecutableFeature(Feature):
5147.5.20 by Martin von Gagern
Move ExecutableFeature class from bash_completion plugin to bzrlib tests.
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
5147.5.23 by Martin von Gagern
Have ExecutableFeature implement and use _probe.
432
        self._path = None
5147.5.20 by Martin von Gagern
Move ExecutableFeature class from bash_completion plugin to bzrlib tests.
433
434
    @property
435
    def path(self):
5147.5.23 by Martin von Gagern
Have ExecutableFeature implement and use _probe.
436
        # This is a property, so accessing path ensures _probe was called
437
        self.available()
438
        return self._path
5147.5.20 by Martin von Gagern
Move ExecutableFeature class from bash_completion plugin to bzrlib tests.
439
5147.5.23 by Martin von Gagern
Have ExecutableFeature implement and use _probe.
440
    def _probe(self):
5321.1.82 by Gordon Tyler
Changed ExecutableFeature to use osutils.find_executable_on_path.
441
        self._path = osutils.find_executable_on_path(self.name)
442
        return self._path is not None
5147.5.20 by Martin von Gagern
Move ExecutableFeature class from bash_completion plugin to bzrlib tests.
443
444
    def feature_name(self):
445
        return '%s executable' % self.name
5147.5.24 by Martin von Gagern
Move ExecutableFeature instances to tests.features module.
446
447
448
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.
449
diff_feature = ExecutableFeature('diff')
5147.5.24 by Martin von Gagern
Move ExecutableFeature instances to tests.features module.
450
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.
451
msgmerge_feature = ExecutableFeature('msgmerge')
5609.47.1 by Alexander Belchenko
Win32Feature
452
453
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
454
class _PosixPermissionsFeature(Feature):
455
456
    def _probe(self):
457
        def has_perms():
458
            # Create temporary file and check if specified perms are
459
            # maintained.
460
            write_perms = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
461
            f = tempfile.mkstemp(prefix='bzr_perms_chk_')
462
            fd, name = f
463
            os.close(fd)
6344.1.1 by Martin Packman
Merge 2.4 into bzr.dev
464
            osutils.chmod_if_possible(name, write_perms)
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
465
6619.3.14 by Jelmer Vernooij
Convert some octal numbers to new notations.
466
            read_perms = os.stat(name).st_mode & 0o777
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
467
            os.unlink(name)
468
            return (write_perms == read_perms)
469
470
        return (os.name == 'posix') and has_perms()
471
472
    def feature_name(self):
473
        return 'POSIX permissions support'
474
475
476
posix_permissions_feature = _PosixPermissionsFeature()
477
478
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
6619.3.2 by Jelmer Vernooij
Apply 2to3 except fix.
488
        except OSError as e:
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
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)
6619.3.2 by Jelmer Vernooij
Apply 2to3 except fix.
509
        except OSError as e:
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
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()
5967.12.2 by Martin Pool
Move all features to bzrlib.tests.features in 2.5
518
519
5967.12.3 by Martin Pool
Unify duplicated UnicodeFilename and _PosixPermissionsFeature
520
class Win32Feature(Feature):
5609.47.1 by Alexander Belchenko
Win32Feature
521
    """Feature testing whether we're running selftest on Windows
5609.47.4 by Alexander Belchenko
fixed typo
522
    or Windows-like platform.
5609.47.1 by Alexander Belchenko
Win32Feature
523
    """
524
525
    def _probe(self):
526
        return sys.platform == 'win32'
527
528
    def feature_name(self):
529
        return "win32 platform"
530
5967.12.4 by Martin Pool
Support (but deprecated) old feature names
531
5609.47.1 by Alexander Belchenko
Win32Feature
532
win32_feature = Win32Feature()
6531.3.8 by Jelmer Vernooij
Move color feature into bzrlib.tests.features.
533
534
535
class _ColorFeature(Feature):
536
537
    def _probe(self):
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
538
        from breezy._termcolor import allow_color
6531.3.8 by Jelmer Vernooij
Move color feature into bzrlib.tests.features.
539
        return allow_color()
540
541
    def feature_name(self):
542
        return "Terminal supports color."
543
544
ColorFeature = _ColorFeature()